dev: import plaintext token into agenix secret
This commit is contained in:
parent
7b69f6200f
commit
223272db7d
5 changed files with 93 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -26,3 +26,6 @@ every_channel_ed25519
|
||||||
*.p12
|
*.p12
|
||||||
*.pfx
|
*.pfx
|
||||||
**/.env
|
**/.env
|
||||||
|
|
||||||
|
# NEVER commit plaintext secrets
|
||||||
|
secrets/token.txt
|
||||||
|
|
|
||||||
81
scripts/agenix-import-token-file.sh
Executable file
81
scripts/agenix-import-token-file.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "${root}"
|
||||||
|
|
||||||
|
in_file="${1:-secrets/token.txt}"
|
||||||
|
out_file="${2:-secrets/codeberg-token.age}"
|
||||||
|
|
||||||
|
rules_file="${EVERY_CHANNEL_AGE_RULES_FILE:-${root}/secrets.nix}"
|
||||||
|
identity_file="${EVERY_CHANNEL_AGE_IDENTITY_FILE:-$HOME/.config/every.channel/keys/founder_ed25519}"
|
||||||
|
|
||||||
|
if [[ ! -f "${in_file}" ]]; then
|
||||||
|
echo "error: input file not found: ${in_file}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ ! -f "${rules_file}" ]]; then
|
||||||
|
echo "error: agenix RULES file not found: ${rules_file}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ ! -f "${identity_file}" ]]; then
|
||||||
|
echo "error: agenix identity file not found: ${identity_file}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v agenix >/dev/null 2>&1; then
|
||||||
|
echo "error: agenix not found in PATH (run: nix develop)" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if ! command -v ssh-keygen >/dev/null 2>&1; then
|
||||||
|
echo "error: ssh-keygen not found in PATH" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify that the configured identity corresponds to the recipient in secrets.nix.
|
||||||
|
founder_key="$(
|
||||||
|
sed -nE 's/^[[:space:]]*founder[[:space:]]*=[[:space:]]*"([^"]+)".*/\\1/p' secrets.nix | head -n 1
|
||||||
|
)"
|
||||||
|
if [[ -z "${founder_key}" ]]; then
|
||||||
|
echo "error: could not parse founder key from secrets.nix" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q "\"${out_file}\"" "${rules_file}"; then
|
||||||
|
echo "error: no rule for ${out_file} in ${rules_file}" >&2
|
||||||
|
echo "hint: add it to secrets.nix or pass a different output path" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
priv_line="$(ssh-keygen -y -f "${identity_file}" 2>/dev/null || true)"
|
||||||
|
if [[ -z "${priv_line}" ]]; then
|
||||||
|
echo "error: unable to derive public key from identity file: ${identity_file}" >&2
|
||||||
|
echo "hint: if the key is passphrase-protected, run an interactive shell or provide a different identity" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
priv_pub="$(printf '%s\n' "${priv_line}" | cut -d' ' -f1-2)"
|
||||||
|
founder_pub="$(printf '%s\n' "${founder_key}" | cut -d' ' -f1-2)"
|
||||||
|
if [[ "${priv_pub}" != "${founder_pub}" ]]; then
|
||||||
|
echo "error: identity public key does not match secrets recipient" >&2
|
||||||
|
echo "identity_file: ${identity_file}" >&2
|
||||||
|
echo "identity_pub: ${priv_pub}" >&2
|
||||||
|
echo "recipient_pub: ${founder_pub}" >&2
|
||||||
|
echo "hint: set EVERY_CHANNEL_AGE_IDENTITY_FILE to the correct private key path" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_dec="$(mktemp "${TMPDIR:-/tmp}/ec-agenix-dec.XXXXXX")"
|
||||||
|
trap 'rm -f "${tmp_dec}"' EXIT
|
||||||
|
|
||||||
|
# Encrypt: non-interactive mode reads plaintext from stdin.
|
||||||
|
RULES="${rules_file}" cat "${in_file}" | agenix -e "${out_file}" -i "${identity_file}" >/dev/null
|
||||||
|
|
||||||
|
# Decrypt and verify byte-for-byte equality.
|
||||||
|
RULES="${rules_file}" agenix -d "${out_file}" -i "${identity_file}" > "${tmp_dec}"
|
||||||
|
if ! cmp -s "${tmp_dec}" "${in_file}"; then
|
||||||
|
echo "error: verification failed: decrypted secret does not match input file" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "${in_file}"
|
||||||
|
echo "ok: wrote ${out_file} and removed ${in_file} after verification"
|
||||||
|
|
@ -3,7 +3,7 @@ let
|
||||||
founder = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCBTSEEcBOhOkf3WF1e8xmblAZHvgTibFsqck2GY8D/";
|
founder = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCBTSEEcBOhOkf3WF1e8xmblAZHvgTibFsqck2GY8D/";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
"cloudflare-api-token.age".publicKeys = [ founder ];
|
"secrets/cloudflare-api-token.age".publicKeys = [ founder ];
|
||||||
"codeberg-token.age".publicKeys = [ founder ];
|
"secrets/codeberg-token.age".publicKeys = [ founder ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,15 +21,13 @@ nix develop
|
||||||
Encrypt (create) a secret:
|
Encrypt (create) a secret:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd secrets
|
agenix -e secrets/cloudflare-api-token.age
|
||||||
agenix -e cloudflare-api-token.age
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Decrypt (inspect) a secret:
|
Decrypt (inspect) a secret:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd secrets
|
agenix -d secrets/cloudflare-api-token.age
|
||||||
agenix -d cloudflare-api-token.age
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Decryption identity
|
## Decryption identity
|
||||||
|
|
|
||||||
5
secrets/codeberg-token.age
Normal file
5
secrets/codeberg-token.age
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 29OJ4A G6byj6PhWofxSh8K5FGSqBs5W5uKtyJ2MGY1JFb+STc
|
||||||
|
d25eWVNmz2+0zKVVRZ/Pib4YZClhJrML6s3hbLh9rMU
|
||||||
|
--- t/6aoMSRLI8vay71VugNOGwKHjHteiC+SinD6gQARYM
|
||||||
|
¹ˆ8ùEÉâò /‚³?ᢿwÝØJlSñæäíz<C3AD>i–8Ç)†äÿƒ`ã;Bæ4LåÑT„Àö?£;ãÀzÇšÐé\
|
||||||
Loading…
Add table
Add a link
Reference in a new issue