1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-10 14:50:40 +02:00

feat: rekeying module finished, sandbox still flaky

This commit is contained in:
oddlama 2023-01-27 17:41:34 +01:00
parent 24a8795226
commit 92cf272cd1
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
6 changed files with 138 additions and 30 deletions

View file

@ -1,6 +1,7 @@
{
lib,
pkgs,
config,
...
}: let
dummyConfig = pkgs.writeText "configuration.nix" ''
@ -26,6 +27,14 @@ in {
services.udisks2.enable = false;
security.sudo.enable = false;
rekey.hostPubkey = ../../secrets/pubkeys + "/${config.networking.hostName}.pub";
rekey.hostPrivkey = lib.head (map (e: e.path) (lib.filter (e: e.type == "ed25519") config.services.openssh.hostKeys));
rekey.masterIdentityPaths = [../../secrets/yk1-nix-rage.pub];
rekey.agePlugins = with pkgs; [age-plugin-yubikey];
rekey.secrets.yolo.file = ./yolo.age;
environment.etc."YOLO" = config.rekey.secrets.yolo.path;
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;

View file

@ -5,38 +5,129 @@
pkgs,
...
}:
let
rekeySecrets = ageLikeSecrets: let
#srcs = map (x: x.file) age; [./secrets/backup.txt ./secrets/recipients.txt];
secretFiles = [ ../../secrets/backup.txt ../../secrets/recipients.txt ];
masterIdentityPaths = [ ../../secrets/yk1-nix-rage.txt ../../secrets/backup.txt ];
masterIdentities = builtins.concatStringsSep " " (map (x: "-i ${x}") masterIdentityPaths);
rekeyCommand = secret: ''
${pkgs.rage}/bin/rage -d ${masterIdentities} ${secret} \
| ${pkgs.rage}/bin/rage -e -i ${rekey.key} -o "$out/${builtins.baseNameOf secret}"
'';
rekeyedSecrets = pkgs.stdenv.mkDerivation {
name = "host-secrets";
dontUnpack = true;
dontConfigure = true;
dontBuild = true;
installPhase = ''
set -euo pipefail
mkdir "$out"
# Temporarily
${builtins.concatStringsSep "\n" (map rekeyCommand ageLikeSecrets)}
'';
};
in
rekeyedSecrets;
in {
with lib; {
config.environment.systemPackages = with pkgs; [rage];
# TODO age.identityPaths = [ (generateKeyForHost config.network.hostName) ];
config.age = {
secrets = let
rekeyedSecrets = lazyDerivation {
derivation = pkgs.stdenv.mkDerivation rec {
pname = "host-secrets";
version = "1.0.0";
description = "Rekeyed secrets for this host.";
# Produce a rekeyed age secret for each of the secrets defined in rekey secrets
options.rekey.secrets = options.age.secrets;
config.age.secrets = rekeySecrets config.rekey.secrets;
allSecrets = mapAttrsToList (_: value: value.file) config.rekey.secrets;
hostPubkeyStr =
if isPath config.rekey.hostPubkey
then readFile config.rekey.hostPubkey
else config.rekey.hostPubkey;
dontMakeSourcesWritable = true;
dontUnpack = true;
dontConfigure = true;
dontBuild = true;
installPhase = let
masterIdentityArgs = concatMapStrings (x: ''-i "${x}" '') config.rekey.masterIdentityPaths;
rekeyCommand = secret: ''
echo "Rekeying ${secret}" >&2
${pkgs.rage}/bin/rage ${masterIdentityArgs} -d ${secret}
| ${pkgs.rage}/bin/rage -r "${hostPubkeyStr}" -o "$out/${baseNameOf secret}" -e
'';
in ''
set -euo pipefail
mkdir "$out"
# Enable selected age plugins
export PATH="$PATH${concatMapStrings (x: ":${x}/bin") config.rekey.agePlugins}"
${concatStringsSep "\n" (map rekeyCommand allSecrets)}
'';
};
};
rekeyedSecretPath = secret: "${rekeyedSecrets}/${baseNameOf secret}";
in
# Produce a rekeyed age secret for each of the secrets defined in our secrets
mapAttrs (_: secret:
mapAttrs (name: value:
if name == "file"
then rekeyedSecretPath value
else value)
secret)
config.rekey.secrets;
identityPaths = mkForce config.rekey.agePubkey;
};
config.assertions = mkIf (config.rekey.secrets != {}) [
{
assertion = pathExists config.rekey.hostPubkey;
message = "The public key required to rekey secrets for this host doesn't exist. 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.";
}
];
config.warnings = let
hasGoodSuffix = x: strings.hasSuffix ".age" x || strings.hasSuffix ".pub" x;
in
mkIf (!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.hostPrivkey = mkOption {
# Str to prevent privkeys from entering the nix store
type = types.str;
description = ''
The age identity (private key) that should be used to decrypt the secrets on the target machine.
This corresponds to age.identityPaths and must match the pubkey set in rekey.hostPubkey.
'';
example = head (map (e: e.path) (filter (e: e.type == "ed25519") config.services.openssh.hostKeys));
};
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];
};
};
}
#rekey.secrets.my_secret.file = ./secrets/somekey.age;
#pwdfile = rekey.secrets.mysecret.path;

7
modules/core/yolo.age Normal file
View file

@ -0,0 +1,7 @@
age-encryption.org/v1
-> X25519 saA0VOfRsGeQ7wIJ4SF80v3rvmf7rRbZcxTbRbWrRn4
NNs7fIU09lYiKZ1R5KE3fJhHM1MlNxC7H6Cz2oAOyus
-> piv-p256 xqSe8Q AgPVLX/r+nz6PWZFJW90BcPVU9kFrLV1PZge80Fyd0og
OGvcmxsXWqwxDOGUy9Slw7zEkgeiHblldbYw4UilPC0
--- wi4PRgJNO6O1TgA3qobLX0wP14vreWRccvuR9wWNN+k
@…�RFJ[½®—(I‡ªu½¯mLSÂ¨Š¾«|P&þð2ú—ævñŠŽ’×2ÓÙ(àüW¦�I^ÞR:¿p�|Zty0óÊ�?ãìivAÛ…§©$ˆ¤Œ‘ÆèÍZZüMÂ�LÂpÿÒÏ Éi›¥ë ø„½š<û‹àQÕÙ

1
secrets/pubkeys/nom.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICOdYhY/DnXpizajoeLefH6gsc/RX9x3Y6T3C1a+0sb0