# Installing and updating RustProxy 0.27.2 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. Ready-to-use Debian packages are published separately at `https://rustproxy.linuxiarz.pl`. The Debian package-build repository and metadata are intentionally maintained outside this source tree. ## 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 keeps administration on `127.0.0.1:9090` over HTTP, matching the normal `install.sh` loopback-safe default. Updates preserve the saved admin/listener settings. For initial remote access use an SSH tunnel, then enable Administration HTTPS before persisting a non-loopback bind. Plain remote HTTP requires both an explicit non-loopback `RUSTPROXY_ADMIN_LISTEN` and `RUSTPROXY_ALLOW_REMOTE_ADMIN=1`, and should be limited to a trusted private network. Equivalent prerequisite commands are: ```bash apt install -y build-essential gcc git ca-certificates python3 python3-venv python3-pip pkg-config wget sudo certbot python3-certbot-dns-rfc2136 python3-certbot-dns-cloudflare python3-certbot-dns-ovh 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. RustProxy prefers a private Python environment at `/acme-python`. When that environment is missing or incomplete, the service creates it automatically and installs the pinned Certbot core plus the Cloudflare, OVH and RFC2136 plugins with `pip`. The application never executes `sudo` and never modifies distribution-owned Python site-packages. The host should provide Python 3 with either the standard `venv` module or `pip`; RustProxy can bootstrap `virtualenv` into its own data directory when a distribution splits `venv` into a separate package. OpenSSL remains a local certificate-inspection fallback. If the private environment cannot be created, RustProxy uses a runnable distribution `certbot` from `PATH` when one is available. If Python is absent (a possible layout on appliances such as OPNsense), or Python is present but no local environment can be created and no system Certbot exists, the Certificates panel shows ACME as unavailable. PEM upload and references to existing certificate files remain available. On the standard service installation the managed environment therefore lives at `/var/lib/rustproxy/acme-python`. Do not run a second Certbot renewal timer over RustProxy's `certs/acme` profiles. See [renewal](RENEWAL.md). A source-tree preflight can still inspect a distribution-provided fallback environment with: ```bash sudo -u rustproxy env PATH=/usr/local/bin:/usr/bin:/bin \ python3 scripts/acme_provider.py --doctor ``` The provider script is embedded in the RustProxy binary; this command is diagnostic only. ### 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 /opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy 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 `/opt/rustproxy/current/rust-proxy --data-dir /var/lib/rustproxy 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.