Thank you for reading this post, don't forget to subscribe!
1.Общее по hashicorp vault
2.Установка keepalived
3.Установка hashicorp vault
3.1Создание ключей
3.2.Конфигурация vault
3.3.Добавление сертификатов в доверенные
4.Создание метода аутентификации user/password
4.1Создание admin policy для user
4.2.Добавление group с admin правами
4.3.Добавление дополнительного пользователя в группу
4.4 Настроим все те же доступы но с помощью команд
5.Аутентификация через gitlab
6. Auto unseal hashicorp vault
6.1 установка minikub
6.2 подготовка хранилища для PV (nfs)
6.3 установка vault в minikub
6.4 auto unseal vault с помощью другого vault
7.Бэкап данных(backup raft)
8. Настройка LDAP — freeipa
9. Добавляем секреты из vault в POD (vault установлен в k8s)
9.1 secrets-store-csi-driver
9.2 Vault Secrets Operator (наиболее оптимальный вариант для использования)
9.2.1 Пример с подкидыванием секрета в deployment
9.2.2 Включение Vault Secrets Operator reloader
10.SSH аутентификация через vault
10.1 SSH аутентификация через vault OTP (временный пароль)
10.2 SSH аутентификация через vault SSH (certificate authority)
Vault позволяет хранить секреты, токены, пароли, сертификаты и т.п., предоставляя к ним ограниченный и безопасный доступ. Естественно, внутри Vault все хранится в зашифрованном виде.
Под катом процедура инсталляция и запуск Vault на трех узлах в отказоустойчивом режиме.
В простом варианте Vault состоит из некоторого количества взаимосвязанных между собой серверов, один из которых работает в режиме active, а остальные в режиме standby.
HA, либо High Availability позволяет выдержать потерю одного или нескольких узлов (в зависимости от кофигурации), сохранив при этом доступ к данным. Не со всеми бэкэндами, используемыми для хранения, возможно использовать HA механизм.
Для хранения данных Vault может использовать большое количество бэкэндов.
Полностью поддерживаемые HC бэкэнды (не все, но основные):
Filesystem – название говорит само за себя. Храним данные на локальной файловой системе, там же, где и установлен Vault. Не подходит для HA конфигурации;
Consul – Так же является разработкой HashiCorp. Использование в качестве бэкэнда позволяет реализовать Vault HA. Однако, данный вариант потребует развертывания дополнительных машин и организации отдельного кластера Consul (ну либо все в одном, но я не любитель такого);
Integrated Storage (Raft) – данные Vault так же хранятся на файловой системе, но при этом реплицируются на другие узлы кластера, позволяя им оставаться доступными даже в случае выхода из строя одного из узлов. Данный вариант поддерживает функционал HA.
Мой выбор в данном случае – Integrated Storage.
Итак, начнем с подготовки машин. В моем случае это три машины (минимальное количество для кворума)
добавляем в hosts на все тачки
cat /etc/hosts
192.168.1.171 vault1 vault1.test.local
192.168.1.172 vault2 vault2.test.local
192.168.1.173 vault3 vault3.test.local
192.168.1.174 vault.test.local
айпишник 192.168.1.174 - виртуальный
ставим на все тачки:
yum install -y yum-utils wget curl
yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
и отключаем SELINUX на ВСЕХ тачках
cat /etc/selinux/config
1 |
SELINUX=disabled |
sed -i 's|SELINUX=enforcing|SELINUX=disabled|g' /etc/selinux/config
ребутаемся.
теперь поставим keepalived ОБЯЗАТЕЛЬНО 2 версии
на каждую ноду ставим:
wget http://www.nosuchhost.net/~cheese/fedora/packages/epel-7/x86_64/cheese-release-7-1.noarch.rpm
rpm -Uvh cheese-release*rpm
yum install keepalived -y
systemctl enable keepalived
если есть firewall то добавляем в разрешённые протокол vrrp
firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
firewall-cmd --reload
mkdir /usr/libexec/keepalived/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
cat <<"EOF" | sudo tee /usr/libexec/keepalived/vault-health > /dev/null #!/bin/bash if [ $# -ne 1 ];then echo "WARNING: You must provide health check URL" exit 1 else CHECK_URL=$1 CMD=$(/usr/bin/curl -k -I ${CHECK_URL} 2>/dev/null | grep "200" | wc -l) if [ ${CMD} -eq 1 ];then exit 0 else exit 1 fi fi EOF |
chmod +x /usr/libexec/keepalived/vault-health
mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.old
cat /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# Global definitions configuration block global_defs { router_id LVS_LB } vrrp_script check_vault_health { script "/usr/libexec/keepalived/vault-health https://localhost:8200/v1/sys/health" interval 1 timeout 3 rise 3 fall 3 } vrrp_instance VI_1 { state MASTER virtual_router_id 100 priority 100 advert_int 1 # Please make sure to verify interface name using 'nmcli connection show' interface enp0s3 virtual_ipaddress { # Floating IP 192.168.1.174 dev enp0s3 label enp0s3:vip } track_interface { # Please make sure to verify interface name using 'nmcli connection show' enp0s3 } track_script { check_vault_health } } |
groupadd -r keepalived_script
useradd -r -s /sbin/nologin -g keepalived_script -M keepalived_script
systemctl enable --now keepalived.service
Далее ставим vault
yum -y install vault
проверяем:
1 2 3 |
[root@vault1 ~]# vault version Vault v1.11.3 (17250b25303c6418c283c95b1d5a9c9f16174fe8), built 2022-08-26T10:27:10Z |
Открываем необходимые для работы Vault порты:
1 2 3 |
firewall-cmd --add-port=8200/tcp --permanent firewall-cmd --add-port=8201/tcp --permanent firewall-cmd –reload |
Далее необходимо обзавестись сертификатами. Если таковые отсутствуют – выпустить свои, самоподписанные. Все операции я выполняю с первой ноды.
Сперва выпустим свой CA сертификат, а затем с его помощью подпишем сертификаты для всех узлов кластера:
1 2 3 |
cd /opt/vault/tls/ openssl genrsa 2048 > vault-ca-key.pem openssl req -new -x509 -nodes -days 3650 -key vault-ca-key.pem -out vault-ca-cert.pem |
1 2 3 4 5 6 7 8 |
Country Name (2 letter code) [XX]: State or Province Name (full name) []: Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) []:test.local Email Address []: |
Теперь подготовим конфигурационные файлы, содержащие Subject Alternate Name (SAN) для каждого из узлов. Важно, чтобы в SAN был корректней хостнейм и IP каждого узла:
1 2 3 4 5 6 7 |
echo "[v3_ca] subjectAltName = @alt_names [alt_names] DNS.1 = vault1.test.local IP.1 = 192.168.1.171 IP.2 = 127.0.0.1 " > ./cert-a.cfg |
1 2 3 4 5 6 7 |
echo "[v3_ca] subjectAltName = @alt_names [alt_names] DNS.1 = vault2.test.local IP.1 = 192.168.1.172 IP.2 = 127.0.0.1 " > ./cert-b.cfg |
1 2 3 4 5 6 7 |
echo "[v3_ca] subjectAltName = @alt_names [alt_names] DNS.1 = vault3.test.local IP.1 = 192.168.1.173 IP.2 = 127.0.0.1 " > ./cert-c.cfg |
Теперь для каждого из узлов сформируем CSR файл:
1 2 3 |
openssl req -newkey rsa:2048 -nodes -keyout vault-a-key.pem -out vault-a-csr.pem -subj "/CN=vault1.test.local" openssl req -newkey rsa:2048 -nodes -keyout vault-b-key.pem -out vault-b-csr.pem -subj "/CN=vault2.test.local" openssl req -newkey rsa:2048 -nodes -keyout vault-c-key.pem -out vault-c-csr.pem -subj "/CN=vault3.test.local" |
И выпустим сертификаты на основании запросов:
1 2 3 |
openssl x509 -req -set_serial 01 -days 3650 -in vault-a-csr.pem -out vault-a-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-a.cfg openssl x509 -req -set_serial 01 -days 3650 -in vault-b-csr.pem -out vault-b-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-b.cfg openssl x509 -req -set_serial 01 -days 3650 -in vault-c-csr.pem -out vault-c-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-c.cfg |
вывод следующий:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[root@vault1 tls]# openssl x509 -req -set_serial 01 -days 3650 -in vault-a-csr.pem -out vault-a-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-a.cfg Signature ok subject=/CN=vault1.test.local Getting CA Private Key [root@vault1 tls]# openssl x509 -req -set_serial 01 -days 3650 -in vault-b-csr.pem -out vault-b-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-b.cfg Signature ok subject=/CN=vault2.test.local Getting CA Private Key [root@vault1 tls]# openssl x509 -req -set_serial 01 -days 3650 -in vault-c-csr.pem -out vault-c-cert.pem -CA vault-ca-cert.pem -CAkey vault-ca-key.pem -extensions v3_ca -extfile ./cert-c.cfg Signature ok subject=/CN=vault3.test.local Getting CA Private Key |
Скопируем сертификаты и ключи на узлы B и C:
1 2 |
scp ./vault-b-key.pem ./vault-b-cert.pem ./vault-ca-cert.pem vault2.test.local:/opt/vault/tls scp ./vault-c-key.pem ./vault-c-cert.pem ./vault-ca-cert.pem vault3.test.local:/opt/vault/tls |
На каждом из узлов установим соответствующие права для доступа к файлам сертификатов и ключам:
1 2 3 4 5 |
chown root:root /opt/vault/tls/vault-*-cert.pem /opt/vault/tls/vault-ca-cert.pem chown root:vault /opt/vault/tls/vault-*-key.pem chmod 0644 /opt/vault/tls/vault-*-cert.pem /opt/vault/tls/vault-ca-cert.pem chmod 0640 /opt/vault/tls/vault-*-key.pem |
Теперь, когда для каждой из нод готовы сертификаты.
Перейдем к конфигурации Vault.
Отредактируем конфигурационный файл vault для первой ноды:
1 2 |
cp /etc/vault.d/vault.hcl /etc/vault.d/vault.hcl.backup vim /etc/vault.d/vault.hcl |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
cluster_addr = "https://192.168.1.171:8201" api_addr = "https://192.168.1.171:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-a-cert.pem" tls_key_file = "/opt/vault/tls/vault-a-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault1.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } } |
вторая нода:
cp /etc/vault.d/vault.hcl /etc/vault.d/vault.hcl.backup
[root@vault2 ~]# cat /etc/vault.d/vault.hcl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
cluster_addr = "https://192.168.1.172:8201" api_addr = "https://192.168.1.172:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-b-cert.pem" tls_key_file = "/opt/vault/tls/vault-b-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault2.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } } |
третья нода:
cp /etc/vault.d/vault.hcl /etc/vault.d/vault.hcl.backup
[root@vault3 ~]# cat /etc/vault.d/vault.hcl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
cluster_addr = "https://192.168.1.173:8201" api_addr = "https://192.168.1.173:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-c-cert.pem" tls_key_file = "/opt/vault/tls/vault-c-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault3.test.local" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } } |
Обратите внимание как меняются поля с адресами, сертификатами, а также node_id.
api_addr – адрес и порт, на котором будет доступен API сервер;
cluster_addr – адрес и порт по которому будут взаимодействовать кластерные сервисы;
disable_mlock – рекомендуемый параметр при использовании Integrated Storage;
ui – включение доступа к веб-интерфейсу Vault;
в секции listener указываются сертификаты, которые будут использованы при сетевом взаимодействии. У каждой ноды они свои, за исключением CA, данный сертификат одинаковый для всех.
Секцию storage стоит так же рассмотреть:
path = “/opt/vault/data” – директория, где будут храниться данные Vault;
node_id = “vault-a.vmik.lab” – id, с которым нода будет участвовать в кластере. У каждой ноды он должен отличаться;
Далее идут несколько секций retry_join, с перечислением всех узлов кластера. Поскольку доподлинно неизвестно, какой из узлов будет активным при запуске служб Vault, будет произведена попытка подключения к каждому из узлов.
Здесь же указываются адреса узлов – leader_api_addr, leader_tls_servername – хостнейм сервера, который должен совпадать с тем, что прописано в сертификате данного сервера. Так же указываются сертификаты, которыми клиент будет подключаться к лидеру (у каждой из нод свои сертификаты, которые мы создавали ранее).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[root@vault1 tls]# systemctl enable --now vault.service [root@vault1 tls]# systemctl status vault.service ● vault.service - "HashiCorp Vault - A tool for managing secrets" Loaded: loaded (/usr/lib/systemd/system/vault.service; enabled; vendor preset: disabled) Active: active (running) since Sun 2022-09-04 12:20:56 +06; 16s ago Docs: https://www.vaultproject.io/docs/ Main PID: 1641 (vault) CGroup: /system.slice/vault.service └─1641 /usr/bin/vault server -config=/etc/vault.d/vault.hcl Sep 04 12:21:12 vault1 vault[1641]: 2022-09-04T12:21:12.987+0600 [ERROR] core: failed to get raft challenge: leader_addr=https://192.168.1.172:8200 error="erro…on refused" Sep 04 12:21:12 vault1 vault[1641]: 2022-09-04T12:21:12.988+0600 [ERROR] core: failed to get raft challenge: leader_addr=https://192.168.1.171:8200 Sep 04 12:21:12 vault1 vault[1641]: error= Sep 04 12:21:12 vault1 vault[1641]: | error during raft bootstrap init call: Error making API request. Sep 04 12:21:12 vault1 vault[1641]: | Sep 04 12:21:12 vault1 vault[1641]: | URL: PUT https://192.168.1.171:8200/v1/sys/storage/raft/bootstrap/challenge Sep 04 12:21:12 vault1 vault[1641]: | Code: 503. Errors: Sep 04 12:21:12 vault1 vault[1641]: | Sep 04 12:21:12 vault1 vault[1641]: | * Vault is sealed Sep 04 12:21:12 vault1 vault[1641]: 2022-09-04T12:21:12.988+0600 [ERROR] core: failed to retry join raft cluster: retry=2s err="failed to get raft challenge" Hint: Some lines were ellipsized, use -l to show in full. |
Проверим статус Vault, предварительно отменив проверку сертификатов, выставив значение специальной переменной:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@vault1 tls]# export VAULT_SKIP_VERIFY="true" [root@vault1 tls]# vault status Key Value --- ----- Seal Type shamir Initialized false Sealed true Total Shares 0 Threshold 0 Unseal Progress 0/0 Unseal Nonce n/a Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true |
Как можно заметить на текущий момент Vault не инициализирован (Initialized false), а также запечатан (Sealed true).
Примечание: добавив наш CA файл, сгенерированный ранее, в список доверенных сертификатов, прибегать к отмене проверки сертификатов будет необязательно.
Инициализируем Vault:
vault operator init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@vault1 tls]# vault operator init Unseal Key 1: vQhtGnKfP6z8ZhH/3LbxaffPzb5FEmEsV/U1MDyHo7mr Unseal Key 2: QebWSJkXEzmFwMA8aYhOYgDohRjIRJW/dVJP9UyYL25z Unseal Key 3: GpnuWi5gTWtAxIkxFXSU69hLUlgSEBTfj6heEn5ZB1vj Unseal Key 4: 9D4UWacP8ysqPDVCgLIBGbl7yp9m35dawvwbH5KtiWAd Unseal Key 5: 5fUt/8zz8/CfongdTRuNASiAxYa+cbsSDNdKajw2/aGc Initial Root Token: hvs.yInppPkxqfAcVcODMVlvLjN3 Vault initialized with 5 key shares and a key threshold of 3. Please securely distribute the key shares printed above. When the Vault is re-sealed, restarted, or stopped, you must supply at least 3 of these keys to unseal it before it can start servicing requests. Vault does not store the generated root key. Without at least 3 keys to reconstruct the root key, Vault will remain permanently sealed! It is possible to generate new unseal keys, provided you have a quorum of existing unseal keys shares. See "vault operator rekey" for more information. |
Vault выдаст 5 ключей, которые необходимо использовать при «распечатке». Как сказано выше, после каждой остановки либо перезапуска, Vault будет вновь находиться в запечатанном состоянии и для открытия нужно будет использовать любые три из пяти предоставленных ключей. Терять эти ключи не стоит!
Так же нам предоставляется Root Token для доступа к Vault с максимальными правами.
Распечатаем Vault. Операцию vault operator unseal
нужно будет провести 3 раза. Каждый раз с разным ключом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
[root@vault1 tls]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 1/3 Unseal Nonce 04777048-4ba3-1574-a858-f8728011541c Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true [root@vault1 tls]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 2/3 Unseal Nonce 04777048-4ba3-1574-a858-f8728011541c Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true [root@vault1 tls]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster n/a HA Mode standby Active Node Address <none> Raft Committed Index 31 Raft Applied Index 31 |
На данном этапе мы настроили одну ноду Vault, а также инициализировали и распечатали ее. Теперь подключим в кластер две других ноды.
Ранее мы уже подготовили и разместили конфигурационные файлы на нодах B и C.
Подключаемся к ноде B, запускаем Vault:
systemctl enable --now vault
systemctl status vault
Теперь, в отличии от первой ноды, инициализировать Vault больше не нужно, но новая нода все еще в запечатанном состоянии. Распечатаем ноду B:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
[root@vault2 ~]# export VAULT_SKIP_VERIFY="true" [root@vault2 ~]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 1/3 Unseal Nonce f53d3c54-f4e4-902f-ba11-a4c826c2feeb Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true [root@vault2 ~]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 2/3 Unseal Nonce f53d3c54-f4e4-902f-ba11-a4c826c2feeb Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true [root@vault2 ~]# vault operator unseal Unseal Key (will be hidden): Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 0/3 Unseal Nonce n/a Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true [root@vault2 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster https://192.168.1.171:8201 HA Mode standby Active Node Address https://192.168.1.171:8200 Raft Committed Index 37 Raft Applied Index 37 |
После распечатки нода будет подключена к кластеру. Можно обратить внимание, что новая нода находится в режиме standby, так же можно определить текущий адрес активной ноды. Это нода A.
Подключимся к ноде C и выполним аналогичные действия:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[root@vault3 ~]# systemctl enable --now vault [root@vault3 ~]# export VAULT_SKIP_VERIFY="true" [root@vault3 ~]# vault operator unseal [root@vault3 ~]# vault operator unseal [root@vault3 ~]# vault operator unseal [root@vault3 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster https://192.168.1.171:8201 HA Mode standby Active Node Address https://192.168.1.171:8200 Raft Committed Index 42 Raft Applied Index 42 |
Теперь мы успешно сформировали кластер из трех нод. Вернемся на первую и проверим состояние кластера.
Для начала авторизуемся с токеном, который был получен ранее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@vault1 tls]# vault login Token (will be hidden): Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token hvs.yInppPkxqfAcVcODMVlvLjN3 token_accessor hmOVGjYCG7nTELrcoyxEeoxL token_duration ∞ token_renewable false token_policies ["root"] identity_policies [] policies ["root"] |
И проверим статус хранилища:
1 2 3 4 5 6 7 |
[root@vault1 tls]# vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- vault1.test.local 192.168.1.171:8201 leader true vault2.test.local 192.168.1.172:8201 follower true vault3.test.local 192.168.1.173:8201 follower true |
По статусу видно 3 сервера, один из которых в статусе лидера, а два других – ведомые.
Попробуем добавить новый секрет в хранилище. Для этого используем механизм kv (key-value):
1 2 |
[root@vault1 tls]# vault secrets enable -path=test-secrets/ kv Success! Enabled the kv secrets engine at: test-secrets/ |
И поместим секрет под именем db:
1 2 |
[root@vault1 tls]# vault kv put test-secrets/db password=123456789 Success! Data written to: test-secrets/db |
Посмотрим список всех секретов в нашем KV хранилище test-secrets:
1 2 3 4 5 |
[root@vault1 tls]# vault kv list test-secrets Keys ---- db |
А также значение секрета db:
1 2 3 4 5 6 |
[root@vault1 tls]# vault kv get test-secrets/db ====== Data ====== Key Value --- ----- password 123456789 |
Проверим работоспособность механизма HA. Отключим ноду A:
[root@vault1 tls]# poweroff
Подключимся к ноде B и проверим статус:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@vault2 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster https://192.168.1.172:8201 HA Mode active Active Since 2022-09-04T06:37:35.678401666Z Raft Committed Index 50 Raft Applied Index 50 |
HA Mode изменено со standby на active. Запросим список узлов raft:
но сначала передадим в переменные окружения наш токен рутовый, мы его получили когда инициировали кластер:
1 2 3 4 5 6 7 8 9 |
[root@vault2 ~]# export VAULT_TOKEN="hvs.yInppPkxqfAcVcODMVlvLjN3" [root@vault2 ~]# vault operator raft list-peers Node Address State Voter ---- ------- ----- ----- vault1.test.local 192.168.1.171:8201 follower true vault2.test.local 192.168.1.172:8201 leader true vault3.test.local 192.168.1.173:8201 follower true |
Лидер так же был перенесен на ноду B. В завершении запросим ранее созданный секрет:
1 2 3 4 5 6 |
[root@vault2 ~]# vault kv get test-secrets/db ====== Data ====== Key Value --- ----- password 123456789 |
Vault продолжает функционировать при потере одной активной ноды. Отключим ноду B. Теперь из трех узлов доступен только один. Проверим работоспособность:
[root@vault2 ~]# poweroff
Статус Vault с ноды C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@vault3 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster https://192.168.1.172:8201 HA Mode standby Active Node Address https://192.168.1.172:8200 Raft Committed Index 53 Raft Applied Index 52 |
Активная нода не изменилась. Нода C все так же в режиме standby. Аналогично, не удастся запросить и секрет, поскольку запрос перенаправляется на ранее активную ноду (B):
1 2 3 |
[root@vault3 ~]# vault kv get test-secrets/db Get "https://192.168.1.172:8200/v1/sys/internal/ui/mounts/test-secrets/db": dial tcp 192.168.1.172:8200: connect: no route to host |
Запустим обратно ноду A. После запуска, как и ожидается, нода A находится в статусе sealed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@vault1 ~]# export VAULT_SKIP_VERIFY="true" [root@vault1 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 0/3 Unseal Nonce n/a Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft HA Enabled true |
Распечатаем:
1 2 3 4 |
[root@vault1 ~]# vault operator unseal [root@vault1 ~]# vault operator unseal [root@vault1 ~]# vault operator unseal |
проверим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@vault1 ~]# vault status Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.11.3 Build Date 2022-08-26T10:27:10Z Storage Type raft Cluster Name vault-cluster-bf7b95a3 Cluster ID 6aa2ee81-da82-bbc8-1f02-789051c5ec44 HA Enabled true HA Cluster https://192.168.1.173:8201 HA Mode standby Active Node Address https://192.168.1.173:8200 Raft Committed Index 58 Raft Applied Index 58 |
Работа кластера восстановлена. Нода С теперь активная. Доступ к секретам так же восстановлен:
1 2 3 4 5 6 7 |
[root@vault3 ~]# export VAULT_TOKEN="hvs.yInppPkxqfAcVcODMVlvLjN3" [root@vault3 ~]# vault kv get test-secrets/db ====== Data ====== Key Value --- ----- password 123456789 |
Запустим обратно выключенную ноду B и распечатаем. На этом работа по настройке кластера Vault закончена.
Нерассмотренным остался один вопрос – как подключаться внешним клиентам? Подключение к любой из standby нод перенаправит запрос на active ноду, поэтому знать текущий адрес активной ноды не обязательно.
Однако, клиент может не знать все адреса Vault и в случае отключения известной ноды, доступ клиента к Vault может прекратиться.
Наиболее приемлемые на мой взгляд решения:
1. Внешний балансировщик нагрузки между узлами Vault с заранее известным и постоянным адресом. HAProxy, либо nginx;
2. Общий IP-адрес между узлами кластера Vault, например, на основе keepalived.
Добавим сертификаты в доверенные, чтобы каждый раз не закидывать в переменные окружения
export VAULT_SKIP_VERIFY="true"
для этого на каждой из нод выполним:
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@vault1 ~]# update-ca-trust enable [root@vault1 ~]# cp /opt/vault/tls/vault-ca-cert.pem /etc/pki/ca-trust/source/anchors/ [root@vault1 ~]# update-ca-trust extract [root@vault2 ~]# update-ca-trust enable [root@vault2 ~]# cp /opt/vault/tls/vault-ca-cert.pem /etc/pki/ca-trust/source/anchors/ [root@vault2 ~]# update-ca-trust extract [root@vault3 ~]# update-ca-trust enable [root@vault3 ~]# cp /opt/vault/tls/vault-ca-cert.pem /etc/pki/ca-trust/source/anchors/ [root@vault3 ~]# update-ca-trust extract |
Подключимся к кластеру:
https://vault.test.local:8200/
аутентифицируемся с помощью root token
Initial Root Token: hvs.yInppPkxqfAcVcODMVlvLjN3
видим созданный нами ранее секрет
теперь создадим метод аутентификации по user/password
создадим теперь пользователя
зайдём под этим пользователем и проверим что нам доступно:
как видим недостаточно доступов.
теперь создадим admin policy пример возьмём отсюда:
https://learn.hashicorp.com/tutorials/vault/policies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# Read system health check path "sys/health" { capabilities = ["read", "sudo"] } # Create and manage ACL policies broadly across Vault # List existing policies path "sys/policies/acl" { capabilities = ["list"] } # Create and manage ACL policies path "sys/policies/acl/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # Enable and manage authentication methods broadly across Vault # Manage auth methods broadly across Vault path "auth/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # Create, update, and delete auth methods path "sys/auth/*" { capabilities = ["create", "update", "delete", "sudo"] } # List auth methods path "sys/auth" { capabilities = ["read"] } # Enable and manage the key/value secrets engine at `secret/` path # List, create, update, and delete key/value secrets path "secret/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # Manage secrets engines path "sys/mounts/*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] } # List existing secrets engines. path "sys/mounts" { capabilities = ["read"] } |
policy создали, теперь нужно создать Entities (сущности)
логинимся под нашим пользователем и проверям какие теперь есть доступы:
как видим теперь мы видим все наши секреты возможность создавать новые policy тоже появилась, попробуем создать новый секрет:
как видим секрет создался.
====================================================
Создание группы с админскими правами
создаём сущность Entities и alias для неё. alias должен совпадать с именем нашего пользователя
"entity" - это абстрактное представление человека или сервиса, который взаимодействует с сервером Vault. Entity может быть связан с одним или несколькими методами аутентификации, такими как userpass
, ldap
или github
, и к нему можно прикрепить политики для управления доступом к ресурсам в Vault.
При использовании метода аутентификации userpass
в Vault для каждого пользователя, зарегистрированного с этим методом, создается entity. Entity хранит информацию о пользователе, такую как имя пользователя, пароль и любые политики, связанные с ним. При входе пользователя с помощью userpass
Vault использует информацию, хранящуюся в entity, чтобы аутентифицировать пользователя и определить, к каким ресурсам у него есть доступ.
отметим что в данном случае мы НЕ ПРИВЯЗЫВАЕМ policy
теперь создаём alias
теперь создаём группу(в текущем примере internal):
и уже тут цепляем нашу policy
проверяем нашу группу:
id в members группы:
это id Entities
заходим под нашим пользователем mid и проверяем что всё ок и доступов на добавление секретов хватает:
создадим ещё пользователя и добавим в ту же группу:
теперь добавим его в нашу группу, для этого создаём Entities
добавляем теперь в группу:
отмечу что все эти действия я выполнял уже под пользователем MID а не root
всё, можно теперь логиниться под этим пользователем(mid2).
Повторим те же действия, но с помощью команд:
- Создадим userpass - доступ по логину и паролю:
1vault auth enable userpass
12[root@vault1 ~]# vault auth enable userpassSuccess! Enabled userpass auth method at: userpass/ - Создаём policy с доступа admin
123echo 'path "*" {capabilities = ["create", "read", "update", "delete", "list", "sudo"]}' | vault policy write admin -
123456[root@vault1 ~]# echo 'path "*" {> capabilities = ["create", "read", "update", "delete", "list", "sudo"]> }' | vault policy write admin -Success! Uploaded policy: admin - Создаём пользователя mid с авторизацией по логину и паролю:
1vault write auth/userpass/users/mid password=123456789
123[root@vault1 ~]# vault write auth/userpass/users/mid password=123456789Success! Data written to: auth/userpass/users/mid - создаём entity для пользователя mid
1vault write identity/entity name="mid"
результат:
1234567[root@vault1 ~]# vault write identity/entity name="mid"Key Value--- -----aliases <nil>id 98cb3fe3-6aac-8c02-7ea1-a576baf676f6name mid
если забыли id его можно посмотреть вот так:
1vault read identity/entity/name/mid
результат:
1234567891011121314151617[root@vault1 ~]# vault read identity/entity/name/midKey Value--- -----aliases []creation_time 2023-01-09T14:34:20.638988226Zdirect_group_ids []disabled falsegroup_ids []id 98cb3fe3-6aac-8c02-7ea1-a576baf676f6inherited_group_ids []last_update_time 2023-01-09T14:34:20.638988226Zmerged_entity_ids <nil>metadata <nil>name midnamespace_id rootpolicies []
ещё нам нужна будет переменная mount_accessor
чтобы увидеть mount_accessor выполним команду:
123456789vault auth list[root@vault1 ~]# vault auth listPath Type Accessor Description Version---- ---- -------- ----------- -------token/ token auth_token_4ad2bc9d token based credentials n/auserpass/ userpass auth_userpass_8fabd83b n/a n/a
всё, мы получили значение для mount_accessor и canonical_id теперь можно выполнять команду на создание alias для entity:
1vault write identity/entity-alias name="mid" canonical_id=<canonical_id> mount_accessor=<Accessor>
результат:
123456[root@vault1 ~]# vault write identity/entity-alias name="mid" canonical_id=98cb3fe3-6aac-8c02-7ea1-a576baf676f6 mount_accessor=auth_userpass_8fabd83bKey Value--- -----canonical_id 98cb3fe3-6aac-8c02-7ea1-a576baf676f6id 9f1bfdf3-a461-2933-2a36-b136815bd32a
если забыли id то его можно посмотреть вот так(тут будут все ключи):
12345[root@vault1 ~]# vault list identity/entity-alias/id/Keys----9f1bfdf3-a461-2933-2a36-b136815bd32a
получить информацию по нему можно так:
1234567891011121314151617[root@vault1 ~]# vault read identity/entity-alias/id/9f1bfdf3-a461-2933-2a36-b136815bd32aKey Value--- -----canonical_id 98cb3fe3-6aac-8c02-7ea1-a576baf676f6creation_time 2023-01-09T14:36:04.149468544Zcustom_metadata map[]id 9f1bfdf3-a461-2933-2a36-b136815bd32alast_update_time 2023-01-09T14:36:04.149468544Zlocal falsemerged_from_canonical_ids <nil>metadata <nil>mount_accessor auth_userpass_8fabd83bmount_path auth/userpass/mount_type userpassname midnamespace_id root
- Создадим теперь group к которой будет присоединена policy и Member Entity IDs, команда выглядит вот так:
1vault write identity/group name="admins" type="internal" policies="admin" member_entity_ids=ID
вот результат:
123456[root@vault1 ~]# vault write identity/group name="admins" type="internal" policies="admin" member_entity_ids=98cb3fe3-6aac-8c02-7ea1-a576baf676f6Key Value--- -----id 35349401-d0fa-3451-d8a6-2d6a2cadb23ename admins
- Добавим нового пользователя в эту же группу admins:
12345678910111213141516171819202122[root@vault1 ~]# vault write auth/userpass/users/user2 password=123456789Success! Data written to: auth/userpass/users/user2[root@vault1 ~]# vault write identity/entity name=user2Key Value--- -----aliases <nil>id d265af9e-6fc4-69f1-a2df-4da7fff75176name user2[root@vault1 ~]# vault write identity/entity-alias name=user2 canonical_id=d265af9e-6fc4-69f1-a2df-4da7fff75176 mount_accessor=auth_userpass_8fabd83bKey Value--- -----canonical_id d265af9e-6fc4-69f1-a2df-4da7fff75176id 104d98d2-98b6-a8e1-e81d-5642fc76f570[root@vault1 ~]# vault write identity/group name="admins" member_entity_ids=d265af9e-6fc4-69f1-a2df-4da7fff75176,98cb3fe3-6aac-8c02-7ea1-a576baf676f6Success! Data written to: identity/group
обратите внимание что при добавлении к группе id entity нового пользователя нужно перечислять их все.чтобы глянуть какие уже id есть в группе admins можем использовать команду:
vault read identity/group/name/admins | grep member_entity_ids
Аутентификация через gitlab
офф инструкция:
https://docs.gitlab.com/ee/integration/vault.html
идём в наш гитлаб:
добавляем:
http://localhost:8250/oidc/callback
http://127.0.0.1:8200/ui/vault/auth/oidc/oidc/callback
Application ID a4b9c2ce0b1a62d4e448450bdb4e458a123a6806549bb660e8179bf70ac13259
Secret 69fa2b783fc889b77cdfdfe20de999585d68183bcdd8341692b421e0dbacc598
переходим в консоль, логинимся в vault
[root@vault3 ~]# vault login
Token (will be hidden):
токен копируем отсюда:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token hvs.CAESIDXIPcxDW1Q8tZ66INGkA_x1SRW5JNjO481O3_4pzmVwGh4KHGh2cy43SHlMcmFVZGZ2Ynl5YmZKODhRWjJWU0I token_accessor GgACUsd9gNHtHoXzLBdugnjm token_duration 596h31m18s token_renewable true token_policies ["default"] identity_policies ["admin-acces"] policies ["default" "admin-acces"] token_meta_username mid2 |
vault auth enable oidc
1 2 |
[root@vault3 ~]# vault auth enable oidc Success! Enabled oidc auth method at: oidc/ |
теперь нужно передать application ID и secret сгенерированный в GitLab
пример:
1 2 3 4 5 6 |
vault write auth/oidc/config \ oidc_discovery_url="https://gitlab.com" \ oidc_client_id="your_application_id" \ oidc_client_secret="your_secret" \ default_role="demo" \ bound_issuer="localhost" |
а вот то что я вставляю:
1 2 3 4 5 6 |
vault write auth/oidc/config \ oidc_discovery_url="http://gitlab.local" \ oidc_client_id="a4b9c2ce0b1a62d4e448450bdb4e458a123a6806549bb660e8179bf70ac13259" \ oidc_client_secret="69fa2b783fc889b77cdfdfe20de999585d68183bcdd8341692b421e0dbacc598" \ default_role="demo" \ bound_issuer="localhost" |
1 2 3 4 5 6 7 8 |
[root@vault3 ~]# vault write auth/oidc/config \ > oidc_discovery_url="http://gitlab.local" \ > oidc_client_id="a4b9c2ce0b1a62d4e448450bdb4e458a123a6806549bb660e8179bf70ac13259" \ > oidc_client_secret="69fa2b783fc889b77cdfdfe20de999585d68183bcdd8341692b421e0dbacc598" \ > default_role="demo" \ > bound_issuer="localhost" Success! Data written to: auth/oidc/config |
теперь надо записать роль
1 2 3 4 5 6 7 8 9 10 11 12 |
vault write auth/oidc/role/demo -<<EOF { "user_claim": "sub", "allowed_redirect_uris": "your_vault_instance_redirect_uris", "bound_audiences": "your_application_id", "oidc_scopes": "openid", "role_type": "oidc", "policies": "demo", "ttl": "1h", "bound_claims": { "groups": ["yourGroup/yourSubgrup"] } } EOF |
а вот что я буду вставлять:
1 2 3 4 5 6 7 8 9 10 11 12 |
vault write auth/oidc/role/demo -<<EOF { "user_claim": "sub", "allowed_redirect_uris": "http://vault.test.local:8200/ui/vault/auth/oidc/oidc/callback", "bound_audiences": "a4b9c2ce0b1a62d4e448450bdb4e458a123a6806549bb660e8179bf70ac13259", "oidc_scopes": "openid", "role_type": "oidc", "policies": "demo", "ttl": "1h", "bound_claims": { "groups": ["admin-acces"] } } EOF |
результат такой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[root@vault3 ~]# vault write auth/oidc/role/demo -<<EOF > { > "user_claim": "sub", > "allowed_redirect_uris": "http://vault.test.local:8250/oidc/callback", > "bound_audiences": "a4b9c2ce0b1a62d4e448450bdb4e458a123a6806549bb660e8179bf70ac13259", > "oidc_scopes": "openid", > "role_type": "oidc", > "policies": "demo", > "ttl": "1h", > "bound_claims": { "groups": ["admin-acces"] } > } > EOF Success! Data written to: auth/oidc/role/demo |
всё можно заходить через гитлаб
6. Auto unseal hashicorp vault
идея в следующем, у нас есть 2 кластера hashicorp vault которые будут делать auto unseal друг друга. один кластер у нас на отдельных тачках а второй будет внутри k8s. Для простоты примера поставим minikub
6.1 установка minikub
отключаем selinux
[root@minikub ~]# cat /etc/selinux/config | grep -v ^#
SELINUX=disabled
ребутаем сервер.
ставим докер:
vi /etc/yum.repos.d/docker.repo
1 2 3 |
[docker] baseurl=https://download.docker.com/linux/centos/7/x86_64/stable/ gpgcheck=0 |
yum -y install docker-ce
systemctl start docker
systemctl enable docker
ставим Conntrack - он является частью платформы Netlifier. требуется для работы сложной сети Kubernetes, поскольку узлы должны отслеживать соединения между тысячами модулей и сервисов.
yum -y install conntrack
1 |
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl |
chmod +x kubectl
mv kubectl /usr/local/bin/
1 |
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 |
chmod +x minikube-linux-amd64
mv minikube-linux-amd64 /usr/local/bin/minikube
minikube start
если при запуске мы получаем следующую ошибку:
1 2 3 4 5 6 7 8 9 |
[root@minikub ~]# minikube start * minikube v1.28.0 on Centos 7.9.2009 * Automatically selected the docker driver. Other choices: none, ssh * The "docker" driver should not be used with root privileges. If you wish to continue as root, use --force. * If you are running minikube within a VM, consider using --driver=none: * https://minikube.sigs.k8s.io/docs/reference/drivers/none/ X Exiting due to DRV_AS_ROOT: The "docker" driver should not be used with root privileges. |
можно запустить с флагом --force - если работаете из под root
minikube start --force
если хотите работать из под пользователя то нужно добавить прав:
[root@minikub ~]# adduser mid
[root@minikub ~]# usermod -aG docker mid
[root@minikub ~]# su - mid
[mid@minikub ~]$ minikube start
включаем ingress для minikube
[root@minikub ~]# minikube addons enable ingress
и включим автодополнение для kubectl
[root@minikub ~]# yum install bash-completion -y
[root@minikub ~]# echo 'source <(kubectl completion bash)' >>~/.bashrc
6.2 подготовка хранилища для PV (nfs)
в качестве хранилища pv будем использовать nfs сервер.
192.168.1.176
настраиваем его:
yum install nfs-utils -y
systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap
systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap
1 2 |
[root@nfs ~]# cat /etc/exports /nfs 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check) |
создадим раздел (можно и не создавать а выбрать удобную для вас директорию, я захотел сделать отдельный раздел)
lvcreate -L 5G -n nfs centos
mkfs.ext4 -L nfs /dev/centos/nfs
mkdir /nfs
mount /dev/centos/nfs /nfs/
vi /etc/fstab
1 2 |
/dev/mapper/centos-nfs /nfs ext4 defaults 1 2 |
добавляем права на запись:
[root@nfs ~]# chmod 777 /nfs
на клиент (minikub) тоже ставим nfs-client
yum install nfs-utils -y
systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap
systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap
теперь настраиваем провижинер (provisioner)
cat rbac.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-client-provisioner-runner rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-client-provisioner subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default subjects: - kind: ServiceAccount name: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: Role name: leader-locking-nfs-client-provisioner apiGroup: rbac.authorization.k8s.io |
cat nfs_class.yaml
1 2 3 4 5 6 7 8 |
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-client provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME' parameters: archiveOnDelete: "false" |
cat nfs_provision.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner labels: app: nfs-client-provisioner # replace with namespace where provisioner is deployed namespace: default spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: k8s-sigs.io/nfs-subdir-external-provisioner - name: NFS_SERVER value: 192.168.1.176 - name: NFS_PATH value: /nfs volumes: - name: nfs-client-root nfs: server: 192.168.1.176 path: /nfs |
применяем:
kubectl apply -f rbac.yaml
kubectl apply -f nfs_class.yaml
kubectl apply -f nfs_provision.yaml
теперь можем проверить создаётся ли PV
cat test-claim.yaml
1 2 3 4 5 6 7 8 9 10 11 12 |
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: test-claim spec: storageClassName: nfs-client accessModes: - ReadWriteMany resources: requests: storage: 50Mi |
kubectl apply -f test-claim.yaml
проверяем:
1 2 3 4 5 6 7 8 |
[root@minikub ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-1307b530-13ae-4dea-b5c2-076b0de5eed0 50Mi RWX nfs-client 116s [root@minikub ~]# [root@minikub ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-1307b530-13ae-4dea-b5c2-076b0de5eed0 50Mi RWX Delete Bound default/test-claim nfs-client 119s |
как видим всё ок.
проверяем на nfs шаре:
1 2 3 4 5 6 7 8 |
[root@nfs ~]# ls -lah /nfs/ total 28K drwxrwxrwx 4 root root 4.0K Jan 11 17:09 . dr-xr-xr-x. 19 root root 4.0K Jan 11 13:02 .. drwxrwxrwx 2 root root 4.0K Jan 11 17:09 default-test-claim-pvc-1307b530-13ae-4dea-b5c2-076b0de5eed0 drwx------ 2 root root 16K Jan 11 13:02 lost+found [root@nfs ~]# |
всё ок.
6.3 установка vault в minikub
ставить будем в namespace vault
[root@minikub ~]# kubectl create ns vault
будем использовать уже готовые helm чарты для vault
для начала установим helm:
https://github.com/helm/helm/releases/tag/v3.10.3
[root@minikub ~]# wget https://get.helm.sh/helm-v3.10.3-linux-amd64.tar.gz
[root@minikub ~]# tar -zxvf helm-v3.10.3-linux-amd64.tar.gz
[root@minikub ~]# mv linux-amd64/helm /usr/local/bin/helm
Возьмём офф чарт:
https://github.com/hashicorp/vault-helm
[root@minikub ~]# git clone https://github.com/hashicorp/vault-helm.git
поправим values включим raft (можно оставить по умолчанию consul можно вообще в базу данных postgres всё отправить)
[root@minikub ~]# vim vault-helm/values-my.yaml
чтобы запустить на minikub нужно выключить affinity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
injector: enabled: false server: image: repository: "hashicorp/vault" tag: "1.8.4" pullPolicy: IfNotPresent logLevel: "info" logFormat: "json" resources: requests: memory: 256Mi cpu: 250m limits: memory: 256Mi cpu: 250m affinity: {} dataStorage: enabled: true size: 1Gi mountPath: "/vault/data" storageClass: nfs-client accessMode: ReadWriteOnce dev: enabled: false standalone: enabled: false ha: enabled: true replicas: 3 # Set the api_addr configuration for Vault HA # See https://www.vaultproject.io/docs/configuration#api_addr # If set to null, this will be set to the Pod IP Address apiAddr: null raft: # Enables Raft integrated storage enabled: true config: | ui = true listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "raft" { path = "/vault/data" } service_registration "kubernetes" {} csi: enabled: true resources: requests: cpu: 50m memory: 128Mi limits: cpu: 50m memory: 128Mi |
а вот такой вид будет при обычном запуске в кластере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
injector: enabled: false server: image: repository: "hashicorp/vault" tag: "1.8.4" pullPolicy: IfNotPresent logLevel: "info" logFormat: "json" resources: requests: memory: 256Mi cpu: 250m limits: memory: 256Mi cpu: 250m dataStorage: enabled: true size: 1Gi mountPath: "/vault/data" storageClass: nfs-client accessMode: ReadWriteOnce dev: enabled: false standalone: enabled: false ha: enabled: true replicas: 3 # Set the api_addr configuration for Vault HA # See https://www.vaultproject.io/docs/configuration#api_addr # If set to null, this will be set to the Pod IP Address apiAddr: null raft: # Enables Raft integrated storage enabled: true config: | ui = true listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "raft" { path = "/vault/data" } service_registration "kubernetes" {} csi: enabled: true resources: requests: cpu: 50m memory: 128Mi limits: cpu: 50m memory: 128Mi |
как видим разница только в
affinity: {}
ставим сам vault:
[root@minikub ~]# helm upgrade --install vault -n vault ./vault-helm/ -f ./vault-helm/values-my.yaml
теперь нам нужно поставить Secrets store CSI driver
он потребуется для подключения сикретов из vault в init контейнеры.
1 2 3 |
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver --namespace kube-system |
теперь нужно инициализировать vault
1 2 |
[root@minikub ~]# kubectl -n vault exec vault-0 -- vault operator init -key-shares=5 -key-threshold=3 -format=json > cluster-keys.json |
Получили ключ для unseal.
далее нам понадобится утилита jq поставим её:
yum install epel-release -y
yum install jq -y
cat cluster-keys.json | jq -r ".unseal_keys_b64[]"
1 2 3 4 5 6 7 |
[root@minikub ~]# cat cluster-keys.json | jq -r ".unseal_keys_b64[]" sUwK9cVo+gZy2IDVryaHS0DHkGkXA9e1T0AQrzIRycHY +CUfPu82zO/TD2osXmqiLIKliEKlbDV6SyT3ugshCK3k XTRjmbHP/33VXy84eEXu/ioD5raBzIIImrzXz0XsBrnz vv6sOVax0jBNQ0RRLJTbeYUVQOfDv5R/uJ/4B5+0oM2j QrlfMN08AzRll1nsf27M6tDtT0iSHR0ZANAXMhio0968 |
далее распечатываем наше хранилище командой:
kubectl -n vault exec vault-0 -- vault operator unseal
ключи выше.
Если лень вводить руками то можно использовать цикл:
1 |
for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-0 -- vault operator unseal $i; done |
результат будет такой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
[root@minikub ~]# for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-0 -- vault operator unseal $i; done Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 1/3 Unseal Nonce ec4e5f2b-dfac-762c-e33f-3c9e7817c1a5 Version 1.8.4 Storage Type raft HA Enabled true Key Value --- ----- Seal Type shamir Initialized true Sealed true Total Shares 5 Threshold 3 Unseal Progress 2/3 Unseal Nonce ec4e5f2b-dfac-762c-e33f-3c9e7817c1a5 Version 1.8.4 Storage Type raft HA Enabled true Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.8.4 Storage Type raft Cluster Name vault-cluster-d58a9688 Cluster ID 637ba7c8-8d57-5bb5-2840-c78cbaad841e HA Enabled true HA Cluster n/a HA Mode standby Active Node Address <none> Raft Committed Index 24 Raft Applied Index 24 Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.8.4 Storage Type raft Cluster Name vault-cluster-d58a9688 Cluster ID 637ba7c8-8d57-5bb5-2840-c78cbaad841e HA Enabled true HA Cluster n/a HA Mode standby Active Node Address <none> Raft Committed Index 24 Raft Applied Index 24 Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.8.4 Storage Type raft Cluster Name vault-cluster-d58a9688 Cluster ID 637ba7c8-8d57-5bb5-2840-c78cbaad841e HA Enabled true HA Cluster n/a HA Mode standby Active Node Address <none> Raft Committed Index 24 Raft Applied Index 24 |
далее нужно собрать raft в кластер:
1 2 3 |
kubectl -n vault exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200 kubectl -n vault exec -ti vault-2 -- vault operator raft join http://vault-0.vault-internal:8200 |
и распечатываем vault-1 vault-2
1 2 3 |
for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-1 -- vault operator unseal $i; done for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-2 -- vault operator unseal $i; done |
6.4 auto unseal vault с помощью другого vault
и так мы выполнили все предварительные шаги:
сейчас у нас на 3х отдельных тачках расположен vault и в minikub расположен другой vault, теперь настроем их auto unseal.
будет использовать метод Transit Auto Unseal
[root@vault1 ~]# vault login
тут я вбивал рут токен полученный при инициализации хранилища
[root@vault1 ~]# vault secrets enable transit
Success! Enabled the transit secrets engine at: transit/
После включения транзита я запишу ключ по этому пути:
[root@vault1 ~]# vault write -f transit/keys/vault-unseal-key
Success! Data written to: transit/keys/vault-unseal-key
[root@vault1 ~]# vault list transit/keys
1 2 3 |
Keys ---- vault-unseal-key |
Теперь я создам policy и токен с прикрепленной к нему политикой. Я буду использовать этот токен в конфигурации второго сервера для Auto Unseal.
[root@vault1 ~]# cat transit-policy.yaml
1 2 3 4 5 6 7 8 |
path "transit/encrypt/vault-unseal-key" { capabilities = [ "update" ] } path "transit/decrypt/vault-unseal-key" { capabilities = [ "update" ] } |
[root@vault1 ~]# vault policy write unseal-policy transit-policy.yaml
Success! Uploaded policy: unseal-policy
[root@vault1 ~]# vault token create -policy=unseal-policy
1 2 3 4 5 6 7 8 9 10 |
Key Value --- ----- token hvs.CAESIFR6S7arwSa5-02dgkGwuz5KHt6SW-ROfPdV1-TVAwipGh4KHGh2cy5paUZsMGkyMndTQXBFT216eEtiTFU4a20 token_accessor aEsm3VPYog9MaGhsH9PAiGuS token_duration 768h token_renewable true token_policies ["default" "unseal-policy"] identity_policies [] policies ["default" "unseal-policy"] |
Теперь на втором Vault(в minikube) я добавлю раздел с конфигурацией для автоматического вскрытия Transit.
Для этого правим конфиг (values) vault-helm/values-my.yaml и добавляем вот такой код:
1 2 3 4 5 6 7 8 9 |
seal "transit" { address = "https://192.168.1.174:8200" token = "hvs.CAESIFR6S7arwSa5-02dgkGwuz5KHt6SW-ROfPdV1-TVAwipGh4KHGh2cy5paUZsMGkyMndTQXBFT216eEtiTFU4a20" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
весь values будет вот в таком виде:
cat vault-helm/values-my.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
injector: enabled: false server: image: repository: "hashicorp/vault" tag: "1.8.4" pullPolicy: IfNotPresent logLevel: "info" logFormat: "json" resources: requests: memory: 256Mi cpu: 250m limits: memory: 256Mi cpu: 250m affinity: {} dataStorage: enabled: true size: 1Gi mountPath: "/vault/data" storageClass: nfs-client accessMode: ReadWriteOnce dev: enabled: false standalone: enabled: false ha: enabled: true replicas: 3 # Set the api_addr configuration for Vault HA # See https://www.vaultproject.io/docs/configuration#api_addr # If set to null, this will be set to the Pod IP Address apiAddr: null raft: # Enables Raft integrated storage enabled: true config: | ui = true listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "raft" { path = "/vault/data" } seal "transit" { address = "https://192.168.1.174:8200" token = "hvs.CAESIFR6S7arwSa5-02dgkGwuz5KHt6SW-ROfPdV1-TVAwipGh4KHGh2cy5paUZsMGkyMndTQXBFT216eEtiTFU4a20" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } service_registration "kubernetes" {} csi: enabled: true resources: requests: cpu: 50m memory: 128Mi limits: cpu: 50m memory: 128Mi |
применяем новую конфигурацию:
[root@minikub ~]# helm upgrade --install vault -n vault ./vault-helm/ -f ./vault-helm/values-my.yaml
теперь нужно грохнуть старые поды и распечатать vault с флагом -migrate
1 2 3 |
kubectl delete pod -n vault vault-2 for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-2 -- vault operator unseal -migrate $i; done |
следующий:
1 2 3 |
kubectl delete pod -n vault vault-1 for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-1 -- vault operator unseal -migrate $i; done |
и последний:
1 2 3 |
kubectl delete pod -n vault vault-0 for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-0 -- vault operator unseal -migrate $i; done |
проверяем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[root@minikub ~]# kubectl delete pod -n vault vault-1 pod "vault-1" deleted [root@minikub ~]# [root@minikub ~]# kubectl get pod -n vault NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 18m vault-1 0/1 Running 0 3s vault-2 1/1 Running 0 17m vault-csi-provider-pxlbr 1/1 Running 0 28m [root@minikub ~]# [root@minikub ~]# kubectl get pod -n vault NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 18m vault-1 0/1 Running 0 7s vault-2 1/1 Running 0 17m vault-csi-provider-pxlbr 1/1 Running 0 28m [root@minikub ~]# [root@minikub ~]# kubectl get pod -n vault NAME READY STATUS RESTARTS AGE vault-0 1/1 Running 0 18m vault-1 1/1 Running 0 19s vault-2 1/1 Running 0 17m vault-csi-provider-pxlbr 1/1 Running 0 28m |
как видим контейнер с vault поднимается и становится ready потому что прошёл auto unseal посмотрим его логи:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
[root@minikub ~]# kubectl logs -n vault vault-1 ==> Vault server configuration: Api Address: http://172.17.0.7:8200 Cgo: disabled Cluster Address: https://vault-1.vault-internal:8201 Go Version: go1.16.7 Listener 1: tcp (addr: "[::]:8200", cluster address: "[::]:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled") Log Level: info Mlock: supported: true, enabled: false Recovery Mode: false Storage: raft (HA available) Version: Vault v1.8.4 Version Sha: 925bc650ad1d997e84fbb832f302a6bfe0105bbb ==> Vault server started! Log data will stream in below: {"@level":"info","@message":"proxy environment","@timestamp":"2023-01-13T13:42:00.233741Z","http_proxy":"","https_proxy":"","no_proxy":""} {"@level":"info","@message":"stored unseal keys supported, attempting fetch","@module":"core","@timestamp":"2023-01-13T13:42:00.286042Z"} {"@level":"info","@message":"starting listener","@module":"core.cluster-listener.tcp","@timestamp":"2023-01-13T13:42:00.290788Z","listener_address":{"IP":"::","Port":8201,"Zone":""}} {"@level":"info","@message":"serving cluster requests","@module":"core.cluster-listener","@timestamp":"2023-01-13T13:42:00.290868Z","cluster_listen_address":{"IP":"::","Port":8201,"Zone":""}} {"@level":"info","@message":"creating Raft","@module":"storage.raft","@timestamp":"2023-01-13T13:42:00.291203Z","config":"\u0026raft.Config{ProtocolVersion:3, HeartbeatTimeout:5000000000, ElectionTimeout:5000000000, CommitTimeout:50000000, MaxAppendEntries:64, BatchApplyCh:true, ShutdownOnRemove:true, TrailingLogs:0x2800, SnapshotInterval:120000000000, SnapshotThreshold:0x2000, LeaderLeaseTimeout:2500000000, LocalID:\"93038423-c5d8-bd96-c321-ccc07765315d\", NotifyCh:(chan\u003c- bool)(0xc000875960), LogOutput:io.Writer(nil), LogLevel:\"DEBUG\", Logger:(*hclog.interceptLogger)(0xc00006f3e0), NoSnapshotRestoreOnStart:true, skipStartup:false}"} {"@level":"info","@message":"initial configuration","@module":"storage.raft","@timestamp":"2023-01-13T13:42:00.309691Z","index":34,"servers":"[{Suffrage:Voter ID:3003f49a-29b5-23b6-97f3-b9f049826e46 Address:vault-0.vault-internal:8201} {Suffrage:Voter ID:93038423-c5d8-bd96-c321-ccc07765315d Address:vault-1.vault-internal:8201} {Suffrage:Voter ID:3221b3b9-7417-a814-3227-1d5bbe1be6f8 Address:vault-2.vault-internal:8201}]"} {"@level":"info","@message":"vault is unsealed","@module":"core","@timestamp":"2023-01-13T13:42:00.309803Z"} {"@level":"info","@message":"entering standby mode","@module":"core","@timestamp":"2023-01-13T13:42:00.309846Z"} {"@level":"info","@message":"entering follower state","@module":"storage.raft","@timestamp":"2023-01-13T13:42:00.309983Z","follower":{},"leader":""} {"@level":"info","@message":"unsealed with stored key","@module":"core","@timestamp":"2023-01-13T13:42:00.347079Z"} |
Это мы настроили auto unseal для vault в kubernetes.Теперь настроим auto unseal для нашего основного Vault который на виртуалках.
чтобы был доступ из вне включаем ingress
1 2 3 4 5 6 7 8 9 10 11 |
ingress: enabled: true annotations: kubernetes.io/ingress.class: nginx activeService: true hosts: - host: chart-example.local paths: - / |
(ЭТОТ КЕЙС ТОЛЬКО В СЛУЧАЕ С MINIKUBE, необходимо прокинуть наружу айпишник по которому будем ходить в кластер) и из-за того что запускал в minikube и его айпишник:
[root@minikube ~]# minikube ip
192.168.49.2
надо прокинуть порт на хостовую ноду:
для этого поставим haproxy:
yum install haproxy -y
и добавим в его конфиг проксирование:
[root@minikube ~]# cat /etc/haproxy/haproxy.cfg
1 2 3 4 5 6 7 8 9 10 11 |
frontend app_main bind *:80 mode http option forwardfor header X-Real-IP default_backend app_main_backend backend app_main_backend mode http balance roundrobin server server1 chart-example.local |
systemctl enable haproxy
systemctl start haproxy
Теперь на наших Vault я добавлю раздел с конфигурацией для автоматического вскрытия Transit.
не забываем добавить в hosts адрес нашего minikube:
(vault1/vault2/vault3)
1 2 3 4 5 6 7 8 9 |
[root@vault1 ~]# cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.1.171 vault1 vault1.test.local 192.168.1.172 vault2 vault2.test.local 192.168.1.173 vault3 vault3.test.local 192.168.1.174 vault.test.local 192.168.1.175 chart-example.local |
Теперь приступаем к настройке auto unseal в k8s
выполняем те же действия:
[root@minikub ~]# kubectl -n vault exec -ti vault-0 sh
vault login
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Token (will be hidden): Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.TRiEaBRPbFIBd8rtlEcOkZmY token_accessor o1gmkAzhpwOWZcOrJhl9glGY token_duration ∞ token_renewable false token_policies ["root"] identity_policies [] policies ["root"] |
vault secrets enable transit
vault write -f transit/keys/vault-unseal-key
/ $ cd /tmp/
/tmp $ cat > transit-policy.yaml
1 2 3 4 5 6 7 8 |
path "transit/encrypt/vault-unseal-key" { capabilities = [ "update" ] } path "transit/decrypt/vault-unseal-key" { capabilities = [ "update" ] } |
/tmp $ vault policy write unseal-policy transit-policy.yaml
/tmp $ vault token create -policy=unseal-policy
1 2 3 4 5 6 7 8 9 10 |
/ $ vault token create -policy=unseal-policy Key Value --- ----- token s.Cs6sHK6FFxpmHC7u6DXBloQD token_accessor d5VMyYK44EZ4pVSTQI2oVxd9 token_duration 768h token_renewable true token_policies ["default" "unseal-policy"] identity_policies [] policies ["default" "unseal-policy"] |
получили токен теперь идём в конфигурацИИ наших vault на виртуалках и добавляем следующую конфигурацию:
1 2 3 4 5 6 7 8 9 |
seal "transit" { address = "http://chart-example.local:80" token = "s.Cs6sHK6FFxpmHC7u6DXBloQD" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
ниже приведён полный конфиг:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
[root@vault1 ~]# cat /etc/vault.d/vault.hcl cluster_addr = "https://192.168.1.171:8201" api_addr = "https://192.168.1.174:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_disable = 0 tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-a-cert.pem" tls_key_file = "/opt/vault/tls/vault-a-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault1.test.local" timeout = "30s" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-a-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-a-key.pem" } } seal "transit" { address = "http://chart-example.local:80" token = "s.Cs6sHK6FFxpmHC7u6DXBloQD" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
[root@vault2 ~]# cat /etc/vault.d/vault.hcl cluster_addr = "https://192.168.1.172:8201" api_addr = "https://192.168.1.174:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-b-cert.pem" tls_key_file = "/opt/vault/tls/vault-b-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault2.test.local" timeout = "30s" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-b-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-b-key.pem" } } seal "transit" { address = "http://chart-example.local:80" token = "s.Cs6sHK6FFxpmHC7u6DXBloQD" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
[root@vault3 ~]# cat /etc/vault.d/vault.hcl cluster_addr = "https://192.168.1.173:8201" api_addr = "https://192.168.1.174:8200" disable_mlock = true ui = true listener "tcp" { address = "0.0.0.0:8200" tls_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" tls_cert_file = "/opt/vault/tls/vault-c-cert.pem" tls_key_file = "/opt/vault/tls/vault-c-key.pem" } storage "raft" { path = "/opt/vault/data" node_id = "vault3.test.local" timeout = "30s" retry_join { leader_tls_servername = "vault1.test.local" leader_api_addr = "https://192.168.1.171:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } retry_join { leader_tls_servername = "vault2.test.local" leader_api_addr = "https://192.168.1.172:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } retry_join { leader_tls_servername = "vault3.test.local" leader_api_addr = "https://192.168.1.173:8200" leader_ca_cert_file = "/opt/vault/tls/vault-ca-cert.pem" leader_client_cert_file = "/opt/vault/tls/vault-c-cert.pem" leader_client_key_file = "/opt/vault/tls/vault-c-key.pem" } } seal "transit" { address = "http://chart-example.local:80" token = "s.Cs6sHK6FFxpmHC7u6DXBloQD" key_name = "vault-unseal-key" mount_path = "transit/" disable_renewal = "false" tls_skip_verify = "true" } |
теперь рестартуем vault на всех тачках:
[root@vault1 ~]# systemctl restart vault
[root@vault2 ~]# systemctl restart vault
[root@vault3 ~]# systemctl restart vault
проверяем что хранилище действительно запечатано:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[root@vault1 ~]# vault status Key Value --- ----- Recovery Seal Type shamir Initialized true Sealed true Total Recovery Shares 5 Threshold 3 Unseal Progress 0/3 Unseal Nonce n/a Seal Migration in Progress true Version 1.12.2 Build Date 2022-11-23T12:53:46Z Storage Type raft HA Enabled true |
теперь нужно распечатать наше хранилище с параметром '-migrate'
[root@vault1 ~]# vault operator unseal -migrate
[root@vault1 ~]# vault operator unseal -migrate
[root@vault1 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault2 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
[root@vault3 ~]# vault operator unseal -migrate
если лень ручками ходить и вбивать то можно использовать цикл:
for i in `cat unseal`; do vault operator unseal -migrate $i; done
всё можем проверять что наш кластер автоматически распечатывается:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@vault1 ~]# systemctl restart vault [root@vault1 ~]# vault status Key Value --- ----- Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 5 Threshold 3 Version 1.12.2 Build Date 2022-11-23T12:53:46Z Storage Type raft Cluster Name vault-cluster-152741cf Cluster ID 8fb040bd-bc90-3150-88b9-0888cdf5fbee HA Enabled true HA Cluster https://192.168.1.173:8201 HA Mode standby Active Node Address https://192.168.1.174:8200 Raft Committed Index 415 Raft Applied Index 415 |
на остальных тачках так же:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@vault2 ~]# systemctl restart vault [root@vault2 ~]# vault status Key Value --- ----- Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 5 Threshold 3 Version 1.12.2 Build Date 2022-11-23T12:53:46Z Storage Type raft Cluster Name vault-cluster-152741cf Cluster ID 8fb040bd-bc90-3150-88b9-0888cdf5fbee HA Enabled true HA Cluster https://192.168.1.173:8201 HA Mode standby Active Node Address https://192.168.1.174:8200 Raft Committed Index 415 Raft Applied Index 415 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@vault3 ~]# systemctl restart vault [root@vault3 ~]# vault status Key Value --- ----- Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 5 Threshold 3 Version 1.12.2 Build Date 2022-11-23T12:53:46Z Storage Type raft Cluster Name vault-cluster-152741cf Cluster ID 8fb040bd-bc90-3150-88b9-0888cdf5fbee HA Enabled true HA Cluster https://192.168.1.171:8201 HA Mode standby Active Node Address https://192.168.1.174:8200 Raft Committed Index 423 Raft Applied Index 423 |
7.Бэкап данных(backup raft)
Благодаря тому, что Raft полностью дублирует все данные на каждый из узлов, выполнять снапшот можно на любом сервере, где запущен Vault.
Для создания снимка необходимо:
Зайти на сервер/pod, где запущен Vault, и выполнить команду:
1 2 |
vault operator raft snapshot save backup.snapshot |
файлbackup.snapshot
можно заархивировать и перенести в место для хранения бэкапов
Для восстановления команду на сервере с Vault надо заменить на:
1 |
vault operator raft snapshot restore backup.snapshot |
если что вот официальная дока
проверим:
на виртуалке:
1 2 3 4 5 6 7 |
[root@vault1 ~]# mkdir test [root@vault1 ~]# cd test/ [root@vault1 test]# vault operator raft snapshot save backup.snapshot [root@vault1 test]# ll total 32 -rw------- 1 root root 29135 Feb 5 12:13 backup.snapshot |
в POD:
1 2 3 4 5 6 7 8 9 10 11 |
[root@minikube ~]# kubectl -n vault exec -ti vault-0 sh kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. / $ mkdir /tmp/test / $ cd /tmp/test/ /tmp/test $ vault operator raft snapshot save backup.snapshot /tmp/test $ ls -lah total 40K drwxr-xr-x 2 vault vault 4.0K Feb 5 06:15 . drwxrwxrwt 1 root root 4.0K Feb 5 06:15 .. -rw-r--r-- 1 vault vault 29.3K Feb 5 06:15 backup.snapshot |
как видим всё ок.
8. Настройка LDAP — freeipa (лично не тестил)
Раздел Access-Auth methods выбираем создать ldap
первый раз просто нажимаем создать параметры все по дефолту, потом вобьем правильные
- задаем UR: в примере ниже это ldapS и на 636 порту, чтобы это корректно работала сразу, сервер должен быть подключен freeipa-clinet к серверу фриипты, либо настравайте ldap обычный по 389 или со с startTLS
- в разделе LDAP options: раскрываем и меняем User atrribut на uid
- Следующий раздел customize user search — тут мы как раз и настраиваем адрес УЗ кем биндимся, а так где искать пользователей, для фриипа это будут такие параметры
- uid=our-system-user,cn=sysaccounts,cn=etc,dc=example,dc=com
- cn=users,cn=accounts,dc=example,dc=com
Так же настроим поиск по группам
Типовое использование
Все шаги ниже, подразумевают, что вы подключились к vault используя клиент vault, как подключиться ниже
Скачать клиент можно тут https://developer.hashicorp.com/vault/downloads
Подключение
Укажем адрес подключения нашего vault и убедимся, что корректно подключились
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
export VAULT_ADDR=https://vault-01.example.com vault status ### output example vault output Key Value -- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.1.3 Cluster Name vault Cluster ID 3ca0b2dd-f36b-0c2a-f999-2a4814371f4e HA Enabled false |
Авторизация в VAULT — token
1 2 |
export VAULT_TOKEN="our-token" |
Авторизация в VAULT — парольная
1 2 |
vault login -method=userpass username=username password=SecretPassword |
Авторизация в VAULT — LDAP
https://www.vaultproject.io/docs/auth/ldap.html
1 |
vault login -method=ldap username=our_username |
ВАЖНО: если мы хотим назначить сразу несколько политик, надо их все перечислить через запятую, иначе my-policy перезатрет все остальные политики
1 |
vault write auth/userpass/users/vasy policies="my-policy" |
много политик
1 |
vault write auth/userpass/users/vasy policies="my-policy","policy2" |
Подключение политики к LDAP группе
1 2 3 |
vault write auth/ldap/groups/ldap-group-name policies="example-policy" |
Узнать какие политики назначены на пользователя
например пользователю vasy, который входит по логину и паролю
1 2 |
vault read auth/userpass/users/vasy |
Узнать какие политики назначены на LDAP группу
1 2 |
vault read auth/ldap/groups/groupname |
Узнать какие политики назначены на пользователя при входе
Видно в выводе при входе
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
vault login -method=ldap username=username Password (will be hidden): Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.bwcvQFpodcXWrRiOYxLO80RI token_accessor 6Ypav42huMYvQfccWYK6CNH0 token_duration 10h token_renewable true token_policies ["default" "example-policy"] identity_policies [] policies ["default" "example-policy"] token_meta_username username |
9. Добавляем секреты из vault в POD (vault установлен в k8s) можно подкидывать определённые key
это я подымал не в миникубе а в кластере быстрой пройдёмся по установке:
1 2 3 |
apt-get install jq kubectl create ns vault |
чарт берём оффициальный
https://github.com/hashicorp/vault-helm.git
я подготовил my-values.yaml
cat my-values.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
injector: enabled: false server: image: repository: "hashicorp/vault" tag: "1.8.4" pullPolicy: IfNotPresent logLevel: "info" logFormat: "json" resources: requests: memory: 256Mi cpu: 250m limits: memory: 256Mi cpu: 250m affinity: {} dataStorage: enabled: true size: 1Gi mountPath: "/vault/data" storageClass: nfs-client accessMode: ReadWriteOnce dev: enabled: false standalone: enabled: false ha: enabled: true replicas: 3 # Set the api_addr configuration for Vault HA # See https://www.vaultproject.io/docs/configuration#api_addr # If set to null, this will be set to the Pod IP Address apiAddr: null raft: # Enables Raft integrated storage enabled: true config: | ui = true listener "tcp" { tls_disable = 1 address = "[::]:8200" cluster_address = "[::]:8201" } storage "raft" { path = "/vault/data" } service_registration "kubernetes" {} ingress: enabled: true ingressClassName: "nginx" activeService: true hosts: - host: vault.test.ru paths: - / csi: enabled: true resources: requests: cpu: 50m memory: 128Mi limits: cpu: 50m memory: 128Mi |
теперь ставим vault
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
helm repo add hashicorp https://helm.releases.hashicorp.com helm install -n vault vault hashicorp/vault -f my-values.yaml инициализируем волт kubectl -n vault exec vault-0 -- vault operator init -key-shares=5 -key-threshold=3 -format=json > cluster-keys.json распечатываем for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-0 -- vault operator unseal $i; done добавляем в рафт kubectl -n vault exec -ti vault-1 -- vault operator raft join http://vault-0.vault-internal:8200 kubectl -n vault exec -ti vault-2 -- vault operator raft join http://vault-0.vault-internal:8200 распечатываем остальные реплики: for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-1 -- vault operator unseal $i; done for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-2 -- vault operator unseal $i; done |
если лень 3 раза запускать распечатывание то можно использовать:
1 |
for j in {0..2}; do for i in `cat cluster-keys.json | jq -r ".unseal_keys_b64[]"`; do kubectl -n vault exec vault-$j -- vault operator unseal $i; done; done |
9.1 secrets-store-csi-driver
ставим secrets-store-csi-driver
Secrets Store CSI (Container Storage Interface) Driver - это драйвер для Kubernetes, который позволяет монтировать конфиденциальные данные из внешнего хранилища, в виде томов CSI.
1 2 3 4 |
helm install -n vault csi secrets-store-csi-driver/secrets-store-csi-driver \ --set syncSecret.enabled=true \ --set enableSecretRotation=true \ --set "rotationPollInterval=30s" |
логинимся в vault
kubectl exec -it vault-0 -n vault -- /bin/sh
vault login s.O9SD65yMtAZDpLu2uh0Otkyv
пароль берём из файла cluster-keys.json
создаём секрет который будем подкидывать:
1 2 |
vault secrets enable -path=secret/ kv vault kv put secret/db-pass password="db-secret-password" |
включаем аутентификацию в k8s
1 2 3 4 5 |
vault auth enable kubernetes vault write auth/kubernetes/config \ kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ disable_iss_validation="true" |
создаём policy
1 2 3 4 5 |
vault policy write internal-app - <<EOF path "secret/db-pass" { capabilities = ["read"] } EOF |
создаём роль
Роль связывает учетную запись службы Kubernetes, webapp-sa
в пространстве имен default,
с политикой Vault, internal-app
. Токены, возвращенные после аутентификации, действительны в течение 10 минут
1 2 3 4 5 |
vault write auth/kubernetes/role/database \ bound_service_account_names=webapp-sa \ bound_service_account_namespaces=default \ policies=internal-app \ ttl=10m |
всё выходим из контейнера vault
создаём SecretProviderClass
Этот ресурс описывает параметры, которые предоставляются Vault CSI. Для его настройки требуется адрес сервера Vault, имя роли аутентификации Vault Kubernetes и секреты.
objectName
— это символическое имя для этого секрета и имя файла для записи.secretPath
— это путь к секрету, определенному в Vault.secretKey
является ключом этом секрете.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
cat > spc-vault-database.yaml <<EOF apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: vault-database spec: provider: vault parameters: vaultAddress: "http://vault.vault:8200" roleName: "database" objects: | - objectName: "db-password" secretPath: "secret/db-pass" secretKey: "password" EOF |
применяем
kubectl apply --filename spc-vault-database.yaml
создаём serviceaccaunt
kubectl create serviceaccount webapp-sa
теперь подкидываем секрет как файл
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
cat > webapp-pod.yaml <<EOF kind: Pod apiVersion: v1 metadata: name: webapp spec: serviceAccountName: webapp-sa containers: - image: jweissig/app:0.0.1 name: webapp volumeMounts: - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true volumes: - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "vault-database" EOF |
применяем
kubectl apply --filename webapp-pod.yaml
проверяем:
1 2 |
root@master1:/etc/ansible/kubespray-official/vault# kubectl exec webapp -- cat /mnt/secrets-store/db-password db-secret-password |
теперь подкидываем секрет как файл и переменную окружения
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
cat > spc-vault-database.yaml <<EOF apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: vault-database spec: provider: vault secretObjects: - data: - key: password objectName: db-password secretName: dbpass type: Opaque parameters: vaultAddress: "http://vault.vault:8200" roleName: "database" objects: | - objectName: "db-password" secretPath: "secret/db-pass" secretKey: "password" EOF |
kubectl apply --filename spc-vault-database.yaml
и теперь сам POD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
cat > webapp-pod.yaml <<EOF kind: Pod apiVersion: v1 metadata: name: webapp spec: serviceAccountName: webapp-sa containers: - image: jweissig/app:0.0.1 name: webapp env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: dbpass key: password volumeMounts: - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true volumes: - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "vault-database" EOF |
удаляем старый запускаем новый:
kubectl delete pod webapp && kubectl apply --filename webapp-pod.yaml
проверяем:
1 2 3 4 5 6 7 |
kubectl get secret NAME TYPE DATA AGE dbpass Opaque 1 82s kubectl exec webapp -- env | grep DB_PASSWORD DB_PASSWORD=db-secret-password |
если нужно подкинуть несколько key к секрет то можем воспользоваться вот этим примером:
https://github.com/kubernetes-sigs/secrets-store-csi-driver/blob/release-1.3/test/bats/tests/vault/vault_v1_multiple_secretproviderclass.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: vault-foo-sync-0 spec: provider: vault secretObjects: - secretName: foosecret-0 type: Opaque data: - objectName: bar key: pwd - objectName: bar1 key: username parameters: roleName: "csi" vaultAddress: "http://vault.vault:8200" objects: | - secretPath: "secret/data/foo" objectName: "bar" secretKey: "bar" - secretPath: "secret/data/foo1" objectName: "bar1" secretKey: "bar1" --- apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: vault-foo-sync-1 spec: provider: vault secretObjects: - secretName: foosecret-1 type: Opaque data: - objectName: bar key: pwd - objectName: bar1 key: username parameters: roleName: "csi" vaultAddress: "http://vault.vault:8200" objects: | - secretPath: "secret/data/foo" objectName: "bar" secretKey: "bar" - secretPath: "secret/data/foo1" objectName: "bar1" secretKey: "bar1" |
в чём неудобство, вам каждый раз нужно ходить и править руками SecretProviderClass когда нужно добавить новую переменную
есть issue
https://github.com/kubernetes-sigs/secrets-store-csi-driver/issues/529
висит давно.
9.2 Vault Secrets Operator
вот оффициальная репка:
https://github.com/hashicorp/vault-secrets-operator
ставим
helm repo add hashicorp https://helm.releases.hashicorp.com
добавляем адресс нашего vault в values чтоб установить
cat vault-secrets-operator.yaml
1 2 3 |
defaultVaultConnection: enabled: true address: "http://vault.vault.svc.cluster.local:8200" |
напоминаю что vault у меня установлен в namespace vault
ставим:
helm upgrade --install --namespace vault vault-secrets-operator hashicorp/vault-secrets-operator --version 0.1.0-beta -f vault-secrets-operator.yaml
данная версия --version 0.1.0-beta это версия чарта https://github.com/hashicorp/vault-secrets-operator/blob/main/chart/Chart.yaml
для проверки работы создадим отдельный неймспейс:
kubectl create ns test
логинимся в vault
kubectl exec -it vault-0 -n vault -- /bin/sh
vault login s.PAx0C3X0nx9vVBMwgtM9nLH6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.PAx0C3X0nx9vVBMwgtM9nLH6 token_accessor R3DZMUHnIg9ovxdUwfHCCGda token_duration ∞ token_renewable false token_policies ["root"] identity_policies [] policies ["root"] |
пароль берём из файла cluster-keys.json
создаём секрет который будем подкидывать:
1 2 3 |
/ $ vault secrets enable -path=test/secret/ kv Success! Enabled the kv secrets engine at: test/secret/ |
добавляем туда ключ значение:
1 2 3 |
/ $ vault kv put test/secret/namespace-test/first-app password="db-secret-password" Success! Data written to: test/secret/namespace-test/first-app |
проверяем:
включаем аутентификацию в k8s (если один раз включили последующие разы ещё раз включать аутентификацию НЕ нужно)
vault auth enable kubernetes
1 2 3 |
vault write auth/kubernetes/config \ kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ disable_iss_validation="true" |
создаём policy "test-policy" на чтение ТОЛЬКО нашего секрета
1 2 3 4 5 |
vault policy write test-policy - <<EOF path "test/secret/namespace-test/first-app" { capabilities = ["read"] } EOF |
создаём роль "test-role"
Роль связывает учетную запись службы Kubernetes(serviceaccaunt), которую назовём test-serviceaccaunt (но лучше использовать уже существующий сервис аккаунт нашего приложения ) в пространстве имен test с политикой Vault, test-policy Токены, возвращенные после аутентификации, действительны в течение 10 минут
1 2 3 4 5 |
vault write auth/kubernetes/role/test-role \ bound_service_account_names=test-serviceaccaunt \ bound_service_account_namespaces=test \ policies=test-policy \ ttl=10m |
создаём serviceaccaunt
kubectl create serviceaccount test-serviceaccaunt -n test
создаём объект vaultauth
cat vault-auth.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: secrets.hashicorp.com/v1alpha1 kind: VaultAuth metadata: labels: app.kubernetes.io/name: vaultauth app.kubernetes.io/instance: vaultauth-sample app.kubernetes.io/part-of: vault-secrets-operator app.kubernetes.io/managed-by: kustomize app.kubernetes.io/created-by: vault-secrets-operator name: vaultauth-app1 namespace: test spec: vaultConnectionRef: method: kubernetes mount: kubernetes kubernetes: role: test-role serviceAccount: test-serviceaccaunt |
тут задаём имя этого ресурса name: vaultauth-app1
неймспейс в котором от будет создан namespace: test
роль которую мы ранее создали в vault указав там наш неймспейс и сервис аккаунт role: test-role
и так же наш сервис аккаунт serviceAccount: test-serviceaccaunt
kubectl apply -f vault-auth.yaml -n test
проверяем:
1 2 3 4 |
kubectl get vaultauths.secrets.hashicorp.com -n test NAME AGE vaultauth-app1 96s |
если в выводе describe
kubectl describe vaultauths.secrets.hashicorp.com -n test vaultauth-app1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
Name: vaultauth-app1 Namespace: test Labels: app.kubernetes.io/created-by=vault-secrets-operator app.kubernetes.io/instance=vaultauth-sample app.kubernetes.io/managed-by=kustomize app.kubernetes.io/name=vaultauth app.kubernetes.io/part-of=vault-secrets-operator Annotations: <none> API Version: secrets.hashicorp.com/v1alpha1 Kind: VaultAuth Metadata: Creation Timestamp: 2023-04-09T11:12:32Z Finalizers: vaultauth.secrets.hashicorp.com/finalizer Generation: 1 Managed Fields: API Version: secrets.hashicorp.com/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:labels: .: f:app.kubernetes.io/created-by: f:app.kubernetes.io/instance: f:app.kubernetes.io/managed-by: f:app.kubernetes.io/name: f:app.kubernetes.io/part-of: f:spec: .: f:kubernetes: .: f:role: f:serviceAccount: f:tokenExpirationSeconds: f:method: f:mount: Manager: kubectl-client-side-apply Operation: Update Time: 2023-04-09T11:12:32Z API Version: secrets.hashicorp.com/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:finalizers: .: v:"vaultauth.secrets.hashicorp.com/finalizer": Manager: vault-secrets-operator Operation: Update Time: 2023-04-09T11:12:32Z API Version: secrets.hashicorp.com/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:status: .: f:error: f:valid: Manager: vault-secrets-operator Operation: Update Subresource: status Time: 2023-04-09T11:12:32Z Resource Version: 331728 UID: 5d92bcca-2e1d-4e8b-9de1-19964dbaa1bb Spec: Kubernetes: Role: test-role Service Account: test-serviceaccaunt Token Expiration Seconds: 600 Method: kubernetes Mount: kubernetes Status: Error: Valid: true Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Accepted 89s VaultAuth Successfully handled VaultAuth resource request |
в эвентах видим
Successfully handled VaultAuth resource request
значит всё ок
теперь создаём объект VaultStaticSecret который будет создавать нам k8s секрет
cat vault-secret.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
--- apiVersion: secrets.hashicorp.com/v1alpha1 kind: VaultStaticSecret metadata: namespace: test name: vault-secret-app1 spec: vaultAuthRef: vaultauth-app1 mount: test/secret type: kv-v1 name: namespace-test/first-app refreshAfter: 10s destination: create: true name: test-secret-k8s type: Opaque |
в данном ресурсе мы задаём его имя name: vault-secret-app1
неймспейс в котором он будет создан namespace: test
в спеке задаём имя ресурса vaultauth vaultAuthRef: vaultauth-app1 который мы создали ранее
имя секрета mount: test/secret и путь до него name: namespace-test/first-app напомню что секрет мы создавали командой
vault secrets enable -path=test/secret/ kv поэтому mount мы задаём test/secret а информацию в секрет мы ложили командой vault kv put test/secret/namespace-test/first-app password="db-secret-password" поэтому name мы задаём по адресу namespace-test/first-app
время обновления данных мы задаём переменной refreshAfter
в destination мы задаём нужно ли создавать секрет create: true
и его имя name: test-secret-k8s
тип указывать не обязательно но я указал для наглядности type: Opaque
kubectl apply -f vault-secret.yaml -n test
проверяем:
1 2 3 4 |
kubectl get vaultstaticsecrets.secrets.hashicorp.com -n test NAME AGE vault-secret-app1 2m21s |
если в describe видим в event следующее сообщение:
Secret synced или Secret sync not required то всё ок
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
kubectl describe vaultstaticsecrets.secrets.hashicorp.com -n test Name: vault-secret-app1 Namespace: test Labels: <none> Annotations: <none> API Version: secrets.hashicorp.com/v1alpha1 Kind: VaultStaticSecret Metadata: Creation Timestamp: 2023-04-09T11:25:40Z Generation: 1 Managed Fields: API Version: secrets.hashicorp.com/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:spec: .: f:destination: .: f:create: f:name: f:type: f:hmacSecretData: f:mount: f:name: f:refreshAfter: f:type: f:vaultAuthRef: Manager: kubectl-client-side-apply Operation: Update Time: 2023-04-09T11:25:40Z API Version: secrets.hashicorp.com/v1alpha1 Fields Type: FieldsV1 fieldsV1: f:status: .: f:secretMAC: Manager: vault-secrets-operator Operation: Update Subresource: status Time: 2023-04-09T11:25:40Z Resource Version: 334402 UID: 52a97f80-6cd4-472e-b800-97baca522b5a Spec: Destination: Create: true Name: test-secret-k8s Type: Opaque Hmac Secret Data: true Mount: test/secret Name: namespace-test/first-app Refresh After: 10s Type: kv-v1 Vault Auth Ref: vaultauth-app1 Status: Secret MAC: iASHxICpaIlLgf/9Tr/6SACfTJ0cRDtdhwtGtVLDEx0= Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SecretSynced 29s VaultStaticSecret Secret synced Normal SecretSync 3s (x3 over 20s) VaultStaticSecret Secret sync not required |
ну и проверяем наш секрет:
1 2 3 4 |
kubectl get secret -n test NAME TYPE DATA AGE test-secret-k8s Opaque 2 3m53s |
посмотрим что внутри секрета:
kubectl get secret -n test test-secret-k8s -o yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
apiVersion: v1 data: _raw: eyJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCJ9 password: ZGItc2VjcmV0LXBhc3N3b3Jk kind: Secret metadata: creationTimestamp: "2023-04-09T11:25:40Z" labels: app.kubernetes.io/component: secret-sync app.kubernetes.io/managed-by: hashicorp-vso app.kubernetes.io/name: vault-secrets-operator name: test-secret-k8s namespace: test ownerReferences: - apiVersion: secrets.hashicorp.com/v1alpha1 kind: VaultStaticSecret name: vault-secret-app1 uid: 52a97f80-6cd4-472e-b800-97baca522b5a resourceVersion: "334401" uid: 990809a7-8fdc-4070-83e8-e9a1a6e20306 type: Opaque |
есть 2 записи:
password и _raw посмотрим что в них:
1 2 |
echo "ZGItc2VjcmV0LXBhc3N3b3Jk" | base64 -d db-secret-password |
и вторая запись
1 2 |
echo "eyJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCJ9" | base64 -d {"password":"db-secret-password"} |
добавим ещё одно значение к этому секрету:
дёрнем секрет ещё раз и глянем что там:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
kubectl get secret -n test test-secret-k8s -o yaml apiVersion: v1 data: _raw: eyJuZXcta2V5LWZvci10ZXN0IjoidHJhdGF0YS10YXRhLXBhbS1wYW0iLCJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCJ9 new-key-for-test: dHJhdGF0YS10YXRhLXBhbS1wYW0= password: ZGItc2VjcmV0LXBhc3N3b3Jk kind: Secret metadata: creationTimestamp: "2023-04-09T11:25:40Z" labels: app.kubernetes.io/component: secret-sync app.kubernetes.io/managed-by: hashicorp-vso app.kubernetes.io/name: vault-secrets-operator name: test-secret-k8s namespace: test ownerReferences: - apiVersion: secrets.hashicorp.com/v1alpha1 kind: VaultStaticSecret name: vault-secret-app1 uid: 52a97f80-6cd4-472e-b800-97baca522b5a resourceVersion: "339185" uid: 990809a7-8fdc-4070-83e8-e9a1a6e20306 type: Opaque |
как видим появился дополнительный key: new-key-for-test со значением:
1 2 |
echo "dHJhdGF0YS10YXRhLXBhbS1wYW0=" | base64 -d tratata-tata-pam-pam |
как видим теперь у нас создаются секреты и их можно динамически менять, т.е. добавляем/меняем/удаляем значение и секрет в k8s обновляется
9.2.1 Пример с подкидыванием секрета в deployment
Проверим на тестовом деплое, создадим чарт:
helm create test-chart
включаем ingress для нашего metallb
vim test-chart/values.yaml
1 2 3 4 5 6 7 8 9 10 11 12 |
ingress: enabled: true className: "nginx" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific |
он будет подтягиваться через className: "nginx"
так же добавляем в deployment envfrom
1 2 3 4 |
envFrom: - secretRef: name: test-secret-k8s |
общий вид будет такой:
cat test-chart/templates/deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "test-chart.fullname" . }} labels: {{- include "test-chart.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "test-chart.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} annotations: {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "test-chart.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "test-chart.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: {{ .Values.service.port }} protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http resources: {{- toYaml .Values.resources | nindent 12 }} envFrom: - secretRef: name: test-secret-k8s {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} |
ставим:
helm install test-work -n test --values test-chart/values.yaml ./test-chart/
проверяем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
helm list -n test NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION test-work test 1 2023-04-09 08:11:03.181479964 -0400 EDT deployed test-chart-0.1.0 1.16.0 kubectl get pod -n test NAME READY STATUS RESTARTS AGE test-work-test-chart-7f859d7bb9-rs8g2 1/1 Running 0 46m kubectl get ingress -n test NAME CLASS HOSTS ADDRESS PORTS AGE test-work-test-chart nginx chart-example.local 192.168.1.191 80 46m kubectl get serviceaccounts -n test NAME SECRETS AGE default 0 134m test-serviceaccaunt 0 109m test-work-test-chart 0 47m kubectl exec -ti -n test test-work-test-chart-7f859d7bb9-rs8g2 env | grep -E 'password|new-key-for-test' password=db-secret-password _raw={"new-key-for-test":"tratata-tata-pam-pam","password":"db-secret-password"} new-key-for-test=tratata-tata-pam-pam |
как видим внутри нашего контейнера наш секрет с 2мя переменными new-key-for-test password и дополнительно _raw
добавляем ещё одну переменную test проверяем что секрет обновился:
1 2 3 4 5 6 7 8 |
kubectl get secret -n test test-secret-k8s -o yaml apiVersion: v1 data: _raw: eyJuZXcta2V5LWZvci10ZXN0IjoidHJhdGF0YS10YXRhLXBhbS1wYW0iLCJwYXNzd29yZCI6ImRiLXNlY3JldC1wYXNzd29yZCIsInRlc3QiOiJ0ZXN0In0= new-key-for-test: dHJhdGF0YS10YXRhLXBhbS1wYW0= password: ZGItc2VjcmV0LXBhc3N3b3Jk test: dGVzdA== |
как видим всё ок.
но внутри контейнера этой переменной нет:
1 2 3 4 5 |
kubectl exec -ti -n test test-work-test-chart-7f859d7bb9-rs8g2 env | grep -E 'password|new-key-for-test|test' password=db-secret-password _raw={"new-key-for-test":"tratata-tata-pam-pam","password":"db-secret-password"} new-key-for-test=tratata-tata-pam-pam |
можно поставить reloader НО можно просто настроить vault operator а именно rolloutRestartTargets
9.2.2 Включение Vault Secrets Operator reloader
смотрим его описание:
kubectl explain VaultStaticSecret.spec.rolloutRestartTargets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
KIND: VaultStaticSecret VERSION: secrets.hashicorp.com/v1alpha1 RESOURCE: rolloutRestartTargets <[]Object> DESCRIPTION: RolloutRestartTargets should be configured whenever the application(s) consuming the Vault secret does not support dynamically reloading a rotated secret. In that case one, or more RolloutRestartTarget(s) can be configured here. The Operator will trigger a "rollout-restart" for each target whenever the Vault secret changes between reconciliation events. All configured targets wil be ignored if HMACSecretData is set to false. See RolloutRestartTarget for more details. RolloutRestartTarget provides the configuration required to perform a rollout-restart of the supported resources upon Vault Secret rotation. The rollout-restart is triggered by patching the target resource's 'spec.template.metadata.annotations' to include 'vso.secrets.hashicorp.com/restartedAt' with a timestamp value of when the trigger was executed. E.g. vso.secrets.hashicorp.com/restartedAt: "2023-03-23T13:39:31Z" Supported resources: Deployment, DaemonSet, StatefulSet FIELDS: kind <string> -required- name <string> -required- |
как видим на момент 23,03,2023 поддерживаются деплоймент демонсет и стейтфулсет
чтобы это использовать добавим в VaultStaticSecret дополнительное поле rolloutRestartTargets
но предварительно глянем как называется наш деплоймент:
1 2 3 4 |
kubectl get deployments.apps -n test NAME READY UP-TO-DATE AVAILABLE AGE test-work-test-chart 1/1 1 1 71m |
cat vault-secret.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: secrets.hashicorp.com/v1alpha1 kind: VaultStaticSecret metadata: namespace: test name: vault-secret-app1 spec: vaultAuthRef: vaultauth-app1 mount: test/secret type: kv-v1 name: namespace-test/first-app refreshAfter: 10s destination: create: true name: test-secret-k8s type: Opaque rolloutRestartTargets: - kind: Deployment name: test-work-test-chart |
применяем:
kubectl apply -f vault-secret.yaml -n test
проверяем:
1 2 3 4 5 |
kubectl exec -ti -n test test-work-test-chart-897949758-wpmmf env | grep -E 'password|new-key-for-test|test' password=db-secret-password _raw={"new-key-for-test":"tratata-tata-pam-pam","password":"db-secret-password"} new-key-for-test=tratata-tata-pam-pam |
как видим наша новая переменная так и не появилась, это потому что после аплая VaultStaticSecret мы не меняли секрет в vault
идём в vault и добавляем новую переменную
в этот же момент смотрим что происходит в нашем неймспейсе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
kubectl get pod -n test -w NAME READY STATUS RESTARTS AGE test-work-test-chart-897949758-wpmmf 1/1 Running 0 8m52s test-work-test-chart-d85f57f76-29bxn 0/1 Pending 0 0s test-work-test-chart-d85f57f76-29bxn 0/1 Pending 0 0s test-work-test-chart-d85f57f76-29bxn 0/1 ContainerCreating 0 1s test-work-test-chart-d85f57f76-29bxn 0/1 ContainerCreating 0 1s test-work-test-chart-d85f57f76-29bxn 0/1 Running 0 2s test-work-test-chart-d85f57f76-29bxn 1/1 Running 0 2s test-work-test-chart-897949758-wpmmf 1/1 Terminating 0 9m7s test-work-test-chart-897949758-wpmmf 1/1 Terminating 0 9m7s test-work-test-chart-897949758-wpmmf 0/1 Terminating 0 9m8s test-work-test-chart-897949758-wpmmf 0/1 Terminating 0 9m8s test-work-test-chart-897949758-wpmmf 0/1 Terminating 0 9m8s |
как видим старый контейнер успешно удалён а новый стартанул, проверим его env
1 2 3 4 5 6 7 |
kubectl exec -ti -n test test-work-test-chart-d85f57f76-29bxn env | grep -E 'password|new-key-for-test|test|rrr' test=test _raw={"new-key-for-test":"tratata-tata-pam-pam","password":"db-secret-password","rrr":"rrrrrrrrr","test":"test"} new-key-for-test=tratata-tata-pam-pam password=db-secret-password rrr=rrrrrrrrr |
как видим всё работает.
10.SSH аутентификация через vault
Если пользователь хочет подключиться по SSH к удаленному компьютеру, ему необходимо аутентифицировать хранилище.
далее он делает запрос к vault получает ключ и с ним уже может подключиться к нужному серверу: Общая схема такая:
10.1 SSH аутентификация через vault OTP (временный пароль)
Наша конфигурация
1- Vault server (IP= 192.168.1.191) vault.test.ru
2- Our server (IP= 192.168.1.180) сервер на который мы сможем подключаться используя временный пароль
логинимся в vault
kubectl exec -ti -n vault vault-0 sh
vault secrets enable ssh
создаём роль otp_key_role
1 |
vault write ssh/roles/otp_key_role key_type=otp default_user=ubuntu cidr_list=0.0.0.0/0 |
"cidr_list": "0.0.0.0/0" - указывает диапазон IP-адресов, из которых разрешен доступ.
"key_type": "otp" - указывает, что SSH-ключи должны быть подписаны временным паролем OTP.
"default_user": "ubuntu" - указывает, что при использовании этой роли, пользователи будут входить в систему с именем "ubuntu"
переходим в директорию /tmp так как там разрешена запись
cd /tmp/
создаём policy
1 2 3 4 5 |
cat > our-policy.hcl path "ssh/creds/otp_key_role" { capabilities = ["create", "read", "update"] } |
применяем policy
vault policy write our-policy ./our-policy.hcl
включаем метод аутентификации по паролю
vault auth enable userpass
теперь для метода userpass пользователю ubuntu вешаем нашу политику our-policy
vault write auth/userpass/users/ubuntu password="somepass" policies="our-policy"
переходим к серверу на который мы будем заходить:
wget https://releases.hashicorp.com/vault-ssh-helper/0.2.1/vault-ssh-helper_0.2.1_linux_amd64.zip
unzip -q vault-ssh-helper_0.2.1_linux_amd64.zip -d /usr/local/bin
chmod 0755 /usr/local/bin/vault-ssh-helper
chown root:root /usr/local/bin/vault-ssh-helper
mkdir /etc/vault-ssh-helper.d/
1 2 3 4 5 6 7 8 |
tee /etc/vault-ssh-helper.d/config.hcl <<EOF vault_addr = "https://vault.test.ru" tls_skip_verify = true #ca_cert = "<PEM_ENCODED_CA_CERT>" ssh_mount_point = "ssh" #namespace = "my_namespace" allowed_roles = "*" EOF |
теперь настраиваем SSHd
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
vim /etc/ssh/sshd_config
1 2 3 |
ChallengeResponseAuthentication yes UsePAM yes PasswordAuthentication no |
systemctl restart sshd
Centos
1 2 3 4 5 6 7 8 9 |
cp /etc/pam.d/sshd /etc/pam.d/sshd.orig vim /etc/pam.d/sshd ## add these lines and comment out the specified line: auth requisite pam_exec.so quiet expose_authtok log=/var/log/vault-ssh.log /usr/local/bin/vault-ssh-helper -config=/etc/vault-ssh-helper.d/config.hcl auth optional pam_unix.so not_set_pass use_first_pass nodelay #auth substack password-auth |
Ubuntu
1 2 3 4 5 6 7 8 9 |
cp /etc/pam.d/sshd /etc/pam.d/sshd.orig vim /etc/pam.d/sshd ## add these lines and comment out the specified line: auth requisite pam_exec.so quiet expose_authtok log=/var/log/vault-ssh.log /usr/local/bin/vault-ssh-helper -config=/etc/vault-ssh-helper.d/config.hcl auth optional pam_unix.so not_set_pass use_first_pass nodelay #@include common-auth |
верифицируем конфигурацию и добавим пользователя:
vault-ssh-helper -verify-only -config /etc/vault-ssh-helper.d/config.hcl
1 2 3 4 |
root@nfs:~# vault-ssh-helper -verify-only -config /etc/vault-ssh-helper.d/config.hcl 2023/04/16 05:53:40 [INFO] using SSH mount point: ssh 2023/04/16 05:53:40 [INFO] using namespace: 2023/04/16 05:53:40 [INFO] vault-ssh-helper verification successful! |
добавляем пользователя пароль можем задать 123
adduser ubuntu
настройка завершена, теперь возвращаемся волт сервер
kubectl exec -ti -n vault vault-0 sh
vault login -method=userpass username=ubuntu password=somepass
1 2 3 4 5 6 7 8 9 10 11 |
Key Value --- ----- token s.NLXE57eJvHNbSro7lewcKXdI token_accessor 1XewtnGnwZ0Id1mji2o6fqXw token_duration 768h token_renewable true token_policies ["default" "our-policy"] identity_policies [] policies ["default" "our-policy"] token_meta_username ubuntu |
создаём токен(временный пароль)
vault write ssh/creds/otp_key_role ip=192.168.1.180
1 2 3 4 5 6 7 8 9 10 11 |
Key Value --- ----- lease_id ssh/creds/otp_key_role/Z52V3xPglmlJBVGTaKnuxJMP lease_duration 768h lease_renewable false ip 192.168.1.180 key e4f6d3ee-ce0c-d13d-a20d-0e3b9e1a6f55 key_type otp port 22 username ubuntu |
в поле key мы получаем пароль, его и используем для аутентификации
всё можем логиниться
ssh ubuntu@192.168.1.180
в качестве пароля используем key e4f6d3ee-ce0c-d13d-a20d-0e3b9e1a6f55
вот лог сервера на который логинимся:
1 2 3 4 5 6 7 8 9 |
Apr 16 06:02:24 nfs sshd[1183]: pam_unix(sshd:auth): unrecognized option [not_set_pass] Apr 16 06:02:24 nfs sshd[1183]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.1.171 user=ubuntu Apr 16 06:02:24 nfs sshd[1181]: Accepted keyboard-interactive/pam for ubuntu from 192.168.1.171 port 45432 ssh2 Apr 16 06:02:24 nfs sshd[1181]: pam_unix(sshd:setcred): unrecognized option [not_set_pass] Apr 16 06:02:24 nfs sshd[1181]: pam_unix(sshd:session): session opened for user ubuntu(uid=1002) by (uid=0) Apr 16 06:02:24 nfs systemd-logind[422]: New session 13 of user ubuntu. Apr 16 06:02:24 nfs systemd: pam_unix(systemd-user:session): session opened for user ubuntu(uid=1002) by (uid=0) Apr 16 06:02:24 nfs sshd[1200]: pam_unix(sshd:setcred): unrecognized option [not_set_pass] |
ВАЖНОЕ!!!!!!!!!!
данный пароль работает только на 1 раз, при следующей аутентификации нужно будет запрашивать новый токен. при старом токене возникает следующая ошибка при попытке авторизоваться:
1 2 3 |
Apr 16 06:04:37 nfs sshd[1209]: pam_exec(sshd:auth): /usr/local/bin/vault-ssh-helper failed: exit code 1 Apr 16 06:04:37 nfs sshd[1207]: error: PAM: System error for ubuntu from 192.168.1.171 |
чтобы логиниться в vault с тачек нужно скачать бинарник:
wget https://releases.hashicorp.com/vault/1.13.0/vault_1.13.0_linux_amd64.zip
unzip -q vault_1.13.0_linux_amd64.zip -d /usr/local/bin/
логинимся
vault login -address=https://vault.test.ru -tls-skip-verify -method=userpass username=ubuntu password=somepass
запрашиваем OTP(временный пароль)
vault write -address=https://vault.test.ru -tls-skip-verify ssh/creds/otp_key_role ip=192.168.1.180
получаем пароль:
1 2 3 4 5 6 7 8 9 10 11 |
Key Value --- ----- lease_id ssh/creds/otp_key_role/HFydXDyDD3PJo2s6puF1XPsR lease_duration 768h lease_renewable false ip 192.168.1.180 key 30dabbcf-b34a-3ead-a51b-ff52a6c672bb key_type otp port 22 username ubuntu |
с которым можем логиниться на сервер 192.168.1.180
10.2 SSH аутентификация через vault SSH (certificate authority)
создаём секрет и пишем в него публичный ключ:
vault secrets enable -path=ssh-user-ca ssh
vault write ssh-user-ca/config/ca generate_signing_key=true
потом если что можем посмотреть этот ключ следующим образом:
vault read -field=public_key ssh-user-ca/config/ca
теперь создаём роль для пользователя test
1 2 3 4 5 6 7 8 9 10 11 12 13 |
vault write ssh-user-ca/roles/ssh-test - <<"EOH" { "algorithm_signer": "rsa-sha2-256", "allow_user_certificates": true, "allowed_users": "test", "default_extensions": { "permit-pty": "" }, "key_type": "ca", "max_ttl": "5m", "ttl": "5m" } EOH |
время жизни этого сертификата задаём в 5 минут
создаём policy
1 2 3 4 5 |
vault policy write ssh-user - <<"EOH" path "ssh-user-ca/sign/ssh-test" { capabilities = ["update"] } EOH |
прикрепляем эту policy к нашему пользователю:
(ВАЖНО!!!!!! необходимо указывать все policy иначе новая затрёт все старые)
vault write auth/userpass/users/myuser policies="default","my-policy","ssh-user"
теперь смотрим сертификат который надо добавить на целевую тачку к которой будем подключаться:
1 2 3 |
vault read -field=public_key ssh-user-ca/config/ca ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDAar6DHRA6+RbzNzgoxSqFKxPoRRSV+dpgOt43pqVySMsJxhIZagALqLgUK0fCVyEXurjR3AwSKRCM0EGtPg6igRy/u4MWJxOHB924TAMessl9QdUdSMEXW/WGfE29qOdla58Kfan9wQ6N9JkTFBl0f16xZaw7PChuuUSrh+b77kAp+xkUyPSRYZOdebgrqamHhsm3U126laiLD64knaZjwfxvpju5PnpccGcHqOLKqr3dK8gU45m3veOd7qn9Oy+ixq4VUddEqbF20njdz/IdKHMHC6izaVFgh/j/kYa/43CEtsOFTAdnH5HHXVQ8WXz3BVvjOmRJx4fnjDxZk761AALUGu90GV8PhLP4uBe+d6Ii4ay7Cumrfg5oV+yLzncw39nfVY598j0lGumJKXqEGmiBLGVAUROikPBOTkAOpqO4qqDs+9d4knqjCvfjleOTvpje8TXIXLjKdprcQxgbanNoJ3HXzKj8D6WyEf/27DF7iyLt+qBKwW3yNv7Nr87uKPd/T3CJs2msbYuLuldr9Xd7UI6MSiJLxdSSpCyIYB8hL99g7VOrnVYkhObk2aZVnjdIXhmzPsNNDX56kX8o2yk9fv0PfvG1fVg8MLjoCtK+RJ2fVvLyxvYlyKEPKCjclwHwJ62RFhN60hDSasQXXbCfMsrKWhD999c6w57LOQ== |
вот в этот файл:
/etc/ssh/ssh_ca.pub
меняем для него права
chmod 0600 /etc/ssh/ssh_ca.pub
так же правим файл:
/etc/ssh/sshd_config
и в самый конец добавляем:
TrustedUserCAKeys /etc/ssh/ssh_ca.pub
а так же добавляем пользователя:
useradd -m test -s /bin/bash
рестартуем sshd
systemctl restart ssh
с ЦЕЛЕВОЙ тачкой(куда будем подключаться закончили)
Теперь настроем тачку с которой будем подключаться:
wget https://releases.hashicorp.com/vault/1.13.0/vault_1.13.0_linux_amd64.zip
unzip -q vault_1.13.0_linux_amd64.zip -d /usr/local/bin/
добавляем в хосты наш vault
cat /etc/hosts
192.168.1.191 vault.test.ru
логинимся:
vault login -address=https://vault.test.ru -tls-skip-verify -method=userpass username=myuser password=mypassword
1 2 3 4 5 6 7 8 9 10 11 |
Key Value --- ----- token s.exj7Kk8EHndCpkfuLTDJ6H4c token_accessor FaubH8o5F7yOdmPJlYhZ5nLX token_duration 768h token_renewable true token_policies ["default" "my-policy" "ssh-user"] identity_policies [] policies ["default" "my-policy" "ssh-user"] token_meta_username myuser |
как видим в policy есть наша политика ssh-user
vault write -address=https://vault.test.ru -tls-skip-verify -field=signed_key ssh-user-ca/sign/ssh-test public_key=@$HOME/.ssh/id_rsa.pub valid_principals="test" >test.cert
рассмотрим параметры:
-field=signed_key ssh-user-ca/sign/ssh-test - тут мы подписываем сертификат и как раз к нему мы в policy доступ и давали.
public_key=@$HOME/.ssh/id_rsa.pub - тут мы указываем путь до нашего публичного ключа
valid_principals - используется для ограничения списка разрешенных пользователей или групп, которые могут использовать SSH сертификат для аутентификации на удаленном хосте
меняем права на полученном сертификате:
chmod 600 test.cert
всё теперь можно подключаться к целевой тачке:
ssh test@192.168.1.180 -i test.cert -i ~/.ssh/id_rsa
тут test.cert сертификат который мы получили из vault
~/.ssh/id_rsa наш публичный сертификат
в логе /var/log/auth.log на целевой тачке мы видим:
1 2 |
sshd[901]: Accepted publickey for test from 192.168.1.170 port 43204 ssh2: RSA-CERT SHA256:965CIUM+FC5jAhGjzAkzCTheBAdCYie8WomlZr8o/tc ID vault-userpass-myuser-f7ae4221433e142e630211a3cc093309385e0407426227bc5a89a566bf28fed7 (serial 17716426715752054390) CA RSA SHA256:5EQYyjrxkiHbIn8NliMLQllGW6YP/uA3RaE9kGplqhQ |
всё ок.
напомню что когда мы создавали role ssh-test мы задали время жизни 5 минут.
спустя 5 минут если мы вновь пытаемся подключиться то ничего не выйдет, так как ttl истёк:
ssh test@192.168.1.180 -i test.cert -i ~/.ssh/id_rsa
test@192.168.1.180's password:
на целевой тачке в логе мы видим:
sshd[918]: error: Certificate invalid: expired
всё ок.
так и задумывалось.
чтобы вновь подключиться нам нужно перезапросить сертификат:
1 2 3 4 5 |
root@debian:~# vault write -address=https://vault.test.ru -tls-skip-verify -field=signed_key ssh-user-ca/sign/ssh-test public_key=@$HOME/.ssh/id_rsa.pub valid_principals="test" >test.cert root@debian:~# ssh test@192.168.1.180 -i test.cert -i ~/.ssh/id_rsa Last login: Sun Apr 23 09:20:40 2023 from 192.168.1.170 test@nfs:~$ |
посмотреть какие роли назначены на секрет можно командой:
1 2 3 4 5 |
vault list ssh-user-ca/roles Keys ---- ssh-test |
посмотреть эту роль можно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
vault read ssh-user-ca/roles/ssh-test Key Value --- ----- algorithm_signer rsa-sha2-256 allow_bare_domains false allow_host_certificates false allow_subdomains false allow_user_certificates true allow_user_key_ids false allowed_critical_options n/a allowed_domains n/a allowed_extensions n/a allowed_user_key_lengths map[] allowed_users test allowed_users_template false default_critical_options map[] default_extensions map[permit-pty:] default_extensions_template false default_user n/a key_bits 0 key_id_format n/a key_type ca max_ttl 5m ttl 5m |