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

@ -1,62 +1,62 @@
{ {
# https://github.com/drduh/config/blob/master/gpg.conf # https://github.com/drduh/config/blob/master/gpg.conf
# https://www.gnupg.org/documentation/manuals/gnupg/GPG-Configuration-Options.html # https://www.gnupg.org/documentation/manuals/gnupg/GPG-Configuration-Options.html
# https://www.gnupg.org/documentation/manuals/gnupg/GPG-Esoteric-Options.html # https://www.gnupg.org/documentation/manuals/gnupg/GPG-Esoteric-Options.html
# Use AES256, 192, or 128 as cipher # Use AES256, 192, or 128 as cipher
"personal-cipher-preferences" = "AES256 AES192 AES"; "personal-cipher-preferences" = "AES256 AES192 AES";
# Use SHA512, 384, or 256 as digest # Use SHA512, 384, or 256 as digest
"personal-digest-preferences" = "SHA512 SHA384 SHA256"; "personal-digest-preferences" = "SHA512 SHA384 SHA256";
# Use ZLIB, BZIP2, ZIP, or no compression # Use ZLIB, BZIP2, ZIP, or no compression
"personal-compress-preferences" = "ZLIB BZIP2 ZIP Uncompressed"; "personal-compress-preferences" = "ZLIB BZIP2 ZIP Uncompressed";
# Default preferences for new keys # Default preferences for new keys
"default-preference-list" = "SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed"; "default-preference-list" = "SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed";
# SHA512 as digest to sign keys # SHA512 as digest to sign keys
"cert-digest-algo" = "SHA512"; "cert-digest-algo" = "SHA512";
# SHA512 as digest for symmetric ops # SHA512 as digest for symmetric ops
"s2k-digest-algo" = "SHA512"; "s2k-digest-algo" = "SHA512";
# AES256 as cipher for symmetric ops # AES256 as cipher for symmetric ops
"s2k-cipher-algo" = "AES256"; "s2k-cipher-algo" = "AES256";
# UTF-8 support for compatibility # UTF-8 support for compatibility
"charset" = "utf-8"; "charset" = "utf-8";
# Show Unix timestamps # Show Unix timestamps
"fixed-list-mode" = true; "fixed-list-mode" = true;
# No comments in signature # No comments in signature
"no-comments" = true; "no-comments" = true;
# No version in signature # No version in signature
"no-emit-version" = true; "no-emit-version" = true;
# Disable banner # Disable banner
"no-greeting" = true; "no-greeting" = true;
# Long hexidecimal key format # Long hexidecimal key format
"keyid-format 0xlong" = true; "keyid-format 0xlong" = true;
# Display UID validity # Display UID validity
"list-options" = "show-uid-validity"; "list-options" = "show-uid-validity";
"verify-options" = "show-uid-validity"; "verify-options" = "show-uid-validity";
# Display all keys and their fingerprints # Display all keys and their fingerprints
"with-fingerprint" = true; "with-fingerprint" = true;
# Display key origins and updates # Display key origins and updates
#with-key-origin #with-key-origin
# Cross-certify subkeys are present and valid # Cross-certify subkeys are present and valid
"require-cross-certification" = true; "require-cross-certification" = true;
# Disable caching of passphrase for symmetrical ops # Disable caching of passphrase for symmetrical ops
"no-symkey-cache" = true; "no-symkey-cache" = true;
# Enable smartcard # Enable smartcard
"use-agent" = true; "use-agent" = true;
# Disable recipient key ID in messages # Disable recipient key ID in messages
"throw-keyids" = true; "throw-keyids" = true;
# Default/trusted key ID to use (helpful with throw-keyids) # Default/trusted key ID to use (helpful with throw-keyids)
#default-key 0xFF3E7D88647EBCDB #default-key 0xFF3E7D88647EBCDB
#trusted-key 0xFF3E7D88647EBCDB #trusted-key 0xFF3E7D88647EBCDB
# Group recipient keys (preferred ID last) # Group recipient keys (preferred ID last)
#group keygroup = 0xFF00000000000001 0xFF00000000000002 0xFF3E7D88647EBCDB #group keygroup = 0xFF00000000000001 0xFF00000000000002 0xFF3E7D88647EBCDB
# Keyserver URL # Keyserver URL
#keyserver hkps://keys.openpgp.org #keyserver hkps://keys.openpgp.org
#keyserver hkps://keyserver.ubuntu.com:443 #keyserver hkps://keyserver.ubuntu.com:443
#keyserver hkps://hkps.pool.sks-keyservers.net #keyserver hkps://hkps.pool.sks-keyservers.net
#keyserver hkps://pgp.ocf.berkeley.edu #keyserver hkps://pgp.ocf.berkeley.edu
# Proxy to use for keyservers # Proxy to use for keyservers
#keyserver-options http-proxy=socks5-hostname://127.0.0.1:9050 #keyserver-options http-proxy=socks5-hostname://127.0.0.1:9050
# Verbose output # Verbose output
#verbose #verbose
# Show expired subkeys # Show expired subkeys
#list-options show-unusable-subkeys #list-options show-unusable-subkeys
} }

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

@ -35,18 +35,16 @@
}; };
home.file.".ssh/1.pub".text = '' home.file.".ssh/1.pub".text = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDZixkix0KfKuq7Q19whS5FQQg51/AJGB5BiNF/7h/LM cardno:15 489 049 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDZixkix0KfKuq7Q19whS5FQQg51/AJGB5BiNF/7h/LM cardno:15 489 049
''; '';
home.file.".ssh/2.pub".text = '' home.file.".ssh/2.pub".text = ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxD4GOrwrBTG4/qQhm5hoSB2CP7W9g1LPWP11oLGOjQ cardno:23 010 997 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxD4GOrwrBTG4/qQhm5hoSB2CP7W9g1LPWP11oLGOjQ cardno:23 010 997
''; '';
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 = {
@ -28,18 +27,18 @@
enable = true; enable = true;
profiles.patrick = { profiles.patrick = {
userChrome = '' userChrome = ''
#TabsToolbar { #TabsToolbar {
visibility: collapse; visibility: collapse;
} }
#titlebar { #titlebar {
margin-bottom: !important; margin-bottom: !important;
} }
#titlebar-buttonbox { #titlebar-buttonbox {
height: 32px !important; height: 32px !important;
} }
''; '';
search.default = "DuckDuckGo"; search.default = "DuckDuckGo";
search.force = true; search.force = true;
}; };