Fix modern DNS startup and parameterize node provisioning

This commit is contained in:
hellcat 2026-10-11 16:58:11 +08:00
parent 12d9f2b47c
commit 3b72c3e3c1
6 changed files with 36 additions and 9 deletions

View File

@ -2,7 +2,7 @@
require getcwd() . '/vendor/autoload.php';
$app = require getcwd() . '/bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
$opts = getopt('', ['backup:']);
$opts = getopt('', ['backup:', 'expected-current-sha256:']);
$dir = $opts['backup'] ?? throw new RuntimeException('Missing backup');
umask(0077);
$old = App\Models\SubscribeTemplate::where('name', 'singbox')->firstOrFail();
@ -11,9 +11,11 @@ $replacement = file_get_contents(__DIR__ . '/sing-box-template.json');
$new = json_decode($replacement, true, flags: JSON_THROW_ON_ERROR);
$current = json_decode($old->content, true, flags: JSON_THROW_ON_ERROR);
if ($current === $new) { echo "Template already updated\n"; exit; }
if ($current !== $default) throw new RuntimeException('Custom template detected; inspect instead of overwriting');
$expected = $opts['expected-current-sha256'] ?? '';
$knownRevision = preg_match('/^[a-f0-9]{64}$/', $expected) && hash_equals($expected, hash('sha256', $old->content));
if ($current !== $default && !$knownRevision) throw new RuntimeException('Custom or unexpected template detected; inspect instead of overwriting');
if (!is_dir($dir)) throw new RuntimeException('Backup directory missing');
$path = "$dir/singbox-template-before.json";
$path = "$dir/singbox-template-before-" . hash('sha256', $old->content) . '.json';
if (is_file($path)) throw new RuntimeException('Existing backup; inspect before retry');
file_put_contents($path, json_encode($old->getAttributes(), JSON_THROW_ON_ERROR));
App\Models\SubscribeTemplate::setContent('singbox', $replacement);

View File

@ -2,11 +2,27 @@
set -euo pipefail
# Credential files are installed separately with mode 0600.
[[ $(id -u) == 0 ]] || exit 1
listen_port=''
tls_domain=''
for arg in "$@"; do
case "$arg" in
--listen-port=*) listen_port=${arg#*=} ;;
--tls-domain=*) tls_domain=${arg#*=} ;;
*) echo 'Usage: install-node.sh --listen-port=24443 --tls-domain=us01.example.com'; exit 1 ;;
esac
done
[[ $listen_port =~ ^[0-9]+$ && ${#listen_port} -le 5 ]] && (( 10#$listen_port >= 1024 && 10#$listen_port <= 65535 )) || { echo 'Explicit valid listen port required'; exit 1; }
[[ $tls_domain =~ ^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$ ]] || { echo 'Explicit TLS domain required'; exit 1; }
[[ -s /etc/dpl-node/config.yml && -s /etc/dpl-node/secret.env ]] || { echo 'Private node configuration missing'; exit 1; }
if ss -lntH '( sport = :24443 )' | grep -q .; then
echo 'Port 24443 already in use; inspect before deployment'; exit 1
[[ ! -e /etc/systemd/system/dpl-node.service && ! -e /opt/dpl-node/xboard-node ]] || { echo 'DPL backend already installed; use a separately reviewed Git update'; exit 1; }
if ss -lntH "( sport = :$listen_port )" | grep -q .; then
echo "Port $listen_port already in use; inspect before deployment"; exit 1
fi
systemctl is-active --quiet XrayR
if systemctl list-unit-files XrayR.service --no-legend | grep -q '^XrayR.service'; then
systemctl is-active --quiet XrayR || { echo 'Existing XrayR is not active; investigate before installation'; exit 1; }
fi
printf '%s\n' "$tls_domain" > /etc/dpl-node/certificate-domain
chmod 644 /etc/dpl-node/certificate-domain
install -d -m 755 /opt/dpl-node
install -d -m 700 /var/lib/dpl-node /var/lib/dpl-node/kernel
curl --fail --location --silent --show-error \

View File

@ -1,5 +1,7 @@
#!/bin/sh
set -eu
# Copy under renewal-hooks/deploy only after auditing all existing hooks.
[ "${RENEWED_LINEAGE:-}" = /etc/letsencrypt/live/us01.deeplink-subscribe.com ] || exit 0
[ -s /etc/dpl-node/certificate-domain ] || exit 0
domain=$(cat /etc/dpl-node/certificate-domain)
[ "${RENEWED_LINEAGE:-}" = "/etc/letsencrypt/live/$domain" ] || exit 0
systemctl try-restart dpl-node

View File

@ -26,7 +26,6 @@
{
"type": "https",
"server": "223.5.5.5",
"detour": "direct",
"tag": "local"
}
],

View File

@ -15,7 +15,7 @@ for version in (4, 6):
assert all(n.version == version and n.prefixlen >= (8 if version == 4 else 16) for n in nets), "Invalid trust range"
ranges.extend(str(n) for n in nets)
assert len(ranges) == len(set(ranges)), "Duplicate ranges"
data = ("# DPL only: synchronized from Cloudflare official lists.\n" +
data = ("# Subscription vhosts: synchronized from Cloudflare official lists.\n" +
"".join(f"set_real_ip_from {n};\n" for n in ranges) +
"real_ip_header CF-Connecting-IP;\nreal_ip_recursive off;\n").encode()
old = target.read_bytes() if target.exists() else None

View File

@ -4,6 +4,7 @@ import argparse
import json
import os
import pathlib
import shutil
import socket
import subprocess
import tempfile
@ -55,6 +56,13 @@ with tempfile.TemporaryDirectory(prefix='dpl-client-') as d:
assert trace.get('ip') == a.expected_exit, 'Second exit check mismatch'
print(json.dumps({'anytls': True, 'relay': q['host'], 'port': q['port'], 'strict_tls': True,
'exit_ip': ip, 'country': trace.get('loc'), 'https_tls': trace.get('tls')}, ensure_ascii=False))
except Exception:
log.flush()
failure = pathlib.Path(a.qa).parent / 'client-failure.log'
shutil.copyfile(pathlib.Path(d) / 'client.log', failure)
failure.chmod(0o600)
print(f'Client failed; protected diagnostic log: {failure}')
raise
finally:
proc.terminate()
try: