This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

iac-vault

IAC do zarządzania HashiCorp Vault przy użyciu OpenTofu.

Repozytorium: iac-vault

Repozytorium zawiera kompletną konfigurację Infrastructure as Code (IaC) umożliwiającą zarządzanie środowiskiem HashiCorp Vault za pomocą OpenTofu. Projekt automatyzuje tworzenie, konfigurację oraz utrzymanie komponentów Vault, w tym trzystopniowego PKI, przestrzeni KV oraz polityk dostępu.


Architektura rozwiązania

Repozytorium iac-vault pełni rolę głównego punktu orkiestracji konfiguracji Vault. Bazuje na dedykowanym module vault-pki, który odpowiada za budowę hierarchii certyfikatów.

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

    n2@{ shape: rounded}

Struktura repozytorium:

.
├── 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

Główny plik main.tf integruje moduły:

  • pki
  • secrets
  • policies
  • auth

Parametry globalne, takie jak var.pki_urls_base (adres publikacji certyfikatów i CRL) oraz var.kv_mount_path, definiowane są w pliku variables.tf.


Public Key Infrastructure (PKI)

Warstwa PKI korzysta z modułu:

👉 vault-pki

Moduł automatyzuje budowę trójwarstwowej hierarchii certyfikatów:

Root CA

  • pki-root – tworzy główny urząd certyfikacji.

Intermediate CA

Dwa niezależne urzędy pośrednie, wykorzystywane zgodnie z podziałem ról:

  • pki-infrastructure – certyfikaty dla infrastruktury (np. routery, systemy HA, bazy danych)
  • pki-apps – certyfikaty dla aplikacji i usług

Certyfikaty końcowe (leaf)

Moduł pozwala generować certyfikaty końcowe oraz zapisywać je jako sekrety Vault.

Integracja z sekcjami secrets

Wygenerowane certyfikaty mogą być opcjonalnie przechowywane w backendach KV (np. kv-certificates), co umożliwia ich łatwe pobieranie przez inne procesy (np. Ansible, CI/CD).


Polityki dostępu (ACL Policies)

Polityki zdefiniowane są w katalogu policies/ i odpowiadają za granularną kontrolę dostępu do zasobów Vault.

Struktura zawiera m.in.:

Polityki ogólne

  • full-admin.hcl – pełne uprawnienia administracyjne.

Polityki dla storage KV

  • kv-devops
  • kv-gitlab
  • kv-certificates

Każda z nich definiuje:

  • uprawnienia odczytu (policy_read.hcl)
  • uprawnienia zapisu (policy_write.hcl)
  • uprawnienia administracyjne (policy_admin.hcl)

Polityki dla PKI

  • pki-root
  • pki-infrastructure
  • pki-apps

Zawierają:

  • pozwolenia na odczyt CA,
  • generowanie CRL,
  • wydawanie certyfikatów,
  • administrowanie backendem PKI.

Polityki są automatycznie rejestrowane w policies/main.tf.


Auth

Mechanizmy uwierzytelniania znajdują się w katalogu auth/.

Obejmują:

userpass

  • zarządzanie użytkownikami Vault,
  • definiowanie ról,
  • konfigurację dostępu (np. użytkownik mrachuna).

AppRole

  • integracja z CI/CD (np. GitLab CI),
  • generowanie ról i secret_id dla pipeline’ów.

Mechanizmy auth są aktywowane i konfigurowane w auth/main.tf.


Podsumowanie

Repozytorium iac-vault zapewnia kompletny, modułowy i skalowalny zestaw IaC do zarządzania HashiCorp Vault w środowisku produkcyjnym. Łączy:

  • trzystopniowy PKI,
  • kontrolę dostępu,
  • storage KV,
  • mechanizmy auth,
  • dobre praktyki organizacji zasobów w Vault.

Całość jest w pełni automatyzowalna dzięki OpenTofu i integruje się z pipeline’ami GitLab CI oraz procesami konfiguracji (np. Ansible).

1 - Secret storage

Tworzenie storage dla secretów

KV (Key-Value) w HashiCorp Vault to jeden z najprostszych i najczęściej używanych backendów sekretów. Służy do przechowywania dowolnych danych w formie klucz–wartość, takich jak:

  • hasła,
  • tokeny API,
  • klucze prywatne,
  • konfiguracje usług,
  • certyfikaty,
  • dane konfiguracyjne aplikacji.

Sekcja secrets definiuje backendy KV v2 przeznaczone do przechowywania poufnych danych, takich jak:

  • konfiguracje projektów,
  • tokeny,
  • hasła,
  • certyfikaty końcowe.

Przykładowa konfiguracja backendu:

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

resource "vault_kv_secret_backend_v2" "kv_gitlab_config" {
  mount        = vault_mount.kv_gitlab.path
  max_versions = 10
  cas_required = false
}

2 - PKI

Tworzenie PKI w organizacji

PKI (Public Key Infrastructure) to infrastruktura klucza publicznego – zestaw technologii, zasad i komponentów, które umożliwiają bezpieczną komunikację, uwierzytelnianie i szyfrowanie w systemach informatycznych.


Utworzenie drzewa certyfikatów Root CA i Intermediate CA

stateDiagram
  direction TB
  classDef Sky stroke-width:1px,stroke-dasharray:none,stroke:#374D7C,fill:#E2EBFF,color:#374D7C;
  classDef Peach stroke-width:1px,stroke-dasharray:none,stroke:#FBB35A,fill:#FFEFDB,color:#8F632D;
  [*] --> s1
  s1 --> s2
  s1 --> s5
  s1:Root CA
s1:*.rachuna-net.pl

  s2:Infrastructure Intermediate CA
s2:Certyfikaty Infrastruktury

  [*]
  s5:Apps Intermediate CA
s5:Certyfikaty Aplikacji

  class s5 Peach
module "vault_pki" {
  source = "git@gitlab.rachuna-net.pl:pl.rachuna-net/infrastructure/opentofu/modules/vault-pki.git?ref=v1.0.0"

  root_domain      = "rachuna-net.pl"
  root_common_name = "rachuna-net.pl ROOT CA"
  pki_urls_base    = "https://ct01101.rachuna-net.pl:8200/"

  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"
    }
  }

}

Przykładowe rozwiązanie reprezentuje certyfikat główny ROOT CA oraz certyfikaty pośredniczące nfrastructure Intermediate CA dedykowany dla infrastruktury oraz Apps Intermediate CA


Wygenerowanie certyfikatu końcowego

  1. Generowanie certyfikaty końcowe (leaf) dla konkretnych usług:

    • gitlab.rachuna-net.pl
    • consul.rachuna-net.pl
    • vault.rachuna-net.pl
    • docs.rachuna-net.pl
module "vault_pki" {
  source = "git@gitlab.rachuna-net.pl:pl.rachuna-net/infrastructure/opentofu/modules/vault-pki.git?ref=v1.0.0"

  root_domain   = "rachuna-net.pl"
  pki_urls_base = "https://ct01101.rachuna-net.pl:8200/"

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

  leaf_requests = {
    "infra-gitlab" = {
      backend_key = "infrastructure"
      common_name = "gitlab.rachuna-net.pl"
      alt_names = [
        "registry.rachuna-net.pl"
      ]
    }
    "infra-consul" = {
      backend_key = "infrastructure"
      common_name = "consul.rachuna-net.pl"
      alt_names = [
        "ct01101.rachuna-net.pl",
        "ct01102.rachuna-net.pl",
        "ct01103.rachuna-net.pl",
      ]
    }
    "infra-vault" = {
      backend_key = "infrastructure"
      common_name = "vault.rachuna-net.pl"
      alt_names = [
        "ct01101.rachuna-net.pl",
        "ct01102.rachuna-net.pl",
        "ct01103.rachuna-net.pl",
      ]
    }
    "apps-docs" = {
      backend_key = "apps"
      common_name = "docs.rachuna-net.pl"
    }
  }
}

Kluczowe parametry:

  • root_domain = "rachuna-net.pl" Root CA będzie wystawiony dla tej domeny, np. Rachuna-Net Root CA.

  • pki_urls_base = "https://ct01101.rachuna-net.pl:8200/" Baza do ustawienia:

    • issuing_certificates
    • crl_distribution_points
    • ocsp_servers czyli gdzie klienci mają się zgłaszać po CRL / certyfikaty.
  • intermediates – definicja CA pośrednich (mapa):

    • klucz (infrastructure, apps) to alias używany dalej w module,
    • path – mount w Vault, np. pki-infrastructure,
    • common_name – CN tego CA,
    • role_name – domyślna rola w tym backendzie (do wystawiania leafów).
  • leaf_requestslista certyfikatów końcowych do wygenerowania:

    • klucz mapy (infra-gitlab, infra-consul…) to tylko nazwa logiczna w module,
    • backend_key – mówi, z którego intermediate CA korzystać (infrastructure / apps),
    • common_name – CN certyfikatu (zazwyczaj FQDN usługi),
    • alt_names – Subject Alternative Names (SAN) – dodatkowe nazwy DNS w certyfikacie.

Pobieranie certyfikatu

Zależy, jak moduł jest napisany, ale typowo:

  • Zapisane są certyfikaty w kv (np. kv-certificates/leaf/grafana),
{
  "alt_names": [],
  "backend_key": "apps",
  "ca_chain": "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
  "certificate": "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
  "common_name": "test.rachuna-net.pl",
  "issuing_ca": "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----",
  "private_key": "-----BEGIN RSA PRIVATE KEY-----\n-----END RSA PRIVATE KEY-----",
  "serial_number": "42:b5:19:73:cd:f9:70:0c:30:81:93:fb:b5:56:a1:7e:f9:25:6a:23"
}
  • bezpośrednio do pobrania z pki
RESP=$(curl \
  --header "X-Vault-Token: $VAULT_TOKEN" \
  --request POST \
  --data '{"common_name": "grafana.rachuna-net.pl"}' \
  https://ct01005.rachuna-net.pl:8200/v1/pki-apps/issue/apps-default)

echo "$RESP" | jq -r '.data.certificate'  > grafana.crt
echo "$RESP" | jq -r '.data.private_key'  > grafana.key
echo "$RESP" | jq -r '.data.issuing_ca'   > grafana.chain.crt

Proponowany podział PKI?

Z poziomu dev/DevOps masz bardzo prosty mentalny model:

  • Root CA – trzymasz w Vault, ale używasz tylko do podpisywania intermediates.

  • Intermediate CA – logiczny podział według typu systemów:

    • infrastructure: Proxmox, Consul, Vault, GitLab, routery, load balancery.
    • apps: aplikacje biznesowe, frontend, API.
    • (opcjonalnie) dmz, internal, storage, kubernetes itd.
  • Leaf certyfikaty – dla konkretnych hostów/usług.

I wszystko opisujesz w dwóch mapach: intermediates i leaf_requests.


Przykłady rozbudowania PKI

Kilka konkretnych trików:

  1. Osobny intermediate dla Kubernetes:

    intermediates = {
      # ...
      kubernetes = {
        path        = "pki-k8s"
        common_name = "Kubernetes Intermediate CA"
        role_name   = "k8s-default"
      }
    }
    
  2. Cert dla Ingress Controller:

    leaf_requests = {
      "k8s-ingress" = {
        backend_key = "kubernetes"
        common_name = "ingress.rachuna-net.pl"
        alt_names = [
          "*.apps.rachuna-net.pl",
        ]
      }
    }
    

Użycie ACME

Ten blok możesz podmienić w sekcji „Utworzenie drzewa certyfikatów Root CA i Intermediate CA” (stary przykład z ref=v1.0.0 → nowy z ref=v1.1.0 + ACME):

module "vault_pki" {
  source = "git@gitlab.rachuna-net.pl:pl.rachuna-net/infrastructure/opentofu/modules/vault-pki.git?ref=v1.1.0"

  root_domain      = "rachuna-net.pl"
  root_common_name = "rachuna-net.pl ROOT CA"
  pki_urls_base    = "https://ct01005.rachuna-net.pl:8200"

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

      # klasyczne parametry PKI
      ttl     = "26280h"
      max_ttl = "26280h"

      # konfiguracja ACME dla infrastruktury
      acme = {
        acme_enabled                = true
        acme_cluster_path           = "https://ct01005.rachuna-net.pl"
        allow_wildcard_certificates = true
        max_ttl                     = "168h"
      }

      # (opcjonalnie) aliasy/parametry dla zachowania wstecznej kompatybilności modułu
      acme_enabled      = true
      acme_cluster_path = "https://ct01005.rachuna-net.pl"
    }

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

      # zezwalamy na wildcardy z tego backendu
      allow_wildcard_certificates = true

      # klasyczne parametry PKI
      ttl     = "26h"
      max_ttl = "26h"

      # konfiguracja ACME dla aplikacji
      acme = {
        acme_enabled      = true
        acme_cluster_path = "https://ct01005.rachuna-net.pl"
        max_ttl           = "25h"
      }
    }
  }
}

3 - Uwierzytelnianie (Auth)

Uwierzytelnianie (Auth)

Uwierzytelnianie (Auth)

Moduł Auth odpowiada za konfigurację metod uwierzytelniania w HashiCorp Vault oraz przypisywanie użytkownikom i procesom automatycznym odpowiednich polityk dostępu (policies). W projekcie wykorzystywane są dwie główne metody:

  • Userpass – dla użytkowników technicznych i administratorów,
  • AppRole – dla automatyzacji (CI/CD, GitLab Runner, systemy integracyjne).

1. Polityki (Policies)

Polityki definiują zakres dostępu do ścieżek w Vault. Każda polityka opisuje:

  • na jakich ścieżkach (path)
  • jakie operacje (capabilities) są dozwolone.

1.1. Minimalny przykład polityki

Plik polityki w formacie HCL:

# policies/app-read.hcl
path "kv-apps/data/apps/example-service" {
  capabilities = ["read"]
}

Rejestracja polityki w Vault:

resource "vault_policy" "app_read" {
  name   = "app-read"
  policy = file("${path.module}/policies/app-read.hcl")
}

1.2. Zasady organizacyjne dla polityk

W projekcie polityki są utrzymywane w sposób modułowy w katalogu:

iac-vault/policies/

Struktura:

  • pki-* – polityki dla PKI,
  • kv-* – polityki dla KV,
  • .full-admin – pełne uprawnienia administracyjne.

Każda nowa polityka powinna zostać dodana jako osobny moduł, aby zachować:

  • spójność zarządzania,
  • możliwość kontroli wersji,
  • łatwe przypisywanie do metod uwierzytelniania.

2. Userpass – użytkownicy z przypisanymi politykami

Metoda userpass umożliwia logowanie do Vault za pomocą:

  • loginu,
  • hasła,
  • przypisanych polityk.

2.1. Włączenie metody userpass

resource "vault_auth_backend" "userpass" {
  type = "userpass"
}

2.2. Tworzenie użytkownika i przypisywanie polityk

resource "vault_generic_endpoint" "user_jan" {
  depends_on = [vault_auth_backend.userpass]

  path = "auth/userpass/users/jan"

  data_json = jsonencode({
    password = "silne-haslo"
    policies = [
      ".full-admin",
      "kv-apps.policy_admin"
    ]
  })
}

2.3. Znaczenie pól

Pole Opis
path Ścieżka użytkownika w metodzie userpass
password Hasło użytkownika
policies Lista polityk przypisanych do użytkownika

✅ Ten mechanizm stosowany jest głównie dla:

  • administratorów,
  • operatorów,
  • użytkowników technicznych.

3. AppRole – tokeny dla automatyzacji (CI/CD, GitLab)

Metoda AppRole przeznaczona jest dla:

  • pipeline’ów GitLab,
  • narzędzi automatyzujących,
  • systemów backendowych.

Logowanie odbywa się przy użyciu:

  • role_id,
  • secret_id.

3.1. Włączenie metody AppRole

resource "vault_auth_backend" "approle" {
  type = "approle"
}

3.2. Definicja roli AppRole

resource "vault_approle_auth_backend_role" "ci_role" {
  backend        = vault_auth_backend.approle.path
  role_name      = "ci-role"

  token_policies = [
    "kv-apps.policy_write",
    "pki-apps.policy_read"
  ]

  token_ttl      = "24h"
  token_max_ttl  = "24h"
}

3.3. Generowanie secret_id

resource "vault_approle_auth_backend_role_secret_id" "ci_role" {
  backend   = vault_auth_backend.approle.path
  role_name = vault_approle_auth_backend_role.ci_role.role_name
}

3.4. Znaczenie parametrów

Parametr Opis
role_name Nazwa roli używana w automatyzacji
token_policies Polityki przypisane do tokenu
token_ttl Czas życia tokenu
token_max_ttl Maksymalny czas życia tokenu
secret_id Jednorazowy sekret do logowania