Purpose: For developers and operators, defines the security policies governing customer-managed repository trust, secret handling, .sops.yaml generation, audit trails, and redaction in the overlay rendering pipeline.
1. Repository Trust Policy
Allowed transport protocols
Customer-managed repository URLs must use one of:
-
ssh://— required whenemit_secretis true (SSH key authentication) -
https://— allowed only whenemit_secretis false (token or public access)
The http:// scheme is rejected by validateOverlayUnitConfig. No other schemes are permitted.
Enforcement: internal/gitops/overlay_units_validation.go:validateCustomerManagedOverlay
Host key validation
When emit_secret is true and the transport is SSH, the known_hosts field in secrets.overlay_units.customer_managed must be non-empty. This value is emitted into the GitRepository Secret so that FluxCD validates the remote host key during Git operations.
The renderer does not validate the content of known_hosts beyond non-emptiness. Operators are responsible for providing a correct host key entry obtained through a trusted channel (e.g., ssh-keyscan against a verified host).
2. Secret Handling Model
Source of truth
Cluster-scoped secret data for overlay units is provided via the typed config surface at secrets.overlay_units.customer_managed in the cluster config file (.k8s-<cluster>-config.yaml).
Fields:
-
identity: SSH private key content (base64-encoded in the emitted Secret) -
identity_pub: SSH public key content -
known_hosts: SSH known hosts entry
Delivery mechanism
Secret values are read from the cluster config file at render time. The config file itself should be:
-
encrypted at rest using SOPS Age encryption
-
stored in a location with appropriate access controls
-
never committed to Git with plaintext secret values
The renderer reads plaintext values from the parsed config and emits them as base64-encoded Kubernetes Secret data. SOPS encryption of the emitted Secret file is handled by the .sops.yaml path regex rules, not by the renderer itself.
Fixture secret policy
Repository fixtures must contain only clearly synthetic placeholder values:
-
All secret fields must use the
PLACEHOLDER-prefix -
The fixture secret scanner (
fixture_secret_test.go) enforces this in CI -
Real credentials must never appear in fixtures, even temporarily
-
If real credentials are found in fixture Git history, they must be rotated immediately
3. .sops.yaml Generation
Security sensitivity
.sops.yaml controls which files SOPS encrypts and which Age recipients can decrypt them. Generating this file from config means the renderer influences encryption scope. Errors in generation can cause:
-
secrets committed in plaintext (if path regex is too narrow)
-
inability to decrypt secrets (if recipients are wrong)
Validation rules
The renderer validates .sops.yaml generation inputs before rendering:
-
at least one rule is required when SOPS is enabled
-
each rule must have a non-empty
path_regex -
each rule must have at least one non-empty
age_recipient
Enforcement: internal/gitops/overlay_units_validation.go:validateSOPSOverlay
Review expectations
Changes to .sops.yaml generation logic or to the SOPSGenerationConfig type surface should be reviewed with attention to:
-
whether the path regex correctly covers all files that contain secret data
-
whether the Age recipients list is correct for the target cluster
-
whether the
encrypted_regex(if set) correctly identifies secret fields within YAML files
4. Redaction Requirements
Principle
Logs, test failure messages, and diagnostic output must not print secret values. This applies to:
-
renderer error messages
-
parity test failure output
-
CI pipeline logs
-
structured diagnostic output
Current state and path forward
A CredentialMasker exists at internal/security/credential_masker.go with patterns for AWS keys, Age keys, private key blocks, passwords, tokens, and generic secrets.
The renderer and parity tests do not currently use the masker. The risk is mitigated by:
-
fixture secrets using
PLACEHOLDER-values (not real credentials) -
the fixture secret scanner rejecting real credential patterns
-
config files being SOPS-encrypted at rest
Integration of the credential masker into renderer diagnostics and parity test output is tracked as a future improvement. The immediate risk is low because the renderer does not log secret field values during normal operation.
5. Audit Trail
Mechanism
Changes to security-sensitive config fields are auditable through Git history of the cluster config files. The following fields are security-sensitive:
-
opencenter.gitops.overlay_units.sops.*(encryption scope) -
opencenter.gitops.overlay_units.customer_managed.repository_url(trust boundary) -
secrets.overlay_units.customer_managed.*(credential material)
Expectations
-
Config files should be committed with meaningful commit messages that explain why security-sensitive fields changed.
-
SOPS-encrypted config files provide an implicit audit trail: decryption requires the correct Age key, and key rotation is tracked by the CLI’s key lifecycle commands.
-
No structured audit logging beyond Git history is currently implemented. If regulatory requirements demand it, a structured audit log can be added as a separate feature.
Implementation References
-
Overlay unit validation:
internal/gitops/overlay_units_validation.go -
Overlay unit types:
internal/config/overlay/types.go -
Credential masker:
internal/security/credential_masker.go -
Fixture secret scanner:
internal/gitops/fixture_secret_test.go -
Validation tests:
internal/gitops/overlay_units_validation_test.go