Vault PKI Kubernetes the hard way v2
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
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 write pki_intermediate_ca/roles/example-dot-home-client-crt \ country="Ukraine" \ locality="Kharkov" \ street_address="Lui Pastera st. 322 app. 131" \ postal_code="61172" \ organization="Home Network" \ ou="IT" \ allow_subdomains=true \ max_ttl="87600h" \ key_bits="2048" \ key_type="rsa" \ allow_any_name=true \ allow_bare_domains=false \ allow_glob_domain=false \ allow_ip_sans=false \ allow_localhost=false \ client_flag=true \ server_flag=false \ enforce_hostnames=false \ key_usage="DigitalSignature" \ ext_key_usage="ClientAuth" \ 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.
Ссылки
Русскоязычные
- О ограничениях накладывемых на сертефикаты https://www.sysadmins.lv/blog-ru/znakomtes-basic-constraints.aspx
- https://pro-ldap.ru/tr/zytrax/tech/ssl.html
- https://sgolubev.ru/openssl-ca/
- На русском с минимальными пояснениями: https://itdraft.ru/2020/12/02/hashicorp-vault-kak-czentr-sertifikaczii-ca-vault-pki/
Vault
- https://russianblogs.com/article/8543260210/
- https://itdraft.ru/2020/12/02/hashicorp-vault-kak-czentr-sertifikaczii-ca-vault-pki/