# Installing and updating RustProxy 0.24.5 The scripts prepare a Linux/systemd installation. This source archive is **not a verified production build**: Rust/Cargo, real CA operations, live systemd startup and rollback against a real Rust database have not been exercised in the packaging environment. Perform the acceptance steps below on an isolated server first. ## Debian 13+ one-line source install/update ```bash # auto: first install or managed update wget -qO- https://git.linuxiarz.pl/gru/RustProxy/raw/branch/master/install-debian13.sh | bash # force the update path wget -qO- https://git.linuxiarz.pl/gru/RustProxy/raw/branch/master/install-debian13.sh | bash -s -- update ``` The bootstrap installs build dependencies, installs Rust with rustup for an unprivileged build user, clones `https://git.linuxiarz.pl/gru/RustProxy` and builds with `cargo build --release --locked`. Its default `auto` mode calls `install.sh` only when no managed installation exists. When `/opt/rustproxy/install.json` exists it calls `update.sh`, which uses the saved installation record, backs up private state, switches the release, verifies health/version and rolls code/data/unit back on failure. First install intentionally starts the admin listener on `0.0.0.0:9090` over plain HTTP using `--allow-remote-admin`; updates preserve the saved admin/listener settings. Firewall/private-network restriction is therefore required. The normal `install.sh` default remains loopback-only. Equivalent prerequisite commands are: ```bash apt install -y build-essential gcc git ca-certificates python3 pkg-config wget sudo python3-certbot-dns-rfc2136 python3-certbot-dns-cloudflare wget -qO- --https-only --secure-protocol=TLSv1_2 https://sh.rustup.rs | sh ``` ## Build as a normal user Requirements: stable Rust (minimum declared 1.88), Cargo, a C compiler/linker, Python 3.10+, Bash. Linux/systemd deployment also requires `systemctl`, `runuser` and a usable non-login service account (created by the installer when missing). The static TOML checker additionally needs Python 3.11+. No script downloads an executable or installs a compiler as root. ```bash cd RustProxy bash dev.sh doctor bash dev.sh check bash dev.sh test bash dev.sh release ``` The first build resolves and creates `Cargo.lock`; retain the real lockfile and binary hash for repeatable subsequent builds. There is no fabricated lockfile or precompiled binary in the archive. Set `--binary` explicitly when using a custom `CARGO_TARGET_DIR`. Review and trust the source and built binary before installation. ## First installation The normal first install also downloads `user-country-ipv4.csv` and `user-country-ipv6.csv` from the `sapics/ip-location-db` latest release over HTTPS and verifies each file against the publisher SHA-256 release before writing it privately to `DATA_DIR/geoip`. This enables country access/rate rules. Use `--skip-geoip` only when you intentionally do not want the database; allow-only country policies then fail closed for unresolved clients. Updates preserve the installed database. ```bash bash install.sh --plan --binary target/release/rust-proxy sudo bash install.sh --binary target/release/rust-proxy ``` Defaults: HTTP `0.0.0.0:80`, HTTPS `0.0.0.0:443`, administration `127.0.0.1:9090`, code `/opt/rustproxy`, all private state `/var/lib/rustproxy`, service account `rustproxy`. The initial page is a small informational response; no demonstration backend or sample application is started. The first database has no configured vhosts. Existing, nonempty data is refused rather than reset. The first install also writes `/etc/profile.d/rustproxy-path.sh`, which prepends the managed `PREFIX/current` directory (normally `/opt/rustproxy/current`) to `PATH` for new login shells. Open a new login shell, or source that profile explicitly, before expecting `rust-proxy` to resolve without its absolute path. HTTPS is enabled by default and starts with a **self-signed certificate**, not a publicly trusted certificate. For an explicit HTTP-only installation use: ```bash sudo bash install.sh --binary target/release/rust-proxy --no-https ``` Assign the intended certificate in the panel before exposing applications. Existing HTTP/3 defaults enable QUIC on TLS listeners: permit the corresponding UDP port when desired, or explicitly disable H3 in Listeners. Review firewall rules yourself; the installer does not change them. Public frontend TLS and administration TLS are independent settings. Generate the one-time setup capability directly from the installed release CLI: ```bash sudo -u rustproxy /opt/rustproxy/current/rust-proxy \ --data-dir /var/lib/rustproxy admin setup-token ``` This works before the first server start. After an administrator exists, setup tokens remain invalid. You can alternatively initialize the account interactively: ```bash sudo -u rustproxy /opt/rustproxy/current/rust-proxy \ --data-dir /var/lib/rustproxy admin init ``` For direct remote administration, first sign in on the default loopback listener and open **Settings -> Administration**. Enable HTTPS, choose the application-managed certificate or a certificate from the inventory, set the bind address (for example `0.0.0.0:9090`) and optionally add DNS names accepted by Host/Origin validation. Save and restart RustProxy. The persisted remote bind is accepted only while administration TLS is enabled. Keep the listener behind a firewall/private network; the certificate must be trusted by the browser if you want a clean HTTPS connection. An SSH tunnel remains available without exposing the listener directly: ```bash ssh -L 9090:127.0.0.1:9090 operator@your-server ``` Open `http://127.0.0.1:9090` on that workstation while the administration listener is still using its default HTTP mode. No default administrator/password is added. Plain-HTTP remote administration is possible with the explicit allow flag: ```bash sudo bash install.sh --binary target/release/rust-proxy \ --admin 0.0.0.0:9090 --allow-remote-admin ``` That plain-HTTP path has no transport confidentiality and should be limited to a trusted private network. Prefer the built-in administration HTTPS mode for a direct remote listener. Other installation switches: `--prefix`, `--data-dir`, `--user`, `--http`, `--https`, `--no-https`, `--admin`, `--allow-remote-admin`, `--sha256` (expected binary digest), `--no-start`. Paths must be dedicated, absolute, separate, non-nested and without spaces or symlink components. `--plan` reads the supplied binary but changes nothing; it does not prove port availability, compiler success or systemd readiness. ### Startup-owned listeners When HTTP, HTTPS or administration listen addresses are supplied explicitly by systemd, CLI arguments or the corresponding `RUST_PROXY_*_LISTEN` environment variables, RustProxy treats those exact binds as process-owned. Settings shows them as locked and configuration updates cannot remove, disable or rewrite them. Listeners not supplied at process start remain normal editable desired-state entries. ## ACME prerequisites Automatic renewal runs inside RustProxy, not a separately installed cron timer. Install `certbot`, the DNS plugins you use (`certbot-dns-cloudflare`, `certbot-dns-ovh`, `certbot-dns-rfc2136`), Python and OpenSSL on the server. HTTP-01 uses Certbot's webroot authenticator, not a competing standalone web server. Certbot and its plugins must be in the **same Python environment**. An optional root-owned virtual environment at `/opt/rustproxy-certbot` is on the service PATH; otherwise use `/usr/local/bin` or `/usr/bin`. Avoid an executable under an administrator's home directory: the unit uses `ProtectHome=true`. Check the effective service account's environment rather than only your shell: ```bash sudo -u rustproxy env PATH=/opt/rustproxy-certbot/bin:/usr/local/bin:/usr/bin:/bin \ python3 scripts/acme_provider.py --doctor ``` The provider scripts are embedded in the binary; the source file above is just a preflight command. The service owns its complete private `certs/acme` tree. Do not run a second Certbot renewal timer over those same profiles. See [renewal](RENEWAL.md). ### Using a separate system Certbot If another service already owns a certificate through the system Certbot, keep that lineage outside RustProxy's managed ACME profiles and create/use a **manual** RustProxy certificate id. A Certbot deploy hook can publish every successful renewal to all vhosts that already select that id: ```bash /usr/bin/rust-proxy tls deploy --id shared-cert \ --cert /etc/letsencrypt/live/example/fullchain.pem \ --key /etc/letsencrypt/live/example/privkey.pem ``` The command reads the Certbot files locally and sends them to the authenticated loopback `POST /api/certificates/deploy` endpoint. The running RustProxy service writes the private generation with its own filesystem identity and hot-publishes it for new public TLS/H2/H3 handshakes. No per-vhost edit is needed. If another local process instead replaces the exact files already referenced by RustProxy, run `/usr/bin/rust-proxy tls reload` (or authenticated `POST /api/certificates/reload`) afterward. If the replacement belongs to one configured manual certificate ID, automation may instead call `POST /api/certificates/{certificate_id}/reload`; the same endpoint accepts a scoped Certificate API Key ID + Bearer token and does not change the configuration revision. Administration TLS is startup-fixed and may still require a service restart. Optional environment overrides go in a root-owned `/etc/rustproxy/environment` (mode 0600); create the directory when needed. Restart after changing service environment values. Provider secrets remain in private profile files, not this file or the exported configuration. A systemd environment file is not a shell script. ## Update an installation created by install.sh Build and test the new release as a normal user first, then: ```bash bash update.sh --plan --binary target/release/rust-proxy sudo bash update.sh --binary target/release/rust-proxy ``` Before stopping the service, the updater fetches the current publisher SHA-256 files for the `user-country` GeoIP IPv4/IPv6 assets. Matching local files are left untouched; stale or missing assets are staged, verified and published only after the private-data backup exists. Use `--skip-geoip` only when intentionally accepting a stale/missing country database (for example during an upstream outage). Use the original `--prefix` for a non-default installation. Other startup/account/ data settings are read from root-owned `install.json`; update does not silently replace them with command-line defaults. Change intentional listener settings in RustProxy, or maintain the unit separately when needed. Update mode checks that `/etc/profile.d/rustproxy-path.sh` still matches the saved installation prefix, but deliberately does not rewrite it. A missing or changed file produces an explicit warning before service downtime; repair the profile separately if direct `rust-proxy` shell invocation is required. The updater verifies the supplied binary hash and runs only its `--version` as the service account. Immediately before stopping a RustProxy 0.14.0+ service it creates a private one-shot managed-update marker. During graceful shutdown the process forces hot objects into the enabled cold tier even when ordinary `cold_on_shutdown=false`; the marker is removed before backup. The managed unit allows up to 120 seconds for controlled stop. If cold storage is disabled, RAM-only cache cannot survive the process exit. The updater then stages an immutable release, stops the service and backs up the **entire** data directory. This includes SQLite and sidecars, certificates, account keys, provider credentials, Certbot renewal configs and internal relative symlinks. Code changes use an atomic `current` symlink switch. It then starts the new process and requires systemd activity, minimal local `/healthz` readiness, and the actual MainPID executable matching the staged, locally version-checked binary. The local health probe follows the persisted administration bind and HTTP/HTTPS mode (including the configured Host allowlist), rather than assuming the install-time plain-HTTP address. Version is no longer exposed by the anonymous health endpoint. The managed systemd unit is regenerated from the saved installation record and `daemon-reload` is run before startup. The previous unit is included in the backup and restored on rollback. Keep custom service policy outside this managed unit or reapply it after an update. A failed start/health check restores the previous release, private data **and** managed systemd unit. The reported error now includes the original update failure; recent service status/journal output is emitted before rollback when available. Failed-generation data is retained alongside the original data directory; the backup is retained under `/opt/rustproxy/backups`. A failed rollback health check is reported explicitly. Protect these directories: they contain private keys and credentials. They are never automatically pruned. Plan enough free space for the full backup, restored data, retained failed state and old/new executable releases. During development, if an older saved vhost/settings document is intentionally incompatible with the current format, use: ```bash sudo bash update.sh --reset-config --binary target/release/rust-proxy ``` This happens only after the backup is created. It deletes the saved desired configuration row so the new process creates current listener defaults; administrator account data, certificates, ACME/provider files and other private state are retained. Do not use this flag when the old vhost/settings configuration must be preserved. After a failed update, **do not run `install.sh`**: rollback leaves the previous installation installed. Fix the reported cause and rerun `update.sh` (optionally with `--reset-config` for this development-only incompatible-config case). An update interrupts traffic; zero-downtime deployment is not implemented. `--no-start` leaves the service stopped and cannot certify a healthy installation. Backup failure leaves the old code/data in place and attempts to restart a previously running service. Keep an independent off-host backup; local rollback is not a disaster-recovery plan. ## Existing source-tree installations from earlier archives Do not point a first installer at your old live data. To keep the current source- tree deployment, stop RustProxy and use the retained source updater: ```bash bash /path/to/new/RustProxy/update.sh --source-tree /path/to/old/RustProxy --stopped ``` This mode keeps the old `upgrade.sh` contract and does not install a systemd service or build a binary. Back up **all** private data and ACME profiles separately before starting it. See [migration](MIGRATION.md). To adopt the new service layout, make an explicit offline migration with ownership, absolute Certbot paths and port settings reviewed; do not merely move a Certbot account tree to another location. ## Server acceptance Check actual non-root HTTP/TLS/QUIC binds, certificate assignment, a real renewal validation (`Dry run`), backend failures, live traffic, Basic Auth over TLS, ACLs, WebSocket enable/disable and location isolation. Verify the backup can be restored on another isolated instance. Run `systemctl status rustproxy` and `journalctl -u rustproxy` after installation/update. Only then decide on rollout.