diff --git a/README.md b/README.md index ce945b4..8495f65 100644 --- a/README.md +++ b/README.md @@ -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 - **Bootloader Choice** - systemd-boot (default) or Limine (prettier, more features) - **Plymouth** - Optional boot splash screen +- **Secrets Management** - Encrypted secrets with agenix (WiFi passwords, API keys, etc.) ## Keybindings @@ -85,6 +86,21 @@ extraModules = [ 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 ``` diff --git a/docs/SECRETS.md b/docs/SECRETS.md new file mode 100644 index 0000000..dda8524 --- /dev/null +++ b/docs/SECRETS.md @@ -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 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 +``` diff --git a/installer/install.sh b/installer/install.sh index 45db5c6..14136d3 100755 --- a/installer/install.sh +++ b/installer/install.sh @@ -271,6 +271,53 @@ download_wallpapers() { 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/.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() { echo -e "${BOLD}Configuration Summary${NC}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @@ -347,6 +394,7 @@ main() { generate_config setup_flake download_wallpapers + setup_secrets apply_config echo "" diff --git a/modules/default.nix b/modules/default.nix index cd72501..56de620 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -6,5 +6,6 @@ ./services ./programs ./performance + ./secrets ]; } diff --git a/modules/secrets/default.nix b/modules/secrets/default.nix new file mode 100644 index 0000000..51ac97c --- /dev/null +++ b/modules/secrets/default.nix @@ -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"; + # }; + # }; + }; +} diff --git a/secrets/secrets.nix b/secrets/secrets.nix new file mode 100644 index 0000000..be12df3 --- /dev/null +++ b/secrets/secrets.nix @@ -0,0 +1,41 @@ +# Secrets configuration for agenix +# +# This file defines which public keys can decrypt each secret. +# Secrets are encrypted with `agenix -e .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 .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 | 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; +}