add sops nix for secret management
This commit is contained in:
parent
940c4c6679
commit
2ea7f853e4
5 changed files with 207 additions and 1 deletions
|
|
@ -18,12 +18,15 @@
|
||||||
nix-ai-tools.url = "github:numtide/nix-ai-tools";
|
nix-ai-tools.url = "github:numtide/nix-ai-tools";
|
||||||
nix-ai-tools.inputs.nixpkgs.follows = "nixpkgs";
|
nix-ai-tools.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
|
sops-nix.url = "github:Mic92/sops-nix";
|
||||||
|
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
||||||
zig-overlay.url = "github:mitchellh/zig-overlay";
|
zig-overlay.url = "github:mitchellh/zig-overlay";
|
||||||
zig-overlay.inputs.nixpkgs.follows = "nixpkgs";
|
zig-overlay.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
inputs@{ self, nixpkgs, darwin, home-manager, ghostty, nix-ai-tools, ... }:
|
inputs@{ self, nixpkgs, darwin, home-manager, ghostty, nix-ai-tools, sops-nix, ... }:
|
||||||
let
|
let
|
||||||
inherit (nixpkgs.lib) genAttrs;
|
inherit (nixpkgs.lib) genAttrs;
|
||||||
lib = nixpkgs.lib;
|
lib = nixpkgs.lib;
|
||||||
|
|
|
||||||
70
modules/home/rsydn/secrets.nix
Normal file
70
modules/home/rsydn/secrets.nix
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{ config, lib, inputs, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkEnableOption mkOption mkIf types;
|
||||||
|
cfg = config.rsydn.secrets;
|
||||||
|
|
||||||
|
sanitizeSecret = name: secret:
|
||||||
|
let
|
||||||
|
sopsFile = secret.sopsFile or cfg.defaultSopsFile;
|
||||||
|
extra = lib.filterAttrs (k: _: !(builtins.elem k [ "path" "mode" "sopsFile" ])) secret;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
path = secret.path or "${config.xdg.configHome}/secrets/${name}";
|
||||||
|
mode = secret.mode or "0400";
|
||||||
|
}
|
||||||
|
// (lib.optionalAttrs (sopsFile != null) { inherit sopsFile; })
|
||||||
|
// extra;
|
||||||
|
in {
|
||||||
|
imports = [ inputs.sops-nix.homeManagerModules.sops ];
|
||||||
|
|
||||||
|
options.rsydn.secrets = {
|
||||||
|
enable = mkEnableOption "sops-nix integration for managing decrypted secrets";
|
||||||
|
|
||||||
|
ageKeyFile = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "${config.home.homeDirectory}/.config/sops/age/keys.txt";
|
||||||
|
description = "Path to the Age key used for decrypting secrets.";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultSopsFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = "Optional default SOPS file used when a secret definition omits `sopsFile`.";
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets = mkOption {
|
||||||
|
type = types.attrsOf types.attrs;
|
||||||
|
default = { };
|
||||||
|
description = "Secret entries forwarded to `sops.secrets` with sensible defaults.";
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
"api-key" = {
|
||||||
|
sopsFile = ./secrets.yaml;
|
||||||
|
path = "${config.xdg.configHome}/secrets/api-key";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
sops =
|
||||||
|
{
|
||||||
|
age = {
|
||||||
|
keyFile = cfg.ageKeyFile;
|
||||||
|
generateKey = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets = lib.mapAttrs sanitizeSecret cfg.secrets;
|
||||||
|
}
|
||||||
|
// (lib.optionalAttrs (cfg.defaultSopsFile != null) {
|
||||||
|
defaultSopsFile = cfg.defaultSopsFile;
|
||||||
|
});
|
||||||
|
|
||||||
|
home.activation.ensureSecretDir = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
mkdir -p "${config.xdg.configHome}/secrets"
|
||||||
|
chmod 700 "${config.xdg.configHome}/secrets"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
70
modules/home/rsydn/shell/oh-my-posh/capr4n.omp.json
Normal file
70
modules/home/rsydn/shell/oh-my-posh/capr4n.omp.json
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"alignment": "right",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"background": "#003543",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"style": "plain",
|
||||||
|
"template": " \ue641 {{ .CurrentDate | date .Format }} ",
|
||||||
|
"type": "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"background": "#83769c",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"properties": {
|
||||||
|
"always_enabled": true
|
||||||
|
},
|
||||||
|
"style": "plain",
|
||||||
|
"template": " \ueba2 {{ .FormattedMs }} ",
|
||||||
|
"type": "executiontime"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "rprompt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alignment": "left",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"background": "#61AFEF",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"properties": {
|
||||||
|
"display_host": false
|
||||||
|
},
|
||||||
|
"style": "diamond",
|
||||||
|
"template": "{{if .Root}} \uf0e7 {{.UserName}} {{else}} {{.UserName}} {{end}}",
|
||||||
|
"trailing_diamond": "\ue0b0",
|
||||||
|
"type": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"background": "#C678DD",
|
||||||
|
"foreground": "#ffffff",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"properties": {
|
||||||
|
"folder_icon": "\uf115",
|
||||||
|
"folder_separator_icon": " \ue0b1 ",
|
||||||
|
"max_depth": 2,
|
||||||
|
"style": "agnoster_short"
|
||||||
|
},
|
||||||
|
"style": "powerline",
|
||||||
|
"template": " {{ .Path }} ",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"background": "#95ffa4",
|
||||||
|
"foreground": "#193549",
|
||||||
|
"powerline_symbol": "\ue0b0",
|
||||||
|
"style": "powerline",
|
||||||
|
"template": " {{ .HEAD }} ",
|
||||||
|
"type": "git"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "prompt"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"console_title_template": "{{if .Root}} \u26a1 {{end}}{{.UserName}} \u2794 📁{{.Folder}}",
|
||||||
|
"final_space": true,
|
||||||
|
"version": 3
|
||||||
|
}
|
||||||
5
secrets/.gitignore
vendored
Normal file
5
secrets/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Ignore decrypted or temporary secrets material; commit only encrypted SOPS files
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!README.md
|
||||||
|
!*.sops.yaml
|
||||||
58
secrets/README.md
Normal file
58
secrets/README.md
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Secrets with sops-nix
|
||||||
|
|
||||||
|
This repo wires [`sops-nix`](https://github.com/Mic92/sops-nix) into the Home Manager profile so secrets are decrypted on-demand into `~/.config/secrets`. The module is enabled by default and auto-generates an Age key if one does not already exist. Only the *encrypted* payloads under `secrets/*.sops.yaml` are meant to live in git; decrypted files never leave your machine.
|
||||||
|
|
||||||
|
## One-time setup
|
||||||
|
1. Ensure `age` and `sops` are available (e.g. `nix develop` or `nix profile install nixpkgs#age nixpkgs#sops`).
|
||||||
|
2. Generate or import an Age key. Either let Home Manager create one automatically on first activation or run `age-keygen -o ~/.config/sops/age/keys.txt` yourself.
|
||||||
|
3. Capture the public half with `age-keygen -y -f ~/.config/sops/age/keys.txt` and add it to the `recipients` list in each encrypted file (`age1…`). Commit the public key under version control or share it through your password manager so other hosts can decrypt.
|
||||||
|
4. (Optional) Store the private key securely in macOS Keychain or 1Password (`security add-generic-password …`) so rebuilds work without manual prompts.
|
||||||
|
|
||||||
|
## Managing secrets
|
||||||
|
- Declare secrets in Home Manager under `rsydn.secrets.secrets`. Each entry is forwarded to `sops.secrets.<name>` and defaults to writing `~/.config/secrets/<name>` with mode `0400`.
|
||||||
|
- Structure encrypted data however you like (`YAML`, `JSON`, `.env`). Only the encrypted file is tracked; plaintext never leaves `sops`.
|
||||||
|
- Keep sensitive values scoped. Per-project secrets usually live in `.env.sops` + `direnv`; long-lived device secrets belong here.
|
||||||
|
|
||||||
|
Example snippet for `modules/home/rsydn/secrets.nix` consumers:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
rsydn.secrets.secrets = {
|
||||||
|
"openai-api-key" = {
|
||||||
|
sopsFile = ./secrets.sops.yaml;
|
||||||
|
path = "${config.xdg.configHome}/secrets/openai-api-key";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Create or update secrets by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt \
|
||||||
|
sops secrets/secrets.sops.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
When Home Manager activates, the secret is decrypted to the target path. Source it in Nushell with something like:
|
||||||
|
|
||||||
|
```nu
|
||||||
|
let-openai-key = (open $env.XDG_CONFIG_HOME ++ "/secrets/openai-api-key" | str trim)
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the secret stays on disk, prefer reading it only when needed (inside commands/functions) instead of exporting it globally. For project workflows, you can also load it into env vars temporarily:
|
||||||
|
|
||||||
|
```nu
|
||||||
|
def with-openai-key [cmd] {
|
||||||
|
let key = open $env.XDG_CONFIG_HOME ++ "/secrets/openai-api-key" | str trim
|
||||||
|
with-env { OPENAI_API_KEY: $key } { nu -c $cmd }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To attach multiple secrets at once, use structured data (e.g. a YAML mapping) and parse it with Nushell's `from yaml`.
|
||||||
|
|
||||||
|
## Rotating keys
|
||||||
|
If you regenerate your Age key, re-encrypt the file with the new recipient (`sops updatekeys secrets/secrets.sops.yaml`) and re-run `darwin-rebuild --dry-run --flake .#macbook-pro` to verify the deployment. Remember to remove old recipients so machines without access can no longer decrypt.
|
||||||
|
|
||||||
|
## Frequently asked questions
|
||||||
|
- **Where do encrypted files live?** In this repo under `secrets/*.sops.yaml`; they are safe to commit.
|
||||||
|
- **Where does plaintext live?** At runtime under `~/.config/secrets/*` (managed by Home Manager). Keep permissions tight and never add these paths to git.
|
||||||
|
- **How do I share secrets with another machine?** Copy the Age public key from that machine into the `recipients` list, re-run `sops updatekeys`, commit, then pull and rebuild on the other host.
|
||||||
|
- **Can I keep using `.env`?** Yes—encrypt it (e.g. `secrets/project.env.sops`) and load it with `sops exec-env` inside `direnv` so per-project shells receive the decrypted variables without storing them on disk.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue