Describe the issue:
When using the OpenSearch Kubernetes Operator with externally provided TLS certificates (generate: false), the securityconfig-update job (which runs securityadmin.sh) fails with ca.crt not found if the CA certificate is provided via a separate caSecret rather than being bundled inside the main TLS secret.
The CRD documents that tls.transport.secret and tls.http.secret should contain ca.crt, tls.key and tls.crt data, but also states: “If ca.crt is in a different secret, provide it via the caSecret field”. While the OpenSearch node pods correctly mount and use the caSecret for TLS, the securityconfig-update job does not. It only mounts the main TLS secret and expects ca.crt to be present there.
This is a problem when using external cert-manager issuers (e.g., enterprise PKI issuers) that only populate tls.crt and tls.key in the TLS secret and do not include ca.crt. The CA certificate is available in a separate secret, which is exactly the use case caSecret is designed for but the security-admin job doesn’t honor it.
Expected behavior
When caSecret is configured, the securityconfig-update job should mount ca.crt from the caSecret (just like the OpenSearch node pods do), rather than only looking for it in the main TLS secret.
Actual behavior
The securityconfig-update job only mounts the main TLS secret and fails because ca.crt is missing from it. The caSecret field is ignored by the job.
Steps to reproduce
- Create a TLS secret with only
tls.keyandtls.crt(noca.crt) this is what many enterprise cert-manager issuers produce. - Create a separate secret containing
ca.crt. - Configure the OpenSearchCluster CR with
generate: false, referencing the TLS secret viasecretand the CA secret viacaSecret:
apiVersion: opensearch.opster.io/v1
kind: OpenSearchCluster
metadata:
name: my-cluster
spec:
security:
tls:
transport:
generate: false
secret:
name: my-tls-secret # contains tls.key + tls.crt only
caSecret:
name: my-ca-secret # contains ca.crt
nodesDn:
- "CN=cluster,O=org"
adminDn:
- "CN=cluster,O=org"
http:
generate: false
secret:
name: my-tls-secret
caSecret:
name: my-ca-secret
config:
adminCredentialsSecret:
name: my-admin-creds
adminSecret:
name: my-tls-secret
- OpenSearch node pods start correctly (they honor
caSecret). - The
securityconfig-updatejob fails because it mounts onlymy-tls-secretand looks forca.crtwhich doesn’t exist there.
Workaround
We work around this by programmatically patching ca.crt into the main TLS secret before creating the OpenSearchCluster CR. Our operator reads ca.crt from the CA secret and injects it into the TLS secret using a Kubernetes merge patch. This ensures ca.crt is present in the TLS secret by the time the securityconfig-update job runs.
This works but is a brittle workaround it requires coordination to ensure the patch happens before the CR is created, and must handle cert-manager rotation cycles that could overwrite the secret and remove the injected ca.crt.
Configuration:
- OpenSearch Operator version: 2.8
- OpenSearch cluster version: 3.5.0
- Kubernetes version: 1.31
- TLS setup:
generate: falsewith externally provided certificates via cert-manager - cert-manager issuer: Enterprise PKI issuer (does not populate
ca.crtin the TLS secret) - Deployment: Multi-tenant OpenSearch-as-a-Service platform with per-customer clusters
Relevant CR snippet:
spec:
security:
tls:
transport:
generate: false
secret:
name: os-<ID>-tls-certs # cert-manager populates tls.key + tls.crt only
caSecret:
name: root-ca # separate secret with ca.crt
nodesDn: ["CN=<ID>,O=<cID>"]
adminDn: ["CN=<ID>,O=<cID>"]
http:
generate: false
secret:
name: os-<ID>-tls-certs
caSecret:
name: root-ca
config:
adminCredentialsSecret:
name: os-<ID>-admin-creds
adminSecret:
name: os-<ID>-tls-certs
Relevant Logs or Screenshots:
securityconfig-update job failure log:
OpenSearch Security Admin v7
Will connect to localhost:9300 ... done
ERR: Unable to read the file tls-http/ca.crt
Job pod volume mounts (observed):
The securityconfig-update job mounts only the main TLS secret:
volumeMounts:
- name: tls-http
mountPath: /certs/tls-http # contains tls.crt, tls.key — but NO ca.crt
- name: tls-transport
mountPath: /certs/tls-transport # same secret, same issue
volumes:
- name: tls-http
secret:
secretName: os-<ID>-tls-certs # ← only this secret is mounted
- name: tls-transport
secret:
secretName: os-<ID>-tls-certs
Missing: The caSecret (root-ca) is not mounted in the job pod, even though it’s configured in the CR.
Expected job volume mounts:
volumes:
- name: tls-http
secret:
secretName: os-<ID>-tls-certs
- name: tls-http-ca # ← should be added when caSecret is set
secret:
secretName: root-ca
- name: tls-transport
secret:
secretName: os-<ID>-tls-certs
- name: tls-transport-ca # ← should be added when caSecret is set
secret:
secretName: root-ca
Or alternatively, the job should project ca.crt from caSecret into the same mount path alongside tls.crt/tls.key.
Questions:
-
Is this a known issue? Is there an existing GitHub issue or discussion tracking this behavior where the
securityconfig-updatejob does not honor thecaSecretfield? -
Is there a better workaround? We are currently patching
ca.crtinto the main TLS secret programmatically before the OpenSearchCluster CR is created. This works but is fragile — cert-manager rotation can overwrite the secret and remove our injectedca.crt, requiring re-injection. Is there a recommended or cleaner way to handle this scenario (e.g., a CR field we’re missing, a job override, or a projected volume approach)? -
Is this a bug that needs a fix? The CRD documentation explicitly says “If ca.crt is in a different secret provide it via the caSecret field” — but the securityconfig-update job doesn’t follow this contract. If this is confirmed as a bug, we’d be happy to contribute a fix. A few follow-up questions on that:
- Which release branch should we target for the fix (e.g.,
main, a specific release branch)? - Are there contribution guidelines or a
CONTRIBUTING.mdwe should follow for the opensearch-k8s-operator repo? - Would this be scoped to just the
securityconfig-updatejob, or are there other jobs/components in the operator that also need to honorcaSecret?
- Which release branch should we target for the fix (e.g.,