From 64a15676ac5b7cb8990d683f19ad78ac9a6bc4ef Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Tue, 9 May 2023 15:57:49 +0530 Subject: [PATCH 1/7] support authorized_keys for users --- modules/lib/write-text.nix | 8 +++++ modules/programs/ssh/default.nix | 61 ++++++++++++++++++++++++++++---- modules/system/etc.nix | 14 ++++++-- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/modules/lib/write-text.nix b/modules/lib/write-text.nix index ddf40769..2fe02aff 100644 --- a/modules/lib/write-text.nix +++ b/modules/lib/write-text.nix @@ -45,6 +45,14 @@ in ''; }; + copy = mkOption { + type = types.bool; + default = false; + description = '' + Whether this file should be copied instead of symlinking. + ''; + }; + knownSha256Hashes = mkOption { internal = true; type = types.listOf types.str; diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index f93890fb..2c0117c3 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -47,10 +47,56 @@ let hostNames = mkDefault [ name ]; }; }; + userOptions = { + + options.openssh.authorizedKeys = { + keys = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of verbatim OpenSSH public keys that should be added to the + user's authorized keys. The keys are added to a file that the SSH + daemon reads in addition to the the user's authorized_keys file. + You can combine the keys and + keyFiles options. + Warning: If you are using NixOps then don't use this + option since it will replace the key required for deployment via ssh. + ''; + }; + + keyFiles = mkOption { + type = types.listOf types.path; + default = []; + description = '' + A list of files each containing one OpenSSH public key that should be + added to the user's authorized keys. The contents of the files are + read at build time and added to a file that the SSH daemon reads in + addition to the the user's authorized_keys file. You can combine the + keyFiles and keys options. + ''; + }; + }; + + }; + authKeysFiles = let + mkAuthKeyFile = u: nameValuePair "ssh/authorized_keys.d/${u.name}" { + text = '' + ${concatStringsSep "\n" u.openssh.authorizedKeys.keys} + ${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles} + ''; + }; + usersWithKeys = attrValues (flip filterAttrs config.users.users (n: u: + length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0 + )); + in listToAttrs (map mkAuthKeyFile usersWithKeys); in { options = { + + users.users = mkOption { + type = with types; attrsOf (submodule userOptions); + }; programs.ssh.knownHosts = mkOption { default = {}; @@ -80,12 +126,13 @@ in (data.publicKey != null && data.publicKeyFile == null); message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; }); - - environment.etc."ssh/ssh_known_hosts".text = (flip (concatMapStringsSep "\n") knownHosts - (h: assert h.hostNames != []; - concatStringsSep "," h.hostNames + " " - + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) - )) + "\n"; - + + environment.etc = authKeysFiles // + { "ssh/ssh_known_hosts".text = (flip (concatMapStringsSep "\n") knownHosts + (h: assert h.hostNames != []; + concatStringsSep "," h.hostNames + " " + + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) + )) + "\n"; + }; }; } diff --git a/modules/system/etc.nix b/modules/system/etc.nix index cccb2b03..a27e49ec 100644 --- a/modules/system/etc.nix +++ b/modules/system/etc.nix @@ -12,6 +12,7 @@ let hasDir = path: length (splitString "/" path) > 1; etc = filter (f: f.enable) (attrValues config.environment.etc); + etcCopy = filter (f: f.copy) (attrValues config.environment.etc); etcDirs = filter (attr: hasDir attr.target) (attrValues config.environment.etc); in @@ -38,6 +39,7 @@ in cd $out/etc ${concatMapStringsSep "\n" (attr: "mkdir -p $(dirname '${attr.target}')") etc} ${concatMapStringsSep "\n" (attr: "ln -s '${attr.source}' '${attr.target}'") etc} + ${concatMapStringsSep "\n" (attr: "touch '${attr.target}'.copy") etcCopy} ''; system.activationScripts.etc.text = '' @@ -63,7 +65,11 @@ in for h in ''${etcSha256Hashes["$l"]}; do if [ "$o" = "$h" ]; then mv "$l" "$l.orig" - ln -s "$f" "$l" + if [ -e "$f".copy ]; then + cp "$f" "$l" + else + ln -s "$f" "$l" + fi break else h= @@ -77,7 +83,11 @@ in fi fi else - ln -s "$f" "$l" + if [ -e "$f".copy ]; then + cp "$f" "$l" + else + ln -s "$f" "$l" + fi fi done From ecb5840f6bc2cdb453bbc0dbbd8f63ec441808d6 Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Wed, 10 May 2023 19:35:23 +0530 Subject: [PATCH 2/7] enable copy --- modules/programs/ssh/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index 2c0117c3..7797e4d3 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -80,6 +80,7 @@ let }; authKeysFiles = let mkAuthKeyFile = u: nameValuePair "ssh/authorized_keys.d/${u.name}" { + copy = true; text = '' ${concatStringsSep "\n" u.openssh.authorizedKeys.keys} ${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles} From ccaa942888f53404b56d979cb3a0a5c9f18a1faa Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Wed, 10 May 2023 20:03:21 +0530 Subject: [PATCH 3/7] don't check knownSha256 for authorized_keys files --- modules/system/etc.nix | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/modules/system/etc.nix b/modules/system/etc.nix index a27e49ec..4b45e3a3 100644 --- a/modules/system/etc.nix +++ b/modules/system/etc.nix @@ -57,6 +57,10 @@ in if [ ! -e "$d" ]; then mkdir -p "$d" fi + if [ -e "$f".copy ]; then + cp "$f" "$l" + continue + fi if [ -e "$l" ]; then if [ "$(readlink "$l")" != "$f" ]; then if ! grep -q /etc/static "$l"; then @@ -65,11 +69,7 @@ in for h in ''${etcSha256Hashes["$l"]}; do if [ "$o" = "$h" ]; then mv "$l" "$l.orig" - if [ -e "$f".copy ]; then - cp "$f" "$l" - else - ln -s "$f" "$l" - fi + ln -s "$f" "$l" break else h= @@ -83,11 +83,7 @@ in fi fi else - if [ -e "$f".copy ]; then - cp "$f" "$l" - else - ln -s "$f" "$l" - fi + ln -s "$f" "$l" fi done From ab2e16159f5a04fd962f3d7de8dc4901d048db17 Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Wed, 10 May 2023 21:16:52 +0530 Subject: [PATCH 4/7] authkeys path in sshd_config --- modules/programs/ssh/default.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index 7797e4d3..b8baec6b 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -90,6 +90,13 @@ let length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0 )); in listToAttrs (map mkAuthKeyFile usersWithKeys); + authKeysConfiguration = + { + "ssh/sshd_config.d/101-authorized-keys.conf" = { + copy = true; + text = "AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u"; + }; + }; in { @@ -128,7 +135,7 @@ in message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; }); - environment.etc = authKeysFiles // + environment.etc = authKeysFiles // authKeysConfiguration // { "ssh/ssh_known_hosts".text = (flip (concatMapStringsSep "\n") knownHosts (h: assert h.hostNames != []; concatStringsSep "," h.hostNames + " " From 4094dbccde0d00fd062d365b8c79526711a6bc95 Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Wed, 10 May 2023 21:30:35 +0530 Subject: [PATCH 5/7] newline eof for authorized-keys conf --- modules/programs/ssh/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index b8baec6b..2d008df5 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -94,7 +94,7 @@ let { "ssh/sshd_config.d/101-authorized-keys.conf" = { copy = true; - text = "AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u"; + text = "AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u\n"; }; }; in From f781cb0ac5ea2daa5183b8df8f4de4ef0747f440 Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Wed, 10 May 2023 21:39:27 +0530 Subject: [PATCH 6/7] give credits --- modules/programs/ssh/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh/default.nix index 2d008df5..f1dde9ae 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh/default.nix @@ -47,6 +47,7 @@ let hostNames = mkDefault [ name ]; }; }; + # Taken from: https://github.com/NixOS/nixpkgs/blob/f4aa6afa5f934ece2d1eb3157e392d056be01617/nixos/modules/services/networking/ssh/sshd.nix#L46-L93 userOptions = { options.openssh.authorizedKeys = { From 3d22883cdb4226306be7583f5513b3ca23a72e24 Mon Sep 17 00:00:00 2001 From: sbh69840 Date: Sun, 14 May 2023 13:20:13 +0530 Subject: [PATCH 7/7] add tests --- tests/programs-ssh.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/programs-ssh.nix b/tests/programs-ssh.nix index 71f285c0..2928cfb6 100644 --- a/tests/programs-ssh.nix +++ b/tests/programs-ssh.nix @@ -6,9 +6,15 @@ publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="; }; }; + users.users.foo.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAA..." ]; test = '' echo >&2 "checking for github.com in /etc/ssh/ssh_known_hosts" grep 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' ${config.out}/etc/ssh/ssh_known_hosts + + echo >&2 "checking for authorized keys for foo in /etc/ssh/authorized_keys.d/foo" + grep 'ssh-ed25519 AAAA...' ${config.out}/etc/ssh/authorized_keys.d/foo + echo >&2 "checking for authorized keys' path in /etc/ssh/sshd_config.d/101-authorized-keys.conf" + grep 'AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u' ${config.out}/etc/ssh/sshd_config.d/101-authorized-keys.conf ''; }