Files
RustProxy/docs/INSTALL.md
T
2026-10-05 09:30:21 +02:00

293 lines
15 KiB
Markdown

# Installing and updating RustProxy 0.25.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 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 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.