Как установить и настроить step-ca на VPS
Как только внутренних сервисов становится больше двух-трёх — админка, registry, мониторинг, внутреннее API — встаёт вопрос TLS между ними. Let's Encrypt не выдаст сертификат на admin.internal или на IP без публичного DNS, самоподписанные сертификаты каждый раз ругаются в браузере и curl, а руками их перевыпускать раз в 90 дней никто не будет. Решение — поднять собственный центр сертификации на VPS с помощью step-ca: он говорит по ACME, как Let's Encrypt, но выдаёт сертификаты на что угодно внутри вашей сети и умеет автоматически их продлевать.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое step-ca и зачем он нужен на VPS
step-ca — открытый сервер сертификации от Smallstep, тот же протокол ACME, что использует Let's Encrypt, только без ограничений публичного CA. Он подходит для трёх задач:
- TLS для внутренних доменов и IP. Сертификаты на
*.internal,10.0.0.5,db.local— то, на что публичный CA принципиально не выдаст сертификат. - mTLS между сервисами. Клиентские сертификаты для взаимной аутентификации: сервис A доверяет только тем, у кого сертификат от вашего CA.
- Замена самоподписанных сертификатов в Docker registry, панелях мониторинга, внутренних API — без ручного распространения
.crtпо каждому клиенту: достаточно один раз довериться корневому сертификату CA.
Из плюсов: сертификаты живут по умолчанию 24 часа (можно настроить дольше), что резко снижает ущерб от утечки ключа — украденный сертификат просто быстро сгорает. Из минусов и честных ограничений: короткий срок жизни требует рабочей автоматической ротации (руками продлевать нереально), а сам CA становится критичной точкой — если сервер с root-ключом недоступен или скомпрометирован, доверие рушится для всей инфраструктуры. Для внешних сайтов, которые видят обычные посетители, step-ca не замена Let's Encrypt — там нужен сертификат от публично доверенного CA. step-ca закрывает именно внутренний контур.
Установка step-ca на сервер
Для чистого внутреннего CA удобно выделить отдельный небольшой VPS — 1 vCPU и 1-2 ГБ RAM хватает с запасом, нагрузка на CA минимальна. Ставим на Ubuntu 24.04 / Debian 12 через официальный репозиторий Smallstep:
sudo apt update && sudo apt install -y curl gnupg
curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg -o /etc/apt/trusted.gpg.d/smallstep.asc
echo "deb [signed-by=/etc/apt/trusted.gpg.d/smallstep.asc] https://packages.smallstep.com/stable/debian debs main" | \
sudo tee /etc/apt/sources.list.d/smallstep.list
sudo apt update
sudo apt install -y step-cli step-ca
Проверяем, что бинарники встали:
step version
step-ca version
step-cli — клиент, которым выпускают и продлевают сертификаты; step-ca — сам сервер. Оба понадобятся: сервер ставим один раз на CA-машину, клиент — на каждой машине, которая будет запрашивать сертификаты.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверИнициализация CA и структура сертификатов
Инициализация создаёт root- и intermediate-сертификаты, ключи и базовый конфиг. Запускаем от имени выделенного системного пользователя, а не root:
sudo useradd -r -m -d /etc/step-ca -s /usr/sbin/nologin step
sudo -u step step ca init \
--name "MAATRIX Internal CA" \
--dns "ca.internal.example.com" \
--address ":8443" \
--provisioner "admin@example.com" \
--password-file /etc/step-ca/.ca-password
Мастер спросит алгоритм ключей (по умолчанию EC P-256 — быстрее и компактнее RSA, для внутреннего CA хватает с запасом) и тип развёртывания. На выходе получаем структуру в /etc/step-ca (или в $HOME/.step, если инициализировали не под системным пользователем):
/etc/step-ca/
├── certs/
│ ├── root_ca.crt # корневой сертификат — раздаём клиентам
│ └── intermediate_ca.crt
├── secrets/
│ ├── root_ca_key # держим офлайн или под замком
│ └── intermediate_ca_key
├── config/
│ ├── ca.json
│ └── defaults.json
└── db/ # BadgerDB с журналом выданных сертификатов
Ключевой момент: root_ca_key подписывает только intermediate-сертификат один раз при инициализации, дальше сервер работает через intermediate-ключ. Это позволяет держать root-ключ офлайн (или хотя бы вынести на отдельный зашифрованный диск) — реальный ежедневный выпуск идёт через intermediate, и его компрометация не требует пересоздания всей цепочки доверия у клиентов.
Поднимаем сервер как systemd-сервис:
sudo tee /etc/systemd/system/step-ca.service <<'EOF'
[Unit]
Description=step-ca
After=network.target
[Service]
Type=simple
User=step
Environment=STEPPATH=/etc/step-ca
ExecStart=/usr/bin/step-ca /etc/step-ca/config/ca.json --password-file /etc/step-ca/.ca-password
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now step-ca
sudo systemctl status step-ca
Сервис слушает :8443 по HTTPS с самоподписанным сертификатом (это нормально — сам CA не нуждается в стороннем сертификате). Откройте порт только для внутренней сети — на публичный интернет CA смотреть не должен:
sudo ufw allow from 10.0.0.0/24 to any port 8443 proto tcp
ACME-провижнер: автоматический выпуск сертификатов внутри сети
По умолчанию step-ca выдаёт сертификаты через собственный протокол (JWK-провижнер с токенами), но для совместимости с привычными ACME-клиентами — certbot, acme.sh, lego, встроенный ACME в Caddy и Traefik — добавляем ACME-провижнер:
step ca provisioner add acme --type ACME
sudo systemctl restart step-ca
Теперь любой ACME-клиент внутри сети может запросить сертификат у вашего CA — так же, как у Let's Encrypt, только указав свой endpoint. Для клиента понадобится директория ACME:
https://ca.internal.example.com:8443/acme/acme/directory
Пример через certbot (нужен доверенный корневой сертификат в системе клиента, см. ниже):
certbot certonly --standalone \
--server https://ca.internal.example.com:8443/acme/acme/directory \
-d service1.internal.example.com
Или через встроенный ACME в Caddy — достаточно одной строки в Caddyfile: acme_ca https://ca.internal.example.com:8443/acme/acme/directory. Это удобно, если у вас уже настроен nginx как reverse proxy для внешних доменов, а для внутренних сервисов хочется того же автоматического продления, но от своего CA.
Чтобы клиенты доверяли вашему CA (иначе TLS-рукопожатие будет падать с ошибкой неизвестного издателя), корневой сертификат нужно один раз положить в системное хранилище доверия каждой машины:
sudo cp root_ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates
Либо, для отдельного клиента без ручного копирования, step-cli умеет забрать и установить доверие одной командой через отпечаток (fingerprint) корневого сертификата:
step ca bootstrap --ca-url https://ca.internal.example.com:8443 \
--fingerprint <отпечаток из вывода step ca init> --install
Автоматическая ротация: step-cli и systemd timer
Сертификаты step-ca по умолчанию живут 24 часа — это осознанное решение, а не ограничение, которое надо обходить. Короткий срок жизни означает, что даже украденный сертификат бесполезен уже через несколько часов. Но это требует рабочей автоматизации продления, без неё сервисы начнут падать по TLS каждый день.
Простой сертификат вручную:
step ca certificate service1.internal.example.com \
service1.crt service1.key \
--ca-url https://ca.internal.example.com:8443
Для автопродления у step-cli есть встроенный демон-режим — он сам следит за сроком годности и обновляет сертификат заранее, без внешнего cron:
step ca renew --daemon \
service1.crt service1.key \
--exec "systemctl reload nginx"
Флаг --exec выполняет команду сразу после успешного продления — типично это reload веб-сервера или контейнера, чтобы подхватить новый сертификат без обрыва соединений. Оборачиваем в systemd-сервис, чтобы демон переживал перезагрузки:
sudo tee /etc/systemd/system/step-renew.service <<'EOF'
[Unit]
Description=step-ca certificate renewal daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/step ca renew --daemon \
/etc/ssl/internal/service1.crt /etc/ssl/internal/service1.key \
--exec "systemctl reload nginx"
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now step-renew
Для сервисов, где демон неудобен (например, короткоживущие контейнеры), тот же результат даёт cron с проверкой каждые несколько часов:
0 */6 * * * step ca renew --force /etc/ssl/internal/service1.crt /etc/ssl/internal/service1.key >> /var/log/step-renew.log 2>&1
Практика: step-ca с nginx и приватным Docker Registry
Два самых частых сценария использования step-ca на своём VPS — терминация TLS во внутреннем nginx и защита приватного Docker Registry, который иначе гоняет образы по HTTP или требует ручной подсовки самоподписанного сертификата на каждый Docker-хост.
Для nginx получаем сертификат и подключаем его как обычный:
server {
listen 443 ssl;
server_name registry.internal.example.com;
ssl_certificate /etc/ssl/internal/registry.crt;
ssl_certificate_key /etc/ssl/internal/registry.key;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
}
}
На всех Docker-хостах, которые будут делать docker push/pull в этот registry, достаточно один раз довериться корневому сертификату CA (шаг с update-ca-certificates выше) — дальше Docker принимает сертификат registry как валидный, без записи в insecure-registries и без копирования .crt в /etc/docker/certs.d/ вручную для каждого демона.
Для сервисов внутри Docker Compose удобнее выпускать сертификат на хосте и монтировать его в контейнер read-only томом, а продление вешать на --exec "docker compose restart nginx" вместо systemctl reload.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем step-ca отличается от Let's Encrypt?
Протокол тот же (ACME), но step-ca — ваш собственный CA: выдаёт сертификаты на внутренние домены и IP, которые публичный CA не подтвердит, и не требует прохождения challenge через публичный интернет.
Нужно ли клиентам step-ca доверять сертификату, или это как самоподписанный?
Разница принципиальная: самоподписанный сертификат нужно добавлять в доверенные на каждой машине отдельно и для каждого сервиса заново. С CA достаточно один раз добавить в доверенные корневой сертификат — и все сертификаты, выданные этим CA, автоматически становятся доверенными.
Что если сервер с step-ca упадёт на день?
Уже выданные сертификаты продолжат работать до истечения срока (по умолчанию до 24 часов), но новые выдать и продлить существующие будет нельзя — поэтому CA стоит держать на стабильном сервере с мониторингом и резервной копией /etc/step-ca/secrets.
Можно ли увеличить срок жизни сертификатов, если 24 часа неудобно?
Да, параметром --default-cert-duration в ca.json или флагом --not-after при выпуске — но чем дольше живёт сертификат, тем важнее не потерять автоматическую ротацию и мониторинг компрометации.
Как защитить root-ключ CA?
Держите root_ca_key офлайн после инициализации (на отдельном зашифрованном носителе), а повседневную работу CA ведите через intermediate-ключ — компрометация intermediate не требует пересоздания доверия у всех клиентов.
Нужен ли отдельный сервер под step-ca или можно на общем VPS?
Технически можно и рядом с другими сервисами, но с точки зрения безопасности лучше выделить отдельную небольшую машину: компрометация CA — это компрометация TLS для всей внутренней инфраструктуры, изоляция снижает площадь атаки.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →