fix: продакшен-фиксы install-flow и шаблонов
- исправлен рендер post-install.env: прокинут HYSTERIA_API_SECRET
- добавлен fail-fast при неразрешенных {{...}} в renderTemplate
- HY2XS_FORCE_PASSWORD_CHANGE приведён к production default=false
- docs синхронизированы: sudo bootstrap на deps-стадии
- builder: VERIFY_TOOLCHAIN_CHECKSUMS=true по умолчанию
- acceptance расширен новыми инвариантами
This commit is contained in:
@@ -44,9 +44,9 @@ apt-get update
|
|||||||
apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables systemd
|
apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables systemd
|
||||||
```
|
```
|
||||||
|
|
||||||
`sudo` является обязательной зависимостью target-хоста. Он используется не для интерактивной установки, а для smoke-проверок прав доступа от имени runtime-пользователей.
|
`sudo` используется для smoke-проверок прав доступа от имени runtime-пользователей и автоматически устанавливается на шаге deps в install-flow.
|
||||||
|
|
||||||
`sudo` должен быть установлен до запуска `./install.sh`: preflight проверяет его до шага `installDeps`, поэтому deps-стадия не является bootstrap-слоем для `sudo`.
|
Предварительная ручная установка `sudo` перед `./install.sh` не требуется: preflight допускает его отсутствие на clean-host и install deps bootstrap'ит его до smoke-проверок.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./install.sh --non-interactive
|
./install.sh --non-interactive
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
|
|
||||||
### 0. Target prerequisites обязательны
|
### 0. Target prerequisites обязательны
|
||||||
|
|
||||||
На target-хосте до запуска install должны быть доступны системные зависимости:
|
На target-хосте install-flow сам обеспечивает системные зависимости (deps stage):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables systemd
|
apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables systemd
|
||||||
```
|
```
|
||||||
|
|
||||||
`sudo` обязателен: используется smoke-проверками прав от имени runtime-пользователей (`hysteria`, `hy2xs-admin`).
|
`sudo` обязателен для smoke-проверок прав от имени runtime-пользователей (`hysteria`, `hy2xs-admin`), но его не нужно ставить вручную заранее: на clean-host он устанавливается на deps-стадии до smoke.
|
||||||
|
|
||||||
### 1. Builder и target — разные миры
|
### 1. Builder и target — разные миры
|
||||||
Если нужно изменить состав install package, это делается в локальном builder layer, а не на target server.
|
Если нужно изменить состав install package, это делается в локальном builder layer, а не на target server.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ apt-get install -y sudo ca-certificates curl iproute2 tar openssl nftables syste
|
|||||||
|
|
||||||
`sudo` обязателен для permission smoke-checks от имени runtime-пользователей.
|
`sudo` обязателен для permission smoke-checks от имени runtime-пользователей.
|
||||||
|
|
||||||
`sudo` должен быть установлен до запуска `./install.sh`: preflight проверяет его до шага установки зависимостей.
|
Предварительная ручная установка `sudo` до запуска `./install.sh` не требуется: на clean-host install-flow ставит его на стадии deps до выполнения smoke-checks.
|
||||||
|
|
||||||
## 3. Required open ports
|
## 3. Required open ports
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export function parseRuntimeEnv(content: string): RuntimeConfig {
|
|||||||
adminUser: requireValue("HY2XS_ADMIN_USER", env.HY2XS_ADMIN_USER || "admin"),
|
adminUser: requireValue("HY2XS_ADMIN_USER", env.HY2XS_ADMIN_USER || "admin"),
|
||||||
adminInitialPassword: valueOrGenerate(env.HY2XS_ADMIN_INITIAL_PASSWORD),
|
adminInitialPassword: valueOrGenerate(env.HY2XS_ADMIN_INITIAL_PASSWORD),
|
||||||
adminConPass: requireValue("HY2XS_ADMIN_CON_PASS", valueOrGenerate(env.HY2XS_ADMIN_CON_PASS)),
|
adminConPass: requireValue("HY2XS_ADMIN_CON_PASS", valueOrGenerate(env.HY2XS_ADMIN_CON_PASS)),
|
||||||
forcePasswordChange: parseBool("HY2XS_FORCE_PASSWORD_CHANGE", env.HY2XS_FORCE_PASSWORD_CHANGE, true),
|
forcePasswordChange: parseBool("HY2XS_FORCE_PASSWORD_CHANGE", env.HY2XS_FORCE_PASSWORD_CHANGE, false),
|
||||||
allowSelfSignedDev: parseBool("HY2XS_ALLOW_SELF_SIGNED_DEV", env.HY2XS_ALLOW_SELF_SIGNED_DEV, false),
|
allowSelfSignedDev: parseBool("HY2XS_ALLOW_SELF_SIGNED_DEV", env.HY2XS_ALLOW_SELF_SIGNED_DEV, false),
|
||||||
tlsMode,
|
tlsMode,
|
||||||
acmeType,
|
acmeType,
|
||||||
|
|||||||
@@ -87,5 +87,12 @@ export function renderTemplate(template: string, values: Record<string, string |
|
|||||||
for (const [key, value] of Object.entries(values)) {
|
for (const [key, value] of Object.entries(values)) {
|
||||||
rendered = rendered.replaceAll(`{{${key}}}`, String(value));
|
rendered = rendered.replaceAll(`{{${key}}}`, String(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unresolved = rendered.match(/\{\{\s*[A-Z0-9_]+\s*\}\}/g) ?? [];
|
||||||
|
if (unresolved.length > 0) {
|
||||||
|
const unique = [...new Set(unresolved.map((item) => item.replace(/\s+/g, "")))].sort();
|
||||||
|
throw new Error(`template render failed: unresolved placeholders: ${unique.join(", ")}`);
|
||||||
|
}
|
||||||
|
|
||||||
return rendered;
|
return rendered;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export async function writePostInstallEnv(context: RuntimeContext): Promise<void
|
|||||||
IGNORE_CLIENT_BANDWIDTH: context.config.hysteriaIgnoreClientBandwidth ? "true" : "false",
|
IGNORE_CLIENT_BANDWIDTH: context.config.hysteriaIgnoreClientBandwidth ? "true" : "false",
|
||||||
HYSTERIA_API_HOST: context.config.hysteriaTrafficStatsHost,
|
HYSTERIA_API_HOST: context.config.hysteriaTrafficStatsHost,
|
||||||
HYSTERIA_API_PORT: context.config.hysteriaTrafficStatsPort,
|
HYSTERIA_API_PORT: context.config.hysteriaTrafficStatsPort,
|
||||||
|
HYSTERIA_API_SECRET: context.config.hysteriaTrafficStatsSecret,
|
||||||
UI_BIND_HOST: context.config.uiBindHost,
|
UI_BIND_HOST: context.config.uiBindHost,
|
||||||
UI_PORT: context.config.uiPort,
|
UI_PORT: context.config.uiPort,
|
||||||
INSTALL_DIR: context.config.installDir,
|
INSTALL_DIR: context.config.installDir,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ HY2XS_UI_PORT=8080
|
|||||||
HY2XS_ADMIN_USER=hy2xsadmin
|
HY2XS_ADMIN_USER=hy2xsadmin
|
||||||
HY2XS_ADMIN_INITIAL_PASSWORD=__GENERATE__
|
HY2XS_ADMIN_INITIAL_PASSWORD=__GENERATE__
|
||||||
HY2XS_ADMIN_CON_PASS=__GENERATE__
|
HY2XS_ADMIN_CON_PASS=__GENERATE__
|
||||||
HY2XS_FORCE_PASSWORD_CHANGE=true
|
HY2XS_FORCE_PASSWORD_CHANGE=false
|
||||||
HY2XS_ALLOW_SELF_SIGNED_DEV=false
|
HY2XS_ALLOW_SELF_SIGNED_DEV=false
|
||||||
HY2XS_TLS_MODE=acme
|
HY2XS_TLS_MODE=acme
|
||||||
HY2XS_ACME_TYPE=http
|
HY2XS_ACME_TYPE=http
|
||||||
|
|||||||
@@ -180,6 +180,12 @@ If frozen install fails, update dependencies intentionally in source control and
|
|||||||
- `TOOLCHAIN_DIR=/custom/path/.toolchain`
|
- `TOOLCHAIN_DIR=/custom/path/.toolchain`
|
||||||
- `VERIFY_TOOLCHAIN_CHECKSUMS=true`
|
- `VERIFY_TOOLCHAIN_CHECKSUMS=true`
|
||||||
|
|
||||||
|
По умолчанию `VERIFY_TOOLCHAIN_CHECKSUMS=true` в [`tools/build/lib/deps.sh`](tools/build/lib/deps.sh), поэтому для production-сборки обязательно передавать контрольные суммы:
|
||||||
|
|
||||||
|
- `GO_ARCHIVE_SHA256=<sha256>`
|
||||||
|
- `NODE_ARCHIVE_SHA256=<sha256>`
|
||||||
|
- `BUN_ARCHIVE_SHA256=<sha256>`
|
||||||
|
|
||||||
## Frontend memory policy
|
## Frontend memory policy
|
||||||
|
|
||||||
Builder applies a safe default Node.js heap limit for frontend build inside [`bundle_ui()`](lib/package.sh):
|
Builder applies a safe default Node.js heap limit for frontend build inside [`bundle_ui()`](lib/package.sh):
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ run_fix20_acceptance_subset() {
|
|||||||
log_step "Acceptance: firewall mode defaults in config"
|
log_step "Acceptance: firewall mode defaults in config"
|
||||||
grep -q '^HY2XS_FIREWALL_MODE=' "$package_dir/config/hy2xs.env" || fail "acceptance: HY2XS_FIREWALL_MODE missing in runtime config"
|
grep -q '^HY2XS_FIREWALL_MODE=' "$package_dir/config/hy2xs.env" || fail "acceptance: HY2XS_FIREWALL_MODE missing in runtime config"
|
||||||
|
|
||||||
|
log_step "Acceptance: force password change production default"
|
||||||
|
grep -q '^HY2XS_FORCE_PASSWORD_CHANGE=false$' "$package_dir/config/hy2xs.env" || fail "acceptance: HY2XS_FORCE_PASSWORD_CHANGE must default to false"
|
||||||
|
|
||||||
log_step "Acceptance: systemd unit production env"
|
log_step "Acceptance: systemd unit production env"
|
||||||
grep -q '^Environment=GIN_MODE=release$' "$package_dir/systemd/hy2xs-admin.service" || fail "acceptance: GIN_MODE=release missing"
|
grep -q '^Environment=GIN_MODE=release$' "$package_dir/systemd/hy2xs-admin.service" || fail "acceptance: GIN_MODE=release missing"
|
||||||
|
|
||||||
@@ -28,4 +31,8 @@ run_fix20_acceptance_subset() {
|
|||||||
log_step "Acceptance: smoke auth checks are tokenized"
|
log_step "Acceptance: smoke auth checks are tokenized"
|
||||||
grep -q 'unexpected auth status without machine token' orchestrator/src/steps/smoke.ts || fail "acceptance: missing 403 negative smoke for auth without machine token"
|
grep -q 'unexpected auth status without machine token' orchestrator/src/steps/smoke.ts || fail "acceptance: missing 403 negative smoke for auth without machine token"
|
||||||
grep -q 'hysteria2/auth?access_token=\${context.config.hysteriaTrafficStatsSecret}' orchestrator/src/steps/smoke.ts || fail "acceptance: smoke auth URL is not tokenized"
|
grep -q 'hysteria2/auth?access_token=\${context.config.hysteriaTrafficStatsSecret}' orchestrator/src/steps/smoke.ts || fail "acceptance: smoke auth URL is not tokenized"
|
||||||
|
|
||||||
|
log_step "Acceptance: env rendering maps machine token and fails on unresolved placeholders"
|
||||||
|
grep -q 'HYSTERIA_API_SECRET: context.config.hysteriaTrafficStatsSecret' orchestrator/src/steps/env.ts || fail "acceptance: writePostInstallEnv must pass HYSTERIA_API_SECRET"
|
||||||
|
grep -q 'template render failed: unresolved placeholders' orchestrator/src/lib/fs.ts || fail "acceptance: renderTemplate must fail on unresolved placeholders"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ BUN_REQUIRED="${BUN_REQUIRED:-1.3.13}"
|
|||||||
NODE_REQUIRED="${NODE_REQUIRED:-20.19.0}"
|
NODE_REQUIRED="${NODE_REQUIRED:-20.19.0}"
|
||||||
PNPM_REQUIRED="${PNPM_REQUIRED:-9.15.9}"
|
PNPM_REQUIRED="${PNPM_REQUIRED:-9.15.9}"
|
||||||
TOOLCHAIN_DIR="${TOOLCHAIN_DIR:-$ROOT_DIR/.toolchain}"
|
TOOLCHAIN_DIR="${TOOLCHAIN_DIR:-$ROOT_DIR/.toolchain}"
|
||||||
VERIFY_TOOLCHAIN_CHECKSUMS="${VERIFY_TOOLCHAIN_CHECKSUMS:-false}"
|
VERIFY_TOOLCHAIN_CHECKSUMS="${VERIFY_TOOLCHAIN_CHECKSUMS:-true}"
|
||||||
GO_ARCHIVE_SHA256="${GO_ARCHIVE_SHA256:-}"
|
GO_ARCHIVE_SHA256="${GO_ARCHIVE_SHA256:-}"
|
||||||
NODE_ARCHIVE_SHA256="${NODE_ARCHIVE_SHA256:-}"
|
NODE_ARCHIVE_SHA256="${NODE_ARCHIVE_SHA256:-}"
|
||||||
BUN_ARCHIVE_SHA256="${BUN_ARCHIVE_SHA256:-}"
|
BUN_ARCHIVE_SHA256="${BUN_ARCHIVE_SHA256:-}"
|
||||||
|
|||||||
Reference in New Issue
Block a user