diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index e6742ba..2b87e74 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -230,6 +230,19 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `upstreamAuthority.disk.secret.data.certificate` | Certificate to store within disk upstreamAuthority. | `""` | | `upstreamAuthority.disk.secret.data.key` | Key corresponding to the upstreamAuthority. | `""` | | `upstreamAuthority.disk.secret.data.bundle` | Trust bundle for upstreamAuthority. | `""` | +| `upstreamAuthority.ejbca.enabled` | Flag to enable upstream authority plugin with EJBCA | `false` | +| `upstreamAuthority.ejbca.hostname` | Hostname of the connected EJBCA server (e.g. "ejbca.example.com:8443") | `""` | +| `upstreamAuthority.ejbca.caName` | Name of a CA in the connected EJBCA instance that will issue the intermediate signing certificates | `""` | +| `upstreamAuthority.ejbca.endEntityProfileName` | Name of an end entity profile in the connected EJBCA instance that is configured to issue SPIFFE certificates | `""` | +| `upstreamAuthority.ejbca.certificateProfileName` | Name of a certificate profile in the connected EJBCA instance that is configured to issue intermediate CA certificates | `""` | +| `upstreamAuthority.ejbca.endEntityName` | (Optional) Customizes how the End Entity Name is selected. One of "cn", "dns", "uri", "ip", or a custom value | `""` | +| `upstreamAuthority.ejbca.accountBindingId` | (Optional) An account binding ID in EJBCA to associate with issued certificates | `""` | +| `upstreamAuthority.ejbca.secret.create` | If disabled requires you to create a secret with the given keys (clientCert, clientKey and optional caCert) yourself. | `true` | +| `upstreamAuthority.ejbca.secret.name` | If secret creation is disabled, the secret with this name will be used. | `spiffe-ejbca-client` | +| `upstreamAuthority.ejbca.secret.data` | If secret creation is enabled, will create a secret with the following mTLS credentials | | +| `upstreamAuthority.ejbca.secret.data.clientCert` | Client certificate (public key) used to authenticate to EJBCA via mTLS. | `""` | +| `upstreamAuthority.ejbca.secret.data.clientKey` | Client key matching the client certificate used to authenticate to EJBCA. | `""` | +| `upstreamAuthority.ejbca.secret.data.caCert` | CA certificate used to validate the EJBCA server's certificate. Leave empty when the EJBCA server uses a publicly trusted certificate. When secret.create is false, set this to any non-empty value to enable ca_cert_path against your externally provided secret (mirrors the disk plugin's bundle behavior). | `""` | | `upstreamAuthority.awsPCA.enabled` | Flag to enable upstream authority plugin with AWS PCA | `false` | | `upstreamAuthority.awsPCA.region` | AWS Region to use | `""` | | `upstreamAuthority.awsPCA.certificateAuthorityARN` | ARN of the "upstream" CA certificate | `""` | diff --git a/charts/spire/charts/spire-server/templates/_helpers.tpl b/charts/spire/charts/spire-server/templates/_helpers.tpl index 978013e..47479ed 100644 --- a/charts/spire/charts/spire-server/templates/_helpers.tpl +++ b/charts/spire/charts/spire-server/templates/_helpers.tpl @@ -164,6 +164,17 @@ Create the name of the service account to use {{- end -}} {{- end }} +{{- define "spire-server.upstream-ejbca-secret" -}} +{{- $root := . }} +{{- with .Values.upstreamAuthority.ejbca -}} +{{- if eq (.secret.create | toString) "true" -}} +{{ include "spire-server.fullname" $root }}-upstream-ejbca +{{- else -}} +{{ default (include "spire-server.fullname" $root) .secret.name }} +{{- end -}} +{{- end -}} +{{- end }} + {{- define "spire-controller-manager.fullname" -}} {{ include "spire-server.fullname" . | trimSuffix "-server" }}-controller-manager {{- end }} diff --git a/charts/spire/charts/spire-server/templates/configmap.yaml b/charts/spire/charts/spire-server/templates/configmap.yaml index 8094fe1..a029070 100644 --- a/charts/spire/charts/spire-server/templates/configmap.yaml +++ b/charts/spire/charts/spire-server/templates/configmap.yaml @@ -525,6 +525,30 @@ plugins: {{- end }} {{- end }} + {{- with .Values.upstreamAuthority.ejbca }} + {{- if eq (.enabled | toString) "true" }} + {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} + UpstreamAuthority: + ejbca: + plugin_data: + hostname: {{ .hostname | quote }} + client_cert_path: "/run/spire/upstream_ejbca/client.crt" + client_cert_key_path: "/run/spire/upstream_ejbca/client.key" + ca_name: {{ .caName | quote }} + end_entity_profile_name: {{ .endEntityProfileName | quote }} + certificate_profile_name: {{ .certificateProfileName | quote }} + {{- if ne .secret.data.caCert "" }} + ca_cert_path: "/run/spire/upstream_ejbca/ca.crt" + {{- end }} + {{- if ne .endEntityName "" }} + end_entity_name: {{ .endEntityName | quote }} + {{- end }} + {{- if ne .accountBindingId "" }} + account_binding_id: {{ .accountBindingId | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.upstreamAuthority.certManager }} {{- if eq (.enabled | toString) "true" }} {{- $upstreamAuthorityUsed = add1 $upstreamAuthorityUsed }} diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index 0b3b288..ecb31ad 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -417,6 +417,11 @@ spec: mountPath: /run/spire/upstream_ca readOnly: false {{ end }} + {{- if eq (.Values.upstreamAuthority.ejbca.enabled | toString) "true" }} + - name: upstream-ejbca + mountPath: /run/spire/upstream_ejbca + readOnly: true + {{ end }} {{- if gt (len .Values.upstreamAuthority.spire.upstreamDriver) 0 }} - name: upstream-agent mountPath: /run/spire/upstream_agent @@ -650,6 +655,11 @@ spec: secret: secretName: {{ include "spire-server.upstream-ca-secret" . }} {{- end }} + {{- if eq (.Values.upstreamAuthority.ejbca.enabled | toString) "true" }} + - name: upstream-ejbca + secret: + secretName: {{ include "spire-server.upstream-ejbca-secret" . }} + {{- end }} {{- if gt (len .Values.upstreamAuthority.spire.upstreamDriver) 0 }} - name: upstream-agent csi: diff --git a/charts/spire/charts/spire-server/templates/upstream-ejbca-secret.yaml b/charts/spire/charts/spire-server/templates/upstream-ejbca-secret.yaml new file mode 100644 index 0000000..2e316b4 --- /dev/null +++ b/charts/spire/charts/spire-server/templates/upstream-ejbca-secret.yaml @@ -0,0 +1,20 @@ +{{- $root := . }} +{{- with .Values.upstreamAuthority.ejbca }} +{{- if and (eq (.enabled | toString) "true") (eq (.secret.create | toString) "true") }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "spire-server.upstream-ejbca-secret" $root }} + namespace: {{ include "spire-server.namespace" $root }} + labels: + {{- include "spire-server.labels" $root | nindent 4 }} +data: + {{- with .secret.data }} + client.crt: {{ .clientCert | b64enc }} + client.key: {{ .clientKey | b64enc }} + {{- if ne .caCert "" }} + ca.crt: {{ .caCert | b64enc }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index eed2367..a6ebb73 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -454,6 +454,34 @@ upstreamAuthority: key: "" ## @param upstreamAuthority.disk.secret.data.bundle Trust bundle for upstreamAuthority. bundle: "" + ejbca: + ## @param upstreamAuthority.ejbca.enabled Flag to enable upstream authority plugin with EJBCA + enabled: false + ## @param upstreamAuthority.ejbca.hostname Hostname of the connected EJBCA server (e.g. "ejbca.example.com:8443") + hostname: "" + ## @param upstreamAuthority.ejbca.caName Name of a CA in the connected EJBCA instance that will issue the intermediate signing certificates + caName: "" + ## @param upstreamAuthority.ejbca.endEntityProfileName Name of an end entity profile in the connected EJBCA instance that is configured to issue SPIFFE certificates + endEntityProfileName: "" + ## @param upstreamAuthority.ejbca.certificateProfileName Name of a certificate profile in the connected EJBCA instance that is configured to issue intermediate CA certificates + certificateProfileName: "" + ## @param upstreamAuthority.ejbca.endEntityName (Optional) Customizes how the End Entity Name is selected. One of "cn", "dns", "uri", "ip", or a custom value + endEntityName: "" + ## @param upstreamAuthority.ejbca.accountBindingId (Optional) An account binding ID in EJBCA to associate with issued certificates + accountBindingId: "" + secret: + ## @param upstreamAuthority.ejbca.secret.create If disabled requires you to create a secret with the given keys (clientCert, clientKey and optional caCert) yourself. + create: true + ## @param upstreamAuthority.ejbca.secret.name If secret creation is disabled, the secret with this name will be used. + name: "spiffe-ejbca-client" + ## @extra upstreamAuthority.ejbca.secret.data If secret creation is enabled, will create a secret with the following mTLS credentials + data: + ## @param upstreamAuthority.ejbca.secret.data.clientCert Client certificate (public key) used to authenticate to EJBCA via mTLS. + clientCert: "" + ## @param upstreamAuthority.ejbca.secret.data.clientKey Client key matching the client certificate used to authenticate to EJBCA. + clientKey: "" + ## @param upstreamAuthority.ejbca.secret.data.caCert CA certificate used to validate the EJBCA server's certificate. Leave empty when the EJBCA server uses a publicly trusted certificate. When secret.create is false, set this to any non-empty value to enable ca_cert_path against your externally provided secret (mirrors the disk plugin's bundle behavior). + caCert: "" awsPCA: ## @param upstreamAuthority.awsPCA.enabled Flag to enable upstream authority plugin with AWS PCA enabled: false diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 1d4ea8d..bee685c 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -118,6 +118,28 @@ spire-server: Expect(notes).Should(ContainSubstring("\"aws_pca\": {")) }) }) + Describe("spire-server.UpstreamAuthority.ejbca", func() { + It("plugin set ok", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + upstreamAuthority: + ejbca: + enabled: true + hostname: ejbca.example.org:8443 + caName: SpireIntermediateCA + endEntityProfileName: SpireEEP + certificateProfileName: SpireIntermediateCACP + secret: + data: + caCert: dummy-ca +`) + Expect(err).Should(Succeed()) + notes := objs["spire/charts/spire-server/templates/configmap.yaml"] + Expect(notes).Should(ContainSubstring("\"ejbca\": {")) + Expect(notes).Should(ContainSubstring("SpireIntermediateCA")) + Expect(notes).Should(ContainSubstring("ca_cert_path")) + }) + }) Describe("spire-agent.customPlugin.tpm", func() { It("plugin set ok", func() { objs, err := ValueStringRender(chart, `