Vault HA + Consul HA + k8s

от автора

В данной статье будет показан процесс установки HashiCorp Vault с связке с Consul, который выступает хранилищем для Vault, в HA режиме с включенным tls шифрованием в Kubernetes кластер.
Исходные данные:
— Kubernetes кластер (например yandex)
— В кластере установлен nginx ingress controller, в статье класс определен как nginx-internal
Для UI существуют TLS/SSL сертификаты.
— Установлен helm 3
— есть доступ к кластеру через kubectl с правами администратора.
— есть возможность запустить docker контейнер

Установка Consul

Скачать исходники чартов consul-k8s https://github.com/hashicorp/consul-k8s/tree/main/charts/consul

Либо через helm, но доступ из России сейчас запрещен:

$ helm repo add hashicorp https://helm.releases.hashicorp.com $ helm pull hashicorp/consul

Создать namespace:

$ kubectl create namespace consul

Создать secret для ssl сертификатов для ingress контроллера.

Допустим у вас есть ssl сертификаты от https://letsencrypt.org/ :
Создать фаил secret-tls-consul.yaml :

apiVersion: v1 kind: Secret metadata:   name: secret-tls data:   tls.crt: <tls.crt base64>   tls.key: <tls.key base64> type: kubernetes.io/tls

где:
вместо <tls.crt base64> подставить заначение:
$ cat fullchain1.pem | base64 -w0
вместо <tls.key base64> подставить значение:
$ cat privkey1.pem | base64 -w0

Применить данный манифест:

$ kubectl -n consul apply -f secret-tls-consul.yaml

Создать фаил values-work.yaml где переопределить некоторые переменные:

global:   enabled: true   logLevel: "info"   logJSON: true   datacenter: vault-storage   gossipEncryption:     autoGenerate: false     secretName: consul-consul-gossip-encryption-key     secretKey: key   tls:     enabled: true     enableAutoEncrypt: true   server:   enabled: "-"   replicas: 3   storage: 5Gi   storageClass: yc-network-ssd   connect: true   client:   enabled: false   connectInject:   enabled: false   ui:   enabled: "-"   service:     enabled: true   ingress:     enabled: true       annotations: |       "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS"       ingressClassName: nginx-internal     pathType: Prefix       hosts:     - host: consul.k8s.domain.com       tls:       - secretName: secret-tls         hosts:           - consul.k8s.domain.com

Основные моменты файла:
gossipEncryption : включаем шифрование, указываем имя секрета, где будет храниться ключ. Сам ключ создается так (<KeyString> нужно сгенерировать):

$ docker run --rm -ti --entrypoint=/bin/sh hashicorp/consul:latest / # consul keygen <KeyString>
$ kubectl -n consul create secret generic \      consul-consul-gossip-encryption-key --from-literal=key=<KeyString>

replicas: 3: количество реплик, должно быть достаточное количество нод в k8s либо включен автоскейлинг
storageClass: yc-network-ssd — один из классов в yandex. Острожно, у него RECLAIMPOLICY=Delete, это означает, что если удалить pvc, то диск, который будет создан, тоже будет удален.
enableAutoEncrypt: true: благодаря этой строчке будет создан самоподписной CA (секреты: consul-consul-ca-cert,consul-consul-ca-key), а также сертифкат и ключ для самого consul (секрет: consul-consul-server-cert).

Можно попробовать запустить:

$ helm -n consul upgrade --install consul -f values-work.yaml ./consul-1.3.1

где ./consul-1.3.1 — папка с чартом consul

$ kubectl -n consul get pods,pvc NAME                         READY   STATUS    RESTARTS   AGE pod/consul-consul-server-0   1/1     Running   0          30m pod/consul-consul-server-1   1/1     Running   0          30m pod/consul-consul-server-2   1/1     Running   0          30m  NAME                                                       STATUS   persistentvolumeclaim/data-consul-consul-consul-server-0   Bound    persistentvolumeclaim/data-consul-consul-consul-server-1   Bound    persistentvolumeclaim/data-consul-consul-consul-server-2   Bound

Самоподписные SSL:
При использовании опции enableAutoEncrypt: true Сертификат для consul создается сроком на два года, чтобы его обновлять автоматом необходимо периодически переустанавливать сам consul. К тому же выпускается от стороннего производителя, примерно такой:

Issuer: C = US, ST = CA, L = San Francisco, street = 101 Second Street, postalCode = 94105, O = HashiCorp Inc., CN = Consul Agent CA

Необходимо создать свой CA и свои сертификаты, заполнить по необходимости, или взять уже существующие:

$ openssl genrsa -out rootCA.key 4096 $ openssl req -x509 -new -key rootCA.key -days 10000 -out rootCA.crt ........ Country Name (2 letter code) [AU]:RU State or Province Name (full name) [Some-State]:Moscow Locality Name (eg, city) []:Moscow Organization Name (eg, company) [Internet Widgits Pty Ltd]:Domain LTD Organizational Unit Name (eg, section) []:devops Common Name (e.g. server FQDN or YOUR name) []: Email Address []:devops@domain.com

Создать фаил consul.conf:

[req] default_bits = 4096 prompt = no default_md = sha256   req_extensions = v3_req distinguished_name = dn   [dn] C = RU ST = Moscow L = Moscow CN = server.vault-storage.consul   [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment

Создать фаил consul.ext:

subjectAltName=DNS:consul-consul-server, DNS:*.consul-consul-server, DNS:*.consul-consul-server.consul, DNS:consul-consul-server.consul, DNS:*.consul-consul-server.consul.svc, DNS:consul-consul-server.consul.svc, DNS:*.server.vault-storage.consul, DNS:server.vault-storage.consul, DNS:localhost, IP:127.0.0.1

Список этих subjectAltName можно подсмотреть в сертификате, который был сгенерирован автоматически.

Сгенерировать ключ и выпустить/подписать сертифкат:

$ openssl genrsa -out consul.key 4096   $ openssl req -new \     -key consul.key \     -out consul.csr \     -config consul.conf   $ openssl x509 -req \     -in consul.csr \     -CA rootCA.crt -CAkey rootCA.key \     -CAcreateserial -out consul.crt \     -extfile consul.ext \     -days 5000

Создать секреты с этими ключом с сертификатами:

$ kubectl -n consul create secret generic root-ca --from-file=tls.crt=rootCA.crt $ kubectl -n consul create secret tls consul --key consul.key --cert consul.crt

Поменять values-work.yaml для использования этих секретов:

global:   enabled: true   logLevel: "info"   logJSON: true   datacenter: vault-storage   gossipEncryption:     autoGenerate: false     secretName: consul-consul-gossip-encryption-key     secretKey: key   tls:     enabled: true     enableAutoEncrypt: false     caCert:       # The name of the Kubernetes secret.       secretName: root-ca       # The key of the Kubernetes secret.       secretKey: tls.crt     verify: true   server:   enabled: "-"   replicas: 3   storage: 5Gi   storageClass: yc-network-ssd   connect: true   serverCert:     # The name of the Vault secret that holds the PEM encoded server certificate.     # @type: string     secretName: consul   client:   enabled: false   connectInject:   enabled: false   ui:   enabled: "-"   service:     enabled: true   ingress:     enabled: true       annotations: |       "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS"       ingressClassName: nginx-internal     pathType: Prefix       hosts:     - host: consul.k8s.domain.com       tls:       - secretName: secret-tls         hosts:           - consul.k8s.domain.com

Удалить ненужные секреты и запустить с новыми параметрами:

$ kubectl -n consul delete secrets consul-consul-ca-cert consul-consul-ca-key consul-consul-server-cert $ helm -n consul upgrade --install consul -f values-work.yaml ./consul-1.3.1

Удалить все поды consul, так как из-за новых сертификатов, они не смогут взаимодействовать друг с другом:

$ kubectl -n consul delete pods --all

Закрыть токеном UI consul

Привести конфигурацию values-work.yaml к виду:

global:   enabled: true   logLevel: "info"   logJSON: true   datacenter: vault-storage   gossipEncryption:     autoGenerate: false     secretName: consul-consul-gossip-encryption-key     secretKey: key   tls:     enabled: true     enableAutoEncrypt: false     caCert:       # The name of the Kubernetes secret.       secretName: root-ca       # The key of the Kubernetes secret.       secretKey: tls.crt     verify: true   server:   enabled: "-"   replicas: 3   storage: 5Gi   storageClass: yc-network-ssd   connect: true   serverCert:     # The name of the Vault secret that holds the PEM encoded server certificate.     # @type: string     secretName: consul     extraConfig: |         {           "addresses": {             "http": "0.0.0.0"           },           "acl": {             "enabled": true,             "default_policy": "deny",             "down_policy": "extend-cache",             "enable_token_persistence": true           }         }   client:   enabled: false   connectInject:   enabled: false   ui:   enabled: "-"   service:     enabled: true   ingress:     enabled: true       annotations: |       "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS"       ingressClassName: nginx-internal     pathType: Prefix       hosts:     - host: consul.k8s.domain.com       tls:       - secretName: secret-tls         hosts:           - consul.k8s.domain.com

Применить/перезапустить:

$ helm -n consul upgrade --install consul -f values-work.yaml ./consul-1.3.1 

Дождаться пока перезапустятся все поды, и будет выбран лидер — можно смотреть по логам:

$ kubectl -n consul logs consul-consul-server-2 -f

Запустить генерацию bootstrap токена:

$ kubectl -n consul exec -ti consul-consul-server-0 -- sh -c "consul acl bootstrap | grep -i secretid" Defaulted container "consul" out of: consul, locality-init (init) SecretID:         <bootstrap token string>

Токен (SecretID) необходимо сохранить в безопасном месте.

Через web интерфейс проверить что все работает: авторизоваться используя полученный токен.

Важный момент для диагностики, если что-то пошло не так
В моем случае в логах была ошибка, что нет ключа для дешифровки сообщений. Необходимо проверить следующее:

$ kubectl -n consul exec -ti consul-consul-server-0 -- \             cat /consul/data/serf/local.keyring ["<KeyString>"]

Ключ должен находиться в списке всех этих файлов (в каждом поде). Это тот самый ключ из секрета consul-consul-gossip-encryption-key, здесь он будет указан без base64 кодировки.

Создать политику и токен для Vault:

Создание Политики

Создание Политики
Создание Токена в связке с Политикой

Создание Токена в связке с Политикой

Токен понадобится далее.

Установка Vault

Создать tls/ssl сертификаты

Создать файлы:
vault.conf:

[req] default_bits = 4096 prompt = no default_md = sha256   req_extensions = v3_req distinguished_name = dn   [dn] C = RU ST = Russia L = Moscow CN = vault-server-tls.vault.svc   [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment

vault.ext:

subjectAltName=DNS:vault,DNS:localhost,DNS:vault-0.vault-internal,DNS:vault-1.vault-internal,DNS:vault-2.vault-internal,IP:127.0.0.1

Создать ключ и сертифкат:

$ openssl genrsa -out vault.key 4096   $ openssl req -new \     -key vault.key \     -out vault.csr \     -config vault.conf   $ openssl x509 -req \     -in vault.csr \     -CA rootCA.crt -CAkey rootCA.key \     -CAcreateserial -out vault.crt \     -extfile vault.ext \     -days 5000

Где rootCA.key и rootCA.crt — ключ и сертифкат CA созданные при установке consul

Создать secret-ы из сертификатов:

$ kubectl create ns vault $ kubectl -n vault create secret tls vault --key vault.key --cert vault.crt $ kubectl -n vault create secret generic root-ca --from-file=tls.crt=rootCA.crt

Скачать с github исходники helm для Vault https://github.com/hashicorp/vault-helm.git

Создать secret для ssl сертификатов для ingress контроллера.

Допустим у вас есть ssl сертификаты от https://letsencrypt.org/ :
Создать фаил secret-tls-vault.yaml :

apiVersion: v1 kind: Secret metadata:   name: secret-tls data:   tls.crt: <tls.crt base64>   tls.key: <tls.key base64> type: kubernetes.io/tls

где:
вместо <tls.crt base64> подставить заначение:
$ cat fullchain1.pem | base64 -w0
вместо <tls.key base64> подставить значение:
$ cat privkey1.pem | base64 -w0

Применить данный манифест:

$ kubectl -n vault apply -f secret-tls-vault.yaml

Создать фаил values-work.yaml, где переопределить некоторые значения:

global:   tlsDisable: false   server:   standalone:     enabled: false   dataStorage:     enabled: false   extraEnvironmentVars:     VAULT_CACERT: /vault/userconfig/root-ca/tls.crt   extraVolumes:     - type: secret       name: root-ca     - type: secret       name: vault   ha:     enabled: true     replicas: 3     config: |       ui = true       listener "tcp" {         tls_disable = 0         address = "[::]:8200"         cluster_address = "[::]:8201"         tls_cert_file = "/vault/userconfig/vault/tls.crt"         tls_key_file = "/vault/userconfig/vault/tls.key"       }       storage "consul" {         path = "vault"         address = "consul-consul-server.consul.svc:8501"         scheme = "https"         tls_ca_file = "/vault/userconfig/root-ca/tls.crt"         token = "<vault-token>"       }       service_registration "kubernetes" {}     ingress:     enabled: yes     annotations:       nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"       ingressClassName: nginx-internal       hosts:       - host: vault.k8s.domain.com     tls:       - secretName: secret-tls         hosts:           - vault.k8s.domain.com   injector:   enabled: false   ui:   enabled: false

Основные моменты файла:

tls_disable = 0: включение шифрования трафика
token = "<vault-token>": токен, который был создан в Consul для Vault.
address = "consul-consul-server.consul.svc:8501": имя consul-consul-server.consul.svc должно быть прописано в ssl сертификате для consul.

Установить:

$ helm upgrade -n vault --install vault -f values-work.yaml ./vault-0.27.0

Запустить инициализацию, подставить свои значения для количества ключей и количества для ключей для unseal:

$ kubectl -n vault exec -ti vault-0 -- /bin/sh / $ vault operator init -key-shares=3 -key-threshold=2

Сохранить ключи и токен root в надежном месте!

Распечатать каждый под Vault необходимое количество раз (значение -key-threshold):

$ kubectl -n vault exec -ti vault-0 -- /bin/sh / $ vault operator unseal Unseal Key (will be hidden): Key                Value ---                ----- Seal Type          shamir Initialized        true Sealed             true Total Shares       3 Threshold          2 Unseal Progress    1/2 Unseal Nonce       cb2c64c9-ca57-0c98-2141-e99235f05853 Version            1.15.2 Build Date         2023-11-06T11:33:28Z Storage Type       consul HA Enabled         true

Успешным итогом будет такой вывод для каждого пода:

$ kubectl -n vault exec -ti vault-0 -- vault status Key                      Value ---                      ----- Recovery Seal Type       shamir Initialized              true Sealed                   false Total Recovery Shares    3 Threshold                2 Version                  1.15.2 Build Date               2023-11-06T11:33:28Z Storage Type             consul Cluster Name             vault-cluster-51496cc6 Cluster ID               b77b3b96-9bfa-11a9-21b9-bcdc331bfe00 HA Enabled               true

Проверить доступность через UI

Можно пользоваться!

Для настройки автоматического Unseal здесь на Хабре есть несколько замечательных статей. Повторять их нет необходимости.


ссылка на оригинал статьи https://habr.com/ru/articles/818509/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *