4) Добавляю в план в `outline_pki.md` §11 два пункта — GitOps для policy и валидацию отзыва на клиентах (по §6.1 RedTeam-списка):

 I'm checking the architecture documentation for any references to `issue` and `sign` grants — section §3.4 mentions `5xx на issue/sign` which is fine as a generic reference, but I need to verify that section §1.2 on Policies, which lists `issue/<ROLE>` and `sign/<ROLE>` patterns, aligns with what Vault actually supports architecturally.

Now for the fourth task, I need to add two items to the deployment plan: GitOps for policy management and remediation for the red-team finding on revocation validation. I'll extend the plan section in outline_pki.md §11 and add a new subsection §3.7 to pki-architecture.md covering policy management, plus a note about enforcing CRL/OCSP checks at the client level.

## 11. План до ввода в `prod`

1. Создать зеркальную роль `P0239` на `pki-prod`.
2. Создать отдельные policy и AppRole для `prod` с изоляцией от `stage`.
3. Утвердить регламент ротации AppRole (`secretId`) и аварийного отзыва.
4. Финализировать правила именования FQDN брокеров.
5. Зафиксировать SLA и регламент для процесса отзыва сертификатов и операционной поддержки.

## 11. План до ввода в `prod`

1. Создать зеркальную роль `P0239` на `pki-prod`.
2. Создать отдельные policy и AppRole для `prod` с изоляцией от `stage`.
3. Утвердить регламент ротации AppRole (`secretId`) и аварийного отзыва.
4. Финализировать правила именования FQDN брокеров.
5. Зафиксировать SLA и регламент для процесса отзыва сертификатов и операционной поддержки.
6. **GitOps для Vault policy**: все изменения политик (`p0239` и будущих продуктовых) — через PR в этом репозитории, обязательный code review от DevSecOps/ИБ, применение через CI-пайплайн (`vault policy write`), запрет на ручные правки через UI/CLI в обход репозитория. Drift-проверка: периодический diff между фактическими политиками в Vault и `files/**/*.hcl`.
7. **Проверка отзыва сертификатов на стороне клиентов**: для Kafka-брокеров и потребителей явно включить валидацию CRL/OCSP (иначе `revoke` остаётся декоративным). Зафиксировать в regression-тесте: отозванный сертификат должен немедленно отклоняться клиентами. По возможности использовать короткие TTL (≤ 24h) как дополнительный компенсирующий контроль.


5) В `docs/pki-architecture.md` добавляю §3.7 про GitOps и дополняю §1.3 про валидацию отзыва:

StrReplace

Отзыв:

- revoke выполняется через Vault по `serial_number`;
- CRL обновляется автоматически и доступен по HTTP;
- процедуру revoke владеют команды DevSecOps/SOC, а не продуктовые команды.

Отзыв:

- revoke выполняется через Vault по `serial_number`;
- CRL обновляется автоматически и доступен по HTTP;
- процедуру revoke владеют команды DevSecOps/SOC, а не продуктовые команды.

Валидация отзыва на стороне клиентов:

- revoke имеет смысл только если потребители сертификатов действительно проверяют CRL/OCSP; без этого отзыв носит декоративный характер.
- для Kafka-брокеров и клиентов обязательна явная настройка проверки CRL/OCSP (регламент и regression-тесты — см. `docs/outline_pki.md` §11).
- компенсирующим контролем служат короткие TTL leaf-сертификатов (≤ 24h в штатном режиме).

Rotate intermediate должен быть отрепетирован на stage до применения на prod. Регламент передачи бандла — общий с §1.4.

Rotate intermediate должен быть отрепетирован на `stage` до применения на `prod`. Регламент передачи бандла — общий с §1.4.

### 3.7. Управление политиками (GitOps)

Все Vault-политики (`p0239` и будущие продуктовые) управляются как код:

- исходники HCL лежат в этом репозитории (`files/<product>/*.hcl`);
- любое изменение политики — через Pull Request с обязательным review DevSecOps и ИБ;
- применение только через CI-пайплайн (`vault policy write <name> <file>.hcl`), ручные правки политик через UI/CLI в обход репозитория запрещены;
- периодический drift-контроль: CI сравнивает вывод `vault policy read` с содержимым репозитория и бьёт тревогу при расхождении;
- история изменений политик — Git history + Vault audit log (двойная фиксация).

Принцип минимальных привилегий для продуктовых политик:

- AppRole получает только то, что действительно требуется автоматике;
- для `cert-manager` это только `sign` на роль продукта (`pki-<env>/sign/<ROLE>`) + `read` на `cert/ca`, `cert/ca_chain`;
- путь `issue` продуктовым AppRole **не выдаётся**: в `cert-manager`-потоке он не нужен, а его наличие позволило бы владельцу `secret_id` получать готовую пару ключ+сертификат вместо подписи CSR.


Готово. Что изменил:

**Политика и её отражение в докe:**

- `files/pki-p0239/p0239-kafka-apim-stage.hcl` — убрал блок `path "pki-stage/issue/P0239"`, осталось `sign` + `read` на `cert/ca`/`cert/ca_chain`. Файл теперь точно соответствует тому, что в Vault.
- `docs/outline_pki.md` §5.4 — из списка capability удалён `issue`, добавлена пометка «`issue` умышленно не выдан».
- `docs/kafka-pki-vault-pki-lmru-procedure.md`:
  - в §1 над примером `vault write ... issue/P0239` добавлено явное пояснение, что это выполняется только под админским токеном через OIDC, AppRole на `issue` прав не имеет;
  - в §2 для `prod` пути уточнены на `pki-prod/sign/P0239`;
  - в §4 таблице эндпоинтов `issue`-строка подписана как «Выпуск от админа (Vault issue API, только OIDC)», ниже подчёркнуто, что политика `p0239` намеренно не содержит `issue`.

**План и архитектурные решения добавлены:**

- `docs/outline_pki.md` §11 — два новых пункта:
  - п.6 — GitOps для Vault policy (PR + review + CI `vault policy write` + drift-контроль, запрет ручных правок через UI/CLI);
  - п.7 — проверка отзыва на стороне клиентов (явная валидация CRL/OCSP для Kafka, regression-тесты, короткие TTL как компенсирующий контроль). Это и есть §6.1 из RedTeam-списка.
- `docs/pki-architecture.md` §1.3 — добавлен подраздел «Валидация отзыва на стороне клиентов» с отсылкой к плану.
- `docs/pki-architecture.md` §3.7 — новая секция «Управление политиками (GitOps)»: исходники в репо, PR/review, применение только через CI, drift-контроль, двойная фиксация истории (Git + audit), отдельно зафиксирован принцип «продуктовым AppRole путь `issue` не выдаётся» с обоснованием.

Теперь на согласовании архитекторам есть что показать как закрытие двух пунктов из RedTeam-списка — policy-as-code и валидация отзыва.


<timestamp>Tuesday, Apr 28, 2026, 3:02 PM (UTC+3)</timestamp>
<user_query>
@docs/adr/ADR-001-pki-architecture.md @docs/adr/architecture.md надо настроить мониторинг
</user_query>


Понял как задачу на практическую проработку мониторинга для Vault PKI: посмотрю текущие ADR/architecture и структуру репозитория, чтобы добавить конкретный план и, если уже есть место под конфиг, подготовить его в существующем стиле.


Вижу, что в `architecture.md` уже есть базовый раздел про audit и минимальный мониторинг, но он пока скорее декларативный. Добавлю отдельный практический блок: какие метрики/логи собирать, какие алерты нужны и что именно считать готовностью мониторинга; ADR дополню как обязательное решение/критерий приёмки.

**Considering architecture for monitoring**

I think it would be a good idea to read up on the rest architecture related to monitoring and backups. I’m curious about how the systems work together to ensure everything is functioning properly and securely. Understanding this could help me better assess the effectiveness and reliability of these systems. I wonder what best practices I might find that would apply to my current needs! Let's explore this together!


**Considering ansible telemetry**

I think I need to implement ansible telemetry. That might involve reading some templates to understand how to integrate and set everything up correctly. It feels like there’s a lot to learn and consider, especially to get everything running smoothly. I wonder what specific details I should focus on first—possibly starting with the basic configuration? I want to make sure I get this right for optimal performance!


**Configuring monitoring**

I'm considering whether I need to configure monitoring by adding some config files. We’ve already noted telemetry in the Vault config, so maybe I should create documentation for that. The actual Prometheus scrape should have unauthenticated metrics access in the listener. The Vault Prometheus endpoint will require a token unless we're using unauthenticated metrics. I've got to decide on that part, and I think I should check the Docker compose tasks and the ports too.


В коде уже включён `telemetry { prometheus_retention_time = "12h" }`, но не хватает отдельной policy для Prometheus/мониторинга и чёткой схемы алертов. Сделаю минимальный безопасный вариант: метрики читаются по токену с отдельной read-only policy, без открытия `/sys/metrics` анонимно.

ApplyPatch

Input:
*** Begin Patch
*** Add File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/files/root_policy_monitoring.hcl
+path "sys/metrics" {
+  capabilities = ["read"]
+}
+
+path "sys/health" {
+  capabilities = ["read"]
+}
+
+path "sys/leader" {
+  capabilities = ["read"]
+}
*** End Patch


Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/tasks/main.yml
@@
   loop:
       - root_policy_admins
       - root_policy_eikthyr
+      - root_policy_monitoring
   tags: policy
@@
   loop:
       - root_policy_admins
       - root_policy_eikthyr
+      - root_policy_monitoring
   tags: policy
*** End Patch


Теперь добавлю в ADR, что мониторинг — часть архитектурного решения, и сделаю его критерием готовности, чтобы это не выглядело как «потом когда-нибудь».

**Considering YAML adjustments**

I might need to read and format with yamllint to address the existing indentation, which has a peculiar 6-space loop. We're instructed to use yamllint, but running it isn't a strict requirement. Checking the new lines for alignment seems essential. I should also modify the documentation, perhaps applying a patch or using strreplace. First, I'm thinking of reading the ADR portion and maybe adding a Monitoring item in the decision and acceptance to cover all bases.

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/adr/ADR-001-pki-architecture.md
@@
 8. **Revoke и CRL**:
    * владельцем процедуры revoke являются DevSecOps/SOC, не продуктовые команды;
    * CRL публикуется Vault и доступен потребителям.
+9. **Мониторинг**:
+   * Vault telemetry включена в конфигурации сервера (`prometheus_retention_time = "12h"`);
+   * Prometheus/система мониторинга читает `/v1/sys/metrics?format=prometheus` по отдельному токену с policy `root_policy_monitoring`;
+   * health-check выполняется по `/v1/sys/health`, при этом `standby` не считается аварией, а `sealed`/`uninitialized` считаются критическим состоянием;
+   * audit log Vault дублируется в SIEM; отключение audit device, изменение policy/issuer и массовый выпуск сертификатов алертятся;
+   * состояние backup контролируется по свежести локального snapshot и наличию off-site копии в Object Storage.
@@
 5. Описан DR-подход: restore из Raft snapshot, перенос backup job на другую ноду при потере `p-pki-as-01.lmru.tech`.
 6. Приняты остаточные риски Vault OSS: отсутствие DR/performance replication, namespace'ов и HSM auto-unseal.
 7. Зафиксирован план ввода `pki-prod`: запрос intermediate у ITOP, импорт PEM-бандла, создание prod-ролей и проверка выпуска.
+8. Настроен мониторинг Vault: health, seal status, leader/standby, telemetry metrics, audit log, сроки действия CA, ошибки issuance/sign/revoke и свежесть backup.
*** End Patch


Дальше заменю короткий раздел `3.4` в архитектуре на рабочий чеклист: источники данных, alert rules, токен/политика и критерии готовности.

**Modifying architecture section**

I need to modify section 3.4 of the architecture document. My plan is to use the strreplace function to handle this. It’s important that I get the exact lines from the old block, specifically lines 316-325. I want to make sure I’m following the guidelines properly, so I’ll pay close attention to those details when I’m making the changes. Making changes accurately is crucial!

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/adr/architecture.md
@@
 ### 3.4. Мониторинг и алерты

-Минимальный набор:
-
-* доступность Vault API (health-check);
-* статус seal (не должен быть `sealed`);
-* срок действия intermediate CA (алерт за 60/30 дней);
-* ошибки выпуска и подписи (5xx на `issue`/`sign`, 4xx на `auth/approle/login`);
-* размер аудит-лога и ротация;
-* рост `vault.expire.num_leases` (индикатор неправильно работающих потребителей).
+Мониторинг строится вокруг четырёх источников: Vault telemetry, health endpoint, audit log и контроль backup.
+
+**Telemetry / Prometheus**
+
+В конфигурации Vault уже включён блок:
+
+```hcl
+telemetry {
+  disable_hostname = true
+  prometheus_retention_time = "12h"
+}
+```
+
+Метрики забираются с каждого Vault-узла по endpoint:
+
+```text
+GET https://<vault-node>:8200/v1/sys/metrics?format=prometheus
+```
+
+Доступ к метрикам выполняется по отдельному токену с policy `root_policy_monitoring`.
+Анонимный доступ к `/sys/metrics` не включаем: токен мониторинга хранится в секрет-хранилище системы мониторинга и ротируется регламентно.
+
+Policy:
+
+```hcl
+path "sys/metrics" {
+  capabilities = ["read"]
+}
+
+path "sys/health" {
+  capabilities = ["read"]
+}
+
+path "sys/leader" {
+  capabilities = ["read"]
+}
+```
+
+**Health-check**
+
+Проверяется endpoint:
+
+```text
+GET https://<vault-node>:8200/v1/sys/health
+```
+
+Интерпретация:
+
+| Состояние | Ожидаемый смысл | Реакция |
+|-----------|-----------------|---------|
+| `active` | Узел лидер, обслуживает запись | OK |
+| `standby` | Узел в standby, штатное состояние для follower | OK |
+| `sealed` | Узел sealed | Critical |
+| `uninitialized` | Vault не инициализирован | Critical |
+| нет ответа / TLS ошибка | Узел недоступен или проблема сертификата | Critical |
+
+Мониторинг должен ходить на **каждый узел**, а не только на LB/FQDN, иначе потеря standby-узла может быть незаметна.
+
+**Обязательные алерты**
+
+| Алерт | Условие | Критичность | Действие |
+|-------|---------|-------------|----------|
+| Vault API недоступен | health endpoint не отвечает или TLS ошибка | Critical | Проверить узел, сеть, контейнер, сертификат |
+| Vault sealed | `sealed=true` на любом узле | Critical | Выполнить unseal по процедуре / проверить INIT-1793 auto-unseal |
+| Нет active leader | `/sys/leader` не показывает active leader | Critical | Проверить Raft quorum |
+| Потеря узла Raft | один из узлов не отвечает | Warning/Critical по длительности | Восстановить узел, проверить quorum |
+| Ошибки `auth/approle/login` | рост 4xx на login | Warning | Проверить истёкший/скомпрометированный `secret_id`, ошибки потребителей |
+| Ошибки `sign`/`issue`/`revoke` | рост 4xx/5xx по PKI endpoints | Warning/Critical | Проверить policy, роль, CA, доступность backend |
+| Массовый выпуск сертификатов | резкий рост `sign`/`issue` за короткое окно | Warning/Critical | Проверить AppRole, возможную утечку `secret_id` |
+| Изменение policy | запись в `/sys/policies/*` | Warning | Сверить с PR/GitOps |
+| Изменение issuer / CA | операции `issuers/import`, `config/ca`, default issuer | Critical | Проверить change request и участников |
+| Отключение audit device | `sys/audit/*` disable/update | Critical | Немедленная проверка владельцами Vault и ИБ |
+| Audit log не пишется | файл не растёт при наличии операций | Critical | Проверить audit device, диск, права |
+| Диск audit/raft заполнен | свободно < 20% warning, < 10% critical | Warning/Critical | Очистка/расширение диска, проверка logrotate |
+| Intermediate CA скоро истекает | < 60 дней warning, < 30 дней critical | Warning/Critical | Запустить процедуру ротации с ITOP |
+| Leaf-сертификаты Kafka скоро истекают | cert-manager не продлил сертификат | Warning/Critical | Проверить cert-manager, AppRole, Vault sign |
+| CRL недоступен | URL CRL не отвечает | Critical | Проверить Vault/API/LB |
+| Backup устарел | нет локального snapshot > 2 часов | Critical | Проверить backup job на `p-pki-as-01.lmru.tech` |
+| Off-site backup не появился | нет нового объекта в `s3://p-pki/p-pki-raft-backup/raft/` > 2 часов | Critical | Проверить Object Storage, access key, сеть |
+
+**Логи и аудит**
+
+* Vault audit file остаётся обязательным локальным audit device.
+* Audit log дублируется в SIEM/syslog, чтобы компрометация одного узла не позволила скрыть действия.
+* В SIEM настраиваются корреляции по событиям: login, token create/revoke, policy write, issuer import, `sign`, `issue`, `revoke`, audit enable/disable.
+* Отключение audit device или отсутствие событий при живом API считается инцидентом.
+
+**Контроль backup**
+
+Мониторинг backup проверяет:
+
+* успешный exit code backup job;
+* свежесть последнего локального snapshot на `p-pki-as-01.lmru.tech`;
+* появление нового объекта в Object Storage bucket `p-pki` по префиксу `p-pki-raft-backup/raft/`;
+* размер snapshot (аномально маленький файл — повод для проверки);
+* отсутствие ошибок при загрузке в Object Storage.
+
+**Критерии готовности мониторинга**
+
+1. Все три Vault-узла видны в мониторинге отдельно.
+2. Prometheus/система мониторинга получает `/v1/sys/metrics?format=prometheus` по токену с `root_policy_monitoring`.
+3. Health-check различает `active`, `standby`, `sealed`, `uninitialized`.
+4. Audit log доставляется в SIEM или есть утверждённый временный компенсирующий контроль.
+5. Настроены алерты на seal, отсутствие leader, ошибки PKI/API, изменение policy/issuer/audit, срок действия CA и свежесть backup.
+6. Проверен тестовый алерт: искусственный отказ одного standby-узла или остановка backup job приводит к уведомлению дежурных.

 ### 3.5. Восстановление после сбоев
*** End Patch


Ещё добавлю команды для выпуска токена мониторинга, но без самого токена: его нужно создать вручную и положить в секрет-хранилище мониторинга.

**Updating README and Policies**

I need to consider updating the README with details on the monitoring policy and the role setup. It might also be good to incorporate commands for applying configurations and maybe modifying the vault_config to allow for optional unauth access, though that's not strictly necessary.

Also, I could add documentation for apply commands and include the procedure for creating monitoring tokens. However, I'll need to be cautious about how these sensitive tokens are handled and stored. It might be best to add a manual command for token creation, ensuring that's explained clearly in the documentation.
**Adding to Architecture**

I need to incorporate some details into the architecture section, specifically under telemetry. This is important for ensuring the documentation is comprehensive and covers essential aspects of the system. I should consider what specific telemetry information needs to be documented, like metrics or data flow, to make sure everything is clear and useful for future reference. I'll definitely want to structure this in a way that is easy to understand and access.

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/adr/architecture.md
@@
 Доступ к метрикам выполняется по отдельному токену с policy `root_policy_monitoring`.
 Анонимный доступ к `/sys/metrics` не включаем: токен мониторинга хранится в секрет-хранилище системы мониторинга и ротируется регламентно.

 Policy:
@@
 path "sys/leader" {
   capabilities = ["read"]
 }
 ```
+
+Policy добавлена в Ansible setup как `roles/vault-oss-setup/files/root_policy_monitoring.hcl` и применяется вместе с остальными root policies.
+
+Токен для мониторинга создаётся вручную администратором Vault и не хранится в Git:
+
+```bash
+vault token create \
+  -policy=root_policy_monitoring \
+  -period=24h \
+  -orphan \
+  -display-name=prometheus-vault-monitoring
+```
+
+Полученный token помещается в секрет-хранилище системы мониторинга. Если система мониторинга не умеет renew periodic token, вместо `-period` используется short TTL + регламентная ротация.

 **Health-check**
*** End Patch


**Updating role documentation**

I’m thinking about updating the role README with a new policy. It might not be absolutely necessary, but it could be a good idea. I also want to ensure the YAML linting is all set, so I’ll need to check that with ReadLints. But wait, before that, it seems like I should check the tasks and read the file lines to ensure everything is valid for yamllint. Let’s keep this organized!


В YAML-роле заодно поправлю отступы у списков `loop`, чтобы соответствовало обычным правилам `yamllint`, а не просто было валидным YAML.

**Fixing YAML Indentation**

I'm considering the user's request to use yamllint rules. It seems like a loop with an indentation of 6 is questionable and might need to be fixed. Yamllint likely prefers 4 spaces for loop indentation, while the hyphens might need adjustment too, possibly using 2 spaces for lists under loops.

Oh, and there could be trailing spaces earlier that we didn't touch. Since the user specifically asked about yamllint, I think running it might help. I should patch only the relevant parts first.

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/tasks/main.yml
@@
   vars:
     policy_content: "{{ lookup('file', item ~ '.hcl') }}"
   loop:
-      - root_policy_admins
-      - root_policy_eikthyr
-      - root_policy_monitoring
+    - root_policy_admins
+    - root_policy_eikthyr
+    - root_policy_monitoring
@@
     headers:
       X-Vault-Token: "{{ vault_token }}"
       Content-Type: "application/json"
     status_code: 204
   loop:
-      - root_policy_admins
-      - root_policy_eikthyr
-      - root_policy_monitoring
+    - root_policy_admins
+    - root_policy_eikthyr
+    - root_policy_monitoring
*** End Patch


**Considering process execution**

I'm thinking about running yamllint, but first, I need to check the terminals to ensure nothing is running. For simple linting, I could consider using ls, but I want to stick to shell operations instead of file ops when possible. It seems I can run yamllint on the changed YAML if it's installed. I don’t think I need to run any long commands, so I'll focus on using shell instructions carefully while verifying the directory.

Shell

Run yamllint on setup tasks


`yamllint` нашёл старые проблемы в этом же файле (длинные строки, trailing spaces, отсутствие newline). Раз уж файл тронут, приведу его к правилам, чтобы проверка была чистой.


**Planning code patch**

I need to work on a patch. First, I should check the long comment on line 2 and ensure that `return_content` is set to true. I also want to remove any trailing spaces. For longer lines, I’m wondering if I can use the folded style with `>-`. Oh, and I shouldn’t have a newline at the end of the file. Once I've collected all these details, I can apply the changes.

ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/tasks/main.yml
@@
-# Ansible running locally - need to manually specify path to vars file from host group
+# Ansible runs locally; vars file is selected explicitly per host group.
@@
-    return_content: yes
+    return_content: true
@@
-      X-Vault-Token: "{{ vault_token }}"
+      X-Vault-Token: "{{ vault_token }}"
@@
-    body:
+    body:
@@
-      X-Vault-Token: "{{ vault_token }}"
+      X-Vault-Token: "{{ vault_token }}"
@@
-  when: vault_token is defined and vault_token != "" and not vault_audit_file_enable_check
+  when: >-
+    vault_token is defined and
+    vault_token != "" and
+    not vault_audit_file_enable_check
@@
-      X-Vault-Token: "{{ vault_token }}"
+      X-Vault-Token: "{{ vault_token }}"
@@
-    body:
+    body:
@@
-      X-Vault-Token: "{{ vault_token }}"
+      X-Vault-Token: "{{ vault_token }}"
@@
-  when: vault_token is defined and vault_token != "" and vault_oidc_enable_check.status == 400
+  when: >-
+    vault_token is defined and
+    vault_token != "" and
+    vault_oidc_enable_check.status == 400
@@
-  tags: oidc
+  tags: oidc
*** End Patch


Shell

Re-run yamllint on setup tasks


ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/tasks/main.yml
@@
-  tags: oidc
+  tags: oidc
+
*** End Patch


Shell

Verify yamllint passes


**Considering file operations**

I'm thinking about using Edit with Python, but not sure it fits. The developer advises against shell for file operations, which has me hesitant. I wonder if I can bypass the issue with `ApplyPatch` and its final newline requirement. Maybe I could use a Shell exception, but I also don’t want to violate the guidelines. Essentially, I need to meet `yamllint` requirements while possibly adding a trailing comment line and another newline.

ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-setup/tasks/main.yml
@@
-  tags: oidc
-
+  tags: oidc
+
*** End of File
*** End Patch


Shell

Check yamllint after newline fix


`ApplyPatch` почему-то сохраняет этот YAML без финального newline, и `yamllint` продолжает падать только на этом. Добью конкретно финальный перевод строки и перепроверю.

python -c "from pathlib import Path; p=Path('roles/vault-oss-setup/tasks/main.yml'); data=p.read_bytes(); p.write_bytes(data if data.endswith(b'\n') else data + b'\n')" && yamllint "roles/vault-oss-setup/tasks/main.yml"

Ensure newline then run yamllint


**Considering commands for deployment**

I'm thinking about whether I need to include a git diff or run rg for documentation, though the user didn't specifically ask. I might inspect architecture line numbers, but that feels unnecessary. The phrase "настроить мониторинг" could indicate the need to apply something to Vault, but since we haven't run a playbook or adjusted Vault policy, I wonder if I should run ansible. It might be too risky without the proper credentials or context. I should clarify that I haven't run the playbook.


**Inspecting the playbook**

I think I might need to take a closer look at the production playbook. I want to ensure I understand everything correctly and see if there are any details I might have missed. It's important to me that I'm thorough. Maybe I could go through the sections step-by-step to verify that everything’s in order. I want to make this process as smooth as possible! Let’s dive in!


Добавил мониторинг в код и в доки.

Что сделано:

- Создал policy `root_policy_monitoring`:
  `roles/vault-oss-setup/files/root_policy_monitoring.hcl`
  Доступ только на:
  - `sys/metrics`
  - `sys/health`
  - `sys/leader`

- Подключил эту policy в Ansible setup:
  `roles/vault-oss-setup/tasks/main.yml`

- В `docs/adr/architecture.md` расширил §3.4 «Мониторинг и алерты»:
  - Prometheus endpoint: `/v1/sys/metrics?format=prometheus`
  - health-check: `/v1/sys/health`
  - отдельный monitoring token с `root_policy_monitoring`
  - алерты на `sealed`, отсутствие leader, ошибки PKI, изменение policy/issuer/audit, срок действия CA, свежесть backup
  - контроль backup в Object Storage
  - критерии готовности мониторинга

- В `docs/adr/ADR-001-pki-architecture.md` добавил мониторинг в архитектурное решение и критерии приёмки ADR.

- Привёл `roles/vault-oss-setup/tasks/main.yml` к `yamllint`; проверка проходит.

Я не применял изменения в живой Vault. Для применения policy нужно будет запустить setup role с тегом `policy`, а затем вручную создать monitoring token и положить его в секрет-хранилище мониторинга:

```bash
vault token create \
  -policy=root_policy_monitoring \
  -period=24h \
  -orphan \
  -display-name=prometheus-vault-monitoring
```

Токен в Git не кладём.


<timestamp>Tuesday, Apr 28, 2026, 3:27 PM (UTC+3)</timestamp>
<user_query>
у меня такой конфиг
@/home/ruslanpi/.cursor/projects/home-ruslanpi-Documents-repos-lmru-devops-core-ansible-vault-oss/terminals/1.txt:119-167
надо конкретная инструкци я только для меня с настройкой, какие метрики, графана дашборд, алертменеджер
</user_query>


Понял: нужен не общий раздел для архитекторов, а рабочая инструкция «как мне руками подключить Prometheus/Grafana/Alertmanager под текущий Vault config». Учту, что у тебя уже включено `unauthenticated_metrics_access = true`, значит для `/v1/sys/metrics` токен мониторинга не нужен. Сейчас добавлю отдельный runbook с готовыми scrape config, alert rules и списком панелей Grafana.

**Updating metric templates**

I need to update the template to include an optional variable for the user’s request for настройка. I’m thinking of adding this to the Ansible template with a default value of true. However, I noticed there's a contradictory token policy from previous documentation. If I'm using unauthenticated metrics access, no token or policy is required for certain endpoints. I’ll also need to be careful about the names for Vault metrics in Prometheus, as they can vary.


Вижу расхождение: в живом `/data/vault/config/config.hcl` у тебя есть `listener.telemetry.unauthenticated_metrics_access = true`, а в Ansible template этого пока нет. Я добавлю это в template, чтобы следующий deploy не затёр метрики, и в инструкции буду исходить из текущей схемы: Prometheus ходит на `/v1/sys/metrics` без Vault token, но доступ ограничиваем сетево.

**Reconciling configuration details**

The user mentioned, "у меня такой конфиг." I need to look at the documentation since the ADR states a token, but the live configuration indicates unauthenticated access. I should update the documentation to clarify that unauthenticated metrics are enabled on the listener and that access must be restricted by network settings for Prometheus only. The monitoring policy is optional for health and leader, but I’m wondering if the policy I created might not be necessary now. Maybe I need to update the architecture document too.

ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/roles/vault-oss-deploy/templates/vault_config.hcl.j2
@@
 listener "tcp" {
   address         = "0.0.0.0:8200"
   tls_disable     = false
   tls_cert_file   = "/vault/tls/{{ tls_cert_file }}"
   tls_key_file    = "/vault/tls/{{ tls_key_file }}"
+  telemetry {
+    unauthenticated_metrics_access = true
+  }
 }
*** End Patch


Чтобы не писать alert rules «в вакууме», проверю реальные имена метрик из твоего Vault endpoint. Если доступ с моей среды есть, подставлю точные PromQL-запросы.

**Drafting documentation and metrics**

I need to create a document and possibly include some examples. For metrics, I think we should establish robust alert rules using `up` and sample metric names. I’m considering using PromQL with Vault metric names like `vault_core_unsealed`, `vault_core_active`, and `vault_core_standby`, but I should confirm these from the Vault documentation. I’m thinking about checking `/sys/metrics` via curl. I might run the curl command to get the actual metric names, avoiding network issues.
**Choosing a method for requests**

I'm considering using Python requests, but there might be TLS complications. A simpler approach could be using a shell command, perhaps with Python’s heredoc to streamline the process, or I could opt for `curl` combined with `rg`. The guideline suggests avoiding commands like grep in favor of rg, which is fine since this isn't a file search. I think running the curl command directly might be the way to go!

Shell

python - <<'PY'
import ssl, urllib.request
url='https://p-pki-as-01.lmru.tech:8200/v1/sys/metrics?format=prometheus'
ctx=ssl._create_unverified_context()
try:
    data=urllib.request.urlopen(url, context=ctx, timeout=10).read().decode('utf-8', 'replace')
except Exception as e:
    print(f'ERROR: {e}')
    raise SystemExit(0)
for line in data.splitlines():
    if line.startswith('# HELP vault_') or line.startswith('# TYPE vault_'):
        name=line.split()[2] if len(line.split()) > 2 else ''
        if any(s in name for s in ['core_', 'raft', 'expire', 'token', 'audit', 'runtime', 'pki', 'http', 'route']):
            print(line)
PY

Inspect live Vault metric names


Метрики доступны без токена и имена подтвердились: `vault_core_active`, `vault_core_unsealed`, `vault_core_response_status_code`, `vault_raft_storage_follower_last_heartbeat_ms`, `vault_expire_num_leases`, `vault_token_count`, runtime/raft метрики. Теперь оформляю инструкцию с конкретными Prometheus rules и панелями Grafana.

**Considering response metrics**

I need to get the status code label names for the response metric. I wonder how to best retrieve that information, and I want to ensure that I'm not missing any specifics. It feels like a straightforward task, but I want to be meticulous about it. Getting the right label names is essential for accurate metrics. Let me see how I can access that effectively!

python - <<'PY'
import ssl, urllib.request
url='https://p-pki-as-01.lmru.tech:8200/v1/sys/metrics?format=prometheus'
ctx=ssl._create_unverified_context()
data=urllib.request.urlopen(url, context=ctx, timeout=10).read().decode('utf-8', 'replace')
for wanted in ['vault_core_response_status_code', 'vault_core_active', 'vault_core_unsealed', 'vault_raft_storage_follower_last_heartbeat_ms', 'vault_raft_storage_follower_applied_index_delta']:
    print(f'--- {wanted} ---')
    c=0
    for line in data.splitlines():
        if line.startswith(wanted):
            print(line)
            c += 1
            if c >= 8:
                break
PY

Inspect Vault metric labels


**Organizing file structure**

I need to add files, and for the YAML rules, it seems like I should create a monitoring directory. I’m thinking I can use the shell command to list the parent directory before I actually make the new directory with `mkdir`. That way, I ensure I’m on the right track and not making any mistakes with the file structure. Let’s make sure everything’s organized properly!

List repository root before adding monitoring dirs


Чтобы было не только в Markdown, добавлю рядом готовые артефакты: `monitoring/prometheus/vault-pki-alerts.yml` с Alertmanager rules и JSON-модель Grafana dashboard, которую можно импортировать.

mkdir -p "monitoring/prometheus" "monitoring/grafana"

Create monitoring config directories


ApplyPatch

Input:
*** Begin Patch
*** Add File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/monitoring/prometheus/vault-pki-alerts.yml
+---
+groups:
+  - name: vault-pki-availability
+    rules:
+      - alert: VaultNodeDown
+        expr: up{job="vault-pki"} == 0
+        for: 2m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault node is unreachable"
+          description: "Prometheus cannot scrape {{ $labels.instance }}."
+
+      - alert: VaultNodeSealed
+        expr: vault_core_unsealed{job="vault-pki"} == 0
+        for: 1m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault node is sealed"
+          description: "{{ $labels.instance }} reports vault_core_unsealed=0."
+
+      - alert: VaultNoActiveLeader
+        expr: sum(vault_core_active{job="vault-pki"}) < 1
+        for: 2m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault cluster has no active leader"
+          description: "No scraped Vault node reports vault_core_active=1."
+
+      - alert: VaultTooManyActiveLeaders
+        expr: sum(vault_core_active{job="vault-pki"}) > 1
+        for: 2m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault cluster reports more than one active leader"
+          description: "More than one Vault node reports vault_core_active=1."
+
+  - name: vault-pki-raft
+    rules:
+      - alert: VaultRaftFollowerHeartbeatLag
+        expr: vault_raft_storage_follower_last_heartbeat_ms{job="vault-pki"} > 10000
+        for: 5m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault Raft follower heartbeat lag is high"
+          description: "Leader sees peer {{ $labels.peer_id }} heartbeat lag > 10s."
+
+      - alert: VaultRaftFollowerIndexLag
+        expr: vault_raft_storage_follower_applied_index_delta{job="vault-pki"} > 1000
+        for: 5m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault Raft follower applied index lag is high"
+          description: "Peer {{ $labels.peer_id }} is behind leader by > 1000 log entries."
+
+  - name: vault-pki-errors
+    rules:
+      - alert: VaultApi5xxErrors
+        expr: increase(vault_core_response_status_code{job="vault-pki",type="5xx"}[5m]) > 0
+        for: 1m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault API returns 5xx errors"
+          description: "{{ $labels.instance }} returned 5xx responses in the last 5m."
+
+      - alert: VaultApi4xxSpike
+        expr: increase(vault_core_response_status_code{job="vault-pki",type="4xx"}[10m]) > 20
+        for: 2m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault API 4xx spike"
+          description: "{{ $labels.instance }} returned >20 4xx responses in the last 10m."
+
+  - name: vault-pki-capacity
+    rules:
+      - alert: VaultLeaseCountHigh
+        expr: vault_expire_num_leases{job="vault-pki"} > 10000
+        for: 15m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault lease count is high"
+          description: "{{ $labels.instance }} has more than 10000 leases."
+
+      - alert: VaultTokenCountHigh
+        expr: vault_token_count{job="vault-pki"} > 5000
+        for: 15m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault token count is high"
+          description: "{{ $labels.instance }} has more than 5000 tokens."
+
+      - alert: VaultRuntimeGoroutinesHigh
+        expr: vault_runtime_num_goroutines{job="vault-pki"} > 5000
+        for: 15m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault runtime goroutine count is high"
+          description: "{{ $labels.instance }} has more than 5000 goroutines."
*** End Patch


**Creating dashboard JSON**

I need to create a JSON for a Grafana dashboard, but it shouldn't be too huge. The dashboard can include panels for cluster active status, unsealed status, API status code rate, request rate, leases, tokens, raft follower heartbeat, index lag, runtime memory, and goroutines. I want to ensure it’s simple and effective. I'll make sure to use Grafana 9 JSON format for this. Let's get started!

Input:
*** Begin Patch
*** Add File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/monitoring/grafana/vault-pki-dashboard.json
+{
+  "annotations": {
+    "list": []
+  },
+  "editable": true,
+  "fiscalYearStartMonth": 0,
+  "graphTooltip": 0,
+  "id": null,
+  "links": [],
+  "liveNow": false,
+  "panels": [
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "red",
+                "value": null
+              },
+              {
+                "color": "green",
+                "value": 1
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 6,
+        "x": 0,
+        "y": 0
+      },
+      "id": 1,
+      "options": {
+        "colorMode": "background",
+        "graphMode": "none",
+        "justifyMode": "center",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "targets": [
+        {
+          "expr": "sum(vault_core_active{job=\"vault-pki\"})",
+          "legendFormat": "active leaders",
+          "refId": "A"
+        }
+      ],
+      "title": "Active Leader Count",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "red",
+                "value": null
+              },
+              {
+                "color": "green",
+                "value": 3
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 6,
+        "x": 6,
+        "y": 0
+      },
+      "id": 2,
+      "options": {
+        "colorMode": "background",
+        "graphMode": "none",
+        "justifyMode": "center",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "targets": [
+        {
+          "expr": "sum(vault_core_unsealed{job=\"vault-pki\"})",
+          "legendFormat": "unsealed nodes",
+          "refId": "A"
+        }
+      ],
+      "title": "Unsealed Nodes",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "red",
+                "value": 1
+              }
+            ]
+          }
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 6,
+        "x": 12,
+        "y": 0
+      },
+      "id": 3,
+      "options": {
+        "colorMode": "background",
+        "graphMode": "none",
+        "justifyMode": "center",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "targets": [
+        {
+          "expr": "sum(up{job=\"vault-pki\"} == 0)",
+          "legendFormat": "down nodes",
+          "refId": "A"
+        }
+      ],
+      "title": "Down Nodes",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "mappings": [],
+          "thresholds": {
+            "mode": "absolute",
+            "steps": [
+              {
+                "color": "green",
+                "value": null
+              },
+              {
+                "color": "orange",
+                "value": 1000
+              }
+            ]
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 4,
+        "w": 6,
+        "x": 18,
+        "y": 0
+      },
+      "id": 4,
+      "options": {
+        "colorMode": "background",
+        "graphMode": "none",
+        "justifyMode": "center",
+        "orientation": "auto",
+        "reduceOptions": {
+          "calcs": [
+            "lastNotNull"
+          ],
+          "fields": "",
+          "values": false
+        },
+        "textMode": "auto"
+      },
+      "targets": [
+        {
+          "expr": "sum(vault_expire_num_leases{job=\"vault-pki\"})",
+          "legendFormat": "leases",
+          "refId": "A"
+        }
+      ],
+      "title": "Leases",
+      "type": "stat"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 4
+      },
+      "id": 5,
+      "targets": [
+        {
+          "expr": "sum by (instance) (rate(vault_core_handle_request_count{job=\"vault-pki\"}[5m]))",
+          "legendFormat": "{{ instance }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Vault API Request Rate",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "reqps"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 4
+      },
+      "id": 6,
+      "targets": [
+        {
+          "expr": "sum by (type) (rate(vault_core_response_status_code{job=\"vault-pki\"}[5m]))",
+          "legendFormat": "{{ type }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Vault Response Codes",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "ms"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 12
+      },
+      "id": 7,
+      "targets": [
+        {
+          "expr": "vault_raft_storage_follower_last_heartbeat_ms{job=\"vault-pki\"}",
+          "legendFormat": "{{ peer_id }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Raft Follower Heartbeat Lag",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 12
+      },
+      "id": 8,
+      "targets": [
+        {
+          "expr": "vault_raft_storage_follower_applied_index_delta{job=\"vault-pki\"}",
+          "legendFormat": "{{ peer_id }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Raft Follower Applied Index Delta",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "bytes"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 20
+      },
+      "id": 9,
+      "targets": [
+        {
+          "expr": "vault_runtime_alloc_bytes{job=\"vault-pki\"}",
+          "legendFormat": "{{ instance }}",
+          "refId": "A"
+        }
+      ],
+      "title": "Runtime Allocated Memory",
+      "type": "timeseries"
+    },
+    {
+      "datasource": {
+        "type": "prometheus",
+        "uid": "${DS_PROMETHEUS}"
+      },
+      "fieldConfig": {
+        "defaults": {
+          "custom": {
+            "drawStyle": "line",
+            "fillOpacity": 10,
+            "lineInterpolation": "linear",
+            "lineWidth": 1,
+            "showPoints": "never"
+          },
+          "unit": "none"
+        },
+        "overrides": []
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 20
+      },
+      "id": 10,
+      "targets": [
+        {
+          "expr": "vault_token_count{job=\"vault-pki\"}",
+          "legendFormat": "{{ instance }}",
+          "refId": "A"
+        },
+        {
+          "expr": "vault_runtime_num_goroutines{job=\"vault-pki\"}",
+          "legendFormat": "goroutines {{ instance }}",
+          "refId": "B"
+        }
+      ],
+      "title": "Tokens and Goroutines",
+      "type": "timeseries"
+    }
+  ],
+  "refresh": "30s",
+  "schemaVersion": 38,
+  "style": "dark",
+  "tags": [
+    "vault",
+    "pki",
+    "lmru"
+  ],
+  "templating": {
+    "list": []
+  },
+  "time": {
+    "from": "now-6h",
+    "to": "now"
+  },
+  "timepicker": {},
+  "timezone": "browser",
+  "title": "Vault PKI",
+  "uid": "vault-pki-lmru",
+  "version": 1,
+  "weekStart": ""
+}
*** End Patch


**Developing CA expiry scripts**

I need a script for fetching CA expiry from Vault’s API and parsing the `notAfter` field, pushing it to a text file collector. The user asked for monitoring, so I'll create a script for that. It's important to note that cert/ca is public, so no secrets are needed. I'll make the script generic, possibly including rules for `vault_pki_ca_expiry_seconds` and ensuring it's executable by potentially mentioning `chmod`.

I need to confirm the backup path from the user.
**Creating a script for node exporter**

I’m thinking about using node exporter and starting to create a script for it. I wonder if I need to adjust the permissions with chmod — that’s probably something to consider. Should I be cautious about any secrets too? It might be important to avoid including sensitive information in the script. Let's make sure it's straightforward and secure as I proceed with this!

Input:
*** Begin Patch
*** Add File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/scripts/vault-pki-textfile-metrics.sh
+#!/usr/bin/env bash
+set -euo pipefail
+
+VAULT_ADDR="${VAULT_ADDR:-https://pki.lmru.tech}"
+PKI_MOUNTS="${PKI_MOUNTS:-pki-stage}"
+TEXTFILE_DIR="${TEXTFILE_DIR:-/var/lib/node_exporter/textfile_collector}"
+OUTPUT_FILE="${OUTPUT_FILE:-${TEXTFILE_DIR}/vault_pki.prom}"
+BACKUP_GLOB="${BACKUP_GLOB:-}"
+
+tmp_file="$(mktemp)"
+trap 'rm -f "${tmp_file}"' EXIT
+
+now="$(date +%s)"
+
+{
+  echo "# HELP vault_pki_ca_not_after_timestamp_seconds Unix timestamp of Vault PKI CA certificate expiration."
+  echo "# TYPE vault_pki_ca_not_after_timestamp_seconds gauge"
+  echo "# HELP vault_pki_ca_expires_in_seconds Seconds before Vault PKI CA certificate expiration."
+  echo "# TYPE vault_pki_ca_expires_in_seconds gauge"
+} >> "${tmp_file}"
+
+for mount in ${PKI_MOUNTS}; do
+  cert_file="$(mktemp)"
+  if curl -fsSk "${VAULT_ADDR}/v1/${mount}/cert/ca" -o "${cert_file}"; then
+    not_after_raw="$(openssl x509 -in "${cert_file}" -noout -enddate | sed 's/^notAfter=//')"
+    not_after_ts="$(date -d "${not_after_raw}" +%s)"
+    echo "vault_pki_ca_not_after_timestamp_seconds{mount=\"${mount}\"} ${not_after_ts}" >> "${tmp_file}"
+    echo "vault_pki_ca_expires_in_seconds{mount=\"${mount}\"} $((not_after_ts - now))" >> "${tmp_file}"
+  else
+    echo "vault_pki_ca_expires_in_seconds{mount=\"${mount}\"} -1" >> "${tmp_file}"
+  fi
+  rm -f "${cert_file}"
+done
+
+if [[ -n "${BACKUP_GLOB}" ]]; then
+  latest_backup="$(compgen -G "${BACKUP_GLOB}" | sort | tail -n 1 || true)"
+  if [[ -n "${latest_backup}" ]]; then
+    latest_mtime="$(stat -c %Y "${latest_backup}")"
+    latest_size="$(stat -c %s "${latest_backup}")"
+    {
+      echo "# HELP vault_pki_backup_latest_mtime_seconds Unix timestamp of latest local Vault Raft snapshot."
+      echo "# TYPE vault_pki_backup_latest_mtime_seconds gauge"
+      echo "vault_pki_backup_latest_mtime_seconds ${latest_mtime}"
+      echo "# HELP vault_pki_backup_latest_size_bytes Size of latest local Vault Raft snapshot."
+      echo "# TYPE vault_pki_backup_latest_size_bytes gauge"
+      echo "vault_pki_backup_latest_size_bytes ${latest_size}"
+    } >> "${tmp_file}"
+  fi
+fi
+
+install -d -m 0755 "${TEXTFILE_DIR}"
+install -m 0644 "${tmp_file}" "${OUTPUT_FILE}"
*** End Patch


Shell

Make textfile metrics script executable


**Updating alert rules**

I need to update the alert rules based on the text file group. I should check what's currently in place and see how I can manage the updates more effectively. It’s important to ensure that the new rules reflect the latest information accurately. I’ll keep everything organized and make sure the changes are easy to understand for whoever might need to reference them later. Let's finalize this process efficiently!

ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/monitoring/prometheus/vault-pki-alerts.yml
@@
   - name: vault-pki-capacity
     rules:
@@
           summary: "Vault runtime goroutine count is high"
           description: "{{ $labels.instance }} has more than 5000 goroutines."
+
+  - name: vault-pki-certificates-and-backup
+    rules:
+      - alert: VaultPkiCaExpiryWarning
+        expr: vault_pki_ca_expires_in_seconds < 60 * 24 * 3600
+        for: 1h
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault PKI CA expires in less than 60 days"
+          description: "Mount {{ $labels.mount }} CA expires in less than 60 days."
+
+      - alert: VaultPkiCaExpiryCritical
+        expr: vault_pki_ca_expires_in_seconds < 30 * 24 * 3600
+        for: 1h
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault PKI CA expires in less than 30 days"
+          description: "Mount {{ $labels.mount }} CA expires in less than 30 days."
+
+      - alert: VaultPkiCaMetricsMissing
+        expr: absent(vault_pki_ca_expires_in_seconds{mount="pki-stage"})
+        for: 30m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault PKI CA expiry metric is missing"
+          description: "Textfile metric vault_pki_ca_expires_in_seconds for pki-stage is missing."
+
+      - alert: VaultRaftBackupStale
+        expr: time() - vault_pki_backup_latest_mtime_seconds > 2 * 3600
+        for: 30m
+        labels:
+          severity: critical
+          service: vault-pki
+        annotations:
+          summary: "Vault Raft backup is stale"
+          description: "Latest local Vault Raft snapshot is older than 2 hours."
+
+      - alert: VaultRaftBackupTooSmall
+        expr: vault_pki_backup_latest_size_bytes < 1024 * 1024
+        for: 30m
+        labels:
+          severity: warning
+          service: vault-pki
+        annotations:
+          summary: "Vault Raft backup size is suspiciously small"
+          description: "Latest local Vault Raft snapshot is smaller than 1 MiB."
*** End Patch


**Considering Prometheus expressions**

I'm looking into a potential Prometheus expression: `60 * 24 * 3600`. I think scalar arithmetic in PromQL should work fine, but I also need to figure out how to handle alerts when metrics are absent. If the backup metric is missing, the expression shouldn't trigger an alert, so I might consider adding an "absent" check. Also, I should run yamllint to check for any issues, like line lengths.

Shell

yamllint "monitoring/prometheus/vault-pki-alerts.yml" && python -m json.tool "monitoring/grafana/vault-pki-dashboard.json" >/dev/null && bash -n "scripts/vault-pki-textfile-metrics.sh"

Validate monitoring artifacts


**Considering the syntax and fixes**

I'm thinking about using the `ApplyPatch` and whether to wrap it with folded scalars. I can see that the Prometheus expression can be `>-`, and it seems that descriptions can be split with folded scalars. However, there seems to be an issue that needs fixing. I’ll need to explore this more to ensure I’m applying the right approach. Let's get it straightened out!

ApplyPatch

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/monitoring/prometheus/vault-pki-alerts.yml
@@
       - alert: VaultRaftFollowerHeartbeatLag
-        expr: vault_raft_storage_follower_last_heartbeat_ms{job="vault-pki"} > 10000
+        expr: >-
+          vault_raft_storage_follower_last_heartbeat_ms{job="vault-pki"}
+          > 10000
@@
-          description: "Leader sees peer {{ $labels.peer_id }} heartbeat lag > 10s."
+          description: >-
+            Leader sees peer {{ $labels.peer_id }} heartbeat lag > 10s.
@@
       - alert: VaultRaftFollowerIndexLag
-        expr: vault_raft_storage_follower_applied_index_delta{job="vault-pki"} > 1000
+        expr: >-
+          vault_raft_storage_follower_applied_index_delta{job="vault-pki"}
+          > 1000
@@
-          description: "Peer {{ $labels.peer_id }} is behind leader by > 1000 log entries."
+          description: >-
+            Peer {{ $labels.peer_id }} is behind leader by > 1000 log entries.
@@
       - alert: VaultApi5xxErrors
-        expr: increase(vault_core_response_status_code{job="vault-pki",type="5xx"}[5m]) > 0
+        expr: >-
+          increase(vault_core_response_status_code{
+          job="vault-pki",type="5xx"}[5m]) > 0
@@
-          description: "{{ $labels.instance }} returned 5xx responses in the last 5m."
+          description: >-
+            {{ $labels.instance }} returned 5xx responses in the last 5m.
@@
       - alert: VaultApi4xxSpike
-        expr: increase(vault_core_response_status_code{job="vault-pki",type="4xx"}[10m]) > 20
+        expr: >-
+          increase(vault_core_response_status_code{
+          job="vault-pki",type="4xx"}[10m]) > 20
@@
-          description: "{{ $labels.instance }} returned >20 4xx responses in the last 10m."
+          description: >-
+            {{ $labels.instance }} returned >20 4xx responses in the last 10m.
@@
-          description: "Mount {{ $labels.mount }} CA expires in less than 60 days."
+          description: >-
+            Mount {{ $labels.mount }} CA expires in less than 60 days.
@@
-          description: "Mount {{ $labels.mount }} CA expires in less than 30 days."
+          description: >-
+            Mount {{ $labels.mount }} CA expires in less than 30 days.
@@
-          description: "Textfile metric vault_pki_ca_expires_in_seconds for pki-stage is missing."
+          description: >-
+            Textfile metric vault_pki_ca_expires_in_seconds for pki-stage
+            is missing.
*** End Patch


Shell

yamllint "monitoring/prometheus/vault-pki-alerts.yml" && python -m json.tool "monitoring/grafana/vault-pki-dashboard.json" >/dev/null && bash -n "scripts/vault-pki-textfile-metrics.sh"

Validate monitoring artifacts after fixes


ApplyPatch

Input:
*** Begin Patch
*** Add File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/vault-pki-monitoring-runbook.md
+# Настройка мониторинга Vault PKI
+
+Документ для ручной настройки мониторинга текущего Vault PKI (`pki.lmru.tech`).
+
+Текущий Vault config уже содержит:
+
+```hcl
+listener "tcp" {
+  address         = "0.0.0.0:8200"
+  tls_disable     = false
+  tls_cert_file   = "/vault/tls/wildcard.lmru.tech.crt"
+  tls_key_file    = "/vault/tls/wildcard.lmru.tech.key"
+  telemetry {
+    unauthenticated_metrics_access = true
+  }
+}
+
+telemetry {
+  disable_hostname = true
+  prometheus_retention_time = "12h"
+}
+```
+
+Это означает:
+
+* Prometheus может читать `/v1/sys/metrics?format=prometheus` **без Vault token**.
+* Доступ к `:8200/v1/sys/metrics` нужно ограничить сетево: только Prometheus/monitoring-сегмент.
+* Собирать метрики нужно с каждого Vault-узла отдельно, не только через LB.
+
+## 1. Проверить метрики руками
+
+```bash
+for node in \
+  p-pki-as-01.lmru.tech \
+  p-pki-as-02.lmru.tech \
+  p-pki-as-03.lmru.tech; do
+  echo "== ${node} =="
+  curl -sk "https://${node}:8200/v1/sys/metrics?format=prometheus" \
+    | rg '^(vault_core_active|vault_core_unsealed|vault_raft_storage_follower_last_heartbeat_ms|vault_expire_num_leases|vault_token_count)'
+done
+```
+
+Ожидаемо:
+
+* `vault_core_unsealed = 1` на всех узлах;
+* `sum(vault_core_active) = 1` по кластеру;
+* `vault_raft_storage_follower_last_heartbeat_ms` есть на leader-узле для follower-нод;
+* `vault_core_response_status_code` содержит коды ответов API.
+
+## 2. Prometheus scrape config
+
+Добавить job в `prometheus.yml`:
+
+```yaml
+scrape_configs:
+  - job_name: vault-pki
+    scheme: https
+    metrics_path: /v1/sys/metrics
+    params:
+      format:
+        - prometheus
+    scrape_interval: 30s
+    scrape_timeout: 10s
+    static_configs:
+      - targets:
+          - p-pki-as-01.lmru.tech:8200
+          - p-pki-as-02.lmru.tech:8200
+          - p-pki-as-03.lmru.tech:8200
+        labels:
+          cluster: pki-lmru
+          env: prod
+    tls_config:
+      # Лучше положить корпоративный CA и проверять цепочку.
+      # ca_file: /etc/prometheus/ca/lmru-root-ca.pem
+
+      # Временный вариант, если Prometheus пока не доверяет корпоративному CA.
+      # После загрузки CA заменить на ca_file.
+      insecure_skip_verify: true
+```
+
+Проверить и перезагрузить Prometheus:
+
+```bash
+promtool check config /etc/prometheus/prometheus.yml
+systemctl reload prometheus
+```
+
+Проверка в Prometheus:
+
+```promql
+up{job="vault-pki"}
+sum(vault_core_active{job="vault-pki"})
+sum(vault_core_unsealed{job="vault-pki"})
+```
+
+## 3. Alert rules
+
+Готовый файл rules добавлен в репозиторий:
+
+```text
+monitoring/prometheus/vault-pki-alerts.yml
+```
+
+Скопировать файл на Prometheus-сервер:
+
+```bash
+install -m 0644 monitoring/prometheus/vault-pki-alerts.yml \
+  /etc/prometheus/rules/vault-pki-alerts.yml
+```
+
+Подключить в `prometheus.yml`:
+
+```yaml
+rule_files:
+  - /etc/prometheus/rules/vault-pki-alerts.yml
+```
+
+Проверить:
+
+```bash
+promtool check rules /etc/prometheus/rules/vault-pki-alerts.yml
+promtool check config /etc/prometheus/prometheus.yml
+systemctl reload prometheus
+```
+
+Минимальный набор алертов:
+
+* `VaultNodeDown` — Prometheus не может собрать метрики с узла.
+* `VaultNodeSealed` — Vault sealed.
+* `VaultNoActiveLeader` — нет active leader.
+* `VaultTooManyActiveLeaders` — больше одного leader.
+* `VaultRaftFollowerHeartbeatLag` — follower давно не получает heartbeat.
+* `VaultRaftFollowerIndexLag` — follower отстал по Raft index.
+* `VaultApi5xxErrors` — 5xx от Vault API.
+* `VaultApi4xxSpike` — всплеск 4xx, часто ошибка AppRole/policy/consumer.
+* `VaultLeaseCountHigh` — много lease.
+* `VaultTokenCountHigh` — много token.
+* `VaultRuntimeGoroutinesHigh` — резкий рост goroutine.
+* `VaultPkiCaExpiryWarning/Critical` — срок CA меньше 60/30 дней.
+* `VaultRaftBackupStale` — локальный snapshot старше 2 часов.
+
+## 4. Alertmanager
+
+Пример маршрутизации для `service="vault-pki"`:
+
+```yaml
+route:
+  group_by:
+    - alertname
+    - cluster
+    - service
+  group_wait: 30s
+  group_interval: 5m
+  repeat_interval: 4h
+  receiver: default
+  routes:
+    - receiver: vault-pki-critical
+      matchers:
+        - service="vault-pki"
+        - severity="critical"
+      repeat_interval: 30m
+    - receiver: vault-pki-warning
+      matchers:
+        - service="vault-pki"
+        - severity="warning"
+      repeat_interval: 4h
+
+receivers:
+  - name: default
+    webhook_configs:
+      - url: http://alert-webhook.default.svc:8080/alert
+
+  - name: vault-pki-critical
+    webhook_configs:
+      - url: http://alert-webhook.default.svc:8080/vault-pki-critical
+        send_resolved: true
+
+  - name: vault-pki-warning
+    webhook_configs:
+      - url: http://alert-webhook.default.svc:8080/vault-pki-warning
+        send_resolved: true
+```
+
+Если у вас не webhook, а Telegram/Slack/почта — заменить только блок `receivers`, labels в rules уже готовы.
+
+## 5. Grafana dashboard
+
+Готовый dashboard:
+
+```text
+monitoring/grafana/vault-pki-dashboard.json
+```
+
+Импорт:
+
+1. Grafana → Dashboards → New → Import.
+2. Загрузить `monitoring/grafana/vault-pki-dashboard.json`.
+3. Выбрать Prometheus datasource.
+4. Проверить, что job называется `vault-pki`.
+
+Панели:
+
+* Active Leader Count — должен быть `1`.
+* Unsealed Nodes — должен быть `3`.
+* Down Nodes — должен быть `0`.
+* Leases — текущий объём lease.
+* Vault API Request Rate — общий поток запросов.
+* Vault Response Codes — 2xx/4xx/5xx.
+* Raft Follower Heartbeat Lag — задержка heartbeat follower-нод.
+* Raft Follower Applied Index Delta — отставание follower-нод.
+* Runtime Allocated Memory.
+* Tokens and Goroutines.
+
+## 6. Метрики срока действия CA и backup
+
+Vault не отдаёт срок действия intermediate CA удобной Prometheus-метрикой. Для этого добавлен textfile-скрипт:
+
+```text
+scripts/vault-pki-textfile-metrics.sh
+```
+
+Он пишет метрики для node_exporter textfile collector:
+
+* `vault_pki_ca_not_after_timestamp_seconds{mount="pki-stage"}`
+* `vault_pki_ca_expires_in_seconds{mount="pki-stage"}`
+* `vault_pki_backup_latest_mtime_seconds`
+* `vault_pki_backup_latest_size_bytes`
+
+Установка на backup-ноду (`p-pki-as-01.lmru.tech`):
+
+```bash
+install -m 0755 scripts/vault-pki-textfile-metrics.sh \
+  /usr/local/bin/vault-pki-textfile-metrics.sh
+
+install -d -m 0755 /var/lib/node_exporter/textfile_collector
+```
+
+Cron:
+
+```cron
+*/15 * * * * VAULT_ADDR=https://pki.lmru.tech PKI_MOUNTS="pki-stage" TEXTFILE_DIR=/var/lib/node_exporter/textfile_collector BACKUP_GLOB="/path/to/local/raft/snapshots/*" /usr/local/bin/vault-pki-textfile-metrics.sh
+```
+
+`BACKUP_GLOB` заменить на фактический локальный каталог snapshot на `p-pki-as-01.lmru.tech`.
+
+Проверить результат:
+
+```bash
+/usr/local/bin/vault-pki-textfile-metrics.sh
+cat /var/lib/node_exporter/textfile_collector/vault_pki.prom
+```
+
+Если node_exporter ещё не включён, добавить отдельный scrape job:
+
+```yaml
+scrape_configs:
+  - job_name: node-vault-pki
+    static_configs:
+      - targets:
+          - p-pki-as-01.lmru.tech:9100
+          - p-pki-as-02.lmru.tech:9100
+          - p-pki-as-03.lmru.tech:9100
+```
+
+## 7. Что смотреть в первую очередь
+
+Быстрая диагностика:
+
+```promql
+up{job="vault-pki"}
+sum(vault_core_active{job="vault-pki"})
+sum(vault_core_unsealed{job="vault-pki"})
+rate(vault_core_response_status_code{job="vault-pki",type="5xx"}[5m])
+rate(vault_core_response_status_code{job="vault-pki",type="4xx"}[5m])
+vault_raft_storage_follower_last_heartbeat_ms{job="vault-pki"}
+vault_raft_storage_follower_applied_index_delta{job="vault-pki"}
+vault_expire_num_leases{job="vault-pki"}
+vault_token_count{job="vault-pki"}
+vault_pki_ca_expires_in_seconds{mount="pki-stage"} / 86400
+time() - vault_pki_backup_latest_mtime_seconds
+```
+
+Нормальное состояние:
+
+* все `up=1`;
+* `sum(vault_core_active)=1`;
+* `sum(vault_core_unsealed)=3`;
+* нет 5xx;
+* 4xx единичные или объяснимые;
+* heartbeat lag обычно секунды, не десятки секунд;
+* applied index delta около 0;
+* backup младше 2 часов;
+* CA до истечения больше 60 дней.
+
+## 8. Тестовый прогон алертов
+
+Безопасные проверки:
+
+1. В Prometheus UI открыть rule `VaultNodeDown` и временно поменять один target в scrape config на несуществующий порт. После проверки вернуть обратно.
+2. Остановить cron textfile-метрик на backup-ноде и дождаться `VaultRaftBackupStale` в тестовом окне. После проверки включить обратно.
+3. Проверить routing в Alertmanager: alert должен уйти в receiver `vault-pki-critical` или `vault-pki-warning`.
+
+Не тестировать на рабочем контуре:
+
+* не seal/unseal для проверки алерта без отдельного окна;
+* не трогать issuer/default issuer;
+* не отключать audit device.
*** End Patch


Теперь синхронизирую архитектурные документы с фактом из твоего config: метрики сейчас без Vault token, поэтому уберу противоречие про обязательный monitoring token из ADR/architecture и оставлю token-policy как запасной вариант, если потом отключите anonymous metrics.

**Updating ADR documentation**

I need to update the ADR docs to reflect that unauthenticated metrics shouldn’t rely on the token. We already included the token in the ADR and the architecture. So, I’ll need to modify it to align with the current configuration. In the architecture section, we wrote about the token policy. I also need to adjust the text to note that unauth is network-limited, and monitoring policy is optional if disabled for system health or leader. I'll read through the relevant sections.


**Considering patch architecture**

I need to think about how to create the patch architecture. I’m also wondering if I should avoid mentioning the listener telemetry in the top of the template. Hm, what’s the best approach here? It’s important to make sure I’m giving clear guidance, and I wonder if omitting that detail might be more beneficial in the long run. Let’s see how that affects the overall structure!

Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/adr/architecture.md
@@
 В конфигурации Vault уже включён блок:

 ```hcl
+listener "tcp" {
+  telemetry {
+    unauthenticated_metrics_access = true
+  }
+}
+
 telemetry {
   disable_hostname = true
   prometheus_retention_time = "12h"
 }
 ```
@@
-Доступ к метрикам выполняется по отдельному токену с policy `root_policy_monitoring`.
-Анонимный доступ к `/sys/metrics` не включаем: токен мониторинга хранится в секрет-хранилище системы мониторинга и ротируется регламентно.
+В текущем контуре доступ к `/sys/metrics` разрешён без Vault token через `unauthenticated_metrics_access = true`. Поэтому обязательный контроль здесь — сетевой: endpoint `:8200/v1/sys/metrics` должен быть доступен только Prometheus/monitoring-сегменту и администраторам.

-Policy:
+Если позже решим отключить unauthenticated metrics, используется отдельная read-only policy:
@@
-Policy добавлена в Ansible setup как `roles/vault-oss-setup/files/root_policy_monitoring.hcl` и применяется вместе с остальными root policies.
+Policy добавлена в Ansible setup как `roles/vault-oss-setup/files/root_policy_monitoring.hcl` и применяется вместе с остальными root policies.

-Токен для мониторинга создаётся вручную администратором Vault и не хранится в Git:
+Токен для мониторинга в token-based варианте создаётся вручную администратором Vault и не хранится в Git:
@@
-Полученный token помещается в секрет-хранилище системы мониторинга. Если система мониторинга не умеет renew periodic token, вместо `-period` используется short TTL + регламентная ротация.
+Полученный token помещается в секрет-хранилище системы мониторинга. Если система мониторинга не умеет renew periodic token, вместо `-period` используется short TTL + регламентная ротация.
@@
-2. Prometheus/система мониторинга получает `/v1/sys/metrics?format=prometheus` по токену с `root_policy_monitoring`.
+2. Prometheus/система мониторинга получает `/v1/sys/metrics?format=prometheus` с каждого Vault-узла.
*** End Patch


Input:
*** Begin Patch
*** Update File: /home/ruslanpi/Documents/repos/lmru--devops-core--ansible-vault-oss/docs/adr/ADR-001-pki-architecture.md
@@
 9. **Мониторинг**:
    * Vault telemetry включена в конфигурации сервера (`prometheus_retention_time = "12h"`);
-   * Prometheus/система мониторинга читает `/v1/sys/metrics?format=prometheus` по отдельному токену с policy `root_policy_monitoring`;
+   * Prometheus/система мониторинга читает `/v1/sys/metrics?format=prometheus` с каждого Vault-узла; в текущем контуре включён `unauthenticated_metrics_access = true`, поэтому доступ к endpoint ограничивается сетево;
    * health-check выполняется по `/v1/sys/health`, при этом `standby` не считается аварией, а `sealed`/`uninitialized` считаются критическим состоянием;
*** End Patch


**Updating monitoring policies**

I think I need to consider updating the `root_policy_monitoring`, or maybe I could just keep it optional for now. I want to validate the yamllint documentation for YAML files. It’s probably a good idea to check the linting requirements and any artifacts that may be associated. Also, I might need to look into shell commands for JSON, YAML, and Bash to ensure everything is compliant. There’s a lot to think about!

Shell

yamllint "monitoring/prometheus/vault-pki-alerts.yml" "roles/vault-oss-setup/tasks/main.yml" && python -m json.tool "monitoring/grafana/vault-pki-dashboard.json" >/dev/null && bash -n "scripts/vault-pki-textfile-metrics.sh"

Validate YAML JSON and shell artifacts


Проверка прошла: `yamllint` для YAML, JSON dashboard и `bash -n` для скрипта — без ошибок. Сейчас быстро сверю список изменённых файлов, чтобы в ответе перечислить только нужное.

**Considering git commands**

I’m thinking about whether I should run `git diff`. It might not be absolutely necessary, but it could help me understand the changes better. I'm also wondering if I should just use `git status` to keep it simple. Both options are fine, but I'm leaning towards using `git diff` to get a clearer picture of the modifications. Let's see what changes are there!

Show changed files summary


Сделал конкретный runbook и готовые артефакты под твой текущий config с:

```hcl
unauthenticated_metrics_access = true
```

То есть Prometheus ходит на `/v1/sys/metrics?format=prometheus` без Vault token, а защита делается сетево.

Что добавил:

- `docs/vault-pki-monitoring-runbook.md` — пошаговая инструкция для тебя: Prometheus scrape config, Alertmanager route, импорт Grafana, textfile-метрики для CA/backup, проверочные PromQL.
- `monitoring/prometheus/vault-pki-alerts.yml` — готовые alert rules.
- `monitoring/grafana/vault-pki-dashboard.json` — импортируемый Grafana dashboard.
- `scripts/vault-pki-textfile-metrics.sh` — генерирует метрики срока действия CA и свежести local Raft snapshot для node_exporter textfile collector.
- `roles/vault-oss-deploy/templates/vault_config.hcl.j2` — добавил `listener.telemetry.unauthenticated_metrics_access = true`, чтобы Ansible template совпадал с реальным config и не затёр метрики при deploy.
- `docs/adr/architecture.md` и `docs/adr/ADR-001-pki-architecture.md` — синхронизировал с фактом, что сейчас метрики без токена, а `root_policy_monitoring` остаётся запасным вариантом для token-based схемы.

Ключевой scrape config из инструкции:

```yaml
scrape_configs:
  - job_name: vault-pki
    scheme: https
    metrics_path: /v1/sys/metrics
    params:
      format:
        - prometheus
    static_configs:
      - targets:
          - p-pki-as-01.lmru.tech:8200
          - p-pki-as-02.lmru.tech:8200
          - p-pki-as-03.lmru.tech:8200
```

Проверки прошли:

```bash
yamllint monitoring/prometheus/vault-pki-alerts.yml roles/vault-oss-setup/tasks/main.yml
python -m json.tool monitoring/grafana/vault-pki-dashboard.json
bash -n scripts/vault-pki-textfile-metrics.sh
```

Важно: я не применял это в живой Prometheus/Grafana/Alertmanager. Следующий практический шаг — взять `docs/vault-pki-monitoring-runbook.md` и по нему добавить scrape job, rule file и импортировать dashboard.

**Finalizing configurations**

I need to ensure that I mention this change hasn't been applied live yet. I should specify the exact files and steps involved. It’s important to note that the current configuration is unauthenticated, so no token is needed, and the root_policy_monitoring is optional. I also updated the template and I might want to include command snippets. Using bullet points could help make this clearer and more organized for easy reference.