Ваш VPN відповідає на підключення о 3 годині ночі, а машина, що підписує його сертифікати, лежить вимкнена в шухляді. Клієнт, який перевіряє сертифікат сервера, ніколи не звертається до вашого CA, тому CA не обов'язково має бути доступним для жодного підключення. Частина, яка потребує планування, — це день, коли сертифікат треба видати, поновити або відкликати поки кореневий ключ залишається під замком.

Що насправді перевіряє підключення

Коли клієнт OpenVPN підключається, він проходить ланцюг підписів від листового сертифіката сервера вгору до сертифіката CA, якому вже довіряє, перевіряє вікно дії і, якщо профіль так каже, звертається до списку відкликання. Він ніколи не шукає ваш кореневий CA. Сервер робить дзеркальну дію для клієнтських сертифікатів. У момент підключення присутні лише лист, проміжний сертифікат і пакет довіри, який ви поширили раніше.

Насправді не спрацьовує одне з трьох: ключ підпису недоступний під час видачі, список відкликання застарів або пакет довіри клієнта старіший за ланцюг, який подає сервер. Перше — проблема процесу; решта дві — проблеми розповсюдження. Жодне не вимагає, щоб кореневий ключ був онлайн, а керована служба VPN, яка для автентифікації тунелю вимагає приватний сертифікат, лише переносить повноваження підпису до постачальника. Azure «точка — сайт» працює навпаки: ви завантажуєте кореневий сертифікат, і сертифікати, підписані під ним, вважаються довіреними, тому питайте, хто підписує, хто перевіряє і як сертифікат відкликають достроково.

Побудуйте корінь на хості, який ніколи не отримує адресу

Офлайн-корінь використовують рідко: щоб підписати сертифікат проміжного CA, а згодом поновити або відкликати його. Усе інше відбувається на рівень нижче. Виконуйте роботу з коренем на машині, яку тримають поза мережею, за можливості із живої системи, і записуйте, хто був у кімнаті. Команди припускають OpenSSL 1.1.1 або новіший, бо -addext немає в старих збірках.

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out root.key
openssl req -x509 -new -key root.key -sha256 -days 7300 \
  -subj "/CN=Example VPN Root CA" -out root.crt \
  -addext "basicConstraints=critical,CA:TRUE" \
  -addext "keyUsage=critical,keyCertSign,cRLSign"

Створіть проміжний сертифікат на хості підпису і віднесіть лише його запит на офлайн-машину. Корінь підписує цей запит з нульовою довжиною шляху, тому проміжний CA може підписувати сертифікати кінцевих точок, але не може створити інший CA під собою. Це єдине розширення не дає скомпрометованому хосту підпису створити повноваження, рівне кореню. Використовуйте ECDSA лише якщо кожен клієнт працює на OpenVPN 2.4 або новішому; інакше згенеруйте ключі RSA і збережіть той самий потік.

openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out intermediate.key
openssl req -new -key intermediate.key -subj "/CN=Example VPN Intermediate CA" -out intermediate.csr
openssl x509 -req -in intermediate.csr -CA root.crt -CAkey root.key \
  -CAcreateserial -days 3650 -sha256 -extfile intermediate.ext -out intermediate.crt

intermediate.ext містить basicConstraints=critical,CA:TRUE,pathlen:0, keyUsage=critical,keyCertSign,cRLSign і authorityKeyIdentifier=keyid:always. Поверніть кореневий ключ до сейфа перед повторним підключенням хоста підпису. Якщо корінь має вміти відкликати проміжний сертифікат, підпишіть його за допомогою openssl ca -config root.cnf -extensions intermediate_ca проти index.txt кореневого рівня замість openssl x509 -req, щоб серійний номер було записано; потім надішліть отриманий кореневий список у профілі клієнта, де файловий crl-verify читає його під час запуску. Надсилайте клієнтам пакет з обома сертифікатами CA; сам лист не перевіриться проти кореня.

Видавайте листи з проміжного CA, з базою даних

Підпис за допомогою openssl x509 -req працює, доки не знадобиться відкликання, бо ніщо не записує серійний номер. Використовуйте openssl ca від першого сертифіката, щоб серійний номер, термін і стан відкликання жили в одній базі даних. Створіть файли бази даних один раз, потім спрямуйте на них конфігурацію:

mkdir -p /etc/vpn-ca/intermediate/newcerts
touch /etc/vpn-ca/intermediate/index.txt
echo 1000 > /etc/vpn-ca/intermediate/serial
echo 1000 > /etc/vpn-ca/intermediate/crlnumber
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /etc/vpn-ca/intermediate
database = $dir/index.txt
new_certs_dir = $dir/newcerts
serial = $dir/serial
crlnumber = $dir/crlnumber
certificate = $dir/intermediate.crt
private_key = $dir/intermediate.key
default_md = sha256
default_days = 365
default_crl_days = 30
unique_subject = no
policy = policy_any
[ policy_any ]
commonName = supplied
[ server_cert ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @server_names
[ client_cert ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = clientAuth
[ server_names ]
DNS.1 = vpn.example.com

Рядок використання ключа припускає ключ ECDSA; для RSA додайте keyEncipherment. Дайте кожному клієнту власне загальне ім'я, бо OpenVPN ідентифікує підключення за цим полем, і два ноутбуки, що спільно використовують CN=vpn-client, неможливо розрізнити в журналах, у правилах client-config-dir або в будь-якому push для користувача, і ви втрачаєте прив'язку, яка робить рішення про відкликання обґрунтованим. Видайте та перевірте лист так:

openssl req -new -key alice.key -subj "/CN=alice-laptop" -out alice.csr
openssl ca -config intermediate.cnf -extensions client_cert -in alice.csr -out alice.crt -batch
openssl verify -CAfile ca-bundle.crt -untrusted intermediate.crt alice.crt

Тримайте ключ підпису за межею політики, а не копіюйте його на хост VPN: невелика служба, яка автентифікує запит, перевіряє загальне ім'я за списком дозволених і повертає лише сертифікат. Записуйте кожен серійний номер; журнал — єдиний зв'язок між сертифікатом і людиною, яка його запросила.

Публікуйте відкликання, не будячи корінь

Клієнтські сертифікати відкликає проміжний CA, ніколи корінь. Кореневий список важливий лише тоді, коли проміжний CA спалено. Спрямуйте сервер на проміжний список за допомогою crl-verify. Файловий CRL завантажується під час запуску служби, тому перезавантажуйте після кожного перегенерування; каталогова форма читає стан у момент підключення і коштує один файл на серійний номер.

Відкличте та перегенеруйте двома командами з тієї ж бази даних:

openssl ca -config intermediate.cnf -revoke /etc/vpn-ca/intermediate/newcerts/1001.pem
openssl ca -config intermediate.cnf -gencrl -out /etc/openvpn/crl.pem

Потім стежте за годинником. CRL несе поле наступного оновлення, і коли ця дата минає, сторона, що перевіряє, відхиляє список, а не ігнорує його. Щомісячне перегенерування з default_crl_days = 30 залишає запас; щорічне — ні. Коротші терміни життя листів — надійніший контроль, бо відкликання діє лише там, де список перевіряють.

Перевірте ланцюг, потім запишіть церемонію

Відтворіть чотири збої, перш ніж довіряти схемі. Перевірте ланцюг за допомогою openssl verify -CAfile ca-bundle.crt -untrusted intermediate.crt server.crt. Підключіться з простроченим клієнтським сертифікатом і переконайтеся, що сервер його відхиляє. Відкличте тестового клієнта, перезавантажте і переконайтеся, що список діє. Переведіть годинник хоста на день уперед, бо неправильний годинник може видати й відхилити сертифікати, які деінде виглядають дійсними.

Офлайн-корінь — це контроль, лише якщо є папери: двоє названих людей для церемонії, запис кожного підписаного серійного номера, зашифрована резервна копія, що зберігається окремо від машини, і тест відновлення на ізольованому хості. Підпишіть запасний проміжний CA під час тієї ж церемонії, щоб мертвий хост підпису не змусив вас відкривати корінь опівночі.

Перш ніж назвати це завершеним, підтвердьте п'ять речей: кореневий ключ не на мережевому хості, проміжний ключ живе лише на хості підпису, пакет клієнта несе обидва сертифікати CA, у CRL є майбутнє наступне оновлення, і хтось має запис у календарі на кожен термін у ланцюзі. Якщо чогось немає, конфігурація, що виглядає офлайн, просто недокументована.

Схожі статті