Kubernetes Secrets with OCI Vault - External Secrets Operator
This cluster uses OCI Vault (Always Free tier) to securely store all sensitive configuration. This approach ensures that secrets are never committed to the repository and can be retrieved for cluster recreation.
OCI Always Free Limits
Section titled “OCI Always Free Limits”| Resource | Free Limit | Our Usage |
|---|---|---|
| Vault Secrets | 150 | ~10 |
| Master Key Versions | 20 | 1 |
| HSM-protected Keys | ✓ | ✓ |
Secrets Inventory
Section titled “Secrets Inventory”All secrets are stored in the k3s-secrets-vault OCI Vault:
| Secret Name | Purpose | Used By |
|---|---|---|
cloudflare-api-token | DNS automation | External DNS, Cert Manager |
cloudflare-zone-id | Cloudflare zone | External DNS |
domain-name | Base domain | Manifests |
github-pat | Repository access | ArgoCD, GHCR |
github-username | GitHub authentication | ArgoCD, GHCR |
git-repo-url | Repository URL | ArgoCD |
k3s-token | Cluster join token | K3s nodes |
acme-email | Let’s Encrypt contact | Cert Manager |
argocd-admin-password | ArgoCD UI login | ArgoCD |
ssh-public-key | Instance access | OCI Compute |
Retrieving Secrets
Section titled “Retrieving Secrets”Prerequisites
Section titled “Prerequisites”Install and configure the OCI CLI:
brew install oci-clioci setup configList All Secrets
Section titled “List All Secrets”oci vault secret list \ --compartment-id <your-compartment-ocid> \ --query 'data[].{"name":"secret-name","id":id}' \ --output tableRetrieve a Secret Value
Section titled “Retrieve a Secret Value”oci secrets secret-bundle get \ --secret-id <secret-ocid> \ --query 'data."secret-bundle-content".content' \ --raw-output | base64 -dRetrieve ArgoCD Password
Section titled “Retrieve ArgoCD Password”The ArgoCD admin password is automatically synced from Vault to the cluster via External Secrets Operator.
# From cluster (password is synced from Vault)kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath='{.data.password}' | base64 -d
# Or directly from VaultARGOCD_SECRET_ID=$(terraform -chdir=tf-k3s output -json secret_ocids | jq -r '.argocd_admin_password')
oci secrets secret-bundle get \ --secret-id "$ARGOCD_SECRET_ID" \ --query 'data."secret-bundle-content".content' \ --raw-output | base64 -dGenerating terraform.tfvars
Section titled “Generating terraform.tfvars”To recreate terraform.tfvars from OCI Vault:
-
Get all secret OCIDs
Terminal window terraform -chdir=tf-k3s output -json secret_ocids -
Create the tfvars file
Terminal window cat > tf-k3s/terraform.tfvars << 'EOF'tenancy_ocid = "<from OCI Console>"user_ocid = "<from OCI Console>"fingerprint = "<from OCI Console>"private_key_path = "/path/to/oci_api_key.pem"region = "<your-region>"compartment_ocid = "<from OCI Console>"EOF -
Add secrets from Vault
Terminal window # Example for one secretecho "cloudflare_api_token = \"$(oci secrets secret-bundle get \--secret-id <cloudflare-api-token-ocid> \--query 'data."secret-bundle-content".content' \--raw-output | base64 -d)\"" >> tf-k3s/terraform.tfvars
External Secrets Operator
Section titled “External Secrets Operator”The cluster runs External Secrets Operator (ESO) to sync OCI Vault secrets to Kubernetes Secrets. This enables GitOps-friendly secret management where:
- Secrets are stored in OCI Vault
- ESO reads secrets from Vault
- ESO creates/updates Kubernetes Secrets
- Applications reference standard Kubernetes Secrets
Authentication
Section titled “Authentication”The cluster uses Instance Principals to authenticate with OCI Vault. This eliminates the need for managing long-lived API keys for the cluster itself.
- Dynamic Group:
k3s-nodes-dggroups all instances in the compartment (instance.compartment.id = '<compartment_ocid>'). - Policy:
k3s-secrets-read-policyallows the dynamic group toread secret-familyanduse vaultsin the compartment. - ClusterSecretStore: Configured with
principalType: InstancePrincipal.
Architecture
Section titled “Architecture”flowchart LR
subgraph OCI["OCI Cloud"]
Vault[(Vault)]
IAM[IAM Policy]
end
subgraph K8s["K3s Cluster"]
Node[Worker Node]
ESO[External Secrets<br/>Operator]
css[ClusterSecretStore]
es[ExternalSecret]
Secret[K8s Secret]
end
Node --"Identity"--> IAM
IAM --"Allow Read"--> Vault
Vault --"Secret Bundle"--> ESO
css --"Config"--> ESO
es --"Ref"--> ESO
ESO --"Sync"--> Secret
Security Best Practices
Section titled “Security Best Practices”- Never commit secrets - All
.tfvarsfiles are gitignored - Use HSM protection - OCI Vault uses HSM-backed master keys
- Enable versioning - Secret versions are retained for rollback
- Least privilege - Use scoped API tokens (Cloudflare Zone.DNS only)
- Rotate regularly - Update secrets and create new versions
Terraform Remote State
Section titled “Terraform Remote State”Terraform state is stored in OCI Object Storage:
| Setting | Value |
|---|---|
| Bucket | k3s-tfstate |
| Versioning | Enabled |
| Access | Private (NoPublicAccess) |
| Encryption | Server-side (default) |