Feature #3006
Updated by Viacheslav Anzhiganov 28 days ago
Реализовать получение 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-challenge
- [ ] Реализова
# Условия
- На сервере ACME LetsEncrypt есть лимиты по запросам с одного IP
- Необходимо стремиться к минимальному количеству неуспешных запросов к Acme серверу
- перед отправкой запорса к ACME-серверу Необходимо убедиться в том, что хост, для которого запрашивается SSL-сертификат был прописан на сервера ProCDN
Схема работы службы Cert для получения Lets encrytp SSL
[[ACME protocol schema.svg]]
## Программа на 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
```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()
```
## ссылки по теме
* [ ] https://github.com/torhve/lua-resty-letsencrypt
* [ ] https://github.com/auto-ssl/lua-resty-auto-ssl