Перейти к содержимому

Руководство по установке и настройке Cert Warden

Cert Warden хранит SSL-сертификаты в одном месте и раздаёт их серверам: сам получает и продлевает их по ACME (Let’s Encrypt), а клиенты забирают файлы по API-ключам и перезапускают свои сервисы. Это удобно, когда доменов и серверов много: за все сертификаты отвечает один сервер, certbot на каждом не нужен.

Где ставим

Развернём на сервере панели Remnawave. Подойдёт и любой другой сервер.

Проверка домена

Метод DNS-01: у Cloudflare свой провайдер, Gcore и Bunny.net подключаются через lego.

Раздача клиентам

Клиент Cert Warden на каждом сервере скачивает сертификаты и перезапускает сервисы.

Два проекта, на которых стоит это руководство, и человек, помогший собрать саму страницу.

Cert Warden

Сервер сертификатов из руководства: получает и продлевает их по ACME, проверяет домен через DNS-01 и раздаёт клиентам по API-ключам.

Форк клиента

Клиент со вкладки «Форк»: сам опрашивает сервер по расписанию, не открывает портов и обходится без AES-ключа.

Помощь по гайду

Руководство по Cert Warden построено в соавторстве: структура, шаги и формулировки этой страницы выверены вместе с maposia.

Рольсоавтор руководстваПомогсобрать структуру, шаги и текст
maposia
maposia@maposia
Let’s Encryptвыдаёт и продлевает по ACMEDNS-провайдерCloudflare · Gcore · BunnyсертификатDNS-01 · TXTCert Warden:4050 · хранит и раздаёт по API-ключамраздача по API-ключамклиент · панельpem-файлы на дискрестарт nginx / caddyклиент · de-fra-01pem-файлы на дискрестарт nginxклиент · nl-ams-02pem-файлы на дискрестарт nginx

Cert Warden заказывает сертификаты у Let’s Encrypt и доказывает владение доменом DNS-01-записью у DNS-провайдера. Готовые сертификаты он хранит у себя и раздаёт клиентам по API-ключам: клиент на каждом сервере сам раз в несколько часов опрашивает Cert Warden, при обновлении скачивает pem-файлы, кладёт их на диск и перезапускает свой веб-сервер. Панели за Caddy файловые сертификаты не нужны: Caddy получает их сам.

  1. Создаём директорию для Cert Warden и файл docker-compose.yml:

    root@server: ~
    mkdir /opt/certwarden && cd /opt/certwarden && nano docker-compose.yml

    Вставляем следующее содержимое:

    docker-compose.yml
    services:
    certwarden:
    container_name: certwarden
    image: ghcr.io/gregtwallace/certwarden:latest
    restart: unless-stopped
    ports:
    - '127.0.0.1:4050:4050'
    volumes:
    - ./certwarden-data:/app/data
    networks:
    - remnawave-network
    logging:
    driver: 'json-file'
    options:
    max-size: '30m'
    max-file: '5'
    networks:
    remnawave-network:
    name: remnawave-network
    driver: bridge
    external: true
  2. Запускаем Cert Warden:

    root@server: ~
    cd /opt/certwarden && docker compose up -d && docker compose logs -f
  3. Добавляем Cert Warden в веб-сервер панели, чтобы его интерфейс был доступен по своему домену:

    root@server: ~
    cd /opt/remnawave && nano nginx.conf

    Добавляем секцию upstream:

    nginx.conf
    upstream certwarden {
    server 127.0.0.1:4050;
    }

    Добавляем секцию server:

    nginx.conf
    server {
    listen 443 ssl;
    server_name certwarden.yourdomain.com; # Не забудьте добавить запись DNS в Cloudflare
    http2 on;
    ssl_certificate "/etc/nginx/ssl/yourdomain.com/fullchain.pem";
    ssl_certificate_key "/etc/nginx/ssl/yourdomain.com/privkey.pem";
    ssl_trusted_certificate "/etc/nginx/ssl/yourdomain.com/fullchain.pem";
    location / {
    proxy_pass http://certwarden;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    }

    Перезапускаем nginx:

    root@server: ~
    cd /opt/remnawave && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
  4. Открываем браузер и переходим по ссылке:

    root@server: ~
    https://certwarden.yourdomain.com
  5. Входим в систему, используя логин admin и пароль password:

    Cert Warden Login
  6. Попадаем на главную страницу:

    Cert Warden dashboard

  7. Переходим в раздел Settings и меняем текущий пароль.

Провайдер отвечает за DNS-записи при проверке домена. У Cloudflare в Cert Warden есть нативный провайдер, Gcore и Bunny.net подключаются через встроенный движок lego.

  1. Переходим в раздел Providers и нажимаем New Provider.

  2. Выбираем DNS-01 Cloudflare.

  3. В поле Domains вводим домен, для которого будем получать wildcard-сертификаты.

  4. В поле API Access Method выбираем API Token. Инструкцию по созданию токена с правами Zone:Edit можно найти в официальной документации Cloudflare.

    Cert Warden New Challenge Provider
  5. Нажимаем Submit.

  1. Переходим в раздел Private Keys и нажимаем New Key.

  2. В Name вводим название ключа (например, ACME).

  3. Key Generation Algorithm указываем ECDSA P-384 (рекомендуемый алгоритм для высокой безопасности и совместимости).

  4. Ставим галочку на Disable API Key.

  5. Нажимаем Submit.

  6. Откроется окно с Private Key, в нём тоже нажимаем Submit.

    Cert Warden New Private Key
  1. Переходим в раздел ACME Accounts и нажимаем New Account.

  2. В поле Name вводим название нашего домена или другое название.

  3. В поле Contact E-mail Address указываем email для уведомлений.

  4. В поле ACME Server выбираем Let’s Encrypt.

  5. В поле Private Key выбираем ранее созданный ключ.

  6. Ставим галочку на Accept ACME Server's Terms of Service.

  7. Нажимаем Submit.

  8. Откроется окно с данными аккаунта:

    Cert Warden New ACME Account
  9. Нажимаем кнопку REGISTER, статус аккаунта должен стать Valid.

  10. Нажимаем Submit.

  1. Переходим в раздел Certificates и нажимаем New Certificate.

  2. В поле Name вводим название нашего домена или другое название.

  3. В поле ACME Account выбираем ранее созданный аккаунт.

  4. В поле Subject (and Common Name) вводим домен 2 уровня, например example.com.

  5. Нажимаем кнопку ADD и в поле Alternate Name 1 вводим wildcard-домен *.example.com.

  6. Нажимаем Submit.

    Cert Warden New Certificate
  7. Внизу появится окно ACME Orders:

    Cert Warden ACME Orders

  8. Нажимаем кнопку PLACE NEW ORDER.

  9. Ждём, пока сертификат будет получен. Состояние видно в разделе ACME Queue: как только домен исчезнет из очереди, сертификат готов.

  10. Переходим в раздел Dashboard и видим, что сертификат получен, и сколько дней осталось до конца срока.

    Cert Warden Dashboard

Клиент Cert Warden работает как Docker-контейнер: забирает сертификаты с сервера по API-ключам, сохраняет файлы на диск и перезапускает указанные контейнеры, чтобы сервисы подхватили обновление.

Гайд покрывает обе версии клиента. Официальный клиент здесь основной: сертификат клиенту отправляет сам сервер, поэтому клиент держит открытый порт и расшифровывает данные AES-ключом. Альтернатива, форк mishkatik/certwarden-client, переворачивает схему: клиент сам опрашивает сервер, портов не открывает, AES-ключ не нужен.

Как ведёт себя форк:

Опрос по расписанию

При запуске и затем раз в заданный интервал клиент делает один запрос к Cert Warden. Если сертификат не менялся, сервер отвечает пустым 304, и ничего не скачивается.

ИнтервалCW_CLIENT_POLL_INTERVAL, по умолчанию 6 часов

Первый запуск

При первом запуске клиент не ждёт окна продления: сразу скачивает сертификат, записывает файлы и перезапускает контейнеры.

Файлыfullchain.pem и privkey.pem

Окно продления

Продлённый сертификат клиент записывает в заданное окно времени, в примере ниже это с 02:00 до 03:00, и после этого перезапускает контейнеры.

ОкноFILE_UPDATE_TIME_START / FILE_UPDATE_TIME_ENDДниFILE_UPDATE_DAYS_OF_WEEKЧасовой поясиз переменной TZ, без неё UTC

Повтор при сбое

Если сервер недоступен или API-ключ неверный, клиент не останавливается: пишет ошибку в лог и повторяет попытку.

Первая пауза15 минутРостпауза удваивается после каждой неудачиПределинтервал опроса
  1. Создаём директорию для клиента:

    root@server: ~
    mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.yml

    Вставляем следующее содержимое:

    docker-compose.yml
    services:
    certwardenclient:
    image: ghcr.io/gregtwallace/certwarden-client:latest
    container_name: certwardenclient
    restart: unless-stopped
    ports:
    - '127.0.0.1:5055:5055'
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    - /opt/certwardenclient/certs:/opt/certwarden/certs
    environment:
    TZ: "Europe/Moscow"
    CW_CLIENT_FILE_UPDATE_TIME_START: "02:00"
    CW_CLIENT_FILE_UPDATE_TIME_END: "03:00"
    CW_CLIENT_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"
    CW_CLIENT_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"
    CW_CLIENT_AES_KEY_BASE64: "pLZEc_QvSka8syfQtuTfArblalMKU6C6Ke1uIji0E5g"
    CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"
    CW_CLIENT_KEY_NAME: "example.com"
    CW_CLIENT_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"
    CW_CLIENT_CERT_NAME: "example.com"
    CW_CLIENT_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"
    CW_CLIENT_CERT_PATH: "/opt/certwarden/certs"
    CW_CLIENT_KEY_PEM_FILENAME: "privkey.pem"
    CW_CLIENT_CERTCHAIN_PEM_FILENAME: "fullchain.pem"
    networks:
    - remnawave-network
    logging:
    driver: 'json-file'
    options:
    max-size: '30m'
    max-file: '5'
    networks:
    remnawave-network:
    name: remnawave-network
    driver: bridge
    external: true

    В CW_CLIENT_RESTART_DOCKER_CONTAINER0 указан remnawave-nginx. Для панели за Caddy это remnawave-caddy.

  2. CW_CLIENT_AES_KEY_BASE64 это ключ из раздела Certificates → Post Processing → Client AES Key. Нажмите кнопку GENERATE, чтобы получить его.

    Cert Warden AES Key
  3. CW_CLIENT_SERVER_ADDRESS это адрес сервера Cert Warden, например https://certwarden.example.com.

  4. CW_CLIENT_KEY_NAME и CW_CLIENT_KEY_APIKEY это название и API-ключ приватного ключа из раздела Private Keys.

  5. CW_CLIENT_CERT_NAME и CW_CLIENT_CERT_APIKEY это название и API-ключ сертификата из раздела Certificates.

  6. Запускаем клиент:

    root@server: ~
    cd /opt/certwardenclient && docker compose up -d && docker compose logs -f
  7. В папке /opt/certwardenclient/certs должны появиться файлы fullchain.pem и privkey.pem.

  8. Монтируем их в docker-compose.yml панели:

    root@server: ~
    cd /opt/remnawave && nano docker-compose.yml

    Добавляем в volumes, не забудьте указать ваш домен:

    docker-compose.yml
    volumes:
    - /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem
    - /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem
  9. Перезапускаем nginx:

    root@server: ~
    cd /opt/remnawave && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
  1. Создаём директорию для клиента:

    root@server: ~
    mkdir -p /opt/certwardenclient/certs && cd /opt/certwardenclient && nano docker-compose.yml

    Вставляем следующее содержимое:

    docker-compose.yml
    services:
    certwardenclient:
    image: ghcr.io/gregtwallace/certwarden-client:latest
    container_name: certwardenclient
    restart: unless-stopped
    ports:
    - '127.0.0.1:5055:5055'
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    - /opt/certwardenclient/certs:/opt/certwarden/certs
    environment:
    TZ: "Europe/Moscow"
    CW_CLIENT_FILE_UPDATE_TIME_START: "02:00"
    CW_CLIENT_FILE_UPDATE_TIME_END: "03:00"
    CW_CLIENT_FILE_UPDATE_DAYS_OF_WEEK: "Mon Tue Wed Thu Fri Sat Sun"
    CW_CLIENT_RESTART_DOCKER_CONTAINER0: "remnawave-nginx"
    CW_CLIENT_AES_KEY_BASE64: "pLZEc_QvSka8syfQtuTfArblalMKU6C6Ke1uIji0E5g"
    CW_CLIENT_SERVER_ADDRESS: "https://certwarden.example.com"
    CW_CLIENT_KEY_NAME: "example.com"
    CW_CLIENT_KEY_APIKEY: "0UkmZEZK7oieQ6hmz9Fta2obMMZRjAuo"
    CW_CLIENT_CERT_NAME: "example.com"
    CW_CLIENT_CERT_APIKEY: "MkYMf7eXO8aB9xiNKqwvNVcDGKJ0Dg6v"
    CW_CLIENT_CERT_PATH: "/opt/certwarden/certs"
    CW_CLIENT_KEY_PEM_FILENAME: "privkey.pem"
    CW_CLIENT_CERTCHAIN_PEM_FILENAME: "fullchain.pem"
    logging:
    driver: 'json-file'
    options:
    max-size: '30m'
    max-file: '5'
  2. CW_CLIENT_AES_KEY_BASE64 это ключ из раздела Certificates → Post Processing → Client AES Key. Нажмите кнопку GENERATE, чтобы получить его.

  3. CW_CLIENT_SERVER_ADDRESS это адрес сервера Cert Warden.

  4. CW_CLIENT_KEY_NAME и CW_CLIENT_KEY_APIKEY это название и API-ключ из раздела Private Keys.

  5. CW_CLIENT_CERT_NAME и CW_CLIENT_CERT_APIKEY это название и API-ключ из раздела Certificates.

  6. Запускаем клиент:

    root@server: ~
    cd /opt/certwardenclient && docker compose up -d && docker compose logs -f
  7. В папке /opt/certwardenclient/certs должны появиться файлы fullchain.pem и privkey.pem.

  8. Монтируем их в docker-compose.yml ноды (её веб-сервер тоже называется remnawave-nginx):

    root@server: ~
    cd /opt/remnanode && nano docker-compose.yml
    docker-compose.yml
    volumes:
    - /opt/certwardenclient/certs/fullchain.pem:/etc/nginx/ssl/yourdomain.com/fullchain.pem
    - /opt/certwardenclient/certs/privkey.pem:/etc/nginx/ssl/yourdomain.com/privkey.pem
  9. Перезапускаем nginx ноды:

    root@server: ~
    cd /opt/remnanode && docker compose down remnawave-nginx && docker compose up -d remnawave-nginx
  1. Добавляем сервер Cert Warden в NetBird. Как его установить, написано здесь.

  2. В панели управления DNS для домена certwarden.yourdomain.com указываем IP-адрес сервера Cert Warden в сети NetBird.

    Cert Warden Cloudflare

  3. Теперь, чтобы попасть на страницу Cert Warden, нужно подключиться к вашей сети NetBird через приложение.