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 <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-01-28 18:46:54 -08:00
commit eafcdb2f28
6 changed files with 199 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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 <item-id> --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 <name> -n <namespace>`
- 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)

View file

@ -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

View file

@ -0,0 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- cluster-secret-store.yaml

View file

@ -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"