Заводим Caddy на DigitalOcean с Let's Encrypt
Dec 12, 2020 13:02 · 912 words · 5 minute read
За свои 15 лет в IT я перепробовал разные веб-серверы. Сначала это был Apache (куда же без него), потом перешёл на более лёгкий nginx (а как меня однажды OpenResty спас!). Поразвлекался с lighttpd когда держал дома FreeNAS, даже заводил pet-project под YAWS. И вот в одном из репозиториев увидел новый для себя Caddy. Поспрашивал в местных чатиках кто его использует, и меня убедили, что он уже вполне production-ready. Более того - настраивать ничего не надо, сертификаты сам обновляет, быстрый, всего один бинарник! Прям чудо, а не веб-сервер, так что решил взять его для очередного хобби проекта.
Настройка среды разработки для Go
Проект размещается на DO в одном контейнере. Приложение пока выглядит как docker-compose.yaml с двумя образами, так что Caddy нужен исключительно как reverse-proxy и управления сертификатами.
Нужен туториал. Первой же ссылкой в Google вышла статья на DigitalOcean, где вроде бы делалось всё, что мне надо. Изначально меня смутило, что надо ставить окружение для Go. Хотелось бы какого-нибудь apt install caddy2
, но дальше я убедился, что настроить среду разарботки всё-таки необходимо. Проблема в том, что плагин для работы с DO можно подключить только через пересборку Caddy. Так что скачиваем, распаковываем, копируем сам Go:
$ curl -O https://dl.google.com/go/go1.15.6.linux-amd64.tar.gz
$ sudo tar -xvf go1.15.6.linux-amd64.tar.gz -C /usr/local
$ sudo chown -R root:root /usr/local/go
Создаём родительский каталог для всего, что связано с go:
$ mkdir -p $HOME/go/{bin,src}
Добавляем переменные окружения в .profile
(если будете добавлять руками, не забудьте снять экранирование):
$ echo "export GOPATH=$HOME/go" >> ~/.profile
$ echo "export PATH=\"\$PATH:\$GOPATH/bin:/usr/local/go/bin\"" >> ~/.profile
$ . ~/.profile
Установка Caddy
Всё, Go поставили! Можно собирать Caddy. В статье было предложено подключать плагины через импорт, но! В этом случае сталкнётесь с ошибкой, что некоторые плагины не совместимы: panic: qtls.ClientSessionState not compatible with tls.ClientSessionState
. Разработчики Caddy рекомендуют использовать xcaddy для пересборки. На деле вышло действительно удобнее. Всего одна команда ./xcaddy build master --with github.com/caddy-dns/digitalocean
соберёт в текущем каталоге бинарник caddy
, который надо лишь правильно положить в систему. Обратите внимание, что для плагина DigitalOcean я использовал не github.com/caddyserver/dnsproviders/digitalocean
, который указан в статье. Всё потому, что его там уже нет. Итак, устанавливаем xcaddy:
$ curl -O https://github.com/caddyserver/xcaddy/releases/download/v0.1.6/xcaddy_0.1.6_linux_amd64.tar.gz
$ sudo tar -xvf xcaddy_0.1.6_linux_amd64.tar.gz -C /usr/local/bin
Копируем бинарник:
$ sudo mv $GOPATH/bin/caddy /usr/local/bin/
$ sudo chown root:root /usr/local/bin/caddy
$ sudo chmod 755 /usr/local/bin/caddy
Создаём каталог с настройками:
$ sudo mkdir /etc/caddy
$ sudo chown -R root:www-data /etc/caddy
$ sudo mkdir /etc/ssl/caddy
$ sudo chown -R root:www-data /etc/ssl/caddy
$ sudo chmod 0770 /etc/ssl/caddy
Подбрасываем конфиг в /etc/caddy/Caddyfile
:
numi.site {
reverse_proxy /* 127.0.0.1:8080
}
Добавляем сервис для systemd:
$ sudo sh -c 'curl https://raw.githubusercontent.com/caddyserver/dist/master/init/caddy.service > /etc/systemd/system/caddy.service'
$ sudo chmod 644 /etc/systemd/system/caddy.service
$ sudo systemctl daemon-reload
$ sudo systemctl status caddy
На этом шаге caddy должен успешно завестись, судя по статусу. Если что-то пошло не так - пишите, разберёмся. Логи можно посмотреть также через journalctl -u caddy.service
.
Подключаем сертификаты Let’s Encrypt
Сначала немного необходимой теории. letsencrypt.org позволяет в автоматическом режиме обновлять сертификаты для вашего сайта. Для этого даже был разработан и утверждён протокол ACME, и существует много программ, которые его поддерживают (в том числе и Caddy). Самый известный, наверно, Certbot, который наиболее универсален и не привязан к языку программирования или веб-серверу. Для выпуска сертификата необходимо подтвердить владение доменом. Есть минимум 2 способа:
- создать по специальному URL страницу с кодом, который скажет LE
- создать DNS запись с определённым значением
Второй - наиболее универсален, т.к. позволяет получать сертификаты для поддоменов, но и более сложный, т.к. необходимо программно создавать DNS запись. К счастью, у всех крупных DNS-провайдеров есть API. К слову, для Caddy это единственный вариант, т.к. он не начнёт обрабатывать 80/443 порты, пока не будет настроен сертификат. И нет, свой подбросить вы не можете.
Далее начинается самое веселье. У Caddy есть плагин для работы с API DigitalOcean. Получить токен можно по короткой инструкции. Я предпочитаю его хранить не в конфигурационном файле, а в переменных окружения процесса. Для этого в /etc/systemd/system/caddy.service
добавим строку в раздел Service
Environment=CADDYPATH=/etc/ssl/caddy DO_AUTH_TOKEN=<token>
Теперь в самом конфиге можно добавить раздел для установки tls сертификата `/etc/caddy/Caddyfile:
numi.site {
reverse_proxy /* 127.0.0.1:8080
tls {
issuer acme {
dir https://acme-v02.api.letsencrypt.org/directory
resolvers 173.245.58.51:53
dns digitalocean {env.DO_AUTH_TOKEN}
}
}
}
Внимание! Обязательно укажите resolvers, где хостятся ваши DNS-записи. В противном случае Let’s Encrypt будет опрашивать какой-нибудь корневой, а до него изменения не успеют дойти за таймаут запроса.
Запустите Caddy и посмотрите какую TXT запись он создаёт. Если она выглядит как _acme-challenge.example.org.example.org
- всё в порядке, баг в плагине DigitalOcean ещё не исправлен. К счастью, добрый человек уже предложил рабочее решение, осталось им воспользоваться. Склонируем этот репозиторий и соберём Caddy с форком где есть исправление:
$ cd ~
$ git clone https://github.com/delthas/digitalocean.git
$ cd digitalocean
$ git checkout fix-record-name
$ xcaddy build --with github.com/caddy-dns/digitalocean --with github.com/libdns/digitalocean=~/digitalocean
После этого заменяем бинарник в /usr/local/bin/
и перезапускаем сервис (sudo systemctl daemon-reload && systemctl restart caddy
). После этих действий должна создасться правильная TXT запись, сертификат должен подключиться, и сайт заработать на 80 и 443 портах. Но у меня оставались артефакты от прошлых проверок и экспериментов в каталоге /var/lib/caddy/.local/
, пришлось их почистить. Вышел я на них через подробные debug логи. Кстати, чтобы включить в Caddy такой режим, надо в конфиге в глобальной секции дописать:
{
debug
}
numi.site {
....
}
Субъективщина
Несмотря на то, что разработчиками была заявлена “лёгкость в установке и настройке”, я потратил на это полдня. Попутно изучив кучу ишью, натыкаясь на отсутствующие репозитории, разбираясь с go и т.п. Однако, инструмент мне понравился своей динамической настройкой (можно пушить конфиги как json файлы через POST запрос) и минимализмом конфигурационного файла. Мне кажется, он отлично зайдёт в микросервисном проекте, а также где везде, где есть service discovery.