From df590070b007b2cd2f64647b2780c903506aa21f Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Tue, 18 Jan 2022 21:56:14 +0100 Subject: [PATCH 1/4] types.singleLineStr: strings that don't contain '\n' Add a new type, inheriting 'types.str' but checking whether the value doesn't contain any newline characters. The motivation comes from a problem with the 'users.users.${u}.openssh.authorizedKeys' option. It is easy to unintentionally insert a newline character at the end of a string, or even in the middle, for example: restricted_ssh_keys = command: keys: let prefix = '' command="${command}",no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding ''; in map (key: "${prefix} ${key}") keys; The 'prefix' string ends with a newline, which ends up in the middle of a key entry after a few manipulations. This is problematic because the key file is built by concatenating all the keys with 'concatStringsSep "\n"', with result in two entries for the faulty key: '' command="...",options... MY_KEY '' This is hard to debug and might be dangerous. This is now caught at build time. --- lib/types.nix | 7 +++++++ nixos/modules/services/networking/ssh/sshd.nix | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/types.nix b/lib/types.nix index 244cbb6b5354..0e702fb2f2ed 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -300,6 +300,13 @@ rec { inherit (str) merge; }; + singleLineStr = mkOptionType { + name = "singleLineStr"; + description = "string that doesn't contain '\\n'"; + check = x: str.check x && !(lib.hasInfix "\n" x); + inherit (str) merge; + }; + strMatching = pattern: mkOptionType { name = "strMatching ${escapeNixString pattern}"; description = "string matching the pattern ${pattern}"; diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix index 004b4f99670f..52a1982b3f0a 100644 --- a/nixos/modules/services/networking/ssh/sshd.nix +++ b/nixos/modules/services/networking/ssh/sshd.nix @@ -30,7 +30,7 @@ let options.openssh.authorizedKeys = { keys = mkOption { - type = types.listOf types.str; + type = types.listOf types.singleLineStr; default = []; description = '' A list of verbatim OpenSSH public keys that should be added to the From f25a13212be9bd716db9420a59cb74ecab02937a Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Tue, 18 Jan 2022 23:56:50 +0100 Subject: [PATCH 2/4] types.singleLineStr: Disallow \r --- lib/types.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 0e702fb2f2ed..18e95caaee84 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -302,9 +302,8 @@ rec { singleLineStr = mkOptionType { name = "singleLineStr"; - description = "string that doesn't contain '\\n'"; - check = x: str.check x && !(lib.hasInfix "\n" x); - inherit (str) merge; + description = "string that doesn't contain [\\n\\r]"; + inherit (strMatching "[^\n\r]*") check merge; }; strMatching = pattern: mkOptionType { From 4baf8548fbf9957b53418e0aad06bd6a798c283e Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Thu, 20 Jan 2022 18:49:54 +0100 Subject: [PATCH 3/4] types.singleLineStr: Allow and trim trailing \n Allow a \n character at the end of the string and remove it during the merge function. An option of this type will resolve to the value "foo" whether it is set to "foo" or "foo\n". This is useful when using 'builtins.readFile' or ''-strings, which might add an unintended newline (for example, bash trim the final newline from a subshell). --- lib/types.nix | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 18e95caaee84..7acfa60f161f 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -300,11 +300,18 @@ rec { inherit (str) merge; }; - singleLineStr = mkOptionType { - name = "singleLineStr"; - description = "string that doesn't contain [\\n\\r]"; - inherit (strMatching "[^\n\r]*") check merge; - }; + # Allow a newline character at the end and trim it in the merge function. + singleLineStr = + let + inherit (strMatching "[^\n\r]*\n?") check merge; + in + mkOptionType { + name = "singleLineStr"; + description = "string that doesn't contain [\\n\\r]"; + inherit check; + merge = loc: defs: + lib.removeSuffix "\n" (merge loc defs); + }; strMatching = pattern: mkOptionType { name = "strMatching ${escapeNixString pattern}"; From 1394bfc32a7f2398815b000ad11812f2da7ea2d5 Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Thu, 20 Jan 2022 22:10:33 +0100 Subject: [PATCH 4/4] types.singleLineStr: Improve description Co-authored-by: pennae <82953136+pennae@users.noreply.github.com> --- lib/types.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types.nix b/lib/types.nix index 7acfa60f161f..cc3ac5fdf6fb 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -307,7 +307,7 @@ rec { in mkOptionType { name = "singleLineStr"; - description = "string that doesn't contain [\\n\\r]"; + description = "(optionally newline-terminated) single-line string"; inherit check; merge = loc: defs: lib.removeSuffix "\n" (merge loc defs);