Terminate TLS on OpenBao with a cert-manager self-signed certificate

Adds a ClusterIssuer (selfSigned) + Certificate, mounts the resulting
secret into the OpenBao pod, and switches the listener config from
tls_disable=1 to a TLS-enabled listener. UI/API is now served over
https://<node-ip>:30200 instead of plain HTTP.

Also updates ClusterSecretStore/bootstrap Job to use https + trust the
self-signed cert via caProvider/BAO_SKIP_VERIFY.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
2026-09-18 20:20:20 +02:00
co-authored by Copilot
parent ffaf7247fe
commit 5ef5aaa0d8
5 changed files with 109 additions and 3 deletions
+21
View File
@@ -8,16 +8,37 @@ via Vault's Kubernetes auth method.
This chart wraps the official [`openbao-helm`](https://github.com/openbao/openbao-helm) This chart wraps the official [`openbao-helm`](https://github.com/openbao/openbao-helm)
chart as a dependency and adds: chart as a dependency and adds:
- A self-signed TLS certificate (via cert-manager) that OpenBao terminates
itself, so both the UI and API are served over HTTPS
- A `ClusterSecretStore` (ESO CRD) pointing at the OpenBao service - A `ClusterSecretStore` (ESO CRD) pointing at the OpenBao service
- A one-time bootstrap `Job` (disabled by default) that enables the - A one-time bootstrap `Job` (disabled by default) that enables the
Kubernetes auth method, a read-only policy, and a role bound to ESO's Kubernetes auth method, a read-only policy, and a role bound to ESO's
ServiceAccount ServiceAccount
## TLS
OpenBao terminates HTTPS itself using a certificate issued by a dedicated
`ClusterIssuer/openbao-selfsigned` (cert-manager, `selfSigned` type - no
external CA involved). The UI/API is reachable at
`https://<node-ip>:30200` (NodePort, same pattern as ArgoCD).
Because the certificate is self-signed, browsers will show a trust warning
- this is expected. `ExternalSecret`/`ClusterSecretStore` traffic from ESO
trusts it automatically via `caProvider` (pointing at the same
`openbao-tls` Secret cert-manager creates), so no manual CA import is
needed for that path.
If you add/replace a node, or want a different reachable hostname, add it to
`tls.ipAddresses` / `tls.dnsNames` in `values.yaml` and cert-manager will
reissue the certificate automatically.
## Prerequisites ## Prerequisites
- ESO must already be installed in the cluster (deployed separately via the - ESO must already be installed in the cluster (deployed separately via the
`apps-in-apps` repo) with a ServiceAccount named `eso.serviceAccountName` `apps-in-apps` repo) with a ServiceAccount named `eso.serviceAccountName`
(default: `external-secrets`) in `eso.namespace` (default: `external-secrets`). (default: `external-secrets`) in `eso.namespace` (default: `external-secrets`).
- cert-manager must already be installed (used for the self-signed TLS cert,
see below).
## Deployment & manual init/unseal ## Deployment & manual init/unseal
+5 -1
View File
@@ -29,7 +29,11 @@ spec:
image: {{ .Values.bootstrap.image }} image: {{ .Values.bootstrap.image }}
env: env:
- name: BAO_ADDR - name: BAO_ADDR
value: "http://{{ include "openbao-gitops.openbaoFullname" . }}:8200" value: "{{ if .Values.tls.enabled }}https{{ else }}http{{ end }}://{{ include "openbao-gitops.openbaoFullname" . }}:8200"
{{- if .Values.tls.enabled }}
- name: BAO_SKIP_VERIFY
value: "true"
{{- end }}
- name: BAO_TOKEN - name: BAO_TOKEN
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
+8 -1
View File
@@ -8,9 +8,16 @@ metadata:
spec: spec:
provider: provider:
vault: vault:
server: "http://{{ include "openbao-gitops.openbaoFullname" . }}.{{ .Release.Namespace }}.svc:8200" server: "{{ if .Values.tls.enabled }}https{{ else }}http{{ end }}://{{ include "openbao-gitops.openbaoFullname" . }}.{{ .Release.Namespace }}.svc:8200"
path: {{ .Values.eso.kvMountPath }} path: {{ .Values.eso.kvMountPath }}
version: v2 version: v2
{{- if .Values.tls.enabled }}
caProvider:
type: Secret
name: {{ .Values.tls.secretName }}
namespace: {{ .Release.Namespace }}
key: tls.crt
{{- end }}
auth: auth:
kubernetes: kubernetes:
mountPath: kubernetes mountPath: kubernetes
+35
View File
@@ -0,0 +1,35 @@
{{- if .Values.tls.enabled }}
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: openbao-selfsigned
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: {{ .Values.tls.secretName }}
namespace: {{ .Release.Namespace }}
spec:
secretName: {{ .Values.tls.secretName }}
duration: {{ .Values.tls.duration }}
renewBefore: {{ .Values.tls.renewBefore }}
issuerRef:
name: openbao-selfsigned
kind: ClusterIssuer
dnsNames:
- {{ include "openbao-gitops.openbaoFullname" . }}
- {{ include "openbao-gitops.openbaoFullname" . }}.{{ .Release.Namespace }}
- {{ include "openbao-gitops.openbaoFullname" . }}.{{ .Release.Namespace }}.svc
- {{ include "openbao-gitops.openbaoFullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
- localhost
{{- range .Values.tls.dnsNames }}
- {{ . }}
{{- end }}
ipAddresses:
- "127.0.0.1"
{{- range .Values.tls.ipAddresses }}
- {{ . | quote }}
{{- end }}
{{- end }}
+40 -1
View File
@@ -10,6 +10,24 @@ openbao:
# Standalone mode: single replica, file storage backend, PVC-backed. # Standalone mode: single replica, file storage backend, PVC-backed.
standalone: standalone:
enabled: true enabled: true
# TLS terminated directly by OpenBao itself, using the cert-manager
# issued self-signed certificate mounted below (see templates/tls.yaml
# and the `tls:` section further down). "-tls-skip-verify" is already
# baked into the chart's readiness/liveness probes, so this doesn't
# need any extra values there.
config: |
ui = true
listener "tcp" {
tls_disable = 0
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/openbao/userconfig/openbao-tls/tls.crt"
tls_key_file = "/openbao/userconfig/openbao-tls/tls.key"
}
storage "file" {
path = "/openbao/data"
}
dataStorage: dataStorage:
enabled: true enabled: true
@@ -24,6 +42,12 @@ openbao:
authDelegator: authDelegator:
enabled: true enabled: true
# Mounts the cert-manager issued TLS secret at
# /openbao/userconfig/openbao-tls/{tls.crt,tls.key,ca.crt}.
extraVolumes:
- type: secret
name: openbao-tls
resources: resources:
requests: requests:
cpu: 250m cpu: 250m
@@ -35,7 +59,7 @@ openbao:
ui: ui:
enabled: true enabled: true
# NodePort, same pattern as ArgoCD (30080/30443) and Gitea (30300): # NodePort, same pattern as ArgoCD (30080/30443) and Gitea (30300):
# reachable at http://<node-ip>:30200 without needing an Ingress. # reachable at https://<node-ip>:30200 without needing an Ingress.
serviceType: NodePort serviceType: NodePort
serviceNodePort: 30200 serviceNodePort: 30200
@@ -43,6 +67,21 @@ openbao:
# Sidecar-injector webhook is not needed for ESO integration. # Sidecar-injector webhook is not needed for ESO integration.
enabled: false enabled: false
# --- TLS (self-signed via cert-manager) -------------------------------------
tls:
enabled: true
secretName: openbao-tls
# cert-manager Certificate validity/renewal.
duration: 2160h # 90d
renewBefore: 360h # 15d
# Extra SANs beyond the in-cluster service names (which are always
# included). Add your node IP(s)/hostname(s) here so browsers hitting the
# NodePort get a matching cert.
ipAddresses:
- 192.168.2.218
dnsNames: []
# --- ESO <-> OpenBao binding ------------------------------------------------- # --- ESO <-> OpenBao binding -------------------------------------------------
eso: eso: