Реализован production-hardening по fix1: env/reconfigure, IPv4-only, TLS, secrets, firewall, docs

This commit is contained in:
2026-04-26 07:27:06 +05:00
parent 2b4a45ad23
commit 3fccd5c442
109 changed files with 1773 additions and 569 deletions
+12 -4
View File
@@ -1,7 +1,9 @@
import { randomBytes } from "node:crypto";
import type { InstallContext, InstallOptions } from "../types/context";
import { readText } from "../lib/fs";
import { exists, readText, writeText } from "../lib/fs";
import { runVisible } from "../lib/process";
import { step } from "../lib/log";
import { defaultRuntimeConfig, parseRuntimeEnv, renderRuntimeEnv } from "../config/env";
import { preflight } from "../steps/preflight";
import { installDeps } from "../steps/deps";
import { prepareFilesystem } from "../steps/filesystem";
@@ -26,15 +28,16 @@ function secret(): string {
}
export async function install(options: InstallOptions): Promise<void> {
const hasConfig = await exists(options.configPath);
const config = hasConfig ? parseRuntimeEnv(await readText(options.configPath)) : defaultRuntimeConfig();
const context: InstallContext = {
options,
config,
packageVersion: await readPackageValue(options.packageDir, "package.version", "unknown"),
packageBuildId: await readPackageValue(options.packageDir, "package.build_id", "unknown"),
installDate: new Date().toISOString(),
hysteriaAuthPassword: secret(),
hysteriaObfsPassword: secret(),
hysteriaApiSecret: secret(),
hysteriaApiPort: 36712,
hysteriaVersion: "unknown"
};
@@ -44,6 +47,11 @@ export async function install(options: InstallOptions): Promise<void> {
await installDeps(context);
step("filesystem");
await prepareFilesystem(context);
if (!hasConfig) {
step("write runtime env");
await runVisible`mkdir -p /etc/hy2xs`;
await writeText(options.configPath, renderRuntimeEnv(config), 0o600);
}
step("bundled UI");
await deployUi(context);
step("Hysteria2 upstream install");
+76
View File
@@ -0,0 +1,76 @@
import type { ReconfigureContext, ReconfigureOptions } from "../types/context";
import { readText, writeText } from "../lib/fs";
import { info, step } from "../lib/log";
import { parseRuntimeEnv, renderRuntimeEnv } from "../config/env";
import { preflight } from "../steps/preflight";
import { generateConfig } from "../steps/config";
import { deploySystemd } from "../steps/systemd";
import { applyFirewall } from "../steps/firewall";
import { writePostInstallEnv } from "../steps/env";
import { smoke } from "../steps/smoke";
import { runVisible } from "../lib/process";
async function backupCurrentState(): Promise<void> {
await runVisible`mkdir -p /etc/hy2xs/backups`;
await runVisible`cp -a /etc/hysteria/config.yaml /etc/hy2xs/backups/config.yaml.bak 2>/dev/null || true`;
await runVisible`cp -a /etc/systemd/system/hy2xs-admin.service /etc/hy2xs/backups/hy2xs-admin.service.bak 2>/dev/null || true`;
await runVisible`cp -a /etc/systemd/system/hysteria-server.service /etc/hy2xs/backups/hysteria-server.service.bak 2>/dev/null || true`;
await runVisible`cp -a /etc/nftables.d/hy2xs.nft /etc/hy2xs/backups/hy2xs.nft.bak 2>/dev/null || true`;
}
async function rollbackCurrentState(): Promise<void> {
await runVisible`cp -a /etc/hy2xs/backups/config.yaml.bak /etc/hysteria/config.yaml 2>/dev/null || true`;
await runVisible`cp -a /etc/hy2xs/backups/hy2xs-admin.service.bak /etc/systemd/system/hy2xs-admin.service 2>/dev/null || true`;
await runVisible`cp -a /etc/hy2xs/backups/hysteria-server.service.bak /etc/systemd/system/hysteria-server.service 2>/dev/null || true`;
await runVisible`cp -a /etc/hy2xs/backups/hy2xs.nft.bak /etc/nftables.d/hy2xs.nft 2>/dev/null || true`;
await runVisible`systemctl daemon-reload`;
await runVisible`systemctl restart hysteria-server hy2xs-admin || true`;
}
export async function reconfigure(options: ReconfigureOptions): Promise<void> {
const configRaw = await readText(options.configPath);
const config = parseRuntimeEnv(configRaw);
const context: ReconfigureContext & { packageVersion: string; packageBuildId: string; installDate: string; hysteriaAuthPassword: string; hysteriaVersion: string } = {
options,
config,
packageVersion: "reconfigure",
packageBuildId: "reconfigure",
installDate: new Date().toISOString(),
hysteriaAuthPassword: "managed-by-ui-auth",
hysteriaVersion: "unknown"
};
step("preflight");
await preflight(context);
if (options.dryRun) {
info("reconfigure dry-run: validated config and execution graph");
info(`config file: ${options.configPath}`);
info(`ui bind: ${config.uiBindHost}:${config.uiPort}`);
info(`hysteria bind: ${config.hysteriaBindHost}:${config.hysteriaPort}`);
info(`public endpoint: ${config.publicHost}:${config.publicPort}`);
return;
}
step("backup");
await backupCurrentState();
try {
step("config generation");
await generateConfig(context);
step("systemd units");
await deploySystemd(context);
step("firewall");
await applyFirewall(context);
step("write env artifacts");
await writeText(options.configPath, renderRuntimeEnv(config), 0o600);
await writePostInstallEnv(context);
step("smoke checks");
await smoke(context);
} catch (error) {
info("reconfigure failed, rollback in progress");
await rollbackCurrentState();
throw error;
}
}