feat(installer): implement Phase 2 — uo-link install and service
Some checks failed
PR Checks / rust-gates (pull_request) Failing after 1m15s
Some checks failed
PR Checks / rust-gates (pull_request) Failing after 1m15s
Adds the sidecar half of a deployment to the same `install` run: download and verify the bundle's binary, provision its config, register and start a service, and print the token handoff PLAN.md §6 specifies. `src/sidecar.rs` owns the binary and the config document; `src/service.rs` owns systemd and the Windows SCM. The order is fixed by PLAN.md §5 and matters: stop anything running the old binary, replace it, then `--print-config` (which writes the config the service will be pointed at), then register. Registering first points a service at a file that does not exist yet. Decisions worth a reviewer's attention: - Both platforms run the sidecar as a dedicated unprivileged identity. Linux gets the `runicgateway` system user the plan already specified; Windows gets a virtual service account, `sc create ... obj= "NT SERVICE\RunicGatewayLink"`, which the SCM creates itself and which has no password. Plain `sc create` runs as LocalSystem — the most privileged local identity there is, for a process listening on two TCP ports while its Linux twin deliberately does not run as root. - `sidecar.toml` holds the auth token and neither default location protects it: /etc is world-readable and %ProgramData% grants Users read by inheritance, so a stock install would leave the shard's token readable by any local account. The lockdown straddles registration because it has to — on Windows the service account does not exist until `sc create` creates it, so the file is first cut down to SYSTEM + Administrators, and the account's read grant comes after. - Only Linux pins UOLINK_DB_PATH. On Windows config and data share a directory and the sidecar anchors a relative [store] path to its config's directory, so the pin is redundant — and `sc.exe` has no per-service environment, only a machine-wide one that every process inherits and that outlives an uninstall. The config path rides in the service's own binPath instead. - `--verify` runs no part of the sidecar half. `--print-config` provisions: it writes the config and mints a token, so a dry run that called it would create the state it claims not to. It also carries an existing `link` section of install.json through untouched, so a dry run cannot make a service disappear from the record. - The installed binary's protocol version is checked against the bundle before the service is registered. Gate 1 read that number from source at the release tag; this is the same check applied to the binary that will actually answer the website. - RUNICGATEWAY_STATE_DIR now relocates the sidecar binary as well, and suppresses service registration and the file-permission hardening. There is no such thing as a relocated systemd unit, and hardening a scratch config against the only account that will ever read it just breaks the next test run. - A host with no systemd, or where the service user cannot be created, still gets a working binary and config plus the exact unit and commands. There is no fallback to User=root or LocalSystem: a service quietly running with more privilege than its documentation promises is worse than one that was not registered. - install.json never records the token. The `link` section carries versions, the binary's hash, the config and database paths, and the service's name, unit path and account. Docs half: docs#91. Tested: cargo fmt --check, clippy --all-targets -D warnings, 72 tests. End to end on Windows against a relocated layout — bundle sidecar downloaded and verified, config provisioned, handoff printed with URLs composed from the host rather than the bind address, second run reporting unchanged with install.json byte-identical, --verify over an installed host writing nothing and preserving the link section, and a tampered binary detected by hash and replaced with no staging file left. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
10
src/lib.rs
10
src/lib.rs
@@ -4,10 +4,10 @@
|
||||
//! record is `docs/installer/PLAN.md`; the operator-facing contract, written before this binary
|
||||
//! existed, is `docs/installer/INSTALL.md`.
|
||||
//!
|
||||
//! **This build implements Phase 1 (installer core):** bundle resolution, ServUO detection and
|
||||
//! validation, the overlay sync, and `install.json`. The uo-link sidecar and its service (Phase 2),
|
||||
//! the patch tier (Phase 3), and `doctor`/`update`/`uninstall` (Phase 4) are not implemented, and
|
||||
//! every one of them says so when reached rather than failing as though it were a typo.
|
||||
//! **This build implements Phases 1 and 2:** bundle resolution, ServUO detection and validation,
|
||||
//! the overlay sync, `install.json`, the uo-link sidecar and its service, and the token handoff.
|
||||
//! The patch tier (Phase 3) and `doctor`/`update`/`uninstall` (Phase 4) are not implemented, and
|
||||
//! each of them says so when reached rather than failing as though it were a typo.
|
||||
//!
|
||||
//! Exit codes: `0` success, `1` the run failed, `2` the arguments were unusable — the same
|
||||
//! convention as the sidecar's CLI.
|
||||
@@ -33,7 +33,9 @@ pub mod net;
|
||||
pub mod overlay;
|
||||
pub mod paths;
|
||||
pub mod record;
|
||||
pub mod service;
|
||||
pub mod servuo;
|
||||
pub mod sidecar;
|
||||
pub mod ui;
|
||||
pub mod util;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user