Skip to content

Secrets & Credentials

Osotechie edited this page Jun 22, 2026 · 1 revision

Secrets & Credentials

This page covers the design and management of secrets across the NAS-as-Code project — how they're stored, how they flow into deployments, and what you need to know when adding or rotating them.

🔗 For the official Azure KeyVault documentation, see Azure Key Vault Overview.

Design Philosophy

Early on I made the decision to never store secrets in the repository — not even encrypted. Instead, I use Azure KeyVault as the single source of truth for all sensitive values. GitHub Actions pulls them at runtime, and they only exist in memory during the workflow execution.

Why Azure KeyVault over just GitHub Secrets?

  • Centralised — one place to manage secrets consumed by both repos
  • Auditable — KeyVault logs who accessed what and when
  • Rotation-friendly — update the secret in one place, both repos pick it up next run
  • Separation of concerns — GitHub Secrets hold the access credentials to KeyVault; KeyVault holds the application secrets

💡 Think of it as: GitHub Secrets are the "keys to the safe", KeyVault is the "safe" itself.

The Three-Tier Secret Flow

Azure KeyVault (source of truth)
    │
    │  az keyvault secret list / show
    ▼
GitHub Actions environment variables (masked, ephemeral)
    │
    │  #{TOKEN}# replacement in .yml / .env files
    ▼
Config files on NAS (deployed, never committed)

What Lives Where

Azure KeyVault

All application-level secrets. Examples:

Secret Name (KeyVault) Env Var Name Used By
samba-password SAMBA_PASSWORD Samba role (sets SMB user password)
nut-password NUT_PASSWORD NUT client role (UPS monitoring auth)
*various container secrets* *VARIOUS* Container .env files

⚠️ Naming convention: KeyVault secret names use lowercase-with-hyphens. The workflow converts these to UPPERCASE_WITH_UNDERSCORES for environment variable injection (e.g. my-secretMY_SECRET).

GitHub Secrets (per-environment)

Credentials for accessing infrastructure — not application secrets:

Secret Purpose
AZURE_SPN_CREDENTIALS Service Principal JSON for Azure login
AZURE_KEYVAULT_NAME Name of the KeyVault to read from
SSH_PRIVATE_KEY SSH key for connecting to the NAS
HOST_FINGERPRINT Pinned SSH host key of the NAS
WG_CONFIG_FILE Full WireGuard config (private key, peer, endpoint)

GitHub Variables (per-environment)

Non-sensitive configuration values:

Variable Purpose
HOST_IP NAS IP address (routable over WireGuard)
HOST_USER SSH user on the NAS
ENV_NAME Environment name (test or production)

Token Replacement Pattern

Secrets flow into configuration files via a token replacement pattern. Any file containing #{SECRET_NAME}# will have that token replaced with the matching environment variable value at deployment time.

In nas-as-code (Ansible group_vars):

# inventory/group_vars/production.yml
samba_password: "#{SAMBA_PASSWORD}#"
nut_password: "#{NUT_PASSWORD}#"

In nas-containers-as-code (.env files):

# media/.env
PLEX_TOKEN=#{PLEX_TOKEN}#
SONARR_API_KEY=#{SONARR_API_KEY}#

The replacement happens via:

  • A Python script in the workflow that uses re.sub for safe handling of special characters

Azure Setup (Key Decisions)

The following outlines the key decisions made when setting up the Azure side. For step-by-step commands, refer to the official docs linked below.

Service Principal (SPN)

A dedicated SPN with minimal permissions is used by GitHub Actions to authenticate to Azure:

  • Only has Key Vault Secrets User role on the specific KeyVault resource
  • No broader subscription-level access
  • Credentials stored as AZURE_SPN_CREDENTIALS in GitHub

🔗 Create a service principal | Assign KeyVault RBAC roles

KeyVault Configuration

  • RBAC authorization (not access policies) — cleaner, more standard
  • Soft-delete enabled — protection against accidental deletion
  • Purge protection — prevents permanent deletion during retention period
  • No network restrictions — accessed from GitHub-hosted runners (dynamic IPs)

🔗 Create a Key Vault | RBAC vs Access Policies

Adding a New Secret

  1. Add to Azure KeyVault — use the portal or az keyvault secret set --vault-name <name> --name "my-new-secret" --value "the-value"
  2. Reference in config — add #{MY_NEW_SECRET}# to the relevant .yml or .env file
  3. Commit and push — the next workflow run will pick up the new secret automatically

No changes needed to the workflow itself — it dynamically lists and pulls all KeyVault secrets.

Rotating a Secret

  1. Update the value in Azure KeyVault
  2. Next workflow run picks up the new value automatically
  3. No code changes or PRs required

Rotating Infrastructure Credentials

If you need to rotate the SSH key, SPN credentials, or WireGuard config:

  1. Generate the new credential
  2. Update the corresponding GitHub Secret in both environments (test + production)
  3. For SSH keys — also update the authorized_keys on the NAS and the HOST_FINGERPRINT if host keys changed

⚠️ Unlike KeyVault secrets, GitHub Secrets require manual updates in the repository settings UI (Settings → Environments → [environment] → Secrets).

Clone this wiki locally