Automatyczne zarządzanie Vault za pomocą OpenTofu

IAC do zarządzania środowiskiem Vault.

Są takie elementy infrastruktury, które na początku wyglądają niewinnie. Ot, postawisz Vaulta, wrzucisz kilka sekretów, wystawisz parę certyfikatów i lecisz dalej. A potem przychodzi życie: kolejna usługa, kolejny zespół, kolejny pipeline, ktoś prosi o dostęp „tylko do tego jednego patha”, a Ty orientujesz się, że Vault zaczyna przypominać szufladę z kablami — daj się w to wciągnąć na chwilę i nagle nie wiesz, co do czego prowadzi.

Właśnie w tym miejscu wjeżdża iac-vault: repozytorium, które robi z Vaulta coś, co da się normalnie utrzymać. Wszystko jest opisane w IaC, wersjonowane w Git, odtwarzalne i gotowe pod automatyzację. Bez ręcznego klikania, bez „a kto to zmieniał?”, bez tajemnej wiedzy w głowie jednej osoby.

To jest wpis o tym, jak ogarniam środowisko Vaulta przy pomocy OpenTofu — od PKI i KV, po polityki i uwierzytelnianie — i dlaczego to podejście jest po prostu… wygodne.


Dlaczego w ogóle chciałem to mieć jako IaC?

Bo Vault jest świetny, ale ręczne utrzymanie Vaulta w rosnącym środowisku to proszenie się o problemy:

  • polityki „tymczasowe”, które zostają na zawsze,
  • sekrety tworzone ad-hoc w różnych mountach,
  • PKI, które żyje własnym życiem,
  • brak jasnej odpowiedzi na pytanie: „jak to odtworzyć od zera?”

Z iac-vault mam jasny układ:

  • Git jest źródłem prawdy,
  • zmiany przechodzą przez Code Review,
  • Rollout jest przewidywalny,
  • a Vault przestaje być czarną skrzynką.
  • jest rozliczalność i możliwości analizowania błędów na danych.

Co to właściwie jest iac-vault?

iac-vault to repozytorium, które pełni rolę orkiestratora konfiguracji Vault. Ono „składa” środowisko z czytelnych klocków:

  • pki – pełna warstwa certyfikatów (Root, Intermediate, leafy),
  • secrets – przestrzenie KV na sekrety (KV v2),
  • policies – polityki ACL (czytelne, modularne),
  • auth – metody logowania i integracje (userpass + AppRole),
  • plus providers.tf, variables.tf i reszta „kleju”.

W praktyce: repo mówi Vaultowi dokładnie, co ma istnieć — i możesz to odtworzyć na nowej instancji w identyczny sposób.

flowchart TB
 subgraph s1["opentofu module"]
        n2["vault-pki"]
  end
    A["iac-vault"] --> n3["auth"] & n4["pki"] & n5["secrets"] & n6["polices"]
    n4 --> n2

Struktura katalogów jest prosta i „czyta się sama”:

.
├── auth                     # mechanizmy autoryzacji (userpass, AppRole)
├── main.tf                  # główny plik wykonawczy OpenTofu
├── pki                      # konfiguracja PKI oparta o moduł vault-pki
├── policies                 # polityki ACL Vault
├── providers.tf             # definicje providerów
├── secrets                  # konfiguracja backendów KV
└── variables.tf             # zmienne globalne

PKI: to tutaj robi się naprawdę fajnie

Jeżeli masz więcej niż kilka usług, to PKI potrafi być największym źródłem bólu. Wiele osób kończy z:

  • jednym intermediate „do wszystkiego”,
  • certyfikatami porozrzucanymi po systemach,
  • rotacją robioną ręcznie,
  • i tym cudownym uczuciem stresu, kiedy cert się kończy „jutro” 😉

W moim podejściu PKI jest robione przez moduł 👉 vault-pki opentofu

I to jest turbo wygodne, bo moduł automatyzuje cały, zdroworozsądkowy model:

  • Root CA istnieje i jest używany wyłącznie do podpisywania intermediates,
  • intermediates są podzielone logicznie (np. infrastructure i apps),
  • leafy są generowane deklaratywnie z jednej mapy.

To na starcie wygląda jak „dodatkowa robota”, ale w praktyce daje ogromny spokój: PKI robi się przewidywalne i banalne do rozbudowy.

Jak wygląda hierarchia?

stateDiagram
  direction TB
  [*] --> s1
  s1 --> s2
  s1 --> s5
  s1:Root CA
  s2:Infrastructure Intermediate CA
  s5:Apps Intermediate CA

Ja trzymam prosty mental model:

  • infrastructure → Vault, Consul, GitLab, routery, load balancery, core infrastruktury,
  • apps → aplikacje, serwisy, fronty, API.

W OpenTofu to jest po prostu czytelna mapa:

intermediates = {
  infrastructure = {
    path        = "pki-infrastructure"
    common_name = "rachuna-net.pl Infrastructure Intermediate CA"
    role_name   = "infrastructure-default"
  }
  apps = {
    path        = "pki-apps"
    common_name = "rachuna-net.pl Apps Intermediate CA"
    role_name   = "apps-default"
  }
}

Leaf: certyfikaty jako lista życzeń

Najlepsze jest to, że wystawianie certyfikatów końcowych robi się trochę jak „zamawianie” usług: wpisujesz co chcesz mieć, a reszta dzieje się sama.

leaf_requests = {
  "infra-gitlab" = {
    backend_key = "infrastructure"
    common_name = "gitlab.rachuna-net.pl"
    alt_names   = ["registry.rachuna-net.pl"]
  }
}

I nagle certyfikaty przestają być „operacją”, a stają się zwykłym elementem konfiguracji. Dodajesz wpis → commit → apply → gotowe.

To podejście jest genialne szczególnie wtedy, kiedy usług jest dużo i ciągle dochodzą nowe.


Certyfikaty: skąd je biorę?

W zależności od use-case:

  • dla automatyzacji i narzędzi (Ansible/CI) — czytam z KV,
  • dla prostych skryptów albo debugowania — biorę bezpośrednio z PKI.

Przykład pobrania bezpośrednio:

RESP=$(curl \
  --header "X-Vault-Token: $VAULT_TOKEN" \
  --request POST \
  --data '{"common_name": "grafana.rachuna-net.pl"}' \
  https://vault.rachuna-net.pl/v1/pki-apps/issue/apps-default)

ACME: czyli „Vault, ale jak Let’s Encrypt”

To jest ten fragment, który sprawia, że całość zaczyna błyszczeć: moduł wspiera ACME, więc możesz podpiąć np. certbota1 i robić odnowienia bardziej „cloud-native” niż „przypominajka w kalendarzu”.

acme = {
  acme_enabled                = true
  acme_cluster_path           = "https://vault.rachuna-net.pl"
  allow_wildcard_certificates = true
  max_ttl                     = "168h"
}

Efekt? Certyfikaty przestają być tematem. Po prostu się robią i odnawiają.


KV: sekrety w końcu mają swoje miejsce

KV v2 to klasyk, ale różnica robi się wtedy, gdy backendy i ich przeznaczenie masz uporządkowane, nazwane i utrzymywane w IaC.

resource "vault_mount" "kv_gitlab" {
  path        = "kv-gitlab"
  type        = "kv-v2"
  description = "Storage for GitLab related secrets"
}

Dzięki temu:

  • łatwiej delegować dostępy,
  • łatwiej utrzymać porządek,
  • łatwiej przenieść środowisko.

Polityki: bezpieczeństwo bez bólu

Polityki to miejsce, gdzie zwykle kończy się „porządek” — bo łatwo dorzucić wyjątek, trudniej go potem wyczyścić.

W iac-vault polityki są modularne: dla KV osobno, dla PKI osobno, dla admina osobno. Do tego klasyczny podział na read/write/admin.

To brzmi prosto, ale działa fenomenalnie przy rosnącej liczbie zespołów i integracji.


Auth: ludzie logują się prosto, automaty logują się bezpiecznie

Dwie metody, które pokrywają 95% potrzeb:

  • userpass — dla ludzi (admin/dev),
  • AppRole — dla CI/CD i automatyzacji.

AppRole jest idealny do GitLaba, bo daje kontrolę nad TTL, rolami, politykami i dostępami, bez zostawiania wszędzie tokenów „na wieczność”.


Podsumowanie: dlaczego to jest fajne?

Bo Vault przestaje być „ustawiony kiedyś” i staje się „zarządzany”.

Z iac-vault dostajesz:

  • ✅ odtwarzalność środowiska,
  • ✅ porządek w PKI (root -> intermediates -> leafy),
  • ✅ certyfikaty jako deklaracja, nie jako operacja,
  • ✅ opcję ACME i automatycznego odnawiania,
  • ✅ przewidywalne, modularne polityki,
  • ✅ przejrzysty model auth dla ludzi i automatyzacji,
  • ✅ repozytorium, które jest dokumentacją samą w sobie.

I najważniejsze: zyskujesz spokój. A to w infrastrukturze jest walutą premium.

Last modified December 19, 2025: docs: Update (72bba37)