{ inputs, ... }: let userName = "john"; sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ="; in { flake.modules.nixos.ssh = { config, pkgs, lib, ... }: let cfg = config.ssh; configDir = "/etc/ssh"; in { options.ssh = { hostKey = lib.mkOption { description = "String path to the host private key file"; type = lib.types.str; default = "ssh_host_ed25519_key"; }; certificates = { enable = lib.mkEnableOption "Enable SSH host certificates"; userCA = lib.mkOption { description = "Content for the SSH user CA file (public key)"; type = lib.types.path; default = ../hosts/janus/ssh_user_ca.pub; }; userCAFile = lib.mkOption { description = "String path to the SSh user CA"; type = lib.types.str; default = "ssh_user_ca.pub"; }; }; }; config = { services.openssh = { enable = true; # require public key authentication for better security settings = lib.mkMerge [ { PasswordAuthentication = false; KbdInteractiveAuthentication = false; HostKey = "${configDir}/${cfg.hostKey}"; } (lib.mkIf cfg.certificates.enable { TrustedUserCAKeys = "${configDir}/${cfg.certificates.userCAFile}"; HostCertificate = "${configDir}/${cfg.hostKey}-cert.pub"; }) ]; }; environment.etc."ssh/${cfg.certificates.userCAFile}" = 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 = { config, pkgs, lib, ... }: let cfg = config.ssh; configDir = "${config.home.homeDirectory}/.ssh"; identityFile = cfg.identityFile; publicKeyFile = "${identityFile}.pub"; certificateFile = "${identityFile}-cert.pub"; in { options.ssh = with lib; { identityFile = mkOption { # Intentionally not using a path type here because that will end up with the private key getting copied into the store type = types.str; default = "${config.home.homeDirectory}/.ssh/id_ed25519"; description = "Path to the SSH identity file."; }; certificates = { enable = mkEnableOption "Enable SSH client certificates"; }; knownHostsFile = mkOption { type = types.str; default = "${configDir}/known_hosts"; }; knownHosts = mkOption { description = ""; type = types.listOf types.str; default = [ ]; }; matchSets = { appdaemon = mkEnableOption "Enable AppDaemon SSH targets"; certs = mkEnableOption "Enable Janus and Soteria SSH targets"; homelab = mkEnableOption "Enable various Homelab targets"; dev = 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 provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path; in { home.file.".ssh/known_hosts" = { text = lib.concatStringsSep "\n" ( cfg.knownHosts ++ lib.optionals cfg.certificates.enable [ "@cert-authority 192.168.1.* ${sshHostCAPubKey}" "@cert-authority *.john-stream.com ${sshHostCAPubKey}" ] ); }; programs.ssh = { enable = true; enableDefaultConfig = false; extraConfig = '' SetEnv TERM="xterm-256color" ''; matchBlocks = lib.mkMerge [ { "john-pc-ubuntu" = { hostname = "192.168.1.85"; }; "*" = lib.mkMerge [ { user = "john"; compression = false; serverAliveInterval = 0; serverAliveCountMax = 3; identitiesOnly = true; inherit identityFile; hashKnownHosts = false; userKnownHostsFile = cfg.knownHostsFile; addKeysToAgent = "yes"; forwardAgent = false; } (lib.mkIf cfg.certificates.enable { inherit certificateFile; }) ]; } (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"; extraOptions = { RequestTTY = "force"; RemoteCommand = "~/.nix-profile/bin/jsl-zsh"; }; }; "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"; }; }) ]; }; }; }; }