{inputs, ... }: let userName = "john"; sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ="; in { flake.modules.nixos.ssh = { pkgs, config, lib, ... }: let cfg = config.ssh; userCAPath = "ssh/ssh_user_ca.pub"; hostKeyFile = "ssh/ssh_host_ed25519_key"; in { options.ssh = { certificates = { enable = lib.mkEnableOption "Enable SSH host certificates"; userCA = lib.mkOption { type = lib.types.path; default = ../../keys/ssh_user_ca.pub; }; }; }; config = { services.openssh = { enable = true; # require public key authentication for better security settings = lib.mkMerge [ { PasswordAuthentication = false; KbdInteractiveAuthentication = false; HostKey = "/etc/${hostKeyFile}"; } (lib.mkIf cfg.certificates.enable { TrustedUserCAKeys = "/etc/${userCAPath}"; HostCertificate = "/etc/${hostKeyFile}-cert.pub"; }) ]; }; environment.etc."${userCAPath}" = lib.mkIf cfg.certificates.enable { source = cfg.certificates.userCA; }; programs.ssh.knownHosts = lib.mkIf cfg.certificates.enable { "192.168.1.*" = { certAuthority = true; publicKey = sshHostCAPubKey; }; "*.john-stream.com" = { certAuthority = true; publicKey = sshHostCAPubKey; }; }; }; }; flake.modules.homeManager.ssh = { pkgs, config, lib, ... }: { options.ssh = { IdentityFile = lib.mkOption { # Intentionally not using a path type here because that will end up with the private key getting copied into the store type = lib.types.str; default = "${config.home.homeDirectory}/.ssh/id_ed25519"; description = "Path to the SSH identity file."; }; certificates = { enable = lib.mkEnableOption "Enable SSH user certificates"; # sshCertProvisioner = lib.mkOption { # type = lib.types.str; # default = "admin"; # }; }; knownHostsFile = lib.mkOption { type = lib.types.str; default = "${config.home.homeDirectory}/.ssh/known_hosts"; }; matchSets = { appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets"; certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets"; homelab = lib.mkEnableOption "Enable various Homelab targets"; dev = lib.mkEnableOption "Enable development targets"; }; }; # All this stuff has to be wrapped in a config attribute because of the presence of the options here? config = let cfg = config.ssh; identityFile = cfg.IdentityFile; publicKeyFile = "${identityFile}.pub"; certificateFile = "${identityFile}-cert.pub"; provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path; in { home.file.".ssh/known_hosts" = { text = lib.concatStringsSep "\n" ( [ "fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh" ] ++ (lib.optional cfg.certificates.enable "@cert-authority 192.168.1.* ${sshHostCAPubKey}") ++ (lib.optional cfg.certificates.enable "@cert-authority *.john-stream.com ${sshHostCAPubKey}") ); }; programs.ssh = { enable = true; enableDefaultConfig = false; extraConfig = '' SetEnv TERM="xterm-256color" IdentityAgent ~/.1password/agent.sock ''; matchBlocks = lib.mkMerge [ { "*" = { user = "john"; compression = false; serverAliveInterval = 0; serverAliveCountMax = 3; identitiesOnly = true; inherit identityFile certificateFile; hashKnownHosts = false; userKnownHostsFile = cfg.knownHostsFile; addKeysToAgent = "yes"; forwardAgent = false; }; } (lib.mkIf cfg.matchSets.appdaemon { "appdaemon" = { hostname = "192.168.1.242"; user = "appdaemon"; }; "ad-nix" = { hostname = "192.168.1.201"; user = "appdaemon"; }; }) (lib.mkIf cfg.matchSets.certs { "janus" = { hostname = "janus.john-stream.com"; user = "root"; }; "soteria" = { hostname = "soteria.john-stream.com"; user = "john"; }; }) (lib.mkIf cfg.matchSets.homelab { "docs" = { hostname = "192.168.1.110"; user = "root"; }; "gitea" = { hostname = "192.168.1.104"; user = "john"; }; "hermes" = { hostname = "192.168.1.150"; user = "root"; }; "panoptes" = { hostname = "192.168.1.107"; user = "panoptes"; }; }) (lib.mkIf cfg.matchSets.dev { "test-nix" = { hostname = "fded:fb16:653e:25da:be24:11ff:fea0:753f"; user = "john"; identityFile = identityFile; }; }) ]; }; }; }; }