From eafcdb2f2809d09330143d0444d886f28e33712b Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Wed, 28 Jan 2026 18:46:54 -0800 Subject: [PATCH] Add External Secrets Operator for GitOps secret management Deploys ESO to sync secrets from 1Password to native K8s Secrets. Replaces manual `op inject` workflow with declarative ExternalSecrets. Includes: - ArgoCD Application for ESO operator (helm-chart-1.3.1) - Separate config app for ClusterSecretStore - ClusterSecretStore connecting to 1Password Connect - Helm values with resource limits for minikube Co-Authored-By: Claude Opus 4.5 --- argocd/apps/external-secrets-config.yaml | 26 ++++++ argocd/apps/external-secrets.yaml | 33 ++++++++ argocd/manifests/external-secrets/README.md | 83 +++++++++++++++++++ .../cluster-secret-store.yaml | 21 +++++ .../external-secrets/kustomization.yaml | 5 ++ argocd/manifests/external-secrets/values.yaml | 31 +++++++ 6 files changed, 199 insertions(+) create mode 100644 argocd/apps/external-secrets-config.yaml create mode 100644 argocd/apps/external-secrets.yaml create mode 100644 argocd/manifests/external-secrets/README.md create mode 100644 argocd/manifests/external-secrets/cluster-secret-store.yaml create mode 100644 argocd/manifests/external-secrets/kustomization.yaml create mode 100644 argocd/manifests/external-secrets/values.yaml diff --git a/argocd/apps/external-secrets-config.yaml b/argocd/apps/external-secrets-config.yaml new file mode 100644 index 0000000..e741d22 --- /dev/null +++ b/argocd/apps/external-secrets-config.yaml @@ -0,0 +1,26 @@ +# External Secrets Configuration - ClusterSecretStore for 1Password +# +# Deploys the ClusterSecretStore that connects ESO to 1Password Connect. +# Must be synced AFTER external-secrets operator is running. +# +# Prerequisites: +# - 1password-connect is deployed and healthy +# - external-secrets operator is deployed and CRDs are installed +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets-config + namespace: argocd +spec: + project: default + source: + repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + path: argocd/manifests/external-secrets + destination: + server: https://kubernetes.default.svc + namespace: external-secrets + syncPolicy: + syncOptions: + - CreateNamespace=true diff --git a/argocd/apps/external-secrets.yaml b/argocd/apps/external-secrets.yaml new file mode 100644 index 0000000..ecb7cb7 --- /dev/null +++ b/argocd/apps/external-secrets.yaml @@ -0,0 +1,33 @@ +# External Secrets Operator - Kubernetes secret sync from external providers +# Syncs secrets from 1Password Connect to native Kubernetes Secrets +# +# Chart mirrored from https://github.com/external-secrets/external-secrets +# +# Prerequisites: +# - 1password-connect must be deployed and healthy +# +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: external-secrets + namespace: argocd +spec: + project: default + sources: + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/external-secrets.git + targetRevision: helm-chart-1.3.1 + path: deploy/charts/external-secrets + helm: + releaseName: external-secrets + valueFiles: + - $values/argocd/manifests/external-secrets/values.yaml + - repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git + targetRevision: main + ref: values + destination: + server: https://kubernetes.default.svc + namespace: external-secrets + syncPolicy: + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/argocd/manifests/external-secrets/README.md b/argocd/manifests/external-secrets/README.md new file mode 100644 index 0000000..71d9e90 --- /dev/null +++ b/argocd/manifests/external-secrets/README.md @@ -0,0 +1,83 @@ +# External Secrets Operator + +External Secrets Operator (ESO) syncs secrets from 1Password Connect to native Kubernetes Secrets. + +## Architecture + +- **ClusterSecretStore** (`onepassword-blumeops`): Cluster-wide access to 1Password via Connect +- **ExternalSecret** (per-namespace): Defines which secrets to sync from 1Password + +## Prerequisites + +1Password Connect must be deployed and healthy before syncing ESO. + +## Deployment + +```bash +argocd app sync external-secrets +``` + +## Verification + +```bash +# Check operator pods +kubectl --context=minikube-indri -n external-secrets get pods + +# Check ClusterSecretStore status +kubectl --context=minikube-indri get clustersecretstore onepassword-blumeops + +# Check all ExternalSecrets across namespaces +kubectl --context=minikube-indri get externalsecret -A +``` + +## Creating ExternalSecrets + +To sync a secret from 1Password, create an ExternalSecret in the target namespace: + +```yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: my-secret + namespace: my-namespace +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: onepassword-blumeops + target: + name: my-secret # Name of K8s Secret to create + creationPolicy: Owner # ESO owns and manages the Secret + data: + - secretKey: password # Key in the K8s Secret + remoteRef: + key: My 1Password Item # Title of item in 1Password + property: password # Field label in 1Password item +``` + +### Finding 1Password Item Details + +```bash +# List items in blumeops vault +op item list --vault blumeops + +# Get field names for an item +op item get --vault blumeops --format json | jq -r '.fields[] | .label' +``` + +## Troubleshooting + +### ClusterSecretStore not ready +- Check 1Password Connect is running: `kubectl --context=minikube-indri -n 1password get pods` +- Verify token secret exists: `kubectl --context=minikube-indri -n 1password get secret onepassword-token` + +### ExternalSecret not syncing +- Check the ExternalSecret status: `kubectl --context=minikube-indri describe externalsecret -n ` +- Verify the 1Password item title and field names match exactly +- Check ESO controller logs: `kubectl --context=minikube-indri -n external-secrets logs -l app.kubernetes.io/name=external-secrets` + +## Related + +- [External Secrets Operator Docs](https://external-secrets.io/) +- [1Password Provider](https://external-secrets.io/latest/provider/1password-automation/) +- [1Password Connect](../1password-connect/README.md) diff --git a/argocd/manifests/external-secrets/cluster-secret-store.yaml b/argocd/manifests/external-secrets/cluster-secret-store.yaml new file mode 100644 index 0000000..6661e77 --- /dev/null +++ b/argocd/manifests/external-secrets/cluster-secret-store.yaml @@ -0,0 +1,21 @@ +# ClusterSecretStore for 1Password Connect +# +# Provides cluster-wide access to the blumeops vault via 1Password Connect. +# ExternalSecret resources in any namespace can reference this store. +# +apiVersion: external-secrets.io/v1beta1 +kind: ClusterSecretStore +metadata: + name: onepassword-blumeops +spec: + provider: + onepassword: + connectHost: http://onepassword-connect.1password.svc.cluster.local:8080 + vaults: + blumeops: 1 + auth: + secretRef: + connectTokenSecretRef: + name: onepassword-token + namespace: 1password + key: token diff --git a/argocd/manifests/external-secrets/kustomization.yaml b/argocd/manifests/external-secrets/kustomization.yaml new file mode 100644 index 0000000..bf834d1 --- /dev/null +++ b/argocd/manifests/external-secrets/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - cluster-secret-store.yaml diff --git a/argocd/manifests/external-secrets/values.yaml b/argocd/manifests/external-secrets/values.yaml new file mode 100644 index 0000000..c5bffbc --- /dev/null +++ b/argocd/manifests/external-secrets/values.yaml @@ -0,0 +1,31 @@ +# External Secrets Operator Helm values for blumeops +# Chart: https://github.com/external-secrets/external-secrets + +installCRDs: true + +# Resource limits for minikube +resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m" + +webhook: + resources: + requests: + memory: "32Mi" + cpu: "25m" + limits: + memory: "128Mi" + cpu: "100m" + +certController: + resources: + requests: + memory: "32Mi" + cpu: "25m" + limits: + memory: "128Mi" + cpu: "100m"