feat: add agenix rekey

This commit is contained in:
Adriano Caloiaro 2026-01-19 08:34:05 -08:00
parent e0c8b30fe2
commit 2315fbdb71
No known key found for this signature in database
11 changed files with 282 additions and 9 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
**/result
secrets/rekeyed

View file

@ -7,6 +7,24 @@ The contents of this repository are organized as follows:
- `systems`: These are complete nix modules for configuring specific systems. Unforutnately they're not "role based" systems, but explicit systems. I'd like to change that.
- `images`: This directory contains a flake for building SD card images. Ideally, SD card images could also be complete systems e.g. for building a pi sd card. However, cross-compiling issues have prevented me from doing so. For now, sd card images are phase 1 in and two-phase install process.
## Adding secrets
1. Encrypt the secret using the master key's public key
> echo "my-super-secret-password" | age -r age13sgljsr9srgxjxncl49qsn9dkkstcqct3ck9s7n2yu4lzelgp4uqcajgtj -o ./path/to/my-secret.age
2. Add the secret to your Nix configuration
```
age.secrets.my-secret = {
rekeyFile = ./secrets/my-secret.age; # Point to the file you just created
# mode = "600"; # Optional, default is 400
# owner = "root"; # Optional
};
```
3. Rekey the secrets. This will read the master key (via your gopass wrapper), decrypt my-secret.age, and re-encrypt it for specific hosts that use it
> nix run .#rekey
## Systems
### z1

View file

@ -14,6 +14,12 @@ in {
with types; {
enable = mkEnableOption "AI Agents";
githubPatPath = mkOption {
type = nullOr path;
default = null;
description = "Path to the decrypted file containing the GitHub Personal Access Token.";
};
mcpServers = mkOption {
description = "MCP Configurations";
type = attrs;
@ -22,6 +28,20 @@ in {
args = ["mcp-server-git" "--repository" "${config.home.homeDirectory}/proj/nixos-system"];
command = "uvx";
};
github =
if cfg.githubPatPath != null
then {
command = "${pkgs.bash}/bin/bash";
args = [
"-c"
"export GITHUB_PERSONAL_ACCESS_TOKEN=$(cat ${cfg.githubPatPath}) && exec ${pkgs.nodejs}/bin/npx -y @modelcontextprotocol/server-github"
];
}
else {
# Opencode is not yet compatible with using github's remote MCP server with oauth
type = "http";
url = "https://api.githubcopilot.com/mcp/";
};
glean = {
type = "http";
url = "https://greenhouse-be.glean.com/mcp/default";
@ -40,21 +60,18 @@ in {
home = {
packages = with pkgs; [
uv # for uvx
nodejs
];
};
programs = {
# aider-chat.enable = true;
# codex.enable = true;
opencode = {
enable = true;
enableMcpIntegration = true;
rules = ''
'';
settings = {
theme = "opencode";
# model = "anthropic/claude-sonnet-4-20250514";
theme = "nord";
autoshare = false;
autoupdate = true;
};

11
common/rekey.nix Normal file
View file

@ -0,0 +1,11 @@
{...}: {
age.rekey = {
masterIdentities = [
{
identity = "master.key";
pubkey = "age13sgljsr9srgxjxncl49qsn9dkkstcqct3ck9s7n2yu4lzelgp4uqcajgtj";
}
];
storageMode = "local";
};
}

5
common/secrets.nix Normal file
View file

@ -0,0 +1,5 @@
{...}: {
age.secrets.opencode-github-mcp-pat = {
rekeyFile = ./secrets/opencode-github-mcp-pat.age;
};
}

View file

@ -0,0 +1,6 @@
age-encryption.org/v1
-> X25519 qGmWsJ4WYLsV4N5P486vamO/L9v9VcB+9aUGrLTefiY
Ic1r0SEf8MDT77iwcmuUFRHUfqYZ87EWiGjCEKhF0+E
--- 0rHqnF7yMeiJgDP0oOzkJMEVdIiAhFiVJcCX/lewpMU
P±M[Œx{…F'\Ħ:LêÎ#R€Òh‡sªKÓkfüáchD˜
·zMÜúP<C3BA>>ŠØ×àQ{?—™²{Š#ˆ¢lA

View file

@ -23,13 +23,37 @@
"type": "github"
}
},
"btsw": {
"agenix-rekey": {
"inputs": {
"devshell": "devshell",
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1759699908,
"narHash": "sha256-kYVGY8sAfqwpNch706Fy2+/b+xbtfidhXSnzvthAhIQ=",
"owner": "oddlama",
"repo": "agenix-rekey",
"rev": "42362b12f59978aabf3ec3334834ce2f3662013d",
"type": "github"
},
"original": {
"owner": "oddlama",
"repo": "agenix-rekey",
"type": "github"
}
},
"btsw": {
"inputs": {
"devshell": "devshell_2",
"flake-parts": "flake-parts_2",
"nixpkgs": [
"nixpkgs"
],
"systems": "systems_2"
},
"locked": {
@ -84,6 +108,27 @@
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1728330715,
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
"owner": "numtide",
"repo": "devshell",
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"devshell_2": {
"inputs": {
"nixpkgs": "nixpkgs"
},
@ -143,7 +188,44 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
@ -161,7 +243,7 @@
"type": "github"
}
},
"flake-parts_2": {
"flake-parts_3": {
"inputs": {
"nixpkgs-lib": [
"nur",
@ -221,6 +303,28 @@
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"greenhouse-nix-modules": {
"inputs": {
"flake-utils": "flake-utils",
@ -563,7 +667,7 @@
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_2",
"flake-parts": "flake-parts_3",
"nixpkgs": "nixpkgs_6"
},
"locked": {
@ -580,9 +684,33 @@
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1735882644,
"narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "a5a961387e75ae44cc20f0a57ae463da5e959656",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"agenix": "agenix",
"agenix-rekey": "agenix-rekey",
"btsw": "btsw",
"default-browser": "default-browser",
"disko": "disko",
@ -742,6 +870,27 @@
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1735135567,
"narHash": "sha256-8T3K5amndEavxnludPyfj3Z1IkcFdRpR23q+T0BVeZE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "9e09d30a644c57257715902efbb3adc56c79cf28",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",

View file

@ -30,6 +30,10 @@
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
};
inputs.agenix-rekey = {
url = "github:oddlama/agenix-rekey";
inputs.nixpkgs.follows = "nixpkgs";
};
inputs.default-browser.url = "github:szympajka/nix-browser";
@ -71,6 +75,7 @@
};
outputs = {
self,
default-browser,
nixpkgs,
nixos-hardware,
@ -78,6 +83,7 @@
home-manager,
homeage,
agenix,
agenix-rekey,
nur,
kitty-grab,
helix,
@ -126,8 +132,34 @@
qutebrowser-overlay
];
};
# Wrapper to handle the dynamic master key
mkRekeyApp = system: pkgs: let
rekeyPackage = inputs.agenix-rekey.packages.${system}.default;
in {
type = "app";
program = "${pkgs.writeShellScript "rekey-wrapper" ''
# Create the file expected by the config
# We use the repo root context
REPO_ROOT=$(git rev-parse --show-toplevel)
MASTER_KEY_FILE="$REPO_ROOT/master.key"
# Cleanup on exit
trap 'rm -f "$MASTER_KEY_FILE"' EXIT
# Get the master key from gopass
echo "Retrieving master key from gopass..."
${pkgs.gopass}/bin/gopass show -o systems/age.master > "$MASTER_KEY_FILE"
chmod 600 "$MASTER_KEY_FILE"
# Run the actual rekey command
${rekeyPackage}/bin/agenix rekey "$@"
''}";
};
in {
inherit lib;
apps.${system}.rekey = mkRekeyApp system pkgs;
apps.${darwinSystem}.rekey = mkRekeyApp darwinSystem darwin-pkgs;
homeConfigurations = {
"adriano@zw" = lib.homeManagerConfiguration {
inherit pkgs;
@ -221,6 +253,8 @@
}
nur.modules.nixos.default
agenix.nixosModules.default
agenix-rekey.nixosModules.default
./common/rekey.nix
./systems/zw/configuration.nix
];
};
@ -232,6 +266,8 @@
{environment.systemPackages = [agenix.packages.x86_64-linux.default];}
nur.modules.nixos.default
agenix.nixosModules.default
agenix-rekey.nixosModules.default
./common/rekey.nix
inputs.disko.nixosModules.default
./systems/jellybee/configuration.nix
home-manager.nixosModules.home-manager
@ -253,6 +289,8 @@
{environment.systemPackages = [agenix.packages.x86_64-linux.default];}
nur.modules.nixos.default
agenix.nixosModules.default
agenix-rekey.nixosModules.default
./common/rekey.nix
./systems/homebee/configuration.nix
home-manager.nixosModules.home-manager
{
@ -273,6 +311,9 @@
modules = [
default-browser.darwinModules.default-browser
agenix.darwinModules.default
agenix-rekey.nixosModules.default
./common/rekey.nix
./systems/greenhouse/configuration.nix
inputs.greenhouse-nix-modules.nix-darwin.${system}
inputs.home-manager.darwinModules.home-manager
@ -286,5 +327,9 @@
}
];
};
agenix-rekey = inputs.agenix-rekey.configure {
userFlake = self;
nixosConfigurations = self.nixosConfigurations // self.darwinConfigurations;
};
};
}

View file

@ -1,9 +1,19 @@
{pkgs, ...}: let
{
pkgs,
inputs,
...
}: let
primaryUser = "adriano.caloiaro";
in {
imports = [
../../common/secrets.nix
];
config = {
age.secrets.opencode-github-mcp-pat.owner = "adriano.caloiaro";
age.rekey = {
hostPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINCARMVM8mwZBCFsnmr/hd0atFEj9oTOATzBajLGkS9V adriano.caloiaro@JJTH7GH17J";
localStorageDir = ./. + "/secrets/rekeyed/";
};
environment = {
etc = {
"dict.conf".text = "server dict.org";
@ -13,6 +23,8 @@ in {
NH_FLAKE = "/Users/${primaryUser}/proj/nixos-system";
};
systemPackages = with pkgs; [
age
inputs.agenix.packages.${pkgs.system}.default
gnupg
gopass
helix

View file

@ -18,6 +18,7 @@
ai-agents = {
enable = true;
crush.enable = true;
githubPatPath = "/run/agenix/opencode-github-mcp-pat";
};
modules.aerospace.enable = true;
programs = {

View file

@ -0,0 +1,8 @@
age-encryption.org/v1
-> ssh-ed25519 gSCyBg GF2cFn5yM9tpobmGGtbsyMx1ij7CqHaLv4G2BqdlH0c
6/lzyxeGW2Qd8Po3Ip2JPRpw74oom8t2Ld+pt1xqQRw
-> @=^/BY^-grease zz[nV *V_j svy3GEzY
LguZReDWJQDqZ9hE3V9WjSm+GKKzTFPBZQ6z8Ij/8YEGjVvKF1R+sUWwT1AQfC7V
HbKHsd744oyZ1y8rF6cS9QUHWUIMGJmXJk23lTCX
--- 1XXOAhgivbMgtoq5r0YUEXv7vcVr/vdE9HQrBaJSC/w
UŔmpOŤ+YÉÎąCŽv<C5BD>ŃV<C583>ďßR~7÷DĽÂ…‚ţąLŢp@Ž Ă)AGyďH$Ł`,Ť<>H* «/NYmăPŐšóň