diff --git a/flake.nix b/flake.nix index 7c2a1e3..5310163 100644 --- a/flake.nix +++ b/flake.nix @@ -50,6 +50,7 @@ nvim, }@inputs: let + inherit (self) outputs; lib = inputs.nixpkgs.lib; utils = import ./utils { inherit lib; }; hostDirNames = utils.dirNames ./hosts; @@ -67,14 +68,18 @@ host: nixpkgs.lib.nixosSystem { modules = [ ./hosts/${host} ]; - specialArgs = { inherit inputs; }; + specialArgs = { + inherit inputs outputs; + }; } ); homeConfigurations = { work = home-manager.lib.homeManagerConfiguration { inherit pkgs; modules = [ ./home/hosts/work ]; - extraSpecialArgs = { inherit inputs; }; + extraSpecialArgs = { + inherit inputs outputs; + }; }; }; }; diff --git a/home/hosts/andromache/default.nix b/home/hosts/andromache/default.nix index 5c73e5e..dd8e811 100644 --- a/home/hosts/andromache/default.nix +++ b/home/hosts/andromache/default.nix @@ -14,6 +14,7 @@ in ../../modules/desktop/niri ../../modules/git.nix ../../modules/k9s.nix + ../../modules/ssh.nix ../../modules/taskwarrior.nix ../../modules/keepassxc.nix ../../modules/anki.nix diff --git a/home/hosts/astyanax/default.nix b/home/hosts/astyanax/default.nix index 280590b..549e503 100644 --- a/home/hosts/astyanax/default.nix +++ b/home/hosts/astyanax/default.nix @@ -14,6 +14,7 @@ in ../../modules/desktop/niri ../../modules/git.nix ../../modules/k9s.nix + ../../modules/ssh.nix ../../modules/taskwarrior.nix ../../modules/keepassxc.nix ../../modules/browser diff --git a/home/modules/ssh.nix b/home/modules/ssh.nix new file mode 100644 index 0000000..13eefba --- /dev/null +++ b/home/modules/ssh.nix @@ -0,0 +1,25 @@ +{ + outputs, + lib, + ... +}: +let + nixosConfigs = builtins.attrNames outputs.nixosConfigurations; + homeConfigs = map (n: lib.last (lib.splitString "@" n)) ( + builtins.attrNames outputs.homeConfigurations + ); + allHosts = lib.unique (homeConfigs ++ nixosConfigs); + hostsWithKeys = lib.filter ( + hostname: builtins.pathExists ../../hosts/${hostname}/ssh_host.pub + ) allHosts; +in +{ + programs.ssh = { + enable = true; + enableDefaultConfig = false; + + matchBlocks = lib.genAttrs hostsWithKeys (hostname: { + host = hostname; + }); + }; +} diff --git a/home/ssh.pub b/home/ssh.pub new file mode 100644 index 0000000..1754d4b --- /dev/null +++ b/home/ssh.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzP1PjIDb1tN9nhPOK88HYDtTNk9SN9ZpEem2id49Fa h@astyanax diff --git a/hosts/andromache/default.nix b/hosts/andromache/default.nix index 2db9b5d..ef7969c 100644 --- a/hosts/andromache/default.nix +++ b/hosts/andromache/default.nix @@ -1,6 +1,7 @@ { lib, inputs, + outputs, config, pkgs, ... @@ -81,7 +82,9 @@ in home-manager = { useGlobalPkgs = true; useUserPackages = true; - extraSpecialArgs = { inherit inputs; }; + extraSpecialArgs = { + inherit inputs outputs; + }; users.${username} = import ../../home/hosts/andromache { inherit lib; inherit inputs; @@ -90,6 +93,8 @@ in }; }; + ssh.authorizedHosts = [ "astyanax" ]; + services.xserver = { videoDrivers = [ "nvidia" ]; }; diff --git a/hosts/astyanax/default.nix b/hosts/astyanax/default.nix index f83f1bd..f9a5267 100644 --- a/hosts/astyanax/default.nix +++ b/hosts/astyanax/default.nix @@ -1,6 +1,7 @@ { lib, inputs, + outputs, config, pkgs, ... @@ -75,7 +76,9 @@ in home-manager = { useGlobalPkgs = true; useUserPackages = true; - extraSpecialArgs = { inherit inputs; }; + extraSpecialArgs = { + inherit inputs outputs; + }; users.${username} = import ../../home/hosts/astyanax { inherit inputs; inherit config; @@ -88,6 +91,8 @@ in hostId = "80eef97e"; }; + ssh.authorizedHosts = [ "andromache" ]; + services = { fwupd.enable = true; openssh = { diff --git a/hosts/astyanax/ssh_host.pub b/hosts/astyanax/ssh_host.pub new file mode 100644 index 0000000..50ccb92 --- /dev/null +++ b/hosts/astyanax/ssh_host.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO8+UOyZbvQeHfFfYox3SQi42KJ0S3RZj79iswSsZeFy root@nixos diff --git a/hosts/astyanax/ssh_user.pub b/hosts/astyanax/ssh_user.pub new file mode 100644 index 0000000..1754d4b --- /dev/null +++ b/hosts/astyanax/ssh_user.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzP1PjIDb1tN9nhPOK88HYDtTNk9SN9ZpEem2id49Fa h@astyanax diff --git a/hosts/hecuba/default.nix b/hosts/hecuba/default.nix index 3f4baf8..41946d4 100644 --- a/hosts/hecuba/default.nix +++ b/hosts/hecuba/default.nix @@ -1,4 +1,11 @@ -{ pkgs, ... }: +{ + lib, + inputs, + outputs, + config, + pkgs, + ... +}: # Also see @@ -31,12 +38,14 @@ username = { isNormalUser = true; extraGroups = [ "wheel" ]; - openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOXPEhdKOVnb6mkeLLUcFGt+mnUR5pMie17JtjrxwgO h@andromache" - ]; }; }; + ssh = { + username = "username"; + authorizedHosts = [ "andromache" ]; + }; + security.sudo.wheelNeedsPassword = false; networking = { @@ -57,4 +66,6 @@ enable = true; harden = true; }; + + networking.hostName = "hecuba"; } diff --git a/hosts/vm/default.nix b/hosts/vm/default.nix index b7b7350..aafca92 100644 --- a/hosts/vm/default.nix +++ b/hosts/vm/default.nix @@ -1,6 +1,7 @@ { lib, inputs, + outputs, config, pkgs, ... @@ -58,7 +59,9 @@ in home-manager = { useGlobalPkgs = true; useUserPackages = true; - extraSpecialArgs = { inherit inputs; }; + extraSpecialArgs = { + inherit inputs outputs; + }; users.${username} = import ../../home/hosts/vm { inherit inputs; inherit config; diff --git a/modules/ssh/authorized-keys.nix b/modules/ssh/authorized-keys.nix new file mode 100644 index 0000000..cf0286a --- /dev/null +++ b/modules/ssh/authorized-keys.nix @@ -0,0 +1,24 @@ +{ lib, config, ... }: +{ + options.ssh = { + authorizedHosts = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + username = lib.mkOption { + type = lib.types.str; + default = "h"; + }; + }; + + # auto generate authorized_keys from `authorizedHosts` + config.users.users.${config.ssh.username}.openssh.authorizedKeys.keys = lib.flatten ( + map ( + hostname: + let + keyFile = ../../hosts/${hostname}/ssh_user.pub; + in + lib.optionals (builtins.pathExists keyFile) (lib.splitString "\n" (builtins.readFile keyFile)) + ) config.ssh.authorizedHosts + ); +} diff --git a/modules/ssh/extract-keys.nix b/modules/ssh/extract-keys.nix new file mode 100644 index 0000000..603685e --- /dev/null +++ b/modules/ssh/extract-keys.nix @@ -0,0 +1,24 @@ +{ lib, config, ... }: +let + username = config.ssh.username; +in +{ + # auto extract SSH keys + system.activationScripts.extractSshKeys = lib.stringAfter [ "etc" ] '' + HOST_KEY="/etc/ssh/ssh_host_ed25519_key.pub" + HOST_DIR="/home/${username}/nix/hosts/${config.networking.hostName}" + + if [ -f "$HOST_KEY" ] && [ -d "$HOST_DIR" ]; then + cp "$HOST_KEY" "$HOST_DIR/ssh_host.pub" + chown ${username}:users "$HOST_DIR/ssh_host.pub" + chmod 644 "$HOST_DIR/ssh_host.pub" + fi + + USER_KEY="/home/${username}/.ssh/id_ed25519.pub" + if [ -f "$USER_KEY" ] && [ -d "$HOST_DIR" ]; then + cp "$USER_KEY" "$HOST_DIR/ssh_user.pub" + chown ${username}:users "$HOST_DIR/ssh_user.pub" + chmod 644 "$HOST_DIR/ssh_user.pub" + fi + ''; +} diff --git a/modules/ssh/hardened-openssh.nix b/modules/ssh/hardened-openssh.nix index 1d6b871..b046f5c 100644 --- a/modules/ssh/hardened-openssh.nix +++ b/modules/ssh/hardened-openssh.nix @@ -4,9 +4,17 @@ let cfg = config.services.openssh; in { + imports = [ + ./known-hosts.nix + ./authorized-keys.nix + ./extract-keys.nix + ]; + options.services.openssh.harden = mkEnableOption "harden ssh server configuration"; + config = { networking.firewall.allowedTCPPorts = [ 22 ]; + services.openssh.settings = optionalAttrs cfg.harden { PermitRootLogin = "no"; PasswordAuthentication = false; diff --git a/modules/ssh/known-hosts.nix b/modules/ssh/known-hosts.nix new file mode 100644 index 0000000..2b81e42 --- /dev/null +++ b/modules/ssh/known-hosts.nix @@ -0,0 +1,19 @@ +{ + lib, + config, + outputs, + ... +}: +let + hosts = lib.attrNames outputs.nixosConfigurations; + hostsWithKeys = lib.filter ( + hostname: builtins.pathExists ../../hosts/${hostname}/ssh_host.pub + ) hosts; +in +{ + # auto generate known_hosts for all hosts in flake + programs.ssh.knownHosts = lib.genAttrs hostsWithKeys (hostname: { + publicKeyFile = ../../hosts/${hostname}/ssh_host.pub; + extraHostNames = lib.optional (hostname == config.networking.hostName) "localhost"; + }); +}