diff options
Diffstat (limited to 'nixos')
24 files changed, 549 insertions, 422 deletions
diff --git a/nixos/doc/manual/configuration/config-syntax.chapter.md b/nixos/doc/manual/configuration/config-syntax.chapter.md index 56d093c0f6e84..9f8d45d588997 100644 --- a/nixos/doc/manual/configuration/config-syntax.chapter.md +++ b/nixos/doc/manual/configuration/config-syntax.chapter.md @@ -15,5 +15,4 @@ NixOS configuration files. <xi:include href="config-file.section.xml" /> <xi:include href="abstractions.section.xml" /> <xi:include href="modularity.section.xml" /> -<xi:include href="summary.section.xml" /> ``` diff --git a/nixos/doc/manual/configuration/summary.section.md b/nixos/doc/manual/configuration/summary.section.md deleted file mode 100644 index 8abbbe257fd90..0000000000000 --- a/nixos/doc/manual/configuration/summary.section.md +++ /dev/null @@ -1,46 +0,0 @@ -# Syntax Summary {#sec-nix-syntax-summary} - -Below is a summary of the most important syntactic constructs in the Nix -expression language. It's not complete. In particular, there are many -other built-in functions. See the [Nix -manual](https://nixos.org/nix/manual/#chap-writing-nix-expressions) for -the rest. - -| Example | Description | -|-----------------------------------------------|--------------------------------------------------------------------------------------------------------------------| -| *Basic values* | | -| `"Hello world"` | A string | -| `"${pkgs.bash}/bin/sh"` | A string containing an expression (expands to `"/nix/store/hash-bash-version/bin/sh"`) | -| `true`, `false` | Booleans | -| `123` | An integer | -| `./foo.png` | A path (relative to the containing Nix expression) | -| *Compound values* | | -| `{ x = 1; y = 2; }` | A set with attributes named `x` and `y` | -| `{ foo.bar = 1; }` | A nested set, equivalent to `{ foo = { bar = 1; }; }` | -| `rec { x = "foo"; y = x + "bar"; }` | A recursive set, equivalent to `{ x = "foo"; y = "foobar"; }` | -| `[ "foo" "bar" ]` | A list with two elements | -| *Operators* | | -| `"foo" + "bar"` | String concatenation | -| `1 + 2` | Integer addition | -| `"foo" == "f" + "oo"` | Equality test (evaluates to `true`) | -| `"foo" != "bar"` | Inequality test (evaluates to `true`) | -| `!true` | Boolean negation | -| `{ x = 1; y = 2; }.x` | Attribute selection (evaluates to `1`) | -| `{ x = 1; y = 2; }.z or 3` | Attribute selection with default (evaluates to `3`) | -| `{ x = 1; y = 2; } // { z = 3; }` | Merge two sets (attributes in the right-hand set taking precedence) | -| *Control structures* | | -| `if 1 + 1 == 2 then "yes!" else "no!"` | Conditional expression | -| `assert 1 + 1 == 2; "yes!"` | Assertion check (evaluates to `"yes!"`). See [](#sec-assertions) for using assertions in modules | -| `let x = "foo"; y = "bar"; in x + y` | Variable definition | -| `with pkgs.lib; head [ 1 2 3 ]` | Add all attributes from the given set to the scope (evaluates to `1`) | -| *Functions (lambdas)* | | -| `x: x + 1` | A function that expects an integer and returns it increased by 1 | -| `(x: x + 1) 100` | A function call (evaluates to 101) | -| `let inc = x: x + 1; in inc (inc (inc 100))` | A function bound to a variable and subsequently called by name (evaluates to 103) | -| `{ x, y }: x + y` | A function that expects a set with required attributes `x` and `y` and concatenates them | -| `{ x, y ? "bar" }: x + y` | A function that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` | -| `{ x, y, ... }: x + y` | A function that expects a set with required attributes `x` and `y` and ignores any other attributes | -| `{ x, y } @ args: x + y` | A function that expects a set with required attributes `x` and `y`, and binds the whole set to `args` | -| *Built-in functions* | | -| `import ./foo.nix` | Load and return Nix expression in given file | -| `map (x: x + x) [ 1 2 3 ]` | Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`) | diff --git a/nixos/doc/manual/configuration/user-mgmt.chapter.md b/nixos/doc/manual/configuration/user-mgmt.chapter.md index 37990664a8f1b..5c3aca3ef9e95 100644 --- a/nixos/doc/manual/configuration/user-mgmt.chapter.md +++ b/nixos/doc/manual/configuration/user-mgmt.chapter.md @@ -32,8 +32,7 @@ account will cease to exist. Also, imperative commands for managing users and groups, such as useradd, are no longer available. Passwords may still be assigned by setting the user\'s [hashedPassword](#opt-users.users._name_.hashedPassword) option. A -hashed password can be generated using `mkpasswd -m - sha-512`. +hashed password can be generated using `mkpasswd`. A user ID (uid) is assigned automatically. You can also specify a uid manually by adding diff --git a/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml b/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml index 01446e53e38ff..baf9639554cc2 100644 --- a/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml +++ b/nixos/doc/manual/from_md/configuration/config-syntax.chapter.xml @@ -17,5 +17,4 @@ <xi:include href="config-file.section.xml" /> <xi:include href="abstractions.section.xml" /> <xi:include href="modularity.section.xml" /> - <xi:include href="summary.section.xml" /> </chapter> diff --git a/nixos/doc/manual/from_md/configuration/summary.section.xml b/nixos/doc/manual/from_md/configuration/summary.section.xml deleted file mode 100644 index 96a178c4930ed..0000000000000 --- a/nixos/doc/manual/from_md/configuration/summary.section.xml +++ /dev/null @@ -1,332 +0,0 @@ -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-nix-syntax-summary"> - <title>Syntax Summary</title> - <para> - Below is a summary of the most important syntactic constructs in the - Nix expression language. It’s not complete. In particular, there are - many other built-in functions. See the - <link xlink:href="https://nixos.org/nix/manual/#chap-writing-nix-expressions">Nix - manual</link> for the rest. - </para> - <informaltable> - <tgroup cols="2"> - <colspec align="left" /> - <colspec align="left" /> - <thead> - <row> - <entry> - Example - </entry> - <entry> - Description - </entry> - </row> - </thead> - <tbody> - <row> - <entry> - <emphasis>Basic values</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>"Hello world"</literal> - </entry> - <entry> - A string - </entry> - </row> - <row> - <entry> - <literal>"${pkgs.bash}/bin/sh"</literal> - </entry> - <entry> - A string containing an expression (expands to - <literal>"/nix/store/hash-bash-version/bin/sh"</literal>) - </entry> - </row> - <row> - <entry> - <literal>true</literal>, <literal>false</literal> - </entry> - <entry> - Booleans - </entry> - </row> - <row> - <entry> - <literal>123</literal> - </entry> - <entry> - An integer - </entry> - </row> - <row> - <entry> - <literal>./foo.png</literal> - </entry> - <entry> - A path (relative to the containing Nix expression) - </entry> - </row> - <row> - <entry> - <emphasis>Compound values</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>{ x = 1; y = 2; }</literal> - </entry> - <entry> - A set with attributes named <literal>x</literal> and - <literal>y</literal> - </entry> - </row> - <row> - <entry> - <literal>{ foo.bar = 1; }</literal> - </entry> - <entry> - A nested set, equivalent to - <literal>{ foo = { bar = 1; }; }</literal> - </entry> - </row> - <row> - <entry> - <literal>rec { x = "foo"; y = x + "bar"; }</literal> - </entry> - <entry> - A recursive set, equivalent to - <literal>{ x = "foo"; y = "foobar"; }</literal> - </entry> - </row> - <row> - <entry> - <literal>[ "foo" "bar" ]</literal> - </entry> - <entry> - A list with two elements - </entry> - </row> - <row> - <entry> - <emphasis>Operators</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>"foo" + "bar"</literal> - </entry> - <entry> - String concatenation - </entry> - </row> - <row> - <entry> - <literal>1 + 2</literal> - </entry> - <entry> - Integer addition - </entry> - </row> - <row> - <entry> - <literal>"foo" == "f" + "oo"</literal> - </entry> - <entry> - Equality test (evaluates to <literal>true</literal>) - </entry> - </row> - <row> - <entry> - <literal>"foo" != "bar"</literal> - </entry> - <entry> - Inequality test (evaluates to <literal>true</literal>) - </entry> - </row> - <row> - <entry> - <literal>!true</literal> - </entry> - <entry> - Boolean negation - </entry> - </row> - <row> - <entry> - <literal>{ x = 1; y = 2; }.x</literal> - </entry> - <entry> - Attribute selection (evaluates to <literal>1</literal>) - </entry> - </row> - <row> - <entry> - <literal>{ x = 1; y = 2; }.z or 3</literal> - </entry> - <entry> - Attribute selection with default (evaluates to - <literal>3</literal>) - </entry> - </row> - <row> - <entry> - <literal>{ x = 1; y = 2; } // { z = 3; }</literal> - </entry> - <entry> - Merge two sets (attributes in the right-hand set taking - precedence) - </entry> - </row> - <row> - <entry> - <emphasis>Control structures</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>if 1 + 1 == 2 then "yes!" else "no!"</literal> - </entry> - <entry> - Conditional expression - </entry> - </row> - <row> - <entry> - <literal>assert 1 + 1 == 2; "yes!"</literal> - </entry> - <entry> - Assertion check (evaluates to - <literal>"yes!"</literal>). See - <xref linkend="sec-assertions" /> for using assertions in - modules - </entry> - </row> - <row> - <entry> - <literal>let x = "foo"; y = "bar"; in x + y</literal> - </entry> - <entry> - Variable definition - </entry> - </row> - <row> - <entry> - <literal>with pkgs.lib; head [ 1 2 3 ]</literal> - </entry> - <entry> - Add all attributes from the given set to the scope - (evaluates to <literal>1</literal>) - </entry> - </row> - <row> - <entry> - <emphasis>Functions (lambdas)</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>x: x + 1</literal> - </entry> - <entry> - A function that expects an integer and returns it increased - by 1 - </entry> - </row> - <row> - <entry> - <literal>(x: x + 1) 100</literal> - </entry> - <entry> - A function call (evaluates to 101) - </entry> - </row> - <row> - <entry> - <literal>let inc = x: x + 1; in inc (inc (inc 100))</literal> - </entry> - <entry> - A function bound to a variable and subsequently called by - name (evaluates to 103) - </entry> - </row> - <row> - <entry> - <literal>{ x, y }: x + y</literal> - </entry> - <entry> - A function that expects a set with required attributes - <literal>x</literal> and <literal>y</literal> and - concatenates them - </entry> - </row> - <row> - <entry> - <literal>{ x, y ? "bar" }: x + y</literal> - </entry> - <entry> - A function that expects a set with required attribute - <literal>x</literal> and optional <literal>y</literal>, - using <literal>"bar"</literal> as default value - for <literal>y</literal> - </entry> - </row> - <row> - <entry> - <literal>{ x, y, ... }: x + y</literal> - </entry> - <entry> - A function that expects a set with required attributes - <literal>x</literal> and <literal>y</literal> and ignores - any other attributes - </entry> - </row> - <row> - <entry> - <literal>{ x, y } @ args: x + y</literal> - </entry> - <entry> - A function that expects a set with required attributes - <literal>x</literal> and <literal>y</literal>, and binds the - whole set to <literal>args</literal> - </entry> - </row> - <row> - <entry> - <emphasis>Built-in functions</emphasis> - </entry> - <entry> - </entry> - </row> - <row> - <entry> - <literal>import ./foo.nix</literal> - </entry> - <entry> - Load and return Nix expression in given file - </entry> - </row> - <row> - <entry> - <literal>map (x: x + x) [ 1 2 3 ]</literal> - </entry> - <entry> - Apply a function to every element of a list (evaluates to - <literal>[ 2 4 6 ]</literal>) - </entry> - </row> - </tbody> - </tgroup> - </informaltable> -</section> diff --git a/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml b/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml index 06492d5c25126..a2d7d2a9f1154 100644 --- a/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml +++ b/nixos/doc/manual/from_md/configuration/user-mgmt.chapter.xml @@ -39,7 +39,7 @@ users.users.alice = { Passwords may still be assigned by setting the user's <link linkend="opt-users.users._name_.hashedPassword">hashedPassword</link> option. A hashed password can be generated using - <literal>mkpasswd -m sha-512</literal>. + <literal>mkpasswd</literal>. </para> <para> A user ID (uid) is assigned automatically. You can also specify a diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py index e45c83086fb52..ffbc7c18e42b6 100644 --- a/nixos/lib/test-driver/test_driver/machine.py +++ b/nixos/lib/test-driver/test_driver/machine.py @@ -684,10 +684,10 @@ class Machine: with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)): retry(tty_matches) - def send_chars(self, chars: str) -> None: + def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None: with self.nested("sending keys ‘{}‘".format(chars)): for char in chars: - self.send_key(char) + self.send_key(char, delay) def wait_for_file(self, filename: str) -> None: """Waits until the file exists in machine's file system.""" @@ -860,10 +860,11 @@ class Machine: if matches is not None: return - def send_key(self, key: str) -> None: + def send_key(self, key: str, delay: Optional[float] = 0.01) -> None: key = CHAR_TO_KEY.get(key, key) self.send_monitor_command("sendkey {}".format(key)) - time.sleep(0.01) + if delay is not None: + time.sleep(delay) def send_console(self, chars: str) -> None: assert self.process diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index b538a0119c06d..2660b0e6c9388 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -35,7 +35,7 @@ let ''; hashedPasswordDescription = '' - To generate a hashed password run `mkpasswd -m sha-512`. + To generate a hashed password run `mkpasswd`. If set to an empty string (`""`), this user will be able to log in without being asked for a password (but not via remote @@ -592,6 +592,26 @@ in { ''; }; + # Warn about user accounts with deprecated password hashing schemes + system.activationScripts.hashes = { + deps = [ "users" ]; + text = '' + users=() + while IFS=: read -r user hash tail; do + if [[ "$hash" = "$"* && ! "$hash" =~ ^\$(y|gy|7|2b|2y|2a|6)\$ ]]; then + users+=("$user") + fi + done </etc/shadow + + if (( "''${#users[@]}" )); then + echo " + WARNING: The following user accounts rely on password hashes that will + be removed in NixOS 23.05. They should be renewed as soon as possible." + printf ' - %s\n' "''${users[@]}" + fi + ''; + }; + # for backwards compatibility system.activationScripts.groups = stringAfter [ "users" ] ""; diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix index 288cbc94a321b..3f3571d253825 100644 --- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix +++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares.nix @@ -14,8 +14,6 @@ in calamares-nixos calamares-nixos-autostart calamares-nixos-extensions - # Needed for calamares QML module packagechooserq - libsForQt5.full # Get list of locales glibcLocales ]; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index a886332e90b70..2a23a32eaba6e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -382,6 +382,7 @@ ./services/databases/pgmanage.nix ./services/databases/postgresql.nix ./services/databases/redis.nix + ./services/databases/surrealdb.nix ./services/databases/victoriametrics.nix ./services/desktops/accountsservice.nix ./services/desktops/bamf.nix @@ -718,6 +719,7 @@ ./services/monitoring/teamviewer.nix ./services/monitoring/telegraf.nix ./services/monitoring/thanos.nix + ./services/monitoring/tremor-rs.nix ./services/monitoring/tuptime.nix ./services/monitoring/unifi-poller.nix ./services/monitoring/ups.nix diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix index adbbf5d9ed4b4..1b69aac98863b 100644 --- a/nixos/modules/programs/steam.nix +++ b/nixos/modules/programs/steam.nix @@ -4,17 +4,31 @@ with lib; let cfg = config.programs.steam; - - steam = pkgs.steam.override { - extraLibraries = pkgs: with config.hardware.opengl; - if pkgs.hostPlatform.is64bit - then [ package ] ++ extraPackages - else [ package32 ] ++ extraPackages32; - }; in { options.programs.steam = { enable = mkEnableOption (lib.mdDoc "steam"); + package = mkOption { + type = types.package; + default = pkgs.steam.override { + extraLibraries = pkgs: with config.hardware.opengl; + if pkgs.hostPlatform.is64bit + then [ package ] ++ extraPackages + else [ package32 ] ++ extraPackages32; + }; + defaultText = literalExpression '' + pkgs.steam.override { + extraLibraries = pkgs: with config.hardware.opengl; + if pkgs.hostPlatform.is64bit + then [ package ] ++ extraPackages + else [ package32 ] ++ extraPackages32; + } + ''; + description = lib.mdDoc '' + steam package to use. + ''; + }; + remotePlay.openFirewall = mkOption { type = types.bool; default = false; @@ -44,7 +58,10 @@ in { hardware.steam-hardware.enable = true; - environment.systemPackages = [ steam steam.run ]; + environment.systemPackages = [ + cfg.package + cfg.package.run + ]; networking.firewall = lib.mkMerge [ (mkIf cfg.remotePlay.openFirewall { diff --git a/nixos/modules/services/databases/surrealdb.nix b/nixos/modules/services/databases/surrealdb.nix new file mode 100644 index 0000000000000..27269eb02f64e --- /dev/null +++ b/nixos/modules/services/databases/surrealdb.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + + cfg = config.services.surrealdb; +in { + + options = { + services.surrealdb = { + enable = mkEnableOption (lib.mdDoc "A scalable, distributed, collaborative, document-graph database, for the realtime web "); + + dbPath = mkOption { + type = types.str; + description = lib.mdDoc '' + The path that surrealdb will write data to. Use null for in-memory. + Can be one of "memory", "file://:path", "tikv://:addr". + ''; + default = "file:///var/lib/surrealdb/"; + example = "memory"; + }; + + host = mkOption { + type = types.str; + description = lib.mdDoc '' + The host that surrealdb will connect to. + ''; + default = "127.0.0.1"; + example = "127.0.0.1"; + }; + + port = mkOption { + type = types.port; + description = lib.mdDoc '' + The port that surrealdb will connect to. + ''; + default = 8000; + example = 8000; + }; + }; + }; + + config = mkIf cfg.enable { + + # Used to connect to the running service + environment.systemPackages = [ pkgs.surrealdb ] ; + + systemd.services.surrealdb = { + description = "A scalable, distributed, collaborative, document-graph database, for the realtime web "; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${pkgs.surrealdb}/bin/surreal start --bind ${cfg.host}:${toString cfg.port} ${optionalString (cfg.dbPath != null) "-- ${cfg.dbPath}"}"; + DynamicUser = true; + Restart = "on-failure"; + StateDirectory = "surrealdb"; + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectClock = true; + ProtectProc = "noaccess"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectHostname = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + RemoveIPC = true; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; + }; +} diff --git a/nixos/modules/services/monitoring/tremor-rs.nix b/nixos/modules/services/monitoring/tremor-rs.nix new file mode 100644 index 0000000000000..213e8a474868c --- /dev/null +++ b/nixos/modules/services/monitoring/tremor-rs.nix @@ -0,0 +1,129 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + + cfg = config.services.tremor-rs; + + loggerSettingsFormat = pkgs.formats.yaml { }; + loggerConfigFile = loggerSettingsFormat.generate "logger.yaml" cfg.loggerSettings; +in { + + options = { + services.tremor-rs = { + enable = lib.mkEnableOption (lib.mdDoc "Tremor event- or stream-processing system"); + + troyFileList = mkOption { + type = types.listOf types.path; + default = []; + description = lib.mdDoc "List of troy files to load."; + }; + + tremorLibDir = mkOption { + type = types.path; + default = ""; + description = lib.mdDoc "Directory where to find /lib containing tremor script files"; + }; + + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc "The host tremor should be listening on"; + }; + + port = mkOption { + type = types.port; + default = 9898; + description = lib.mdDoc "the port tremor should be listening on"; + }; + + loggerSettings = mkOption { + description = lib.mdDoc "Tremor logger configuration"; + default = {}; + type = loggerSettingsFormat.type; + + example = { + refresh_rate = "30 seconds"; + appenders.stdout.kind = "console"; + root = { + level = "warn"; + appenders = [ "stdout" ]; + }; + loggers = { + tremor_runtime = { + level = "debug"; + appenders = [ "stdout" ]; + additive = false; + }; + tremor = { + level = "debug"; + appenders = [ "stdout" ]; + additive = false; + }; + }; + }; + + defaultText = literalExpression '' + { + refresh_rate = "30 seconds"; + appenders.stdout.kind = "console"; + root = { + level = "warn"; + appenders = [ "stdout" ]; + }; + loggers = { + tremor_runtime = { + level = "debug"; + appenders = [ "stdout" ]; + additive = false; + }; + tremor = { + level = "debug"; + appenders = [ "stdout" ]; + additive = false; + }; + }; + } + ''; + + }; + }; + }; + + config = mkIf (cfg.enable) { + + environment.systemPackages = [ pkgs.tremor-rs ] ; + + systemd.services.tremor-rs = { + description = "Tremor event- or stream-processing system"; + wantedBy = [ "multi-user.target" ]; + requires = [ "network-online.target" ]; + after = [ "network-online.target" ]; + + environment.TREMOR_PATH = "${pkgs.tremor-rs}/lib:${cfg.tremorLibDir}"; + + serviceConfig = { + ExecStart = "${pkgs.tremor-rs}/bin/tremor --logger-config ${loggerConfigFile} server run ${concatStringsSep " " cfg.troyFileList} --api-host ${cfg.host}:${toString cfg.port}"; + DynamicUser = true; + Restart = "always"; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectClock = true; + ProtectProc = "noaccess"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectHostname = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + RemoveIPC = true; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; + }; +} diff --git a/nixos/modules/services/web-apps/alps.nix b/nixos/modules/services/web-apps/alps.nix index b73cb82925d9b..4681739af4ab0 100644 --- a/nixos/modules/services/web-apps/alps.nix +++ b/nixos/modules/services/web-apps/alps.nix @@ -70,6 +70,23 @@ in { ''; }; }; + + package = mkOption { + internal = true; + type = types.package; + default = pkgs.alps; + }; + + args = mkOption { + internal = true; + type = types.listOf types.str; + default = [ + "-addr" "${cfg.bindIP}:${toString cfg.port}" + "-theme" "${cfg.theme}" + "imaps://${cfg.imaps.host}:${toString cfg.imaps.port}" + "smpts://${cfg.smtps.host}:${toString cfg.smtps.port}" + ]; + }; }; config = mkIf cfg.enable { @@ -80,16 +97,33 @@ in { after = [ "network.target" "network-online.target" ]; serviceConfig = { - ExecStart = '' - ${pkgs.alps}/bin/alps \ - -addr ${cfg.bindIP}:${toString cfg.port} \ - -theme ${cfg.theme} \ - imaps://${cfg.imaps.host}:${toString cfg.imaps.port} \ - smpts://${cfg.smtps.host}:${toString cfg.smtps.port} - ''; - StateDirectory = "alps"; - WorkingDirectory = "/var/lib/alps"; + ExecStart = "${cfg.package}/bin/alps ${escapeShellArgs cfg.args}"; DynamicUser = true; + ## This is desirable but would restrict bindIP to 127.0.0.1 + #IPAddressAllow = "localhost"; + #IPAddressDeny = "any"; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateIPC = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service @resources" "~@privileged @obsolete" ]; }; }; }; diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix index 521cf778a36bf..d52190a28648e 100644 --- a/nixos/modules/services/web-apps/keycloak.nix +++ b/nixos/modules/services/web-apps/keycloak.nix @@ -482,6 +482,10 @@ in assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null); message = "A CA certificate must be specified (in 'services.keycloak.database.caCert') when PostgreSQL is used with SSL"; } + { + assertion = createLocalPostgreSQL -> config.services.postgresql.settings.standard_conforming_strings or true; + message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably"; + } ]; environment.systemPackages = [ keycloakBuild ]; @@ -544,7 +548,13 @@ in create_role="$(mktemp)" trap 'rm -f "$create_role"' EXIT + # Read the password from the credentials directory and + # escape any single quotes by adding additional single + # quotes after them, following the rules laid out here: + # https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")" + db_password="''${db_password//\'/\'\'}" + echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB" > "$create_role" psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || psql -tA --file="$create_role" psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || psql -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"' @@ -566,8 +576,16 @@ in script = '' set -o errexit -o pipefail -o nounset -o errtrace shopt -s inherit_errexit + + # Read the password from the credentials directory and + # escape any single quotes by adding additional single + # quotes after them, following the rules laid out here: + # https://dev.mysql.com/doc/refman/8.0/en/string-literals.html db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")" - ( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';" + db_password="''${db_password//\'/\'\'}" + + ( echo "SET sql_mode = 'NO_BACKSLASH_ESCAPES';" + echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';" echo "CREATE DATABASE IF NOT EXISTS keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;" echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';" ) | mysql -N @@ -632,12 +650,17 @@ in ${secretReplacements} + # Escape any backslashes in the db parameters, since + # they're otherwise unexpectedly read as escape + # sequences. + sed -i '/db-/ s|\\|\\\\|g' /run/keycloak/conf/keycloak.conf + '' + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) '' mkdir -p /run/keycloak/ssl cp $CREDENTIALS_DIRECTORY/ssl_{cert,key} /run/keycloak/ssl/ '' + '' export KEYCLOAK_ADMIN=admin - export KEYCLOAK_ADMIN_PASSWORD=${cfg.initialAdminPassword} + export KEYCLOAK_ADMIN_PASSWORD=${escapeShellArg cfg.initialAdminPassword} kc.sh start --optimized ''; }; diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index 994aa0e33cbfa..4596c160a9571 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -342,6 +342,14 @@ checkFS() { return 0 } +escapeFstab() { + local original="$1" + + # Replace space + local escaped="${original// /\\040}" + # Replace tab + echo "${escaped//$'\t'/\\011}" +} # Function for mounting a file system. mountFS() { @@ -569,7 +577,7 @@ while read -u 3 mountPoint; do continue fi - mountFS "$device" "$mountPoint" "$options" "$fsType" + mountFS "$device" "$(escapeFstab "$mountPoint")" "$(escapeFstab "$options")" "$fsType" done exec 3>&- diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index 399ea9eabe08d..7ab8f8dc676c7 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -167,7 +167,7 @@ let else throw "No device specified for mount point ‘${fs.mountPoint}’.") + " " + escape (rootPrefix + fs.mountPoint) + " " + fs.fsType - + " " + builtins.concatStringsSep "," (fs.options ++ (extraOpts fs)) + + " " + escape (builtins.concatStringsSep "," (fs.options ++ (extraOpts fs))) + " " + (optionalString (!excludeChecks) ("0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2"))) + "\n" diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index bd7b452735f1c..337b5192776f8 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -77,6 +77,7 @@ in rec { (onFullSupported "nixos.tests.i3wm") (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSimple") (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvolDefault") + (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvolEscape") (onSystems ["x86_64-linux"] "nixos.tests.installer.btrfsSubvols") (onSystems ["x86_64-linux"] "nixos.tests.installer.luksroot") (onSystems ["x86_64-linux"] "nixos.tests.installer.lvm") diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index bd0fe18c4f8c2..96330bd40f60e 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -74,6 +74,7 @@ in { agda = handleTest ./agda.nix {}; airsonic = handleTest ./airsonic.nix {}; allTerminfo = handleTest ./all-terminfo.nix {}; + alps = handleTest ./alps.nix {}; amazon-init-shell = handleTest ./amazon-init-shell.nix {}; apfs = handleTest ./apfs.nix {}; apparmor = handleTest ./apparmor.nix {}; @@ -251,8 +252,8 @@ in { haproxy = handleTest ./haproxy.nix {}; hardened = handleTest ./hardened.nix {}; healthchecks = handleTest ./web-apps/healthchecks.nix {}; - hbase1 = handleTest ./hbase.nix { package=pkgs.hbase1; }; hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; }; + hbase_2_4 = handleTest ./hbase.nix { package=pkgs.hbase_2_4; }; hbase3 = handleTest ./hbase.nix { package=pkgs.hbase3; }; hedgedoc = handleTest ./hedgedoc.nix {}; herbstluftwm = handleTest ./herbstluftwm.nix {}; @@ -490,6 +491,7 @@ in { pgadmin4-standalone = handleTest ./pgadmin4-standalone.nix {}; pgjwt = handleTest ./pgjwt.nix {}; pgmanage = handleTest ./pgmanage.nix {}; + phosh = handleTest ./phosh.nix {}; php = handleTest ./php {}; php80 = handleTest ./php { php = pkgs.php80; }; php81 = handleTest ./php { php = pkgs.php81; }; diff --git a/nixos/tests/alps.nix b/nixos/tests/alps.nix new file mode 100644 index 0000000000000..8d7814117df1e --- /dev/null +++ b/nixos/tests/alps.nix @@ -0,0 +1,104 @@ +let + certs = import ./common/acme/server/snakeoil-certs.nix; + domain = certs.domain; +in +import ./make-test-python.nix ({ pkgs, ... }: { + name = "alps"; + + nodes = { + server = { + imports = [ ./common/user-account.nix ]; + security.pki.certificateFiles = [ + certs.ca.cert + ]; + networking.extraHosts = '' + 127.0.0.1 ${domain} + ''; + networking.firewall.allowedTCPPorts = [ 25 465 993 ]; + services.postfix = { + enable = true; + enableSubmission = true; + enableSubmissions = true; + tlsTrustedAuthorities = "${certs.ca.cert}"; + sslCert = "${certs.${domain}.cert}"; + sslKey = "${certs.${domain}.key}"; + }; + services.dovecot2 = { + enable = true; + enableImap = true; + sslCACert = "${certs.ca.cert}"; + sslServerCert = "${certs.${domain}.cert}"; + sslServerKey = "${certs.${domain}.key}"; + }; + }; + + client = { nodes, config, ... }: { + security.pki.certificateFiles = [ + certs.ca.cert + ]; + networking.extraHosts = '' + ${nodes.server.config.networking.primaryIPAddress} ${domain} + ''; + services.alps = { + enable = true; + theme = "alps"; + imaps = { + host = domain; + port = 993; + }; + smtps = { + host = domain; + port = 465; + }; + }; + environment.systemPackages = [ + (pkgs.writers.writePython3Bin "test-alps-login" { } '' + from urllib.request import build_opener, HTTPCookieProcessor, Request + from urllib.parse import urlencode, urljoin + from http.cookiejar import CookieJar + + baseurl = "http://localhost:${toString config.services.alps.port}" + username = "alice" + password = "${nodes.server.config.users.users.alice.password}" + cookiejar = CookieJar() + cookieprocessor = HTTPCookieProcessor(cookiejar) + opener = build_opener(cookieprocessor) + + data = urlencode({"username": username, "password": password}).encode() + req = Request(urljoin(baseurl, "login"), data=data, method="POST") + with opener.open(req) as ret: + # Check that the alps_session cookie is set + print(cookiejar) + assert any(cookie.name == "alps_session" for cookie in cookiejar) + + req = Request(baseurl) + with opener.open(req) as ret: + # Check that the alps_session cookie is still there... + print(cookiejar) + assert any(cookie.name == "alps_session" for cookie in cookiejar) + # ...and that we have not been redirected back to the login page + print(ret.url) + assert ret.url == urljoin(baseurl, "mailbox/INBOX") + + req = Request(urljoin(baseurl, "logout")) + with opener.open(req) as ret: + # Check that the alps_session cookie is now gone + print(cookiejar) + assert all(cookie.name != "alps_session" for cookie in cookiejar) + '') + ]; + }; + }; + + testScript = '' + server.start() + server.wait_for_unit("postfix.service") + server.wait_for_unit("dovecot2.service") + server.wait_for_open_port(465) + server.wait_for_open_port(993) + + client.start() + client.wait_for_unit("alps.service") + client.succeed("test-alps-login") + ''; +}) diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix index d02387ee80e09..03f0ec8d746b8 100644 --- a/nixos/tests/installer-systemd-stage-1.nix +++ b/nixos/tests/installer-systemd-stage-1.nix @@ -8,9 +8,10 @@ # them when fixed. inherit (import ./installer.nix { inherit system config pkgs; systemdStage1 = true; }) # bcache - # btrfsSimple - # btrfsSubvolDefault - # btrfsSubvols + btrfsSimple + btrfsSubvolDefault + btrfsSubvolEscape + btrfsSubvols # encryptedFSWithKeyfile # grub1 # luksroot diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix index d9f64a781c57e..9b3c8a762991b 100644 --- a/nixos/tests/installer.nix +++ b/nixos/tests/installer.nix @@ -911,4 +911,25 @@ in { ) ''; }; + + # Test to see if we can deal with subvols that need to be escaped in fstab + btrfsSubvolEscape = makeInstallerTest "btrfsSubvolEscape" { + createPartitions = '' + machine.succeed( + "sgdisk -Z /dev/vda", + "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda", + "mkswap /dev/vda2 -L swap", + "swapon -L swap", + "mkfs.btrfs -L root /dev/vda3", + "btrfs device scan", + "mount LABEL=root /mnt", + "btrfs subvol create '/mnt/nixos in space'", + "btrfs subvol create /mnt/boot", + "umount /mnt", + "mount -o 'defaults,subvol=nixos in space' LABEL=root /mnt", + "mkdir /mnt/boot", + "mount -o defaults,subvol=boot LABEL=root /mnt/boot", + ) + ''; + }; } diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix index 6ce136330d438..228e57d1cdd6f 100644 --- a/nixos/tests/keycloak.nix +++ b/nixos/tests/keycloak.nix @@ -5,10 +5,13 @@ let certs = import ./common/acme/server/snakeoil-certs.nix; frontendUrl = "https://${certs.domain}"; - initialAdminPassword = "h4IhoJFnt2iQIR9"; keycloakTest = import ./make-test-python.nix ( { pkgs, databaseType, ... }: + let + initialAdminPassword = "h4Iho\"JFn't2>iQIR9"; + adminPasswordFile = pkgs.writeText "admin-password" "${initialAdminPassword}"; + in { name = "keycloak"; meta = with pkgs.lib.maintainers; { @@ -37,7 +40,7 @@ let type = databaseType; username = "bogus"; name = "also bogus"; - passwordFile = "${pkgs.writeText "dbPassword" "wzf6vOCbPp6cqTH"}"; + passwordFile = "${pkgs.writeText "dbPassword" ''wzf6\"vO"Cb\nP>p#6;c&o?eu=q'THE'''H''''E''}"; }; plugins = with config.services.keycloak.package.plugins; [ keycloak-discord @@ -111,7 +114,7 @@ let keycloak.succeed(""" curl -sSf -d 'client_id=admin-cli' \ -d 'username=admin' \ - -d 'password=${initialAdminPassword}' \ + -d "password=$(<${adminPasswordFile})" \ -d 'grant_type=password' \ '${frontendUrl}/realms/master/protocol/openid-connect/token' \ | jq -r '"Authorization: bearer " + .access_token' >admin_auth_header @@ -119,10 +122,10 @@ let # Register the metrics SPI keycloak.succeed( - "${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt", - "KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password '${initialAdminPassword}'", - "KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'", - "curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'" + """${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt""", + """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password "$(<${adminPasswordFile})" """, + """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'""", + """curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'""" ) # Publish the realm, including a test OIDC client and user diff --git a/nixos/tests/phosh.nix b/nixos/tests/phosh.nix new file mode 100644 index 0000000000000..6c6357f580968 --- /dev/null +++ b/nixos/tests/phosh.nix @@ -0,0 +1,65 @@ +import ./make-test-python.nix ({ pkgs, ...}: let + pin = "1234"; +in { + name = "phosh"; + meta = with pkgs.lib.maintainers; { + maintainers = [ zhaofengli ]; + }; + + nodes = { + phone = { config, pkgs, ... }: { + users.users.nixos = { + isNormalUser = true; + password = pin; + }; + + services.xserver.desktopManager.phosh = { + enable = true; + user = "nixos"; + group = "users"; + + phocConfig = { + outputs.Virtual-1 = { + scale = 2; + }; + }; + }; + + systemd.services.phosh = { + environment = { + # Accelerated graphics fail on phoc 0.20 (wlroots 0.15) + "WLR_RENDERER" = "pixman"; + }; + }; + + virtualisation.resolution = { x = 720; y = 1440; }; + virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci,xres=720,yres=1440" ]; + }; + }; + + enableOCR = true; + + testScript = '' + import time + + start_all() + phone.wait_for_unit("phosh.service") + + with subtest("Check that we can see the lock screen info page"): + # Saturday, January 1 + phone.succeed("timedatectl set-time '2022-01-01 07:00'") + + phone.wait_for_text("Saturday") + phone.screenshot("01lockinfo") + + with subtest("Check that we can unlock the screen"): + phone.send_chars("${pin}", delay=0.2) + time.sleep(1) + phone.screenshot("02unlock") + + phone.send_chars("\n") + + phone.wait_for_text("All Apps") + phone.screenshot("03launcher") + ''; +}) |