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.com → 95.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
