diff --git a/config/host-config/lambda.nix b/config/host-config/lambda.nix index 141cc2c..ede2eed 100644 --- a/config/host-config/lambda.nix +++ b/config/host-config/lambda.nix @@ -23,6 +23,14 @@ in { }; }; + fudo.secrets.host-secrets.lambda = { + host-keytab = { + source-file = /state/secrets/kerberos/lambda.keytab; + target-file = "/etc/krb5.keytab"; + user = "root"; + }; + }; + fudo.ipfs = { enable = true; users = [ "niten" ]; diff --git a/config/host-config/limina.nix b/config/host-config/limina.nix index c976437..ad02a8c 100644 --- a/config/host-config/limina.nix +++ b/config/host-config/limina.nix @@ -59,12 +59,18 @@ in { network-definition = config.fudo.networks.${domain-name}; }; - secrets.limina = { + secrets.host-secrets.limina = { backplane-client-passwd = { - source-file = "/srv/secrets/backplane-client/limina.passwd"; + source-file = /state/secrets/backplane-client/limina.passwd; target-file = "/srv/backplane/dns/client.passwd"; user = config.fudo.client.dns.user; }; + + host-keytab = { + source-file = /state/secrets/kerberos/limina.keytab; + target-file = "/etc/krb5.keytab"; + user = "root"; + }; }; client.dns = { @@ -74,7 +80,7 @@ in { user = "fudo-client"; external-interface = "enp1s0"; password-file = - config.fudo.secrets.limina.backplane-client-passwd.target-file; + config.fudo.secrets.host-secrets.limina.backplane-client-passwd.target-file; }; garbage-collector = { diff --git a/config/host-config/plato.nix b/config/host-config/plato.nix index f0de4c8..75866dd 100644 --- a/config/host-config/plato.nix +++ b/config/host-config/plato.nix @@ -1,37 +1,27 @@ { config, lib, pkgs, ... }: with lib; -let - primary-ip = "10.0.0.21"; - deploy-group = "nixops-deploy"; - secrets-path = "/srv/secrets"; +let primary-ip = "10.0.0.21"; in { config = { - users.groups = { ${deploy-group} = { members = [ "niten" ]; }; }; + + fudo.secrets = { + host-secrets.plato = { + host-keytab = { + source-file = /state/secrets/kerberos/plato.keytab; + target-file = "/etc/krb5.keytab"; + user = "root"; + }; + }; + + secret-group = "fudo-secrets"; + secret-users = [ "niten" ]; + secret-paths = [ "/state/secrets" ]; + }; systemd = let secrets-watcher-name = "secrets-ownership-fixer"; in { - paths.${secrets-watcher-name} = { - description = "Watch ${secrets-path} and correct perms on change."; - wantedBy = [ "multi-user.target" ]; - pathConfig = { - PathChanged = secrets-path; - Unit = "${secrets-watcher-name}.service"; - }; - }; - - services.${secrets-watcher-name} = { - wantedBy = [ "multi-user.target" ]; - description = "Correct perms on ${secrets-path}."; - serviceConfig = { - ExecStart = pkgs.writeShellScript "${secrets-watcher-name}.sh" '' - chown -R root:${deploy-group} ${secrets-path} - chmod -R ug=rX,o= ${secrets-path} - ''; - }; - }; - tmpfiles.rules = [ "L /root/.gnupg - - - - /state/root/gnupg" # "L /root/.emacs.d - - - - /state/root/emacs.d" @@ -49,12 +39,12 @@ in { NIXOS.source = "/state/etc/NIXOS"; machine-id.source = "/state/etc/machine-id"; "host-config.nix".source = "/state/etc/host-config.nix"; - "krb5.keytab" = { - source = "/state/etc/plato.keytab"; - user = "root"; - group = "root"; - mode = "0600"; - }; + # "krb5.keytab" = { + # source = "/state/etc/plato.keytab"; + # user = "root"; + # group = "root"; + # mode = "0600"; + # }; }; system.stateVersion = "20.09"; diff --git a/config/host-config/procul.nix b/config/host-config/procul.nix index 6ba765d..f2a4d5b 100644 --- a/config/host-config/procul.nix +++ b/config/host-config/procul.nix @@ -71,24 +71,30 @@ in { fudo = { hosts.procul.external-interfaces = [ "extif0" ]; - secrets.procul = { + secrets.host-secrets.procul = { backplane-client-passwd = { - source-file = "/srv/secrets/backplane-client/procul.passwd"; + source-file = /state/secrets/backplane-client/procul.passwd; target-file = "/srv/backplane/dns/client.passwd"; user = config.fudo.client.dns.user; }; postgres-keytab = { - source-file = "/srv/secrets/kerberos/procul-postgres.keytab"; + source-file = /state/secrets/kerberos/procul-postgres.keytab; target-file = "/srv/postgres/secure/postgres.keytab"; user = "root"; }; gitea-database-password = { - source-file = "/srv/secrets/gitea/procul-database.passwd"; + source-file = /state/secrets/gitea/procul-database.passwd; target-file = "/srv/gitea/secure/database.passwd"; user = config.fudo.git.user; }; + + # host-keytab = { + # source-file = /state/secrets/kerberos/procul.keytab; + # target-file = "/etc/krb5.keytab"; + # user = "root"; + # }; }; client.dns = { @@ -98,7 +104,7 @@ in { user = "fudo-client"; external-interface = "extif0"; password-file = - config.fudo.secrets.procul.backplane-client-passwd.target-file; + config.fudo.secrets.host-secrets.procul.backplane-client-passwd.target-file; }; auth.kdc = { @@ -195,13 +201,14 @@ in { enable = true; ssl-certificate = (acme-certificate host-fqdn); ssl-private-key = (acme-private-key host-fqdn); - keytab = config.fudo.secrets.procul.postgres-keytab.target-file; + keytab = + config.fudo.secrets.host-secrets.procul.postgres-keytab.target-file; local-networks = local-networks; users = { gituser = { password-file = - config.fudo.secrets.procul.gitea-database-password.target-file; + config.fudo.secrets.host-secrets.procul.gitea-database-password.target-file; databases = { git = { access = "CONNECT"; @@ -227,7 +234,7 @@ in { database = { user = "gituser"; password-file = - config.fudo.secrets.procul.gitea-database-password.target-file; + config.fudo.secrets.host-secrets.procul.gitea-database-password.target-file; hostname = "127.0.0.1"; name = "git"; }; diff --git a/config/sites.nix b/config/sites.nix index c9adc6e..17e56da 100644 --- a/config/sites.nix +++ b/config/sites.nix @@ -22,6 +22,7 @@ }; }; enable-distributed-builds = true; + keytab-directory = "/state/secrets/kerberos"; # FIXME: good idea? # network-mounts = { # "/mnt/documents" = { @@ -75,6 +76,7 @@ timezone = "America/Winnipeg"; deploy-pubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDPwh522lvafTJYA0X2uFdP7Ws+Um1f8gZsARK1Y5nMzf6ZcWBF1jplTOKUVSOl4isMWni0Tu0TnX4zqCcgocWUVbwIwXSIRYqdiCPvVOH+/Ibc97n1/dYxk5JPMtbrsEw6/gWZxVg0qwe0J3dQWldEMiDY7iWhlrmIr7YL+Y3PUd7DOwp3PbfWfNyzTfE1kXcz5YvTeN+txFhbbXT0oS2R2wtc1vYXFZ/KbNstjqd+i8jszAq3ZkbbwL3aNR0RO4n8+GoIILGw8Ya4eP7D6+mYk608IhAoxpGyMrUch2TC2uvOK3rd/rw1hsTxf4AKjAZbrfd/FJaYru9ZeoLjD4bRGMdVp56F1m7pLvRiWRK62pV2Q/fjx+4KjHUrgyPd601eUIP0ayS/Rfuq8ijLpBJgO5/Y/6mFus/kjZIfRR9dXfLM67IMpyEzEITYrc/R2sedWf+YHxSh6eguAZ/kLzioar1nHLR7Wzgeu0tgWkD78WQGjpXGoefAz3xHeBg3Et0="; + keytab-directory = "/state/secrets/kerberos"; }; }; } diff --git a/home-manager/niten.nix b/home-manager/niten.nix index 2cf0cf0..3db177a 100644 --- a/home-manager/niten.nix +++ b/home-manager/niten.nix @@ -57,6 +57,7 @@ let tmux unzip xclip + youtube-dl yubikey-manager yubikey-personalization diff --git a/lib/fudo/git.nix b/lib/fudo/git.nix index 31a7d9a..7dba9b7 100644 --- a/lib/fudo/git.nix +++ b/lib/fudo/git.nix @@ -25,20 +25,22 @@ let }; }; - sshOpts = { ... }: with types; { - options = { - listen-ip = mkOption { - type = str; - description = "IP on which to listen for SSH connections."; - }; + sshOpts = { ... }: + with types; { + options = { + listen-ip = mkOption { + type = str; + description = "IP on which to listen for SSH connections."; + }; - listen-port = mkOption { - type = port; - description = "Port on which to listen for SSH connections, on ."; - default = 22; + listen-port = mkOption { + type = port; + description = + "Port on which to listen for SSH connections, on ."; + default = 22; + }; }; }; - }; in { options.fudo.git = with types; { @@ -81,7 +83,8 @@ in { local-port = mkOption { type = port; - description = "Local port to which the Gitea server will bind. Not globally accessible."; + description = + "Local port to which the Gitea server will bind. Not globally accessible."; default = 3543; }; @@ -93,9 +96,12 @@ in { }; config = mkIf cfg.enable { - security.acme.certs.${cfg.hostname}.email = let - domain-name = config.fudo.hosts.${config.instance.hostname}.domain; - in config.fudo.domains.${domain-name}.admin-email; + security.acme.certs.${cfg.hostname}.email = + let domain-name = config.fudo.hosts.${config.instance.hostname}.domain; + in config.fudo.domains.${domain-name}.admin-email; + + networking.firewall.allowedTCPPorts = + mkIf (cfg.ssh != null) [ cfg.ssh.listen-port ]; services = { gitea = { @@ -123,7 +129,7 @@ in { settings = mkIf (cfg.ssh != null) { server = { SSH_DOMAIN = cfg.hostname; - SSH_LISTEN_PORT = cfg.ssh.listen-port; + # SSH_LISTEN_PORT = cfg.ssh.listen-port; SSH_LISTEN_HOST = cfg.ssh.listen-ip; }; }; diff --git a/lib/fudo/hosts.nix b/lib/fudo/hosts.nix index 477957e..e3998f3 100644 --- a/lib/fudo/hosts.nix +++ b/lib/fudo/hosts.nix @@ -125,6 +125,12 @@ let description = "A list of interfaces on which to enable the firewall."; default = [ ]; }; + + keytab-secret-file = mkOption { + type = nullOr str; + description = "Keytab from which to create a keytab secret."; + default = null; + }; }; }; @@ -208,6 +214,20 @@ in { boot.tmpOnTmpfs = host-cfg.tmp-on-tmpfs; + fudo.secrets.host-secrets.${hostname}.host-keytab = let + mapOptional = f: val: if (val != null) then (f val) else null; + keytab-file = mapOptional (keytab-path: + if (pathExists keytab-path) then + /. + builtins.toPath keytab-path + else + null) (mapOptional (keytab-dir: "${keytab-dir}/${hostname}.keytab") + site.keytab-directory); + in mkIf (keytab-file != null) { + source-file = /. + builtins.toPath keytab-file; + target-file = "/etc/krb5.keytab"; + user = "root"; + }; + programs.ssh.knownHosts = let keyed-hosts = filterAttrs (host: opts: opts.ssh-pubkey != null) config.fudo.hosts; diff --git a/lib/fudo/secrets.nix b/lib/fudo/secrets.nix index 5822d14..78571b6 100644 --- a/lib/fudo/secrets.nix +++ b/lib/fudo/secrets.nix @@ -2,6 +2,8 @@ with lib; let + cfg = config.fudo.secrets; + encrypt-on-disk = { secret-name, target-host, source-file }: pkgs.stdenv.mkDerivation { name = "${target-host}-${secret-name}-secret"; @@ -39,9 +41,6 @@ let mkdir -p "$TARGET_DIR" fi ''; - ExecStop = pkgs.writeShellScript "clear-${secret-name}-secret.sh" '' - rm -f ${target-file} - ''; ExecStart = let decrypt-keys = filter (key: key.type == key-type) config.services.openssh.hostKeys; @@ -57,7 +56,7 @@ let secretOpts = { ... }: { options = with types; { source-file = mkOption { - type = str; + type = path; # CAREFUL: this will copy the file to nixstore...I think? description = "File from which to load the secret."; }; @@ -86,32 +85,80 @@ let }; }; + nix-build-users = let usernames = attrNames config.users.users; + in filter (user: (builtins.match "^nixbld[0-9]{1,2}$" user) != null) + usernames; + in { - options.fudo.secrets = with types; - mkOption { + options.fudo.secrets = with types; { + host-secrets = mkOption { type = attrsOf (attrsOf (submodule secretOpts)); - description = "Map of hosts, to secrets, to secret config."; + description = "Map of hosts to host secrets"; default = { }; - example = { - my-host = { - my-host-secret = { - source-file = /path/to/file/on/this/host; - target-file = "/target/path/on/host"; - user = "some-user"; + }; + + secret-users = mkOption { + type = listOf str; + description = "List of users with read-access to secrets."; + default = [ ]; + }; + + secret-group = mkOption { + type = str; + description = "Group to which secrets will belong."; + default = "nixops-secrets"; + }; + + secret-paths = mkOption { + type = listOf str; + description = + "Paths which contain (only) secrets. The contents will be reabable by the secret-group."; + default = [ ]; + }; + }; + + config = { + users.groups = { + ${cfg.secret-group} = { members = cfg.secret-users ++ nix-build-users; }; + }; + + systemd = let + hostname = config.instance.hostname; + host-secrets = if (hasAttr hostname cfg.host-secrets) then + cfg.host-secrets.${hostname} + else + { }; + host-secret-services = mapAttrs' (secret: secretOpts: + (nameValuePair "fudo-secret-${hostname}-${secret}" + (secret-service hostname secret secretOpts))) host-secrets; + + in { + services = host-secret-services // { + fudo-secrets-watcher = { + wantedBy = [ "multi-user.target" ]; + description = + "Ensure access for group ${cfg.secret-group} to fudo secret paths."; + serviceConfig = { + ExecStart = pkgs.writeShellScript "fudo-secrets-watcher.sh" + (concatStringsSep "\n" (map (path: '' + chown -R root:${cfg.secret-group} ${path} + chmod -R u=rwX,g=rX,o= ${path} + '') cfg.secret-paths)); }; }; }; - }; - config = { - systemd.services = let - hostname = config.instance.hostname; - host-secrets = if (hasAttr hostname config.fudo.secrets) then - config.fudo.secrets.${hostname} - else - { }; - in mapAttrs' (secret: secretOpts: - (nameValuePair "fudo-secret-${hostname}-${secret}" - (secret-service hostname secret secretOpts))) host-secrets; + paths.fudo-secrets-watcher = mkIf ((length cfg.secret-paths) > 0) { + wantedBy = [ "multi-user.target" ]; + description = "Watch fudo secret paths, and correct perms on changes."; + pathConfig = { + PathChanged = cfg.secret-paths; + Unit = "fudo-secrets-watcher.service"; + }; + }; + + tmpfiles.rules = map (path: "d '${path}' - root ${cfg.secret-group} - -") + cfg.secret-paths; + }; }; } diff --git a/lib/fudo/sites.nix b/lib/fudo/sites.nix index 5888406..e8ef282 100644 --- a/lib/fudo/sites.nix +++ b/lib/fudo/sites.nix @@ -139,6 +139,16 @@ let description = "List of networks to consider local at this site."; default = [ ]; }; + + keytab-directory = mkOption { + type = nullOr str; + description = '' + Directory containing site keytabs (files named $hostname.keytab). + + Should exist only on build host. + ''; + default = null; + }; }; }; diff --git a/nixops/informis.nix b/nixops/informis.nix index 3464c3d..b63f7bf 100644 --- a/nixops/informis.nix +++ b/nixops/informis.nix @@ -6,7 +6,10 @@ let define-host = hosts.host-config; in { - network.description = "Informis network"; + network = { + description = "Informis network"; + enableRollback = true; + }; procul = define-host "172.86.179.18" "procul"; } diff --git a/nixops/seattle.nix b/nixops/seattle.nix index 36b1ba9..4b79990 100644 --- a/nixops/seattle.nix +++ b/nixops/seattle.nix @@ -6,7 +6,10 @@ let define-host = hosts.host-config; in { - network.description = "Seattle home network."; + network = { + description = "Seattle home network."; + enableRollback = true; + }; limina = define-host "10.0.0.1" "limina"; lambda = define-host "10.0.0.11" "lambda";