--- # Интеграция Vault ↔ k3s: метод auth/kubernetes. # # Vault контура живёт в docker-compose, ВНЕ кластера, поэтому не может # воспользоваться ни собственным ServiceAccount-токеном, ни CA из # /var/run/secrets — всё это ему нужно передать явно: # kubernetes_host — адрес apiserver, резолвится по compose-DNS; # kubernetes_ca_cert — CA кластера (из секрета token reviewer'а); # token_reviewer_jwt — долгоживущий токен SA vault-auth. # # После этого под с аннотациями vault.hashicorp.com/* получает от injector'а # init-контейнер vault-agent, тот логинится в Vault токеном ServiceAccount'а # пода, Vault валидирует его через TokenReview и отдаёт секреты файлами # в /vault/secrets/. # # Все шаги идемпотентны: повторный прогон на настроенном Vault — no-op. # # ВНИМАНИЕ по стилю: команды здесь задаются ЛИТЕРАЛЬНЫМ блоком (|), а не # складывающим (>-). В складывающем скаляре строка с бОльшим отступом считается # "more-indented", и YAML СОХРАНЯЕТ перед ней перевод строки — многострочная # команда разрывается посреди аргументов. Именно на этом падал первый вариант: # `vault write auth/kubernetes/config` отрывался от своих параметров. - name: Применить манифесты vault в k3s (DNS-мост + token reviewer) ansible.builtin.shell: cmd: | {{ compose_cmd }} exec -T k3s-server sh -ec ' sed "s|__VAULT_BRIDGE_IP__|{{ vault_bridge_ip }}|g" /output/manifests/vault/bridge-vault.yaml | kubectl apply -f - kubectl apply -f /output/manifests/vault/vault-auth.yaml ' chdir: "{{ deploy_dir }}" changed_when: true # Секрет читается ЦЕЛИКОМ в JSON, а поля достаются фильтром from_json. # # Так сделано не из вкуса: вариант `-o jsonpath={.data.ca\.crt}` здесь НЕ # работает. Модуль command разбирает строку через shlex с обработкой escape- # последовательностей и съедает обратный слэш, поэтому kubectl получает # {.data.ca.crt} — ищет вложенное поле crt внутри ca, не находит и возвращает # ПУСТУЮ строку с кодом 0. Отказ тихий: задача чтения проходит успешно, а # падает уже `vault write` с пустым CA. Ключ token при этом читался верно — # в нём нет точки, — что делало симптом ещё запутаннее. # # token-controller заполняет секрет асинхронно, поэтому ждём оба поля. - name: Прочитать секрет token reviewer'а (дождавшись заполнения) ansible.builtin.command: cmd: >- {{ compose_cmd }} exec -T k3s-server kubectl -n vault get secret vault-auth-token -o json chdir: "{{ deploy_dir }}" register: vault_auth_secret until: >- vault_auth_secret.rc == 0 and '"ca.crt"' in vault_auth_secret.stdout and '"token"' in vault_auth_secret.stdout retries: 30 delay: 2 changed_when: false no_log: true - name: Выделить CA и JWT из секрета ansible.builtin.set_fact: vault_k8s_ca_b64: "{{ (vault_auth_secret.stdout | from_json).data['ca.crt'] }}" vault_sa_token_b64: "{{ (vault_auth_secret.stdout | from_json).data['token'] }}" no_log: true - name: Проверить, что CA и JWT непустые ansible.builtin.assert: that: - vault_k8s_ca_b64 | length > 0 - vault_sa_token_b64 | length > 0 fail_msg: >- Секрет vault-auth-token прочитан, но CA или JWT пусты — Vault нельзя настроить на валидацию токенов подов. success_msg: "CA и JWT получены" # Повторный вызов на уже включённом пути отдаёт "path is already in use" # и ненулевой код — это штатно, глушим. - name: Включить метод auth/kubernetes в Vault ansible.builtin.shell: cmd: | {{ compose_cmd }} exec -T vault-init sh -ec ' export VAULT_TOKEN=$(cat /vault/init/root.token) vault auth enable kubernetes 2>&1 | grep -v "path is already in use" || true ' chdir: "{{ deploy_dir }}" changed_when: true # CA и JWT передаём внутрь контейнера base64-строкой и раскодируем во временные # файлы: vault CLI умеет читать значение параметра из файла синтаксисом @путь, # и так не приходится экранировать многострочный PEM в аргументах. - name: Настроить auth/kubernetes (адрес apiserver, CA, token reviewer) ansible.builtin.shell: cmd: | {{ compose_cmd }} exec -T vault-init sh -ec ' export VAULT_TOKEN=$(cat /vault/init/root.token) umask 077 echo "{{ vault_k8s_ca_b64 }}" | base64 -d > /tmp/k8s-ca.crt echo "{{ vault_sa_token_b64 }}" | base64 -d > /tmp/k8s-jwt vault write auth/kubernetes/config kubernetes_host="{{ vault_k8s_host }}" kubernetes_ca_cert=@/tmp/k8s-ca.crt token_reviewer_jwt=@/tmp/k8s-jwt rm -f /tmp/k8s-ca.crt /tmp/k8s-jwt ' chdir: "{{ deploy_dir }}" changed_when: true no_log: true # Политика доступа к kv-v2 на пути secrets/ — том самом, что включает vault-init # и на который ссылаются аннотации приложений (secrets/data/apps//...). - name: Создать политику чтения секретов ansible.builtin.shell: cmd: | {{ compose_cmd }} exec -T vault-init sh -ec ' export VAULT_TOKEN=$(cat /vault/init/root.token) vault policy write {{ vault_apps_policy }} - <