Managing secrets in Kubernetes can be challenging, especially when integrating with external secret management systems. In this post, we’ll explore how to build a Kubernetes Operator that integrates with VMware Secrets Manager (VSecM) to automatically sync and manage secrets in your cluster.
What We’re Building
Our operator will watch for custom VSecMSecret
resources that define how secrets should be fetched from VSecM and stored in Kubernetes. It will handle:
- Fetching secrets from VSecM using SPIFFE-based mTLS
- Creating or updating Kubernetes Secrets based on the fetched data
- Periodic refresh of secrets based on configurable intervals
- Mapping of secret data, labels, and annotations
The Custom Resource Definition
First, let’s look at how users will define their secrets using our custom resource:
apiVersion: vsecm.com/v1alpha1
kind: VSecMSecret
metadata:
name: database-credentials
spec:
vsecmSafeURL: https://path/to/vsecm/safe/api/endpoint
secretType: k8s
refreshInterval: 1h
target:
name: database-credentials
data:
- key: username
ref: "databaseCredentials.cocaCola.data.username"
- key: password
ref: "databaseCredentials.cocaCola.data.password"
labels:
- key: app
ref: databaseCredentials.cocaCola.labels.app
annotations:
- key: env
ref: databaseCredentials.cocaCola.annotations.env
Setting Up the Project
While we could build the operator from scratch, using the Operator SDK makes our lives easier. Let’s walk through the process:
- Initialize the project:
operator-sdk init --domain vsecm.com --repo github.com/yourusername/vsecm-operator
- Create the API and controller:
operator-sdk create api --group vsecm --version v1alpha1 --kind VSecMSecret --resource --controller
Implementing the Controller
The heart of our operator is the controller. Here’s the key implementation:
func (r *VSecMSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// Fetch the VSecMSecret instance
var vSecMSecret vsecmv1alpha1.VSecMSecret
if err := r.Get(ctx, req.NamespacedName, &vSecMSecret); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// Fetch secrets from VSecM
secretData, err := r.fetchSecretsFromVSecM(vSecMSecret.Spec.VSecMSafeURL)
if err != nil {
logger.Error(err, "Failed to fetch secrets from VSecM")
return ctrl.Result{}, err
}
// Create or update Kubernetes Secret
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: vSecMSecret.Spec.Target.Name,
Namespace: vSecMSecret.Namespace,
},
}
// Parse and set secret data, labels, and annotations
// ... (implementation details)
// Schedule the next reconciliation based on the refresh interval
refreshInterval, _ := time.ParseDuration(vSecMSecret.Spec.RefreshInterval)
return ctrl.Result{RequeueAfter: refreshInterval}, nil
}
Deploying the Operator
To deploy our operator, we need several components:
- The Custom Resource Definition (CRD):
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: vsecmsecrets.vsecm.com
spec:
group: vsecm.com
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
# ... (schema details)
- RBAC Configuration:
apiVersion: v1
kind: ServiceAccount
metadata:
name: vsecm-operator
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: vsecm-operator
rules:
- apiGroups: ["vsecm.com"]
resources: ["vsecmsecrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- Operator Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vsecm-operator
spec:
replicas: 1
selector:
matchLabels:
app: vsecm-operator
template:
metadata:
labels:
app: vsecm-operator
spec:
serviceAccountName: vsecm-operator
containers:
- name: operator
image: your-registry/vsecm-operator:v0.1.0
Using the Operator
Once deployed, using the operator is as simple as creating a VSecMSecret resource:
kubectl apply -f - <<EOF
apiVersion: vsecm.com/v1alpha1
kind: VSecMSecret
metadata:
name: database-credentials
spec:
vsecmSafeURL: https://vsecm.example.com
secretType: k8s
refreshInterval: 1h
target:
name: database-credentials
data:
- key: username
ref: "databaseCredentials.cocaCola.data.username"
- key: password
ref: "databaseCredentials.cocaCola.data.password"
EOF
Production Considerations
When deploying this operator in production, consider:
Security:
- Implement proper SPIFFE mTLS authentication
- Use secure connection strings
- Implement proper error handling for secret management
Performance:
- Set appropriate resource limits
- Configure reasonable refresh intervals
- Implement caching if needed
Monitoring:
- Add metrics for secret refresh operations
- Monitor operator health
- Set up alerts for failed operations
High Availability:
- Consider running multiple replicas
- Implement proper leader election
- Handle network partitions gracefully
Conclusion
Building a Kubernetes Operator for secret management demonstrates the power and flexibility of Kubernetes’ extension mechanisms. By automating the synchronization of secrets between VSecM and Kubernetes, we’ve created a solution that:
- Reduces manual intervention in secret management
- Provides a declarative way to configure secret synchronization
- Maintains consistency between VSecM and Kubernetes secrets
- Allows for easy integration with existing workflows
The complete source code for this operator is available on GitHub at github.com/yourusername/vsecm-operator.
Remember that secret management is critical to your application’s security. Always review and test thoroughly before deploying to production, and ensure you follow your organization’s security policies and best practices.