Автоматическое распечатывание волта
При инициализации волт кластера мы получаем root токен и 5 ключей, они необходимы для распечатывания кластера в случае его падения, делать это можно в ручную, но делать это ночью, если вдруг поды упали, не совсем удобно…
Для автоматичечкого распечатывания можно ипользовать различные KMS сервисы от облачных провайдеров, подробнее почитать об этом можно в официальной документации hashicorp
В этой статье мы настроим автоматический unseal “боевого” кластера с ипользованием промежуточного волта в реалиях Yandex Cloud
Проблема которую мы будем решать
Яндекс предоставляет KMS который можно использовать для автоматического распечатывания волта в случае падения, в таком случае приходится использовать не официальный контейнер, а тот который предоставляет Яндекс, если вас это устраивает, на этом можно остановиться
В таком случае вы сможете использовать только ту версию волта которую предоставляет Яндекс и зависить от их политики обновлений
Мы будем использовать transit engine, как это работает:
- Будем использовать два кластера волта
- Первый будет использоваться только для распечатывания второго (боевого), сам по себе он будет распечатываться с помощью Yandex KMS
- Второй кластер боевой, который вы используете в проде (обновляя и конфигурируя его по своему усмотрению, без зависимости от Yandex Cloud)
Настройка первого (unwrap) кластера
Unwrap кластер ипользуется для распечатывания боевого кластера, а сам распечатывается средствами Яндекса
Получаем креды для распечатывания unwrap волта
yc iam service-account create --name vault-unwrap-sa
yc iam key create \
--service-account-name vault-unwrap-sa \
--output key.json
yc kms symmetric-key create \
--name vault-unwrap \
--default-algorithm aes-256 \
--deletion-protection \
--description "Ключ для vault-unwrap разворачивающего основной vault в prod"
# Сохраните идентификатор ключа (id) — он понадобится при установке приложения.
yc kms symmetric-key add-access-binding \
--name prod-vault-unwrap \
--service-account-name prod-vault-unwrap-sa \
--role kms.keys.encrypterDecrypter
Деплоим волт так как вы привыкли, хельм чарт и все параметры доступны на Github
Используем image от Yandex Cloud, что бы корректно работал KMS
image:
repository: "cr.yandex/yc/vault"
tag: "1.11.5_yckms"
pullPolicy: IfNotPresent
Готовим базу данных
В качестве базы данных волт может использовать почти все, что угодно, мы будем использовать PostgreSQL, для корректной работы базу данных нужно немножко подготовить
При выборе PostgreSQL в качестве бекенда, таблички не будут созданы автоматически, создать их можно в ручную
CREATE TABLE vault_kv_store (
parent_path TEXT COLLATE "C" NOT NULL,
path TEXT COLLATE "C",
key TEXT COLLATE "C",
value BYTEA,
CONSTRAINT pkey PRIMARY KEY (path, key)
);
CREATE INDEX parent_path_idx ON vault_kv_store (parent_path);
Табличка для HA мода
CREATE TABLE vault_ha_locks (
ha_key TEXT COLLATE "C" NOT NULL,
ha_identity TEXT COLLATE "C" NOT NULL,
ha_value TEXT COLLATE "C",
valid_until TIMESTAMP WITH TIME ZONE NOT NULL,
CONSTRAINT ha_key PRIMARY KEY (ha_key)
);
Если используется PostgreSQL до 9.5, создайте функцию
CREATE FUNCTION vault_kv_put(_parent_path TEXT, _path TEXT, _key TEXT, _value BYTEA) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE vault_kv_store
SET (parent_path, path, key, value) = (_parent_path, _path, _key, _value)
WHERE _path = path AND key = _key;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO vault_kv_store (parent_path, path, key, value)
VALUES (_parent_path, _path, _key, _value);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
Конфиг кластера должен выглядеть следующим образом
extraconfig-from-values.hcl: |-
disable_mlock = true
ui = true
listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
}
storage "postgresql" {
connection_url = "postgres://<USER>:<PASSWORD>@<HOSTS>:6432/vault?sslmode=require"
table = "vault_kv_store"
ha_enabled = true
ha_table = "vault_ha_locks"
tls_ca_file = "/etc/ssl/certs/ca-yandex.pem"
}
seal "yandexcloudkms" {
kms_key_id = ""
service_account_key_file = "/vault/ssl/key.json"
}
tls_ca_file - публичный сертификат для подлючения к managed сервисам с SSL kms_key_id - id ключа который мы получили в первом шаге service_account_key_file - получаем при создании аккаунта в первом шаге
Инициализируем кластер
Для инициализации достаточно запустить vault operator init
в любом поде, если все настроенно верно, вывод будет следующий
Recovery Key 1: iz1XWxe4CM+wrOGqRCx8ex8kB2XvGJEdfjhXFC+MA6Rc
Recovery Key 2: rKZETr6IAy686IxfO3ZBKXPDAOkkwkpSepIME+bjeUT7
Recovery Key 3: 4XA/KJqFOm+jzbBkKQuRVePEYPrQe3H3TmFVmdlUjRFv
Recovery Key 4: lfnaYoZufP0uhooO3mHDAKGNZB5HLP9HYYb+LAfKkUmd
Recovery Key 5: L169hHj3DMpphGsOnS8TEz3Febvdx3vsG3Xr8kGWdUtW
Initial Root Token: s.AWnDagUkKNNbvkENiL72wysn
Success! Vault is initialized
Recovery key initialized with 5 key shares and a key threshold of 3. Please
securely distribute the key shares printed above.
Для автоматического распечатывания, первый раз кластер нужно распечатать вручную vault operator unseal <key>
Вводим 3 ключа, кластер будет распечатан
Теперь при падении подов кластер будет распечатываться автоматически
Включаем transit engine
Все настройки описанные ниже можно выполнить с помощью terraform, я опишу более простой способ (через консоль), все команды достаточно выполнить в одном поде
- Включаем transit
vault secrets enable transit
- Создаем ключ шифрования
vault write -f transit/keys/autounseal
- Создаем полиси
vault policy write autounseal -<<EOF
path "transit/encrypt/autounseal" {
capabilities = [ "update" ]
}
path "transit/decrypt/autounseal" {
capabilities = [ "update" ]
}
EOF
Если все сделанно правильно вывод должен быть следующим
Success! Uploaded policy: autounseal
- Создаем
orphan periodic
токен с политикойautounseal
и TTL 120 сек, использовать его мы будем когда будет готов боевой прод волт, поэтому создать его можно и позже
#Логинимся
$ vault login
#Генерим токен в соответствии с политикой
$ vault token create -policy="autounseal" -wrap-ttl=120 -orphan -period=30m
Key Value
--- -----
wrapping_token: s.AO55UYmKpT4TmPVn2AZ0UkNT
wrapping_accessor: 7N3PtIwhuAtjJZ2GvA9RM0iV
wrapping_token_ttl: 2m
wrapping_token_creation_time: 2019-06-25 19:24:46.345501602 +0000 UTC
wrapping_token_creation_path: auth/token/create
wrapped_accessor: B8kWfoLfXYOAxFI2P2TIduaI
#Получаем токен для распечатывания
$ VAULT_TOKEN="s.AO55UYmKpT4TmPVn2AZ0UkNT" vault unwrap
Key Value
--- -----
token s.LvLFn4InVdIorAFS5E9j6xd3
......
Настройка второго (prod) кластера
Деплоим волт
Все точно так же как для unwrap кластера, только в другой неймспейс
Готовим базу данных
Все точно так же как для unwrap кластера
Инициализируем кластер
Здесь все тоже без изменений, так же как для unwrap
Конфиг кластера должен выглядеть следующим образом(добавляем seal transit)
seal "transit" {
address = "http://vault.vault-unwrap-system:8200"
token = "s.LvLFn4InVdIorAFS5E9j6xd3"
disable_renewal = "false"
key_name = "autounseal"
mount_path = "transit/"
tls_skip_verify = "true"
}
Если все сделано правильно волт перейдет в режим миграции ( это видно в логах )
core: entering seal migration mode; Vault will not automatically unseal even if using an autoseal: from_barrier_type=shamir to_barrier_type=transit
Распечатываем волт в режиме миграции ( достаточно проделать это с одним подом )
vault operator unseal -migrate <key>
Вводим три ключа для распечатывания продового вольта, после этого все поды поднимутся и будут распечатаны.
Таким образом unwrap vault распечатывается с помощью Яндекс kms, а боевой с помощью vault unwrap Проверяем - убиваем под в промежуточном и основном вольте и проверяем что они автоматически распечатались.
Если по какой-то причине боевой волт не может достучаться до unwrap волта в течении 30 минут, токен не будет обновлен и волт упадет с ошибкой
| Error making API request.
|
| URL: PUT http://vault.vault-unwrap-system:8200/v1/auth/token/renew-self
| Code: 403. Errors:
|
| * permission denied
Время в 30 минут устанавливается здесь:
vault token create -policy="autounseal" -wrap-ttl=120 -orphan -period=30m
В таком случае необходимо обновить токен в unwrap волте и подложить его в конфигмап боевого волта, либо увеличить -period
до удобного вам значения.