From 0545990968ab3eb1d6352609e8fd3ddbdb0a66a6 Mon Sep 17 00:00:00 2001 From: Crimson Date: Wed, 29 Apr 2026 02:25:07 +0500 Subject: [PATCH] =?UTF-8?q?fix8:=20=D1=83=D1=81=D0=B8=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20rollback=20reconfigure,=20=D0=B8=D0=B4=D0=B5=D0=BC?= =?UTF-8?q?=D0=BF=D0=BE=D1=82=D0=B5=D0=BD=D1=82=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=8C=20firewall=20=D0=B8=20prod-hardening=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/frontend/src/views/config/list/index.vue | 127 ++---------------- .../02-target-repo-structure.txt | 4 +- .../04-runtime-package-layout.txt | 4 +- orchestrator/src/commands/reconfigure.ts | 16 ++- orchestrator/src/steps/firewall.ts | 11 +- package/config/hy2xs.env | 2 +- tools/build/lib/deps.sh | 22 +++ 7 files changed, 65 insertions(+), 121 deletions(-) diff --git a/apps/frontend/src/views/config/list/index.vue b/apps/frontend/src/views/config/list/index.vue index 1c1a2b1..7ecd611 100644 --- a/apps/frontend/src/views/config/list/index.vue +++ b/apps/frontend/src/views/config/list/index.vue @@ -43,6 +43,13 @@ + + @@ -61,6 +69,7 @@ @@ -75,7 +84,7 @@ /> - + - {{ t("config.useHysteria2Cert") }} - - - - - - - { dataFormRef.value.validate((valid: boolean) => { if (valid) { - if (state.huiHttps) { - if (!state.dataForm.huiCrtPath || !state.dataForm.huiKeyPath) { - ElMessage.error("crt and key required"); - return; - } - } - - if (!state.huiHttps) { - state.dataForm.huiCrtPath = ""; - state.dataForm.huiKeyPath = ""; - } - let configs: ConfigsUpdateDto[] = [ - { - key: huiWebPortKey, - value: state.dataForm.huiWebPort, - }, - { - key: huiWebContext, - value: state.dataForm.huiWebContext, - }, { key: hysteria2TrafficTimeKey, value: state.dataForm.hysteria2TrafficTime, }, - { - key: huiCrtPathKey, - value: state.dataForm.huiCrtPath, - }, - { - key: huiKeyPathKey, - value: state.dataForm.huiKeyPath, - }, { key: resetTrafficCronKey, value: state.dataForm.resetTrafficCron, @@ -415,41 +351,6 @@ const handleExport = async () => { } }; -const setCertPath = async () => { - try { - const { data } = await hysteria2AcmePathApi(); - const { crtPath, keyPath } = data; - state.dataForm.huiCrtPath = crtPath; - state.dataForm.huiKeyPath = keyPath; - } catch (e) { - /* empty */ - } -}; - -const uploadCertFile = async (params: UploadRequestOptions) => { - try { - if ( - !params.file.name.endsWith(".crt") && - !params.file.name.endsWith(".key") - ) { - ElMessage.error("file format not supported"); - } - if (params.file.size > 1024 * 1024) { - ElMessage.error("the file is too big"); - } - let formData = new FormData(); - formData.append("file", params.file); - const { data } = await uploadCertFileApi(formData); - if (params.file.name.endsWith(".crt")) { - state.dataForm.huiCrtPath = data; - } else if (params.file.name.endsWith(".key")) { - state.dataForm.huiKeyPath = data; - } - } catch (e) { - /* empty */ - } -}; - const handleRestartServer = async () => { try { ElMessageBox.confirm("Are you sure to restart panel?", "Warning", { diff --git a/hy2xs_implementation_plan-no_git/02-target-repo-structure.txt b/hy2xs_implementation_plan-no_git/02-target-repo-structure.txt index 3981bf9..0d26bd4 100644 --- a/hy2xs_implementation_plan-no_git/02-target-repo-structure.txt +++ b/hy2xs_implementation_plan-no_git/02-target-repo-structure.txt @@ -71,8 +71,8 @@ project/ │ ├── branding/ │ ├── patches/ │ └── BUILD_NOTES.md -├── examples/ -│ └── post-install.env.example +├── config/ +│ └── hy2xs.env └── dist/ └── hy2xs-install-.tar.gz diff --git a/hy2xs_implementation_plan-no_git/04-runtime-package-layout.txt b/hy2xs_implementation_plan-no_git/04-runtime-package-layout.txt index 27d3483..2178f03 100644 --- a/hy2xs_implementation_plan-no_git/04-runtime-package-layout.txt +++ b/hy2xs_implementation_plan-no_git/04-runtime-package-layout.txt @@ -30,9 +30,9 @@ HY2XS: runtime package layout - package.build_id - checksums.txt -7. docs/examples +7. docs/config - короткий README по установке -- post-install.env.example +- canonical runtime source config (hy2xs.env) Что не должно быть в runtime package - builder/ diff --git a/orchestrator/src/commands/reconfigure.ts b/orchestrator/src/commands/reconfigure.ts index 17ff8a2..7dac97b 100644 --- a/orchestrator/src/commands/reconfigure.ts +++ b/orchestrator/src/commands/reconfigure.ts @@ -15,14 +15,28 @@ async function backupCurrentState(): Promise { 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/hy2xs/hy2xs.env /etc/hy2xs/backups/hy2xs.env.bak 2>/dev/null || true`; + await runVisible`cp -a /etc/hysteria/post-install.env /etc/hy2xs/backups/post-install.env.bak 2>/dev/null || true`; + await runVisible`cp -a /etc/nftables.conf /etc/hy2xs/backups/nftables.conf.bak 2>/dev/null || true`; await runVisible`cp -a /etc/nftables.d/hy2xs.nft /etc/hy2xs/backups/hy2xs.nft.bak 2>/dev/null || true`; + + await runVisible`test -f /etc/hy2xs/hy2xs.env && echo 1 > /etc/hy2xs/backups/hy2xs.env.existed || rm -f /etc/hy2xs/backups/hy2xs.env.existed`; + await runVisible`test -f /etc/hysteria/post-install.env && echo 1 > /etc/hy2xs/backups/post-install.env.existed || rm -f /etc/hy2xs/backups/post-install.env.existed`; + await runVisible`test -f /etc/nftables.conf && echo 1 > /etc/hy2xs/backups/nftables.conf.existed || rm -f /etc/hy2xs/backups/nftables.conf.existed`; + await runVisible`test -f /etc/nftables.d/hy2xs.nft && echo 1 > /etc/hy2xs/backups/hy2xs.nft.existed || rm -f /etc/hy2xs/backups/hy2xs.nft.existed`; } async function rollbackCurrentState(): Promise { 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`if [ -f /etc/hy2xs/backups/hy2xs.env.existed ]; then cp -a /etc/hy2xs/backups/hy2xs.env.bak /etc/hy2xs/hy2xs.env 2>/dev/null || true; else rm -f /etc/hy2xs/hy2xs.env; fi`; + await runVisible`if [ -f /etc/hy2xs/backups/post-install.env.existed ]; then cp -a /etc/hy2xs/backups/post-install.env.bak /etc/hysteria/post-install.env 2>/dev/null || true; else rm -f /etc/hysteria/post-install.env; fi`; + await runVisible`if [ -f /etc/hy2xs/backups/nftables.conf.existed ]; then cp -a /etc/hy2xs/backups/nftables.conf.bak /etc/nftables.conf 2>/dev/null || true; fi`; + await runVisible`if [ -f /etc/hy2xs/backups/hy2xs.nft.existed ]; then cp -a /etc/hy2xs/backups/hy2xs.nft.bak /etc/nftables.d/hy2xs.nft 2>/dev/null || true; else rm -f /etc/nftables.d/hy2xs.nft; fi`; + + await runVisible`nft -f /etc/nftables.conf >/dev/null 2>&1 || true`; await runVisible`systemctl daemon-reload`; await runVisible`systemctl restart hysteria-server hy2xs-admin || true`; } diff --git a/orchestrator/src/steps/firewall.ts b/orchestrator/src/steps/firewall.ts index 8b059aa..2c39e5f 100644 --- a/orchestrator/src/steps/firewall.ts +++ b/orchestrator/src/steps/firewall.ts @@ -29,12 +29,19 @@ export async function applyFirewall(context: InstallContext): Promise { await writeText("/etc/nftables.d/hy2xs.nft.candidate", rendered, 0o600); await runVisible`nft -c -f /etc/nftables.d/hy2xs.nft.candidate`; + const nftablesConf = `#!/usr/sbin/nft -f +flush ruleset + +include "/etc/nftables.d/hy2xs.nft" +`; + await writeText("/etc/nftables.conf", nftablesConf, 0o644); + await runVisible`nft -c -f /etc/nftables.conf`; + if (context.config.firewallStagedApply) { - await runVisible`systemd-run --unit hy2xs-fw-rollback --on-active=45s /bin/sh -c 'cp -a /etc/nftables.conf.hy2xs.bak /etc/nftables.conf 2>/dev/null || true; if [ -f /etc/nftables.d/hy2xs.nft.existed ]; then cp -a /etc/nftables.d/hy2xs.nft.bak /etc/nftables.d/hy2xs.nft 2>/dev/null || true; else rm -f /etc/nftables.d/hy2xs.nft; fi; nft -f /etc/nftables.conf >/dev/null 2>&1 || true'`; + await runVisible`systemd-run --unit hy2xs-fw-rollback --on-active=45s /bin/sh -c 'if [ -f /etc/nftables.conf.hy2xs.bak ]; then cp -a /etc/nftables.conf.hy2xs.bak /etc/nftables.conf 2>/dev/null || true; fi; if [ -f /etc/nftables.d/hy2xs.nft.existed ]; then cp -a /etc/nftables.d/hy2xs.nft.bak /etc/nftables.d/hy2xs.nft 2>/dev/null || true; else rm -f /etc/nftables.d/hy2xs.nft; fi; nft -f /etc/nftables.conf >/dev/null 2>&1 || true'`; } await runVisible`mv /etc/nftables.d/hy2xs.nft.candidate /etc/nftables.d/hy2xs.nft`; - await runVisible`grep -q 'include "/etc/nftables.d/hy2xs.nft"' /etc/nftables.conf || printf '\ninclude "/etc/nftables.d/hy2xs.nft"\n' >> /etc/nftables.conf`; await runVisible`nft -f /etc/nftables.conf`; await runVisible`systemctl enable --now nftables`; diff --git a/package/config/hy2xs.env b/package/config/hy2xs.env index 02acb4f..7b40692 100644 --- a/package/config/hy2xs.env +++ b/package/config/hy2xs.env @@ -30,7 +30,7 @@ HY2XS_HYSTERIA_BANDWIDTH_UP=50 mbps HY2XS_HYSTERIA_BANDWIDTH_DOWN=50 mbps HY2XS_HYSTERIA_IGNORE_CLIENT_BANDWIDTH=false HY2XS_HYSTERIA_CONFIG_PATH=/etc/hysteria/config.yaml -HY2XS_HYSTERIA_VERSION=latest +HY2XS_HYSTERIA_VERSION=v2.6.0 HY2XS_INSTALL_DIR=/opt/hy2xs-admin HY2XS_DATA_DIR=/var/lib/hy2xs-admin HY2XS_LOG_DIR=/var/log/hy2xs diff --git a/tools/build/lib/deps.sh b/tools/build/lib/deps.sh index 6bf72bc..260cb00 100644 --- a/tools/build/lib/deps.sh +++ b/tools/build/lib/deps.sh @@ -6,6 +6,25 @@ BUN_REQUIRED="${BUN_REQUIRED:-1.1.45}" NODE_REQUIRED="${NODE_REQUIRED:-20.19.0}" PNPM_REQUIRED="${PNPM_REQUIRED:-9.15.9}" TOOLCHAIN_DIR="${TOOLCHAIN_DIR:-$ROOT_DIR/.toolchain}" +VERIFY_TOOLCHAIN_CHECKSUMS="${VERIFY_TOOLCHAIN_CHECKSUMS:-false}" +GO_ARCHIVE_SHA256="${GO_ARCHIVE_SHA256:-}" +NODE_ARCHIVE_SHA256="${NODE_ARCHIVE_SHA256:-}" +BUN_ARCHIVE_SHA256="${BUN_ARCHIVE_SHA256:-}" + +verify_archive_sha256() { + local archive="$1" + local expected="$2" + local label="$3" + + if [ "$VERIFY_TOOLCHAIN_CHECKSUMS" != "true" ]; then + return 0 + fi + + [ -n "$expected" ] || fail "missing expected SHA256 for $label (set ${label}_SHA256 env)" + local actual + actual="$(sha256sum "$archive" | awk '{print $1}')" + [ "$actual" = "$expected" ] || fail "$label sha256 mismatch: expected $expected, got $actual" +} require_linux_debian12_amd64() { [ "$(uname -s)" = "Linux" ] || fail "production builder supports only Linux Debian 12 amd64" @@ -79,6 +98,7 @@ ensure_go() { mkdir -p "$TOOLCHAIN_DIR/downloads" local archive="$TOOLCHAIN_DIR/downloads/go${GO_REQUIRED}.linux-amd64.tar.gz" download_file "https://go.dev/dl/go${GO_REQUIRED}.linux-amd64.tar.gz" "$archive" + verify_archive_sha256 "$archive" "$GO_ARCHIVE_SHA256" "GO_ARCHIVE" rm -rf "$TOOLCHAIN_DIR/go" tar -C "$TOOLCHAIN_DIR" -xzf "$archive" GO_BIN="$managed" @@ -102,6 +122,7 @@ ensure_bun() { mkdir -p "$TOOLCHAIN_DIR/downloads" "$TOOLCHAIN_DIR/bun" local archive="$TOOLCHAIN_DIR/downloads/bun-linux-x64-${BUN_REQUIRED}.zip" download_file "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_REQUIRED}/bun-linux-x64.zip" "$archive" + verify_archive_sha256 "$archive" "$BUN_ARCHIVE_SHA256" "BUN_ARCHIVE" rm -rf "$TOOLCHAIN_DIR/bun-tmp" "$TOOLCHAIN_DIR/bun" mkdir -p "$TOOLCHAIN_DIR/bun-tmp" unzip -q "$archive" -d "$TOOLCHAIN_DIR/bun-tmp" @@ -131,6 +152,7 @@ ensure_node() { mkdir -p "$TOOLCHAIN_DIR/downloads" local archive="$TOOLCHAIN_DIR/downloads/node-v${NODE_REQUIRED}-linux-x64.tar.xz" download_file "https://nodejs.org/dist/v${NODE_REQUIRED}/node-v${NODE_REQUIRED}-linux-x64.tar.xz" "$archive" + verify_archive_sha256 "$archive" "$NODE_ARCHIVE_SHA256" "NODE_ARCHIVE" rm -rf "$TOOLCHAIN_DIR/node" "$TOOLCHAIN_DIR/node-v${NODE_REQUIRED}-linux-x64" tar -C "$TOOLCHAIN_DIR" -xJf "$archive" mv "$TOOLCHAIN_DIR/node-v${NODE_REQUIRED}-linux-x64" "$TOOLCHAIN_DIR/node"