Actions
Feature #3006
openACME Protocol Support
Start date:
11/09/2025
Due date:
% Done:
0%
Estimated time:
(Total: 0:00 h)
Description
Реализовать получение SSL-сертификатов по протоколу ACME для получения SSL-сертификатов Let'a Encrypt - ACME Protocol Support
Based on perplexity promt: https://www.perplexity.ai/search/napishi-programmu-na-python-ko-Ak_4QyRdS0OpFM1reQ6dRg
Условия¶
- На сервере ACME LetsEncrypt есть лимиты по запросам с одного IP
- Необходимо стремиться к минимальному количеству неуспешных запросов к Acme серверу
- перед отправкой запорса к ACME-серверу Необходимо убедиться в том, что хост, для которого запрашивается SSL-сертификат был прописан на сервера ProCDN
Схема работы службы Cert для получения Lets encrytp SSL

Программа на Python для получения SSL-сертификата Let's Encrypt по ACME и сохранения в БД¶
Ниже приведён пример программы, которая:
- Использует библиотеку
acmeдля взаимодействия с Let's Encrypt по протоколу ACME. - Проходит HTTP-01 challenge для валидации домена.
- Получает SSL-сертификат.
- Сохраняет сертификат и приватный ключ в базу данных PostgreSQL в формате PEM.
Основные зависимости¶
-
acme(часть certbot): для работы с ACME-протоколом. -
cryptography: для генерации ключей и создания CSR. -
psycopg2илиasyncpg: для работы с PostgreSQL.
Пример кода¶
python
import time
import psycopg2
from acme import client, messages, challenges
from acme import crypto_util
from josepy import JWKRSA
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
# Константы
DIRECTORY_URL = 'https://acme-v02.api.letsencrypt.org/directory'
DOMAIN = 'example.com'
EMAIL = 'admin@example.com'
# 1. Генерируем ключ пользователя
user_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
jwk = JWKRSA(key=user_key)
# 2. Создаём клиента ACME
net = client.ClientNetwork(jwk)
directory = client.ClientV2.get_directory(DIRECTORY_URL, net)
acme_client = client.ClientV2(directory, net=net)
# 3. Регистрируем аккаунт
account = acme_client.new_account(
messages.NewRegistration.from_data(
email=EMAIL,
terms_of_service_agreed=True
)
)
# 4. Запрашиваем авторизацию для домена
order = acme_client.new_order(
csr_pem=None,
identifiers=[
messages.Identifier(
typ=messages.IDENTIFIER_FQDN,
value=DOMAIN
)
]
)
authz = order.authorizations[0]
challenge = next(c for c in authz.body.challenges if isinstance(c.chall, challenges.HTTP01))
# 5. Вам нужно разместить challenge token на вашем сервере по адресу /.well-known/acme-challenge/{token}
# challenge.chall.path, challenge.chall.validation(jwk)
# Этот шаг требует настройки веб-сервера!
# 6. Отвечаем на challenge
acme_client.answer_challenge(challenge, challenge.chall.response(jwk))
# 7. Ожидаем подтверждения авторизации
while True:
authz = acme_client.poll(authz)[0]
if authz.body.status == 'valid':
break
time.sleep(2)
# 8. Генерируем ключ для сертификата и CSR
cert_key = crypto_util.make_key(bits=2048)
csr_pem = crypto_util.make_csr(cert_key, [DOMAIN])
# 9. Запрашиваем выпуск сертификата
final_order = acme_client.finalize_order(order, csr_pem)
certificate_pem = acme_client.fetch_chain(final_order)[0].public_bytes().decode()
# 10. Сохраняем сертификат и ключ в PostgreSQL
conn = psycopg2.connect("dbname=yourdb user=youruser password=yourpass")
cur = conn.cursor()
cur.execute("""
INSERT INTO certificates (domain, cert_pem, key_pem)
VALUES (%s, %s, %s)
""", (DOMAIN, certificate_pem, cert_key.decode()))
conn.commit()
cur.close()
conn.close()
ссылки по теме¶
Files
Updated by Viacheslav Anzhiganov 28 days ago
- Status changed from New to In Progress
- Assignee set to Viacheslav Anzhiganov
- Priority changed from Normal to High
Actions