Те, кто в своей инфраструктуре использует Microsoft CA, могли часто встречаться с проблемой выпуска сертификатов для linux машин\cервисов.
Все время приходилось вручную генерировать сертификат с помощью openssl, а далее идти в веб интерфейс MS CA,запрашивать, потом скачивать сертификат и заливать его на машину.
К счастью, нашелся модуль python, который облегачет получение сертификатов в Linux- certsrv
Пример получения сертификата
При установке модуля по умолчанию используется basic-аутентификация, мы будем рассматривать аутентификацию с помощью NTLM, поэтому установим модуль с добавлением [ntlm] к названию.
Установим модуль certsrv c поддержкой NTLM
1pip install certsrv[ntlm]
С certsrv должен установиться модуль requests_ntlm, если этого не произошло - установите его вручную и повторите установку certsrv.
В документации есть пример генерации сертификата c использованием шаблона WebServer,попробуем запустить его, предварительно установив модуль cryptography :
Установка модуля cryptography:
1pip install cryptography
Создадим файл скрипта для генерации:
1from certsrv import Certsrv
2
3from cryptography.hazmat.backends import default_backend
4from cryptography.hazmat.primitives import serialization
5from cryptography.hazmat.primitives.asymmetric import rsa
6from cryptography import x509
7from cryptography.x509.oid import NameOID
8from cryptography.hazmat.primitives import hashes
9
10# Generate a key
11key = rsa.generate_private_key(
12 public_exponent=65537,
13 key_size=2048,
14 backend=default_backend()
15)
16
17# Generate a CSR
18csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
19 x509.NameAttribute(NameOID.COMMON_NAME, u"{FQDN}"),
20])).add_extension(
21 x509.SubjectAlternativeName([
22 x509.DNSName(u"{FQDN}"),
23 ]),
24 critical=False,
25).sign(key, hashes.SHA256(), default_backend())
26
27# Get the cert from the ADCS server
28pem_req = csr.public_bytes(serialization.Encoding.PEM)
29
30ca_server = Certsrv("{MS_CA_URL}", "{USER}", "{PASSWORD}")
31pem_cert = ca_server.get_cert(pem_req, "{CERT_TYPE}")
32
33# Print the key and the cert
34pem_key = key.private_bytes(
35 encoding=serialization.Encoding.PEM,
36 format=serialization.PrivateFormat.TraditionalOpenSSL,
37 encryption_algorithm=serialization.NoEncryption(),
38)
39
40print("Cert:\n{}".format(pem_cert.decode()))
41print("Key:\n{}".format(pem_key.decode()))
Не забываем указать:
{CERT_TYPE} - шаблон сертификата[^1] ;
{MS_CA_URL} - ссылка на страницу получения сертификатов MS CA;
{USER} - доменный пользователь с правами выдачи сертификатов;
{PASSWORD} - пароль пользоваетеля;
{FQDN} - полное доменное имя сайта\сервера;
После запуска скрипта он должен выдать в STDOUT содержимое файлов сертификата и ключа. Далее их нужно скопировать и создать соответсвтующие файлы:
- Файл сертификата с расширением pem;
- Файл ключа с расширением key;
Известные проблемы
ValueError: unsupported hash type md4
При использовании NTLM-аутентификации может встретиться ошибка:
1ValueError: unsupported hash type md4
Решение: Добавить в /usr/lib/openssl.cnf следующие строки:
1[provider_sect]
2default = default_sect
3legacy = legacy_sect
4
5[default_sect]
6activate = 1
7
8[legacy_sect]
9activate = 1
Примечания:
Пример наименования некоторых стандартных шаблонов для выпуска сертификатов
- WebServerfor10years
- WebServer - (на 1 год)
- User
- EFS
- Administrator
- EFSRecovery
- CodeSigning
- CodeSignin10years
- SubCA
Дополнительные аттрибуты для запроса сертификата
При формировании запроса можно добавить дополнительные атрибуты (подробнее в документации модуля cryptography), например:
1csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
2 x509.NameAttribute(NameOID.COMMON_NAME, 0xbbeer.ru),
3 x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
4 x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Alabama"),
5 x509.NameAttribute(NameOID.LOCALITY_NAME, u"SweetHomeAlabama"),
6 x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"SuperORG"),
7])).add_extension(
8 x509.SubjectAlternativeName([
9 x509.DNSName('0xbbeer.ru'),
10 x509.IPAddress('192.168.0.1'),
11 ]),
12 critical=False,
13).sign(key, hashes.SHA256(), default_backend())