Skip to main content

Integrate External Secrets Operator

External Secrets Operator (ESO) runs in a cluster, creates Kubernetes Secrets from values retrieved from Secrets Manager, and refreshes them.

This guide covers integration configuration, supported scope, and secret usage for the KakaoCloud ESO distribution used with Kubernetes Engine.

info

Scope and limitations​

The supported features and limitations of the KakaoCloud ESO distribution are as follows.

Supported features​

  • Retrieve values by secret ID
  • Retrieve the default or a specified version
  • Store a selected JSON field under a specified key
  • Store all top-level keys and values in a JSON object
  • Create and refresh Kubernetes Secrets according to a synchronization policy

Limitations​

  • The KakaoCloud distribution supports only the KakaoCloud provider.
  • Creating, modifying, or deleting Secrets Manager secrets through ESO is not supported.
  • Searching for and synchronizing multiple Secrets Manager secrets by name or tag (dataFrom.find) is not supported.

Prerequisites​

Distribution information​

This section describes the installation tools, image, and version of the KakaoCloud ESO distribution used with Kubernetes Engine.

Deployment information​

ItemValue
Installation toolsHelm 3, kubectl
Helm chartoci://ghcr.io/kakaoenterprise/charts/external-secrets
Helm chart version2.10.0-kc.1
Container imageghcr.io/kakaoenterprise/external-secrets-kakaocloud:v2.10.0-kc.1
Base ESO versionExternal Secrets Operator 2.10.0
ProviderKakaoCloud Secrets Manager
Provider modeRead-only

Version information​

This distribution is based on External Secrets Operator (ESO) 2.10.0 and adds integration with KakaoCloud Secrets Manager. Helm chart versions and image tags use the kc identifier to distinguish this distribution from the ESO project distribution.

The current version in the table is 2.10.0-kc.1. 2.10.0-kc.2 and 2.11.0-kc.1 are examples for explaining version numbering and do not indicate actual availability or a release schedule.

ItemHelm chart versionImage tag
First KakaoCloud distribution based on ESO 2.10.02.10.0-kc.1v2.10.0-kc.1
Example: KakaoCloud revision based on the same ESO version2.10.0-kc.2v2.10.0-kc.2
Example: First KakaoCloud distribution based on ESO 2.11.02.11.0-kc.1v2.11.0-kc.1
caution

The KakaoCloud distribution is configured for the KakaoCloud environment based on External Secrets Operator. Check the complete image address and deployment version when installing, troubleshooting, upgrading, or rolling back.

Changelog​

Major changes to the KakaoCloud ESO distribution are listed below. Entries are shown from newest to oldest; release dates are based on GHCR publication.

VersionRelease dateMajor changes
2.10.0-kc.12026-09-15KakaoCloud distribution based on ESO 2.10.0

Integration architecture​

ESO uses the SecretStore specified by an ExternalSecret to obtain connection settings and credentials. IAM access keys stored in a Kubernetes Secret are used for authentication.

ResourceRole
Kubernetes Secret — IAM credentialsCreated by the user to store the IAM access key ID and secret access key used by ESO
SecretStoreDefines the KakaoCloud endpoints and references the Secret containing IAM credentials
ExternalSecretDefines the Secrets Manager secret ID to retrieve and the synchronization target
Secrets Manager secretManages source values and versions delivered to applications
Kubernetes Secret — synchronized valueStores values retrieved by ESO for use as environment variables or files

Basic integration configuration​

Use the installed ESO to synchronize Secrets Manager values to Kubernetes Secrets. For the complete procedure and commands, see the basic integration steps in the installation tutorial.

  1. Prepare a namespace.
  2. Create an IAM credentials Secret.
  3. Configure and apply the endpoints and credential reference in a SecretStore.
  4. Specify the Secrets Manager secret ID and target Kubernetes Secret in an ExternalSecret, then apply it.
  5. Verify synchronization.

The examples below use the eso-demo namespace and kakaocloud-secret-store created by this procedure. Follow each example for instructions on writing and applying its YAML.

The main settings are as follows.

ResourceSettingDescription
SecretStorespec.provider.kakaocloud.secretsManagerEndpointHTTPS base URL for Secrets Manager
SecretStorespec.provider.kakaocloud.auth.iamEndpointHTTPS base URL for IAM
SecretStorespec.provider.kakaocloud.auth.secretRefReference to the Kubernetes Secret containing IAM access keys
ExternalSecretspec.secretStoreRefName and kind of the SecretStore
ExternalSecretspec.target.nameName of the synchronized Kubernetes Secret
ExternalSecretspec.data[].remoteRef.keySecrets Manager secret ID, not its name
ExternalSecretspec.data[].remoteRef.versionVersion number string; omitted means the default version
ExternalSecretspec.data[].secretKeyKey under which to store the retrieved value
ExternalSecretspec.data[].remoteRef.propertyJSON field to retrieve; omitted means the entire value
ExternalSecretspec.dataFrom[].extract.keyID of the JSON secret whose top-level fields are extracted
ExternalSecretspec.dataFrom[].extract.versionVersion number string; omitted means the default version
Configuration notes
  • Enter only HTTPS base URLs for endpoints. Do not include API paths, query strings, or credentials.
  • The Secrets Manager value must be a valid JSON object when using remoteRef.property or dataFrom.extract.
  • Applying field extraction to a plain string or specifying a missing field causes synchronization to fail.

Use private endpoints​

To use private endpoints, set spec.provider.kakaocloud.secretsManagerEndpoint and spec.provider.kakaocloud.auth.iamEndpoint in the SecretStore to the HTTPS base URLs provided for each service. Use the actual endpoint addresses, such as those under *.kakaocloud-in.com, and do not append /identity/v3/auth/tokens to the IAM URL.

Verify that the ESO controller pod can resolve the addresses to private IPs, connect over HTTPS, and validate the TLS certificates. Services that do not use private endpoints, as well as GHCR, may still require an external communication path.

Pin specific secret version​

To keep using a specific version for reproducible deployments or staged rotation, set remoteRef.version in spec.data and reapply the manifest. Enter the secret ID and version in the fields below. When a version is pinned, changing the default version does not make the target Kubernetes Secret follow that new default.

Pin a version
  data:
- secretKey: password
remoteRef:
key: ${SECRET_ID}
version: "${SECRET_VERSION}"
VariableDescription
SECRET_ID🖌︎Secrets Manager secret ID
SECRET_VERSION🖌︎Version number

To pin a specific version while synchronizing all top-level keys and values of a JSON object, set extract.version in spec.dataFrom. If omitted, the default version is retrieved. Place the following configuration under spec.

Pin a JSON object version
  dataFrom:
- extract:
key: ${SECRET_ID}
version: "${SECRET_VERSION}"
VariableDescription
SECRET_ID🖌︎Secrets Manager secret ID
SECRET_VERSION🖌︎Version number
caution

remoteRef.version and extract.version refer to Secrets Manager versions. Both fields are strings, so enclose version numbers in quotation marks.

Synchronize JSON secret​

Prepare a Secrets Manager secret containing the JSON object below and enter its ID in each YAML example.

Both examples use refreshInterval: 1h0m0s. The Kubernetes Secret is first created after the manifest is applied; subsequent changes to the remote value are reflected at the configured one-hour interval.

Example secret value
{
"username": "app-user",
"password": "app-password",
"endpoint": "db.example.internal"
}

Select specific properties​

Use remoteRef.property to select fields and secretKey to set their Kubernetes Secret keys. The following example stores username and password under keys with the same names.

app-credentials.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-credentials
namespace: eso-demo
spec:
refreshPolicy: Periodic
refreshInterval: 1h0m0s
secretStoreRef:
name: kakaocloud-secret-store
kind: SecretStore
target:
name: app-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: ${SECRET_ID}
property: username
- secretKey: password
remoteRef:
key: ${SECRET_ID}
property: password
VariableDescription
SECRET_ID🖌︎Secrets Manager secret ID
Apply and verify app-credentials
kubectl apply -f app-credentials.yaml
kubectl wait --for=condition=Ready \
externalsecret/app-credentials -n eso-demo --timeout=60s
kubectl get secret app-credentials -n eso-demo \
-o go-template='{{range $key, $_ := .data}}{{$key}}{{"\n"}}{{end}}'

This example succeeds when the ExternalSecret reports Ready=True and the output contains the username and password keys.

Extract all top-level JSON fields​

Use dataFrom.extract to store every top-level key and value in the Kubernetes Secret. The following example creates the username, password, and endpoint keys and stores each field value under its corresponding key.

database-credentials.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: eso-demo
spec:
refreshPolicy: Periodic
refreshInterval: 1h0m0s
secretStoreRef:
name: kakaocloud-secret-store
kind: SecretStore
target:
name: database-credentials
creationPolicy: Owner
dataFrom:
- extract:
key: ${SECRET_ID}
VariableDescription
SECRET_ID🖌︎Secrets Manager secret ID
Apply and verify database-credentials
kubectl apply -f database-credentials.yaml
kubectl wait --for=condition=Ready \
externalsecret/database-credentials -n eso-demo --timeout=60s
kubectl get secret database-credentials -n eso-demo \
-o go-template='{{range $key, $_ := .data}}{{$key}}{{"\n"}}{{end}}'

This example succeeds when the ExternalSecret reports Ready=True and the output contains the username, password, and endpoint keys.

To pass only the fields required by the application, select them with remoteRef.property as shown above.

Pass application settings​

Pass the app-credentials Secret created in the JSON synchronization example to an application as environment variables or mounted files. Choose one of the following methods to match how the application uses the values.

Inject environment variables​

First create app-credentials using the JSON synchronization example above. Add the following partial configuration to the relevant container under spec.template.spec.containers[] in a Deployment in the eso-demo namespace.

Environment variable mapping
env:
- name: APP_USERNAME
valueFrom:
secretKeyRef:
name: app-credentials
key: username
- name: APP_PASSWORD
valueFrom:
secretKeyRef:
name: app-credentials
key: password
info

Refreshing a Kubernetes Secret does not automatically change environment variables in running containers. Recreate the pod or configure a workload restart policy to apply new values.

Mount Secret volume​

Mount each key of app-credentials from the JSON synchronization example as a file. Enter the image address of an application configured to read these files in the input field below the code, then create the following file.

app-credentials-volume.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-credentials-volume
namespace: eso-demo
spec:
replicas: 1
selector:
matchLabels:
app: app-credentials-volume
template:
metadata:
labels:
app: app-credentials-volume
spec:
containers:
- name: app
image: ${APPLICATION_IMAGE}
volumeMounts:
- name: app-credentials
mountPath: /etc/app-credentials
readOnly: true
volumes:
- name: app-credentials
secret:
secretName: app-credentials
items:
- key: username
path: username
- key: password
path: password
VariableDescription
APPLICATION_IMAGE🖌︎Application image address
Apply the volume mount workload
kubectl apply -f app-credentials-volume.yaml
kubectl rollout status deployment/app-credentials-volume -n eso-demo --timeout=120s

The container receives /etc/app-credentials/username and /etc/app-credentials/password, each containing the value of the corresponding Secret key. In this example, they contain app-user and app-password. In addition to checking Deployment readiness, verify that the application reads these paths.

When ESO updates the Kubernetes Secret, the mounted files are automatically updated after a delay that depends on cluster settings and state. If the application reads its configuration only at startup, reload the configuration or restart the pod to use the new values.

Pass TLS certificate to Ingress​

Synchronize a TLS certificate and private key to a Kubernetes Secret and reference it from an existing Ingress.

Prepare the following resources:

  • An Ingress and backend service in eso-demo
  • An ingress controller that handles that Ingress
  • A domain for the service

Create a separate JSON secret in KakaoCloud Secrets Manager with the following fields.

FieldValue
certificateA PEM certificate including any required intermediate certificate chain
privateKeyThe PEM private key paired with certificate

Enter the certificate and private key as PEM strings without additional Base64 encoding.

Preserve PEM line breaks

Preserve line breaks in the PEM certificate and private key. In JSON, represent line breaks within strings as \n. Do not replace line breaks with spaces.

The following example shows the JSON input format. Replace each complete string, including certificate content and private key content, with the actual PEM certificate and private key. Preserve the original private key's BEGIN and END markers.

TLS certificate and private key to store in Secrets Manager
{
"certificate": "-----BEGIN CERTIFICATE-----\ncertificate content\n-----END CERTIFICATE-----\n",
"privateKey": "-----BEGIN PRIVATE KEY-----\nprivate key content\n-----END PRIVATE KEY-----\n"
}

The example uses refreshInterval: 1h0m0s. The TLS Secret is first created after applying the manifest, and subsequent remote changes are reflected at the configured one-hour interval.

Enter the TLS Secrets Manager secret ID in the field below the code. The certificate and private key are mapped to tls.crt and tls.key, and the target Kubernetes Secret type is set to kubernetes.io/tls.

ingress-tls-secret.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: ingress-tls
namespace: eso-demo
spec:
refreshPolicy: Periodic
refreshInterval: 1h0m0s
secretStoreRef:
name: kakaocloud-secret-store
kind: SecretStore
target:
name: ingress-tls
creationPolicy: Owner
template:
type: kubernetes.io/tls
data:
- secretKey: tls.crt
remoteRef:
key: ${SECRET_ID}
property: certificate
- secretKey: tls.key
remoteRef:
key: ${SECRET_ID}
property: privateKey
VariableDescription
SECRET_ID🖌︎TLS Secrets Manager secret ID

Apply the manifest and check the Kubernetes Secret type and data keys.

Apply and verify the TLS Secret
kubectl apply -f ingress-tls-secret.yaml
kubectl wait --for=condition=Ready \
externalsecret/ingress-tls -n eso-demo --timeout=60s
kubectl get secret ingress-tls -n eso-demo \
-o go-template='{{.type}}{{"\n"}}{{range $key, $_ := .data}}{{$key}}{{"\n"}}{{end}}'

Verify that the output includes type kubernetes.io/tls and keys tls.crt and tls.key. This check alone does not validate the certificate or confirm that it matches the private key.

The following is a partial configuration for an existing Ingress in eso-demo. Enter the actual service domain in the field below the code and use it with the existing host and backend settings.

TLS settings in an existing ingress.yaml
spec:
tls:
- hosts:
- ${DOMAIN}
secretName: ingress-tls
VariableDescription
DOMAIN🖌︎Service domain

After configuring the Ingress, verify HTTPS access using the actual domain. If the connection fails, check the ingress controller, DNS, and Load Balancer settings. Also verify that the certificate matches the domain and that no intermediate certificates are missing.

Clean up example resources

After running all service guide examples, see Clean up service guide example resources.

Operations and verification​

This section covers synchronization policies, configuration checks, security, and use across multiple namespaces.

Security and operations
  • Grant IAM principals only the project and Secrets Manager read permissions they need.
  • Use the secret ID, not its name, in remoteRef.key. Never store actual secret values or IAM access keys in YAML files.
  • Do not print secret values during operational checks. Check Ready=True, SecretSynced, and the latest synchronization time; verify data changes separately.
  • Restrict access to kakaocloud-credentials and synchronized Secrets to required users and workloads.

Synchronization policies​

refreshPolicy determines when a Kubernetes Secret is refreshed. If omitted, it behaves as Periodic.

PolicyBehaviorExample
PeriodicRetrieves remote values at refreshInterval; 0s disables periodic retrieval after initial synchronizationAutomatically reflect secret rotation
OnChangeSynchronizes when ExternalSecret metadata or spec changes; refreshInterval does not applyManually apply changes after approval
CreatedOnceDoes not periodically refresh after initial synchronization; refreshInterval does not applyNo periodic remote updates needed

With Periodic and refreshInterval: 0s, changing the force-sync annotation does not trigger a refresh after initial synchronization while the target Kubernetes Secret remains valid.

With creationPolicy: Owner in these examples, even CreatedOnce resynchronizes the target Kubernetes Secret if it is deleted or its data is modified.

Verification criteria​

CheckSuccess criteriaWhat to check first if it fails
SecretStore configuration and authenticationSecretStore reports Ready=TrueIAM credentials, endpoints, and network connectivity
Initial secret retrieval and synchronizationExternalSecret reports Ready=True; the target Kubernetes Secret and expected data keys existSecret ID, read permissions, Secrets Manager connectivity, SecretStore reference, and controller events
Kubernetes Secret refreshThe output matches the value of the Secrets Manager secret version being retrievedWhether the new version exists, refreshPolicy, and version pinning
Environment variable injectionRecreated pods use the new valuePod restart and secretKeyRef typos
Secret volume mountMounted files are updated and the application reloads themWhether subPath is used and whether the application rereads changed files
Ingress TLS connectionCorrect TLS Secret type and successful HTTPS accessIngress controller, DNS, certificate matching the domain, and missing intermediate certificates

SecretStore Ready=True alone does not verify read permission or a successful secret read. Verify the ExternalSecret status and target data.

Use across multiple namespaces​

Use ClusterSecretStore to share connection and authentication settings across namespaces, and ClusterExternalSecret to create and manage the same ExternalSecret configuration in batches. See the ESO documentation for ClusterSecretStore and ClusterExternalSecret.

Troubleshooting​

For installation errors, webhook timeouts, or synchronization failures, see ESO integration troubleshooting.