Secret management¶
Secrets never live in Git. They flow:
The pieces¶
- A
ClusterSecretStorenamedonepassword-connectreads theTalos1Password vault. - Per-app
externalsecret.yamlfiles reference specific 1Password items by title. Apps that use one shoulddependsOnonepassword-connectinexternal-secrets. cluster-secrets: a single 1Password item extracted into a Secret and injected into every app'spostBuild.substituteFrom. Holds cluster-wide sensitive values, including${SECRET_DOMAIN},${SECRET_INTERNAL_DOMAIN}, and internal device DNS names.cluster-settings: a git-tracked ConfigMap (components/global-vars/) holding cluster-wide non-sensitive${...}values (non-secret feature flags and the like; currently empty).
Rules for a public repo¶
- Use the
${SECRET_DOMAIN}/${SECRET_INTERNAL_DOMAIN}placeholders in Git; Flux substitutes the real values at apply time. - Any literal
${VAR}you want to survive substitution (Grafana dashboards, envsubst templates, shell snippets) must be escaped as$${VAR}. - Device address / lookup tables (e.g. router backup inventories, SNMP/NUT targets) must be
templated inside an ExternalSecret's
target.template.datablock and mounted from the rendered Secret, never rendered into a ConfigMap in Git. Internal device DNS names live in thecluster-secrets1Password item, not incluster-settings. - A CI guard (
.github/scripts/check_internal_identifiers.py, run by the security-scans workflow) fails any pull request that introduces a LAN IP, node name, site-prefixed device hostname (cr-*/sw-*), MAC address, or internal hostname (.lan/.internal) outside a small allowlist of accepted functional configs. Device models are intentionally not enumerated in the script to avoid self-disclosure in this public repository.
Talos machine secrets (talsecret)¶
The talhelper secrets bundle (cluster CA/PKI, etcd certs, bootstrap tokens) follows the same
"1Password owns it" rule as everything else. There is no SOPS anywhere in this repository:
the historical talos/talsecret.sops.yaml was removed in PR #3463 (2026-07) after the age key for
it was lost; the encrypted blob left in git history is dead ciphertext.
- The bundle is stored as the
talsecretdocument in theTalos1Password vault. just talos gen-configfetches it (op document get talsecret --vault Talos) to a temp file, runstalhelper genconfig, and cleans up. Node configs land in the gitignoredtalos/clusterconfig/.just talos gen-secretcreates the 1Password document (fromtalhelper gensecret) only if it does not already exist.- Recovery without 1Password: any previously generated node config contains the full secret material, so the bundle can be reconstructed offline from the gitignored output of a prior run:
talosctl gen secrets \
--from-controlplane-config talos/clusterconfig/kubernetes-<node>.yaml \
-o /tmp/talsecret.yaml --force
This was how the bundle was recovered when the age key disappeared: regenerated configs were verified byte-identical before the SOPS file was deleted.