Add agenix secrets management

Phase 3: Encrypted secrets

- Add secrets module with agenix integration
- Create secrets/secrets.nix template for key definitions
- Installer generates SSH key if missing
- Installer creates personalized secrets.nix with user's key
- Full documentation in docs/SECRETS.md

Features:
- Secrets encrypted with age using SSH keys
- Decrypted automatically at system activation
- Safe to commit .age files to git
- Support for WiFi passwords, API keys, service credentials

Usage:
  agenix -e secrets/my-secret.age
  age.secrets.my-secret.file = ./secrets/my-secret.age;

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 02:56:25 -05:00
parent 6686a9f6b6
commit 5a52b3c159
6 changed files with 297 additions and 0 deletions

View File

@@ -29,6 +29,7 @@ Download the ISO from the releases page and boot from it for a fresh installatio
- **Classical Theme** - Earthy, vintage aesthetic inspired by historical paintings - **Classical Theme** - Earthy, vintage aesthetic inspired by historical paintings
- **Bootloader Choice** - systemd-boot (default) or Limine (prettier, more features) - **Bootloader Choice** - systemd-boot (default) or Limine (prettier, more features)
- **Plymouth** - Optional boot splash screen - **Plymouth** - Optional boot splash screen
- **Secrets Management** - Encrypted secrets with agenix (WiFi passwords, API keys, etc.)
## Keybindings ## Keybindings
@@ -85,6 +86,21 @@ extraModules = [
Copy your wallpapers to `~/.config/nomarchy/wallpapers/` and they'll be used for the random rotation. Copy your wallpapers to `~/.config/nomarchy/wallpapers/` and they'll be used for the random rotation.
## Secrets Management
Nomarchy uses [agenix](https://github.com/ryantm/agenix) for encrypted secrets:
```bash
# Create a secret
cd ~/.config/nomarchy
agenix -e secrets/wifi-password.age
# Use in your config
age.secrets.wifi-password.file = ./secrets/wifi-password.age;
```
See [docs/SECRETS.md](docs/SECRETS.md) for full documentation.
## Structure ## Structure
``` ```

149
docs/SECRETS.md Normal file
View File

@@ -0,0 +1,149 @@
# Secrets Management with agenix
Nomarchy uses [agenix](https://github.com/ryantm/agenix) for managing encrypted secrets. Secrets are encrypted with age using SSH public keys and decrypted at system activation.
## How It Works
1. **Secrets are encrypted** with your SSH public key(s)
2. **Stored in git** as `.age` files (safe to commit)
3. **Decrypted at boot** using the host's SSH private key
4. **Available at runtime** in `/run/agenix/`
## Quick Start
### 1. Get Your SSH Public Key
```bash
cat ~/.ssh/id_ed25519.pub
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host
```
### 2. Get the Host's SSH Public Key
After first boot:
```bash
ssh-keyscan localhost 2>/dev/null | grep ed25519
```
Or from your hardware-configuration.nix's host:
```bash
ssh-keyscan <hostname> 2>/dev/null | grep ed25519
```
### 3. Edit secrets/secrets.nix
```nix
let
user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...";
host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...";
allKeys = [ user host ];
in {
"wifi-home.age".publicKeys = allKeys;
"github-token.age".publicKeys = allKeys;
}
```
### 4. Create a Secret
```bash
cd ~/.config/nomarchy
agenix -e secrets/wifi-home.age
```
This opens your editor. Enter the secret content, save, and exit.
### 5. Use the Secret in Your Config
```nix
# In your custom module
{ config, ... }: {
age.secrets.wifi-home = {
file = ./secrets/wifi-home.age;
owner = "root";
mode = "0400";
};
# Use the decrypted secret
networking.wireless.networks."MyNetwork".pskRaw =
"file:${config.age.secrets.wifi-home.path}";
}
```
## Common Use Cases
### WiFi Passwords
```nix
age.secrets.wifi-password.file = ./secrets/wifi.age;
# For NetworkManager (wpa_supplicant)
networking.wireless.networks."NetworkName".psk =
builtins.readFile config.age.secrets.wifi-password.path;
```
### API Keys
```nix
age.secrets.github-token = {
file = ./secrets/github-token.age;
owner = "youruser";
mode = "0400";
};
# In your shell config
home.sessionVariables.GITHUB_TOKEN =
"$(cat ${config.age.secrets.github-token.path})";
```
### Service Credentials
```nix
age.secrets.syncthing-key = {
file = ./secrets/syncthing-key.age;
owner = "youruser";
};
services.syncthing.key = config.age.secrets.syncthing-key.path;
```
## Re-keying Secrets
If you change hosts or keys, re-encrypt all secrets:
```bash
cd ~/.config/nomarchy
agenix --rekey
```
## Security Notes
- **Never commit unencrypted secrets**
- Keep your SSH private key secure
- The host's SSH key is generated during install
- Secrets are decrypted to a tmpfs (`/run/agenix/`)
- Consider using separate keys for different security levels
## Troubleshooting
### "No identity found"
Ensure `/etc/ssh/ssh_host_ed25519_key` exists and is readable by root.
### "Failed to decrypt"
Check that the host's public key is in `secrets.nix` and re-encrypt:
```bash
agenix --rekey
```
### "Secret file not found"
Make sure the `.age` file exists and the path in your config is correct.
## File Structure
```
~/.config/nomarchy/
├── secrets/
│ ├── secrets.nix # Key definitions
│ ├── wifi.age # Encrypted WiFi password
│ └── github.age # Encrypted API key
└── flake.nix
```

View File

@@ -271,6 +271,53 @@ download_wallpapers() {
echo "" echo ""
} }
setup_secrets() {
local secrets_dir="$HOME/.config/nomarchy/secrets"
mkdir -p "$secrets_dir"
echo -e "${BLUE}Setting up secrets management...${NC}"
# Check for SSH key
if [ ! -f "$HOME/.ssh/id_ed25519.pub" ]; then
echo -e "${YELLOW}No SSH key found. Generating one...${NC}"
ssh-keygen -t ed25519 -f "$HOME/.ssh/id_ed25519" -N "" -C "${username}@${hostname_input}"
fi
local user_key
user_key=$(cat "$HOME/.ssh/id_ed25519.pub")
# Create secrets.nix template
cat > "$secrets_dir/secrets.nix" << EOF
# Nomarchy Secrets Configuration
# Generated on $(date)
#
# To add a secret:
# 1. Add it to this file with the keys below
# 2. Run: agenix -e secrets/<name>.age
# 3. Reference in your NixOS config
let
# Your SSH public key (for encrypting)
user = "${user_key}";
# Host SSH key - add after first boot:
# Run: ssh-keyscan localhost 2>/dev/null | grep ed25519
# host = "ssh-ed25519 AAAA...";
allKeys = [ user ];
# After adding host key: allKeys = [ user host ];
in {
# Example secrets (uncomment to use):
# "wifi-password.age".publicKeys = allKeys;
# "api-key.age".publicKeys = allKeys;
}
EOF
echo -e "${GREEN}Secrets directory created at ${secrets_dir}${NC}"
echo -e "${YELLOW}After first boot, add your host's SSH key to secrets.nix${NC}"
echo ""
}
show_summary() { show_summary() {
echo -e "${BOLD}Configuration Summary${NC}" echo -e "${BOLD}Configuration Summary${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -347,6 +394,7 @@ main() {
generate_config generate_config
setup_flake setup_flake
download_wallpapers download_wallpapers
setup_secrets
apply_config apply_config
echo "" echo ""

View File

@@ -6,5 +6,6 @@
./services ./services
./programs ./programs
./performance ./performance
./secrets
]; ];
} }

View File

@@ -0,0 +1,42 @@
# Secrets management with agenix
# Secrets are encrypted with age and decrypted at system activation
{
config,
lib,
pkgs,
inputs,
nomarchyConfig,
...
}: let
# Check if secrets directory exists and has secrets
secretsPath = ../../secrets;
hasSecrets = builtins.pathExists secretsPath;
in {
# Import agenix module
imports = lib.optionals hasSecrets [
inputs.agenix.nixosModules.default
];
config = lib.mkIf hasSecrets {
# Install agenix CLI for managing secrets
environment.systemPackages = [
inputs.agenix.packages.${pkgs.system}.default
];
# Configure age to use SSH host keys for decryption
age.identityPaths = [
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_rsa_key"
];
# Example secrets configuration (users override in their config)
# age.secrets = {
# wifi-password = {
# file = ../../secrets/wifi-password.age;
# owner = "root";
# group = "root";
# mode = "0400";
# };
# };
};
}

41
secrets/secrets.nix Normal file
View File

@@ -0,0 +1,41 @@
# Secrets configuration for agenix
#
# This file defines which public keys can decrypt each secret.
# Secrets are encrypted with `agenix -e <secret>.age`
#
# To set up:
# 1. Get your user's SSH public key: cat ~/.ssh/id_ed25519.pub
# 2. Get the host's SSH public key: ssh-keyscan localhost 2>/dev/null | grep ed25519
# 3. Add keys below and run: agenix -e <secret>.age
let
# User SSH public keys (for encrypting secrets on your machine)
# Example: user = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host";
# Host SSH public keys (for decrypting on target machines)
# Get with: ssh-keyscan <hostname> | grep ed25519
# Example: host = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...";
# Define your keys here:
# user = "ssh-ed25519 AAAAC3...";
# host = "ssh-ed25519 AAAAC3...";
# For testing/example, use an empty list (secrets won't be encryptable)
allKeys = [
# user
# host
];
in {
# Example secrets - uncomment and add keys above to use:
# WiFi password for specific network
# "wifi-home.age".publicKeys = allKeys;
# API keys
# "github-token.age".publicKeys = allKeys;
# "openai-api-key.age".publicKeys = allKeys;
# Application secrets
# "syncthing-key.age".publicKeys = allKeys;
# "mullvad-account.age".publicKeys = allKeys;
}