Let's Encrypt на Nginx и IIS: выдача и продление без боли
За последние годы мы внедрили Let's Encrypt на десятки корпоративных сайтов: от бухгалтерских кабинетов на Debian до шлюзов RDS Gateway на Windows Server. Первый вопрос от клиентов всегда один: «А это точно бесплатно и нормально работать будет?» Да, точно. Дальше — как мы это делаем на практике: Certbot для Nginx, win-acme для IIS, wildcard-сертификаты, автопродление и те два-три сценария, когда что-то идёт не так.
Почему DV-сертификата от Let's Encrypt достаточно 90% офисов
На корпоративном сайте, скажем, условного ООО «УютТрикотаж», клиенту совершенно не нужен EV-сертификат с зелёной строкой — тем более в новых браузерах её уже попросту нет. Нужен надёжный TLS, который не будет показывать красный замок. Let's Encrypt даёт именно это: RSA 2048 или ECDSA P-256, надёжная цепочка через ISRG Root X1 и полная поддержка TLS 1.2/1.3 — всё это браузеры принимают без единого щелчка.
Как я обычно объясняю нашим заказчикам? Смотрите, платный сертификат, который запросто может стоить 18 000 рублей в год, от бесплатного Let's Encrypt по сути отличается всего одной графой в свойствах. Это «Organization Validation». Скажу вам честно: в самом HTTPS-туннеле эта штука не шифрует ровным счётом ничего. А при реальных атаках, увы, она тоже не выручит. Именно поэтому мы, в ITFresh, за последние три года выдали нашим клиентам всего два «платных» сертификата. И знаете почему? Оба раза это были просто обязательные требования из тендерной документации — ну, обычная бумажная волокита, и ничего более.
Эти 90 дней — не баг, а фича: если ключ утечёт, у злоумышленника будет максимум три месяца, чтобы им воспользоваться. Крупные удостоверяющие центры (CA) тоже движутся в этом направлении: CA/Browser Forum голосует за то, чтобы сократить сроки и для платных сертификатов.
Certbot на Ubuntu для Nginx: быстрый путь
Когда речь идёт о свежей установке Ubuntu 22.04 или Debian 12, я Certbot всегда ставлю только из snap. Знаете почему? Пакет в apt обычно безнадёжно отстаёт, и любая попытка поправить через него ошибки превращается в настоящую лотерею. Оно вам надо?
sudo apt remove certbot python3-certbot* -y
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version
И вот, всего одна команда — и ваш Nginx уже уверенно "говорит" по HTTPS.
sudo certbot --nginx -d crm.example.ru -d www.crm.example.ru \
--email admin@example.ru --agree-tos --redirect --non-interactive
Флаг --redirect добавит серверный блок return 301 https://... в конфиг — HTTP сразу перекидывается на HTTPS. Это экономит десять минут на ручной правке.
Файлы сертификата лежат в /etc/letsencrypt/live/crm.example.ru/. Для упрощённой поддержки я советую ссылаться именно на fullchain.pem и privkey.pem — это симлинки, которые Certbot автоматически перецепляет на новые файлы при каждом продлении.
Боевой конфиг Nginx с A+ на SSL Labs
Автоматический конфиг от Certbot, конечно, неплох для начала. Как база он вполне годится. Но лично я для продакшена всегда переписываю его вручную. Зачем? Да просто чтобы выжать всё возможное и получить то самое заветное A+ на ssllabs.com! И вот сейчас я поделюсь нашим рабочим шаблоном. Он, кстати, сейчас уверенно крутится у нас на восьми серверах Dell Xeon Platinum 8280 в дата-центре МТС. И знаете что? Спокойно отдаёт сотни клиентских сайтов, даже не поперхнувшись.
server {
listen 80;
server_name crm.example.ru www.crm.example.ru;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name crm.example.ru www.crm.example.ru;
ssl_certificate /etc/letsencrypt/live/crm.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crm.example.ru/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/crm.example.ru/chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
root /var/www/crm.example.ru;
}
Теперь важно: сначала проверим синтаксис, а потом аккуратно перезагрузим Nginx. Только так, чтобы без разрыва текущих соединений!
sudo nginx -t && sudo systemctl reload nginx
Автопродление: systemd-таймер и хуки
Кстати, snap-версия Certbot сама ставит systemd-таймер. Если хотите проверить, что всё на месте, вот как это сделать:
sudo systemctl list-timers | grep certbot
sudo certbot renew --dry-run
Вот что действительно важно, запомните: нужно обязательно добавить deploy-хук для перезагрузки Nginx. Потому что если его не будет, сертификат хоть и обновится в папке, но веб-сервер будет продолжать отдавать пользователям старый.
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'EOF'
#!/bin/bash
systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
По моей статистике, именно забытый хук — чемпион по части внезапных проблем. Это самая частая причина, по которой HTTPS вдруг «протухает» через четыре месяца после всех настроек.
Wildcard и DNS-01: когда поддоменов много
Один клиент — сеть салонов оптики, 14 городов, 14 поддоменов spb.lenses.ru, kzn.lenses.ru и так далее. Ставить 14 сертификатов — больно. Выпускаем один *.lenses.ru через DNS-01.
sudo certbot certonly --manual \
--preferred-challenges dns \
-d lenses.ru -d '*.lenses.ru' \
--agree-tos -m admin@lenses.ru
Certbot выдаст TXT-запись, и на тестовом сервере её можно спокойно прописать в DNS-зоне вручную. Но для продакшена ручной режим не вариант: каждое продление требовало бы, чтобы человек сидел и что-то вбивал. Куда проще поставить DNS-плагин, если провайдер поддерживает API. Например, для Cloudflare это выглядит так:
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d lenses.ru -d '*.lenses.ru'
win-acme на IIS: привычный инструмент Windows-админа
На Windows Server стандарт — утилита win-acme (раньше называлась letsencrypt-win-simple). Лежит на win-acme.com, качаем zip, распаковываем в C:\tools\win-acme\. Запускаем от Administrator.
C:\tools\win-acme\wacs.exe
Мастер пошаговый: N — новый сертификат, 1 — Single binding of an IIS site, выбираем сайт, подтверждаем. Win-acme сам поднимет HTTP-01-валидацию в папке сайта, получит сертификат, импортирует в хранилище WebHosting и перепривяжет binding. Плюс создаст задание в Task Scheduler: win-acme renew (acme-v02.api.letsencrypt.org), которое запускается ежедневно.
| Параметр | Nginx + Certbot | IIS + win-acme |
|---|---|---|
| Расположение сертификатов | /etc/letsencrypt/live/ | C:\ProgramData\win-acme\…\Certificates\ |
| Планировщик | systemd timer (снап) | Task Scheduler |
| Хуки после продления | renewal-hooks/deploy/ | Installation steps → Scripts |
| Поддержка wildcard | да, через DNS-плагин | да, через скрипты DNS |
| Интеграция с ОС | файлы + symlink | Windows Cert Store |
Реальный кейс: юридическая фирма, 11 сайтов и RDS-шлюз
Весной 2025 года мы забирали на поддержку юрфирму в Москве — 11 поддоменов (www, cabinet, mail, portal, docs и другие), все в одном домене, плюс Remote Desktop Gateway для 27 юристов на удалёнке. До нас коллеги каждый квартал вручную покупали и ставили сертификаты от платного CA — общая стоимость на сайте 52 000 руб/год и ещё 11 000 руб/год на RDS.
За полтора рабочих дня мы развернули: один wildcard *.lawfirm.ru через Cloudflare API, отдельный сертификат на RDS Gateway и скрипт PowerShell, который после продления автоматически перецепляет thumbprint к RDS. В результате — 63 000 рублей годовой экономии, ноль ручных операций и никаких «а у нас опять красный замок» по понедельникам. Скрипт для RDS ниже.
# ImportRDGateway_Cert.ps1 — цепляет свежий cert к RD Gateway
$site = "Default Web Site"
$bind = Get-WebBinding -Name $site -Protocol "https"
$cert = Get-ChildItem Cert:\LocalMachine\WebHosting |
Where-Object { $_.Thumbprint -eq $bind.certificateHash }
Import-Module RemoteDesktopServices
Set-Item -Path "RDS:\GatewayServer\SSLCertificate\Thumbprint" -Value $cert.Thumbprint
Restart-Service TSGateway
Этот скрипт вызываем из win-acme прямо как post-install. А вот тут есть нюанс: перезапуск TSGateway, увы, рвёт все активные RDP-сессии. Поэтому мы всегда ставим это задание на 03:40 утра, в ночь с воскресенья на понедельник. Лучше пусть оно будет тогда, когда никого не будет в работе, правда?
Грабли, на которые я наступал лично
- Порт 80 закрыт файрволом «чтобы безопасно». HTTP-01 валидация летит. Либо пробросьте 80/tcp, либо переходите на DNS-01. Проверка:
curl -I http://yoursite/.well-known/acme-challenge/test. - Rate limit 50 сертификатов в неделю на домен. Если отлаживаетесь — используйте
--staging, это отдельный тестовый CA с почти бесконечными лимитами, но без реального доверия браузером. - Сертификат продлился, но сервис отдаёт старый. Значит, забыт deploy-хук. Проверьте:
openssl s_client -connect host:443 < /dev/null 2>/dev/null | openssl x509 -noout -dates. - Certbot ругается на DNS A-запись. Зонтик CDN или DNSSEC сломан на стороне провайдера. Попросите отдел DNS временно отключить CNAME flatten.
- В win-acme исчезли привязки после IIS-обновления. Так бывает после накатов апдейтов Windows Server 2019 → 2022. Запустите
wacs.exe --renew --force— win-acme перевыпустит и заново привяжет. - HSTS включён, а сертификат протух. Браузер показывает жёсткую ошибку без «Продолжить». Урок: включайте HSTS только когда уверены, что автопродление работает хотя бы один цикл.
Когда Let's Encrypt не подходит
Есть несколько случаев, когда лучше купить платный сертификат, а не использовать Let's Encrypt:
- Государственные ИС. Для интеграции с СМЭВ, ЕГИС и другими гос-системами часто требуется сертификат от российского УЦ (КриптоПро, «ИнфоТеКС»). Let's Encrypt юридически не подходит.
- Внутренние сервисы без выхода в интернет и без внешнего DNS. Если контур закрытый, валидацию Let's Encrypt пройти не получится — нужен внутренний УЦ на базе AD CS или EJBCA.
- Сертификаты для подписи кода (Code Signing). Такие Let's Encrypt не выдаёт, нужен платный сертификат от другого CA.
- Клиентские сертификаты для аутентификации. Для mTLS с проверкой клиента нужен собственный CA, а не Let's Encrypt.
Во всех остальных случаях — а это подавляющее большинство сайтов малого и среднего бизнеса — Let's Encrypt полностью закрывает потребности.
Мониторинг: узнать о проблеме раньше, чем клиент
Лично я, чтобы не пропустить, на каждом клиентском сервере в LibreNMS настроил SNMP-чек срока действия сертификата. Если до окончания остаётся две недели, мне сразу же прилетает алерт — прямо в Telegram! Но если у вас нет полноценной NMS, это не проблема. Есть и минимальный, но очень надёжный вариант: обычный shell-скрипт, который мы запускаем через cron. Он тоже отлично справляется со своей задачей:
#!/bin/bash
for host in crm.example.ru portal.example.ru; do
end=$(echo | openssl s_client -servername $host -connect $host:443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
[ $days -lt 14 ] && echo "WARN: $host истекает через $days дней"
done
Этот скрипт запускается каждое утро, раз в сутки, и аккуратно записывает всё в лог-файл. А вот если там что-то появилось, то это верный знак: пора зайти и разобраться. Причём сделать это нужно обязательно до того, как клиент успеет вам позвонить!
FAQ — Let's Encrypt в корпоративной среде
- Подходит ли Let's Encrypt для корпоративного сайта и интернет-магазина?
- Да. DV-сертификат Let's Encrypt криптографически не отличается от платного — тот же RSA/ECDSA, та же длина ключа. Разница только в валидации организации (OV/EV), которая для обычного сайта роли не играет.
- Почему срок жизни сертификата — 90 дней?
- Короткий срок заставляет автоматизировать процесс, а также ограничивает ущерб при утечке приватного ключа. С Certbot и win-acme всё продлевается по таймеру, без участия человека.
- Как получить wildcard на *.company.ru?
- Только через DNS-01: Let's Encrypt проверяет TXT-запись _acme-challenge.company.ru. Для автоматизации ставится DNS-плагин (cloudflare, route53, регрулир и другие) — тогда certbot сам создаёт и удаляет записи при каждом продлении.
- Что делать, если у меня IIS за NAT без прямого 80-го порта?
- Либо пробросить 80/tcp до IIS только на время валидации, либо полностью перейти на DNS-01 — тогда входящий трафик вообще не нужен, достаточно API DNS-провайдера.
- Можно ли поставить один сертификат сразу на несколько поддоменов?
- Да, SAN-сертификат в Let's Encrypt поддерживает до 100 имён в одном сертификате. Для Certbot — просто перечислите -d name1 -d name2; для win-acme — выберите несколько сайтов на шаге bindings.
