From a308e80666bd6113816d570a817f4208c0db0422 Mon Sep 17 00:00:00 2001 From: gcaracuel <633810+gcaracuel@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:20:46 +0200 Subject: [PATCH 1/4] Fix namespaced mode --- README.md | 567 ++++++++---------- cmd/main.go | 18 +- config/default/kustomization.yaml | 2 +- config/manager/kustomization.yaml | 4 +- config/rbac/metrics_auth_role-namespaced.yaml | 0 .../templates/deployment.yaml | 6 +- deploy/helm/dapr-trustbundle/values.yaml | 2 +- examples/certificate.yaml | 1 - examples/test-secret.yaml | 1 - 9 files changed, 288 insertions(+), 313 deletions(-) create mode 100644 config/rbac/metrics_auth_role-namespaced.yaml diff --git a/README.md b/README.md index de7bdf9..b7b1d93 100644 --- a/README.md +++ b/README.md @@ -1,428 +1,387 @@ # Dapr Trust Bundle Operator -A Kubernetes operator that helps Dapr to meet Cert-Manager so we can let cert-manager efficiently manage the lifecycle of Dapr's trust bundle certificates. +A Kubernetes operator that bridges the gap between cert-manager and Dapr's trust bundle requirements, enabling automated certificate lifecycle management for Dapr environments. -## The problem +## Overview -Cert Manager is a great tool and pretty much the standard to certificates management in Kubernetes spaces. However Cert Manager comes with some strong opinion on resulting secrets format and key names which collision with Dapr having its own strong opinion on `dapr-trust-bundle` secret and keys to be used by Dapr Sentinel service. +Cert-manager is the de facto standard for certificate management in Kubernetes, but it has strong opinions about secret formats and key names that don't align with Dapr's trust bundle expectations. This operator solves that compatibility issue by automatically transforming cert-manager secrets into Dapr-compatible trust bundles. -## Description +## How It Works -This operator monitors the `dapr-trust-bundle-from-cert-manager` secret and automatically creates/updates a `dapr-trust-bundle` secret with the same data whenever the source secret changes. It's designed to work seamlessly with Dapr's certificate management workflow in the `dapr-system` namespace. -So you will simply generate that `dapr-trust-bundle-from-cert-manager` as a result of a chained cert-manager certificate issuance. +The operator monitors a source secret (typically generated by cert-manager) and automatically creates and maintains a Dapr-compatible trust bundle: -### How it works +1. **Watches** for changes to the source secret (default: `dapr-trust-bundle-from-cert-manager`) +2. **Transforms** the certificate data with proper key renaming: + - `ca.crt` → `ca.crt` (unchanged) + - `tls.key` → `issuer.key` + - `tls.crt` → `issuer.crt` + - Other keys are copied as-is +3. **Creates** both a secret and configmap named `dapr-trust-bundle` +4. **Maintains** synchronization and handles cleanup automatically -1. The operator watches for changes to secrets named `dapr-trust-bundle-from-cert-manager` -2. When this secret is created or updated, the operator: - - Creates or updates a secret named `dapr-trust-bundle` in the same namespace - - Copies all data from the source secret to the target secret with key renaming: - - `ca.crt` → `ca.crt` (unchanged) - - `tls.key` → `issuer.key` - - `tls.crt` → `issuer.crt` - - All other keys are copied as-is - - Sets the target secret type to `Opaque` - - Adds management labels to track the created secret -3. When the source secret is deleted, the target secret is also cleaned up - -``` mermaid +```mermaid graph TD subgraph "Cert Manager" - A[certificate : ca] - B[certificate : bundle] + A[Certificate: CA] + B[Certificate: Bundle] end - C{Dapr-Trustbundle} + C{Dapr Trust Bundle
Operator} - subgraph "Dapr" - D[secrets : dapr-trust-bundle] - E[configmap : dapr-trust-bundle] + subgraph "Dapr Resources" + D[Secret: dapr-trust-bundle] + E[ConfigMap: dapr-trust-bundle] end - subgraph cert-manager_bundle [secret : dapr-cert-bundle-from-cert-manager] + subgraph "Source" + F[Secret: dapr-trust-bundle-from-cert-manager] end - B --> cert-manager_bundle - A --> cert-manager_bundle - cert-manager_bundle --> C - C -- "ca.crt => ca.crt
issuer.crt => tls.crt
issuer.key => tls.key" --> D - C -- "ca.crt => ca.crt" --> E + A --> F + B --> F + F --> C + C -- "Key transformation
ca.crt → ca.crt
tls.crt → issuer.crt
tls.key → issuer.key" --> D + C -- "ca.crt only" --> E ``` -### Configuration +## Quick Start -The operator can be configured using command-line flags: +### Install with Helm (Recommended) -**Configurable Parameters:** -- **Source secret name**: Configurable via `--source-secret-name` flag (default: `dapr-trust-bundle-from-cert-manager`) -- **Target namespace**: Configurable via `--target-namespace` flag (default: `dapr-system`) -- **Target secret name**: Always `dapr-trust-bundle` (created in target namespace) -- **Target ConfigMap name**: Always `dapr-trust-bundle` (created in target namespace) +```bash +# Add and install the Helm chart +helm install dapr-trustbundle deploy/helm/dapr-trustbundle -**Default Configuration:** -- **Source secret name**: `dapr-trust-bundle-from-cert-manager` -- **Target namespace**: `dapr-system` -- **Operator scope**: Single namespace (only monitors the configured target namespace) +# Or install from source +git clone https://github.com/elenpay/dapr-trustbundle.git +cd dapr-trustbundle +helm install dapr-trustbundle deploy/helm/dapr-trustbundle +``` + +### Install with kubectl -**Example with custom configuration:** ```bash -# Using custom source secret and namespace ---source-secret-name=my-cert-secret --target-namespace=production +# Install the latest release +kubectl apply -f https://github.com/elenpay/dapr-trustbundle/releases/latest/download/install.yaml + +# Or use the installation script +curl -sSL https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/scripts/install.sh | bash ``` -**Helm Configuration:** +### Verify Installation + +```bash +# Check operator status +kubectl get pods -n dapr-system -l control-plane=operator + +# Create a test secret in the dapr-system namespace +kubectl apply -f examples/test-secret.yaml -n dapr-system + +# Verify the trust bundle was created +kubectl get secret,configmap dapr-trust-bundle -n dapr-system +``` + +## Configuration + +### Command Line Flags + +The operator supports the following configuration flags: + +| Flag | Default | Description | +|------|---------|-------------| +| `--source-secret-name` | `dapr-trust-bundle-from-cert-manager` | Name of the source secret to monitor | +| `--target-namespace` | `dapr-system` | Namespace to monitor and create resources in | +| `--leader-elect` | `false` | Enable leader election for high availability | +| `--metrics-bind-address` | `:8443` | Address for the metrics endpoint | +| `--health-probe-bind-address` | `:8081` | Address for health and readiness probes | + +### Helm Configuration + ```yaml +# Custom source secret and namespace controller: sourceSecretName: "my-cert-secret" targetNamespace: "production" + +# Use namespace-scoped RBAC for enhanced security rbac: - useClusterRole: false # Use namespace-scoped permissions -``` + useClusterRole: false -## RBAC Permissions +# Enable monitoring +metrics: + serviceMonitor: + enabled: true +``` -The operator requires different permissions based on the deployment mode: +### RBAC Modes -### Cluster-wide RBAC (default) -- `get`, `list`, `watch` on secrets (to monitor source secrets in any namespace) -- `create`, `update`, `patch`, `delete` on secrets and configmaps (to manage target resources) +#### Cluster-wide RBAC +- Grants cluster-wide permissions to manage secrets and configmaps +- Suitable for monitoring multiple namespaces -### Namespace-scoped RBAC (enhanced security) -- `get`, `list`, `watch` on specific source secret by name (e.g., `dapr-trust-bundle-from-cert-manager`) -- `create`, `delete`, `patch`, `update` on secrets and configmaps (for resource creation) -- `get`, `list`, `watch`, `patch`, `update` on `dapr-trust-bundle` secret and configmap specifically -- `update` on finalizers and status for specific secrets only +#### Namespace-scoped RBAC (Default for Enhanced Security) +- Restricts permissions to the target namespace only +- Uses specific resource names for fine-grained access control +- Automatically configures namespace-scoped caching to prevent cluster-wide resource listing +- Recommended for production environments monitoring a single namespace -**Enable namespace-scoped RBAC:** ```yaml -# Helm configuration rbac: - useClusterRole: false + useClusterRole: false # Set true to move to Cluster-wide RBAC controller: - sourceSecretName: "my-cert-secret" targetNamespace: "my-namespace" ``` -## Cleanup +## Installation Methods -**Remove the operator:** +### Helm Chart -```sh -make undeploy +**Basic Installation:** +```bash +helm install dapr-trustbundle deploy/helm/dapr-trustbundle ``` -**For KiND clusters, you can also clean up the entire cluster:** - -```sh -kind delete cluster --name kind +**Production Installation:** +```bash +helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ + --set replicaCount=2 \ + --set resources.limits.memory=256Mi \ + --set metrics.serviceMonitor.enabled=true \ + --set podDisruptionBudget.enabled=true ``` -## Development - -### Prerequisites -- go version v1.24.0+ -- docker version 17.03+ -- kubectl version v1.11.3+ -- Access to a Kubernetes cluster ([KiND](https://kind.sigs.k8s.io/) cluster recommended for development) +**Custom Configuration:** +```bash +helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ + --set controller.sourceSecretName="custom-cert-secret" \ + --set controller.targetNamespace="custom-namespace" \ + --set rbac.useClusterRole=false +``` +### Manifest Files -**Build and push your image to a registry:** +```bash +# Latest stable release +kubectl apply -f https://github.com/elenpay/dapr-trustbundle/releases/latest/download/install.yaml -```sh -make docker-build docker-push IMG=/dapr-trustbundle:tag +# Development version +kubectl apply -f https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/deploy/install.yaml ``` -**Deploy the operator to the cluster:** +### Installation Script + +```bash +# Quick install +curl -sSL https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/scripts/install.sh | bash -```sh -make deploy IMG=/dapr-trustbundle:tag +# Install and test +curl -sSL https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/scripts/install.sh | bash -s -- --test ``` -Or if you are using a a [KiND](https://kind.sigs.k8s.io/) cluster +## Usage Examples -**Quick deployment to KiND cluster:** +### Basic Usage with cert-manager -```sh -# Build Docker image, load to KiND, and deploy -make kind-deploy +1. **Create a cert-manager Certificate:** +```yaml +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: dapr-trust-bundle-cert + namespace: dapr-system +spec: + secretName: dapr-trust-bundle-from-cert-manager + issuerRef: + name: ca-issuer + kind: ClusterIssuer + commonName: dapr-trust-bundle + dnsNames: + - dapr-trust-bundle ``` -**For step-by-step deployment:** +2. **The operator automatically creates:** +- `Secret/dapr-trust-bundle` with transformed keys +- `ConfigMap/dapr-trust-bundle` with CA certificate only -```sh -# Build and load image to KiND cluster -make kind-load +### Testing the Operator -# Deploy to cluster -make deploy -``` +```bash +# 1. Create a test secret in the dapr-system namespace +kubectl apply -f examples/test-secret.yaml -n dapr-system -### Testing the Operator +# 2. Verify target resources were created +kubectl get secret dapr-trust-bundle -n dapr-system -o yaml +kubectl get configmap dapr-trust-bundle -n dapr-system -o yaml -1. **Create a test secret** (this creates it in the `dapr-system` namespace): - ```sh - kubectl apply -f examples/test-secret.yaml - ``` +# 3. Test automatic updates +kubectl patch secret dapr-trust-bundle-from-cert-manager -n dapr-system \ + -p '{"data":{"new-key":"bmV3LXZhbHVl"}}' -2. **Verify the target secret was created:** - ```sh - kubectl get secret dapr-trust-bundle -n dapr-system -o yaml - ``` +# 4. Verify the changes were propagated +kubectl get secret dapr-trust-bundle -n dapr-system -o yaml -3. **Test automatic updates** by modifying the source secret: - ```sh - kubectl patch secret dapr-trust-bundle-from-cert-manager -n dapr-system -p '{"data":{"new-key":"bmV3LXZhbHVl"}}' - kubectl get secret dapr-trust-bundle -n dapr-system -o yaml - ``` +# 5. Monitor operator logs +kubectl logs -n dapr-system -l control-plane=operator -f +``` -4. **Monitor operator logs:** - ```sh - kubectl logs -n dapr-system -l control-plane=operator -f - ``` +## Development -For more details take a look to [./examples/README.md](./examples/README.md) +### Prerequisites -### CI/CD Pipeline +- Go 1.24.0+ +- Docker 17.03+ +- kubectl 1.11.3+ +- Kind (recommended for local development) -This project includes GitHub Actions workflows for automated building and releasing: +### Development Workflow -- **Linting** (`.github/workflows/lint.yml`): - - Runs `golangci-lint` to check Go code for style and correctness issues +```bash +# Clone the repository +git clone https://github.com/elenpay/dapr-trustbundle.git +cd dapr-trustbundle -- **Testing** (`.github/workflows/test.yml`): - - Runs unit tests and integration tests - - Ensures code changes do not break existing functionality +# Create a dedicated Kind cluster for this project +kind create cluster --name dapr-trustbundle -- **End-to-End Testing** (`.github/workflows/test-e2e.yml`): - - Deploys the operator to a test cluster - - Runs end-to-end tests against the deployed operator +# Set kubectl context to the new cluster +kubectl cluster-info --context kind-dapr-trustbundle -- **Docker Build & Push** (`.github/workflows/docker-build-push.yml`): - - Triggers on pushes to `main` branch and tag creation - - Builds multi-platform Docker images (linux/amd64, linux/arm64) - - Pushes to GitHub Container Registry (`ghcr.io`) - - Generates deployment manifests as artifacts - -**Image Tagging Strategy:** -- `main` - Latest development version from main branch -- `v1.0.0` - Stable release versions -- `pr-123` - Pull request builds for testing +# Build and load the operator image into the Kind cluster +make docker-build +kind load docker-image dapr-trustbundle-operator:latest --name dapr-trustbundle -### Project Structure +# Deploy the operator +make deploy +# Run tests +make test ``` -├── cmd/main.go # Application entry point -├── internal/controller/ # Controller implementation -│ └── secret_controller.go # Secret synchronization logic -├── config/ # Kubernetes manifests -│ ├── default/ # Default deployment configuration -│ ├── manager/ # Manager deployment -│ └── rbac/ # RBAC permissions -├── deploy/ # Deployment artifacts -│ ├── install.yaml # Complete installation manifest -│ ├── helm/ # Helm chart -│ │ └── dapr-trustbundle/ # Helm chart directory -│ │ ├── Chart.yaml # Chart metadata -│ │ ├── values.yaml # Default values -│ │ ├── README.md # Chart documentation -│ │ └── templates/ # Kubernetes templates -│ └── helm-packages/ # Packaged Helm charts (.tgz) -├── examples/ # Example secrets and documentation -├── scripts/ # Installation and utility scripts -│ └── install.sh # Automated installation script -├── .github/workflows/ # CI/CD workflows -│ ├── docker-build-push.yml # Docker build and push -│ └── release.yml # GitHub releases -└── Dockerfile # Container image definition -``` - -### Making Changes -1. **Modify controller logic** in `internal/controller/secret_controller.go` -2. **Update RBAC** by adding kubebuilder annotations and running `make manifests` -3. **Test locally** with `make kind-deploy` -4. **Check logs** with `kubectl logs -n dapr-system -l control-plane=operator -f` +### Testing Changes -### Custom Configuration +```bash +# Apply example secret in the dapr-system namespace +kubectl apply -n dapr-system -f examples/test-secret.yaml -The operator now supports configuration via command-line flags, eliminating the need to modify source code: +# Check operator logs +kubectl logs -n dapr-system -l control-plane=operator -f -**Runtime Configuration (Recommended):** -```bash -# Kubernetes deployment with custom flags -kubectl patch deployment controller-manager -n dapr-trustbundle-system \ - --patch '{"spec":{"template":{"spec":{"containers":[{"name":"manager","args":["--leader-elect","--health-probe-bind-address=:8081","--source-secret-name=my-cert-secret","--target-namespace=my-namespace"]}]}}}}' +# Clean up +make undeploy -# Helm deployment with custom values -helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ - --set controller.sourceSecretName=my-cert-secret \ - --set controller.targetNamespace=my-namespace \ - --set rbac.useClusterRole=false +# Clean up when done +make undeploy +kind delete cluster --name dapr-trustbundle ``` -**Development Configuration:** -To modify the controller logic in `internal/controller/secret_controller.go`: +### Project Structure -- Adjust the data transformation logic (key renaming) -- Modify cleanup behavior -- Add custom validation logic -- Extend monitoring capabilities +``` +├── cmd/main.go # Application entry point +├── internal/controller/ # Controller implementation +├── config/ # Kubernetes manifests +│ ├── default/ # Default deployment +│ ├── rbac/ # RBAC permissions +│ └── manager/ # Manager deployment +├── deploy/ # Distribution artifacts +│ ├── install.yaml # Complete installation manifest +│ ├── helm/dapr-trustbundle/ # Helm chart +│ └── helm-packages/ # Packaged charts +├── examples/ # Usage examples +├── scripts/install.sh # Installation script +└── .github/workflows/ # CI/CD pipelines +``` ## Troubleshooting ### Common Issues -1. **ImagePullBackOff**: Ensure you send the newly built image to your cluster, we use `make kind-load` that takes care of this -2. **RBAC errors**: Run `make manifests` and `make deploy` to update permissions -3. **Secret not syncing**: Check operator logs and verify the source secret name matches exactly +| Issue | Cause | Solution | +|-------|-------|----------| +| `ERROR: no nodes found for cluster "kind"` | Using named Kind cluster but make targets expect default cluster | Either use default cluster name (`kind create cluster`) or manually load image (`kind load docker-image dapr-trustbundle-operator:latest --name your-cluster-name`) | +| `ImagePullBackOff` | Image not available in cluster | Run `make kind-load` for Kind clusters | +| RBAC errors | Insufficient permissions | Run `make manifests deploy` to update RBAC | +| `configmaps is forbidden at cluster scope` | Using namespace RBAC but operator tries cluster-wide access | Ensure `rbac.useClusterRole: false` and operator will auto-configure namespace caching | +| Secret not syncing | Source secret name mismatch | Verify `--source-secret-name` flag matches actual secret | +| Operator not starting | Configuration errors | Check operator logs and verify flags | ### Debug Commands -```sh +```bash # Check operator status kubectl get pods -n dapr-system -l control-plane=operator -# View operator logs -kubectl logs -n dapr-system -l control-plane=operator --tail=50 +# View detailed logs +kubectl logs -n dapr-system -l control-plane=operator --tail=100 -# Check RBAC permissions +# Test RBAC permissions kubectl auth can-i create secrets --as=system:serviceaccount:dapr-system:dapr-trustbundle-operator -# List all secrets in dapr-system -kubectl get secrets,configmaps -n dapr-system -``` - -## Project Distribution - -Following the options to release and provide this solution to the users. - -### GitHub Releases (Recommended) +# List all resources +kubectl get secrets,configmaps -n dapr-system | grep dapr-trust-bundle -This project automatically builds and publishes Docker images to GitHub Container Registry when code is pushed to the main branch or when tags are created. - -**Install the latest release:** - -```sh -# Quick install with automated script -curl -sSL https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/dapr-trustbundle/scripts/install.sh | bash - -# Or install and test -curl -sSL https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/dapr-trustbundle/scripts/install.sh | bash -s -- --test - -# Install directly from GitHub releases -kubectl apply -f https://github.com/elenpay/dapr-trustbundle/releases/latest/download/install.yaml +# Check operator configuration +kubectl get deployment -n dapr-system -o yaml ``` -**Docker Images:** - -- **Latest (main branch)**: `ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:main` -- **Stable releases**: `ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:v1.0.0` -- **Development**: `ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:pr-123` - -### Manual Build and Distribution - -#### By providing a bundle with all YAML files - -1. Build the installer for the image built and published in the registry: - -```sh -make build-installer IMG=ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:main -``` - -**NOTE:** The makefile target mentioned above generates an 'install.yaml' -file in the deploy directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without its -dependencies. - -2. Using the installer +### Health Checks -Users can just run 'kubectl apply -f ' to install -the project, i.e.: +```bash +# Check health endpoint +kubectl port-forward -n dapr-system deployment/dapr-trustbundle-operator 8081:8081 +curl http://localhost:8081/healthz -```sh -kubectl apply -f https://raw.githubusercontent.com/elenpay/dapr-trustbundle/main/dapr-trustbundle/deploy/install.yaml +# Check metrics (if enabled) +kubectl port-forward -n dapr-system service/dapr-trustbundle-operator-metrics-service 8443:8443 +curl -k https://localhost:8443/metrics ``` -### By providing a Helm Chart +## CI/CD and Releases -**Install using Helm:** +### Automated Workflows -```sh -# Install from local chart -helm install dapr-trustbundle deploy/helm/dapr-trustbundle +- **Linting**: Code quality checks with golangci-lint +- **Testing**: Unit and integration tests +- **E2E Testing**: Full deployment and functionality testing +- **Docker Build**: Multi-platform images (linux/amd64, linux/arm64) +- **Releases**: Automated GitHub releases with artifacts -# Install with custom values -helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ - --set image.tag=v1.0.0 \ - --set metrics.serviceMonitor.enabled=true +### Image Tagging Strategy -# Install in custom namespace -helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ - --namespace my-namespace \ - --create-namespace \ - --set namespace.name=my-namespace -``` - -**Package the Helm chart:** +- `main` - Latest development version +- `v1.0.0` - Stable release versions -```sh -make helm-package -``` +### Available Images -**Template and verify the Helm chart:** +```bash +# Latest development +ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:main -```sh -make helm-template +# Stable releases +ghcr.io/elenpay/dapr-trustbundle/dapr-trustbundle:v1.0.0 ``` -The Helm chart provides extensive configuration options including: -- Resource limits and requests -- Horizontal Pod Autoscaling -- ServiceMonitor for Prometheus -- Pod Disruption Budgets -- Network Policies -- Custom environment variables - -See `deploy/helm/dapr-trustbundle/README.md` for complete configuration options. - -### Installing with Pre-built Manifests +## Contributing -1. **Build the installer:** - ```sh - make build-installer - ``` +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests for new functionality +5. Run `make test lint` +6. Submit a pull request -2. **Apply the generated manifests:** - ```sh - kubectl apply -f deploy/install.yaml - ``` +### Development Guidelines -### Installing from Source - -1. **Clone the repository:** - ```sh - git clone - cd dapr-trustbundle - ``` - -2. **Deploy with KiND:** - ```sh - make kind-deploy - ``` +- Follow Go best practices and formatting +- Add tests for new features +- Update documentation for user-facing changes +- Ensure CI passes before submitting PRs ## License -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. - http://www.apache.org/licenses/LICENSE-2.0 +--- -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +For more detailed examples and advanced configuration, see the [`examples/`](examples/) directory and [Helm chart documentation](deploy/helm/dapr-trustbundle/README.md). diff --git a/cmd/main.go b/cmd/main.go index 5abfc10..38e4ad1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -30,6 +30,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -186,7 +187,8 @@ func main() { }) } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + // Configure manager options + managerOptions := ctrl.Options{ Scheme: scheme, Metrics: metricsServerOptions, WebhookServer: webhookServer, @@ -204,7 +206,19 @@ func main() { // if you are doing or is intended to do any operation such as perform cleanups // after the manager stops then its usage might be unsafe. // LeaderElectionReleaseOnCancel: true, - }) + } + + // Check if we should use namespace-scoped caching based on environment variable + // This should be set when using namespace-scoped RBAC (Role instead of ClusterRole) + if useNamespaceCache := os.Getenv("USE_NAMESPACE_CACHE"); useNamespaceCache == "true" { + setupLog.Info("Using namespace-scoped cache for enhanced security", "namespace", targetNamespace) + // Limit the cache to only watch the target namespace + managerOptions.Cache.DefaultNamespaces = map[string]cache.Config{ + targetNamespace: {}, + } + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 8e2785b..3d51269 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,5 +1,5 @@ # Adds namespace to all resources. -namespace: dapr-trustbundle-system +namespace: dapr-system # Value of this field is prepended to the # names of all resources, e.g. a deployment named diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index b1269b9..f12b890 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: example.com/dapr-trustbundle - newTag: v0.0.1 + newName: dapr-trustbundle-operator + newTag: latest diff --git a/config/rbac/metrics_auth_role-namespaced.yaml b/config/rbac/metrics_auth_role-namespaced.yaml new file mode 100644 index 0000000..e69de29 diff --git a/deploy/helm/dapr-trustbundle/templates/deployment.yaml b/deploy/helm/dapr-trustbundle/templates/deployment.yaml index 330e7cc..e2981ef 100644 --- a/deploy/helm/dapr-trustbundle/templates/deployment.yaml +++ b/deploy/helm/dapr-trustbundle/templates/deployment.yaml @@ -40,8 +40,12 @@ spec: {{- end }} - --source-secret-name={{ .Values.controller.sourceSecretName }} - --target-namespace={{ .Values.controller.targetNamespace }} - {{- with .Values.env }} env: + {{- if not .Values.rbac.useClusterRole }} + - name: USE_NAMESPACE_CACHE + value: "true" + {{- end }} + {{- with .Values.env }} {{- toYaml . | nindent 10 }} {{- end }} ports: diff --git a/deploy/helm/dapr-trustbundle/values.yaml b/deploy/helm/dapr-trustbundle/values.yaml index 4a3d23c..f014d72 100644 --- a/deploy/helm/dapr-trustbundle/values.yaml +++ b/deploy/helm/dapr-trustbundle/values.yaml @@ -103,7 +103,7 @@ rbac: # Use cluster-wide permissions (ClusterRole) or namespace-scoped permissions (Role) # Only applicable when rbac.create is true # Set to false for namespace-scoped permissions when operator only monitors one namespace - useClusterRole: true + useClusterRole: false # Additional RBAC rules can be added here additionalRules: [] diff --git a/examples/certificate.yaml b/examples/certificate.yaml index 5ec866a..2035769 100644 --- a/examples/certificate.yaml +++ b/examples/certificate.yaml @@ -10,7 +10,6 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: dapr-sentry-root-ca - namespace: dapr-system spec: secretName: dapr-sentry-root-ca commonName: dapr-sentry-root-ca-from-cert-manager diff --git a/examples/test-secret.yaml b/examples/test-secret.yaml index 1e68307..e1895a5 100644 --- a/examples/test-secret.yaml +++ b/examples/test-secret.yaml @@ -2,7 +2,6 @@ apiVersion: v1 kind: Secret metadata: name: dapr-trust-bundle-from-cert-manager - namespace: dapr-system type: kubernetes.io/tls data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJvRENDQVVhZ0F3SUJBZ0lRUjNFV09oZitUOHRCbHBRbTFLWDVnREFLQmdncWhrak9QUVFEQWpBd01TNHcKTEFZRFZRUURFeVZrWVhCeUxYTmxiblJ5ZVMxeWIyOTBMV05oTFdaeWIyMHRZMlZ5ZEMxdFlXNWhaMlZ5TUI0WApEVEkxTURnd09ERXlNemN6TWxvWERUTXdNRGd3TnpFeU16Y3pNbG93TURFdU1Dd0dBMVVFQXhNbFpHRndjaTF6ClpXNTBjbmt0Y205dmRDMWpZUzFtY205dExXTmxjblF0YldGdVlXZGxjakJaTUJNR0J5cUdTTTQ5QWdFR0NDcUcKU000OUF3RUhBMElBQlBTb3JORjBYKzNpZ09YNFBuOHZ5dWxOeC9NeU5ZWlZFcU9UV01IVlJUZFFnVDRYWXc2UApINldER1RCK2oxVlVKVC9mOFFLSWxNMUR1WWVKV2lPNklUU2pRakJBTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSMDBlUHliVTFUY2w5UU9rYStuQXRtbG5jcVBqQUsKQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBL2FieG0rQ2EyZlN1OElrSHJmY2F6UUFLSTVwaS83L3RkYU9zb3lWWQo3V1lDSURWWXFKKzBFcXB6TTZ1S3UvNysybmcwN01qc0NhSEFQeXllYmZhOFhRR04KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= From f960d454a8cc4d27207c2044dd65c43cf79a572a Mon Sep 17 00:00:00 2001 From: gcaracuel <633810+gcaracuel@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:36:56 +0200 Subject: [PATCH 2/4] Fix namespaced mode --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7b1d93..842fab1 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ kubectl patch secret dapr-trust-bundle-from-cert-manager -n dapr-system \ -p '{"data":{"new-key":"bmV3LXZhbHVl"}}' # 4. Verify the changes were propagated -kubectl get secret dapr-trust-bundle -n dapr-system -o yaml +kubectl get secret,configmap dapr-trust-bundle -n dapr-system -o yaml # 5. Monitor operator logs kubectl logs -n dapr-system -l control-plane=operator -f From 9b4b6a0f32104f6d2dbbd1e3e1b5a005f97eb639 Mon Sep 17 00:00:00 2001 From: gcaracuel <633810+gcaracuel@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:40:48 +0200 Subject: [PATCH 3/4] Fix namespaced mode --- test/e2e/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 593b5d7..455813c 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -31,7 +31,7 @@ import ( ) // namespace where the project is deployed in -const namespace = "dapr-trustbundle-system" +const namespace = "dapr-system" // serviceAccountName created for the project const serviceAccountName = "dapr-trustbundle-operator" From 98f469c3db47677e51d00ce49c7beef2a26b2578 Mon Sep 17 00:00:00 2001 From: gcaracuel <633810+gcaracuel@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:25:19 +0200 Subject: [PATCH 4/4] Fix namespaced mode --- README.md | 35 ++++++++++++++----- cmd/main.go | 11 ------ config/rbac/role-namespaced.yaml | 15 ++++---- .../templates/deployment.yaml | 4 --- .../helm/dapr-trustbundle/templates/role.yaml | 11 ++++-- 5 files changed, 44 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 842fab1..90a39d2 100644 --- a/README.md +++ b/README.md @@ -115,21 +115,32 @@ metrics: ### RBAC Modes -#### Cluster-wide RBAC +The operator supports two RBAC modes depending on your deployment method: + +#### Cluster-wide RBAC (Kustomize Default) +- **Used by**: `make deploy` (kustomize) - Grants cluster-wide permissions to manage secrets and configmaps - Suitable for monitoring multiple namespaces +- Uses ClusterRole and ClusterRoleBinding -#### Namespace-scoped RBAC (Default for Enhanced Security) +#### Namespace-scoped RBAC (Helm Default - Enhanced Security) +- **Used by**: Helm chart with `rbac.useClusterRole: false` (default) - Restricts permissions to the target namespace only - Uses specific resource names for fine-grained access control - Automatically configures namespace-scoped caching to prevent cluster-wide resource listing - Recommended for production environments monitoring a single namespace +**Helm Configuration Examples:** ```yaml +# Use namespace-scoped RBAC (default) rbac: - useClusterRole: false # Set true to move to Cluster-wide RBAC + useClusterRole: false controller: targetNamespace: "my-namespace" + +# Or switch to cluster-wide RBAC +rbac: + useClusterRole: true ``` ## Installation Methods @@ -250,7 +261,15 @@ kubectl cluster-info --context kind-dapr-trustbundle make docker-build kind load docker-image dapr-trustbundle-operator:latest --name dapr-trustbundle -# Deploy the operator +# Deploy the operator (choose one method) + +# Option 1: Deploy with Helm (Recommended - namespace-scoped RBAC by default) +helm install dapr-trustbundle deploy/helm/dapr-trustbundle \ + --set image.repository=dapr-trustbundle-operator \ + --set image.tag=latest \ + --set image.pullPolicy=Never + +# Option 2: Deploy with kustomize (cluster-wide RBAC) make deploy # Run tests @@ -267,10 +286,6 @@ kubectl apply -n dapr-system -f examples/test-secret.yaml kubectl logs -n dapr-system -l control-plane=operator -f # Clean up -make undeploy - -# Clean up when done -make undeploy kind delete cluster --name dapr-trustbundle ``` @@ -301,10 +316,12 @@ kind delete cluster --name dapr-trustbundle | `ERROR: no nodes found for cluster "kind"` | Using named Kind cluster but make targets expect default cluster | Either use default cluster name (`kind create cluster`) or manually load image (`kind load docker-image dapr-trustbundle-operator:latest --name your-cluster-name`) | | `ImagePullBackOff` | Image not available in cluster | Run `make kind-load` for Kind clusters | | RBAC errors | Insufficient permissions | Run `make manifests deploy` to update RBAC | -| `configmaps is forbidden at cluster scope` | Using namespace RBAC but operator tries cluster-wide access | Ensure `rbac.useClusterRole: false` and operator will auto-configure namespace caching | +| `configmaps is forbidden at cluster scope` | Using namespace RBAC but operator tries cluster-wide access | Switch to Helm deployment (uses namespace-scoped RBAC by default) or set `rbac.useClusterRole: true` | | Secret not syncing | Source secret name mismatch | Verify `--source-secret-name` flag matches actual secret | | Operator not starting | Configuration errors | Check operator logs and verify flags | +> **Note**: `make deploy` (kustomize) uses cluster-wide RBAC by default, while Helm uses namespace-scoped RBAC by default. For enhanced security, prefer Helm deployment. + ### Debug Commands ```bash diff --git a/cmd/main.go b/cmd/main.go index 38e4ad1..38c03dc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -30,7 +30,6 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -208,16 +207,6 @@ func main() { // LeaderElectionReleaseOnCancel: true, } - // Check if we should use namespace-scoped caching based on environment variable - // This should be set when using namespace-scoped RBAC (Role instead of ClusterRole) - if useNamespaceCache := os.Getenv("USE_NAMESPACE_CACHE"); useNamespaceCache == "true" { - setupLog.Info("Using namespace-scoped cache for enhanced security", "namespace", targetNamespace) - // Limit the cache to only watch the target namespace - managerOptions.Cache.DefaultNamespaces = map[string]cache.Config{ - targetNamespace: {}, - } - } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions) if err != nil { setupLog.Error(err, "unable to start manager") diff --git a/config/rbac/role-namespaced.yaml b/config/rbac/role-namespaced.yaml index 9c04279..e0b9ba3 100644 --- a/config/rbac/role-namespaced.yaml +++ b/config/rbac/role-namespaced.yaml @@ -5,15 +5,22 @@ metadata: name: manager-role-namespaced namespace: dapr-system # This would be replaced with actual target namespace rules: -# Permission to read the source secret +# Permission to list secrets and configmaps in namespace (required for controller-runtime cache) - apiGroups: - "" resources: - secrets + - configmaps verbs: - - get - list - watch +# Permission to read the source secret by name +- apiGroups: + - "" + resources: + - secrets + verbs: + - get resourceNames: - dapr-trust-bundle-from-cert-manager # This would be replaced with actual source secret name # Permission to create secrets and configmaps (no resourceNames needed for create) @@ -32,8 +39,6 @@ rules: - secrets verbs: - get - - list - - watch - patch - update resourceNames: @@ -44,8 +49,6 @@ rules: - configmaps verbs: - get - - list - - watch - patch - update resourceNames: diff --git a/deploy/helm/dapr-trustbundle/templates/deployment.yaml b/deploy/helm/dapr-trustbundle/templates/deployment.yaml index e2981ef..1f8d3f9 100644 --- a/deploy/helm/dapr-trustbundle/templates/deployment.yaml +++ b/deploy/helm/dapr-trustbundle/templates/deployment.yaml @@ -41,10 +41,6 @@ spec: - --source-secret-name={{ .Values.controller.sourceSecretName }} - --target-namespace={{ .Values.controller.targetNamespace }} env: - {{- if not .Values.rbac.useClusterRole }} - - name: USE_NAMESPACE_CACHE - value: "true" - {{- end }} {{- with .Values.env }} {{- toYaml . | nindent 10 }} {{- end }} diff --git a/deploy/helm/dapr-trustbundle/templates/role.yaml b/deploy/helm/dapr-trustbundle/templates/role.yaml index b3d7d80..3300f19 100644 --- a/deploy/helm/dapr-trustbundle/templates/role.yaml +++ b/deploy/helm/dapr-trustbundle/templates/role.yaml @@ -7,15 +7,22 @@ metadata: labels: {{- include "dapr-trustbundle.labels" . | nindent 4 }} rules: -# Permission to read the source secret +# Permission to list secrets and configmaps in namespace (required for controller-runtime cache) - apiGroups: - "" resources: - secrets + - configmaps verbs: - - get - list - watch +# Permission to read the source secret by name +- apiGroups: + - "" + resources: + - secrets + verbs: + - get resourceNames: - {{ .Values.controller.sourceSecretName }} # Permission to create secrets and configmaps (no resourceNames needed for create)