feat: extract rekeying as separate repo

This commit is contained in:
oddlama 2023-01-30 22:03:39 +01:00
parent 7c6461d8e2
commit 266945d242
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
7 changed files with 50 additions and 185 deletions

View file

@ -1,24 +0,0 @@
pkgs: config:
with pkgs.lib;
pkgs.stdenv.mkDerivation rec {
pname = "host-secrets";
version = "1.0";
description = "Rekeyed secrets for this host.";
srcs = mapAttrsToList (_: x: x.file) config.rekey.secrets;
sourcePath = ".";
# Required as input to have the derivation rebuild if this changes
hostPubkey = let
pubkey = config.rekey.hostPubkey;
in
if isPath pubkey
then readFile pubkey
else pubkey;
dontMakeSourcesWritable = true;
dontUnpack = true;
dontConfigure = true;
dontBuild = true;
installPhase = ''cp -r "/tmp/nix-rekey/${builtins.hashString "sha1" hostPubkey}/." "$out"'';
}

View file

@ -1,60 +0,0 @@
{
self,
nixpkgs,
...
}: system:
with nixpkgs.lib; let
pkgs = self.pkgs.${system};
toPubkey = pubkey:
if isPath pubkey
then readFile pubkey
else pubkey;
rekeyCommandsForHost = hostName: hostAttrs: let
hostPubkeyStr = toPubkey hostAttrs.config.rekey.hostPubkey;
secretDir = "/tmp/nix-rekey/${builtins.hashString "sha1" hostPubkeyStr}";
rekeyCommand = secretName: secretAttrs: let
masterIdentityArgs = concatMapStrings (x: ''-i "${x}" '') hostAttrs.config.rekey.masterIdentityPaths;
secretOut = "${secretDir}/${secretName}.age";
in ''
echo "Rekeying ${secretName} for host ${hostName}"
${pkgs.rage}/bin/rage ${masterIdentityArgs} -d ${secretAttrs.file} \
| ${pkgs.rage}/bin/rage -r "${hostPubkeyStr}" -o "${secretOut}" -e \
|| { \
echo "Failed to rekey secret ${secretName} for ${hostName}!" ; \
echo "This is a dummy replacement value. The actual secret could not be rekeyed." \
| ${pkgs.rage}/bin/rage -r "${hostPubkeyStr}" -o "${secretOut}" -e ; \
}
'';
in ''
mkdir -p "${secretDir}"
# Enable selected age plugins for this host
export PATH="$PATH${concatMapStrings (x: ":${x}/bin") hostAttrs.config.rekey.agePlugins}"
${concatStringsSep "\n" (mapAttrsToList rekeyCommand hostAttrs.config.rekey.secrets)}
'';
rekeyScript = pkgs.writeShellScript "rekey" ''
set -euo pipefail
${concatStringsSep "\n" (mapAttrsToList rekeyCommandsForHost self.nixosConfigurations)}
nix run --extra-sandbox-paths /tmp "${../.}#rekey-save-outputs";
'';
rekeySaveOutputsScript = let
copyHostSecrets = hostName: hostAttrs: let
drv = import ./rekey-output-derivation.nix pkgs hostAttrs.config;
in ''echo "Stored rekeyed secrets for ${hostAttrs.config.networking.hostName} in ${drv}"'';
in
pkgs.writeShellScript "rekey-save-outputs" ''
set -euo pipefail
${concatStringsSep "\n" (mapAttrsToList copyHostSecrets self.nixosConfigurations)}
'';
in {
rekey = {
type = "app";
program = "${rekeyScript}";
};
rekey-save-outputs = {
type = "app";
program = "${rekeySaveOutputsScript}";
};
}

39
flake.lock generated
View file

@ -3,16 +3,15 @@
"agenix": {
"inputs": {
"nixpkgs": [
"ragenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1665870395,
"narHash": "sha256-Tsbqb27LDNxOoPLh0gw2hIb6L/6Ow/6lIBvqcHzEKBI=",
"lastModified": 1675030834,
"narHash": "sha256-e1/7Z7rVRqy2NuEOxrRm560wc/Kn8NU7gz8CDfmu9F0=",
"owner": "ryantm",
"repo": "agenix",
"rev": "a630400067c6d03c9b3e0455347dc8559db14288",
"rev": "49798e535ebc07fec82256b283d35be36d8c6c9a",
"type": "github"
},
"original": {
@ -21,6 +20,32 @@
"type": "github"
}
},
"agenix-rekey": {
"inputs": {
"agenix": [
"agenix"
],
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1675112851,
"narHash": "sha256-PAyCKssbhSTZfIMgfRdHuECjR5Bzw74oK+zLSVHcVwU=",
"owner": "oddlama",
"repo": "agenix-rekey",
"rev": "cae8d9ffeaed228382f60a208f1447385720c137",
"type": "github"
},
"original": {
"owner": "oddlama",
"repo": "agenix-rekey",
"type": "github"
}
},
"deploy-rs": {
"inputs": {
"flake-compat": [
@ -214,7 +239,9 @@
},
"ragenix": {
"inputs": {
"agenix": "agenix",
"agenix": [
"agenix"
],
"flake-utils": [
"flake-utils"
],
@ -239,6 +266,8 @@
},
"root": {
"inputs": {
"agenix": "agenix",
"agenix-rekey": "agenix-rekey",
"deploy-rs": "deploy-rs",
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",

View file

@ -33,8 +33,19 @@
inputs.flake-compat.follows = "flake-compat";
};
agenix = {
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
};
ragenix = {
url = "github:yaxitech/ragenix";
inputs.agenix.follows = "agenix";
inputs.flake-utils.follows = "flake-utils";
inputs.nixpkgs.follows = "nixpkgs";
};
agenix-rekey = {
url = "github:oddlama/agenix-rekey";
inputs.agenix.follows = "agenix";
inputs.flake-utils.follows = "flake-utils";
inputs.nixpkgs.follows = "nixpkgs";
};
@ -76,7 +87,5 @@
];
config.allowUnfree = true;
};
apps = {} // import ./apps/rekey.nix inputs localSystem;
});
}

View file

@ -10,7 +10,6 @@
'';
in {
imports = [
./rekey.nix
./inputrc.nix
./issue.nix
./nix.nix

View file

@ -1,90 +0,0 @@
{
lib,
options,
config,
pkgs,
...
}:
with lib; {
config = let
drv = import ../../apps/rekey-output-derivation.nix pkgs config;
in
mkIf (config.rekey.secrets != {}) {
# Produce a rekeyed age secret for each of the secrets defined in our secrets
age.secrets = mapAttrs (secretName:
flip mergeAttrs {
file = "${drv}/${secretName}";
})
config.rekey.secrets;
assertions = [
{
assertion = pathExists config.rekey.hostPubkey;
message = ''
The path config.rekey.hostPubkey (${toString config.rekey.hostPubkey}) doesn't exist, but is required to rekey secrets for this host.
If this is the first deploy, use a mock key until you know the real one.
'';
}
{
assertion = config.rekey.masterIdentityPaths != [];
message = "rekey.masterIdentityPaths must be set.";
}
];
warnings = let
hasGoodSuffix = x: strings.hasSuffix ".age" x || strings.hasSuffix ".pub" x;
in
# drv.drvPath doesn't force evaluation, which allows the warning to be displayed
# in case the derivation is not built before deploying
optional (!pathExists (removeSuffix ".drv" drv.drvPath)) ''
The secrets for host ${config.networking.hostName} have not yet been rekeyed!
Be sure to run `nix run ".#rekey"` after changing your secrets!
''
++ optional (!all hasGoodSuffix config.rekey.masterIdentityPaths) ''
It seems like at least one of your rekey.masterIdentityPaths contains an
unencrypted age identity. These files will be copied to the nix store, so
make sure they don't contain any secret information!
To silence this warning, encrypt your keys and name them *.pub or *.age.
'';
};
options = {
rekey.secrets = options.age.secrets;
rekey.hostPubkey = mkOption {
type = types.either types.path types.str;
description = ''
The age public key to use as a recipient when rekeying.
This either has to be the path to an age public key file,
or the public key itself in string form.
Make sure to NEVER use a private key here, as it will end
up in the public nix store!
'';
#example = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEyH9Vx7WJZWW+6tnDsF7JuflcxgjhAQHoCWVrjLXQ2U my-host";
#example = "age159tavn5rcfnq30zge2jfq4yx60uksz8udndp0g3njzhrns67ca5qq3n0tj";
example = /etc/ssh/ssh_host_ed25519_key.pub;
};
rekey.masterIdentityPaths = mkOption {
type = types.listOf types.path;
description = ''
The age identity used to decrypt the secrets stored in the repository, so they can be rekeyed for a specific host.
This identity will be stored in the nix store, so be sure to use a split-identity (like a yubikey identity, which is public),
or an encrypted age identity. You can encrypt an age identity using `rage -p -o privkey.age privkey` to protect it in your store.
All identities given here will be passed to age, which will select one of them for decryption.
'';
default = [];
example = [./secrets/my-yubikey-identity.txt];
};
rekey.agePlugins = mkOption {
type = types.listOf types.package;
default = [];
description = ''
A list of plugins that should be available to rage while rekeying.
They will be added to the PATH before rage is invoked.
'';
example = [pkgs.age-plugin-yubikey];
};
};
}

View file

@ -5,6 +5,7 @@
nixos-hardware,
nixpkgs,
ragenix,
agenix-rekey,
templates,
...
}: let
@ -32,9 +33,10 @@
nixpkgs.hostPlatform = hostPlatform;
}
nixRegistry
home-manager.nixosModules.home-manager
#impermanence.nixosModules.impermanence
ragenix.nixosModules.age
home-manager.nixosModules.default
#impermanence.nixosModules.default
ragenix.nixosModules.default
agenix-rekey.nixosModules.default
];
specialArgs = {
#impermanence = impermanence.nixosModules;