feat: rekey funktioniert.

This commit is contained in:
Patrick Großmann 2023-01-28 18:41:31 +01:00
parent 4fa6cc7d79
commit 6666d56b65
Signed by: patrick
GPG key ID: 451F95EFB8BECD0F
6 changed files with 154 additions and 121 deletions

79
apps/rekey.nix Normal file
View file

@ -0,0 +1,79 @@
{
self,
nixpkgs,
...
}: system:
with nixpkgs.lib; let
pkgs = import nixpkgs {inherit system;};
rekeyCommandForHost = hostName: hostAttrs: let
secretPath = "/tmp/nix-rekey.d/${hostName}/";
masterIdentities = strings.concatMapStrings (x: "-i ${x} ") hostAttrs.config.rekey.masterIdentityPaths;
rekeyCommand = secretName: secretAttrs: let
pubKeyStr = let
key = hostAttrs.config.rekey.pubKey;
in
if isPath key
then readFile key
else key;
in ''
echo "Rekeying secret ${secretName} for host ${hostName}"
echo "${secretAttrs.file}"
${pkgs.rage}/bin/rage ${masterIdentities} -d ${secretAttrs.file} \
| ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "${secretPath}/${secretName}.age" -e \
|| { echo "Could not rekey secrets. Inserting dummy values" \
; echo "Invalide due to failure when rekeying." \
| ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "${secretPath}/${secretName}.age" -e ;}
'';
in
if masterIdentities == ""
then ''
echo "No Identities set for host ${hostName}. Can not decrypt.\n\
Make sure you set 'config.rekey.masterIdentityPaths'"
''
else if
let
key = hostAttrs.config.rekey.pubKey;
in
isPath key && (! pathExists key)
then ''
echo "No public keys available for host ${hostName}. Can not decrypt.\n\
Make sure the public keys are reachable by the building system'"
''
else ''
mkdir -p ${secretPath}
# TODO
${concatStringsSep "\n" (mapAttrsToList rekeyCommand (hostAttrs.config.rekey.secrets))}
nix run --extra-sandbox-paths /tmp/nix-rekey.d/ "${../.}#rekey-copy-secrets"
'';
rekeyScript = ''
set -euo pipefail
${concatStringsSep "\n" (mapAttrsToList rekeyCommandForHost self.nixosConfigurations)}
'';
rekey-exe = pkgs.writeShellScript "rekey.sh" rekeyScript;
rekey-copy-secretsForHost = hostName: hostAttrs: let
secretFiles = mapAttrsToList (_: x: x.file) hostAttrs.config.rekey.secrets;
drv = import ../modules/rekey-drv.nix pkgs secretFiles;
in ''
echo ${drv}
'';
rekey-copy-secrets-exe = pkgs.writeShellScript "rekey-copy-secrets.sh" ''
${concatStringsSep "\n" (mapAttrsToList rekey-copy-secretsForHost self.nixosConfigurations)}
'';
in {
rekey = {
type = "app";
program = "${rekey-exe}";
};
rekey-copy-secrets = {
type = "app";
program = "${rekey-copy-secrets-exe}";
};
}

View file

@ -28,7 +28,6 @@
rekey.masterIdentityPaths = [./secrets/NIXOSc.key ./secrets/NIXOSa.key];
rekey.pubKey = ./keys + "/${config.networking.hostName}.pub";
rekey.privKey = "/etc/ssh/ssh_host_ed25519_key";
rekey.plugins = [pkgs.age-plugin-yubikey];
networking.wireless.iwd.enable = true;
@ -87,17 +86,8 @@
#TODO sollte nur bestimmte packages sein nicht alle
nixpkgs.config.allowUnfree = true;
# services.xserver.xkbOptions = {
# "eurosign:e";
# "caps:escape" # map caps to escape.
# };
# Enable CUPS to print documents.
# services.printing.enable = true;
# Enable sound.
# sound.enable = true;
# hardware.pulseaudio.enable = true;
powerManagement.powertop.enable = true;
# Define a user account. Don't forget to set a password with passwd.
users.users.patrick = {

View file

@ -20,6 +20,21 @@
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@ -60,6 +75,7 @@
"root": {
"inputs": {
"agenix": "agenix",
"flake-utils": "flake-utils",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs"
}

View file

@ -10,6 +10,8 @@
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
@ -17,10 +19,12 @@
nixpkgs,
home-manager,
agenix,
flake-utils,
...
}: let
} @ inputs: let
system = "x86_64-linux";
in {
in
{
nixosConfigurations.patricknix = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
@ -40,5 +44,8 @@
}
];
};
};
}
// flake-utils.lib.eachSystem [system] (localSystem: {
apps = import ./apps/rekey.nix inputs localSystem;
});
}

20
modules/rekey-drv.nix Normal file
View file

@ -0,0 +1,20 @@
pkgs: secretFiles:
(
pkgs.stdenv.mkDerivation rec {
pname = "host-secrets";
version = "1";
description = "Rekeyed secrets for this host";
srcs = secretFiles;
sourceRoot = ".";
dontMakeSourcesWriteable = true;
dontUnpack = true;
dontConfigure = true;
dontBuild = true;
installPhase = ''
cp -r /tmp/nix-rekey.d/ $out
'';
}
)

View file

@ -6,88 +6,19 @@
options,
...
}: {
# TODO add a with lib um mir die ganzen lib. zu ersparen
config = let
masterIdentities = lib.strings.concatMapStrings (x: "-i ${x} ") config.rekey.masterIdentityPaths;
rekeyedSecrets = pkgs.stdenv.mkDerivation rec {
pname = "age-rekey";
version = "1.0.0";
allSecrets = lib.mapAttrsToList (_: x: x.file) config.rekey.secrets;
pubKeyStr =
if builtins.isPath config.rekey.pubKey
then builtins.readFile config.rekey.pubKey
else config.rekey.pubKey;
dontMakeSourceWriteable = 1;
dontUnpack = true;
dontPatch = true;
dontConfigure = true;
dontBuild = true;
installPhase = let
pluginPaths = lib.strings.concatMapStrings (x: ":${x}/bin") config.rekey.plugins;
rekeyCommand = secret: ''
echo "Rekeying secret ${secret}" >&2
${pkgs.rage}/bin/rage ${masterIdentities} -d ${secret} \
| ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "$out/${builtins.baseNameOf secret}" -e \
|| { echo 1 > "$out"/status; echo "disabled due to failure in rekey.nix" | ${pkgs.rage}/bin/rage -r "${pubKeyStr}" -o "$out/${builtins.baseNameOf secret}" -e ;}
'';
in ''
set -euo pipefail
mkdir $out
echo 0 > "$out"/status
export PATH=$PATH${pluginPaths}
${lib.concatStringsSep "\n" (map rekeyCommand allSecrets)}
'';
};
config = with lib; let
secretFiles = mapAttrsToList (_: x: x.file) config.rekey.secrets;
drv = import ./rekey-drv.nix pkgs secretFiles;
in
lib.mkIf (config.rekey.secrets != {}) {
# Polkit rule to enable the build process to access the keys saved on a yubikey
# This rule allows any user named nixbld<num> to accesst pcscd
security.polkit.extraConfig = lib.mkIf (lib.elem pkgs.age-plugin-yubikey config.rekey.plugins) ''
polkit.addRule(function(action, subject) {
if ((action.id == "org.debian.pcsc-lite.access_pcsc" || action.id == "org.debian.pcsc-lite.access_card") &&
subject.user.match(/^nixbld\d+$/)) {
return polkit.Result.YES;
}
});
'';
environment.systemPackages = with pkgs; [
rage
];
mkIf (config.rekey.secrets != {}) {
age = {
secrets = let
newPath = x: "${rekeyedSecrets}/${builtins.baseNameOf x}";
hostName = config.networking.hostName;
secretPath = "${drv}/${hostName}/";
newPath = x: "${secretPath}/${x}.age";
in
builtins.mapAttrs (_:
builtins.mapAttrs (name: value:
if name == "file"
then "${newPath value}"
else value))
config.rekey.secrets;
mapAttrs (name: value: value // {file = newPath name;}) config.rekey.secrets;
};
assertions = [
{
assertion = builtins.pathExists config.rekey.pubKey;
message = "Did not find key file: ${config.rekey.pubKey}.
Make sure your public key is available for rekeying.";
}
{
assertion = config.rekey.masterIdentityPaths != [];
message = "rekey.masterIdentityPaths must be set!";
}
];
warnings =
lib.optional (builtins.any (x: !(lib.strings.hasSuffix ".pub" x || lib.strings.hasSuffix ".age" x)) config.rekey.masterIdentityPaths) ''
It seems at least one of your master masterIdentities files is not encrypted or not a public handle.
Please make sure it does not contain any secret Information.
''
++ lib.optional (lib.toInt (builtins.readFile "${rekeyedSecrets}/status") == 1) ''
Could not rekey. Might be due to a chicken/egg problem, then a retry will fix this.
'';
};
options = with lib; {
@ -103,22 +34,12 @@
example = /etc/ssh/ssh_host_ed25519_key.pub;
};
rekey.privKey = mkOption {
type = types.str;
description = ''
The age private key part, corresponding to the public key set in "rekey.pubKey".
Used by agenix for decryption.
Preferably set this to your ed25519 host key.
'';
example = "/etc/ssh/ssh_host_ed25519_key";
};
rekey.masterIdentityPaths = mkOption {
type = types.listOf types.path;
description = ''
A list of Identities used for decrypting your secrets before rekeying.
**WARING this will end up in the nix-store**
Only use yubikeys or encrypted age keys
Only use yubikeys or password encrypted age keys
'';
};