Synchronize Kubernetes TLS certificates between namespaces using two CRDs: CertificateExport and CertificateImport. A lightweight controller periodically copies TLS secrets based on cron-like schedules.
CertificateExport (source namespace): points to a TLS secret (kubernetes.io/tls) to be shared.CertificateImport (target namespace): references a CertificateExport (same namespace or ns/name) and copies the secret data to a target TLS secret.CertificateImport supports cron scheduling. Default: @every 1h.# Build the controller binary
make build
# Build Docker image
make docker-build TAG=0.1.0
# Push to registry (replace with your registry)
docker tag nazman/cert-trust:0.1.0 YOUR_REGISTRY/cert-trust:0.1.0
docker push YOUR_REGISTRY/cert-trust:0.1.0
# Install with Helm
make helm-install IMAGE=YOUR_REGISTRY/cert-trust TAG=0.1.0
make help # Show all available commands
make build # Build controller binary
make docker-build TAG=0.1.0 # Build Docker image
make docker-push TAG=0.1.0 # Push Docker image
make helm-install # Install/upgrade Helm chart
make helm-uninstall # Uninstall Helm chart
make print # Show current variables
# Set your image registry and tag
export IMAGE=ghcr.io/your-org/cert-trust
export TAG=0.1.0
# Build and deploy
make docker-build TAG=$TAG
make docker-push TAG=$TAG
make helm-install IMAGE=$IMAGE TAG=$TAG
helm install cert-trust ./charts/cert-trust -n cert-trust --create-namespace
This installs:
CertificateExport and CertificateImportValues of interest in values.yaml:
image.repository, image.tagleaderElectionimmediateSyncOnStartThe controller binary accepts the following flags:
--metrics-bind-address string The address the metric endpoint binds to (default ":8080")
--health-probe-bind-address string The address the probe endpoint binds to (default ":8081")
--leader-elect Enable leader election for controller manager (default false)
--immediate-sync-on-start Trigger a one-time immediate sync when the scheduler starts (default false)
Helm chart maps values to flags:
leaderElection → --leader-electimmediateSyncOnStart → --immediate-sync-on-start1) Create a TLS secret in the source namespace:
kubectl create secret tls myapp-tls \
--cert=path/to/cert.pem \
--key=path/to/key.pem \
-n backend
2) Create a CertificateExport:
apiVersion: cert.trust.flolive.io/v1
kind: CertificateExport
metadata:
name: export-myapp-cert
namespace: backend
spec:
secretRef: myapp-tls
3) Create a CertificateImport in target namespace:
apiVersion: cert.trust.flolive.io/v1
kind: CertificateImport
metadata:
name: import-myapp-cert
namespace: frontend
spec:
fromExport: backend/export-myapp-cert
targetSecret: myapp-tls
schedule: "@every 2h" # optional, default: @every 1h
# In gateway namespace - export the certificate
apiVersion: cert.trust.flolive.io/v1
kind: CertificateExport
metadata:
name: export-wildcard-cert
namespace: gateway
spec:
secretRef: wildcard-tls
---
# In api namespace - import the certificate
apiVersion: cert.trust.flolive.io/v1
kind: CertificateImport
metadata:
name: import-wildcard-cert
namespace: api
spec:
fromExport: gateway/export-wildcard-cert
targetSecret: wildcard-tls
schedule: "*/30 * * * *" # every 30 minutes
---
# In web namespace - import the same certificate
apiVersion: cert.trust.flolive.io/v1
kind: CertificateImport
metadata:
name: import-wildcard-cert
namespace: web
spec:
fromExport: gateway/export-wildcard-cert
targetSecret: wildcard-tls
schedule: "*/30 * * * *" # every 30 minutes
# Both resources in the same namespace
apiVersion: cert.trust.flolive.io/v1
kind: CertificateExport
metadata:
name: export-main-cert
namespace: production
spec:
secretRef: main-tls
---
apiVersion: cert.trust.flolive.io/v1
kind: CertificateImport
metadata:
name: import-main-cert
namespace: production
spec:
fromExport: export-main-cert # No namespace prefix needed
targetSecret: main-tls-copy
schedule: "@every 1h" # optional
# Check if controller is running
kubectl get pods -n cert-trust
# Check controller logs
kubectl logs -n cert-trust deployment/cert-trust-cert-trust
# Check CRD resources
kubectl get certificateexport -A
kubectl get certificateimport -A
# Check last sync time
kubectl get certificateexport export-myapp-cert -n backend -o yaml
kubectl get certificateimport import-myapp-cert -n frontend -o yaml
# Check if target secret was created
kubectl get secret myapp-tls -n frontend
# Build and run locally
make build
./bin/manager --leader-elect=false --immediate-sync-on-start=false
# Run with debug logging
go run ./cmd/cert-trust --leader-elect=false --immediate-sync-on-start=true
# Apply test resources
kubectl apply -f test/mainfest.yaml
# Check if sync worked
kubectl get secret srvx-cc-tls-default -n default
"*/2 * * * *" - Every 2 minutes"0 */6 * * *" - Every 6 hours"@every 1h" - Every hour"@every 30m" - Every 30 minutes"0 0 * * *" - Daily at midnight"0 0 * * 0" - Weekly on SundayNote: Only CertificateImport resources support scheduling. CertificateExport resources are static references to source secrets.
# charts/cert-trust/values.yaml
replicaCount: 1
image:
repository: ghcr.io/your-org/cert-trust
tag: "0.1.0"
pullPolicy: IfNotPresent
leaderElection: false
immediateSyncOnStart: false
timezone: "Europe/Athens"
resources: {}
To enable a one-time immediate sync on deployment with Helm:
helm upgrade --install cert-trust ./charts/cert-trust \
-n cert-trust --create-namespace \
--set immediateSyncOnStart=true
kubernetes.io/tlsfromExport reference format# Check RBAC permissions
kubectl auth can-i list certificateexports --as=system:serviceaccount:cert-trust:cert-trust-cert-trust
# Check controller logs
kubectl logs -n cert-trust deployment/cert-trust-cert-trust --tail=50
# Check resource status
kubectl describe certificateexport -n backend export-myapp-cert
kubectl describe certificateimport -n frontend import-myapp-cert
The project includes GitHub Actions workflows for:
.github/workflows/ci.yml):
.github/workflows/release.yml):
v0.1.0)# Create and push a tag
git tag v0.1.0
git push origin v0.1.0
# The GitHub Action will automatically:
# 1. Build and push Docker image to ghcr.io/your-org/cert-trust:v0.1.0
# 2. Package Helm chart
# 3. Create GitHub release with chart artifacts
# Add the repository using GitHub releases
helm repo add cert-trust https://github.com/your-org/cert-trust/releases/download/v0.1.0
helm repo update
# Install the chart
helm install cert-trust cert-trust/cert-trust -n cert-trust --create-namespace
kubernetes.io/tlsstatus.lastSyncTime are updated on best-effort basis