Back to Blog

Securing Kubernetes Secrets via External Secret Operators

Securing Kubernetes Secrets via External Secret Operators

In a mature GitOps workflow, the mantra is "everything in code." We version control our manifests, our Helm charts, and our infrastructure definitions. However, this paradigm hits a hard wall when encountering sensitive data. You cannot commit raw database credentials, TLS private keys, or API tokens to a Git repository.

The traditional workaround-manually creating Kubernetes `Secret` objects via `kubectl`-is an operational nightmare that breaks the declarative nature of GitOps. While tools like Sealed Secrets provide a way to encrypt secrets within Git, they introduce a complex decryption overhead and-management of private keys.

The modern, enterprise-grade solution is to shift the source of truth entirely out of the cluster and into a dedicated, hardened Secret Management System (SMS) like Hashi/Vault, AWS Secrets Manager, or Google Secret Manager. To bridge the gap between these external providers and the Kubernetes API, we use the External Secrets Operator (ESO).

The Fundamental Problem: The Illusion of Kubernetes Secret Security

Before diving into the solution, we must address a common misconception: Kubernetes Secrets are not inherently secure. By default, a Kubernetes Secret is merely a `base64` encoded string stored in `etcd`. Anyone with `get secret` permissions in the namespace can decode it instantly.

While you can implement Encryption at Rest for `etcd` using a KMS provider, this only protects the data from an attacker gaining access to the physical storage or the etcd backups. It does not protect against a compromised service account or a misconfigured RBAC policy within the cluster.

The goal of a robust security architecture is to ensure that sensitive data is never "at rest" in a vulnerable state within the cluster's configuration, and that access is governed by identity-based policies rather than static, long-lived strings.

How External Secrets Operator (ESO) Works

The External Secrets Operator acts as a synchronization controller. It does not replace your Secret Manager; rather, it automates the lifecycle of synchronizing external values into native Kubernetes `Secret` objects.

The architecture relies on two primary Custom Resource Definitions (CRDs):

  1. `SecretStore` (or `ClusterSecretStore`): This defines how the operator connects to the external provider. It contains the authentication credentials (e.g., IAM roles, ServiceAccount tokens, or API keys) required to talk to AWS, GCP, or Vault.
  2. `ExternalSecret`: This defines what to fetch. It maps specific keys from the external provider to a target Kubernetes `Secret`.

The Reconciliation Loop

The operational magic happens within the ESO controller's reconciliation loop:

  1. Discovery: The controller detects a new `ExternalSecret` resource.
  2. Authentication: The controller uses the credentials defined in the `SecretStore` to authenticate with the provider (e.g., via AWS IRSA or GCP Workload Identity).
  3. Fetching: The controller retrieves the raw, unencrypted payload from the external provider.
  4. Transformation: The controller performs any necessary data transformation (e.g., parsing a JSON string into individual keys).
  5. Injection: The controller creates or updates a native Kubernetes `Secret` in the target namespace.

Practical Implementation: An AWS Example

Let's look at a concrete implementation using AWS Secrets Manager and IAM Roles for Service Accounts (IRSA).

1. The SecretStore Configuration

First, we define a `SecretStore`. Instead of using static IAM user keys, we leverage the Pod's identity. This is critical for a zero-trust architecture.

```yaml

apiVersion: external-secrets.io/v1beta1

kind: SecretStore

metadata:

name: aws-secrets-manager

namespace: app-namespace

spec:

provider:

aws:

service: SecretsManager

region: us-east-1

The controller uses the IAM role attached to its

ServiceAccount to authenticate.

```

2. The ExternalSecret Definition

Now, we define the mapping. Suppose our AWS Secrets Manager contains a secret named `prod/api/credentials` which holds a JSON object: `{"api_key": "super-secret-token", "db_pass": "password123"}`.

```yaml

apiVersion: external-secrets.io/v1beta1

kind: ExternalSecret

metadata:

name: api-credentials-sync

namespace: app-namespace

spec:

refreshInterval: "1h" # How often to poll the provider

secretStore:

name: aws-secrets-manager

target:

name: app-api-secret # The name of the K8s Secret to be created

creationPolicy: Owner

data:

  • secretKey: api_token # The key inside the K8s Secret

remoteRef:

key: prod/api/credentials

property: api_key # The specific key inside the AWS JSON

  • secretKey: db_password

remoteRef:

key: prod/api/credentials

property: db_pass

```

When this is applied, ESO will create a Kubernetes Secret named `app-api-secret` containing the decrypted values.

Operational Considerations

Identity-Based Access (The Perimeter)

The most critical operational requirement is the implementation of Workload Identity. In EKS, this means using IRSA; in GKE, Workload Identity. The `ExternalSecret` controller's ServiceAccount should have an IAM policy that grants `secretsmanager:GetSecretValue` only to the specific ARNs required. This follows the principle of least privilege.

Secret Rotation and Refresh Intervals

One of the primary advantages of ESO is the `refreshInterval`. If you rotate

Conclusion

As shown across "The Fundamental Problem: The Illusion of Kubernetes Secret Security", "How External Secrets Operator (ESO) Works", "Practical Implementation: An AWS Example", a secure implementation for securing kubernetes secrets via external secret operators depends on execution discipline as much as design.

The practical hardening path is to enforce strict token/claim validation and replay resistance, deterministic identity policy evaluation with deny-by-default semantics, and admission-policy enforcement plus workload isolation and network policy controls. This combination reduces both exploitability and attacker dwell time by forcing failures across multiple independent control layers.

Operational confidence should be measured, not assumed: track false-allow rate and time-to-revoke privileged access and mean time to detect and remediate configuration drift, then use those results to tune preventive policy, detection fidelity, and response runbooks on a fixed review cadence.

Related Articles

Explore related cybersecurity topics:

Recommended Next Steps

If this topic is relevant to your organisation, use one of these paths: