deploy: support runtime env files

This commit is contained in:
server
2026-04-14 09:41:31 +02:00
parent b7c54b909f
commit beb1a4481d
7 changed files with 114 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ python3 deploy/systemd/install_systemd.py \
--user mt2.jakubkadlec.dev \ --user mt2.jakubkadlec.dev \
--group mt2.jakubkadlec.dev \ --group mt2.jakubkadlec.dev \
--runtime-root /home/mt2.jakubkadlec.dev/metin/runtime/server \ --runtime-root /home/mt2.jakubkadlec.dev/metin/runtime/server \
--env-file /etc/metin/metin.env \
--channel 1 \ --channel 1 \
--channel 99 \ --channel 99 \
--restart --restart
@@ -30,3 +31,14 @@ python3 deploy/systemd/install_systemd.py \
- `/usr/local/libexec/metin-wait-port` - `/usr/local/libexec/metin-wait-port`
The `metin-db-ready.service` gate waits until the DB socket is actually accepting connections before `auth` and `game` units start. The `metin-db-ready.service` gate waits until the DB socket is actually accepting connections before `auth` and `game` units start.
## Optional Environment File
The runtime units support an optional `EnvironmentFile` for host-local overrides:
- default path: `/etc/metin/metin.env`
- it is loaded with `EnvironmentFile=-...`, so the file may be absent
- recommended ownership: `root:root`
- recommended mode: `0600`
This is the preferred place for production-only values such as DB credentials and the admin page password.

View File

@@ -24,6 +24,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--runtime-root", required=True, help="Absolute path to the live runtime root") parser.add_argument("--runtime-root", required=True, help="Absolute path to the live runtime root")
parser.add_argument("--systemd-dir", default="/etc/systemd/system", help="systemd unit destination") parser.add_argument("--systemd-dir", default="/etc/systemd/system", help="systemd unit destination")
parser.add_argument("--libexec-dir", default="/usr/local/libexec", help="Helper script destination") parser.add_argument("--libexec-dir", default="/usr/local/libexec", help="Helper script destination")
parser.add_argument("--env-file", default="/etc/metin/metin.env", help="Optional EnvironmentFile path for runtime overrides")
parser.add_argument("--wait-host", default="127.0.0.1", help="DB readiness host") parser.add_argument("--wait-host", default="127.0.0.1", help="DB readiness host")
parser.add_argument("--wait-port", type=int, default=9000, help="DB readiness port") parser.add_argument("--wait-port", type=int, default=9000, help="DB readiness port")
parser.add_argument("--wait-timeout", type=int, default=30, help="DB readiness timeout in seconds") parser.add_argument("--wait-timeout", type=int, default=30, help="DB readiness timeout in seconds")
@@ -118,6 +119,7 @@ def main() -> int:
"USER_NAME": args.user, "USER_NAME": args.user,
"GROUP_NAME": group_name, "GROUP_NAME": group_name,
"RUNTIME_ROOT": runtime_root, "RUNTIME_ROOT": runtime_root,
"ENV_FILE": args.env_file,
"WAIT_HOST": args.wait_host, "WAIT_HOST": args.wait_host,
"WAIT_PORT": str(args.wait_port), "WAIT_PORT": str(args.wait_port),
"WAIT_TIMEOUT": str(args.wait_timeout), "WAIT_TIMEOUT": str(args.wait_timeout),

View File

@@ -10,6 +10,7 @@ Before=metin-server.service
Type=simple Type=simple
User={{USER_NAME}} User={{USER_NAME}}
Group={{GROUP_NAME}} Group={{GROUP_NAME}}
EnvironmentFile=-{{ENV_FILE}}
WorkingDirectory={{RUNTIME_ROOT}}/channels/auth WorkingDirectory={{RUNTIME_ROOT}}/channels/auth
ExecStart={{RUNTIME_ROOT}}/channels/auth/game_auth ExecStart={{RUNTIME_ROOT}}/channels/auth/game_auth
Restart=on-failure Restart=on-failure

View File

@@ -10,6 +10,7 @@ Before=metin-server.service
Type=simple Type=simple
User={{USER_NAME}} User={{USER_NAME}}
Group={{GROUP_NAME}} Group={{GROUP_NAME}}
EnvironmentFile=-{{ENV_FILE}}
WorkingDirectory={{RUNTIME_ROOT}}/channels/db WorkingDirectory={{RUNTIME_ROOT}}/channels/db
ExecStart={{RUNTIME_ROOT}}/channels/db/db ExecStart={{RUNTIME_ROOT}}/channels/db/db
Restart=on-failure Restart=on-failure

View File

@@ -10,6 +10,7 @@ Before=metin-server.service
Type=simple Type=simple
User={{USER_NAME}} User={{USER_NAME}}
Group={{GROUP_NAME}} Group={{GROUP_NAME}}
EnvironmentFile=-{{ENV_FILE}}
WorkingDirectory={{RUNTIME_ROOT}} WorkingDirectory={{RUNTIME_ROOT}}
ExecStart=/usr/local/libexec/metin-game-instance-start %i ExecStart=/usr/local/libexec/metin-game-instance-start %i
Restart=on-failure Restart=on-failure

View File

@@ -36,6 +36,7 @@ For the current Debian VPS:
- root-only operational wrappers may inject short-lived values locally - root-only operational wrappers may inject short-lived values locally
- headless login healthcheck uses a temporary password via environment, not a command-line literal - headless login healthcheck uses a temporary password via environment, not a command-line literal
- the installed wrapper is root-only and not network-facing - the installed wrapper is root-only and not network-facing
- `systemd` units may load a host-local env file from `/etc/metin/metin.env`
## Admin Page Password ## Admin Page Password
@@ -54,3 +55,83 @@ The Debian deployment should eventually move to a clearer contract such as:
- documented override points - documented override points
Until that is done, keep all real secret rotation and secret overrides on the host, not in commits. Until that is done, keep all real secret rotation and secret overrides on the host, not in commits.
## Environment Override Contract
The source/runtime stack now supports these host-local environment overrides:
- `METIN2_ADMINPAGE_PASSWORD`
- `METIN2_DB_ADDR`
- `METIN2_DB_PORT`
- `METIN2_ACCOUNT_SQL_HOST`
- `METIN2_ACCOUNT_SQL_USER`
- `METIN2_ACCOUNT_SQL_PASSWORD`
- `METIN2_ACCOUNT_SQL_DB`
- `METIN2_ACCOUNT_SQL_PORT`
- `METIN2_PLAYER_SQL_HOST`
- `METIN2_PLAYER_SQL_USER`
- `METIN2_PLAYER_SQL_PASSWORD`
- `METIN2_PLAYER_SQL_DB`
- `METIN2_PLAYER_SQL_PORT`
- `METIN2_COMMON_SQL_HOST`
- `METIN2_COMMON_SQL_USER`
- `METIN2_COMMON_SQL_PASSWORD`
- `METIN2_COMMON_SQL_DB`
- `METIN2_COMMON_SQL_PORT`
- `METIN2_LOG_SQL_HOST`
- `METIN2_LOG_SQL_USER`
- `METIN2_LOG_SQL_PASSWORD`
- `METIN2_LOG_SQL_DB`
- `METIN2_LOG_SQL_PORT`
- `METIN2_HOTBACKUP_SQL_HOST`
- `METIN2_HOTBACKUP_SQL_USER`
- `METIN2_HOTBACKUP_SQL_PASSWORD`
- `METIN2_HOTBACKUP_SQL_DB`
- `METIN2_HOTBACKUP_SQL_PORT`
`game_auth` and `game` consume the `ACCOUNT/PLAYER/COMMON/LOG` variants. The `db` process consumes `ACCOUNT/PLAYER/COMMON/HOTBACKUP`.
Recommended deployment model:
- keep git-tracked `share/conf/*.txt` as bootstrap defaults only
- install `/etc/metin/metin.env` as `root:root` with mode `0600`
- point systemd at that env file via `deploy/systemd/install_systemd.py --env-file /etc/metin/metin.env`
Example:
```bash
mkdir -p /etc/metin
chmod 700 /etc/metin
cat >/etc/metin/metin.env <<'EOF'
METIN2_ADMINPAGE_PASSWORD=replace-me
METIN2_DB_ADDR=127.0.0.1
METIN2_DB_PORT=9000
METIN2_ACCOUNT_SQL_HOST=127.0.0.1
METIN2_ACCOUNT_SQL_USER=mt2
METIN2_ACCOUNT_SQL_PASSWORD=replace-me
METIN2_ACCOUNT_SQL_DB=account
METIN2_ACCOUNT_SQL_PORT=0
METIN2_PLAYER_SQL_HOST=127.0.0.1
METIN2_PLAYER_SQL_USER=mt2
METIN2_PLAYER_SQL_PASSWORD=replace-me
METIN2_PLAYER_SQL_DB=player
METIN2_PLAYER_SQL_PORT=0
METIN2_COMMON_SQL_HOST=127.0.0.1
METIN2_COMMON_SQL_USER=mt2
METIN2_COMMON_SQL_PASSWORD=replace-me
METIN2_COMMON_SQL_DB=common
METIN2_COMMON_SQL_PORT=0
METIN2_LOG_SQL_HOST=127.0.0.1
METIN2_LOG_SQL_USER=mt2
METIN2_LOG_SQL_PASSWORD=replace-me
METIN2_LOG_SQL_DB=log
METIN2_LOG_SQL_PORT=0
METIN2_HOTBACKUP_SQL_HOST=127.0.0.1
METIN2_HOTBACKUP_SQL_USER=mt2
METIN2_HOTBACKUP_SQL_PASSWORD=replace-me
METIN2_HOTBACKUP_SQL_DB=hotbackup
METIN2_HOTBACKUP_SQL_PORT=0
EOF
chown root:root /etc/metin/metin.env
chmod 600 /etc/metin/metin.env
```

View File

@@ -94,6 +94,19 @@ systemctl restart metin-server
journalctl -u metin-auth.service -n 100 --no-pager journalctl -u metin-auth.service -n 100 --no-pager
``` ```
Install or refresh the systemd stack with a host-local env file:
```bash
python3 deploy/systemd/install_systemd.py \
--user mt2.jakubkadlec.dev \
--group mt2.jakubkadlec.dev \
--runtime-root /home/mt2.jakubkadlec.dev/metin/runtime/server \
--env-file /etc/metin/metin.env \
--channel 1 \
--channel 99 \
--restart
```
Rebuild the login smoke utility: Rebuild the login smoke utility:
```bash ```bash
@@ -109,6 +122,8 @@ Current operational stance:
- password SSH login is disabled - password SSH login is disabled
- `root` login is allowed only by SSH key - `root` login is allowed only by SSH key
- production helper scripts that touch the DB directly are root-only - production helper scripts that touch the DB directly are root-only
- runtime repo and source repo do not store secrets - git-tracked runtime configs are treated as bootstrap defaults, not as the final secret source of truth
Do not store production secrets in markdown, `systemd` templates, or git-tracked shell scripts. Do not store production secrets in markdown, `systemd` templates, or git-tracked shell scripts.
For production overrides, prefer `/etc/metin/metin.env` with `root:root` ownership and mode `0600`.