diff --git a/clanModules/sshd/README.md b/clanModules/sshd/README.md index 34e6ab287..b72182680 100644 --- a/clanModules/sshd/README.md +++ b/clanModules/sshd/README.md @@ -1,3 +1,13 @@ --- -description = "Enables secure remote access to the machine over ssh" +description = "Enables secure remote access to the machine over ssh." +categories = ["System"] +features = [ "inventory" ] --- + +This module will setup the opensshd service. +It will generate a host key for each machine + + +## Roles + +### diff --git a/clanModules/sshd/default.nix b/clanModules/sshd/default.nix index d68fce4fe..ab24e1e49 100644 --- a/clanModules/sshd/default.nix +++ b/clanModules/sshd/default.nix @@ -1,25 +1,6 @@ -{ config, pkgs, ... }: +# Dont import this file +# It is only here for backwards compatibility. +# Dont author new modules with this file. { - services.openssh.enable = true; - services.openssh.settings.PasswordAuthentication = false; - - services.openssh.hostKeys = [ - { - path = config.clan.core.facts.services.openssh.secret."ssh.id_ed25519".path; - type = "ed25519"; - } - ]; - - clan.core.facts.services.openssh = { - secret."ssh.id_ed25519" = { }; - public."ssh.id_ed25519.pub" = { }; - generator.path = [ - pkgs.coreutils - pkgs.openssh - ]; - generator.script = '' - ssh-keygen -t ed25519 -N "" -f $secrets/ssh.id_ed25519 - mv $secrets/ssh.id_ed25519.pub $facts/ssh.id_ed25519.pub - ''; - }; + imports = [ ./roles/server.nix ]; } diff --git a/clanModules/sshd/roles/client.nix b/clanModules/sshd/roles/client.nix new file mode 100644 index 000000000..777363f14 --- /dev/null +++ b/clanModules/sshd/roles/client.nix @@ -0,0 +1,11 @@ +{ ... }: +{ + imports = [ + ../shared.nix + ]; + programs.ssh.knownHosts.ssh-ca = lib.mkIf (config.clan.sshd.certificate.searchDomains != [ ]) { + certAuthority = true; + extraHostNames = builtins.map (domain: "*.${domain}") config.clan.sshd.certificate.searchDomains; + publicKey = config.clan.core.vars.generators.openssh-ca.files."id_ed25519.pub".value; + }; +} diff --git a/clanModules/sshd/roles/server.nix b/clanModules/sshd/roles/server.nix new file mode 100644 index 000000000..6edf9c43c --- /dev/null +++ b/clanModules/sshd/roles/server.nix @@ -0,0 +1,68 @@ +{ + config, + pkgs, + lib, + ... +}: +let + stringSet = list: builtins.attrNames (builtins.groupBy lib.id list); + + signArgs = builtins.concatStringsSep " " ( + builtins.map (domain: "-n ${lib.escapeShellArg "${config.clan.core.machineName}.${domain}"}") ( + stringSet config.clan.sshd.certificate.searchDomains + ) + ); + cfg = config.clan.sshd; +in +{ + imports = [ ../shared.nix ]; + config = { + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + + settings.HostCertificate = lib.mkIf ( + cfg.certificate.searchDomains != [ ] + ) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path; + + hostKeys = [ + { + path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path; + type = "ed25519"; + } + ]; + }; + clan.core.vars.generators.openssh = { + files."ssh.id_ed25519" = { }; + files."ssh.id_ed25519.pub".secret = false; + migrateFact = "openssh"; + runtimeInputs = [ + pkgs.coreutils + pkgs.openssh + ]; + script = '' + ssh-keygen -t ed25519 -N "" -f $out/ssh.id_ed25519 + ''; + }; + + clan.core.vars.generators.openssh-cert = lib.mkIf (cfg.certificate.searchDomains != [ ]) { + files."ssh.id_ed25519-cert.pub".secret = false; + dependencies = [ + "openssh" + "openssh-ca" + ]; + runtimeInputs = [ + pkgs.openssh + pkgs.jq + ]; + script = '' + ssh-keygen \ + -s $in/openssh-ca/id_ed25519 \ + -I ${config.clan.core.machineName} \ + ${builtins.toString signArgs} \ + $in/openssh/ssh.id_ed25519.pub + mv $in/openssh/ssh.id_ed25519-cert.pub $out/ssh.id_ed25519-cert.pub + ''; + }; + }; +} diff --git a/clanModules/sshd/shared.nix b/clanModules/sshd/shared.nix new file mode 100644 index 000000000..768a54988 --- /dev/null +++ b/clanModules/sshd/shared.nix @@ -0,0 +1,43 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + options = { + clan.sshd.certificate = { + # TODO: allow per-server domains that we than collect in the inventory + #domains = lib.mkOption { + # type = lib.types.listOf lib.types.str; + # default = [ ]; + # example = [ "git.mydomain.com" ]; + # description = "List of domains to include in the certificate. This option will not prepend the machine name in front of each domain."; + #}; + searchDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "mydomain.com" ]; + description = "List of domains to include in the certificate. This option will prepend the machine name in front of each domain before adding it to the certificate."; + }; + }; + }; + config = { + clan.core.vars.generators.openssh-ca = + lib.mkIf (config.clan.sshd.certificate.searchDomains != [ ]) + { + share = true; + files.id_ed25519.deploy = false; + files."id_ed25519.pub" = { + deploy = false; + secret = false; + }; + runtimeInputs = [ + pkgs.openssh + ]; + script = '' + ssh-keygen -t ed25519 -N "" -f $out/id_ed25519 + ''; + }; + }; +}