forked from mirrors_public/oddlama_nix-config
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": {
|
"agenix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"ragenix",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665870395,
|
"lastModified": 1675030834,
|
||||||
"narHash": "sha256-Tsbqb27LDNxOoPLh0gw2hIb6L/6Ow/6lIBvqcHzEKBI=",
|
"narHash": "sha256-e1/7Z7rVRqy2NuEOxrRm560wc/Kn8NU7gz8CDfmu9F0=",
|
||||||
"owner": "ryantm",
|
"owner": "ryantm",
|
||||||
"repo": "agenix",
|
"repo": "agenix",
|
||||||
"rev": "a630400067c6d03c9b3e0455347dc8559db14288",
|
"rev": "49798e535ebc07fec82256b283d35be36d8c6c9a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -21,6 +20,32 @@
|
||||||
"type": "github"
|
"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": {
|
"deploy-rs": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": [
|
"flake-compat": [
|
||||||
|
@ -214,7 +239,9 @@
|
||||||
},
|
},
|
||||||
"ragenix": {
|
"ragenix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"agenix": "agenix",
|
"agenix": [
|
||||||
|
"agenix"
|
||||||
|
],
|
||||||
"flake-utils": [
|
"flake-utils": [
|
||||||
"flake-utils"
|
"flake-utils"
|
||||||
],
|
],
|
||||||
|
@ -239,6 +266,8 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"agenix": "agenix",
|
||||||
|
"agenix-rekey": "agenix-rekey",
|
||||||
"deploy-rs": "deploy-rs",
|
"deploy-rs": "deploy-rs",
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
|
|
13
flake.nix
13
flake.nix
|
@ -33,8 +33,19 @@
|
||||||
inputs.flake-compat.follows = "flake-compat";
|
inputs.flake-compat.follows = "flake-compat";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
agenix = {
|
||||||
|
url = "github:ryantm/agenix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
ragenix = {
|
ragenix = {
|
||||||
url = "github:yaxitech/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.flake-utils.follows = "flake-utils";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
@ -76,7 +87,5 @@
|
||||||
];
|
];
|
||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
apps = {} // import ./apps/rekey.nix inputs localSystem;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
'';
|
'';
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./rekey.nix
|
|
||||||
./inputrc.nix
|
./inputrc.nix
|
||||||
./issue.nix
|
./issue.nix
|
||||||
./nix.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,
|
nixos-hardware,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
ragenix,
|
ragenix,
|
||||||
|
agenix-rekey,
|
||||||
templates,
|
templates,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
|
@ -32,9 +33,10 @@
|
||||||
nixpkgs.hostPlatform = hostPlatform;
|
nixpkgs.hostPlatform = hostPlatform;
|
||||||
}
|
}
|
||||||
nixRegistry
|
nixRegistry
|
||||||
home-manager.nixosModules.home-manager
|
home-manager.nixosModules.default
|
||||||
#impermanence.nixosModules.impermanence
|
#impermanence.nixosModules.default
|
||||||
ragenix.nixosModules.age
|
ragenix.nixosModules.default
|
||||||
|
agenix-rekey.nixosModules.default
|
||||||
];
|
];
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
#impermanence = impermanence.nixosModules;
|
#impermanence = impermanence.nixosModules;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue