Skip to content

Commit

Permalink
chore: support broker tls
Browse files Browse the repository at this point in the history
  • Loading branch information
shreddedbacon committed Feb 27, 2025
1 parent 6d19a73 commit c9f7de2
Show file tree
Hide file tree
Showing 21 changed files with 389 additions and 10 deletions.
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,6 @@ endif
$$([ $(INSTALL_STABLE_BUILDDEPLOY) = true ] && [ $(STABLE_BUILDDEPLOY_CHART_VERSION) ] && echo '--version=$(STABLE_BUILDDEPLOY_CHART_VERSION)') \
$$(if [ $(INSTALL_STABLE_BUILDDEPLOY) = true ]; then echo '--values https://raw.githubusercontent.com/uselagoon/lagoon-charts/refs/tags/lagoon-build-deploy-$(STABLE_BUILDDEPLOY_CHART_VERSION)/charts/lagoon-build-deploy/ci/linter-values.yaml'; else echo '--values ./charts/lagoon-build-deploy/ci/linter-values.yaml'; fi) \
--set "rabbitMQPassword=$$($(KUBECTL) -n lagoon-core get secret lagoon-core-broker -o json | $(JQ) -r '.data.RABBITMQ_PASSWORD | @base64d')" \
--set "rabbitMQHostname=lagoon-core-broker.lagoon-core.svc" \
--set "lagoonFeatureFlagEnableQoS=true" \
$$([ $(LAGOON_SSH_PORTAL_LOADBALANCER) ] && echo "--set sshPortalHost=$$($(KUBECTL) -n lagoon get services lagoon-remote-ssh-portal -o jsonpath='{.status.loadBalancer.ingress[0].ip}')") \
$$([ $(LAGOON_SSH_PORTAL_LOADBALANCER) ] && echo "--set sshPortalPort=$$($(KUBECTL) -n lagoon get services lagoon-remote-ssh-portal -o jsonpath='{.spec.ports[0].port}')") \
Expand All @@ -600,9 +599,9 @@ endif
$$([ $(INSTALL_UNAUTHENTICATED_REGISTRY) = false ] && echo --set "harbor.adminUser=admin") \
$$([ $(INSTALL_UNAUTHENTICATED_REGISTRY) = false ] && echo --set "harbor.host=https://registry.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io") \
$$([ $(INSTALL_UNAUTHENTICATED_REGISTRY) = true ] && echo --set "unauthenticatedRegistry=registry.$$($(KUBECTL) -n ingress-nginx get services ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}').nip.io") \
$$([ $(OVERRIDE_BUILD_DEPLOY_DIND_IMAGE) ] && [ ! $(INSTALL_STABLE_BUILDDEPLOY) ] && echo '--set overrideBuildDeployImage=$(OVERRIDE_BUILD_DEPLOY_DIND_IMAGE)') \
$$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG) ] && [ ! $(INSTALL_STABLE_BUILDDEPLOY) ] && echo '--set image.tag=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG)') \
$$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY) ] && [ ! $(INSTALL_STABLE_BUILDDEPLOY) ] && echo '--set image.repository=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY)') \
$$([ $(OVERRIDE_BUILD_DEPLOY_DIND_IMAGE) ] && [ $(INSTALL_STABLE_BUILDDEPLOY) = false ] && echo '--set overrideBuildDeployImage=$(OVERRIDE_BUILD_DEPLOY_DIND_IMAGE)') \
$$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG) ] && [ $(INSTALL_STABLE_BUILDDEPLOY) = false ] && echo '--set image.tag=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGETAG)') \
$$([ $(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY) ] && [ $(INSTALL_STABLE_BUILDDEPLOY) = false ] && echo '--set image.repository=$(OVERRIDE_BUILD_DEPLOY_CONTROLLER_IMAGE_REPOSITORY)') \
$$([ $(BUILD_DEPLOY_CONTROLLER_ROOTLESS_BUILD_PODS) ] && echo '--set rootlessBuildPods=true') \
$$([ $(LAGOON_FEATURE_FLAG_DEFAULT_ROOTLESS_WORKLOAD) ] && echo '--set lagoonFeatureFlagDefaultRootlessWorkload=$(LAGOON_FEATURE_FLAG_DEFAULT_ROOTLESS_WORKLOAD)') \
$$([ $(LAGOON_FEATURE_FLAG_DEFAULT_ISOLATION_NETWORK_POLICY) ] && echo '--set lagoonFeatureFlagDefaultIsolationNetworkPolicy=$(LAGOON_FEATURE_FLAG_DEFAULT_ISOLATION_NETWORK_POLICY)') \
Expand Down
36 changes: 35 additions & 1 deletion charts/lagoon-build-deploy/ci/linter-values.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
rabbitMQUsername: lagoon
rabbitMQPassword: ci
rabbitMQHostname: lagoon-core-broker.lagoon-core.svc
rabbitMQHostname: lagoon-core-broker.lagoon-core.svc:5671
lagoonTargetName: ci-local-control-k8s
sshPortalHost: lagoon-remote-ssh-portal.lagoon.svc
sshPortalPort: 22
Expand All @@ -9,3 +9,37 @@ lagoonTokenPort: 22
lagoonAPIHost: http://lagoon-core-api.lagoon-core.svc:80
extraArgs:
- "--skip-tls-verify=true"
amqp:
tls:
secretData:
ca.crt: |
-----BEGIN CERTIFICATE-----
MIIBgzCCASqgAwIBAgIUW02z87Rcotu3NfrhhTu2/NwP4bowCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUuY29tMB4XDTI1MDIxMTAwMjEw
MFoXDTM1MDIwOTAwMjEwMFowIDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUu
Y29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsbClCaHFE8aoTA4a+GA8zrI3
6ExBeE8gnf0C+hdDxTBkga8MrOMPI3yB9TdA7115SQ4mL30QkEkb20bvsMRXhKNC
MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHSU
GS+kUMICtHSuyX4bRP0vqt+RMAoGCCqGSM49BAMCA0cAMEQCIFeJ76F4Ts93PL8A
9kmacM2i2mHOHn5IBGU2xUPKiUQrAiBJ9UOk/SHA6Dfpvl3c9cDazRPwbypydRys
HpUD5zUYRg==
-----END CERTIFICATE-----
tls.crt: |
-----BEGIN CERTIFICATE-----
MIIB2TCCAX+gAwIBAgIUDbWf70mZryK9j/y7bDMxXqQQynAwCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUuY29tMB4XDTI1MDIxMTAwMjIw
MFoXDTM1MDIwOTAwMjIwMFowHzEdMBsGA1UEAxMUY2ktbG9jYWwtY29udHJvbC1r
OHMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT5YEI6UE+ZExQYf8Z4Vik87YpH
kIb+k19GwF17kXCbfzzf6GCZV8KDj1z+4aoss+0KxCp14kqXUWqfbgyNkv8Io4GX
MIGUMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMB
Af8EAjAAMB0GA1UdDgQWBBSb9n4931KyXGFDpxGr+MAGjjaJSTAfBgNVHSMEGDAW
gBR0lBkvpFDCArR0rsl+G0T9L6rfkTAfBgNVHREEGDAWghRjaS1sb2NhbC1jb250
cm9sLWs4czAKBggqhkjOPQQDAgNIADBFAiEAnBOr6QdSbH3/irJGj7LYSMhnd8lO
+NETqiR/O2ELan4CIAcC8BKhkRMtPCFY/kge6FAslK1txEBacEYcKvln76pD
-----END CERTIFICATE-----
tls.key: |
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICEZfAV/KTK9P8ZyUEXIuqJErxIc7YIYatHYa7jgPJevoAoGCCqGSM49
AwEHoUQDQgAE+WBCOlBPmRMUGH/GeFYpPO2KR5CG/pNfRsBde5Fwm3883+hgmVfC
g49c/uGqLLPtCsQqdeJKl1Fqn24MjZL/CA==
-----END EC PRIVATE KEY-----
29 changes: 29 additions & 0 deletions charts/lagoon-build-deploy/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ spec:
args:
- "--metrics-bind-address=:8443"
- "--leader-elect=true"
{{- if .Values.amqp.tls.secretData }}
- "--rabbitmq-tls"
{{- end }}
{{- if .Values.harbor.enabled }}
- "--enable-harbor=true"
- "--harbor-password={{ .Values.harbor.adminPassword }}"
Expand Down Expand Up @@ -243,6 +246,14 @@ spec:
value: {{ .Values.pendingMessageCron | quote }}
- name: RABBITMQ_HOSTNAME
value: {{ required "A valid rabbitMQHostname required!" $rabbitMQHostname | quote }}
{{- if .Values.amqp.tls.secretData }}
- name: RABBITMQ_CACERT
value: "/ca.crt"
- name: RABBITMQ_CLIENTCERT
value: "/tls.crt"
- name: RABBITMQ_CLIENTKEY
value: "/tls.key"
{{- end }}
- name: RABBITMQ_PASSWORD
valueFrom:
secretKeyRef:
Expand All @@ -259,8 +270,26 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
{{- if .Values.amqp.tls.secretData }}
- mountPath: /ca.crt
name: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
subPath: ca.crt
- mountPath: /tls.crt
name: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
subPath: tls.crt
- mountPath: /tls.key
name: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
subPath: tls.key
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
volumes:
{{- if .Values.amqp.tls.secretData }}
- name: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
secret:
secretName: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
14 changes: 14 additions & 0 deletions charts/lagoon-build-deploy/templates/secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ metadata:
stringData:
RABBITMQ_PASSWORD: {{ required "A valid rabbitMQPassword required!" $rabbitMQPassword | quote }}
RABBITMQ_USERNAME: {{ required "A valid rabbitMQUsername required!" $rabbitMQUsername | quote }}
{{- if .Values.amqp.tls.secretData }}
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: {{ include "lagoon-build-deploy.fullname" . }}-amqp-tls
labels:
{{- include "lagoon-build-deploy.labels" . | nindent 4 }}
stringData:
{{- with .Values.amqp.tls.secretData }}
{{- . | toYaml | nindent 2 }}
{{- end }}
{{- end }}
10 changes: 10 additions & 0 deletions charts/lagoon-build-deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,13 @@ nodeSelector: {}
tolerations: []

affinity: {}

amqp:
tls: {}
# secretData:
# ca.crt: |
# ...
# tls.crt: |
# ...
# tls.key: |
# ...

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.26.15@sha256:1cc15d7b1edd2126ef051e359bf864f37bbcf1568e61be4d2ed1df7a3e87b354)

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.27.17@sha256:3fd82731af34efe19cd54ea5c25e882985bafa2c9baefe14f8deab1737d9fabe)

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.28.13@sha256:45d319897776e11167e4698f6b14938eb4d52eb381d9e3d7a9086c16c69a8110)

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.31.0@sha256:53df588e04085fd41ae12de0c3fe4c72f7013bba32a20e7325357a1ac94ba865)

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.25.16@sha256:6110314339b3b44d10da7d27881849a87e092124afab5956f2e10ecdb463b025)

208:14 [new-line-at-end-of-file] no new line character at the end of file

Check failure on line 208 in charts/lagoon-build-deploy/values.yaml

View workflow job for this annotation

GitHub Actions / lint-test (v1.29.8@sha256:d46b7aa29567e93b27f7531d258c372e829d7224b25e3fc6ffdefed12476d3aa)

208:14 [new-line-at-end-of-file] no new line character at the end of file
52 changes: 52 additions & 0 deletions charts/lagoon-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,58 @@ Lagoon uses S3 compatible storage for it, it can be configured via these helm va
- `s3FilesAccessKeyID` - AccessKey for the S3 Bucket
- `s3FilesSecretAccessKey` - AccessKey Secret for the S3 Bucket

## Broker

### Securing Broker

The broker supports connections via TLS, to enable it you first need to configure the certificate for the broker itself.

In the values for `broker`, there are the following options. When configuring the certificates for the server, uncomment the `secretData` section
```
broker:
tls:
# https://www.rabbitmq.com/docs/ssl#enabling-tls for what these options can be set to
verify: verify_peer
failIfNoPeerCert: true
secretData:
ca.crt: |
...
tls.crt: |
...
tls.key: |
...
```
Additionally, to enable the exposed tls enabled port, you have to set `broker.service.amqpsExternal.enabled: true` in your values, if you're using this method of exposing broker.
Ideally this will be a valid public TLS certificate. You can also use a private certificate authority.
Clients will need client certificates generated too, and they will need to be added to the clients in the appropriate charts.
##### Private CA
You can generate a valid CA, leafnode server, and leafnode client certificate using `cfssl` and the configuration files in the `broker-tls/` directory.
Edit the files:
* For `ca-csr.json` select a CA hostname.
* For `server.json` set the CN/SAN to the server hostname. This has to be the hostname used by the client to connect to the server.
* For `client.json` set the CN/SAN to the client leafnode username.
Generate the certificates:
```
# CA
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
rm ca.csr

# Server
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server
rm server.csr

# Client
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client
rm client.csr
```
## NATS
This section only applies if using NATS for ssh-portal support.
Expand Down
3 changes: 3 additions & 0 deletions charts/lagoon-core/broker-tls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Broker TLS

This directory contains example configuration for generating certificates for broker connections.
25 changes: 25 additions & 0 deletions charts/lagoon-core/broker-tls/ca-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"server": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth"
]
},
"client": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
}
}
}
}
13 changes: 13 additions & 0 deletions charts/lagoon-core/broker-tls/ca-csr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"CN": "broker-ca.example.com",
"hosts": [
"broker-ca.example.com"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"ca": {
"expiry": "87600h"
}
}
10 changes: 10 additions & 0 deletions charts/lagoon-core/broker-tls/client.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"hosts": [
"ci-local-control-k8s"
],
"CN": "ci-local-control-k8s",
"key": {
"algo": "ecdsa",
"size": 256
}
}
11 changes: 11 additions & 0 deletions charts/lagoon-core/broker-tls/server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"hosts": [
"lagoon-core-broker",
"lagoon-core-broker.lagoon-core.svc"
],
"CN": "lagoon-core-broker",
"key": {
"algo": "ecdsa",
"size": 256
}
}
39 changes: 39 additions & 0 deletions charts/lagoon-core/ci/linter-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,45 @@ broker:
resources:
requests:
cpu: "10m"
service:
amqpExternal:
enabled: false
amqpsExternal:
enabled: true
tls:
secretData:
ca.crt: |
-----BEGIN CERTIFICATE-----
MIIBgzCCASqgAwIBAgIUW02z87Rcotu3NfrhhTu2/NwP4bowCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUuY29tMB4XDTI1MDIxMTAwMjEw
MFoXDTM1MDIwOTAwMjEwMFowIDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUu
Y29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsbClCaHFE8aoTA4a+GA8zrI3
6ExBeE8gnf0C+hdDxTBkga8MrOMPI3yB9TdA7115SQ4mL30QkEkb20bvsMRXhKNC
MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHSU
GS+kUMICtHSuyX4bRP0vqt+RMAoGCCqGSM49BAMCA0cAMEQCIFeJ76F4Ts93PL8A
9kmacM2i2mHOHn5IBGU2xUPKiUQrAiBJ9UOk/SHA6Dfpvl3c9cDazRPwbypydRys
HpUD5zUYRg==
-----END CERTIFICATE-----
tls.crt: |
-----BEGIN CERTIFICATE-----
MIIB+TCCAZ+gAwIBAgIUAya5IFehLfZqweTEH570kbxhMyYwCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAxMVYnJva2VyLWNhLmV4YW1wbGUuY29tMB4XDTI1MDIxMTAwMjEw
MFoXDTM1MDIwOTAwMjEwMFowHTEbMBkGA1UEAxMSbGFnb29uLWNvcmUtYnJva2Vy
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkdAfcIwBNRBJhxMDQhDIaqrWHUto
19IQgcdEgrikih9yQl2cJNvepztKBRfcinHaQgbTnf/vnxCZyTQ5fzCp0KOBuTCB
tjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/
BAIwADAdBgNVHQ4EFgQUxKjvIHisTYfMCCVlf1WwV8FBn4gwHwYDVR0jBBgwFoAU
dJQZL6RQwgK0dK7JfhtE/S+q35EwQQYDVR0RBDowOIISbGFnb29uLWNvcmUtYnJv
a2VygiJsYWdvb24tY29yZS1icm9rZXIubGFnb29uLWNvcmUuc3ZjMAoGCCqGSM49
BAMCA0gAMEUCIQD+0wveZTwYVcCrSmxdLyCVg3rv1mgX6zIYUb+ACllMzwIgYQ3Q
emnvPaJxI9RhHZo8htqC3ljuhQ1lgXrjScEWM/4=
-----END CERTIFICATE-----
tls.key: |
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHioktcYuADFDDQq5Ke8NoEtDsFPRshZwwgz+rwK91ddoAoGCCqGSM49
AwEHoUQDQgAEkdAfcIwBNRBJhxMDQhDIaqrWHUto19IQgcdEgrikih9yQl2cJNve
pztKBRfcinHaQgbTnf/vnxCZyTQ5fzCp0A==
-----END EC PRIVATE KEY-----
authServer:
replicaCount: 1
Expand Down
20 changes: 20 additions & 0 deletions charts/lagoon-core/templates/broker.configmap.tlsconf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

{{- if .Values.broker.tls.secretData }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "lagoon-core.broker.fullname" . }}-tls-conf
labels:
{{- include "lagoon-core.broker.labels" . | nindent 4 }}
data:
tls.conf: |
## tls-listener configuration
listeners.ssl.default = {{ .Values.broker.service.ports.amqps }}
## tls certificate configurations
ssl_options.cacertfile = /ca.crt
ssl_options.certfile = /tls.crt
ssl_options.keyfile = /tls.key
ssl_options.verify = {{ .Values.broker.tls.verify }}
ssl_options.fail_if_no_peer_cert = {{ .Values.broker.tls.failIfNoPeerCert }}
{{- end}}
14 changes: 14 additions & 0 deletions charts/lagoon-core/templates/broker.secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ metadata:
stringData:
RABBITMQ_PASSWORD: {{ $rabbitMQPassword | quote }}
RABBITMQ_USERNAME: {{ required "A valid .Values.rabbitMQUsername required!" .Values.rabbitMQUsername | quote }}
{{- if .Values.broker.tls.secretData }}
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: {{ include "lagoon-core.broker.fullname" . }}-tls
labels:
{{- include "lagoon-core.labels" . | nindent 4 }}
stringData:
{{- with .Values.broker.tls.secretData }}
{{- . | toYaml | nindent 2 }}
{{- end }}
{{- end }}
Loading

0 comments on commit c9f7de2

Please sign in to comment.