diff --git a/modules/module-list.nix b/modules/module-list.nix index ff57502..e31cf24 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -113,7 +113,7 @@ ./programs/man.nix ./programs/info ./programs/nix-index - ./programs/ssh + ./programs/ssh.nix ./programs/tmux.nix ./programs/vim.nix ./programs/zsh diff --git a/modules/programs/ssh/default.nix b/modules/programs/ssh.nix similarity index 58% rename from modules/programs/ssh/default.nix rename to modules/programs/ssh.nix index 1e87732..0a7233e 100644 --- a/modules/programs/ssh/default.nix +++ b/modules/programs/ssh.nix @@ -1,14 +1,12 @@ { config, lib, ... }: -with lib; - let cfg = config.programs.ssh; - knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts); + knownHosts = builtins.attrValues cfg.knownHosts; host = - { name, ... }: + { name, config, ... }: { options = { certAuthority = lib.mkOption { @@ -19,17 +17,30 @@ let individual host's key. ''; }; - hostNames = mkOption { - type = types.listOf types.str; - default = []; + hostNames = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ name ] ++ config.extraHostNames; description = '' - A list of host names and/or IP numbers used for accessing - the host's ssh service. + The set of system-wide known SSH hosts. To make simple setups more + convenient the name of an attribute in this set is used as a host name + for the entry. This behaviour can be disabled by setting + `hostNames` explicitly. You can use + `extraHostNames` to add additional host names without + disabling this default. ''; }; - publicKey = mkOption { + extraHostNames = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = '' + A list of additional host names and/or IP numbers used for + accessing the host's ssh service. This list is ignored if + `hostNames` is set explicitly. + ''; + }; + publicKey = lib.mkOption { default = null; - type = types.nullOr types.str; + type = lib.types.nullOr lib.types.str; example = "ecdsa-sha2-nistp521 AAAAE2VjZHN...UEPg=="; description = '' The public key data for the host. You can fetch a public key @@ -38,9 +49,9 @@ let the key type and the key itself. ''; }; - publicKeyFile = mkOption { + publicKeyFile = lib.mkOption { default = null; - type = types.nullOr types.path; + type = lib.types.nullOr lib.types.path; description = '' The path to the public key file for the host. The public key file is read at build time and saved in the Nix store. @@ -51,16 +62,13 @@ let ''; }; }; - config = { - 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; + keys = lib.mkOption { + type = lib.types.listOf lib.types.str; default = []; description = '' A list of verbatim OpenSSH public keys that should be added to the @@ -73,8 +81,8 @@ let ''; }; - keyFiles = mkOption { - type = types.listOf types.path; + keyFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; default = []; description = '' A list of files each containing one OpenSSH public key that should be @@ -89,29 +97,29 @@ let }; authKeysFiles = let - mkAuthKeyFile = u: nameValuePair "ssh/nix_authorized_keys.d/${u.name}" { + mkAuthKeyFile = u: lib.nameValuePair "ssh/nix_authorized_keys.d/${u.name}" { text = '' - ${concatStringsSep "\n" u.openssh.authorizedKeys.keys} - ${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles} + ${builtins.concatStringsSep "\n" u.openssh.authorizedKeys.keys} + ${lib.concatMapStrings (f: builtins.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 + usersWithKeys = builtins.attrValues (lib.flip lib.filterAttrs config.users.users (n: u: + lib.length u.openssh.authorizedKeys.keys != 0 || lib.length u.openssh.authorizedKeys.keyFiles != 0 )); - in listToAttrs (map mkAuthKeyFile usersWithKeys); + in lib.listToAttrs (map mkAuthKeyFile usersWithKeys); oldAuthorizedKeysHash = "5a5dc1e20e8abc162ad1cc0259bfd1dbb77981013d87625f97d9bd215175fc0a"; in { imports = [ - (mkRemovedOptionModule [ "services" "openssh" "authorizedKeysFiles" ] "No `nix-darwin` equivalent to this NixOS option.") + (lib.mkRemovedOptionModule [ "services" "openssh" "authorizedKeysFiles" ] "No `nix-darwin` equivalent to this NixOS option.") ]; options = { - users.users = mkOption { - type = with types; attrsOf (submodule userOptions); + users.users = lib.mkOption { + type = with lib.types; attrsOf (submodule userOptions); }; programs.ssh.extraConfig = lib.mkOption { @@ -123,41 +131,47 @@ in ''; }; - programs.ssh.knownHosts = mkOption { + programs.ssh.knownHosts = lib.mkOption { default = {}; - type = types.attrsOf (types.submodule host); + type = lib.types.attrsOf (lib.types.submodule host); description = '' - The set of system-wide known SSH hosts. + The set of system-wide known SSH hosts. To make simple setups more + convenient the name of an attribute in this set is used as a host name + for the entry. This behaviour can be disabled by setting + `hostNames` explicitly. You can use + `extraHostNames` to add additional host names without + disabling this default. ''; - example = literalExpression '' - [ - { - hostNames = [ "myhost" "myhost.mydomain.com" "10.10.1.4" ]; + example = lib.literalExpression '' + { + myhost = { + extraHostNames = [ "myhost.mydomain.com" "10.10.1.4" ]; publicKeyFile = ./pubkeys/myhost_ssh_host_dsa_key.pub; - } - { - hostNames = [ "myhost2" ]; + }; + "myhost2.net".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIRuJ8p1Fi+m6WkHV0KWnRfpM1WxoW8XAS+XvsSKsTK"; + "myhost2.net/dsa" = { + hostNames = [ "myhost2.net" ]; publicKeyFile = ./pubkeys/myhost2_ssh_host_dsa_key.pub; - } - ] + }; + } ''; }; }; config = { - assertions = flip mapAttrsToList cfg.knownHosts (name: data: { + assertions = lib.flip lib.mapAttrsToList cfg.knownHosts (name: data: { assertion = (data.publicKey == null && data.publicKeyFile != null) || (data.publicKey != null && data.publicKeyFile == null); message = "knownHost ${name} must contain either a publicKey or publicKeyFile"; }); environment.etc = authKeysFiles // - { "ssh/ssh_known_hosts" = mkIf (builtins.length knownHosts > 0) { - text = (flip (concatMapStringsSep "\n") knownHosts + { "ssh/ssh_known_hosts" = lib.mkIf (builtins.length knownHosts > 0) { + text = (lib.flip (lib.concatMapStringsSep "\n") knownHosts (h: assert h.hostNames != []; - lib.optionalString h.certAuthority "@cert-authority " + concatStringsSep "," h.hostNames + " " - + (if h.publicKey != null then h.publicKey else readFile h.publicKeyFile) + lib.optionalString h.certAuthority "@cert-authority " + builtins.concatStringsSep "," h.hostNames + " " + + (if h.publicKey != null then h.publicKey else builtins.readFile h.publicKeyFile) )) + "\n"; }; "ssh/ssh_config.d/100-nix-darwin.conf".text = config.programs.ssh.extraConfig;