Vault PKI Kubernetes the hard way v2

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску


Vault PKI for k8s

Эта статья основана на Vault_PKI и во многом дублирует ее, но тут я постарался подробно описать все применительно к "самодельному" домашнему кластеру

Установка Vault

Предполагается что Vault уже установлен и настроен ( в моем случае по адресу http://vault.home:8200 )

Работа с Vault

Для создания endpoints потребуется токен с правами root и выставленная переменная VAULT_ADDR

export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN="s.Yb1J2VamFyYoav3VVE2YQQ88"

PKI

Логика работы (кратко)

  • Создается несколько инстансов PKI внутри одного процесса Vault (для большей безопастности)
  • Корневой сертефикат выписывается корневым PKI
    • Кроме прочего - он может быть удален из волта, закрыт в сейфе и т п для безопастности так как используется только для создания промежуточного СА
  • этот сертификат (он НЕ является секретным) распространяется по всем нодам и используется для проверки
  • этим сертификатом подписвается промежуточный CA
  • промежуточный CA подписывает клиентские сертификаты

Кроме того можно удлинить эту цепочку используя уже готовый СА и промежуточный СА и импортировав их в Vault (но примера тут нет - я не делал)


Создание корневого CA

Создание корневого СА: Vault_PKI_Kubernetes_the_hard_way_v2_Root_CA

Создание промежуточных СА

В целом можно использовать и корневой СА для создания конечных сертификатов но "лучшие собаководы" рекомендуют использовать промежуточный CA
ВАЖНО: В K8s используется множество разных CA, хотя большая часть документации об этом умалчивает - но каждый сервис может иметь свой собственный CA и подписанные им сертификаты, и именно так я и буду делать для лучшего понимания

CA для etcd

Пример настройки прав и ролей и создания сертефикатов для etcd Vault_PKI_Intermediate_ca_etcd_Kubernetes_the_hard_way_v2

Работа с PKI

Использование PKI для создания сертификатов


Создание пользователей и ролей не относится непосредсвенно к настройке PKI но добавлено для полноты так как все равно надо настроить =)

Тестовая роль для серверных сертификатов

  • example-dot-home-server-server-crt - имя роли
vault write pki_intermediate_ca/roles/example-dot-home-server-crt \
    country="Ukraine" \
    locality="Kharkov" \
    street_address="Lui Pastera st 322 app. 311"\
    postal_code="61172" \
    organization="Home Network" \
    ou="IT" \
    allowed_domains="example.home" \
    allow_subdomains=true \
    max_ttl="87600h" \
    key_bits="2048" \
    key_type="rsa" \
    allow_any_name=false \
    allow_bare_domains=false \
    allow_glob_domain=false \
    allow_ip_sans=true \
    allow_localhost=false \
    client_flag=false \
    server_flag=true \
    enforce_hostnames=true \
    key_usage="DigitalSignature,KeyEncipherment" \
    ext_key_usage="ServerAuth" \
    require_cn=true

Просмотр ролей

vault list  pki_intermediate_ca/roles
Keys
----
example-dot-home-client-crt
example-dot-home-server-crt
vault read  pki_intermediate_ca/roles/example-dot-home-server-crt
Key                                   Value
---                                   -----
allow_any_name                        false
allow_bare_domains                    false
allow_glob_domains                    false
allow_ip_sans                         true
allow_localhost                       false
allow_subdomains                      true
allow_token_displayname               false
allowed_domains                       [example.home]
allowed_domains_template              false
allowed_other_sans                    []
allowed_serial_numbers                []
allowed_uri_sans                      []
basic_constraints_valid_for_non_ca    false
client_flag                           false
code_signing_flag                     false
country                               [Ukraine]
email_protection_flag                 false
enforce_hostnames                     true
ext_key_usage                         [ServerAuth]
ext_key_usage_oids                    []
generate_lease                        false
key_bits                              2048
key_type                              rsa
key_usage                             [DigitalSignature KeyEncipherment]
locality                              [Kharkov]
max_ttl                               87600h
no_store                              false
not_before_duration                   30s
organization                          [Home Network]
ou                                    [IT]
policy_identifiers                    []
postal_code                           [61172]
province                              []
require_cn                            true
server_flag                           true
street_address                        [Lui Pastera st 322 app. 311]
ttl                                   0s
use_csr_common_name                   true
use_csr_sans                          true

Политики

  • По сути политики определяют права доступа ТОКЕНА к определнному пути. Токен же выдается при логине - т е в простейшем случае политики должны назначаться на пользователя (авторизация по логину/паролю)

Для доступа к ролям создаем 2 файла с политиками:

Политика для серверных сертефикатов

example-dot-home-server-crt-policy.hlc
path "pki_intermediate_ca/issue/example-dot-home-server-crt"
{
  capabilities = ["read", "create", "list", "update"]
}

Политика для клиентских сертефикатов

example-dot-home-client-crt-policy.hlc
path "pki_intermediate_ca/issue/example-dot-home-client-crt"
{
  capabilities = ["read", "create", "list", "update"]
}

Создание политик из файлов

vault policy write example-dot-home-server-crt-policy  example-dot-home-server-crt-policy.hlc
vault policy write example-dot-home-client-crt-policy  example-dot-home-client-crt-policy.hlc

Проверка созданных политик

vault policy list
default
example-dot-home-client-crt-policy
example-dot-home-server-crt-policy
root
vault policy read example-dot-home-server-crt-policy
path "pki_intermediate_ca/issue/example-dot-home-server-crt"
{
  capabilities = ["read", "create", "list", "update"]
}
vault policy read example-dot-home-client-crt-policy
path "pki_intermediate_ca/issue/example-dot-home-client-crt"
{
  capabilities = ["read", "create", "list", "update"]
}

Привязка политик к пользователям

  • Пользователи с логином и паролем хорошо подходят для тестирования - в реальных окружениях будет использоваться что-то дроугое, сервисные аккаунты или еще что-то.
  • Включить авторизацию по логину/паролю нужно заранее (https://noname.com.ua/mediawiki/index.php/Vault)

Просмотр пользователей

vault list  auth/userpass/users

Создание пользователей

  • Политики перечисляются через запятую (что не очевидно)
  • Имена пользователей "говорят" о их назначении
  • Пароли простые для тестирования но разные у разных пользователей (для того что бы не допустить ошибок при тестировании и не перепутать пользователя)

Пользователь для получения серверных сертификатов

vault \
  write auth/userpass/users/example-dot-home-server-crt-user \
  password=server \
  policies="example-dot-home-server-crt-policy,default"

Пользователь для получения клиентских сертификатов

vault \
  write auth/userpass/users/example-dot-home-client-crt-user \
  password=client \
  policies="example-dot-home-client-crt-policy,default"

Пользователь для получения и серверных и клиентских сертификатов

vault \
  write auth/userpass/users/example-dot-home-any-crt-user \
  password=any \
  policies="example-dot-home-server-crt-policy,example-dot-home-client-crt-policy,default"

Просмотр созданных пользователей

vault list  auth/userpass/users
Keys
----
example-dot-home-any-crt-user
example-dot-home-client-crt-user
example-dot-home-server-crt-user
vault read  auth/userpass/users/example-dot-home-any-crt-user
Key                        Value
---                        -----
policies                   [default example-dot-home-client-crt-policy example-dot-home-server-crt-policy]
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              0s
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [default example-dot-home-client-crt-policy example-dot-home-server-crt-policy]
token_ttl                  0s
token_type                 default
vault read  auth/userpass/users/example-dot-home-server-crt-user
Key                        Value
---                        -----
policies                   [default example-dot-home-server-crt-policy]
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              0s
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [default example-dot-home-server-crt-policy]
token_ttl                  0s
token_type                 default
vault read  auth/userpass/users/example-dot-home-client-crt-user
Key                        Value
---                        -----
policies                   [default example-dot-home-client-crt-policy]
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              0s
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [default example-dot-home-client-crt-policy]
token_ttl                  0s
token_type                 default

Проверка получения сертефикатов

Получение серверного сертефиката

Авторизация с логином и паролем

vault \
    login \
    -method=userpass \
    username=example-dot-home-server-crt-user \
    password=server
Key                    Value
---                    -----
token                  s.Wiy7YVCrte88i0QIHc4jmvQP
token_accessor         ptB7o9d6yOq5w4Ra5Q44W1FF
token_duration         768h
token_renewable        true
token_policies         ["default" "example-dot-home-server-crt-policy"]
identity_policies      []
policies               ["default" "example-dot-home-server-crt-policy"]
token_meta_username    example-dot-home-server-crt-user

Получение сертефиката

vault write -format=json pki_intermediate_ca/issue/example-dot-home-server-crt \
    common_name="vault.example.home" \
    alt_names="pki.example.home" \
    ttl="43800h" > vault.example.home.crt


Проверка полученного сертефиката

cat vault.example.home.crt
{
  "request_id": "3d679cb8-0aa1-9920-672e-b56831fc53b8",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "ca_chain": [
      "-----BEGIN CERTIFICATE-----uVGvIFquMpVj\n0ajUAed1yuVd7S2USE1s8RyN7j3t0D7FG7pRECTBnZYKqBc7OI2YdiwdPvQH\n-----END CERTIFICATE-----"
    ],
    "certificate": "-----BEGIN CERTIFICATE-----dNVFGyG16ZABF66E1j1O/MTRjB3cZIHj1rMP\nc84dH3ykPoe80T6zGblRgxrZOIh/EQ==\n-----END CERTIFICATE-----",
    "expiration": 1791633878,
    "issuing_ca": "-----BEGIN CERTIFICATE-----uVGvIFquMpVj\n0ajUAed1yuVd7S2USE1s8RyN7j3t0D7FG7pRECTBnZYKqBc7OI2YdiwdPvQH\n-----END CERTIFICATE-----",
    "private_key": "-----BEGIN RSA PRIVATE KEY-----aAg7YWcEsZMl\ntdqBAoGAX5UJPX9VSbm/kI5nruAnQXcfkmUWIQA3GDWdbGfqaY4AZZleQv/0mMJf\nAgUT+e/vFuHaDAxY6rXh7dl3aM3JiJgdLxJ1vFFeaKqHHonH83PT0921S+MLiB5/\n9EdomgHndqzJXVrMLThUR6PiUTZtrG48tsI6w9rOk1ZELvE5HJI=\n-----END RSA PRIVATE KEY-----",
    "private_key_type": "rsa",
    "serial_number": "3b:a5:4d:03:64:bf:91:b5:6a:6f:0a:c8:aa:05:11:68:29:a8:6d:dd"
  },
  "warnings": null
}
cat vault.example.home.crt | jq -r .data.ca_chain[] > ca_chain.pem
cat vault.example.home.crt | jq -r .data.certificate > certificate.pem
cat vault.example.home.crt | jq -r .data.issuing_ca > issuing_ca.pem
Сертификат сервера

Наиболее интересные поля - ожидаемые значения:

  • CN = vault.example.home
  • X509v3 Subject Alternative Name: DNS:pki.example.home, DNS:vault.example.home
openssl  x509 -in certificate.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3b:a5:4d:03:64:bf:91:b5:6a:6f:0a:c8:aa:05:11:68:29:a8:6d:dd
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = Ukraine, L = Kharkov, street = Lui Pastera st. 322 app. 131, postalCode = 61172, O = Home Network, OU = IT, CN = Intermediate CA
        Validity
            Not Before: Oct 11 12:04:12 2021 GMT
            Not After : Oct 10 12:04:38 2026 GMT
        Subject: C = Ukraine, L = Kharkov, street = Lui Pastera st 322 app. 311, postalCode = 61172, O = Home Network, OU = IT, CN = vault.example.home
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Subject Key Identifier:
                5B:00:DB:44:EB:59:B4:9A:86:BE:9D:3D:A3:E0:DC:C6:EC:71:E4:BB
            X509v3 Authority Key Identifier:
                keyid:57:CC:17:CF:CF:21:94:71:42:B8:AB:06:7C:FD:FC:45:6D:F9:6A:4F

            Authority Information Access:
                CA Issuers - URI:http://vault.home:8200/v1/pki_intermediate_ca/ca

            X509v3 Subject Alternative Name:
                DNS:pki.example.home, DNS:vault.example.home
            X509v3 CRL Distribution Points:

                Full Name:
                  URI:http://vault.home:8200/v1/pki_intermediate_ca/crl

    Signature Algorithm: sha256WithRSAEncryption
Промежуточный CA
openssl  x509 -in issuing_ca.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            40:b2:aa:32:ee:ea:2f:84:75:bb:8f:ec:2e:98:56:70:1c:66:f6:0d
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = Ukraine, L = Kharkov, street = app. 131 + street = Lui Pastera St. 322, postalCode = 61172, O = Home Network, OU = IT, CN = Root Certificate Authority for Home Network
        Validity
            Not Before: Oct 11 09:22:37 2021 GMT
            Not After : Oct  6 09:23:07 2041 GMT
        Subject: C = Ukraine, L = Kharkov, street = Lui Pastera st. 322 app. 131, postalCode = 61172, O = Home Network, OU = IT, CN = Intermediate CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier:
                57:CC:17:CF:CF:21:94:71:42:B8:AB:06:7C:FD:FC:45:6D:F9:6A:4F
            X509v3 Authority Key Identifier:
                keyid:DB:3A:73:7A:03:25:2E:17:48:46:58:67:19:AD:86:2E:04:3D:EB:EC

    Signature Algorithm: sha256WithRSAEncryption
Верефикация
  • Указать промежуточный как untrusted (или добавить rootCA в доверенные)
openssl verify -verbose -CAfile rootCA.pem -untrusted issuing_ca.pem certificate.pem
certificate.pem: OK

Получение клиентского сертефиката

Проверка прав

С "серверным" пользователем - нет прав

URL: PUT http://vault.home:8200/v1/pki_intermediate_ca/issue/example-dot-home-client-crt
Code: 403. Errors:

* 1 error occurred:
	* permission denied

Получение клиентского сертификата c правильным пользователем

vault \
    login \
    -method=userpass \
    username=example-dot-home-client-crt-user \
    password=client

vault write -format=json pki_intermediate_ca/issue/example-dot-home-client-crt \
    common_name="vault.example.home" \
    alt_names="pki.example.home" \
    ttl="43800h" > vault.example.home.CLIENT.json

Ожидаемый результат:

X509v3 Extended Key Usage:
  TLS Web Client Authentication
openssl x509 -noout -text -in certificate_client.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            29:02:a3:28:8e:fc:48:bd:1a:5b:f9:46:e1:bf:87:27:e0:80:79:c3
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = Ukraine, L = Kharkov, street = Lui Pastera st. 322 app. 131, postalCode = 61172, O = Home Network, OU = IT, CN = Intermediate CA
        Validity
            Not Before: Oct 11 12:31:10 2021 GMT
            Not After : Oct 10 12:31:37 2026 GMT
        Subject: C = Ukraine, L = Kharkov, street = Lui Pastera st. 322 app. 131, postalCode = 61172, O = Home Network, OU = IT, CN = vault.example.home
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
           Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Subject Key Identifier:
                4A:1E:33:0C:03:E0:91:6D:17:1E:2B:4A:9F:53:98:7D:4C:80:43:83

Проверка прав для пользователя any

  • работает каки ожидалось - можно получить сертификаты как для сервера так и для клиента
vault \
    login \
    -method=userpass \
    username=example-dot-home-any-crt-user \
    password=any


vault write -format=json pki_intermediate_ca/issue/example-dot-home-client-crt \
    common_name="vault.example.home" \
    alt_names="pki.example.home" \
    ttl="43800h" > vault.example.home.CLIENT_by_any_user.json


vault write -format=json pki_intermediate_ca/issue/example-dot-home-server-crt \
    common_name="vault.example.home" \
    alt_names="pki.example.home" \
    ttl="43800h" > vault.example.home.SERVER_by_any_user.json

Работа с PKI

Посмотреть список сертификатов

vault list pki_intermediate_ca/certs
Keys
----
0e-be-9c-c3-80-0c-e2-51-e0-f8-e3-00-0d-5a-42-d3-cd-13-54-be
10-3c-b0-8c-50-8c-4f-21-14-a6-fd-32-36-87-ce-85-ef-e9-23-d2
29-02-a3-28-8e-fc-48-bd-1a-5b-f9-46-e1-bf-87-27-e0-80-79-c3
29-6a-69-20-c3-c2-a8-f3-ae-e7-13-0c-6b-52-c5-b6-49-c4-a3-77
38-b4-31-fc-82-6a-39-26-08-29-24-2d-da-ed-63-95-5c-7d-77-2b
3b-a5-4d-03-64-bf-91-b5-6a-6f-0a-c8-aa-05-11-68-29-a8-6d-dd
40-b2-aa-32-ee-ea-2f-84-75-bb-8f-ec-2e-98-56-70-1c-66-f6-0d

Прочитать сертификат

  • 40-b2-aa-32-ee-ea-2f-84-75-bb-8f-ec-2e-98-56-70-1c-66-f6-0d это serial сертификата
vault read pki_intermediate_ca/cert/40-b2-aa-32-ee-ea-2f-84-75-bb-8f-ec-2e-98-56-70-1c-66-f6-0d --format=json
{
  "request_id": "a7af8390-c94b-6055-2148-283746cd64fa",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "certificate": "-----BEGIN CERTIFICATE-----\nMIIESTCCAzGgAwIBAgIUQLKqMu7qL4R1u4/sLphWcBxm9g0wDQYJKoZIhvcNAQEL\nBQAwgb0xEDAOBgNVBAYTB1VrcmFpbmUxEDAOBgNVBAcTB0toYXJrb3YxLTAPBgNV\nBAkTCGFwcC4gMTMxMBoGA1UECRMTTHVpIFBhc3RlcmEgU3QuIDMyMjEOMAwGA1UE\nERMFNjExNzIxFTATBgNVBAoTDEhvbWUgTmV0d29yazELMAkGA1UECxMCSVQxNDAy\nBgNVBAMTK1Jvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IGZvciBIb21lIE5ldHdv\ncmswHhcNMjExMDExMDkyMjM3WhcNNDExMDA2MDkyMzA3WjCBmTEQMA4GA1UEBhMH\nVWtyYWluZTEQMA4GA1UEBxMHS2hhcmtvdjElMCMGA1UECRMcTHVpIFBhc3RlcmEg\nc3QuIDMyMiBhcHAuIDEzMTEOMAwGA1UEERMFNjExNzIxFTATBgNVBAoTDEhvbWUg\nTmV0d29yazELMAkGA1UECxMCSVQxGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgmWZZFkFcuAzKZa2VQKf37\ncjUFLVb4rLJrZOC5z1FuWFzKPNvUOyG2QkB2iGbdvA+cQII/ILcBEo7I3zGt6lg6\nWmGyff4ve2mM5++rtNIleXX3iCORREbsCHPYUH+g+rT531gtgH9NMKHYfav7eRcx\nf4HIlNwgdD7Tf7oSvvf5/So6ZjLL9TrWWaNgZBPOWEvHMFZnXG8s0kpowahkeAPI\nnXlsvIJy5KnQZgWcGG3C9LcEvrC+6Gifk0FOMt2c9HNgDyxs+rRJWmlRDJ1pVje9\n0EbceyharZ32FCzpznpTKylrGmyQqOkR2lKn//+N9DZwcC6ngB5AFBqKd74R/MUC\nAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O\nBBYEFFfMF8/PIZRxQrirBnz9/EVt+WpPMB8GA1UdIwQYMBaAFNs6c3oDJS4XSEZY\nZxmthi4EPevsMA0GCSqGSIb3DQEBCwUAA4IBAQCI5j1vsxGmb2zhd1p7rLJibntp\nJHxTg0qG9pDKzO3erUDia53ifTRchRjNqgcdTJO89MbCVpMcK88+E01X3KtGZMFR\n3V4I1Gmptdg4luicYzrO92S40CiRHr9UFz8Cftg9JxGZAk0MN3ScpjtxPM1fZs4d\n2INtQtyjtZ/I86itogPsKHo7hrIdo9IGmFa7OHuul/uYl3Z9cNLOAEHcBFarQ9Vn\nvQmPpdaq3t4ArwFHRrn5ZMgM9HbvRbgr3ns5U4uX9TdSefHashoAuVGvIFquMpVj\n0ajUAed1yuVd7S2USE1s8RyN7j3t0D7FG7pRECTBnZYKqBc7OI2YdiwdPvQH\n-----END CERTIFICATE-----",
    "revocation_time": 0
  },
  "warnings": null
}

Отозвать сертификат

vault write pki_intermediate_ca/revoke serial_number=
<serial number>

Очистить просроченные / отозванные сертификаты

vault write pki_intermediate_ca/tidy \
    safety_buffer=5s \
    tidy_cert_store=true \
    tidy_revocation_list=true

Проверка сертификатов с Nginx

Для проверки работы сертификатов настроим nginx как proxy. в качестве бекенда выступает сам Vault

Настройка Nginx

Предварительный конфиг виртуалхоста (пока не загружаю - так как ни ключа ни сертификата пока нет)

server {
    listen 8202 default_server ssl;
    root /var/www/html;
    server_name vault.home vault.local;
    access_log /var/log/nginx/vault-access.log postdata;
    error_log /var/log/nginx/vault-error.log;

    ssl_certificate     /etc/nginx/certs/vault.crt;
    ssl_certificate_key /etc/nginx/certs/vault.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        client_body_buffer_size      64k;
        client_body_in_single_buffer on;
        proxy_pass                   http://127.0.0.1:8200;
        proxy_set_header             Host $host:$server_port;
        proxy_set_header             X-Real-IP $remote_addr;
        proxy_set_header             X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header             X-Forwarded-Proto $scheme;
        proxy_http_version           1.1;
        proxy_request_buffering      off;
    }
}

Настройка Vault

Создание роли

Роль ждя сертификата для домена vault.home

vault write pki_intermediate_ca/roles/vault-dot-home-server-crt \
    country="Ukraine" \
    locality="Kharkov" \
    street_address="Lui Pastera st 322 app. 311"\
    postal_code="61172" \
    organization="Home Network" \
    ou="IT" \
    allowed_domains="vault.home" \
    allow_subdomains=false \
    max_ttl="87600h" \
    key_bits="2048" \
    key_type="rsa" \
    allow_any_name=false \
    allow_bare_domains=true \
    allow_glob_domain=false \
    allow_ip_sans=true \
    allow_localhost=false \
    client_flag=false \
    server_flag=true \
    enforce_hostnames=true \
    key_usage="DigitalSignature,KeyEncipherment" \
    ext_key_usage="ServerAuth" \
    require_cn=true

Создание политик

  • Файл vault-dot-home-server-crt-policy.hlc
path "pki_intermediate_ca/issue/vault-dot-home-server-crt"
{
  capabilities = ["read", "create", "list", "update"]
}
vault \
    policy \
        write \
            vault-dot-home-server-crt-policy \
            vault-dot-home-server-crt-policy.hlc

Создание пользователя для работы с сертефикатами

Еще раз подчеркну что пользователи используются тут ТОЛЬКО В ТЕСТОВЫХ ЦЕЛЯХ.

Существующие пользователи

vault list  auth/userpass/users
Keys
----
example-dot-home-any-crt-user
example-dot-home-client-crt-user
example-dot-home-server-crt-user

Новый пользователь с политикой для домена vault.home

  • username: vault-dot-home-server-crt-user
  • password: vault
  • политики: vault-dot-home-server-crt-policy и default
vault \
  write auth/userpass/users/vault-dot-home-server-crt-user \
  password=vault \
  policies="vault-dot-home-server-crt-policy,default"

Проверка

export VAULT_ADDR="http://vault.home:8200"
unset VAULT_TOKEN


vault \
    login \
    -method=userpass \
    username=vault-dot-home-server-crt-user \
    password=vault


vault \
    write \
    -format=json pki_intermediate_ca/issue/vault-dot-home-server-crt \
    common_name="vault.home" \
    ttl="43800h" > vault.home.crt

Подготовка Consul Template

Установка

Так или иначе в зависимости от системы (это пример из интернета). Для arm версия другая.

$ wget https://releases.hashicorp.com/consul-template/0.19.5/consul-template_0.19.5_linux_amd64.zip
$ apt-get install unzip
$ unzip consul-template_0.19.5_linux_amd64.zip
$ mv consul-template /user/local/bin

Темплейты для сертефиката и ключа

  • /etc/consul-template.d/templates/vault-cert.tpl
{{- /* vault-cert.tpl */ -}}
{{ with secret "pki_intermediate_ca/issue/vault-dot-home-server-crt" "common_name=vault.home" "ttl=2m" }}
{{ .Data.certificate }}
{{ .Data.issuing_ca }}{{ end }}


  • /etc/consul-template.d/templates/vault-key.tpl
{{- /* vault-dot-home-key.tpl */ -}}
{{ with secret "pki_intermediate_ca/issue/vault-dot-home-server-crt" "common_name=vault.home" "ttl=2m"}}
{{ .Data.private_key }}{{ end }}

Конфигурация consul-template

  • /etc/consul-template.d/configs/nginx-vault.hcl

token пропущен так как авторизация через логин/пароль и токен получается динамически

vault {
  address = "http://vault.home:8200"
#  token = "<TOKEN_HERE>"
  renew_token = true

  retry {
    enabled = true
    attempts = 5
    backoff = "250ms"
  }
}

template {
  source      = "/etc/consul-template.d/templates/vault-cert.tpl"
  destination = "/etc/nginx/certs/vault.crt"
  perms       = "0600"
  command     = "systemctl reload nginx"
}

template {
  source      = "/etc/consul-template.d/templates/vault-key.tpl"
  destination = "/etc/nginx/certs/vault.key"
  perms       = "0600"
  command     = "systemctl reload nginx"
}

Тестовый запуск

Скрипт

#!/bin/bash

VAULT_TOKEN=$(vault \
    login \
    -method=userpass \
    username=vault-dot-home-server-crt-user \
    password=vault \
    --format=json | jq -r .auth.client_token
)


#echo ${VAULT_TOKEN}

export VAULT_TOKEN=${VAULT_TOKEN}

/usr/local/consul/bin/consul-template \
    -config /etc/consul-template.d/configs/nginx-vault.hcl

systemd unit

Создаю Unit-file:

  • имя файла /etc/systemd/system/consul-template-vault-nginx.service
  • Юнит "с дебагом"
  • Кроме всего прочего выводит переменные окружения (что бы убедиться что они нормально загружаются) и переменные systemd
  • Токен сохраняется во временном файле и потом отттуда попадает в переменные окружения
[Unit]
Description=consul-template for Nginx/Vault
Requires=network-online.target
After=network-online.target

[Service]
EnvironmentFile=-/etc/default/consul-template
EnvironmentFile=-/etc/default/%N
EnvironmentFile=-/tmp/vault-token-%N

Restart=on-failure
ExecStartPre=/usr/bin/env
ExecStartPre=/bin/bash -c " echo b=%b"
ExecStartPre=/bin/bash -c " echo g=%g"
ExecStartPre=/bin/bash -c " echo G=%G"
ExecStartPre=/bin/bash -c " echo H=%H"
ExecStartPre=/bin/bash -c " echo i=%i"
ExecStartPre=/bin/bash -c " echo j=%j"
ExecStartPre=/bin/bash -c " echo m=%m"
ExecStartPre=/bin/bash -c " echo n=%n"
ExecStartPre=/bin/bash -c " echo N=%N"
ExecStartPre=/bin/bash -c " echo p=%p"
ExecStartPre=/bin/bash -c " echo u=%u"
ExecStartPre=/bin/bash -c " echo U=%U"
ExecStartPre=/bin/bash -c " echo v=%v"
ExecStartPre=/bin/bash -c " echo %%"

ExecStartPre=/bin/bash -c "printf 'VAULT_TOKEN=' > /tmp/vault-token-%N; \
    /usr/local/vault/bin/vault \
        login \
        -method=userpass \
        username=$VAULT_USERNAME \
        password=$VAULT_PASSWORD \
        --format=json | jq -r .auth.client_token >> /tmp/vault-token-%N"

ExecStartPre=/usr/bin/env
ExecStart=/usr/local/consul/bin/consul-template \
    $OPTIONS \
    -config='/etc/consul-template.d/configs/nginx-vault.hcl'

KillSignal=SIGINT

[Install]
WantedBy=multi-user.target

Проверка работы

Безопастность игнорируется в тестом окружении - в проде нужно спрятать токены

systemctl  status consul-template-vault-nginx.service
● consul-template-vault-nginx.service - consul-template for Nginx/Vault
   Loaded: loaded (/etc/systemd/system/consul-template-vault-nginx.service; disabled; vendor preset: enabled)
   Active: active (running) since Fri 2021-10-15 14:07:37 EEST; 21min ago
  Process: 19036 ExecStartPre=/usr/bin/env (code=exited, status=0/SUCCESS)
  Process: 19037 ExecStartPre=/bin/bash -c  echo b=7d0758bee53b4ddbba5674ef66a7c067 (code=exited, status=0/SUCCESS)
  Process: 19038 ExecStartPre=/bin/bash -c  echo g=root (code=exited, status=0/SUCCESS)
  Process: 19039 ExecStartPre=/bin/bash -c  echo G=0 (code=exited, status=0/SUCCESS)
  Process: 19040 ExecStartPre=/bin/bash -c  echo H=raspberrypi4 (code=exited, status=0/SUCCESS)
  Process: 19041 ExecStartPre=/bin/bash -c  echo i= (code=exited, status=0/SUCCESS)
  Process: 19042 ExecStartPre=/bin/bash -c  echo j=nginx (code=exited, status=0/SUCCESS)
  Process: 19043 ExecStartPre=/bin/bash -c  echo m=f7cb830984574abd90d3b1a9616450f3 (code=exited, status=0/SUCCESS)
  Process: 19044 ExecStartPre=/bin/bash -c  echo n=consul-template-vault-nginx.service (code=exited, status=0/SUCCESS)
  Process: 19045 ExecStartPre=/bin/bash -c  echo N=consul-template-vault-nginx (code=exited, status=0/SUCCESS)
  Process: 19046 ExecStartPre=/bin/bash -c  echo p=consul-template-vault-nginx (code=exited, status=0/SUCCESS)
  Process: 19047 ExecStartPre=/bin/bash -c  echo u=root (code=exited, status=0/SUCCESS)
  Process: 19048 ExecStartPre=/bin/bash -c  echo U=0 (code=exited, status=0/SUCCESS)
  Process: 19049 ExecStartPre=/bin/bash -c  echo v=5.4.51-v7l+ (code=exited, status=0/SUCCESS)
  Process: 19050 ExecStartPre=/bin/bash -c  echo % (code=exited, status=0/SUCCESS)
  Process: 19051 ExecStartPre=/bin/bash -c printf 'VAULT_TOKEN=' > /tmp/vault-token-consul-template-vault-nginx (code=exited, status=0/SUCCESS)
  Process: 19052 ExecStartPre=/bin/bash -c printf 'VAULT_TOKEN=' > /tmp/vault-token-consul-template-vault-nginx;      /usr/local/vault/bin/vault          login          -method=userpass          username=$VAULT_USERNAME          password=$VAULT_PASSWORD          --format=json | jq -r .auth.
  Process: 19064 ExecStartPre=/usr/bin/env (code=exited, status=0/SUCCESS)
 Main PID: 19065 (consul-template)
    Tasks: 13 (limit: 4915)
   CGroup: /system.slice/consul-template-vault-nginx.service
           └─19065 /usr/local/consul/bin/consul-template -config=/etc/consul-template.d/configs/nginx-vault.hcl

Oct 15 14:07:37 raspberrypi4 env[19064]: LANGUAGE=en_US.UTF-8
Oct 15 14:07:37 raspberrypi4 env[19064]: LC_TIME=C.UTF-8
Oct 15 14:07:37 raspberrypi4 env[19064]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Oct 15 14:07:37 raspberrypi4 env[19064]: INVOCATION_ID=01f3f30994c34ca6888059f640df7bce
Oct 15 14:07:37 raspberrypi4 env[19064]: JOURNAL_STREAM=8:79140900
Oct 15 14:07:37 raspberrypi4 env[19064]: VAULT_ADDR=http://127.0.0.1:8200
Oct 15 14:07:37 raspberrypi4 env[19064]: VAULT_USERNAME=vault-dot-home-server-crt-user
Oct 15 14:07:37 raspberrypi4 env[19064]: VAULT_PASSWORD=vault
Oct 15 14:07:37 raspberrypi4 env[19064]: VAULT_TOKEN=s.tByQQL0DBdUjQH2lXAu2wuS8
Oct 15 14:07:37 raspberrypi4 systemd[1]: Started consul-template for Nginx/Vault.

Проверка работы Nginx

Без корневого сертификата

Ожидаемо - не работает

curl https://vault.home:8202 -v
* Expire in 5 ms for 1 (transfer 0x1682880)
*   Trying 192.168.1.223...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x1682880)
* Connected to vault.home (192.168.1.223) port 8202 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Добавление корневого сертификата

  • Внимание - расширение файла ВАЖНО для утилиты update-ca-certificates. Если оставить PEM то сертификат не будет добавлен.
  • rootCA.pem - файл корневого сертификата (см выше по тексту)
  • Корневой сертификат (НО НЕ КЛЮЧ!) всегда можно получить curl http://vault.home:8200/v1/pki_root_ca/ca/pem
cp rootCA.pem /usr/local/share/ca-certificates/rootCA.crt
update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

Adding debian:rootCA.pem
done.
done.
root@raspberrypi4:/usr/local/share/ca-certificates/extra#

С добавленным корневым сертификатом

Ожидаемо работает: (404 это НЕ ошибка SSL)

curl https://vault.home:8202
404 page not found

Проверка обновления сертефикатов

while true;  do echo "" | openssl s_client -connect vault.home:8202  > 111 2>/dev/null ;  openssl x509 -in 111 -text -noout | grep -E 'Not [AB]'; sleep 30; done
            Not Before: Oct 15 11:55:07 2021 GMT
            Not After : Oct 15 11:57:35 2021 GMT
            Not Before: Oct 15 11:55:07 2021 GMT
            Not After : Oct 15 11:57:35 2021 GMT
            Not Before: Oct 15 11:55:07 2021 GMT
            Not After : Oct 15 11:57:35 2021 GMT
            Not Before: Oct 15 11:56:11 2021 GMT

Проверка отзыва сертификатов

TBD!

Дополнения

Параметры сертификатов

  • name (string: <required>) – Specifies the name of the role to create. This is part of the request URL.
  • ttl (string: "") – Specifies the Time To Live value provided as a string duration with time suffix. Hour is the largest suffix. If not set, uses the system default value or the value of max_ttl, whichever is shorter.
  • max_ttl (string: "") – Specifies the maximum Time To Live provided as a string duration with time suffix. Hour is the largest suffix. If not set, defaults to the system maximum lease TTL.
  • allow_localhost (bool: true) – Specifies if clients can request certificates for localhost as one of the requested common names. This is useful for testing and to allow clients on a single host to talk securely.
  • allowed_domains (list: []) – Specifies the domains of the role. This is used with the allow_bare_domains and allow_subdomains options.
  • allowed_domains_template ()bool: false) – When set, allowed_domains may contain templates, as with ACL Path Templating.
  • allow_bare_domains (bool: false) – Specifies if clients can request certificates matching the value of the actual domains themselves; e.g. if a configured domain set with allowed_domains is example.com, this allows clients to actually request a certificate containing the name example.com as one of the DNS values on the final certificate. In some scenarios, this can be considered a security risk.
  • allow_subdomains (bool: false) – Specifies if clients can request certificates with CNs that are subdomains of the CNs allowed by the other role options. This includes wildcard subdomains. For example, an allowed_domains value of example.com with this option set to true will allow foo.example.com and bar.example.com as well as *.example.com. This is redundant when using the allow_any_name option.
  • allow_glob_domains (bool: false) - Allows names specified in allowed_domains to contain glob patterns (e.g. ftp*.example.com). Clients will be allowed to request certificates with names matching the glob patterns.
  • allow_any_name (bool: false) – Specifies if clients can request any CN. Useful in some circumstances, but make sure you understand whether it is appropriate for your installation before enabling it.
  • enforce_hostnames (bool: true) – Specifies if only valid host names are allowed for CNs, DNS SANs, and the host part of email addresses.
  • allow_ip_sans (bool: true) – Specifies if clients can request IP Subject Alternative Names. No authorization checking is performed except to verify that the given values are valid IP addresses.
  • allowed_uri_sans (string: "") - Defines allowed URI Subject Alternative Names. No authorization checking is performed except to verify that the given values are valid URIs. This can be a comma-delimited list or a JSON string slice. Values can contain glob patterns (e.g. spiffe://hostname/*).
  • allowed_other_sans (string: "") – Defines allowed custom OID/UTF8-string SANs. This can be a comma-delimited list or a JSON string slice, where each element has the same format as OpenSSL: <oid>;<type>:<value>, but the only valid type is UTF8 or UTF-8. The value part of an element may be a * to allow any value with that OID. Alternatively, specifying a single * will allow any other_sans input.
  • server_flag (bool: true) – Specifies if certificates are flagged for server use.
  • client_flag (bool: true) – Specifies if certificates are flagged for client use.
  • code_signing_flag (bool: false) – Specifies if certificates are flagged for code signing use.
  • email_protection_flag (bool: false) – Specifies if certificates are flagged for email protection use.
  • key_type (string: "rsa") – Specifies the type of key to generate for generated private keys and the type of key expected for submitted CSRs. Currently, rsa and ec are supported, or when signing CSRs any can be specified to allow keys of either type and with any bit size (subject to > 1024 bits for RSA keys).
  • key_bits (int: 2048) – Specifies the number of bits to use for the generated keys. This will need to be changed for ec keys, e.g., 224, 256, 384 or 521.
  • key_usage (list: ["DigitalSignature", "KeyAgreement", "KeyEncipherment"]) – Specifies the allowed key usage constraint on issued certificates. Valid values can be found at https://golang.org/pkg/crypto/x509/#KeyUsage - simply drop the KeyUsage part of the value. Values are not case-sensitive. To specify no key usage constraints, set this to an empty list.
  • ext_key_usage (list: []) – Specifies the allowed extended key usage constraint on issued certificates. Valid values can be found at https://golang.org/pkg/crypto/x509/#ExtKeyUsage - simply drop the ExtKeyUsage part of the value. Values are not case-sensitive. To specify no key usage constraints, set this to an empty list.
  • ext_key_usage_oids (string: "") - A comma-separated string or list of extended key usage oids.
  • use_csr_common_name (bool: true) – When used with the CSR signing endpoint, the common name in the CSR will be used instead of taken from the JSON data. This does not include any requested SANs in the CSR; use use_csr_sans for that.
  • use_csr_sans (bool: true) – When used with the CSR signing endpoint, the subject alternate names in the CSR will be used instead of taken from the JSON data. This does not include the common name in the CSR; use use_csr_common_name for that.
  • ou (string: "") – Specifies the OU (OrganizationalUnit) values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • organization (string: "") – Specifies the O (Organization) values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • country (string: "") – Specifies the C (Country) values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • locality (string: "") – Specifies the L (Locality) values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • province (string: "") – Specifies the ST (Province) values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • street_address (string: "") – Specifies the Street Address values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • postal_code (string: "") – Specifies the Postal Code values in the subject field of issued certificates. This is a comma-separated string or JSON array.
  • serial_number (string: "") – Specifies the Serial Number, if any. Otherwise Vault will generate a random serial for you. If you want more than one, specify alternative names in the alt_names map using OID 2.5.4.5.
  • generate_lease (bool: false) – Specifies if certificates issued/signed against this role will have Vault leases attached to them. Certificates can be added to the CRL by vault revoke <lease_id> when certificates are associated with leases. It can also be done using the pki/revoke endpoint. However, when lease generation is disabled, invoking pki/revoke would be the only way to add the certificates to the CRL.
  • no_store (bool: false) – If set, certificates issued/signed against this role will not be stored in the storage backend. This can improve performance when issuing large numbers of certificates. However, certificates issued in this way cannot be enumerated or revoked, so this option is recommended only for certificates that are non-sensitive, or extremely short-lived. This option implies a value of false for generate_lease.
  • require_cn (bool: true) - If set to false, makes the common_name field optional while generating a certificate.
  • policy_identifiers (list: []) – A comma-separated string or list of policy OIDs.
  • basic_constraints_valid_for_non_ca (bool: false) - Mark Basic Constraints valid when issuing non-CA certificates.
  • not_before_duration (duration: "30s") – Specifies the duration by which to backdate the NotBefore property.

Ссылки

Русскоязычные

Vault

Прочие