Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions helm/testnet-wallet/templates/configMap.backend.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,44 @@
{{/*
The override adds the HSM adapter block to the ConfigMap the contentMap builds.

It is not a contentMap entry. The backend validates ATALLA_HOST and ATALLA_PORT
when HSM_ENABLED is true and rejects an empty string for either, so a contentMap
entry would emit `ATALLA_HOST: ""` in every environment that leaves it unset and
stop the backend from starting. Here the whole block appears only when
config.backend.hsm.enabled is true.

common.utils.merge merges maps deeply, so these keys join the contentMap's
`data` rather than replacing it.
*/}}
{{- define "testnet-wallet.backend.ConfigMap" -}}
{{- $top := first . }}
{{- $hsm := $top.Values.config.backend.hsm }}
{{- if $hsm.enabled }}
{{- if not $hsm.atalla.host }}
{{- fail "config.backend.hsm.atalla.host is required when config.backend.hsm.enabled is true" }}
{{- end }}
{{- if not $hsm.atalla.port }}
{{- fail "config.backend.hsm.atalla.port is required when config.backend.hsm.enabled is true" }}
{{- end }}
data:
HSM_ENABLED: "true"
HSM_SERVER_ROLE: {{ $hsm.serverRole | quote }}
{{- /* ISSUER_GRPC_HOST and MERCHANT_GRPC_HOST are omitted: the backend falls
back to HSM_GRPC_HOST, and one host serves both roles here. */}}
HSM_GRPC_HOST: {{ $hsm.grpcHost | quote }}
ISSUER_GRPC_PORT: {{ $hsm.issuerPort | quote }}
MERCHANT_GRPC_PORT: {{ $hsm.merchantPort | quote }}
ATALLA_HOST: {{ $hsm.atalla.host | quote }}
ATALLA_PORT: {{ $hsm.atalla.port | quote }}
ATALLA_TIMEOUT_MS: {{ $hsm.atalla.timeoutMs | quote }}
ATALLA_POOL_SIZE: {{ $hsm.atalla.poolSize | quote }}
ATALLA_TLS_ENABLED: {{ $hsm.atalla.tls.enabled | quote }}
{{- if $hsm.atalla.tls.enabled }}
ATALLA_SKIP_SERVER_IDENTITY_CHECK: {{ $hsm.atalla.tls.skipServerIdentityCheck | quote }}
ATALLA_CA_CERT_PATH: {{ $hsm.atalla.tls.caCertPath | quote }}
ATALLA_CLIENT_CERT_PATH: {{ $hsm.atalla.tls.clientCertPath | quote }}
ATALLA_CLIENT_KEY_PATH: {{ $hsm.atalla.tls.clientKeyPath | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- include "common.configMapper" (list . .Values.configMaps.backend "testnet-wallet.backend.ConfigMap") }}
109 changes: 109 additions & 0 deletions helm/testnet-wallet/tests/configMap.backend_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
suite: testnet-wallet backend - HSM adapter configuration
templates:
- configMap.backend.yaml
set:
fullnameOverride: test-testnet-wallet
release:
name: test-testnet-wallet
tests:
# The backend rejects an empty ATALLA_HOST, so the keys must be absent
# rather than blank in every environment that does not run the adapter.
- it: 'omits every HSM key when the adapter is disabled'
asserts:
- notExists:
path: data.HSM_ENABLED
- notExists:
path: data.ATALLA_HOST
- notExists:
path: data.ISSUER_GRPC_PORT
# The contentMap keys are still there.
- equal:
path: data.NODE_ENV
value: production

- it: 'emits the adapter block when enabled, alongside the contentMap keys'
set:
config:
backend:
hsm:
enabled: true
atalla:
host: '10.0.0.1'
port: 13506
asserts:
- equal:
path: data.HSM_ENABLED
value: 'true'
- equal:
path: data.HSM_SERVER_ROLE
value: both
- equal:
path: data.HSM_GRPC_HOST
value: 0.0.0.0
- equal:
path: data.ISSUER_GRPC_PORT
value: '50051'
- equal:
path: data.MERCHANT_GRPC_PORT
value: '50052'
- equal:
path: data.ATALLA_HOST
value: '10.0.0.1'
# Numbers must reach the ConfigMap as strings.
- equal:
path: data.ATALLA_PORT
value: '13506'
- equal:
path: data.ATALLA_POOL_SIZE
value: '1'
- equal:
path: data.ATALLA_CA_CERT_PATH
value: /etc/atalla/tls/ca.crt
# The override must merge with the contentMap, not replace it.
- equal:
path: data.NODE_ENV
value: production

- it: 'drops the TLS paths when Atalla TLS is off'
set:
config:
backend:
hsm:
enabled: true
atalla:
host: '10.0.0.1'
port: 13506
tls:
enabled: false
asserts:
- equal:
path: data.ATALLA_TLS_ENABLED
value: 'false'
- notExists:
path: data.ATALLA_CA_CERT_PATH
- notExists:
path: data.ATALLA_SKIP_SERVER_IDENTITY_CHECK

- it: 'fails the render when the adapter is enabled without an Atalla host'
set:
config:
backend:
hsm:
enabled: true
atalla:
port: 13506
asserts:
- failedTemplate:
errorMessage: 'config.backend.hsm.atalla.host is required when config.backend.hsm.enabled is true'

- it: 'fails the render when the adapter is enabled without an Atalla port'
set:
config:
backend:
hsm:
enabled: true
atalla:
host: '10.0.0.1'
asserts:
- failedTemplate:
errorMessage: 'config.backend.hsm.atalla.port is required when config.backend.hsm.enabled is true'
41 changes: 41 additions & 0 deletions helm/testnet-wallet/tests/deployment.backend_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,44 @@ tests:
- equal:
path: data.GATEHUB_SEPA_ACCESS_KEY
value: test-sepa-access-key
- it: 'secret references default to the Secret this chart names'
template: deployment.backend.yaml
asserts:
- equal:
path: spec.template.spec.containers[0].env[0].valueFrom.secretKeyRef.name
value: test-testnet-wallet-backend
- it: 'secret references follow config.backend.secretName when it is set'
template: deployment.backend.yaml
set:
config:
backend:
secretName: wallet-credentials-from-operator
asserts:
- equal:
path: spec.template.spec.containers[0].env[0].valueFrom.secretKeyRef.name
value: wallet-credentials-from-operator
# The key is unchanged; only the Secret it is read from moves.
- equal:
path: spec.template.spec.containers[0].env[0].valueFrom.secretKeyRef.key
value: databaseUrl
- it: 'mounts the Atalla TLS volume when an environment supplies one'
template: deployment.backend.yaml
set:
deployments:
backend:
volumeMounts:
- name: atalla-tls
mountPath: /etc/atalla/tls
readOnly: true
volumes:
- name: atalla-tls
secret:
secretName: wallet-atalla
defaultMode: 0400
asserts:
- equal:
path: spec.template.spec.volumes[0].secret.secretName
value: wallet-atalla
- equal:
path: spec.template.spec.containers[0].volumeMounts[0].mountPath
value: /etc/atalla/tls
111 changes: 96 additions & 15 deletions helm/testnet-wallet/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,25 @@ config:
# already exist.
shouldCreateSecrets: false

# Name of the Secret that holds the backend credentials. Empty means the
# Secret this chart names, `<fullname>-backend`, which is also the one
# shouldCreateSecrets creates.
#
# Set this when the Secret comes from somewhere else — a secret operator
# writing a Secret of its own name, for example. The keys it must hold are
# the `secretKeyRef.key` values below.
secretName: ''
Comment thread
bosbaber marked this conversation as resolved.

nodeEnv: production
logLevel: info
port: 4003
metricsPort: 9464

cookie:
name: 'testnet.cookie'
ttl: 2630000
# Quoted. Helm parses a bare number as float64, and the ConfigMap would
# then read `COOKIE_TTL: "2.63e+06"`.
ttl: '2630000'
password: # value: "a-very-secure-random-password"
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
Expand Down Expand Up @@ -160,6 +171,52 @@ config:
secretKeyRef:
key: 'rafiki.adminApiSecret'

# The backend serves the issuer and merchant HSM adapter contracts over
# gRPC when `enabled` is true. One process runs both servers; `serverRole`
# chooses which. Only the cards environments run this, so it is off here.
#
# These keys reach the backend through the ConfigMap, but not through
# `configMaps.backend.contentMap`. The backend requires ATALLA_HOST and
# ATALLA_PORT when HSM_ENABLED is true and rejects an empty string for
# either, so a contentMap entry would break every environment that leaves
# them unset. templates/configMap.backend.yaml adds the whole block
# instead, and only when `enabled` is true.
#
# Turning this on is not enough by itself. Also give the backend:
# - the gRPC container ports, in `deployments.backend.ports`
# - the matching Service ports, in `services.backend.ports`
# - the Atalla TLS files, through `deployments.backend.volumes` and
# `deployments.backend.volumeMounts`
# Helm replaces a list rather than merging it, so each of those must be
# written out in full in the environment's values file.
hsm:
enabled: false
# both | issuer | merchant
serverRole: both
grpcHost: '0.0.0.0'
issuerPort: 50051
merchantPort: 50052

atalla:
# Required when hsm.enabled is true. The render fails without them.
host: ''
port: ''
timeoutMs: 2000
poolSize: 1
tls:
enabled: true
# Skips the hostname check only. The transport still sets
# rejectUnauthorized, so the HSM certificate must chain to the CA at
# caCertPath. Needed while the backend dials an IP and the
# certificate carries no matching IP SAN.
skipServerIdentityCheck: true
# Paths, not PEM text. The backend reads each file at start-up and
# exits if one is missing, unreadable, empty, or not PEM. Mount them
# with deployments.backend.volumes.
caCertPath: /etc/atalla/tls/ca.crt
clientCertPath: /etc/atalla/tls/client.crt
clientKeyPath: /etc/atalla/tls/client.key

deployments:
backend:
name: backend
Expand All @@ -169,81 +226,105 @@ deployments:
name: test-wallet-backend
# tag: "v0.1.66" # Use this to override the image tag for just this image
imagePullPolicy: IfNotPresent
# Two ways to give the backend its secrets. Pick one per environment.
#
# 1. Per-key (the default below). The chart maps each environment variable
# to one key of a single Secret. `config.backend.secretName` chooses the
# Secret; `config.backend.<x>.secretKeyRef.key` chooses the key. Every
# key listed must exist: a missing key stops the pod from starting,
# unless the entry is marked `optional`. Use this when the same Secret
# holds all of them.
#
# 2. Whole-Secret. Add a `secretRef` to `envFrom` and set `env: []`. Every
# key of that Secret becomes an environment variable under its own name,
# so the Secret's keys must themselves be environment variable names,
# and keys that are absent are simply absent. Use this when the Secret
# comes from an operator that writes only the values the environment
# needs — see env/cards-playground in interledger/testnet-deploy.
#
# Helm replaces a list rather than merging it, so an environment that
# rewrites `envFrom` must repeat the configMapRef entry below.
envFrom:
- configMapRef:
name: '{{ include "common.fullname" . }}-backend'

# Volumes for the backend pod. Empty here; the HSM adapter needs the Atalla
# TLS files, so an environment that sets config.backend.hsm.enabled must
# also fill these in. See config.backend.hsm.
volumes: []
volumeMounts: []

env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.database.url.secretKeyRef.key }}'
- name: COOKIE_PASSWORD
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.cookie.password.secretKeyRef.key }}'
- name: AUTH_IDENTITY_SERVER_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.identity.serverSecret.secretKeyRef.key }}'
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.redis.url.secretKeyRef.key }}'
- name: RAFIKI_WEBHOOK_SIGNATURE_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.webhook.signatureSecret.secretKeyRef.key }}'
- name: GATEHUB_SECRET_KEY
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.gatehub.secretKey.secretKeyRef.key }}'
- name: GATEHUB_SEPA_SECRET_KEY
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.gatehub.secretSepaKey.secretKeyRef.key }}'
- name: GATEHUB_WEBHOOK_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.gatehub.webhookSecret.secretKeyRef.key }}'
- name: SENDGRID_API_KEY
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.email.sendgridKey.secretKeyRef.key }}'
- name: ADMIN_NOTIFICATION_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.email.adminNotificationSecret.secretKeyRef.key }}'
optional: true
- name: RATE_API_KEY
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.rate.apiKey.secretKeyRef.key }}'
- name: STRIPE_WEBHOOK_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.stripe.webhookSecret.secretKeyRef.key }}'
- name: STRIPE_SECRET_KEY
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.stripe.secretKey.secretKeyRef.key }}'
- name: ADMIN_API_SECRET
valueFrom:
secretKeyRef:
name: '{{ include "common.fullname" . }}-backend'
name: '{{ .Values.config.backend.secretName | default (printf "%s-backend" (include "common.fullname" .)) }}'
key: '{{ .Values.config.backend.rafiki.adminApiSecret.secretKeyRef.key }}'

ports:
Expand Down
Loading