Реализован production-hardening по fix1: env/reconfigure, IPv4-only, TLS, secrets, firewall, docs
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { InstallContext } from "../types/context";
|
||||
import { exists, readText } from "../lib/fs";
|
||||
import { fail } from "../lib/log";
|
||||
import { fail, info } from "../lib/log";
|
||||
import { run } from "../lib/process";
|
||||
|
||||
async function isPortBusy(port: number): Promise<boolean> {
|
||||
@@ -13,6 +13,8 @@ async function isPortBusy(port: number): Promise<boolean> {
|
||||
}
|
||||
|
||||
export async function preflight(context: InstallContext): Promise<void> {
|
||||
const isReconfigure = context.packageVersion === "reconfigure";
|
||||
|
||||
if (process.getuid?.() !== 0) {
|
||||
fail("installer must run as root");
|
||||
}
|
||||
@@ -26,28 +28,59 @@ export async function preflight(context: InstallContext): Promise<void> {
|
||||
fail("bundled HY2XS admin is missing from install package");
|
||||
}
|
||||
|
||||
if (await exists("/etc/hysteria/post-install.env")) {
|
||||
if (!isReconfigure && (await exists("/etc/hysteria/post-install.env"))) {
|
||||
fail("existing HY2XS post-install.env found; update/repair is out of scope");
|
||||
}
|
||||
|
||||
if (await exists("/opt/hy2xs-admin")) {
|
||||
if (!isReconfigure && (await exists(context.config.installDir))) {
|
||||
fail("existing /opt/hy2xs-admin found; conflicting old state");
|
||||
}
|
||||
|
||||
const ports = new Set([context.options.port, context.options.uiPort]);
|
||||
const ports = new Set([context.config.hysteriaPort, context.config.uiPort]);
|
||||
if (ports.size !== 2) {
|
||||
fail("Hysteria port and UI port must be different");
|
||||
}
|
||||
|
||||
if (context.options.domain && !/^[a-zA-Z0-9.-]+$/.test(context.options.domain)) {
|
||||
if (context.config.domain && !/^[a-zA-Z0-9.-]+$/.test(context.config.domain)) {
|
||||
fail("domain contains unsupported characters");
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.options.port)) {
|
||||
fail(`Hysteria UDP/TCP port already appears to be in use: ${context.options.port}`);
|
||||
if (context.config.uiBindHost.includes(":")) {
|
||||
fail("HY2XS UI bind host must be IPv4-only");
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.options.uiPort)) {
|
||||
fail(`HY2XS admin port already appears to be in use: ${context.options.uiPort}`);
|
||||
if (context.config.hysteriaBindHost !== "0.0.0.0") {
|
||||
fail("HY2XS_HYSTERIA_BIND_HOST must be 0.0.0.0 for production profile");
|
||||
}
|
||||
|
||||
if (context.config.tlsMode === "acme" && (!context.config.domain || !context.config.acmeEmail)) {
|
||||
fail("acme mode requires HY2XS_DOMAIN and HY2XS_ACME_EMAIL");
|
||||
}
|
||||
|
||||
if (context.config.domain) {
|
||||
try {
|
||||
const a = await run`getent ahostsv4 ${context.config.domain}`;
|
||||
if (!a.trim()) {
|
||||
fail(`domain has no A-record: ${context.config.domain}`);
|
||||
}
|
||||
} catch {
|
||||
fail(`domain has no A-record: ${context.config.domain}`);
|
||||
}
|
||||
try {
|
||||
const aaaa = await run`getent ahostsv6 ${context.config.domain}`;
|
||||
if (aaaa.trim()) {
|
||||
info(`warning: domain ${context.config.domain} has AAAA record; HY2XS remains IPv4-only`);
|
||||
}
|
||||
} catch {
|
||||
// no AAAA is acceptable
|
||||
}
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.config.hysteriaPort)) {
|
||||
fail(`Hysteria UDP/TCP port already appears to be in use: ${context.config.hysteriaPort}`);
|
||||
}
|
||||
|
||||
if (await isPortBusy(context.config.uiPort)) {
|
||||
fail(`HY2XS admin port already appears to be in use: ${context.config.uiPort}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user