mirror of
https://github.com/oddlama/nix-config.git
synced 2025-10-11 07:10:39 +02:00
feat: extract rekeying as separate repo
This commit is contained in:
parent
7c6461d8e2
commit
266945d242
7 changed files with 50 additions and 185 deletions
|
@ -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"'';
|
||||
}
|
|
@ -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 "[1;31mFailed to rekey secret ${secretName} for ${hostName}![m" ; \
|
||||
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
39
flake.lock
generated
|
@ -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",
|
||||
|
|
13
flake.nix
13
flake.nix
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
'';
|
||||
in {
|
||||
imports = [
|
||||
./rekey.nix
|
||||
./inputrc.nix
|
||||
./issue.nix
|
||||
./nix.nix
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue