WIP: rekey module to rekey all secrets using the yubikey

Work apart from interactivity. Pins are thus currently unsopported
Will be supperseeded by a flake runable to rekey secrets
on demand
This commit is contained in:
Patrick Großmann 2023-01-28 02:50:14 +01:00
parent f355c527ee
commit 4fa6cc7d79
Signed by: patrick
GPG key ID: 451F95EFB8BECD0F
17 changed files with 501 additions and 327 deletions

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# Meine wundervolle nix config
For secrets:
- encrypt using: `rage -R recipients.txt -o [OUT] -e [IN] `
- decrypt using: `rage -R recipients.txt -o [OUT] -d [IN] `

View file

@ -4,7 +4,7 @@
{ {
config, config,
pkgs, pkgs,
age, lib,
... ...
}: { }: {
imports = [ imports = [
@ -14,6 +14,7 @@
./users ./users
# #
./modules/pipewire.nix ./modules/pipewire.nix
./modules/rekey.nix
]; ];
# Use the systemd-boot EFI boot loader. # Use the systemd-boot EFI boot loader.
@ -22,20 +23,24 @@
networking.hostName = "patricknix"; # Define your hostname. networking.hostName = "patricknix"; # Define your hostname.
networking.hostId = "68438432"; networking.hostId = "68438432";
# Pick only one of the below networking options.
# Identities with which all secrets are encrypted
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; networking.wireless.iwd.enable = true;
age.identityPaths = [ ./secrets/NIXOSc.key ./secrets/NIXOSa.key ]; rekey.secrets.eduroam = {
age.plugins = [ pkgs.age-plugin-yubikey ];
age.secrets.eduroam = {
file = ./secrets/iwd/eduroam.8021x.age; file = ./secrets/iwd/eduroam.8021x.age;
path = "/etc/iwd/eduroam.8021x"; path = "/etc/iwd/eduroam.8021x";
}; };
age.secrets.devoloog = { rekey.secrets.devoloog = {
file = ./secrets/iwd/devolo-og.psk.age; file = ./secrets/iwd/devolo-og.psk.age;
path = "/etc/iwd/devolo-og.psk"; path = "/etc/iwd/devolo-og.psk";
}; };
networking.useNetworkd = true; networking.useNetworkd = true;
networking.dhcpcd.enable = false; networking.dhcpcd.enable = false;
systemd.network.wait-online.anyInterface = true; systemd.network.wait-online.anyInterface = true;
@ -139,6 +144,9 @@
}; };
hostKeys = [ hostKeys = [
{ {
# never set this to an actual nix type path
# or else .....
# it will end up in the nix store
path = "/etc/ssh/ssh_host_ed25519_key"; path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519"; type = "ed25519";
} }
@ -196,6 +204,10 @@
]; ];
cores = 0; cores = 0;
max-jobs = "auto"; max-jobs = "auto";
# If the yubikey is needed for rekeying my secrets the sandbox need acces to the pcscd daemon socket
# TODO only give the one derivation access to this path
extra-sandbox-paths = lib.mkIf (lib.elem pkgs.age-plugin-yubikey config.rekey.plugins) ["/run/pcscd/"];
}; };
daemonCPUSchedPolicy = "batch"; daemonCPUSchedPolicy = "batch";
daemonIOSchedPriority = 5; daemonIOSchedPriority = 5;

View file

@ -7,15 +7,15 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1674681075, "lastModified": 1673301561,
"narHash": "sha256-hXbIv9WHHEQvoXtK4hWKx4EzmTLUzMdjV8e/x/R9nP8=", "narHash": "sha256-gRUWHbBAtMuPDJQXotoI8u6+3DGBIUZHkyQWpIv7WpM=",
"owner": "oddlama", "owner": "ryantm",
"repo": "agenix", "repo": "agenix",
"rev": "12d1b138188dda50704c2816be73d6e183f45797", "rev": "42d371d861a227149dc9a7e03350c9ab8b8ddd68",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "oddlama", "owner": "ryantm",
"repo": "agenix", "repo": "agenix",
"type": "github" "type": "github"
} }
@ -28,11 +28,11 @@
"utils": "utils" "utils": "utils"
}, },
"locked": { "locked": {
"lastModified": 1674556204, "lastModified": 1674771519,
"narHash": "sha256-HCRmkZsq01h2Evch08zpgE9jeHdMtGdT1okWotyvuhY=", "narHash": "sha256-U0W3S1nX6yEvLh3Vq70EORbmXecAKXfmEfCfaA4A+I8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "c59f0eac51da91c6989fd13a68e156f63c0e60b6", "rev": "bb4b25b302dbf0f527f190461b080b5262871756",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -43,11 +43,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1674459583, "lastModified": 1674641431,
"narHash": "sha256-L0UZl/u2H3HGsrhN+by42c5kNYeKtdmJiPzIRvEVeiM=", "narHash": "sha256-qfo19qVZBP4qn5M5gXc/h1MDgAtPA5VxJm9s8RUAkVk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1b1f50645af2a70dc93eae18bfd88d330bfbcf7f", "rev": "9b97ad7b4330aacda9b2343396eb3df8a853b4fc",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,17 +1,27 @@
{ {
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; inputs = {
inputs.home-manager = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager"; url = "github:nix-community/home-manager";
# should use system nixpkgs instead of their own # should use system nixpkgs instead of their own
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
inputs.agenix.url = "github:oddlama/agenix"; agenix = {
inputs.agenix.inputs.nixpkgs.follows = "nixpkgs"; url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, agenix, ... }: let outputs = {
self,
nixpkgs,
home-manager,
agenix,
...
}: let
system = "x86_64-linux"; system = "x86_64-linux";
in {nixosConfigurations.patricknix = in {
nixpkgs.lib.nixosSystem { nixosConfigurations.patricknix = nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = [ modules = [
./configuration.nix ./configuration.nix
@ -21,6 +31,13 @@
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
} }
agenix.nixosModule agenix.nixosModule
{
nix.registry = {
nixpkgs.flake = nixpkgs;
p.flake = nixpkgs;
pkgs.flake = nixpkgs;
};
}
]; ];
}; };
}; };

1
keys/patricknix.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJrr6bJgWzCuS+00EEBQRoylwput69tqvotgPjSF5xhz root@patricknix

134
modules/rekey.nix Normal file
View file

@ -0,0 +1,134 @@
{
lib,
config,
pkgs,
stdenv,
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)}
'';
};
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
];
age = {
secrets = let
newPath = x: "${rekeyedSecrets}/${builtins.baseNameOf x}";
in
builtins.mapAttrs (_:
builtins.mapAttrs (name: value:
if name == "file"
then "${newPath value}"
else value))
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; {
rekey.secrets = options.age.secrets;
rekey.pubKey = mkOption {
type = types.either types.path types.str;
description = ''
The age public key set as a recipient when rekeying.
either a path to a public key file or a string public key
**NEVER set this to a private key part**
~~This will end up in the nix store.~~
'';
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
'';
};
rekey.plugins = mkOption {
type = types.listOf types.package;
default = [];
description = ''
A list of plugins that should be available in your path when rekeying.
'';
example = [pkgs.age-plugin-yubikey];
};
};
}

Binary file not shown.

Binary file not shown.

6
secrets/recipients.txt Normal file
View file

@ -0,0 +1,6 @@
age1faus9en5ywxc69rewmjvz63vqpv5n08f4w7qsd97k6mldd8avqks52ghyl
# Backup Key
age1yubikey1q2w0nrz60e75shexudc0s3j8n4kggdp87cjzejvc6mzzge5h5yp9sj6sqk5
# yubikey A
age1yubikey1qfu3708kl2anypfzas7mn78z5rqnqpy0ffmg9hqn8uxlgcws5r9czuqs6y7
# yubikey C

View file

@ -1,13 +1,14 @@
{config,pkgs,...}:
{ {
programs.autorandr = config,
let pkgs,
...
}: {
programs.autorandr = let
dpi_hd = 96; dpi_hd = 96;
dpi_uhd = 192; dpi_uhd = 192;
set_dpi = dpi: "echo 'Xft.dpi: ${toString dpi}' | ${pkgs.xorg.xrdb}/bin/xrdb -merge"; set_dpi = dpi: "echo 'Xft.dpi: ${toString dpi}' | ${pkgs.xorg.xrdb}/bin/xrdb -merge";
eDP-1 = "00ffffffffffff0006afeb3000000000251b0104a5221378020925a5564f9b270c50540000000101010101010101010101010101010152d000a0f0703e803020350058c11000001852d000a0f07095843020350025a51000001800000000000000000000000000000000000000000002001430ff123caa8f0e29aa202020003e"; eDP-1 = "00ffffffffffff0006afeb3000000000251b0104a5221378020925a5564f9b270c50540000000101010101010101010101010101010152d000a0f0703e803020350058c11000001852d000a0f07095843020350025a51000001800000000000000000000000000000000000000000002001430ff123caa8f0e29aa202020003e";
in in {
{
enable = true; enable = true;
profiles.AStA = { profiles.AStA = {
fingerprint = { fingerprint = {

View file

@ -42,11 +42,9 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxD4GOrwrBTG4/qQhm5hoSB2CP7W9g1LPWP11oLGOjQ
''; '';
programs.ssh = { programs.ssh = {
enable = true; enable = true;
matchBlocks = matchBlocks = let
let
identityFile = ["~/.ssh/1.pub" "~/.ssh/2.pub"]; identityFile = ["~/.ssh/1.pub" "~/.ssh/2.pub"];
in in {
{
"elisabeth" = { "elisabeth" = {
hostname = "lel.lol"; hostname = "lel.lol";
user = "root"; user = "root";
@ -107,5 +105,4 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxD4GOrwrBTG4/qQhm5hoSB2CP7W9g1LPWP11oLGOjQ
signByDefault = true; signByDefault = true;
}; };
}; };
} }

View file

@ -1,5 +1,8 @@
{ config,pkgs,...}:
{ {
config,
pkgs,
...
}: {
programs.zsh = { programs.zsh = {
enable = true; enable = true;
initExtra = builtins.readFile ../../data/zsh/zshrc; initExtra = builtins.readFile ../../data/zsh/zshrc;

View file

@ -2,8 +2,7 @@
config, config,
home-manager, home-manager,
... ...
}: }: {
{
home-manager.users.patrick.imports = [./patrick.nix]; home-manager.users.patrick.imports = [./patrick.nix];
home-manager.users.root = { home-manager.users.root = {
imports = [./common]; imports = [./common];

View file

@ -9,7 +9,6 @@
common/autorandr.nix common/autorandr.nix
common/desktop.nix common/desktop.nix
./common ./common
]; ];
home = { home = {