Add DC9 HAProxy TLS front door

This commit is contained in:
Codex
2026-09-02 22:54:05 -07:00
parent 7ea4515b27
commit 787a1128c1
8 changed files with 142 additions and 72 deletions
@@ -0,0 +1,3 @@
# Stage on 8444 first. Change to 443 only after the Arcane Agent Caddy is
# reachable through its temporary management port and host TCP 443 is free.
HAPROXY_PUBLIC_PORT=8444
+12
View File
@@ -0,0 +1,12 @@
# DC9 TLS front door
HAProxy inspects TCP SNI without terminating TLS:
- `dc9-tls-e2f48b.opfo.xyz` -> managed sing-box Trojan on host TCP 22591
- `sb-dd65a3.kais.cloud` -> managed sing-box AnyTLS on host TCP 43824
- `dc9.kais.cloud` -> the existing Arcane Agent Caddy on `arcane_default`
- unmatched SNI -> the dedicated Caddy fallback serving `dc9.opfo.xyz`
The first deployment uses host TCP 8444. Moving it to TCP 443 requires a staged
handoff from the existing Arcane Agent Caddy so the Direct HTTPS management path
remains reachable throughout the change.
@@ -0,0 +1,21 @@
{
servers {
protocols h1 h2
}
}
dc9.opfo.xyz {
encode zstd gzip
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy no-referrer
}
respond <<HTML 200
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Service Status</title></head>
<body><main><h1>Service online</h1><p>The requested service is available.</p></main></body>
</html>
HTML
}
+44
View File
@@ -0,0 +1,44 @@
services:
haproxy:
image: haproxy:3.4.4-alpine3.24
container_name: dc9-haproxy
restart: unless-stopped
ports:
- "${HAPROXY_PUBLIC_PORT:-8444}:8443/tcp"
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- decoy-caddy
networks:
- default
- arcane
volumes:
- ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
logging:
driver: json-file
options:
max-size: 200k
max-file: "2"
decoy-caddy:
image: caddy:2.11.4
container_name: dc9-decoy-caddy
restart: unless-stopped
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
logging:
driver: json-file
options:
max-size: 200k
max-file: "2"
networks:
arcane:
external: true
name: arcane_default
volumes:
caddy-data:
caddy-config:
@@ -0,0 +1,37 @@
global
log stdout format raw local0
maxconn 4096
defaults
log global
mode tcp
option tcplog
timeout connect 5s
timeout client 24h
timeout server 24h
frontend tls_443
bind 0.0.0.0:8443
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
acl sni_trojan req.ssl_sni -i dc9-tls-e2f48b.opfo.xyz
acl sni_anytls req.ssl_sni -i sb-dd65a3.kais.cloud
acl sni_arcane req.ssl_sni -i dc9.kais.cloud
use_backend singbox_trojan if sni_trojan
use_backend singbox_anytls if sni_anytls
use_backend arcane_agent if sni_arcane
default_backend decoy_https
backend singbox_trojan
server trojan host.docker.internal:22591
backend singbox_anytls
server anytls host.docker.internal:43824
backend arcane_agent
server arcane arcane-agent-caddy:443
backend decoy_https
server caddy decoy-caddy:443
+2 -7
View File
@@ -5,16 +5,11 @@ USER1_PASSWORD=change-me
CLOUDFLARE_API_TOKEN=change-me CLOUDFLARE_API_TOKEN=change-me
ANYTLS_DOMAIN=sb-dd65a3.kais.cloud ANYTLS_DOMAIN=sb-dd65a3.kais.cloud
ANYTLS_HY2_PORT=43824 ANYTLS_PORT=43824
ANYTLS_PASSWORD=change-me ANYTLS_PASSWORD=change-me
HY2_PASSWORD=change-me HY2_PASSWORD=change-me
HY2_OBFS_PASSWORD=change-me HY2_OBFS_PASSWORD=change-me
HY2_PORT=443
VLESS_PORT=22590
VLESS_UUID=00000000-0000-4000-8000-000000000000
REALITY_SERVER_NAME=www.cloudflare.com
REALITY_PRIVATE_KEY=change-me
REALITY_SHORT_ID=0123456789abcdef
# sing-box uses version 5 for the Snell v4-compatible TCP wire protocol. # sing-box uses version 5 for the Snell v4-compatible TCP wire protocol.
SNELL_V4_PORT=53469 SNELL_V4_PORT=53469
+3 -7
View File
@@ -1,6 +1,6 @@
services: services:
config-render: config-render:
image: alpine:3.20 image: alpine:3.24
environment: environment:
DOMAIN: ${DOMAIN} DOMAIN: ${DOMAIN}
ACME_EMAIL: ${ACME_EMAIL} ACME_EMAIL: ${ACME_EMAIL}
@@ -8,15 +8,11 @@ services:
USER1_PASSWORD: ${USER1_PASSWORD} USER1_PASSWORD: ${USER1_PASSWORD}
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN} CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
ANYTLS_DOMAIN: ${ANYTLS_DOMAIN} ANYTLS_DOMAIN: ${ANYTLS_DOMAIN}
ANYTLS_HY2_PORT: ${ANYTLS_HY2_PORT} ANYTLS_PORT: ${ANYTLS_PORT}
ANYTLS_PASSWORD: ${ANYTLS_PASSWORD} ANYTLS_PASSWORD: ${ANYTLS_PASSWORD}
HY2_PASSWORD: ${HY2_PASSWORD} HY2_PASSWORD: ${HY2_PASSWORD}
HY2_OBFS_PASSWORD: ${HY2_OBFS_PASSWORD} HY2_OBFS_PASSWORD: ${HY2_OBFS_PASSWORD}
VLESS_PORT: ${VLESS_PORT} HY2_PORT: ${HY2_PORT}
VLESS_UUID: ${VLESS_UUID}
REALITY_SERVER_NAME: ${REALITY_SERVER_NAME}
REALITY_PRIVATE_KEY: ${REALITY_PRIVATE_KEY}
REALITY_SHORT_ID: ${REALITY_SHORT_ID}
SNELL_V4_PORT: ${SNELL_V4_PORT} SNELL_V4_PORT: ${SNELL_V4_PORT}
SNELL_V4_PSK: ${SNELL_V4_PSK} SNELL_V4_PSK: ${SNELL_V4_PSK}
SNELL_V6_PORT: ${SNELL_V6_PORT} SNELL_V6_PORT: ${SNELL_V6_PORT}
@@ -1,4 +1,19 @@
{ {
"certificate_providers": [
{
"type": "acme",
"tag": "public-cert",
"domain": [
"${DOMAIN}",
"${ANYTLS_DOMAIN}"
],
"email": "${ACME_EMAIL}",
"dns01_challenge": {
"provider": "cloudflare",
"api_token": "${CLOUDFLARE_API_TOKEN}"
}
}
],
"inbounds": [ "inbounds": [
{ {
"type": "trojan", "type": "trojan",
@@ -14,15 +29,7 @@
"tls": { "tls": {
"enabled": true, "enabled": true,
"server_name": "${DOMAIN}", "server_name": "${DOMAIN}",
"acme": { "certificate_provider": "public-cert"
"domain": "${DOMAIN}",
"data_directory": "/var/lib/sing-box/certmagic-anytls",
"email": "${ACME_EMAIL}",
"dns01_challenge": {
"provider": "cloudflare",
"api_token": "${CLOUDFLARE_API_TOKEN}"
}
}
}, },
"multiplex": { "multiplex": {
"enabled": true "enabled": true
@@ -32,7 +39,7 @@
"type": "anytls", "type": "anytls",
"tag": "anytls-in", "tag": "anytls-in",
"listen": "::", "listen": "::",
"listen_port": ${ANYTLS_HY2_PORT}, "listen_port": ${ANYTLS_PORT},
"users": [ "users": [
{ {
"name": "user1", "name": "user1",
@@ -42,22 +49,14 @@
"tls": { "tls": {
"enabled": true, "enabled": true,
"server_name": "${ANYTLS_DOMAIN}", "server_name": "${ANYTLS_DOMAIN}",
"acme": { "certificate_provider": "public-cert"
"domain": "${ANYTLS_DOMAIN}",
"data_directory": "/var/lib/sing-box/certmagic-anytls",
"email": "${ACME_EMAIL}",
"dns01_challenge": {
"provider": "cloudflare",
"api_token": "${CLOUDFLARE_API_TOKEN}"
}
}
} }
}, },
{ {
"type": "hysteria2", "type": "hysteria2",
"tag": "hysteria2-in", "tag": "hysteria2-in",
"listen": "0.0.0.0", "listen": "0.0.0.0",
"listen_port": ${ANYTLS_HY2_PORT}, "listen_port": ${HY2_PORT},
"obfs": { "obfs": {
"type": "salamander", "type": "salamander",
"password": "${HY2_OBFS_PASSWORD}" "password": "${HY2_OBFS_PASSWORD}"
@@ -74,44 +73,7 @@
"alpn": [ "alpn": [
"h3" "h3"
], ],
"acme": { "certificate_provider": "public-cert"
"domain": "${ANYTLS_DOMAIN}",
"data_directory": "/var/lib/sing-box/certmagic-anytls",
"email": "${ACME_EMAIL}",
"dns01_challenge": {
"provider": "cloudflare",
"api_token": "${CLOUDFLARE_API_TOKEN}"
}
}
}
},
{
"type": "vless",
"tag": "vless-reality-in",
"listen": "::",
"listen_port": ${VLESS_PORT},
"users": [
{
"name": "user1",
"uuid": "${VLESS_UUID}",
"flow": "xtls-rprx-vision"
}
],
"tls": {
"enabled": true,
"server_name": "${REALITY_SERVER_NAME}",
"reality": {
"enabled": true,
"handshake": {
"server": "${REALITY_SERVER_NAME}",
"server_port": 443
},
"private_key": "${REALITY_PRIVATE_KEY}",
"short_id": [
"${REALITY_SHORT_ID}"
],
"max_time_difference": "2m"
}
} }
}, },
{ {