fix(deploy): add optional Turnstile and customer mTLS proxy

This commit is contained in:
root committed 2026-09-04 14:17:18 +00:00
1 parent e06a43c76f
commit 35068319e5
7 files changed
+186 -28

No files matched your search

+10 -1
View File
@@ -13,7 +13,16 @@ Generated keys are written only to `/opt/overcooked/production.env` with mode
600. The installer does not log generated values, activation codes, or Auth
configuration. Failure logs are redacted before display.
Turnstile is optional. Empty site and secret keys disable only that abuse
prevention challenge; Auth, license validation, and login policy remain on.
E production control-plane traffic uses mTLS. The installer creates a local
CA and service/client certificates, then generates a minimal HTTPS proxy in the
installation directory and runs it from the approved S WebUI image. The proxy
has no public port and only forwards authenticated `/internal/v1/` calls to
S. The approved Media Node image is exported to the node-image archive used by
S node deployment.
The installer must stop on missing prerequisites, invalid public address,
occupied ports, failed image verification, failed schema migration, or failed
health checks. It must never continue with a partially initialized database.
+19 -5
View File
@@ -12,6 +12,13 @@ only these public images:
It does not download or start an Auth image, and it does not use Docker build
contexts.
Because E production mode requires an encrypted control-plane connection, the
installer generates a minimal allowlisted HTTPS reverse proxy at install time
and runs it from the already-listed S WebUI image. The proxy only accepts
mTLS-authenticated `/internal/v1/` requests and `/health/live`, and forwards
only to the S control service; it is not a public endpoint and does not expose
Shell, SQL, Docker, or arbitrary file access.
## Install
```sh
@@ -23,8 +30,6 @@ sudo env \\
OVERCOOKED_SERVER_IP=203.0.113.10 \\
OVERCOOKED_AUTH_PUBLIC_KEYS='{"auth-1":"PUBLIC_KEY"}' \\
OVERCOOKED_ACTIVATION_CODE='ACTIVATION_CODE' \\
OVERCOOKED_TURNSTILE_SITE_KEY='TURNSTILE_SITE_KEY' \\
OVERCOOKED_TURNSTILE_SECRET_KEY='TURNSTILE_SECRET_KEY' \\
./install.sh
```
@@ -33,9 +38,10 @@ with `OVERCOOKED_AUTH_URL`. Auth private keys, administrator credentials,
license databases, provider credentials, and production secrets are never
included in this repository.
The current S production image requires the Turnstile key pair shown above.
The installer fails early if the pair is missing or incomplete; it never uses
placeholder credentials.
Turnstile is optional. Set `OVERCOOKED_TURNSTILE_SITE_KEY` and
`OVERCOOKED_TURNSTILE_SECRET_KEY` together to enable it; leaving both empty
disables only the Turnstile challenge and does not disable Auth, license, or
login policy checks. A partial pair is rejected by S configuration validation.
## Endpoints
@@ -48,6 +54,10 @@ The installer checks these host ports before changing service state and does
not bind ports 80 or 443. Configure the firewall to allow the chosen public
ports and restrict database/Valkey traffic to the Docker networks.
S's E client uses the private backend Docker network (`http://e-server:8096`)
with its explicit private-network opt-in. E's control-plane client is a
separate production path and always uses the generated HTTPS/mTLS proxy.
The server address is selected from `OVERCOOKED_SERVER_IP`, or from the
interface selected by `ip -4 route get 1.1.1.1`. A detected RFC1918,
loopback, link-local, or carrier-grade NAT address is rejected unless explicitly
@@ -73,6 +83,10 @@ image. S uses only the schema migration assets published in this repository.
If either migration fails, the installer exits non-zero and does not start the
corresponding application service.
The installer also exports the approved Media Node image into
`/opt/overcooked/node-images/media-node.tar.gz`; S uses that archive when a
customer adds a node without a source checkout.
## Common 404/401 download failures
The customer command must fetch the raw script from the public Installer
+3 -3
View File
@@ -1,6 +1,6 @@
3395c27ad4158e70d47e7d002c141b1261f502875f2e7ac5b26ade51b7689138 install.sh
028d415f0c0aef0ed9a807573239079f9e7e8cb3ed796d9dec90f4be8cafdd05 compose/s.yml
fab213abb69e0141139c0d4234a7b1636588206f6588942a55c3380114069d92 compose/e.yml
80d42d9797b20d0b8fd53ac9529cd579fae9fe294267ac8e8ab990ed961e88d1 install.sh
e8cabc5a989d171206361e6ae8b4e0baf9bb9a611ac41106bbe42de0782e8975 compose/s.yml
203b6da918dee21dbfabbcf5466111bd92c7a8c9e754dad61e7e75e20513b880 compose/e.yml
c226c5e1feb5cd6948ee9b32897e77240e21793090d8f42234ca999450ec90b0 config/e-server.production.yaml
3e3dc775017577fad3b36b6c21036dfcb6e530793de4c18c825dda888f62e4e3 s-migrate.sh
07f432c281ba90565cd376b42967909b6ff2d19d04454a3ba4e3b184117d45af s-migrations.tar.gz
+4 -1
View File
@@ -62,6 +62,7 @@ services:
- e-data:/data
- e-plugin-data:/data/plugins
- ${OVERCOOKED_ROOT:-/opt/overcooked}/config/e-server.production.yaml:/etc/overcooked-e/config.yaml:ro
- ${OVERCOOKED_ROOT:-/opt/overcooked}/control-plane:/run/overcooked-control-plane:ro
environment:
PRODUCT_E_CONFIG: /etc/overcooked-e/config.yaml
PRODUCT_E_ENVIRONMENT: production
@@ -72,6 +73,9 @@ services:
PRODUCT_E_CONTROL_PLANE_URL: ${PRODUCT_E_CONTROL_PLANE_URL:?required}
PRODUCT_E_CONTROL_PLANE_KEY_ID: ${PRODUCT_E_CONTROL_PLANE_KEY_ID:?required}
PRODUCT_E_CONTROL_PLANE_SHARED_SECRET: ${PRODUCT_E_CONTROL_PLANE_SHARED_SECRET:?required}
PRODUCT_E_CONTROL_PLANE_TLS_CERT_FILE: ${PRODUCT_E_CONTROL_PLANE_TLS_CERT_FILE:?required}
PRODUCT_E_CONTROL_PLANE_TLS_KEY_FILE: ${PRODUCT_E_CONTROL_PLANE_TLS_KEY_FILE:?required}
PRODUCT_E_CONTROL_PLANE_TLS_CA_FILE: ${PRODUCT_E_CONTROL_PLANE_TLS_CA_FILE:?required}
PRODUCT_E_LICENSE_ENABLED: ${PRODUCT_E_LICENSE_ENABLED:-true}
PRODUCT_E_AUTH_URL: ${PRODUCT_E_AUTH_URL:?required}
PRODUCT_E_AUTH_ALLOW_INSECURE_HTTP: ${PRODUCT_E_AUTH_ALLOW_INSECURE_HTTP:-false}
@@ -123,4 +127,3 @@ volumes:
e-data:
e-plugin-data:
e-backup-staging:
+33 -3
View File
@@ -102,8 +102,8 @@ services:
S_VERSION: ${S_VERSION:-customer}
S_BACKUP_DIRECTORY: /var/lib/overcooked-s/backups
S_UPDATE_DIRECTORY: /var/lib/overcooked-s/updates
S_TURNSTILE_SITE_KEY: ${S_TURNSTILE_SITE_KEY:?required}
S_TURNSTILE_SECRET_KEY: ${S_TURNSTILE_SECRET_KEY:?required}
S_TURNSTILE_SITE_KEY: ${S_TURNSTILE_SITE_KEY:-}
S_TURNSTILE_SECRET_KEY: ${S_TURNSTILE_SECRET_KEY:-}
S_TURNSTILE_HOSTNAME: ${S_TURNSTILE_HOSTNAME:-}
S_TELEGRAM_MTPROTO_URL: ${S_TELEGRAM_MTPROTO_URL:-http://telegram-disabled.invalid:8098}
S_PORTAL_LOCATION: ${S_PORTAL_LOCATION:-Private Node}
@@ -112,7 +112,7 @@ services:
S_PORTAL_ANNOUNCEMENT: ${S_PORTAL_ANNOUNCEMENT:-}
S_STORAGE_WEBHOOK_SECRET: ${S_STORAGE_WEBHOOK_SECRET:-}
S_NODE_IMAGE_ARCHIVE: /var/lib/overcooked-s/node-images/media-node.tar.gz
S_NODE_IMAGE_NAME: ${S_NODE_IMAGE_NAME:-overcooked-media-node:latest}
S_NODE_IMAGE_NAME: ${S_NODE_IMAGE_NAME:-git.leee.xin/overcooked/overcooked-media-node:latest}
S_NODE_CONTROLLER_HOST: ${S_NODE_CONTROLLER_HOST:-}
volumes:
- s-backups:/var/lib/overcooked-s/backups
@@ -133,6 +133,36 @@ services:
logging: *logging
mem_limit: 384m
control-proxy:
image: ${S_WEBUI_IMAGE:?required}
restart: unless-stopped
init: true
depends_on:
s-control: {condition: service_healthy}
command: ["node", "/run/overcooked-control-plane/control-proxy.mjs"]
volumes:
- ${OVERCOOKED_ROOT:-/opt/overcooked}/control-plane:/run/overcooked-control-plane:ro
healthcheck:
test:
- CMD
- node
- -e
- |
const fs = require("node:fs");
const https = require("node:https");
const request = https.get("https://control-proxy:8443/health/live", {ca: fs.readFileSync("/run/overcooked-control-plane/ca.crt"), cert: fs.readFileSync("/run/overcooked-control-plane/client.crt"), key: fs.readFileSync("/run/overcooked-control-plane/client.key"), servername: "control-proxy", rejectUnauthorized: true}, (response) => { response.resume(); process.exit(response.statusCode === 200 ? 0 : 1); });
request.setTimeout(2000, () => { request.destroy(); process.exit(1); });
request.on("error", () => process.exit(1));
interval: 10s
timeout: 3s
start_period: 10s
retries: 6
networks: [control-plane]
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
logging: *logging
mem_limit: 128m
webui:
image: ${S_WEBUI_IMAGE:?required}
restart: unless-stopped
+113 -14
View File
@@ -12,8 +12,6 @@ ACTIVATION_CODE=${OVERCOOKED_ACTIVATION_CODE:-}
AUTH_URL=${OVERCOOKED_AUTH_URL:-http://148.135.97.10:18091}
AUTH_PUBLIC_KEYS=${OVERCOOKED_AUTH_PUBLIC_KEYS:-}
INSTALLER_BASE_URL=${OVERCOOKED_INSTALLER_BASE_URL:-https://git.leee.xin/OverCooked/OverCooked-Installer/raw/branch/main}
TURNSTILE_SITE_KEY=${OVERCOOKED_TURNSTILE_SITE_KEY:-}
TURNSTILE_SECRET_KEY=${OVERCOOKED_TURNSTILE_SECRET_KEY:-}
S_COMPOSE=$ROOT/compose/s.yml
E_COMPOSE=$ROOT/compose/e.yml
ENV_FILE=$ROOT/production.env
@@ -51,7 +49,8 @@ is_private_ipv4() {
}
check_port() {
port=$1
if command -v ss >/dev/null 2>&1 && ss -H -ltn 2>/dev/null | awk -v p=":$port" '$4 ~ p "$" || $4 ~ p "\\]$" { found=1 } END { exit !found }'; then
command -v ss >/dev/null 2>&1 || die "未找到 ss,无法安全检测端口 $port;请安装 iproute2 后重试"
if ss -H -ltn 2>/dev/null | awk -v p=":$port" '$4 ~ p "$" || $4 ~ p "\\]$" { found=1 } END { exit !found }'; then
die "端口 $port 已被占用,请停止占用进程或调整部署端口"
fi
}
@@ -116,12 +115,9 @@ if [ -z "$ACTIVATION_CODE" ]; then
printf '请输入授权码: ' >&2; IFS= read -r ACTIVATION_CODE
fi
[ -n "$ACTIVATION_CODE" ] || die "授权码不能为空"
if [ -z "$TURNSTILE_SITE_KEY" ] || [ -z "$TURNSTILE_SECRET_KEY" ]; then
die "当前 S 生产镜像需要 OVERCOOKED_TURNSTILE_SITE_KEY 和 OVERCOOKED_TURNSTILE_SECRET_KEY;请提供成对配置"
fi
check_port 18080; check_port 18083; check_port 8096
install -d -m 700 "$ROOT" "$ROOT/config" "$ROOT/compose" "$ROOT/migrations" "$ROOT/node-images"
install -d -m 700 "$ROOT" "$ROOT/config" "$ROOT/compose" "$ROOT/migrations" "$ROOT/node-images" "$ROOT/control-plane"
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
fetch_asset() {
relative=$1; destination=$2; source="$SCRIPT_DIR/$relative"
@@ -134,11 +130,100 @@ fetch_asset() {
fetch_asset compose/s.yml "$S_COMPOSE"
fetch_asset compose/e.yml "$E_COMPOSE"
fetch_asset s-migrate.sh "$ROOT/s-migrate.sh"
chmod 644 "$ROOT/s-migrate.sh"
fetch_asset config/e-server.production.yaml "$ROOT/config/e-server.production.yaml"
chmod 644 "$ROOT/config/e-server.production.yaml"
fetch_asset s-migrations.tar.gz "$ROOT/s-migrations.tar.gz"
tar -xzf "$ROOT/s-migrations.tar.gz" -C "$ROOT/migrations"; rm -f "$ROOT/s-migrations.tar.gz"
find "$ROOT/migrations" -type f ! -name '*.up.sql' -delete
find "$ROOT/migrations" -type f -name '*.up.sql' -exec chmod 600 {} \;
find "$ROOT/migrations" -type f -name '*.up.sql' -exec chmod 644 {} \;
chmod 755 "$ROOT/migrations" "$ROOT/node-images"
# E requires an HTTPS control-plane origin in production. Generate a local
# CA and service/client certificates for the restricted proxy sidecar. The
# proxy is generated locally and runs from the already-approved S WebUI image;
# no additional image or source checkout is needed.
CONTROL_PLANE_DIR="$ROOT/control-plane"
cat > "$CONTROL_PLANE_DIR/control-proxy.mjs" <<'NODE_PROXY'
import fs from "node:fs";
import http from "node:http";
import https from "node:https";
const listenPort = 8443;
const options = {
key: fs.readFileSync("/run/overcooked-control-plane/server.key"),
cert: fs.readFileSync("/run/overcooked-control-plane/server.crt"),
ca: fs.readFileSync("/run/overcooked-control-plane/ca.crt"),
requestCert: true,
rejectUnauthorized: true,
minVersion: "TLSv1.2",
};
function reject(response, status, message) {
response.writeHead(status, {"content-type": "text/plain; charset=utf-8"});
response.end(message);
}
const server = https.createServer(options, (request, response) => {
let target;
try {
target = new URL(request.url ?? "/", "https://control-proxy");
} catch {
reject(response, 400, "invalid request target");
return;
}
if (target.origin !== "https://control-proxy" || target.username || target.password ||
target.pathname.includes("..") || target.pathname !== "/health/live" && !target.pathname.startsWith("/internal/v1/")) {
reject(response, 404, "not found");
return;
}
if (target.pathname === "/health/live") {
response.writeHead(200, {"content-type": "application/json"});
response.end('{"status":"live"}');
return;
}
const headers = {...request.headers};
delete headers.host;
delete headers.connection;
delete headers["x-forwarded-for"];
delete headers["x-forwarded-host"];
delete headers["x-forwarded-proto"];
headers.host = "s-control:8080";
const upstream = http.request({hostname: "s-control", port: 8080, method: request.method, path: `${target.pathname}${target.search}`, headers, timeout: 10000}, (upstreamResponse) => {
response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);
upstreamResponse.pipe(response);
});
upstream.on("timeout", () => upstream.destroy(new Error("upstream timeout")));
upstream.on("error", () => {
if (!response.headersSent) reject(response, 502, "control plane unavailable");
else response.destroy();
});
request.pipe(upstream);
});
server.listen(listenPort, "0.0.0.0");
NODE_PROXY
chmod 644 "$CONTROL_PLANE_DIR/control-proxy.mjs"
chown 1000:1000 "$CONTROL_PLANE_DIR/control-proxy.mjs"
openssl genrsa -out "$CONTROL_PLANE_DIR/ca.key" 3072
openssl req -x509 -new -nodes -key "$CONTROL_PLANE_DIR/ca.key" -sha256 -days 3650 -subj "/CN=OverCooked customer control plane CA" -out "$CONTROL_PLANE_DIR/ca.crt"
openssl genrsa -out "$CONTROL_PLANE_DIR/server.key" 2048
openssl req -new -key "$CONTROL_PLANE_DIR/server.key" -subj "/CN=control-proxy" -out "$CONTROL_PLANE_DIR/server.csr"
printf '%s\n' 'subjectAltName=DNS:control-proxy,DNS:product-e' 'extendedKeyUsage=serverAuth' > "$CONTROL_PLANE_DIR/server.ext"
openssl x509 -req -in "$CONTROL_PLANE_DIR/server.csr" -CA "$CONTROL_PLANE_DIR/ca.crt" -CAkey "$CONTROL_PLANE_DIR/ca.key" -CAcreateserial -out "$CONTROL_PLANE_DIR/server.crt" -days 825 -sha256 -extfile "$CONTROL_PLANE_DIR/server.ext"
openssl genrsa -out "$CONTROL_PLANE_DIR/client.key" 2048
openssl req -new -key "$CONTROL_PLANE_DIR/client.key" -subj "/CN=overcooked-e" -out "$CONTROL_PLANE_DIR/client.csr"
printf '%s\n' 'extendedKeyUsage=clientAuth' > "$CONTROL_PLANE_DIR/client.ext"
openssl x509 -req -in "$CONTROL_PLANE_DIR/client.csr" -CA "$CONTROL_PLANE_DIR/ca.crt" -CAkey "$CONTROL_PLANE_DIR/ca.key" -CAcreateserial -out "$CONTROL_PLANE_DIR/client.crt" -days 825 -sha256 -extfile "$CONTROL_PLANE_DIR/client.ext"
rm -f "$CONTROL_PLANE_DIR/ca.key" "$CONTROL_PLANE_DIR/server.csr" "$CONTROL_PLANE_DIR/server.ext" "$CONTROL_PLANE_DIR/client.csr" "$CONTROL_PLANE_DIR/client.ext" "$CONTROL_PLANE_DIR/ca.srl"
chmod 644 "$CONTROL_PLANE_DIR/ca.crt" "$CONTROL_PLANE_DIR/server.crt" "$CONTROL_PLANE_DIR/client.crt"
chmod 640 "$CONTROL_PLANE_DIR/server.key" "$CONTROL_PLANE_DIR/client.key"
chown 1000:1000 "$CONTROL_PLANE_DIR/server.key"
# The proxy healthcheck (uid 1000) and E (uid 10001) both need the client key;
# a shared group keeps it unreadable to other container users.
chown 1000:10001 "$CONTROL_PLANE_DIR/client.key"
chmod 755 "$CONTROL_PLANE_DIR"
secret() { openssl rand -hex 32; }
S_POSTGRES_PASSWORD=$(secret); S_VALKEY_PASSWORD=$(secret)
@@ -161,6 +246,8 @@ S_E_BASE_URL=http://e-server:8096
S_E_PUBLIC_URL=http://$SERVER_IP:8096
S_E_KEY_ID=customer-e
S_E_INTERNAL_SECRET=$S_INTERNAL_SHARED_SECRET
# S's private backend client talks to E on the internal Docker network. This
# does not weaken E's production mTLS control-plane connection below.
S_E_ALLOW_INSECURE_HTTP=true
S_AUTH_BASE_URL=$AUTH_URL
S_AUTH_ALLOW_INSECURE_HTTP=$(case "$AUTH_URL" in http://*) echo true ;; *) echo false ;; esac)
@@ -173,16 +260,20 @@ S_PORTAL_SERVER_URL=http://$SERVER_IP:18080
S_PORTAL_WEB_CLIENT_URL=http://$SERVER_IP:18083/login
S_API_PORT_MAPPING=0.0.0.0:18080:8080
S_WEB_PORT_MAPPING=0.0.0.0:18083:3000
S_TURNSTILE_SITE_KEY=$TURNSTILE_SITE_KEY
S_TURNSTILE_SECRET_KEY=$TURNSTILE_SECRET_KEY
S_TURNSTILE_SITE_KEY=${OVERCOOKED_TURNSTILE_SITE_KEY:-}
S_TURNSTILE_SECRET_KEY=${OVERCOOKED_TURNSTILE_SECRET_KEY:-}
S_NODE_IMAGE_NAME=$REGISTRY/overcooked-media-node:$VERSION
PRODUCT_E_IMAGE=$REGISTRY/overcooked-e:$VERSION
PRODUCT_E_INSTANCE_ID=e-$INSTANCE_SUFFIX
PRODUCT_E_DATABASE_URL=postgres://product_e:$E_POSTGRES_PASSWORD@postgres:5432/product_e?sslmode=disable
PRODUCT_E_POSTGRES_PASSWORD=$E_POSTGRES_PASSWORD
PRODUCT_E_VALKEY_PASSWORD=$S_VALKEY_PASSWORD
PRODUCT_E_CONTROL_PLANE_URL=http://s-control:8080
PRODUCT_E_CONTROL_PLANE_URL=https://control-proxy:8443
PRODUCT_E_CONTROL_PLANE_KEY_ID=customer-s
PRODUCT_E_CONTROL_PLANE_SHARED_SECRET=$S_INTERNAL_SHARED_SECRET
PRODUCT_E_CONTROL_PLANE_TLS_CERT_FILE=/run/overcooked-control-plane/client.crt
PRODUCT_E_CONTROL_PLANE_TLS_KEY_FILE=/run/overcooked-control-plane/client.key
PRODUCT_E_CONTROL_PLANE_TLS_CA_FILE=/run/overcooked-control-plane/ca.crt
PRODUCT_E_LICENSE_ENABLED=true
PRODUCT_E_AUTH_URL=$AUTH_URL
PRODUCT_E_AUTH_ALLOW_INSECURE_HTTP=$(case "$AUTH_URL" in http://*) echo true ;; *) echo false ;; esac)
@@ -236,23 +327,31 @@ plugins:
network_allowed_domains: []
network_allowed_cidrs: []
EOF
chmod 600 "$ROOT/config/e-server.production.yaml"
chmod 644 "$ROOT/config/e-server.production.yaml"
docker network inspect overcooked-control >/dev/null 2>&1 || docker network create overcooked-control >/dev/null
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" config >/dev/null
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" config >/dev/null
for image in "$REGISTRY/overcooked-e:$VERSION" "$REGISTRY/overcooked-s-control:$VERSION" "$REGISTRY/overcooked-s-webui:$VERSION" "$REGISTRY/overcooked-media-node:$VERSION"; do
MEDIA_NODE_IMAGE="$REGISTRY/overcooked-media-node:$VERSION"
for image in "$REGISTRY/overcooked-e:$VERSION" "$REGISTRY/overcooked-s-control:$VERSION" "$REGISTRY/overcooked-s-webui:$VERSION" "$MEDIA_NODE_IMAGE"; do
docker pull "$image"
docker image inspect "$image" >/dev/null 2>&1 || die "镜像校验失败:$image"
done
image_tar=$(mktemp "$ROOT/node-images/media-node.tar.XXXXXX")
image_archive=$(mktemp "$ROOT/node-images/media-node.tar.gz.XXXXXX")
docker save "$MEDIA_NODE_IMAGE" -o "$image_tar" || die "无法导出 Media Node 镜像归档"
gzip -c "$image_tar" > "$image_archive" || die "无法压缩 Media Node 镜像归档"
rm -f "$image_tar"
mv "$image_archive" "$ROOT/node-images/media-node.tar.gz"
chmod 644 "$ROOT/node-images/media-node.tar.gz"
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" up -d postgres valkey
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" run --rm --no-deps e-server migrate -config /etc/overcooked-e/config.yaml
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d postgres valkey
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" run --rm --no-deps s-migrate
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d s-control control-proxy webui
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" up -d e-server
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d s-control webui
wait_for() {
label=$1; url=$2; attempt=1
+4 -1
View File
@@ -17,7 +17,10 @@ test -f compose/e.yml
test -f config/e-server.production.yaml
test -f s-migrations.tar.gz
test "$(find s-migrations -type f -name '*.up.sql' | wc -l)" -gt 0
sha256sum -c SHA256SUMS >/dev/null
! rg -n '^([[:space:]]*)build:' compose
! rg -n 'overcooked-auth|overcooked-auth-webui' compose install.sh
! rg -n 'S_TURNSTILE_(SITE|SECRET)_KEY: .*\\?required' compose
! rg -n 'S_INTERNAL_TLS_(ADDR|CERT_FILE|KEY_FILE|CLIENT_CA_FILE)' compose install.sh
rg -n 'control-proxy|PRODUCT_E_CONTROL_PLANE_TLS_' compose install.sh >/dev/null
echo 'static customer installer checks passed'