Инструменты пользователя

Инструменты сайта


selfhosted:network

DNS, TLS и рецепт деплоя

Как *.melnoff.com раскрывается в интернет и как добавить новый сервис.

DNS

  • Зона: melnoff.com на Cloudflare (NS: erin.ns.cloudflare.com, west.ns.cloudflare.com).
  • Режим: все публичные сабдомены — DNS-only (grey cloud). Cloudflare proxy выключен.
  • Все записи — A на 95.165.74.182 (публичный IP домашнего интернет-канала).
  • Что важно: включение Cloudflare proxy (orange cloud) сломает текущую схему ACME — у нас webroot http-01, требующий публично доступного :80. Если хотите включать proxy — сначала переключить acme.sh на DNS-01 challenge через Cloudflare API.

TLS

  • CA: Let's Encrypt. ZeroSSL (default в свежих acme.sh) ушёл за EAB/email — обходим явным –server letsencrypt.
  • Утилита: acme.sh, бинарь /usr/lib/acme/client/acme.sh, ACME home /etc/acme/.
  • Тип ключа: ECDSA-256 (–keylength ec-256).
  • Challenge: http-01 webroot, корень /var/run/acme/challenge, обработчик — /etc/nginx/conf.d/acme80.conf (один общий вhost listen 80, в server_name перечислены все домены).
  • Auto-renewal: через UCI-секцию acme (/etc/config/acme), демон /etc/init.d/acme.

Pattern: один сервис — один файл ''<name>.conf''

Все vhost'ы построены по одному шаблону. Пример (bw.conf):

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    listen 8080;
    server_name bw.melnoff.com;
 
    ssl_certificate     /etc/acme/bw.melnoff.com_ecc/fullchain.cer;
    ssl_certificate_key /etc/acme/bw.melnoff.com_ecc/bw.melnoff.com.key;
    ssl_session_cache   shared:SSL:32k;
    ssl_session_timeout 64m;
 
    location / {
        proxy_pass         http://172.16.10.1:83;
        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 https;
        proxy_read_timeout 300s;
    }
}
  • Backend всегда http://172.16.10.1:<port> — это IP LXC 107 в VLAN 2. Не 192.168.0.6!
  • listen 8080 — параллельный non-TLS листен для внутреннего использования.

Deploy recipe — добавить новый ''<name>.melnoff.com''

0. Выбрать порт

Свободные на LXC 107 (на 2026-04-27): 8084+, 84-99. Занятые: 83 (vw), 3000 (gitea), 3001 (grafana), 8000+9443 (portainer), 8082 (zabbix-web), 8083 (dokuwiki), 10051 (zabbix-server), 2244 (gitea-ssh).

1. Cloudflare DNS

Добавить A-запись <name>.melnoff.com95.165.74.182, Proxy: DNS-only. Дождаться пропагации:

dig @1.1.1.1 +short <name>.melnoff.com A

2. Контейнер на LXC 107

ssh root@192.168.0.6
pct exec 107 -- /bin/sh
 
mkdir -p /opt/<name>
cat > /opt/<name>/docker-compose.yml <<YAML
services:
  <name>:
    image: <image>
    restart: unless-stopped
    ports:
      - "<port>:80"
    volumes:
      - /opt/<name>/data:/...
    environment:
      TZ: Europe/Moscow
YAML
 
cd /opt/<name> && docker compose up -d

3. ACME challenge handler

ssh root@192.168.0.1
 
# бэкап
cp /etc/nginx/conf.d/acme80.conf /etc/nginx/conf.d/acme80.conf.bak.$(date +%s)
 
# добавить домен в server_name
sed -i 's/\(server_name [^;]*\)/\1 <name>.melnoff.com/' /etc/nginx/conf.d/acme80.conf
 
# reload
/etc/init.d/nginx reload

4. Self-test ACME path (полезно перед issue)

mkdir -p /var/run/acme/challenge/.well-known/acme-challenge
echo HELLO > /var/run/acme/challenge/.well-known/acme-challenge/test
curl -H "Host: <name>.melnoff.com" http://127.0.0.1/.well-known/acme-challenge/test
# должен вернуть HTTP 200 и 'HELLO'
rm /var/run/acme/challenge/.well-known/acme-challenge/test

5. Issue cert

/usr/lib/acme/client/acme.sh --home /etc/acme \
  --issue --server letsencrypt \
  --webroot /var/run/acme/challenge \
  -d <name>.melnoff.com \
  --keylength ec-256

6. UCI запись для auto-renewal

uci set acme.<short>=cert
uci set acme.<short>.enabled=1
uci set acme.<short>.staging=0
uci set acme.<short>.validation_method=webroot
uci set acme.<short>.key_type=ec256
uci set acme.<short>.domains=<name>.melnoff.com
uci commit acme

7. vhost

cat > /etc/nginx/conf.d/<name>.conf <<NGINX
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    listen 8080;
    server_name <name>.melnoff.com;
 
    ssl_certificate     /etc/acme/<name>.melnoff.com_ecc/fullchain.cer;
    ssl_certificate_key /etc/acme/<name>.melnoff.com_ecc/<name>.melnoff.com.key;
    ssl_session_cache   shared:SSL:32k;
    ssl_session_timeout 64m;
 
    location / {
        proxy_pass         http://172.16.10.1:<port>;
        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 https;
        proxy_read_timeout 300s;
    }
}
NGINX
 
/etc/init.d/nginx reload

8. Проверка

curl -sI https://<name>.melnoff.com/

Известные грабли

  • bash в Alpine LXC отсутствует. pct exec 107 – bash … даст ошибку. Использовать /bin/sh.
  • curl в Alpine LXC по умолчанию не установлен. Поставить: apk add –no-cache curl.
  • nginx -t без флагов не работает на OpenWrt nginx-package (он использует /etc/nginx/uci.conf). Используйте /etc/init.d/nginx reload. Для инспекции — nginx -T.
  • acme.sh по умолчанию хочет ZeroSSL и требует EAB/email. Всегда передавайте –server letsencrypt.
  • ash (busybox) не любит круглые скобки в неэкранированном echo. Берите в кавычки.
  • vhost не должен ссылаться на отсутствующий cert — иначе nginx reload упадёт. Порядок: acme80 update → reload → issue cert → vhost → reload again.
  • При включении Cloudflare proxy webroot http-01 challenge сломается — нужно перейти на DNS-01.
selfhosted/network.txt · Последнее изменение: melnoff