From 69a54b99c85e725dc084f872c2fe9f371a79b047 Mon Sep 17 00:00:00 2001 From: Noah Masur <7386960+nmasur@users.noreply.github.com> Date: Sun, 16 Oct 2022 01:32:39 +0000 Subject: [PATCH] new secrets management system --- apps/encrypt-secret.nix | 19 ++++++ apps/reencrypt-secrets.nix | 27 +++++++++ flake.nix | 7 +++ hosts/public-keys | 4 ++ modules/services/secrets.nix | 97 +++++++++++++++++++++++++++++++ modules/services/transmission.nix | 34 ++++------- modules/shell/age.nix | 28 ++++----- private/transmission.json.age | 15 +++-- 8 files changed, 188 insertions(+), 43 deletions(-) create mode 100644 apps/encrypt-secret.nix create mode 100644 apps/reencrypt-secrets.nix create mode 100644 hosts/public-keys create mode 100644 modules/services/secrets.nix diff --git a/apps/encrypt-secret.nix b/apps/encrypt-secret.nix new file mode 100644 index 0000000..325d942 --- /dev/null +++ b/apps/encrypt-secret.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: { + + # nix run github:nmasur/dotfiles#encrypt-secret > private/mysecret.age + + type = "app"; + + program = builtins.toString (pkgs.writeShellScript "encrypt-secret" '' + printf "\nEnter the secret data to encrypt for all hosts...\n\n" 1>&2 + read -p "Secret: " secret + printf "\nEncrypting...\n\n" 1>&2 + tmpfile=$(mktemp) + echo "''${secret}" > ''${tmpfile} + ${pkgs.age}/bin/age --encrypt --armor --recipients-file ${ + builtins.toString ../hosts/public-keys + } $tmpfile + rm $tmpfile + ''); + +} diff --git a/apps/reencrypt-secrets.nix b/apps/reencrypt-secrets.nix new file mode 100644 index 0000000..f1c2c2d --- /dev/null +++ b/apps/reencrypt-secrets.nix @@ -0,0 +1,27 @@ +{ pkgs, ... }: { + + # nix run github:nmasur/dotfiles#reencrypt-secrets ./private + + type = "app"; + + program = builtins.toString (pkgs.writeShellScript "reencrypt-secrets" '' + if [ $# -eq 0 ]; then + echo "Must provide directory to reencrypt." + exit 1 + fi + encrypted=$1 + for encryptedfile in ''${1}/*; do + tmpfile=$(mktemp) + echo "Decrypting ''${encryptedfile}..." + ${pkgs.age}/bin/age --decrypt \ + --identity ~/.ssh/id_ed25519 $encryptedfile > $tmpfile + echo "Encrypting ''${encryptedfile}..." + ${pkgs.age}/bin/age --encrypt --armor --recipients-file ${ + builtins.toString ../hosts/public-keys + } $tmpfile > $encryptedfile + rm $tmpfile + done + echo "Finished." + ''); + +} diff --git a/flake.nix b/flake.nix index 04c5811..1a9709c 100644 --- a/flake.nix +++ b/flake.nix @@ -92,6 +92,13 @@ # Load the SSH key for this machine loadkey = import ./apps/loadkey.nix { inherit pkgs; }; + # Encrypt secret for all machines + encrypt-secret = import ./apps/encrypt-secret.nix { inherit pkgs; }; + + # Re-encrypt secrets for all machines + reencrypt-secrets = + import ./apps/reencrypt-secrets.nix { inherit pkgs; }; + # Connect machine metrics to Netdata Cloud netdata = import ./apps/netdata-cloud.nix { inherit pkgs; }; diff --git a/hosts/public-keys b/hosts/public-keys new file mode 100644 index 0000000..74beb04 --- /dev/null +++ b/hosts/public-keys @@ -0,0 +1,4 @@ +# Scan hosts: ssh-keyscan -t ed25519 + +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s noah +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHVknmPi7sG6ES0G0jcsvebzKGWWaMfJTYgvOue6EULI oracle.masu.rs diff --git a/modules/services/secrets.nix b/modules/services/secrets.nix new file mode 100644 index 0000000..79187b6 --- /dev/null +++ b/modules/services/secrets.nix @@ -0,0 +1,97 @@ +# Secrets management method taken from here: +# https://xeiaso.net/blog/nixos-encrypted-secrets-2021-01-20 + +# In my case, I pre-encrypt my secrets and commit them to git. + +{ config, pkgs, lib, ... }: { + + options = { + + identityFile = lib.mkOption { + type = lib.types.str; + description = "Path to existing identity file."; + default = "/etc/ssh/ssh_host_ed25519_key"; + }; + + # secretsDirectory = lib.mkOption { + # type = lib.types.str; + # description = "Default path to place secrets."; + # default = "/var/lib/private"; + # }; + + secrets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options = { + source = lib.mkOption { + type = lib.types.path; + description = "Path to encrypted secret."; + }; + dest = lib.mkOption { + type = lib.types.str; + description = "Resulting path for decrypted secret."; + }; + owner = lib.mkOption { + default = "root"; + type = lib.types.str; + description = "User to own the secret."; + }; + group = lib.mkOption { + default = "root"; + type = lib.types.str; + description = "Group to own the secret."; + }; + permissions = lib.mkOption { + default = "0400"; + type = lib.types.str; + description = "Permissions expressed as octal."; + }; + }; + }); + description = "Set of secrets to decrypt to disk."; + default = { }; + }; + + }; + + config = { + + # Create a default directory to place secrets + + # systemd.tmpfiles.rules = [ "d ${config.secretsDirectory} 0750 root wheel" ]; + + # Declare oneshot service to decrypt secret using SSH host key + # - Requires that the secret is already encrypted for the host + # - Encrypt secrets: nix run github:nmasur/dotfiles#encrypt-secret + + systemd.services = lib.mapAttrs' (name: attrs: { + name = "${name}-secret"; + value = { + + description = "Decrypt secret for ${name}"; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "oneshot"; + script = '' + ${pkgs.age}/bin/age --decrypt \ + --identity ${config.identityFile} \ + --output ${attrs.dest} \ + ${attrs.source} + + chown '${attrs.owner}':'${attrs.group}' '${attrs.dest}' + chmod '${attrs.permissions}' '${attrs.dest}' + ''; + + }; + }) config.secrets; + + # Example declaration + # config.secrets.my-secret = { + # source = ../../private/my-secret.age; + # dest = "/var/lib/private/my-secret"; + # owner = "my-app"; + # group = "my-app"; + # permissions = "0440"; + # }; + + }; + +} diff --git a/modules/services/transmission.nix b/modules/services/transmission.nix index 0a6f9dd..f35088d 100644 --- a/modules/services/transmission.nix +++ b/modules/services/transmission.nix @@ -1,10 +1,6 @@ -{ config, pkgs, lib, ... }: +{ config, pkgs, lib, ... }: { -let credentialsFile = "/var/lib/private/transmission.json"; - -in { - - imports = [ ./wireguard.nix ]; + imports = [ ./wireguard.nix ./secrets.nix ]; options = { transmissionServer = lib.mkOption { @@ -33,14 +29,14 @@ in { rpc-whitelist = "127.0.0.1,${vpnIp}"; rpc-whitelist-enabled = true; }; - credentialsFile = credentialsFile; + credentialsFile = config.secrets.transmission.dest; }; # Bind transmission to wireguard namespace systemd.services.transmission = { bindsTo = [ "netns@${namespace}.service" ]; - requires = [ "network-online.target" ]; - after = [ "wireguard-wg0.service" ]; + requires = [ "network-online.target" "transmission-secret.service" ]; + after = [ "wireguard-wg0.service" "transmission-secret.service" ]; unitConfig.JoinsNamespaceOf = "netns@${namespace}.service"; serviceConfig.NetworkNamespacePath = "/var/run/netns/${namespace}"; }; @@ -71,21 +67,11 @@ in { }; # Create credentials file for transmission - systemd.services.transmission-creds = { - requiredBy = [ "transmission.service" ]; - before = [ "transmission.service" ]; - serviceConfig = { Type = "oneshot"; }; - script = '' - if [ ! -f "${credentialsFile}" ]; then - mkdir --parents ${builtins.dirOf credentialsFile} - ${pkgs.age}/bin/age --decrypt \ - --identity ${config.identityFile} \ - --output ${credentialsFile} \ - ${builtins.toString ../../private/transmission.json.age} - chown transmission:transmission ${credentialsFile} - chmod 0700 ${credentialsFile} - fi - ''; + secrets.transmission = { + source = ../../private/transmission.json.age; + dest = "/var/lib/private/transmission.json"; + owner = "transmission"; + group = "transmission"; }; }; diff --git a/modules/shell/age.nix b/modules/shell/age.nix index 87cf6df..7dcc33b 100644 --- a/modules/shell/age.nix +++ b/modules/shell/age.nix @@ -1,25 +1,25 @@ { config, pkgs, lib, ... }: { options = { - identityFile = lib.mkOption { - type = lib.types.str; - description = "Path to SSH key for age"; - default = "${config.homePath}/.ssh/id_ed25519"; - }; + # identityFile = lib.mkOption { + # type = lib.types.str; + # description = "Path to SSH key for age"; + # default = "${config.homePath}/.ssh/id_ed25519"; + # }; }; config = { home-manager.users.${config.user}.home.packages = with pkgs; [ age ]; - system.activationScripts.age.text = '' - if [ ! -f "${config.identityFile}" ]; then - $DRY_RUN_CMD echo -e \nEnter the seed phrase for your SSH key...\n - $DRY_RUN_CMD echo -e \nThen press ^D when complete.\n\n - $DRY_RUN_CMD ${pkgs.melt}/bin/melt restore ${config.identityFile} - $DRY_RUN_CMD chown ${config.user}:wheel ${config.identityFile}* - $DRY_RUN_CMD echo -e \n\nContinuing activation.\n\n - fi - ''; + # system.activationScripts.age.text = '' + # if [ ! -f "${config.identityFile}" ]; then + # $DRY_RUN_CMD echo -e \nEnter the seed phrase for your SSH key...\n + # $DRY_RUN_CMD echo -e \nThen press ^D when complete.\n\n + # $DRY_RUN_CMD ${pkgs.melt}/bin/melt restore ${config.identityFile} + # $DRY_RUN_CMD chown ${config.user}:wheel ${config.identityFile}* + # $DRY_RUN_CMD echo -e \n\nContinuing activation.\n\n + # fi + # ''; }; } diff --git a/private/transmission.json.age b/private/transmission.json.age index a98cddd..091ed0c 100644 --- a/private/transmission.json.age +++ b/private/transmission.json.age @@ -1,5 +1,10 @@ -age-encryption.org/v1 --> ssh-ed25519 MgHaOw PAAWnpc5bJ5S972U+L6YgHpI2a7aqwxWaNZrvQIODVg -A6zRWD6TmlVb8b5J3gdMf3JAeHIHgUQA3C8PpR8GveQ ---- xP8vbUGtTlvaZ0K2J0+J0ICoL9gvCbhQg6GxG8ZYCS0 -75L2cJĀe,ݝTn$Mi4Yi[! ŁL%(iF;6ԊjO \ No newline at end of file +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBSYUU2 +OWp1ZDRLVTJrR1k3SVdXZnRPN3RUNDY5RFM2WEZaTzRmdU1zSWdrCjV1VHpNMG81 +VHA4LzdsN3FpOUNoTGNlWmlHS3E4dTVvWTVoZHJMSlNYTHMKLT4gc3NoLWVkMjU1 +MTkgWXlTVU1RIDVjM1JmclgxQThKcU1XQWptWmN0MjlKU1NvMEpwMnYyd3Y4czBT +RTVkQ0UKc0pOYkRxZldsWnloQnBYMWk1eFU0M3R5SkZVTUYyaldIcENONE1PWVJv +NAotLS0gclZDQndaREZpZ2Z0R0d0alBPeW1tZFVOVHhSaHNlQTRXdTRoZmFDUFFK +SQqueOUzTFuhSryWW4Do+NAUcq2YdOtN8gmP5Zcp1oMe/9+JIs6Upjsc3eWn+dSA +7QwbGlTyd6D0+PLJxHA18Xfgpj5owGeTDtwykFPgdO1BjE8C3KlgzUfN +-----END AGE ENCRYPTED FILE-----