Note
Этот репозиторий является частью проекта "Феникс" и содержит конфигурацию для автоматической сборки Docker-образа веб-сервера Caddy с дополнительными модулями.
- Образ автоматически пересобирается каждое 1-е число месяца, подтягивая свежую версию Caddy и обновленные зависимости.
- Публикация происходит в GitHub Container Registry —
ghcr.io/octohare/caddy:latest
Образ ghcr.io/octohare/caddy:latest собран на базе официального caddy:latest с добавлением модулей:
Оба модуля входят в официальный реестр модулей на сайте продукта:
CaddyServer.com/Docs/Modules
- Модуль http.handlers.replace_response
- Модуль http.handlers.postauth2fa
- Модуль
Replace-Responseдобавляет вCaddyfileподдержку директивыreplace, которая позволяет подменять текст (HTML/JS/CSS/JSON) в теле ответов бэкенда на лету. Это необходимо для корректного проксирования сервисов по subpath (например,domain.com/subpath/), когда проксируемый сервис завязан на абсолютные пути (/static/,/api/) и не умеет работать с базовыми префиксами из коробки. - Модуль
PostAuth-2FAдобавляет вCaddyfileподдержку директивыpostauth_2fa, которая добавляет возможность включить двухфакторную аутентификацию для любого проксируемого приложения.
Important
Требования к окружению
Данная инструкция предполагает развертывание сервиса с помощью Stack в графической панели Portainer.
Для выполнения описанных шагов на сервере должны быть заранее установлены Docker и Portainer.
- Создаём каталоги для настроек и сертификатов:
# Создаём каталоги # Для конфигурационного файла Caddy mkdir -p /etc/caddy # Для сертификатов mkdir -p /etc/caddy/data
- Модуль
PostAuth-2FAтребует секретный ключ не менее 32 байт для подписи JWT-токенов, генерируем Base64 ключ для подписи сессий:
Вывод будет выглядеть примерно так:openssl rand -base64 32
BgaZRtx8BcT0bcmPMJbAgNQH7363CcC7ydkw2xwJkq4=
- Для 2FA необходим секрет, генерируем Base32-секрет для пользователя:
Вывод будет выглядеть примерно так:openssl rand 30 | base32 | tr --delete '='
ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7
-
Необходимо передать секрет в ваше 2FA приложение, например Google Authenticator или любое другое. Есть два пути:
- Ручной ввод. Скопируйте сам секрет и любым способом введите или вставьте его в 2FA приложение.
- QR код. Для удобства можем превратить секрет в QR код прямо в консоле:
Ставим приложение
Запускаем команду с названием вашего приложения (sudo apt install qrencode
MySecretAppName) и секретомsecret=
📱 Сканируем получившийся QR код через 2FA приложениеqrencode -t UTF8 "otpauth://totp/MySecretAppName?secret=ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7"
-
Создаём файл с секретом для модуля
PostAuth-2FAи скачиваем красивую форму ввода ключа:cat > /etc/caddy/totp_secrets.json << 'EOF' { "admin": { "totp_secret": "ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7" } } EOF curl -fsSL https://raw.githubusercontent.com/steffenbusch/caddy-postauth-2fa/main/alternative-2fa-form.html -o /etc/caddy/alternative-2fa-form.html
-
Создаём
Caddyfileфайл с конфигурацие для Caddy:cat > /etc/caddy/Caddyfile << 'EOF' { email your-email@example.com } example.com { handle_path /subpath/* { reverse_proxy 127.0.0.1:3030 } handle_path /mysecretapp/* { reverse_proxy 127.0.0.1:4040 } } EOF
-
Далее для установки веб-сервера Caddy переходим в Portainer:
- Раздел "Stacks"
- Кнопка "+ Add stack" вверху справа
- Задаём имя - Name:
caddy - В поле Web editor вставляем:
services: caddy: image: ghcr.io/octohare/caddy:latest container_name: caddy restart: unless-stopped network_mode: host volumes: - /etc/caddy:/etc/caddy - /etc/caddy/data:/data - caddy_config:/config healthcheck: test: ["CMD-SHELL", "curl -fI http://127.0.0.1:2019/metrics"] interval: 30s timeout: 5s retries: 3 start_period: 10s logging: driver: "json-file" options: max-size: "10m" max-file: "2" volumes: caddy_config:
- Кнопка "Deploy the stack" внизу слева
-
Генерируем хеш пароля для Basic Auth:
docker exec -it caddy caddy hash-password --plaintext 'y0uR_SeCrEt_P@s$w0rD'
Вывод будет выглядеть примерно так:
$2a$14$l8E4ux1gwzi4cxCnohyjNOFOGKzXhBz30kPbyDV02/LvXbIirGX2i -
Редактируем файл настроек
Caddyfile:nano /etc/caddy/Caddyfile
Пример файла конфигурации сервера Caddy в котором продемонстрированно использование новых доступных директив
replaceиpostauth_2fa. В данном примере они применены условно к разным сайтам/сервисам, но их также можно применить и вместе к одному домену/порту/адресу.{ email your-email@example.com } example.com { handle_path /subpath/* { reverse_proxy 127.0.0.1:3030 { header_up X-Forwarded-Prefix /subpath } replace { "href=\"/" "href=\"/subpath/" "src=\"/" "src=\"/subpath/" "fetch(\"/" "fetch(\"/subpath/" "\"/api/" "\"/subpath/api/" "EventSource(\"/" "EventSource(\"/subpath/" } } handle_path /mysecretapp/* { basic_auth { admin $2a$14$l8E4ux1gwzi4cxCnohyjNOFOGKzXhBz30kPbyDV02/LvXbIirGX2i } postauth_2fa { session_inactivity_timeout 12h secrets_file_path /etc/caddy/totp_secrets.json sign_key "BgaZRtx8BcT0bcmPMJbAgNQH7363CcC7ydkw2xwJkq4=" form_response_header X-2FA-Required true form_template /etc/caddy/alternative-2fa-form.html } reverse_proxy 127.0.0.1:4040 } } -
Применяем новые настройки:
docker exec -it caddy caddy reload --config /etc/caddy/Caddyfile
Note
This repository is part of the "Phoenix" project and contains the configuration for automated building of a Docker image of the Caddy web server with additional modules.
- The image is automatically rebuilt on the 1st of every month, pulling the latest Caddy version and updated dependencies.
- Published to GitHub Container Registry —
ghcr.io/octohare/caddy:latest
The ghcr.io/octohare/caddy:latest image is built on top of the official caddy:latest with the following modules added:
Both modules are listed in the official module registry on the product website:
CaddyServer.com/Docs/Modules
- Module http.handlers.replace_response
- Module http.handlers.postauth2fa
- The
Replace-Responsemodule adds support for thereplacedirective in theCaddyfile, which allows on-the-fly text replacement (HTML/JS/CSS/JSON) in backend response bodies. This is necessary for correct proxying of services under a subpath (e.g.,domain.com/subpath/), when the proxied service relies on absolute paths (/static/,/api/) and does not support base prefixes out of the box. - The
PostAuth-2FAmodule adds support for thepostauth_2fadirective in theCaddyfile, which enables two-factor authentication for any proxied application.
Important
Environment requirements
This guide assumes deployment of the service using a Stack in the Portainer graphical interface.
The server must have Docker and Portainer pre-installed to follow these steps.
- Create directories for configuration and certificates:
# Create directories # For Caddy configuration file mkdir -p /etc/caddy # For certificates mkdir -p /etc/caddy/data
- The
PostAuth-2FAmodule requires a secret key of at least 32 bytes to sign JWT tokens. Generate a Base64 key for session signing:
The output will look something like:openssl rand -base64 32
BgaZRtx8BcT0bcmPMJbAgNQH7363CcC7ydkw2xwJkq4=
- For 2FA you need a secret. Generate a Base32 secret for the user:
The output will look something like:openssl rand 30 | base32 | tr --delete '='
ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7
-
You need to transfer the secret to your 2FA app, such as Google Authenticator or any other. There are two ways:
- Manual entry. Copy the secret itself and type or paste it into the 2FA app.
- QR code. For convenience, we can turn the secret into a QR code directly in the terminal:
Install the utility
Run the command with your application name (sudo apt install qrencode
MySecretAppName) and thesecret=parameter
📱 Scan the resulting QR code with your 2FA appqrencode -t UTF8 "otpauth://totp/MySecretAppName?secret=ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7"
-
Create the secrets file for the
PostAuth-2FAmodule and download a nice key entry form:cat > /etc/caddy/totp_secrets.json << 'EOF' { "admin": { "totp_secret": "ZLUP2T66JIHV3OI3NSUOYJIPLTKM43FSPM2I6N3RTI72ILL7" } } EOF curl -fsSL https://raw.githubusercontent.com/steffenbusch/caddy-postauth-2fa/main/alternative-2fa-form.html -o /etc/caddy/alternative-2fa-form.html
-
Create the
Caddyfileconfiguration file for Caddy:cat > /etc/caddy/Caddyfile << 'EOF' { email your-email@example.com } example.com { handle_path /subpath/* { reverse_proxy 127.0.0.1:3030 } handle_path /mysecretapp/* { reverse_proxy 127.0.0.1:4040 } } EOF
-
Next, to install the Caddy web server, go to Portainer:
- "Stacks" section
- "+ Add stack" button in the top right
- Set name — Name:
caddy - In the Web editor field paste:
services: caddy: image: ghcr.io/octohare/caddy:latest container_name: caddy restart: unless-stopped network_mode: host volumes: - /etc/caddy:/etc/caddy - /etc/caddy/data:/data - caddy_config:/config healthcheck: test: ["CMD-SHELL", "curl -fI http://127.0.0.1:2019/metrics"] interval: 30s timeout: 5s retries: 3 start_period: 10s logging: driver: "json-file" options: max-size: "10m" max-file: "2" volumes: caddy_config:
- "Deploy the stack" button at the bottom left
-
Generate a password hash for Basic Auth:
docker exec -it caddy caddy hash-password --plaintext 'y0uR_SeCrEt_P@s$w0rD'
The output will look something like:
$2a$14$l8E4ux1gwzi4cxCnohyjNOFOGKzXhBz30kPbyDV02/LvXbIirGX2i -
Edit the
Caddyfileconfiguration file:nano /etc/caddy/Caddyfile
Example of a Caddy server configuration file demonstrating the use of the new
replaceandpostauth_2fadirectives. In this example they are applied conditionally to different sites/services, but they can also be used together on a single domain/port/address.{ email your-email@example.com } example.com { handle_path /subpath/* { reverse_proxy 127.0.0.1:3030 { header_up X-Forwarded-Prefix /subpath } replace { "href=\"/" "href=\"/subpath/" "src=\"/" "src=\"/subpath/" "fetch(\"/" "fetch(\"/subpath/" "\"/api/" "\"/subpath/api/" "EventSource(\"/" "EventSource(\"/subpath/" } } handle_path /mysecretapp/* { basic_auth { admin $2a$14$l8E4ux1gwzi4cxCnohyjNOFOGKzXhBz30kPbyDV02/LvXbIirGX2i } postauth_2fa { session_inactivity_timeout 12h secrets_file_path /etc/caddy/totp_secrets.json sign_key "BgaZRtx8BcT0bcmPMJbAgNQH7363CcC7ydkw2xwJkq4=" form_response_header X-2FA-Required true form_template /etc/caddy/alternative-2fa-form.html } reverse_proxy 127.0.0.1:4040 } } -
Apply the new settings:
docker exec -it caddy caddy reload --config /etc/caddy/Caddyfile