diff --git a/docs/FAQ.md b/docs/FAQ.md deleted file mode 100644 index ff90316..0000000 --- a/docs/FAQ.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -title: "The secrets-store-integration module: FAQ" -description: Hashicorp Vault configuration examples. Example of secret autorotation implementation. ---- - -## How to set up the Hashicorp Vault as a secret store to use with the secrets-store-integration module: - -{{< alert level="info">}} -First of all, you'll need a root or similiar token and the vault address. -You can get such a root token while initializing a new secrets store. - - -```bash -export VAULT_TOKEN=xxxxxxxxxxx -export VAULT_ADDR=https://secretstoreexample.com -``` -{{< /alert >}} - -> This guide will cover two ways to do this: -> * using the console version of HashiCorp Vault (see the [Vault installation guide](https://developer.hashicorp.com/vault/docs/install)); -> * using curl to make direct requests to the secrets store API. - -This section provides an example of the settings that need to be made so that the service pod can access the secret located in the Key-Value storage. The secret will be the password for the database that the Python application uses. - -* Enable and create the Key-Value store: - - ```bash - vault secrets enable -path=secret -version=2 kv - ``` - - The same command as a curl HTTP request: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kv","options":{"version":"2"}}' \ - ${VAULT_ADDR}/v1/sys/mounts/secret - ``` - -* Set the database password as the secret value: - - ```bash - vault kv put secret/database-for-python-app password="db-secret-password" - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"data":{"password":"db-secret-password"}}' \ - ${VAULT_ADDR}/v1/secret/data/database-for-python-app - ``` - -* Double-check that the password has been saved successfully: - - ```bash - vault kv get secret/database-for-python-app - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - ${VAULT_ADDR}/v1/secret/data/database-for-python-app - ``` - -* Set the authentication path (`authPath`) and enable authentication and authorization in Vault using the Kubernetes API: - - ```bash - vault auth enable -path=main-kube kubernetes - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kubernetes"}' \ - ${VAULT_ADDR}/v1/sys/auth/main-kube - ``` - -* If you have more than one cluster, set the authentication path (`authPath`) and enable authentication and authorization in Vault using the Kubernetes API of the second cluster: - - ```bash - vault auth enable -path=secondary-kube kubernetes - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kubernetes"}' \ - ${VAULT_ADDR}/v1/sys/auth/secondary-kube - ``` - -* Set the Kubernetes API address for each cluster (in this case, it is the K8s's API server service): - - ```bash - vault write auth/main-kube/config \ - kubernetes_host="https://api.kube.my-deckhouse.com" - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"kubernetes_host":"https://api.kube.my-deckhouse.com"}' \ - ${VAULT_ADDR}/v1/auth/main-kube/config - ``` - For another cluster: - - ```bash - vault write auth/secondary-kube/config \ - kubernetes_host="https://10.11.12.10:443" - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"kubernetes_host":"https://10.11.12.10:443"}' \ - ${VAULT_ADDR}/v1/auth/secondary-kube/config - ``` - -* Create a policy in Vault called "backend" that allows reading of the `database-for-python-app` secret: - - ```bash - vault policy write backend - <}} - **Important!** - In addition to the Vault side settings, you must configure the authorization permissions of the `serviceAccount` used in the kubernetes cluster. - See the [FAQ](faq.html#how-to-allow-serviceaccount-to-authorize-in-vault) section for details. - {{< /alert >}} - - ```bash - vault write auth/main-kube/role/my-namespace1_backend \ - bound_service_account_names=backend-sa \ - bound_service_account_namespaces=my-namespace1 \ - policies=backend \ - ttl=10m - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"bound_service_account_names":"backend-sa","bound_service_account_namespaces":"my-namespace1","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/main-kube/role/my-namespace1_backend - ``` - - Do the same for the second K8s cluster: - - ```bash - vault write auth/secondary-kube/role/my-namespace1_backend \ - bound_service_account_names=backend-sa \ - bound_service_account_namespaces=my-namespace1 \ - policies=backend \ - ttl=10m - ``` - - The curl equivalent of the above command: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"bound_service_account_names":"backend-sa","bound_service_account_namespaces":"my-namespace1","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/secondary-kube/role/my-namespace1_backend - ``` - {{< alert level="info">}} - **Important!** - The recommended TTL value of the Kubernetes token is 10m. - {{< /alert >}} - -These settings allow any pod within the `my-namespace1` namespace in both K8s clusters that uses the `backend-sa` ServiceAccount to authenticate, authorize, and read secrets in the Vault according to the `backend` policy. - -## How to allow a ServiceAccount to log in to Vault? - -To log in to Vault, a k8s pod uses a token generated for its ServiceAccount. In order for Vault to be able to check the validity of the ServiceAccount data provided by the service, Vault must have permission to `get`, `list`, and `watch` for the `tokenreviews.authentication.k8s.io` and `subjectaccessreviews.authorization.k8s.io` endpoints. You can also use the `system:auth-delegator` clusterRole for this. - -Vault can use different credentials to make requests to the Kubernetes API: -1. Use the token of the application that is trying to log in to Vault. In this case, each service that logs in to Vault must have the `system:auth-delegator` clusterRole (or the API rights listed above) in the ServiceAccount it uses. -2. Use a static token created specifically for Vault `ServiceAccount` that has the necessary rights. Setting up Vault for this case is described in detail in [Vault documentation](https://developer.hashicorp.com/vault/docs/auth/kubernetes#continue-using-long-lived-tokens). - -## How to autorotate secrets mounted as files in containers without restarting them? - -The autorotation feature of the secret-store-integration module is enabled by default. Every two minutes, the module polls Vault and synchronizes the secrets in the mounted file if it has been changed. - -Create the ```backend-sa``` ServiceAccount - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: backend-sa - namespace: my-namespace1 -``` - -Below is an example of the SecretStoreImport definition: - -```yaml -apiVersion: deckhouse.io/v1alpha1 -kind: SecretsStoreImport -metadata: - name: python-backend - namespace: my-namespace1 -spec: - type: CSI - role: my-namespace1_backend - files: - - name: "db-password" - source: - path: "secret/data/database-for-python-app" - key: "password" -``` - -In the `backend` example below, the SecretStoreImport (defined above) is mounted as a volume to push the database password to the application: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: backend - namespace: my-namespace1 - labels: - app: backend -spec: - selector: - matchLabels: - app: backend - template: - metadata: - labels: - app: backend - spec: - serviceAccountName: backend-sa - containers: - - image: some/app:0.0.1 - name: backend - volumeMounts: - - name: secrets - mountPath: "/mnt/secrets" - volumes: - - name: secrets - csi: - driver: secrets-store.csi.deckhouse.io - volumeAttributes: - secretsStoreImport: "python-backend" -``` - -Once these resources have been applied, a `backend` pod will be started. In it, there will be a `/mnt/secrets` directory with the `secrets` volume mounted. The directory will contain a `db-password` file with the password for the Vault database. - -There are two ways to keep track of changes to the secret file in the pod. The first is to keep track of when the mounted file changes (mtime), reacting to changes in the file. The second is to use the inotify API, which provides a mechanism for subscribing to file system events. Inotify is part of the Linux kernel. Once a change is detected, there are a large number of options for responding to the change event, depending on the application architecture and programming language used. The most simple one is to force K8s to restart the pod by failing the liveness probe. - -Here is how you can use inotify in a Python application leveraging the `inotify` Python package: - -```python -#!/usr/bin/python3 - -import inotify.adapters - -def _main(): - i = inotify.adapters.Inotify() - i.add_watch('/mnt/secrets-store/db-password') - - for event in i.event_gen(yield_nones=False): - (_, type_names, path, filename) = event - - if 'IN_MODIFY' in type_names: - print("file modified") - -if __name__ == '__main__': - _main() -``` - -Sample code to detect whether a password has been changed within a Go application using inotify and the `inotify` Go package: - -```python -watcher, err := inotify.NewWatcher() -if err != nil { - log.Fatal(err) -} -err = watcher.Watch("/mnt/secrets-store/db-password") -if err != nil { - log.Fatal(err) -} -for { - select { - case ev := <-watcher.Event: - if ev == 'InModify' { - log.Println("file modified")} - case err := <-watcher.Error: - log.Println("error:", err) - } -} -``` - -## Secret rotation limitations - -A container that uses the `subPath` volume mount will not get secret updates when the latter is rotated. - -```yaml - volumeMounts: - - mountPath: /app/settings.ini - name: app-config - subPath: settings.ini -... - volumes: - - name: app-config - csi: - driver: secrets-store.csi.deckhouse.io - volumeAttributes: - secretsStoreImport: "python-backend" -``` diff --git a/docs/FAQ_RU.md b/docs/FAQ_RU.md deleted file mode 100644 index 30879ad..0000000 --- a/docs/FAQ_RU.md +++ /dev/null @@ -1,347 +0,0 @@ ---- -title: "Модуль secrets-store-integration: FAQ" -description: Как настроить HashiCorp Vault в качестве secret store. Пример реализации авторотации секретов. ---- - -## Как настроить HashiCorp Vault в качестве secret store для использования с модулем secrets-store-integration? - -{{< alert level="info">}} -Для выполнения дальнейших команд необходим адрес и токен с правами root от Vault. -Такой токен можно получить во время инициализации нового secrets store. - -Далее в командах будет подразумеваться что данные настойки указаны в переменных окружения. -```bash -export VAULT_TOKEN=xxxxxxxxxxx -export VAULT_ADDR=https://secretstoreexample.com -``` -{{< /alert >}} - -> В этом руководстве мы приводим два вида примерных команд: -> * команда с использованием консольной версии HashiCorp Vault ([руководство по установке](https://developer.hashicorp.com/vault/docs/install)); -> * команда с использованием curl для выполнения прямых запросов в API secrets store. - - -В данном разделе в качестве примера приводятся настройки которые необходимо произвести, для того чтобы под сервиса мог получить доступ до секрета, расположенного в Key-Value хранилище. В качестве секрета будет рассмотрен пароль для базы данных который использует приложение на Python. - - -* Включим и создадим Key-Value хранилище: - - ```bash - vault secrets enable -path=secret -version=2 kv - ``` - Команда с использованием curl: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kv","options":{"version":"2"}}' \ - ${VAULT_ADDR}/v1/sys/mounts/secret - ``` - - - -* Зададим пароль базы в качестве значения секрета: - - ```bash - vault kv put secret/database-for-python-app password="db-secret-password" - ``` - Команда с использованием curl: - - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"data":{"password":"db-secret-password"}}' \ - ${VAULT_ADDR}/v1/secret/data/database-for-python-app - ``` - - - -* Проверим, правильно ли записался пароль: - - ```bash - vault kv get secret/database-for-python-app - ``` - - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - ${VAULT_ADDR}/v1/secret/data/database-for-python-app - ``` - - - - -* Задаём путь аутентификации (`authPath`) и включаем аутентификацию и авторизацию в Vault с помощью Kubernetes API: - - ```bash - vault auth enable -path=main-kube kubernetes - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kubernetes"}' \ - ${VAULT_ADDR}/v1/sys/auth/main-kube - ``` - - -* Если требуется настроить доступ для более чем одного кластера, то задаём путь аутентификации (`authPath`) и включаем аутентификацию и авторизацию в Vault с помощью Kubernetes API для каждого кластера кластера: - - ```bash - vault auth enable -path=secondary-kube kubernetes - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request POST \ - --data '{"type":"kubernetes"}' \ - ${VAULT_ADDR}/v1/sys/auth/secondary-kube - ``` - - -* Задаём адрес Kubernetes API для каждого кластера: - - ```bash - vault write auth/main-kube/config \ - kubernetes_host="https://api.kube.my-deckhouse.com" - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"kubernetes_host":"https://api.kube.my-deckhouse.com"}' \ - ${VAULT_ADDR}/v1/auth/main-kube/config - ``` - - - Для другого кластера: - - ```bash - vault write auth/secondary-kube/config \ - kubernetes_host="https://10.11.12.10:443" - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"kubernetes_host":"https://10.11.12.10:443"}' \ - ${VAULT_ADDR}/v1/auth/secondary-kube/config - ``` - - -* Создаём в Vault политику с названием «backend», разрешающую чтение секрета `database-for-python-app`: - - ```bash - vault policy write backend - <}} - **Важно!** - Помимо настроек со стороны Vault, вы должны настроить разрешения авторизации используемых `serviceAccount` в кластере kubernetes. - Подробности в разделе [FAQ](faq.html#как-разрешить-serviceaccount-авторизоваться-в-vault) - {{< /alert >}} - - ```bash - vault write auth/main-kube/role/my-namespace1_backend \ - bound_service_account_names=backend-sa \ - bound_service_account_namespaces=my-namespace1 \ - policies=backend \ - ttl=10m - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"bound_service_account_names":"backend-sa","bound_service_account_namespaces":"my-namespace1","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/main-kube/role/my-namespace1_backend - ``` - - -* Повторяем то же самое для остальных кластеров, указав другой путь аутентификации: - - ```bash - vault write auth/secondary-kube/role/my-namespace1_backend \ - bound_service_account_names=backend-sa \ - bound_service_account_namespaces=my-namespace1 \ - policies=backend \ - ttl=10m - ``` - Команда с использованием curl: - ```bash - curl \ - --header "X-Vault-Token: ${VAULT_TOKEN}" \ - --request PUT \ - --data '{"bound_service_account_names":"backend-sa","bound_service_account_namespaces":"my-namespace1","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/secondary-kube/role/my-namespace1_backend - ``` - - - {{< alert level="info">}} - **Важно!** - Рекомендованное значение TTL для токена Kubernetes составляет 10m. - {{< /alert >}} - -Эти настройки позволяют любому поду из пространства имён `my-namespace1` из обоих K8s-кластеров, который использует ServiceAccount `backend-sa`, аутентифицироваться и авторизоваться в Vault для чтения секретов согласно политике `backend`. - -## Как разрешить ServiceAccount авторизоваться в Vault? - -Для авторизации в Vault pod k8s использует токен сгенерированный для своего ServiceAccount. Для того чтобы Vault мог проверить валидность предоставляемых данных ServiceAccount используемый сервисом, Vault должен иметь разрешение на действия `get`, `list` и `watch` для endpoints `tokenreviews.authentication.k8s.io` и `subjectaccessreviews.authorization.k8s.io`. Для этого также можно использовать clusterRole `system:auth-delegator`. - -Vault может использовать различные авторизационные данные для осуществления запросов в API Kubernetes: -1. Использовать токен приложения, которое пытается авторизоваться в Vault. В этом случае для каждому сервису авторизующейся в Vault требуется в используемом ServiceAccount иметь clusterRole `system:auth-delegator` (либо права на API представленные выше). -2. Использовать статичный токен отдельно созданного специально для Vault `ServiceAccount` у которого имеются необходимые права. Настройка Vault для такого случая подробно описана в [документации Vault](https://developer.hashicorp.com/vault/docs/auth/kubernetes#continue-using-long-lived-tokens). - - -## Как использовать авторотацию секретов, примонтированных как файл в контейнер без его перезапуска? - -Функция авторотации секретов в модуле secret-store-integration включена по умолчанию. Каждые две минуты модуль опрашивает Vault и синхронизирует секреты в примонтированном файле в случае его изменения. - -Создадим ServiceAccount `backend-sa` - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: backend-sa - namespace: my-namespace1 -``` - -Пример CustomResource SecretStoreImport: - -```yaml -apiVersion: deckhouse.io/v1alpha1 -kind: SecretsStoreImport -metadata: - name: python-backend - namespace: my-namespace1 -spec: - type: CSI - role: my-namespace1_backend - files: - - name: "db-password" - source: - path: "secret/data/database-for-python-app" - key: "password" -``` - -Пример Deployment `backend`, который использует указанный выше SecretStoreImport как том, чтоб доставить пароль от базы данных в файловую систему приложения: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: backend - namespace: my-namespace1 - labels: - app: backend -spec: - selector: - matchLabels: - app: backend - template: - metadata: - labels: - app: backend - spec: - serviceAccountName: backend-sa - containers: - - image: some/app:0.0.1 - name: backend - volumeMounts: - - name: secrets - mountPath: "/mnt/secrets" - volumes: - - name: secrets - csi: - driver: secrets-store.csi.deckhouse.io - volumeAttributes: - secretsStoreImport: "python-backend" -``` - -После применения этих ресурсов будет запущен под с названием `backend`, внутри которого будет каталог `/mnt/secrets` с примонтированным внутрь томом `secrets`. Внутри каталога будет лежать файл `db-password` с паролем от базы данных из Vault. - -Есть два варианта следить за изменениями файла с секретом в поде. Первый - следить за временем изменения примонтированного файла, реагируя на его изменение. Второй - использовать inotify API, который предоставляет механизм для подписки на события файловой системы. Inotify является частью ядра Linux. После обнаружения изменений есть большое количество вариантов реагирования на событие изменения в зависимости от используемой архитектуры приложения и используемого языка программирования. Самый простой — заставить K8s перезапустить под, перестав отвечать на liveness-пробу. - -Пример использования inotify в приложении на Python с использованием пакета inotify: - -```python -#!/usr/bin/python3 - -import inotify.adapters - -def _main(): - i = inotify.adapters.Inotify() - i.add_watch('/mnt/secrets-store/db-password') - - for event in i.event_gen(yield_nones=False): - (_, type_names, path, filename) = event - - if 'IN_MODIFY' in type_names: - print("file modified") - -if __name__ == '__main__': - _main() -``` - -Пример использования inotify в приложении на Go, используя пакет inotify: - -```python -watcher, err := inotify.NewWatcher() -if err != nil { - log.Fatal(err) -} -err = watcher.Watch("/mnt/secrets-store/db-password") -if err != nil { - log.Fatal(err) -} -for { - select { - case ev := <-watcher.Event: - if ev == 'InModify' { - log.Println("file modified")} - case err := <-watcher.Error: - log.Println("error:", err) - } -} -``` - -## Ограничения при обновлении секретов - -Файлы с секретами не будут обновляться, если будет использован `subPath`. - -```yaml - volumeMounts: - - mountPath: /app/settings.ini - name: app-config - subPath: settings.ini -... - volumes: - - name: app-config - csi: - driver: secrets-store.csi.deckhouse.io - volumeAttributes: - secretsStoreImport: "python-backend" -``` diff --git a/docs/USAGE.md b/docs/USAGE.md index dfc133a..5050cda 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -7,7 +7,7 @@ description: Usage of the secrets-store-integration Deckhouse module. [Enable](../../stronghold/stable/usage.html#how-to-enable-the-module) the Stronghold module beforehand to automatically configure the secrets-store-integration module to work with [Deckhouse Stronghold](../../stronghold/). -Next, apply the ModuleConfig: +Next, apply the `ModuleConfig`: ```yaml apiVersion: deckhouse.io/v1alpha1 @@ -23,11 +23,11 @@ The [connectionConfiguration](../../secrets-store-integration/stable/configurati ## Configuring the module to work with the external secret store -The module requires a pre-configured secret vault compatible with HashiCorp Vault. An authentication path must be preconfigured in the vault. An example of how to configure the secret vault is provided in the FAQ. +The module requires a pre-configured secret vault compatible with HashiCorp Vault. An authentication path must be preconfigured in the vault. An example of how to configure the secret vault is provided in [Setting up the test environment](#setting-up-the-test-environment). To ensure that each API request is encrypted, sent to, and replied by the correct recipient, a valid public Certificate Authority certificate used by the secret store is required. A `caCert` variable in the module configuration must refer to such a CA certificate in PEM format. -The following is an example module configuration for using a Vault-compliant secret store running at "secretstoreexample.com" on a regular port (443 TLS). Note that you will need to replace the variable values in the configuration with the values that match your environment. +The following is an example module configuration for using a Vault-compliant secret store running at "secretstoreexample.com" on a regular port (443 TLS). Note that you will need to replace the parameters values in the configuration with the values that match your environment. ```yaml apiVersion: deckhouse.io/v1alpha1 @@ -50,49 +50,197 @@ spec: -----END CERTIFICATE----- ``` -To ensure that each API request is encrypted, sent and responded to by the exact host that sent it, insert the PEM-formatted caCert for the Vault-compatible secret store into ModuleConfig. - **It is strongly recommended to set the `caCert` variable. Otherwise, the module will use system ca-certificates.** ## Setting up the test environment -Before moving on to the instructions for secret injection given in the examples below, - -1. Create a kv2 type secret in Stronghold in `secret/myapp` and copy `DB_USER` and `DB_PASS` there. -2. Create a policy in Stronghold that allows reading secrets from `secret/myapp`. -3. Create a `myapp` role in Stronghold for the `myapp` service account in the `my-namespace` namespace and bind the policy you created earlier to it. -4. Create a `my-namespace` namespace in the cluster. -5. Create a `myapp` service account in the created namespace. - -Example commands to set up the environment: +{{< alert level="info">}} +First of all, you'll need a root or similiar token and the Stronghold address. +You can get such a root token while initializing a new secrets store. +All subsequent commands will assume that these settings are specified in environment variables. ```bash -stronghold secrets enable -path=secret -version=2 kv +export VAULT_TOKEN=xxxxxxxxxxx +export VAULT_ADDR=https://secretstoreexample.com +``` +{{< /alert >}} -stronghold kv put secret/myapp DB_USER="username" DB_PASS="secret-password" +> This guide will cover two ways to do this: +> * using the console version of Stronghold ([Get stronghold cli](#get-stronghold-cli)); +> * using curl to make direct requests to the secrets store API. -stronghold policy write myapp - <}} + **Important!** + In addition to the Stronghold side settings, you must configure the authorization permissions of the `serviceAccount` used in the kubernetes cluster. + See the [paragraph below](#how-to-allow-a-serviceaccount-to-log-in-to-stronghold) section for details. + {{< /alert >}} + + ```bash + stronghold write auth/kubernetes_local/role/myapp-role \ + bound_service_account_names=myapp-sa \ + bound_service_account_namespaces=myapp-namespace \ + policies=myapp-ro-policy \ + ttl=10m + ``` + The curl equivalent of the above command: + ```bash + curl \ + --header "X-Vault-Token: ${VAULT_TOKEN}" \ + --request PUT \ + --data '{"bound_service_account_names":"myapp-sa","bound_service_account_namespaces":"myapp-namespace","policies":"myapp-ro-policy","ttl":"10m"}' \ + ${VAULT_ADDR}/v1/auth/kubernetes_local/role/myapp-role + ``` + + +* Repeat the same for the rest of the clusters, specifying a different authentication path: + + ```bash + stronghold write auth/remote-kube-1/role/myapp-role \ + bound_service_account_names=myapp-sa \ + bound_service_account_namespaces=myapp-namespace \ + policies=myapp-ro-policy \ + ttl=10m + ``` + The curl equivalent of the above command: + ```bash + curl \ + --header "X-Vault-Token: ${VAULT_TOKEN}" \ + --request PUT \ + --data '{"bound_service_account_names":"myapp-sa","bound_service_account_namespaces":"myapp-namespace","policies":"myapp-ro-policy","ttl":"10m"}' \ + ${VAULT_ADDR}/v1/auth/remote-kube-1/role/myapp-role + ``` + + + {{< alert level="info">}} + **Important!** + The recommended TTL value of the Kubernetes token is 10m. + {{< /alert >}} + +These settings allow any pod within the `myapp-namespace` namespace in both K8s clusters that uses the `myapp-sa` ServiceAccount to authenticate, authorize, and read secrets in the Stronghold according to the `myapp-ro-policy` policy. + +* Create namespace and then ServiceAccount in the specified namespace: + ```bash + kubectl create namespace myapp-namespace + kubectl -n myapp-namespace create serviceaccount myapp-sa + ``` + +## How to allow a ServiceAccount to log in to Stronghold? + +To log in to Stronghold, a k8s pod uses a token generated for its ServiceAccount. In order for Stronghold to be able to check the validity of the ServiceAccount data provided by the service, Stronghold must have permission to `get`, `list`, and `watch` for the `tokenreviews.authentication.k8s.io` and `subjectaccessreviews.authorization.k8s.io` endpoints. You can also use the `system:auth-delegator` clusterRole for this. + +Stronghold can use different credentials to make requests to the Kubernetes API: +1. Use the token of the application that is trying to log in to Stronghold. In this case, each service that logs in to Stronghold must have the `system:auth-delegator` clusterRole (or the API rights listed above) in the ServiceAccount it uses. +2. Use a static token created specifically for Stronghold `ServiceAccount` that has the necessary rights. Setting up Stronghold for this case is described in detail in [Vault documentation](https://developer.hashicorp.com/vault/docs/auth/kubernetes#continue-using-long-lived-tokens). ## Injecting environment variables ### How it works -When the module is enabled, a mutating-webhook becomes available in the cluster. It modifies the pod manifest, adding an injector, if the pod has the `secrets-store.deckhouse.io/role` annotation An init container is added to the modified pod. Its mission is to copy a statically compiled binary injector file from a service image into a temporary directory shared by all containers in the pod. In the other containers, the original startup commands are replaced with a command that starts the injector. It then fetches the required data from a Vault-compatible storage using the application's service account, sets these variables in the process ENV, and then issues an execve system call, invoking the original command. +When the module is enabled, a mutating-webhook becomes available in the cluster. It modifies the pod manifest, adding an injector, if the pod has the `secrets-store.deckhouse.io/role` annotation an init container is added to the modified pod. Its mission is to copy a statically compiled binary injector file from a service image into a temporary directory shared by all containers in the pod. In the other containers, the original startup commands are replaced with a command that starts the injector. It then fetches the required data from a Vault-compatible storage using the application's service account, sets these variables in the process ENV, and then issues an execve system call, invoking the original command. If the container does not have a startup command in the pod manifest, the image manifest is retrieved from the image registry, and the command is retrieved from it. @@ -112,20 +260,20 @@ The following are the available annotations to modify the injector behavior: The injector allows you to specify env templates instead of values in the pod manifests. They will be replaced at the container startup stage with the values from the store. -For example, here's how you can retrieve the `mypassword` key from the kv2-secret at `secret/myapp` from the Vault-compatible store: +For example, here's how you can retrieve the `DB_PASS` key from the kv2-secret at `demo-kv/myapp-secret` from the Vault-compatible store: ```yaml env: - name: PASSWORD - value: secrets-store:secret/data/myapp#mypassword + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS ``` -The example below retrieves the `mypassword` key version `4` from the kv2 secret at `secret/myapp` from the Vault-compatible store: +The example below retrieves the `DB_PASS` key version `4` from the kv2 secret at `demo-kv/myapp-secret` from the Vault-compatible store: ```yaml env: - name: PASSWORD - value: secrets-store:secret/data/myapp#mypassword#4 + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS#4 ``` The template can also be stored in the ConfigMap or in the Secret and can be hooked up using `envFrom`: @@ -142,19 +290,19 @@ The actual secrets from the Vault-compatible store will be injected at the appli ### Setting environment variables by specifying the path to the secret in the vault to retrieve all keys from -The following is the specification of a pod named `myapp1`. In it, all the values are retrieved from the store at the `secret/data/myapp` path and stored as environment variables: +The following is the specification of a pod named `myapp1`. In it, all the values are retrieved from the store at the `demo-kv/data/myapp-secret` path and stored as environment variables: ```yaml kind: Pod apiVersion: v1 metadata: name: myapp1 - namespace: my-namespace + namespace: myapp-namespace annotations: - secrets-store.deckhouse.io/role: "myapp" - secrets-store.deckhouse.io/env-from-path: secret/data/myapp + secrets-store.deckhouse.io/role: "myapp-role" + secrets-store.deckhouse.io/env-from-path: demo-kv/data/myapp-secret spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 name: myapp @@ -170,16 +318,16 @@ Let's apply it: kubectl create --filename myapp1.yaml ``` -Check the pod logs after it has been successfully started. You should see all the values from `secret/data/myapp`: +Check the pod logs after it has been successfully started. You should see all the values from `demo-kv/data/myapp-secret`: ```bash -kubectl -n my-namespace logs myapp1 +kubectl -n myapp-namespace logs myapp1 ``` Delete the pod: ```bash -kubectl -n my-namespace delete pod myapp1 --force +kubectl -n myapp-namespace delete pod myapp1 --force ``` ### Explicitly specifying the values to be retrieved from the vault and used as environment variables @@ -191,18 +339,18 @@ kind: Pod apiVersion: v1 metadata: name: myapp2 - namespace: my-namespace + namespace: myapp-namespace annotations: - secrets-store.deckhouse.io/role: "myapp" + secrets-store.deckhouse.io/role: "myapp-role" spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 env: - name: DB_USER - value: secrets-store:secret/data/myapp#DB_USER + value: secrets-store:demo-kv/data/myapp-secret#DB_USER - name: DB_PASS - value: secrets-store:secret/data/myapp#DB_PASS + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS name: myapp command: - sh @@ -216,43 +364,39 @@ Apply it: kubectl create --filename myapp2.yaml ``` -Check the pod logs after it has been successfully started. You should see the values from `secret/data/myapp` matching those in the pod specification: +Check the pod logs after it has been successfully started. You should see the values from `demo-kv/data/myapp-secret` matching those in the pod specification: ```bash -kubectl -n my-namespace logs myapp2 +kubectl -n myapp-namespace logs myapp2 ``` Delete the pod: ```bash -kubectl -n my-namespace delete pod myapp2 --force +kubectl -n myapp-namespace delete pod myapp2 --force ``` ## Retrieving a secret from the vault and mounting it as a file in a container Use the `SecretStoreImport` CustomResource to deliver secrets to the application. -Create a namespace: +In this example, we use the already created ServiceAccount `myapp-sa` and namespace `myapp-namespace` from step [Setting up the test environment](#setting-up-the-test-environment) -```bash -kubectl create namespace my-namespace -``` - -Create a _SecretsStoreImport_ CustomResource named `myapp` in the cluster: +Create a _SecretsStoreImport_ CustomResource named `myapp-ssi` in the cluster: ```yaml apiVersion: deckhouse.io/v1alpha1 kind: SecretsStoreImport metadata: - name: myapp - namespace: my-namespace + name: myapp-ssi + namespace: myapp-namespace spec: type: CSI - role: myapp + role: myapp-role files: - name: "db-password" source: - path: "secret/data/myapp" + path: "demo-kv/data/myapp-secret" key: "DB_PASS" ``` @@ -263,9 +407,9 @@ kind: Pod apiVersion: v1 metadata: name: myapp3 - namespace: my-namespace + namespace: myapp-namespace spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 name: myapp @@ -282,17 +426,96 @@ spec: csi: driver: secrets-store.csi.deckhouse.io volumeAttributes: - secretsStoreImport: "myapp" + secretsStoreImport: "myapp-ssi" ``` +Once these resources have been applied, a pod will be created, inside which a container named `backend` will then be started. This container's filesystem will have a directory `/mnt/secrets`, with the `secrets` volume mounted to it. The directory will contain a `db-password` file with the password for database (`DB_PASS`) from the Stronghold key-value store. + Check the pod logs after it has been successfully started (you should see the contents of the `/mnt/secrets/db-password` file): ```bash -kubectl -n my-namespace logs myapp3 +kubectl -n myapp-namespace logs myapp3 ``` Delete the pod: ```bash -kubectl -n my-namespace delete pod myapp3 --force +kubectl -n myapp-namespace delete pod myapp3 --force +``` + +### The autorotation feature + +The autorotation feature of the secret-store-integration module is enabled by default. Every two minutes, the module polls Stronghold and synchronizes the secrets in the mounted file if it has been changed. + +There are two ways to keep track of changes to the secret file in the pod. The first is to keep track of when the mounted file changes (mtime), reacting to changes in the file. The second is to use the inotify API, which provides a mechanism for subscribing to file system events. Inotify is part of the Linux kernel. Once a change is detected, there are a large number of options for responding to the change event, depending on the application architecture and programming language used. The most simple one is to force K8s to restart the pod by failing the liveness probe. + +Here is how you can use inotify in a Python application leveraging the `inotify` Python package: + +```python +#!/usr/bin/python3 + +import inotify.adapters + +def _main(): + i = inotify.adapters.Inotify() + i.add_watch('/mnt/secrets-store/db-password') + + for event in i.event_gen(yield_nones=False): + (_, type_names, path, filename) = event + + if 'IN_MODIFY' in type_names: + print("file modified") + +if __name__ == '__main__': + _main() +``` + +Sample code to detect whether a password has been changed within a Go application using inotify and the `inotify` Go package: + +```python +watcher, err := inotify.NewWatcher() +if err != nil { + log.Fatal(err) +} +err = watcher.Watch("/mnt/secrets-store/db-password") +if err != nil { + log.Fatal(err) +} +for { + select { + case ev := <-watcher.Event: + if ev == 'InModify' { + log.Println("file modified")} + case err := <-watcher.Error: + log.Println("error:", err) + } +} +``` + +#### Secret rotation limitations + +A container that uses the `subPath` volume mount will not get secret updates when the latter is rotated. + +```yaml + volumeMounts: + - mountPath: /app/settings.ini + name: app-config + subPath: settings.ini +... + volumes: + - name: app-config + csi: + driver: secrets-store.csi.deckhouse.io + volumeAttributes: + secretsStoreImport: "python-backend" +``` + +## Get stronghold cli + +On the cluster's master node, run the following commands as `root`: +```bash +mkdir $HOME/bin +sudo cp /proc/$(pidof stronghold)/root/usr/bin/stronghold bin && sudo chmod a+x bin/stronghold +export PATH=$PATH:$HOME/bin ``` +As a result, the command `stronhold` is ready to be used. \ No newline at end of file diff --git a/docs/USAGE_RU.md b/docs/USAGE_RU.md index 54e0c0c..c495d43 100644 --- a/docs/USAGE_RU.md +++ b/docs/USAGE_RU.md @@ -23,7 +23,7 @@ spec: ## Настройка модуля для работы с внешним хранилищем -Для работы модуля требуется предварительно настроенное хранилище секретов, совместимое с HashiCorp Vault. В хранилище предварительно должен быть настроен путь аутентификации. Пример настройки хранилища секретом в FAQ. +Для работы модуля требуется предварительно настроенное хранилище секретов, совместимое с HashiCorp Vault. В хранилище предварительно должен быть настроен путь аутентификации. Пример настройки хранилища секретов [ниже](#подготовка-тестового-окружения). Чтобы убедиться, что каждый API запрос зашифрован, послан и отвечен правильным адресатом, потребуется валидный публичный сертификат Certificate Authority, который используется хранилищем секретов. Такой публичный сертификат CA в PEM-формате необходимо использовать в качестве переменной `caCert` в конфигурации модуля. @@ -66,24 +66,24 @@ export VAULT_ADDR=https://secretstoreexample.com {{< /alert >}} > В этом руководстве мы приводим два вида примерных команд: -> * команда с использованием консольной версии HashiCorp Vault ([руководство по установке](https://developer.hashicorp.com/vault/docs/install)); +> * команда с использованием консольной версии Stronghold ([Как получить бинарный файл stronghold](#как-получить-бинарный-файл-stronghold)); > * команда с использованием curl для выполнения прямых запросов в API secrets store. Для использования инструкций по инжектированию секретов из примеров ниже вам понадобится: -1. Создать в Stronghold секрет типа kv2 по пути `secret/myapp` и поместить туда значения `DB_USER` и `DB_PASS`. +1. Создать в Stronghold секрет типа kv2 по пути `demo-kv/myapp-secret` и поместить туда значения `DB_USER` и `DB_PASS`. 2. При необходимости добавляем путь аутентификации (authPath) для аутентификации и авторизации в Stronghold с помощью Kubernetes API удалённого кластера -2. Создать в Stronghold политику, разрешающую чтение секретов по пути `secret/myapp`. -3. Создать в Stronghold роль `my-namespace_backend` для сервис-аккаунта `myapp` в неймспейсе `my-namespace` и привязать к ней созданную ранее политику. -4. Создать в кластере неймспейс `my-namespace`. -5. Создать в созданном неймспейсе сервис-аккаунт `myapp`. +3. Создать в Stronghold политику `myapp-ro-policy`, разрешающую чтение секретов по пути `demo-kv/myapp-secret`. +4. Создать в Stronghold роль `myapp-role` для сервис-аккаунта `myapp-sa` в неймспейсе `myapp-namespace` и привязать к ней созданную ранее политику. +5. Создать в кластере неймспейс `myapp-namespace`. +6. Создать в созданном неймспейсе сервис-аккаунт `myapp-sa`. Пример команд, с помощью которых можно подготовить окружение * Включим и создадим Key-Value хранилище: ```bash - stronghold secrets enable -path=secret -version=2 kv + stronghold secrets enable -path=demo-kv -version=2 kv ``` Команда с использованием curl: @@ -92,13 +92,13 @@ export VAULT_ADDR=https://secretstoreexample.com --header "X-Vault-Token: ${VAULT_TOKEN}" \ --request POST \ --data '{"type":"kv","options":{"version":"2"}}' \ - ${VAULT_ADDR}/v1/sys/mounts/secret + ${VAULT_ADDR}/v1/sys/mounts/demo-kv ``` * Зададим имя пользователя и пароль базы данных в качестве значения секрета: ```bash - stronghold kv put secret/myapp DB_USER="username" DB_PASS="secret-password" + stronghold kv put demo-kv/myapp-secret DB_USER="username" DB_PASS="secret-password" ``` Команда с использованием curl: @@ -107,20 +107,20 @@ export VAULT_ADDR=https://secretstoreexample.com --header "X-Vault-Token: ${VAULT_TOKEN}" \ --request PUT \ --data '{"data":{"DB_USER":"username","DB_PASS":"secret-password"}}' \ - ${VAULT_ADDR}/v1/secret/data/myapp + ${VAULT_ADDR}/v1/demo-kv/data/myapp-secret ``` * Проверим, правильно ли записались секреты: ```bash - stronghold kv get secret/myapp + stronghold kv get demo-kv/myapp-secret ``` Команда с использованием curl: ```bash curl \ --header "X-Vault-Token: ${VAULT_TOKEN}" \ - ${VAULT_ADDR}/v1/secret/data/myapp + ${VAULT_ADDR}/v1/demo-kv/data/myapp-secret ``` * По умолчанию метод аутентификации в Stronghold через Kubernetes API кластера, на котором запущен сам Stronghold, – включён и настроен под именем `kubernetes_local`. Если требуется настроить доступ через удалённые кластера, задаём путь аутентификации (`authPath`) и включаем аутентификацию и авторизацию в Stronghold с помощью Kubernetes API для каждого кластера: @@ -152,11 +152,11 @@ export VAULT_ADDR=https://secretstoreexample.com ${VAULT_ADDR}/v1/auth/remote-kube-1/config ``` -* Создаём в Stronghold политику с названием `backend`, разрешающую чтение секрета `myapp`: +* Создаём в Stronghold политику с названием `myapp-ro-policy`, разрешающую чтение секрета `myapp-secret`: ```bash - stronghold policy write backend - <}} **Важно!** @@ -180,10 +180,10 @@ export VAULT_ADDR=https://secretstoreexample.com {{< /alert >}} ```bash - stronghold write auth/kubernetes_local/role/my-namespace_backend \ - bound_service_account_names=myapp \ - bound_service_account_namespaces=my-namespace \ - policies=backend \ + stronghold write auth/kubernetes_local/role/myapp-role \ + bound_service_account_names=myapp-sa \ + bound_service_account_namespaces=myapp-namespace \ + policies=myapp-ro-policy \ ttl=10m ``` Команда с использованием curl: @@ -191,18 +191,18 @@ export VAULT_ADDR=https://secretstoreexample.com curl \ --header "X-Vault-Token: ${VAULT_TOKEN}" \ --request PUT \ - --data '{"bound_service_account_names":"myapp","bound_service_account_namespaces":"my-namespace","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/kubernetes_local/role/my-namespace_backend + --data '{"bound_service_account_names":"myapp-sa","bound_service_account_namespaces":"myapp-namespace","policies":"myapp-ro-policy","ttl":"10m"}' \ + ${VAULT_ADDR}/v1/auth/kubernetes_local/role/myapp-role ``` * Повторяем то же самое для остальных кластеров, указав другой путь аутентификации: ```bash - stronghold write auth/remote-kube-1/role/my-namespace_backend \ - bound_service_account_names=myapp \ - bound_service_account_namespaces=my-namespace \ - policies=backend \ + stronghold write auth/remote-kube-1/role/myapp-role \ + bound_service_account_names=myapp-sa \ + bound_service_account_namespaces=myapp-namespace \ + policies=myapp-ro-policy \ ttl=10m ``` Команда с использованием curl: @@ -210,8 +210,8 @@ export VAULT_ADDR=https://secretstoreexample.com curl \ --header "X-Vault-Token: ${VAULT_TOKEN}" \ --request PUT \ - --data '{"bound_service_account_names":"myapp","bound_service_account_namespaces":"my-namespace","policies":"backend","ttl":"10m"}' \ - ${VAULT_ADDR}/v1/auth/remote-kube-1/role/my-namespace_backend + --data '{"bound_service_account_names":"myapp-sa","bound_service_account_namespaces":"myapp-namespace","policies":"myapp-ro-policy","ttl":"10m"}' \ + ${VAULT_ADDR}/v1/auth/remote-kube-1/role/myapp-role ``` @@ -220,12 +220,12 @@ export VAULT_ADDR=https://secretstoreexample.com Рекомендованное значение TTL для токена Kubernetes составляет 10m. {{< /alert >}} -Эти настройки позволяют любому поду из пространства имён `my-namespace` из обоих K8s-кластеров, который использует ServiceAccount `myapp`, аутентифицироваться и авторизоваться в Stronghold для чтения секретов согласно политике `backend`. +Эти настройки позволяют любому поду из пространства имён `myapp-namespace` из обоих K8s-кластеров, который использует ServiceAccount `myapp-sa`, аутентифицироваться и авторизоваться в Stronghold для чтения секретов согласно политике `myapp-ro-policy`. * Создадим namespace и ServiceAccount в указанном namespace: ```bash - kubectl create namespace my-namespace - kubectl -n my-namespace create serviceaccount myapp + kubectl create namespace myapp-namespace + kubectl -n myapp-namespace create serviceaccount myapp-sa ``` ## Как разрешить ServiceAccount авторизоваться в Stronghold? @@ -240,13 +240,9 @@ Stronghold может использовать различные авториз ### Как работает -При включении модуля в кластере появляется mutating-webhook, который при наличии у пода аннотации `secrets-store.deckhouse.io/role` изменяет манифест пода, -добавляя туда инжектор. В измененном поде добавляется инит-контейнер, который помещает из служебного образа собранный статически бинарный файл-инжектор -в общую для всех контейнеров пода временную директорию. В остальных контейнерах оригинальные команды запуска заменяются на запуск файла-инжектора, -который получает из Vault-совместимого хранилища необходимые данные, используя для подключения сервисный аккаунт приложения, помещает эти переменные в ENV процесса, после чего выполняет системный вызов execve, запуская оригинальную команду. +При включении модуля в кластере появляется mutating-webhook, который при наличии у пода аннотации `secrets-store.deckhouse.io/role` изменяет манифест пода, добавляя туда инжектор. В измененном поде добавляется инит-контейнер, который помещает из служебного образа собранный статически бинарный файл-инжектор в общую для всех контейнеров пода временную директорию. В остальных контейнерах оригинальные команды запуска заменяются на запуск файла-инжектора, который получает из Vault-совместимого хранилища необходимые данные, используя для подключения сервисный аккаунт приложения, помещает эти переменные в ENV процесса, после чего выполняет системный вызов execve, запуская оригинальную команду. -Если в манифесте пода у контейнера отсутствует команда запуска, то выполняется извлечение манифеста образа из хранилица образов (реджистри), -и команда извлекается из него. +Если в манифесте пода у контейнера отсутствует команда запуска, то выполняется извлечение манифеста образа из хранилица образов (реджистри), и команда извлекается из него. Для получения манифеста из приватного хранилища образов используются заданные в манифесте пода учетные данные из `imagePullSecrets`. Доступные аннотации, позволяющие изменять поведение инжектора @@ -262,20 +258,20 @@ Stronghold может использовать различные авториз Используя инжектор вы сможете задавать в манифестах пода вместо значений env-шаблоны, которые будут заменяться на этапе запуска контейнера на значения из хранилища. -Пример: извлечь из Vault-совместимого хранилища ключ `mypassword` из kv2-секрета по адресу `secret/myapp`: +Пример: извлечь из Vault-совместимого хранилища ключ `DB_PASS` из kv2-секрета по адресу `demo-kv/myapp-secret`: ```yaml env: - name: PASSWORD - value: secrets-store:secret/data/myapp#mypassword + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS ``` -Пример: извлечь из Vault-совместимого хранилища ключ `mypassword` версии `4` из kv2-секрета по адресу `secret/myapp`: +Пример: извлечь из Vault-совместимого хранилища ключ `DB_PASS` версии `4` из kv2-секрета по адресу `demo-kv/myapp-secret`: ```yaml env: - name: PASSWORD - value: secrets-store:secret/data/myapp#mypassword#4 + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS#4 ``` Шаблон может также находиться в ConfigMap или в Secret и быть подключен с помощью `envFrom` @@ -292,19 +288,19 @@ envFrom: ### Подключение переменных из ветки хранилища (всех ключей одного секрета) -Создадим под с названием `myapp1`, который подключит все переменные из хранилища по пути `secret/data/myapp`: +Создадим под с названием `myapp1`, который подключит все переменные из хранилища по пути `demo-kv/data/myapp-secret`: ```yaml kind: Pod apiVersion: v1 metadata: name: myapp1 - namespace: my-namespace + namespace: myapp-namespace annotations: - secrets-store.deckhouse.io/role: "my-namespace_backend" - secrets-store.deckhouse.io/env-from-path: secret/data/myapp + secrets-store.deckhouse.io/role: "myapp-role" + secrets-store.deckhouse.io/env-from-path: demo-kv/data/myapp-secret spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 name: myapp @@ -320,16 +316,16 @@ spec: kubectl create --filename myapp1.yaml ``` -Проверим логи пода после его запуска, мы должны увидеть все переменные из `secret/data/myapp`: +Проверим логи пода после его запуска, мы должны увидеть все переменные из `demo-kv/data/myapp-secret`: ```bash -kubectl -n my-namespace logs myapp1 +kubectl -n myapp-namespace logs myapp1 ``` Удалим под ```bash -kubectl -n my-namespace delete pod myapp1 --force +kubectl -n myapp-namespace delete pod myapp1 --force ``` ### Подключение явно заданных переменных из хранилища @@ -341,18 +337,18 @@ kind: Pod apiVersion: v1 metadata: name: myapp2 - namespace: my-namespace + namespace: myapp-namespace annotations: - secrets-store.deckhouse.io/role: "my-namespace_backend" + secrets-store.deckhouse.io/role: "myapp-role" spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 env: - name: DB_USER - value: secrets-store:secret/data/myapp#DB_USER + value: secrets-store:demo-kv/data/myapp-secret#DB_USER - name: DB_PASS - value: secrets-store:secret/data/myapp#DB_PASS + value: secrets-store:demo-kv/data/myapp-secret#DB_PASS name: myapp command: - sh @@ -366,39 +362,39 @@ spec: kubectl create --filename myapp2.yaml ``` -Проверим логи пода после его запуска, мы должны увидеть переменные из `secret/data/myapp`: +Проверим логи пода после его запуска, мы должны увидеть переменные из `demo-kv/data/myapp-secret`: ```bash -kubectl -n my-namespace logs myapp2 +kubectl -n myapp-namespace logs myapp2 ``` Удалим под ```bash -kubectl -n my-namespace delete pod myapp2 --force +kubectl -n myapp-namespace delete pod myapp2 --force ``` ## Монтирование секрета из хранилища в качестве файла в контейнер -Для доставки секретов в приложение нужно использовать CustomResource “SecretStoreImport”. +Для доставки секретов в приложение нужно использовать CustomResource `SecretStoreImport`. -В этом примере используем уже созданные ServiceAccount `myapp` и namespace `my-namespace` из шага [Подготовка тестового окружения](#подготовка-тестового-окружения) +В этом примере используем уже созданные ServiceAccount `myapp-sa` и namespace `myapp-namespace` из шага [Подготовка тестового окружения](#подготовка-тестового-окружения) -Создайте в кластере CustomResource _SecretsStoreImport_ с названием “myapp”: +Создайте в кластере CustomResource _SecretsStoreImport_ с названием `myapp-ssi`: ```yaml apiVersion: deckhouse.io/v1alpha1 kind: SecretsStoreImport metadata: name: myapp-ssi - namespace: my-namespace + namespace: myapp-namespace spec: type: CSI - role: my-namespace_backend + role: myapp-role files: - name: "db-password" source: - path: "secret/data/myapp" + path: "demo-kv/data/myapp-secret" key: "DB_PASS" ``` @@ -409,9 +405,9 @@ kind: Pod apiVersion: v1 metadata: name: myapp3 - namespace: my-namespace + namespace: myapp-namespace spec: - serviceAccountName: myapp + serviceAccountName: myapp-sa containers: - image: alpine:3.20 name: myapp @@ -430,17 +426,17 @@ spec: volumeAttributes: secretsStoreImport: "myapp-ssi" ``` -После применения этих ресурсов будет запущен под с названием `backend`, внутри которого будет каталог `/mnt/secrets` с примонтированным внутрь томом `secrets`. Внутри каталога будет лежать файл `db-password` с паролем от базы данных из Stronghold. +После применения этих ресурсов будет создан под, внутри которого запустится контейнер с названием `backend`. В файловой системе этого контейнера будет каталог `/mnt/secrets` с примонтированным к нему томом `secrets`. Внутри этого каталога будет лежать файл `db-password` с паролем от базы данных (`DB_PASS`) из хранилища ключ-значение Stronghold. Проверьте логи пода после его запуска (должно выводиться содержимое файла `/mnt/secrets/db-password`): ```bash -kubectl -n my-namespace logs myapp3 +kubectl -n myapp-namespace logs myapp3 ``` Удалите под: ```bash -kubectl -n my-namespace delete pod myapp3 --force +kubectl -n myapp-namespace delete pod myapp3 --force ``` ### Функция авторотации @@ -509,3 +505,13 @@ for { volumeAttributes: secretsStoreImport: "python-backend" ``` + +## Как получить бинарный файл stronghold + +На мастер-ноде кластера необходимо выполнить следующие команды через `root` пользователя: +```bash +mkdir $HOME/bin +sudo cp /proc/$(pidof stronghold)/root/usr/bin/stronghold bin && sudo chmod a+x bin/stronghold +export PATH=$PATH:$HOME/bin +``` +Команда `stronhold` готова к использованию. \ No newline at end of file