mcpField
A
agenix-secrets

agenix-secrets

github.com/edmundmillerセキュリティ8047 回インストール今日更新
リポジトリ

agenix-secrets について

age で暗号化したシークレットを作成し、NixOS モジュールに接続するワークフロースキルです。受信者キー、自動設定、サービスの所有者、一般的なデプロイ方法を扱います。

主な機能

  1. 01公開鍵と暗号化ファイルの対応付け
  2. 02age の受信者を指定してシークレットを作成
  3. 03NixOS へのシークレットの自動設定
  4. 04所有者とパスの設定
  5. 05サービスと Home Assistant の設定例

活用シーン

  1. 01ホスト用の暗号化シークレットを追加
  2. 02NixOS サービスに認証情報を提供
  3. 03Home Assistant の secrets.yaml の値を管理
  4. 04既存の暗号化環境ファイルを更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/edmundmiller/dotfiles --skill agenix-secrets

インストール後、Claude Code で/agenix-secretsを入力して読み込まれたか確認します。

詳細

Agenix のシークレット

age で暗号化したシークレットを作成し、NixOS モジュールで利用します。

リポジトリのシークレット構成

hosts/<host>/secrets/
├── secrets.nix          # Public key → .age file mapping (NOT imported into NixOS)
├── my-secret.age        # Encrypted secret file
└── restic/              # Subdirectories supported
    └── repo.age
hosts/shared/secrets/
├── secrets.nix          # Shared cross-host secrets
└── host-keys.nix        # Maps hostname → public key for filtering

自動設定

modules/agenix.nix は secrets.nix から age.secrets を自動生成します。

  • 各 "name.age" エントリは age.secrets.<name> になります(.age サフィックスは削除されます)
  • デフォルトの owner = config.user.name(mkDefault 経由で設定され、上書き可能です)
  • デフォルトの file は hosts/<host>/secrets/<name>.age を指します
  • 有効化時に /run/agenix/<name> に復号されます

age.secrets.<name>.file を設定する必要はありません。デフォルトのユーザーに所有させない場合に限り、owner/group/mode を上書きしてください。

ワークフロー:新しいシークレットを追加する

1. secrets.nix にエントリを追加する
# hosts/<host>/secrets/secrets.nix
let
  edmundmiller = "ssh-ed25519 AAAAC3...";
  nuc = "ssh-ed25519 AAAAC3...";
in {
  "my-secret.age".publicKeys = [ edmundmiller nuc ];
}

ユーザーキーとホストキーの両方が必要です。ユーザーキーは暗号化と編集に、ホストキーはデプロイ時の復号に使います。

2. 暗号化ファイルを作成する
cd hosts/<host>/secrets

# Pipe content (non-interactive)
printf 'SECRET_VALUE' | age \
  -r "ssh-ed25519 AAAA...user" \
  -r "ssh-ed25519 AAAA...host" \
  -o my-secret.age

# Verify decryption
age -d -i ~/.ssh/id_ed25519 my-secret.age

agenix -e CLI には対話型エディターが必要です。エージェントのワークフローでは、secrets.nix の各受信者の公開鍵に対して -r を指定し、age を直接使用してください。

3. NixOS モジュールで参照する
# Simple: file path reference (most common)
services.myapp.environmentFile = config.age.secrets.my-secret.path;

# Override owner when service runs as different user
age.secrets.my-secret = {
  owner = "myapp";
  group = "myapp";
};
4. デプロイする
# Stage by directory — NOT by filename (staging a .age path directly is blocked by the sandbox)
git add hosts/<host>/secrets/
git commit -m "secrets: add my-secret"
git push && hey nuc

サンドボックスに関する注意: pi サンドボックスは、.age ファイルのパスを明示的に 参照する git add(およびすべての bash コマンド)をブロックします。親ディレクトリをステージすると、 .age ファイルを直接指定せずにディレクトリ内のすべての変更をステージできます(git add hosts/<host>/secrets/)。

パターン:!secret を使った HA secrets.yaml

Home Assistant の YAML では、!secret key 形式の参照を使用できます。nixpkgs の HA モジュールは 生成された YAML を後処理し、! タグの引用符を取り除きます(sed が '!secret foo' を !secret foo に変換します)。

# In HA module config:
services.home-assistant.config.homeassistant = {
  latitude = "!secret latitude";    # Unquoted by nixpkgs sed post-processor
  longitude = "!secret longitude";
};

# Decrypt with correct owner and symlink into HA config dir:
age.secrets.hass-secrets = {
  owner = "hass";
  group = "hass";
};
systemd.tmpfiles.settings."10-hass-nix-yaml" = {
  "${config.services.home-assistant.configDir}/secrets.yaml" = {
    L.argument = config.age.secrets.hass-secrets.path;
  };
};

.age ファイルには、標準の HA secrets.yaml 形式で記述します。

latitude: 33.083423
longitude: -96.820367

パターン:既存の .age ファイルを更新する

復号 → 変更 → 再暗号化します。既存の env ファイルに変数を追加する場合によく使います。

# Decrypt to temp
age -d -i ~/.ssh/id_ed25519 hosts/<host>/secrets/my-env.age > /tmp/my-env.txt

# Modify
echo "NEW_VAR=value" >> /tmp/my-env.txt

# Re-encrypt (overwrites existing .age)
age \
  -r "ssh-ed25519 AAAA...user" \
  -r "ssh-ed25519 AAAA...host" \
  -o hosts/<host>/secrets/my-env.age /tmp/my-env.txt

# Clean up
rm /tmp/my-env.txt

# Verify
age -d -i ~/.ssh/id_ed25519 hosts/<host>/secrets/my-env.age

パターン:1Password と Agenix の併用(ログイン認証情報)

ユーザー名/パスワードが必要なサービスでは、1Password(人がアクセスするため)と agenix(マシンがアクセスするため)の両方に保存します。

# 1. Generate creds and store in 1Password
PASSWORD=$(op item create \
  --category=login \
  --title="MyService" \
  --vault="Private" \
  --url="http://nuc:8080" \
  --generate-password="32,letters,digits" \
  username="emiller" \
  --format=json | jq -r '.fields[] | select(.id == "password") | .value')

# 2. Create agenix env file
printf 'MYSERVICE_USER=emiller\nMYSERVICE_PASSWORD=%s' "$PASSWORD" | age \
  -r "ssh-ed25519 AAAA...user" \
  -r "ssh-ed25519 AAAA...host" \
  -o hosts/<host>/secrets/myservice-env.age

# 3. Add to secrets.nix, wire environmentFile, set owner (see below)

パターン:所有者を上書きしたサービス環境ファイル

ほとんどの NixOS サービスは専用ユーザー(emiller ではありません)で実行されます。サービスが読み取れるよう、シークレットの所有者を上書きしてください。

# In host config (e.g., hosts/nuc/default.nix):
modules.services.myservice = {
  enable = true;
  environmentFile = config.age.secrets.myservice-env.path;
};

# Override default owner (emiller) → service user
age.secrets.myservice-env.owner = "myservice";

サービスのユーザー名は通常、サービス名と一致します。不明な場合は、対象ホストで grep -r "DynamicUser\|User=" /etc/systemd/system/<service>* を実行して確認してください。

キーの公開鍵

hosts/<host>/secrets/secrets.nix から読み取ってください。ハードコードしないでください。このファイルでは edmundmiller(ユーザーの SSH キー)と、ホスト固有のキー(例:nuc)を定義しています。

よくある落とし穴

  • シークレットのパスに builtins.readFile を使わないでください — 平文が誰でも読み取り可能な Nix ストアに漏えいします
  • 非対話型シェルでは agenix -e は失敗します — 代わりに age -r を直接使用してください
  • publicKeys にホストキーを忘れた場合 — 対象マシンでシークレットを復号できません
  • 所有者が間違っている場合 — サービスが /run/agenix/<name> を読み取れません(デフォルトの所有者は config.user.name です)
  • 共有シークレットには hosts/shared/secrets/secrets.nix のエントリと、host-keys.nix のホストキーが必要です

コメント