diff --git a/modules/lib/write-text.nix b/modules/lib/write-text.nix
index ddf4076..2fe02af 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 f93890f..f1dde9a 100644
--- a/modules/programs/ssh/default.nix
+++ b/modules/programs/ssh/default.nix
@@ -47,10 +47,65 @@ 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 = {
+ 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}" {
+ copy = true;
+ 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);
+ authKeysConfiguration =
+ {
+ "ssh/sshd_config.d/101-authorized-keys.conf" = {
+ copy = true;
+ text = "AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u\n";
+ };
+ };
in
{
options = {
+
+ users.users = mkOption {
+ type = with types; attrsOf (submodule userOptions);
+ };
programs.ssh.knownHosts = mkOption {
default = {};
@@ -80,12 +135,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 // authKeysConfiguration //
+ { "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 cccb2b0..4b45e3a 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 = ''
@@ -55,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
diff --git a/tests/programs-ssh.nix b/tests/programs-ssh.nix
index 71f285c..2928cfb 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
'';
}