mirror of
https://github.com/bitnami/charts.git
synced 2026-03-07 08:07:55 +08:00
[bitnami/zookeeper] Add support for autogenerated certs (#6532)
* [bitnami/zookeeper] Add support for autogenerated certs * Update README.md and Chart version bump * Add major change notes to README.md * Apply suggestions * Add initContainer for Keystore autogeneration * Add validation, annotations and minor fix * Remove tls.image and update README.md
This commit is contained in:
@@ -21,4 +21,4 @@ name: zookeeper
|
||||
sources:
|
||||
- https://github.com/bitnami/bitnami-docker-zookeeper
|
||||
- https://zookeeper.apache.org/
|
||||
version: 6.10.0
|
||||
version: 7.0.0
|
||||
|
||||
@@ -147,21 +147,9 @@ The following tables lists the configurable parameters of the ZooKeeper chart an
|
||||
| `service.publishNotReadyAddresses` | If the ZooKeeper headless service should publish DNS records for not ready pods | `true` |
|
||||
| `serviceAccount.create` | Enable creation of ServiceAccount for zookeeper pod | `false` |
|
||||
| `serviceAccount.name` | The name of the service account to use. If not set and `create` is `true`, a name is generated | Generated using the `common.names.fullname` template |
|
||||
`serviceAccount.automountServiceAccountToken` | Enable/Disable automountServiceAccountToken for Service Account | `true` |
|
||||
| `service.tls.client_enable` | Enable tls for client connections | `false` |
|
||||
| `service.tls.quorum_enable` | Enable tls for quorum protocol | `false` |
|
||||
| `service.tls.disable_base_client_port` | Remove client port from service definitions. | `false` |
|
||||
| `service.tls.client_port` | Service port for tls client connections | `3181` |
|
||||
| `service.tls.client_key_pem_path` | Key pem file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_key_store/key_store_file` |
|
||||
| `service.tls.client_cert_pem_path` | Cert pem file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_key_store/key_store_file` |
|
||||
| `service.tls.client_keystore_path` | KeyStore file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_key_store/key_store_file` |
|
||||
| `service.tls.client_keystore_password` | KeyStore password. You can use environment variables. | `nil` |
|
||||
| `service.tls.client_truststore_path` | TrustStore file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_trust_store/trust_store_file` |
|
||||
| `service.tls.client_truststore_password` | TrustStore password. You can use environment variables. | `nil` |
|
||||
| `service.tls.quorum_keystore_path` | KeyStore file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_key_store/key_store_file` |
|
||||
| `service.tls.quorum_keystore_password` | KeyStore password. You can use environment variables. | `nil` |
|
||||
| `service.tls.quorum_truststore_path` | TrustStore file path. Refer to extraVolumes amd extraVolumeMounts for mounting files into the pods | `/tls_trust_store/trust_store_file` |
|
||||
| `service.tls.quorum_truststore_password` | TrustStore password. You can use environment variables. | `nil` |
|
||||
| `serviceAccount.automountServiceAccountToken` | Enable/Disable automountServiceAccountToken for Service Account | `true` |
|
||||
| `service.disableBaseClientPort` | Remove client port from service definitions. | `false` |
|
||||
| `service.tlsClientPort` | Service port for tls client connections | `3181` |
|
||||
| `service.annotations` | Annotations for the Service | `{}` |
|
||||
| `service.headless.annotations` | Annotations for the Headless Service | `{}` |
|
||||
| `networkPolicy.enabled` | Enable NetworkPolicy | `false` |
|
||||
@@ -212,6 +200,27 @@ The following tables lists the configurable parameters of the ZooKeeper chart an
|
||||
| `metrics.prometheusRule.selector` | Prometheus instance selector labels | `nil` |
|
||||
| `metrics.prometheusRule.rules` | Prometheus Rule definitions (see values.yaml for examples) | `[]` |
|
||||
|
||||
### TLS/SSL parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
|
||||
| `tls.client.enabled` | Enable TLS for client connections | `false` |
|
||||
| `tls.client.autoGenerated` | Generate automatically self-signed TLS certificates for Zookeeper client communications. Currently only supports PEM certificates. | `false` |
|
||||
| `tls.client.existingSecret` | Name of the existing secret containing the TLS certificates for Zookeper client communications | `nil` |
|
||||
| `tls.client.keystorePath` | Location of the KeyStore file used for Client connections | `/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks` |
|
||||
| `tls.client.truststorePath` | Location of the TrustStore file used for Client connections | `/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks` |
|
||||
| `tls.client.keystorePassword` | Password to access KeyStore if needed | `nil` |
|
||||
| `tls.client.truststorePassword` | Password to access TrustStore if needed | `nil` |
|
||||
| `tls.quorum.enabled` | Enable TLS for quorum protocol | `false` |
|
||||
| `tls.quorum.autoGenerated` | Generate automatically self-signed TLS certificates for Zookeeper quorum protocol. Currently only supports PEM certificates. | `false` |
|
||||
| `tls.quorum.existingSecret` | Name of the existing secret containing the TLS certificates for Zookeper quorum protocol | `nil` |
|
||||
| `tls.quorum.keystorePath` | Location of the KeyStore file used for Quorum protocol | `/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks` |
|
||||
| `tls.quorum.truststorePath` | Location of the TrustStore file used for Quorum protocol | `/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks` |
|
||||
| `tls.quorum.keystorePassword` | Password to access KeyStore if needed | `nil` |
|
||||
| `tls.quorum.truststorePassword` | Password to access TrustStore if needed | `nil` |
|
||||
| `tls.resources.limits` | The resources limits for the TLS init container | `{}` |
|
||||
| `tls.resources.requests` | The requested resources for the TLS init container | `{}` |
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
```console
|
||||
@@ -279,6 +288,23 @@ Find more information about how to deal with common errors related to Bitnami’
|
||||
|
||||
## Upgrading
|
||||
|
||||
### To 7.0.0
|
||||
|
||||
This new version renames the parameters used to configure TLS for both client and quorum.
|
||||
|
||||
- `service.tls.disable_base_client_port` is renamed to `service.disableBaseClientPort`
|
||||
- `service.tls.client_port` is renamed to `service.tlsClientPort`
|
||||
- `service.tls.client_enable` is renamed to `tls.client.enabled`
|
||||
- `service.tls.client_keystore_path` is renamed to `tls.client.keystorePath`
|
||||
- `service.tls.client_truststore_path` is renamed to `tls.client.truststorePath`
|
||||
- `service.tls.client_keystore_password` is renamed to `tls.client.keystorePassword`
|
||||
- `service.tls.client_truststore_password` is renamed to `tls.client.truststorePassword`
|
||||
- `service.tls.quorum_enable` is renamed to `tls.quorum.enabled`
|
||||
- `service.tls.quorum_keystore_path` is renamed to `tls.quorum.keystorePath`
|
||||
- `service.tls.quorum_truststore_path` is renamed to `tls.quorum.truststorePath`
|
||||
- `service.tls.quorum_keystore_password` is renamed to `tls.quorum.keystorePassword`
|
||||
- `service.tls.quorum_truststore_password` is renamed to `tls.quorum.truststorePassword`
|
||||
|
||||
### 6.1.0
|
||||
|
||||
This version introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/master/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade.
|
||||
|
||||
@@ -49,4 +49,5 @@ To connect to your ZooKeeper server from outside the cluster execute the followi
|
||||
|
||||
{{- end }}
|
||||
|
||||
{{- include "zookeeper.validateValues" . }}
|
||||
{{- include "zookeeper.checkRollingTags" . }}
|
||||
|
||||
@@ -70,4 +70,105 @@ Return ZooKeeper Namespace to use
|
||||
{{- else }}
|
||||
{{- .Release.Namespace -}}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the secret containing Zookeeper quorum TLS certificates
|
||||
*/}}
|
||||
{{- define "zookeeper.quorum.tlsSecretName" -}}
|
||||
{{- $secretName := .Values.tls.quorum.existingSecret -}}
|
||||
{{- if $secretName -}}
|
||||
{{- printf "%s" (tpl $secretName $) -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-quorum-crt" (include "common.names.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return true if a TLS secret object should be created
|
||||
*/}}
|
||||
{{- define "zookeeper.quorum.createTlsSecret" -}}
|
||||
{{- if and .Values.tls.quorum.enabled .Values.tls.quorum.autoGenerated (not .Values.tls.quorum.existingSecret)}}
|
||||
{{- true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the name of the secret containing the Keystore and truststore password
|
||||
*/}}
|
||||
{{- define "zookeeper.quorum.tlsPasswordsSecret" -}}
|
||||
{{- $secretName := .Values.tls.quorum.passwordsSecretName -}}
|
||||
{{- if $secretName -}}
|
||||
{{- printf "%s" (tpl $secretName $) -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-quorum-tls-pass" (include "common.names.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the secret containing Zookeper client TLS certificates
|
||||
*/}}
|
||||
{{- define "zookeeper.client.tlsSecretName" -}}
|
||||
{{- $secretName := .Values.tls.client.existingSecret -}}
|
||||
{{- if $secretName -}}
|
||||
{{- printf "%s" (tpl $secretName $) -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-client-crt" (include "common.names.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return true if a TLS secret object should be created
|
||||
*/}}
|
||||
{{- define "zookeeper.client.createTlsSecret" -}}
|
||||
{{- if and .Values.tls.client.enabled .Values.tls.client.autoGenerated (not .Values.tls.client.existingSecret) }}
|
||||
{{- true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Return the name of the secret containing the Keystore and truststore password
|
||||
*/}}
|
||||
{{- define "zookeeper.client.tlsPasswordsSecret" -}}
|
||||
{{- $secretName := .Values.tls.client.passwordsSecretName -}}
|
||||
{{- if $secretName -}}
|
||||
{{- printf "%s" (tpl $secretName $) -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-client-tls-pass" (include "common.names.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Compile all warnings into a single message.
|
||||
*/}}
|
||||
{{- define "zookeeper.validateValues" -}}
|
||||
{{- $messages := list -}}
|
||||
{{- $messages := append $messages (include "zookeeper.validateValues.client.tls" .) -}}
|
||||
{{- $messages := append $messages (include "zookeeper.validateValues.quorum.tls" .) -}}
|
||||
{{- $messages := without $messages "" -}}
|
||||
{{- $message := join "\n" $messages -}}
|
||||
|
||||
{{- if $message -}}
|
||||
{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Validate values of Zookeeper - Client TLS enabled */}}
|
||||
{{- define "zookeeper.validateValues.client.tls" -}}
|
||||
{{- if and .Values.tls.client.enabled (not .Values.tls.client.autoGenerated) (not .Values.tls.client.existingSecret) }}
|
||||
zookeeper: tls.client.enabled
|
||||
In order to enable Client TLS encryption, you also need to provide
|
||||
an existing secret containing the Keystore and Truststore or
|
||||
enable auto-generated certificates.
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Validate values of Zookeeper - Quorum TLS enabled */}}
|
||||
{{- define "zookeeper.validateValues.quorum.tls" -}}
|
||||
{{- if and .Values.tls.quorum.enabled (not .Values.tls.quorum.autoGenerated) (not .Values.tls.quorum.existingSecret) }}
|
||||
zookeeper: tls.quorum.enabled
|
||||
In order to enable Quorum TLS, you also need to provide
|
||||
an existing secret containing the Keystore and Truststore or
|
||||
enable auto-generated certificates.
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -15,4 +15,51 @@ type: Opaque
|
||||
data:
|
||||
client-password: {{ include "zookeeper.clientPassword" . | b64enc | quote }}
|
||||
server-password: {{ include "zookeeper.serverPasswords" . | b64enc | quote }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- if and .Values.tls.client.enabled (not .Values.tls.client.existingSecret) (or .Values.tls.client.keystorePassword .Values.tls.client.truststorePassword .Values.tls.client.autoGenerated) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ template "common.names.fullname" . }}-client-tls-pass
|
||||
namespace: {{ template "zookeeper.namespace" . }}
|
||||
labels: {{- include "common.labels.standard" . | nindent 4 }}
|
||||
{{- if .Values.commonLabels }}
|
||||
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{- if or .Values.tls.client.keystorePassword .Values.tls.client.autoGenerated }}
|
||||
keystore-password: {{ (.Values.tls.client.keystorePassword | default (randAlphaNum 10)) | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.client.truststorePassword .Values.tls.client.autoGenerated }}
|
||||
truststore-password: {{ (.Values.tls.client.truststorePassword | default (randAlphaNum 10))| b64enc | quote }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
{{- if and .Values.tls.quorum.enabled (not .Values.tls.quorum.existingSecret) (or .Values.tls.quorum.keystorePassword .Values.tls.quorum.truststorePassword .Values.tls.quorum.autoGenerated) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ template "common.names.fullname" . }}-quorum-tls-pass
|
||||
namespace: {{ template "zookeeper.namespace" . }}
|
||||
labels: {{- include "common.labels.standard" . | nindent 4 }}
|
||||
{{- if .Values.commonLabels }}
|
||||
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{- if or .Values.tls.quorum.keystorePassword .Values.tls.quorum.autoGenerated }}
|
||||
keystore-password: {{ (.Values.tls.quorum.keystorePassword | default (randAlphaNum 10)) | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.quorum.truststorePassword .Values.tls.quorum.autoGenerated }}
|
||||
truststore-password: {{ (.Values.tls.quorum.truststorePassword | default (randAlphaNum 10))| b64enc | quote }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- end }}
|
||||
|
||||
@@ -35,8 +35,17 @@ spec:
|
||||
{{- if .Values.podLabels }}
|
||||
{{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
|
||||
{{- if or .Values.podAnnotations (include "zookeeper.client.createTlsSecret" .) (include "zookeeper.quorum.createTlsSecret" .) }}
|
||||
annotations:
|
||||
{{- if (include "zookeeper.client.createTlsSecret" .) }}
|
||||
checksum/tls-client: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if (include "zookeeper.quorum.createTlsSecret" .) }}
|
||||
checksum/tls-quorum: {{ include (print $.Template.BasePath "/tls-secret.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{- include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.schedulerName }}
|
||||
@@ -68,7 +77,7 @@ spec:
|
||||
{{- if .Values.priorityClassName }}
|
||||
priorityClassName: {{ .Values.priorityClassName }}
|
||||
{{- end }}
|
||||
{{- if or .Values.initContainers (and .Values.volumePermissions.enabled .Values.persistence.enabled) }}
|
||||
{{- if or .Values.initContainers (and .Values.volumePermissions.enabled .Values.persistence.enabled) (or .Values.tls.client.enabled .Values.tls.quorum.enabled) }}
|
||||
initContainers:
|
||||
{{- if .Values.initContainers }}
|
||||
{{ include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | trim | nindent 8 }}
|
||||
@@ -99,6 +108,131 @@ spec:
|
||||
mountPath: {{ .Values.dataLogDir }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.client.enabled .Values.tls.quorum.enabled }}
|
||||
- name: init-certs
|
||||
image: {{ include "zookeeper.image" . }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy | quote }}
|
||||
{{- if .Values.securityContext.enabled }}
|
||||
securityContext:
|
||||
runAsUser: {{ .Values.securityContext.runAsUser }}
|
||||
{{- end }}
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |-
|
||||
{{- if .Values.tls.client.enabled }}
|
||||
{{- if .Values.tls.client.autoGenerated }}
|
||||
if [[ -f "/certs/client/tls.key" ]] && [[ -f "/certs/client/tls.crt" ]] && [[ -f "/certs/client/ca.crt" ]]; then
|
||||
openssl pkcs12 -export -in "/certs/client/tls.crt" \
|
||||
-passout pass:"${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \
|
||||
-inkey "/certs/client/tls.key" \
|
||||
-out "/tmp/keystore.p12"
|
||||
keytool -importkeystore -srckeystore "/tmp/keystore.p12" \
|
||||
-srcstoretype PKCS12 \
|
||||
-srcstorepass "${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \
|
||||
-deststorepass "${ZOO_TLS_CLIENT_KEYSTORE_PASSWORD}" \
|
||||
-destkeystore "/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks"
|
||||
rm "/tmp/keystore.p12"
|
||||
keytool -import -file "/certs/client/ca.crt" \
|
||||
-keystore "/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks" \
|
||||
-storepass "${ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD}" \
|
||||
-noprompt
|
||||
else
|
||||
echo "Couldn't find the expected PEM certificates! They are mandatory when Client encryption via TLS is enabled."
|
||||
exit 1
|
||||
fi
|
||||
{{- else }}
|
||||
if [[ -f "/certs/client/zookeeper.truststore.jks" ]] && [[ -f "/certs/client/zookeeper.keystore.jks" ]]; then
|
||||
cp "/certs/client/zookeeper.truststore.jks" "/opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks"
|
||||
cp "/certs/client/zookeeper.keystore.jks" "/opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks"
|
||||
else
|
||||
echo "Couldn't find the expected Java Key Stores (JKS) files! They are mandatory when Client encryption via TLS is enabled."
|
||||
exit 1
|
||||
fi
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.tls.quorum.enabled }}
|
||||
{{- if .Values.tls.quorum.autoGenerated }}
|
||||
if [[ -f "/certs/quorum/tls.key" ]] && [[ -f "/certs/quorum/tls.crt" ]] && [[ -f "/certs/quorum/ca.crt" ]]; then
|
||||
openssl pkcs12 -export -in "/certs/quorum/tls.crt" \
|
||||
-passout pass:"${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \
|
||||
-inkey "/certs/quorum/tls.key" \
|
||||
-out "/tmp/keystore.p12"
|
||||
keytool -importkeystore -srckeystore "/tmp/keystore.p12" \
|
||||
-srcstoretype PKCS12 \
|
||||
-srcstorepass "${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \
|
||||
-deststorepass "${ZOO_TLS_QUORUM_KEYSTORE_PASSWORD}" \
|
||||
-destkeystore "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks"
|
||||
rm "/tmp/keystore.p12"
|
||||
keytool -import -file "/certs/quorum/ca.crt" \
|
||||
-keystore "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks" \
|
||||
-storepass "${ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD}" \
|
||||
-noprompt
|
||||
else
|
||||
echo "Couldn't find the expected PEM certificates! They are mandatory when encryption Quorum via TLS is enabled."
|
||||
exit 1
|
||||
fi
|
||||
{{- else }}
|
||||
if [[ -f "/certs/quorum/zookeeper.truststore.jks" ]] && [[ -f "/certs/quorum/zookeeper.keystore.jks" ]]; then
|
||||
cp "/certs/quorum/zookeeper.truststore.jks" "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks"
|
||||
cp "/certs/quorum/zookeeper.keystore.jks" "/opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks"
|
||||
else
|
||||
echo "Couldn't find the expected Java Key Stores (JKS) files! They are mandatory when Quorum encryption via TLS is enabled."
|
||||
exit 1
|
||||
fi
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: metadata.name
|
||||
{{- if or .Values.tls.client.keystorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }}
|
||||
- name: ZOO_TLS_CLIENT_KEYSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.client.tlsPasswordsSecret" . }}
|
||||
key: "keystore-password"
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.client.truststorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }}
|
||||
- name: ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.client.tlsPasswordsSecret" . }}
|
||||
key: "truststore-password"
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.quorum.keystorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }}
|
||||
- name: ZOO_TLS_QUORUM_KEYSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }}
|
||||
key: "keystore-password"
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.quorum.truststorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }}
|
||||
- name: ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }}
|
||||
key: "truststore-password"
|
||||
{{- end }}
|
||||
{{- if .Values.tls.resources }}
|
||||
resources: {{- toYaml .Values.tls.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
{{- if or .Values.tls.client.enabled }}
|
||||
- name: client-certificates
|
||||
mountPath: /certs/client
|
||||
- name: client-shared-certs
|
||||
mountPath: /opt/bitnami/zookeeper/config/certs/client
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.quorum.enabled }}
|
||||
- name: quorum-certificates
|
||||
mountPath: /certs/quorum
|
||||
- name: quorum-shared-certs
|
||||
mountPath: /opt/bitnami/zookeeper/config/certs/quorum
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: zookeeper
|
||||
@@ -198,30 +332,50 @@ spec:
|
||||
- name: ZOO_PROMETHEUS_METRICS_PORT_NUMBER
|
||||
value: {{ .Values.metrics.containerPort | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.tls.client_enable }}
|
||||
{{- if .Values.tls.client.enabled }}
|
||||
- name: ZOO_TLS_CLIENT_ENABLE
|
||||
value: {{ .Values.service.tls.client_enable | quote }}
|
||||
value: {{ .Values.tls.client.enabled | quote }}
|
||||
- name: ZOO_TLS_CLIENT_KEYSTORE_FILE
|
||||
value: {{ .Values.service.tls.client_keystore_path | quote }}
|
||||
- name: ZOO_TLS_CLIENT_KEYSTORE_PASSWORD
|
||||
value: {{ .Values.service.tls.client_keystore_password | quote }}
|
||||
value: {{ .Values.tls.client.keystorePath | quote }}
|
||||
- name: ZOO_TLS_CLIENT_TRUSTSTORE_FILE
|
||||
value: {{ .Values.service.tls.client_truststore_path | quote }}
|
||||
value: {{ .Values.tls.client.truststorePath | quote }}
|
||||
{{- if or .Values.tls.client.keystorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }}
|
||||
- name: ZOO_TLS_CLIENT_KEYSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.client.tlsPasswordsSecret" . }}
|
||||
key: "keystore-password"
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.client.truststorePassword .Values.tls.client.passwordsSecretName .Values.tls.client.autoGenerated }}
|
||||
- name: ZOO_TLS_CLIENT_TRUSTSTORE_PASSWORD
|
||||
value: {{ .Values.service.tls.client_truststore_password | quote }}
|
||||
{{ end }}
|
||||
{{- if .Values.service.tls.quorum_enable }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.client.tlsPasswordsSecret" . }}
|
||||
key: "truststore-password"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.tls.quorum.enabled }}
|
||||
- name: ZOO_TLS_QUORUM_ENABLE
|
||||
value: {{ .Values.service.tls.quorum_enable | quote }}
|
||||
value: {{ .Values.tls.quorum.enabled | quote }}
|
||||
- name: ZOO_TLS_QUORUM_KEYSTORE_FILE
|
||||
value: {{ .Values.service.tls.quorum_keystore_path | quote }}
|
||||
- name: ZOO_TLS_QUORUM_KEYSTORE_PASSWORD
|
||||
value: {{ .Values.service.tls.quorum_keystore_password | quote }}
|
||||
value: {{ .Values.tls.quorum.keystorePath | quote }}
|
||||
- name: ZOO_TLS_QUORUM_TRUSTSTORE_FILE
|
||||
value: {{ .Values.service.tls.quorum_truststore_path | quote }}
|
||||
value: {{ .Values.tls.quorum.truststorePath | quote }}
|
||||
{{- if or .Values.tls.quorum.keystorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }}
|
||||
- name: ZOO_TLS_QUORUM_KEYSTORE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }}
|
||||
key: "keystore-password"
|
||||
{{- end }}
|
||||
{{- if or .Values.tls.quorum.truststorePassword .Values.tls.quorum.passwordsSecretName .Values.tls.quorum.autoGenerated }}
|
||||
- name: ZOO_TLS_QUORUM_TRUSTSTORE_PASSWORD
|
||||
value: {{ .Values.service.tls.quorum_truststore_password | quote }}
|
||||
{{ end }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "zookeeper.quorum.tlsPasswordsSecret" . }}
|
||||
key: "truststore-password"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
@@ -231,14 +385,14 @@ spec:
|
||||
{{- toYaml .Values.extraEnvVars | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
{{ if not .Values.service.tls.disable_base_client_port }}
|
||||
{{- if not .Values.service.disableBaseClientPort }}
|
||||
- name: client
|
||||
containerPort: {{ .Values.service.port }}
|
||||
{{ end }}
|
||||
{{ if .Values.service.tls.client_enable }}
|
||||
{{- end }}
|
||||
{{- if .Values.tls.client.enabled }}
|
||||
- name: client-tls
|
||||
containerPort: {{ .Values.service.tls.client_port }}
|
||||
{{ end }}
|
||||
containerPort: {{ .Values.service.tlsClientPort }}
|
||||
{{- end }}
|
||||
- name: follower
|
||||
containerPort: {{ .Values.service.followerPort }}
|
||||
- name: election
|
||||
@@ -250,13 +404,13 @@ spec:
|
||||
{{- if .Values.livenessProbe.enabled }}
|
||||
livenessProbe:
|
||||
exec:
|
||||
{{- if not .Values.service.tls.disable_base_client_port }}
|
||||
{{- if not .Values.service.disableBaseClientPort }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} nc -w {{ .Values.livenessProbe.probeCommandTimeout }} localhost {{ .Values.service.port }} | grep imok']
|
||||
{{- else }}
|
||||
{{- if not .Values.service.tls.client_enable }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tls.client_port }} | grep imok']
|
||||
{{- if not .Values.tls.client.enabled }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tlsClientPort }} | grep imok']
|
||||
{{- else }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tls.client_port }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok']
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tlsClientPort }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok']
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
|
||||
@@ -270,13 +424,13 @@ spec:
|
||||
{{- if .Values.readinessProbe.enabled }}
|
||||
readinessProbe:
|
||||
exec:
|
||||
{{- if not .Values.service.tls.disable_base_client_port }}
|
||||
{{- if not .Values.service.disableBaseClientPort }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.readinessProbe.probeCommandTimeout }} nc -w {{ .Values.readinessProbe.probeCommandTimeout }} localhost {{ .Values.service.port }} | grep imok']
|
||||
{{- else }}
|
||||
{{- if not .Values.service.tls.client_enable }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tls.client_port }} | grep imok']
|
||||
{{- if not .Values.tls.client.enabled }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tlsClientPort }} | grep imok']
|
||||
{{- else }}
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tls.client_port }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok']
|
||||
command: ['/bin/bash', '-c', 'echo "ruok" | timeout {{ .Values.livenessProbe.probeCommandTimeout }} openssl s_client -quiet -crlf -connect localhost:{{ .Values.service.tlsClientPort }} -cert {{ .Values.service.tls.client_cert_pem_path }} -key {{ .Values.service.tls.client_key_pem_path }} | grep imok']
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
|
||||
@@ -299,6 +453,16 @@ spec:
|
||||
mountPath: /opt/bitnami/zookeeper/conf/zoo.cfg
|
||||
subPath: zoo.cfg
|
||||
{{- end }}
|
||||
{{- if .Values.tls.client.enabled }}
|
||||
- name: client-shared-certs
|
||||
mountPath: /opt/bitnami/zookeeper/config/certs/client
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if .Values.tls.quorum.enabled }}
|
||||
- name: quorum-shared-certs
|
||||
mountPath: /opt/bitnami/zookeeper/config/certs/quorum
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if .Values.extraVolumeMounts }}
|
||||
{{- toYaml .Values.extraVolumeMounts | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -324,6 +488,22 @@ spec:
|
||||
- name: data-log
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- if .Values.tls.client.enabled }}
|
||||
- name: client-certificates
|
||||
secret:
|
||||
secretName: {{ include "zookeeper.client.tlsSecretName" . }}
|
||||
defaultMode: 256
|
||||
- name: client-shared-certs
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- if .Values.tls.quorum.enabled }}
|
||||
- name: quorum-certificates
|
||||
secret:
|
||||
secretName: {{ include "zookeeper.quorum.tlsSecretName" . }}
|
||||
defaultMode: 256
|
||||
- name: quorum-shared-certs
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- if .Values.extraVolumes }}
|
||||
{{- toYaml .Values.extraVolumes | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -22,14 +22,14 @@ spec:
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: {{ .Values.service.publishNotReadyAddresses }}
|
||||
ports:
|
||||
{{ if not .Values.service.tls.disable_base_client_port }}
|
||||
{{ if not .Values.service.disableBaseClientPort }}
|
||||
- name: tcp-client
|
||||
port: 2181
|
||||
targetPort: client
|
||||
{{ end }}
|
||||
{{ if .Values.service.tls.client_enable }}
|
||||
{{ if .Values.tls.client.enabled }}
|
||||
- name: tcp-client-tls
|
||||
port: {{ .Values.service.tls.client_port }}
|
||||
port: {{ .Values.service.tlsClientPort }}
|
||||
targetPort: client-tls
|
||||
{{ end }}
|
||||
- name: follower
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
|
||||
{{- end }}
|
||||
ports:
|
||||
{{ if not .Values.service.tls.disable_base_client_port }}
|
||||
{{ if not .Values.service.disableBaseClientPort }}
|
||||
- name: tcp-client
|
||||
port: 2181
|
||||
targetPort: client
|
||||
@@ -33,9 +33,9 @@ spec:
|
||||
nodePort: null
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ if .Values.service.tls.client_enable }}
|
||||
{{ if .Values.tls.client.enabled }}
|
||||
- name: tcp-client-tls
|
||||
port: {{ .Values.service.tls.client_port }}
|
||||
port: {{ .Values.service.tlsClientPort }}
|
||||
targetPort: client-tls
|
||||
{{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.clientTls)) }}
|
||||
nodePort: {{ .Values.service.nodePorts.clientTls }}
|
||||
|
||||
53
bitnami/zookeeper/templates/tls-secret.yaml
Normal file
53
bitnami/zookeeper/templates/tls-secret.yaml
Normal file
@@ -0,0 +1,53 @@
|
||||
{{- if (include "zookeeper.client.createTlsSecret" .) }}
|
||||
---
|
||||
{{- $ca := genCA "zookeeper-client-ca" 365 }}
|
||||
{{- $releaseNamespace := .Release.Namespace }}
|
||||
{{- $clusterDomain := .Values.clusterDomain }}
|
||||
{{- $fullname := include "common.names.fullname" . }}
|
||||
{{- $serviceName := include "common.names.fullname" . }}
|
||||
{{- $altNames := list (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname }}
|
||||
{{- $crt := genSignedCert $fullname nil $altNames 365 $ca }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "common.names.fullname" . }}-client-crt
|
||||
labels: {{- include "common.labels.standard" . | nindent 4 }}
|
||||
{{- if .Values.commonLabels }}
|
||||
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
type: kubernetes.io/tls
|
||||
data:
|
||||
ca.crt: {{ $ca.Cert | b64enc | quote }}
|
||||
tls.crt: {{ $crt.Cert | b64enc | quote }}
|
||||
tls.key: {{ $crt.Key | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- if (include "zookeeper.quorum.createTlsSecret" .) }}
|
||||
---
|
||||
{{- $ca := genCA "zookeeper-quorum-ca" 365 }}
|
||||
{{- $releaseNamespace := .Release.Namespace }}
|
||||
{{- $clusterDomain := .Values.clusterDomain }}
|
||||
{{- $fullname := include "common.names.fullname" . }}
|
||||
{{- $serviceName := include "common.names.fullname" . }}
|
||||
{{- $headlessServiceName := printf "%s-headless" (include "common.names.fullname" .) }}
|
||||
{{- $altNames := list (printf "*.%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) $fullname }}
|
||||
{{- $crt := genSignedCert $fullname nil $altNames 365 $ca }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "common.names.fullname" . }}-quorum-crt
|
||||
labels: {{- include "common.labels.standard" . | nindent 4 }}
|
||||
{{- if .Values.commonLabels }}
|
||||
{{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.commonAnnotations }}
|
||||
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
|
||||
{{- end }}
|
||||
type: kubernetes.io/tls
|
||||
data:
|
||||
ca.crt: {{ $ca.Cert | b64enc | quote }}
|
||||
tls.crt: {{ $crt.Cert | b64enc | quote }}
|
||||
tls.key: {{ $crt.Key | b64enc | quote }}
|
||||
{{- end }}
|
||||
@@ -234,26 +234,80 @@ service:
|
||||
client: ""
|
||||
clientTls: ""
|
||||
publishNotReadyAddresses: true
|
||||
tls:
|
||||
client_enable: false
|
||||
quorum_enable: false
|
||||
disable_base_client_port: false
|
||||
|
||||
client_port: 3181
|
||||
## TLS port settings. Previously service.tls.service_port and service.tls.disable_base_client_port
|
||||
##
|
||||
tlsClientPort: 3181
|
||||
disableBaseClientPort: false
|
||||
|
||||
client_keystore_path: /tls_key_store/key_store_file
|
||||
client_keystore_password: ''
|
||||
client_truststore_path: /tls_trust_store/trust_store_file
|
||||
client_truststore_password: ''
|
||||
|
||||
quorum_keystore_path: /tls_key_store/key_store_file
|
||||
quorum_keystore_password: ''
|
||||
quorum_truststore_path: /tls_trust_store/trust_store_file
|
||||
quorum_truststore_password: ''
|
||||
annotations: {}
|
||||
headless:
|
||||
annotations: {}
|
||||
|
||||
## Enable SSL/TLS encryption
|
||||
##
|
||||
tls:
|
||||
client:
|
||||
enabled: false
|
||||
|
||||
## Create self-signed TLS certificates. Currently only supports PEM certificates.
|
||||
##
|
||||
autoGenerated: false
|
||||
|
||||
## Name of the existing secret containing Kibana server certificates
|
||||
##
|
||||
existingSecret:
|
||||
|
||||
## Keystore and Truststore Path
|
||||
##
|
||||
keystorePath: /opt/bitnami/zookeeper/config/certs/client/zookeeper.keystore.jks
|
||||
truststorePath: /opt/bitnami/zookeeper/config/certs/client/zookeeper.truststore.jks
|
||||
|
||||
## Existing secret containing Keystore and truststore passwords
|
||||
##
|
||||
passwordsSecretName:
|
||||
## Keystore and Truststore Password
|
||||
##
|
||||
keystorePassword: ''
|
||||
truststorePassword: ''
|
||||
|
||||
quorum:
|
||||
## Create self-signed TLS certificates. Currently only supports PEM certificates.
|
||||
##
|
||||
autoGenerated: false
|
||||
|
||||
## Name of the existing secret containing Kibana server certificates
|
||||
##
|
||||
existingSecret:
|
||||
|
||||
## Keystore and Truststore Path
|
||||
##
|
||||
keystorePath: /opt/bitnami/zookeeper/config/certs/quorum/zookeeper.keystore.jks
|
||||
truststorePath: /opt/bitnami/zookeeper/config/certs/quorum/zookeeper.truststore.jks
|
||||
|
||||
## Existing secret containing Keystore and truststore passwords
|
||||
##
|
||||
passwordsSecretName:
|
||||
## Keystore and Truststore Password
|
||||
##
|
||||
keystorePassword: ''
|
||||
truststorePassword: ''
|
||||
|
||||
resources:
|
||||
## We usually recommend not to specify default resources and to leave this as a conscious
|
||||
## choice for the user. This also increases chances charts run on environments with little
|
||||
## resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
## lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
##
|
||||
limits: {}
|
||||
## cpu: 100m
|
||||
## memory: 128Mi
|
||||
##
|
||||
requests: {}
|
||||
## cpu: 100m
|
||||
## memory: 128Mi
|
||||
##
|
||||
|
||||
## Service account for Zookeeper to use.
|
||||
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
|
||||
##
|
||||
|
||||
Reference in New Issue
Block a user