diff --git a/changelog.d/2-features/WPB-6997-smallstep-accomp b/changelog.d/2-features/WPB-6997-smallstep-accomp new file mode 100644 index 00000000000..009652bc588 --- /dev/null +++ b/changelog.d/2-features/WPB-6997-smallstep-accomp @@ -0,0 +1 @@ +Add E2EI configuration setup to smallstep-accomp chart diff --git a/charts/smallstep-accomp/Chart.yaml b/charts/smallstep-accomp/Chart.yaml new file mode 100644 index 00000000000..6dad899102f --- /dev/null +++ b/charts/smallstep-accomp/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: Accompanying chart for Smallstep E2EI support +name: smallstep-accomp +version: 1.0.4 diff --git a/charts/smallstep-accomp/README.md b/charts/smallstep-accomp/README.md new file mode 100644 index 00000000000..3165073192c --- /dev/null +++ b/charts/smallstep-accomp/README.md @@ -0,0 +1,111 @@ +# smallstep-acomp - Helm chart accompanying smallstep + +This Helm chart is meant to be installed alongside the [step-certificates Helm +chart](https://smallstep.github.io/helm-charts) in the same namespace. It has been tested with Helm +chart version `1.25.0` and image + +``` +image: + repository: cr.step.sm/smallstep/step-ca + tag: "0.25.3-rc7" +``` + +This Helm chart provides: + +- A reverse-proxy for Certificate Revocation List (CR) distribution endpoints to federating smallstep + servers +- Smallstep server configuration for the End-to-End Identity setup + + +## Reverse-proxy for CRL distribution points + +This Helm chart installs a reverse proxy that proxies the Certificate Revocation List (CRL) +Distribution Point of the Smallstep servers CRL Certificate Authority (CA) from federating domains +and the own domain. This reverse proxy is required for a working End-to-End Identity setup. + +The Helm chart deploys a nginx server that reverse-proxies +`https:///proxyCrl/` to `https://{other_acme_domain}/crl` +as well as an ingress for the `/proxyCrl` endpoint. For example if `upstreams.proxiedHosts` is set +to `[acme.alpha.example.com, acme.beta.example.com]` and the host for the Smallstep server on the +own domain is `acme.alpha.example.com` this helm chart will forward requests + +- `https://acme.alpha.example.com/proxyCrl/acme.alpha.example.com` to `https://acme.alpha.example.com/crl` +- `https://acme.alpha.example.com/proxyCrl/acme.beta.example.com` to `https://acme.beta.example.com/crl` + +| Name | Description | +| ------------------------- | ----------------------------------------------------------------------------------------- | +| `upstreams.enable` | Set to `false` in case you want to write custom nginx server block for the upstream rules | +| `upstreams.dnsResolver` | DNS server that nginx uses to resolve the proxied hostnames | +| `upstreams.proxiedHosts` | List of smallstep hostnames to proxy. Please also include the own smallstep host here | +| `nginx.ingress.enable` | Set to `false` in case you'd like to define a custom ingress for the /proxyCrl endpoint | +| `nginx.ingress.hostname` | Hostname of the step-ca server | + +For more details on `nginx.*` parameters see README.md documentation in the `nginx` dependency chart. + +## Smallstep server configuration for the End-to-End Identity setup + +This Helm chart helps to create configuration file for step-ca. If `stepConfig.enabled` is `true` a +configmap that contains a `ca.json` will be created. The name of that configmap is compatible with the +step-certificates Helm chart, so that it can be directly used. However since step-ca is deployed +from a seperate Helm release updating and deploying a configuration won't cause an automatic reload +of the step-ca server. It is therefore recommended to manually restart step-ca after configuartion +changes if this Helm chart is used for these purposes. + +For references see: + +- [[1] Configuring `step-ca`](https://smallstep.com/docs/step-ca/configuration/) +- [[2] Configuring `step-ca` Provisioners - ACME for Wire messenger clients ](https://smallstep.com/docs/step-ca/provisioners/#acme-for-wire-messenger-clients) + +| Parameter | Description | +|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| `stepConfig.enabled` | Create a configmap with configuration file for `step-certificates` Helm chart. | +| | If `true` then almost all `stepConfig.*` parameters are required. | +| `stepConfig.configTemplate` | Template for the configuration file. Overwrite this if the default value is not generic enough for your use case. | +| `stepConfig.address` | See [1] | +| `stepConfig.dnsName` | Used in `dnsNames` config entry (See [1]) and to define the CRL URL. | +| `stepConfig.additionalDNSNames` | Optional. Additional entries to `dnsNames` configuration field | +| `stepConfig.root` | See [1]. Public key of the Root CA | +| `stepConfig.crt` | See [1]. Public key of the Intermediate CA | +| `stepConfig.key` | See [1]. Private key of the Intermediate CA | +| `stepConfig.federatedRoots` | See [1]. Add all cross-signed Intermediate CA certs from federating domains here. | +| `stepConfig.db` | See [1] | +| `stepConfig.tls` | See [1] | +| `stepConfig.logger` | See [1] | +| `stepConfig.authority.claims` | See [1] | +| `stepConfig.authority.jwk` | JSON string of the JWK provisioner to use. A JWK provisioner can be created | +| | by running `step ca init` then copying it out of the generated `ca.json`, discarding the `ca.json`. | +| `stepConfig.authority.acme.name` | Name of the ACME provisioner. Default: `"keycloakteams"` | +| `stepConfig.authority.acme.claims` | See [1] | +| `stepConfig.authority.acme.dpop.key` | See [2]. Public half of the DPoP signature key bundle configured of the Wire deployment. | +| | Use the same value as `brig.secrets.dpopSigKeyBundle` value of the `wire-server` Helm chart. | +| | Base64 encoded string of the PEM encoded public key. | +| `stepConfig.authority.acme.dpop.wireDomain` | Set this to the federation domain of the backend | +| `stepConfig.authority.acme.oidc.clientId` | Name of the OIDC client. Default: "wireapp". | +| `stepConfig.authority.acme.oidc.discoveryBaseUrl` | OpenID Connect Discovery endpoint. The OIDC provider must respond with its configuration when `/.well-known/openid-configuration` | +| | is appended to the URL. For Keycloak this URL is of format `https:///auth/realms/`. | +| `stepConfig.authority.acme.oidc.issuerUrl` | For Keycloak this must be of the format `https:///auth/realms/?client_id=wireapp` | +| `stepConfig.authority.acme.oidc.signatureAlgorithms` | See [2] | +| `stepConfig.authority.acme.oidc.transform` | See [2]. Has sensible default. There shouldn't be any need to customize this setting. | +| `stepConfig.authority.acme.x509.organization` | Set this to the federation domain of the backend | +| `stepConfig.authority.acme.x509.template` | See [2]. Go template for a client certificate which is evaluated by step-ca. | +| | This string is evaluated as template of the Helm chart first. | +| | Has a sensible default. There shouldn't be a need to customize this setting. | + +| Parameter | Description | +|-----------------------|-------------------------------------------------------------------------------------------------------| +| `caPassword.enabled` | If `true` generate Secret with a name that the `step-certificates` Helm chart will automatically use. | +| | The Helm chart will mount this at `/home/step/secrets/passwords/password`. | +| `caPassword.password` | Password that decrypts the intermediate CA private key | + +| Parameter | Description | +|---------------------------|-------------------------------------------------------------------------------------------------------| +| `existingSecrets.enabled` | If `true` generate Secret with a name that the `step-certificates` Helm chart will automatically use. | +| `existingSecrets.data` | Map from filename to content. Each entry will be mounted as file `/home/step/secrets/` | +| | Add the private key of the Intermediate CA here. | + +| Parameter | Description | +|-------------------------|-----------------------------------------------------------------------------------------------------| +| `existingCerts.enabled` | If `true` generate ConfigMap with a name that the Helm chart will automatically use. | +| `existingCerts.data` | Map from filename to content. Each entry will be mounted as file `/home/step/certs/` | +| `existingCerts.data` | Use it to make public keys of the Root, intermediate CA as well as the cross-signed certs available | +| | to step-ca. Each entry will be mounted as file `/home/step/certs/` | diff --git a/charts/smallstep-accomp/requirements.yaml b/charts/smallstep-accomp/requirements.yaml new file mode 100644 index 00000000000..e9d0780c6e9 --- /dev/null +++ b/charts/smallstep-accomp/requirements.yaml @@ -0,0 +1,4 @@ +dependencies: +- name: nginx + version: 15.10.4 + repository: https://charts.bitnami.com/bitnami diff --git a/charts/smallstep-accomp/templates/_helpers.tpl b/charts/smallstep-accomp/templates/_helpers.tpl new file mode 100644 index 00000000000..fb5cb93c9ce --- /dev/null +++ b/charts/smallstep-accomp/templates/_helpers.tpl @@ -0,0 +1,3 @@ +{{- define "fullname" -}} +smallstep-step-certificates +{{- end -}} diff --git a/charts/smallstep-accomp/templates/ca-password.yaml b/charts/smallstep-accomp/templates/ca-password.yaml new file mode 100644 index 00000000000..cd1bdc962a9 --- /dev/null +++ b/charts/smallstep-accomp/templates/ca-password.yaml @@ -0,0 +1,12 @@ +{{- if .Values.caPassword.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: smallstep-step-certificates-ca-password + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" +type: Opaque +data: + password: {{ .Values.caPassword.password | b64enc | quote }} +{{- end }} diff --git a/charts/smallstep-accomp/templates/certs.yaml b/charts/smallstep-accomp/templates/certs.yaml new file mode 100644 index 00000000000..c9ef0ce45a9 --- /dev/null +++ b/charts/smallstep-accomp/templates/certs.yaml @@ -0,0 +1,13 @@ +{{- if .Values.existingCerts.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: smallstep-step-certificates-certs + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" +data: + {{- range $key, $value := .Values.existingCerts.data }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} diff --git a/charts/smallstep-accomp/templates/secrets.yaml b/charts/smallstep-accomp/templates/secrets.yaml new file mode 100644 index 00000000000..8448fbc7f8f --- /dev/null +++ b/charts/smallstep-accomp/templates/secrets.yaml @@ -0,0 +1,14 @@ +{{- if .Values.existingSecrets.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: smallstep-step-certificates-secrets + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" +type: Opaque +data: + {{- range $key, $value := .Values.existingSecrets.data }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/charts/smallstep-accomp/templates/server-block-configmap.yaml b/charts/smallstep-accomp/templates/server-block-configmap.yaml new file mode 100644 index 00000000000..366dad7e92e --- /dev/null +++ b/charts/smallstep-accomp/templates/server-block-configmap.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.upstreams.enabled .Values.nginx.existingServerBlockConfigmap }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.nginx.existingServerBlockConfigmap }} + labels: + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + server.conf: | + resolver {{ .Values.upstreams.dnsResolver }}; + + server { + listen 0.0.0.0:8080; + + {{- range .Values.upstreams.proxiedHosts }} + + location /proxyCrl/{{ . }} { + proxy_redirect off; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header Host {{ . }}; + proxy_hide_header Content-Type; + add_header Content-Type application/pkix-crl; + proxy_pass "https://{{ . }}/crl"; + } + + {{- end }} + } +{{- end }} diff --git a/charts/smallstep-accomp/templates/step-config.yaml b/charts/smallstep-accomp/templates/step-config.yaml new file mode 100644 index 00000000000..0cb957fa88c --- /dev/null +++ b/charts/smallstep-accomp/templates/step-config.yaml @@ -0,0 +1,9 @@ +{{- if .Values.stepConfig.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: smallstep-step-certificates-config +data: + ca.json: |- + {{(tpl .Values.stepConfig.configTemplate .) | fromYaml | toJson }} +{{- end }} diff --git a/charts/smallstep-accomp/values.yaml b/charts/smallstep-accomp/values.yaml new file mode 100644 index 00000000000..2f99e7b8334 --- /dev/null +++ b/charts/smallstep-accomp/values.yaml @@ -0,0 +1,208 @@ +nginx: + existingServerBlockConfigmap: "smallstep-accomp-server-block" + + service: + type: ClusterIP + + ingress: + enabled: true + # ingressClassName: "nginx" + + # hostname: "acme.alpha.example.com" + path: "/proxyCrl" + pathType: "Prefix" + + # extraTls: + # - + # hosts: [ "acme.alpha.example.com" ] + # secretName: "smallstep-step-certificates-ingress-cert" + # + +upstreams: + enabled: true + # dnsResolver: 9.9.9.9 + + # Note: include the smallstep host of the own domain here as well + proxiedHosts: [] + # proxiedHosts: + # - acme.alpha.example.com + # - acme.beta.example.com + # - acme.gamma.example.com + + +caPassword: + enabled: true + password: "...." + +existingSecrets: + enabled: false + # data: + # ca.key: foobar + +existingCerts: + enabled: false + # data: + # ca.crt: "-----BEGIN CERTIFICATE-----...." + # root_ca.crt: "-----BEGIN CERTIFICATE-----...." + # ca-other2-cross-signed.crt: "-----BEGIN CERTIFICATE-----...." + # ca-other3-cross-signed.crt: "-----BEGIN CERTIFICATE-----...." + +stepConfig: + enabled: true + + address: "0.0.0.0:9000" + + # dnsName: acme.alpha.example.com + + # additionalDNSNames: + # - localhost + + root: /home/step/certs/root_ca.crt + crt: /home/step/certs/ca.crt + key: /home/step/secrets/ca.key + + federatedRoots: + - /home/step/certs/ca.crt + + # federatedRoots: + # - /home/step/certs/ca.crt + # - /home/step/certs/acme.beta.example.com-xsigned-by-acme.alpha.example.com + + db: + badgerFileLoadingMode: "" + dataSource: /home/step/db + type: badgerv2 + + tls: + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + maxVersion: 1.3 + minVersion: 1.2 + renegotiation: false + + logger: + format: text + + authority: + claims: + maxTLSCertDuration: 87701h + + # jwk: |- + # { + # "type": "JWK", + # "name": "..example.com", + # "key": { ... }, + # "encryptedKey": "e..." + # } + + acme: + name: keycloakteams + + claims: + allowRenewalAfterExpiry: false + defaultTLSCertDuration: 2160h + disableRenewal: false + maxTLSCertDuration: 2160h + minTLSCertDuration: 60s + + dpop: + # key: + wireDomain: alpha.example.com + + oidc: + clientId: wireapp + # discoveryBaseUrl: https://keycloak.example.com/auth/realms/master + # issuerUrl: https://keycloak.example.com/auth/realms/master?client_id=wireapp + signatureAlgorithms: + - RS256 + - ES256 + - ES384 + - EdDSA + transform: | + { + "name": "{{ .name }}", + "preferred_username": "wireapp://%40{{ .preferred_username }}" + } + + x509: + # organization: alpha.example.com + template: | + { + "subject": { + "organization": {{ required "stepConfig.authority.acme.x509.organization is missing" .Values.stepConfig.authority.acme.x509.organization | toJson }}, + "commonName": {{ "{{" }} toJson .Oidc.name {{ "}}" }} + }, + "uris": [{{ "{{" }} toJson .Oidc.preferred_username {{ "}}" }}, {{ "{{" }} toJson .Dpop.sub {{ "}}" }}], + "keyUsage": ["digitalSignature"], + "extKeyUsage": ["clientAuth"], + "crlDistributionPoints": {{ tpl "[ https://{{ required \"stepConfig.dnsName is missing\" .Values.stepConfig.dnsName }}/crl ]" . | fromYamlArray | toJson }} + } + + configTemplate: |- + address: {{ required "stepConfig.address is missing" .Values.stepConfig.address }} + + dnsNames: + - {{ required "stepConfig.dnsName is missing" .Values.stepConfig.dnsName }} + {{- if .Values.stepConfig.additionalDNSNames }} + {{- .Values.stepConfig.additionalDNSNames | toYaml | nindent 2 }} + {{- end }} + + crt: {{ required "stepConfig.crt is missing" .Values.stepConfig.crt }} + key: {{ required "stepConfig.key is missing" .Values.stepConfig.key }} + root: {{ required "stepConfig.root is missing" .Values.stepConfig.root }} + + federatedRoots: + {{- required "stepConfig.federatedRoots is missing" .Values.stepConfig.federatedRoots | toYaml | nindent 2 }} + + crl: + enabled: true + generateOnRevoke: true + idpURL: https://{{ required "stepConfig.dnsName is missing" .Values.stepConfig.dnsName }}/crl + + db: + {{ required "stepConfig.db is missing" .Values.stepConfig.db | toYaml | nindent 2 }} + + tls: + {{ required "stepConfig.tls is missing" .Values.stepConfig.tls | toYaml | nindent 2 }} + + logger: + {{ required "stepConfig.logger is missing" .Values.stepConfig.logger | toYaml | nindent 2 }} + + authority: + claims: + {{ required "stepConfig.authority.claims is missing" .Values.stepConfig.authority.claims | toYaml | nindent 4 }} + provisioners: + - {{ required "stepConfig.authority.jwk is missing" .Values.stepConfig.authority.jwk | fromJson | toYaml | nindent 6 }} + - name: {{ required "stepConfig.authority.acme.name is missing" .Values.stepConfig.authority.acme.name }} + type: ACME + forceCN: true + challenges: + - wire-oidc-01 + - wire-dpop-01 + claims: + {{ required "stepConfig.authority.acme.claims is missing" .Values.stepConfig.authority.acme.claims | toYaml | nindent 8 }} + options: + wire: + dpop: + key: {{ required "stepConfig.authority.acme.dpop.key is missing" .Values.stepConfig.authority.acme.dpop.key }} + target: https://{{ required "stepConfig.authority.acme.dpop.wireDomain" .Values.stepConfig.authority.acme.dpop.wireDomain }}/clients/{{ "{{" }}.DeviceID{{ "}}" }}/access-token + oidc: + config: + clientId: {{ required "stepConfig.authority.acme.oidc.clientId is missing" .Values.stepConfig.authority.acme.oidc.clientId }} + signatureAlgorithms: + {{ required "stepConfig.authority.acme.oidc.signatureAlgorithms is missing" .Values.stepConfig.authority.acme.oidc.signatureAlgorithms | toYaml | nindent 14 }} + provider: + discoveryBaseUrl: {{ required "stepConfig.authority.acme.oidc.discoveryBaseUrl is missing" .Values.stepConfig.authority.acme.oidc.discoveryBaseUrl }} + id_token_signing_alg_values_supported: + {{ required "stepConfig.authority.acme.oidc.signatureAlgorithms is missing" .Values.stepConfig.authority.acme.oidc.signatureAlgorithms | toYaml | nindent 14 }} + issuerUrl: {{ required "stepConfig.authority.acme.oidc.issuerUrl is missing" .Values.stepConfig.authority.acme.oidc.issuerUrl }} + transform: {{ required "stepConfig.authority.acme.oidc.transform is missing" .Values.stepConfig.authority.acme.oidc.transform | toJson }} + x509: + template: {{ (tpl .Values.stepConfig.authority.acme.x509.template .) | toJson }} + + {{- if .Values.stepConfig.extraConfig }} + {{ .Values.stepConfig.extraconfig | toYaml }} + {{- end }} + + +