Files
RustProxy/README.md
T
2026-10-04 23:13:27 +02:00

22 KiB

RustProxy 0.25.4

Independent Rust reverse proxy and static Web Server with a dashboard, SQLite configuration, streaming HTTP/TLS/WebSocket forwarding, raw TCP/UDP stream hosts, IP-bound vhosts and backend pools.

0.25.4 release source.

Layer-4 streams

Raw TCP/UDP forwarding has its own Streams page in the primary navigation, alongside Vhosts, Redirects and 404 hosts. Streams use the same card/modal workflow as vhosts, but each stream owns a dedicated Layer-4 listener and one upstream and intentionally bypasses HTTP ACL/cache/header/compression logic. The page exposes live per-stream connections/sessions, rejects, errors, transferred bytes and RX/TX rate charts. Listener changes require a restart. See TCP/UDP streams.

Named HTTP/HTTPS listener bind addresses use the same chip-style editor as vhost hostnames, including multi-address listeners.

What changed in 0.25.4

  • Add authenticated restore from retained manual-configuration history. Selecting an older revision validates it through the normal configuration pipeline and publishes it as a new revision; history is never rewound or overwritten.
  • Protect restore with the same revision compare-and-swap, administrator session and CSRF controls as ordinary configuration writes, and record config.restore with source/new revision metadata in the administration audit.
  • Keep SQLite schema 8 and the existing 512-revision retention limit; upgrading from 0.25.3 requires no data migration.

The 0.25.3 method-policy, location ACL/country, SHA-256 request-ID and revision-history features remain unchanged. See host access, Custom locations, documentation index and the 0.25.4 release audit. Open follow-up work is tracked separately in TODO.

Documentation

Release history

Historical user/operator-visible changes are maintained in CHANGELOG.md. Detailed historical implementation notes are retained in docs/CHANGELOG_TECHNICAL_ARCHIVE.md, and completed roadmap milestones are retained in docs/ROADMAP_HISTORY.md.

Core capabilities and references

Managed certificates imported into the library renew automatically unless disabled. Initial issuance/import remains explicit and staging-first; imported PEM alone is not auto-managed. See renewal and recovery.

Read installation and update, host access and locations, migration, NPM migration, ACME providers, Prometheus, cache diagnosis, HTTP/3 scope, TCP/UDP streams and container deployment. Cloudflare Tunnel remains deferred. Docker discovery is available through the standalone agent; see Docker discovery. Examples are disposable full configurations: applying one replaces, not merges, an existing document.

Requirements and first build

Debian 13+ one-line install/update

The bootstrap installs the compiler/build dependencies and Rust, clones this repository and builds the locked release. With no argument it runs in auto mode: a host without a managed RustProxy installation is installed, while an existing /opt/rustproxy/install.json triggers the safe update.sh path with backup, health check and rollback. First install starts HTTP on 0.0.0.0:80, HTTPS on 0.0.0.0:443 and keeps the administration panel on loopback HTTP 127.0.0.1:9090. Updates preserve the saved listener and service settings. Use an SSH tunnel for initial remote access, then enable Administration HTTPS before exposing a non-loopback admin bind.

# automatic: install on a new host, update an existing managed installation
wget -qO- https://git.linuxiarz.pl/gru/RustProxy/raw/branch/master/install-debian13.sh | bash

# explicit update
wget -qO- https://git.linuxiarz.pl/gru/RustProxy/raw/branch/master/install-debian13.sh | bash -s -- update

Manual build prerequisites used by that bootstrap:

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

Rust stable (declared minimum 1.88), Cargo, C compiler/linker, Bash and Python 3.10+. Linux is the primary deployment target; macOS/WSL2 need normal native build tools. Detailed OS diagnostics currently use Linux procfs and cgroup v2. Unsupported measurements remain null/N/A.

The first build needs access to the Rust toolchain and crate registry. dev.sh never installs system software or runs sudo. Rustls handles runtime TLS; zstd's native dependency still needs a C build toolchain.

The Debian bootstrap intentionally builds with one Cargo job, LTO disabled and codegen-units=8. This avoids rustc being killed by the OOM killer on small VPSes while leaving the repository's normal release profile unchanged. For a manual low-memory build use:

CARGO_BUILD_JOBS=1 CARGO_PROFILE_RELEASE_LTO=false \
  CARGO_PROFILE_RELEASE_CODEGEN_UNITS=8 cargo build --release --locked

The 0.25.4 source archive preserves the resolved Cargo.lock supplied with the source tree and updates only this package's own version entry. This packaging environment has no Cargo, so the dependency graph was not re-resolved here. On the compiler-enabled deployment host, run the locked check/test/release gates below and retain the verified lockfile. dev.sh can generate a lockfile only when one is genuinely absent; it does not replace normal locked verification.

cd RustProxy
bash dev.sh doctor
bash dev.sh check
bash dev.sh test
bash dev.sh
Service Development default
Default HTTP http://127.0.0.1:8080
Default HTTPS https://localhost:8443
Administration (default) http://127.0.0.1:9090; HTTPS/remote bind is opt-in in Settings
Optional protocol fixture 127.0.0.1:9000, only with RUST_PROXY_DEMO=1

In a second terminal, print the one-time setup capability and use the browser, or initialize an administrator locally:

bash dev.sh admin setup-token
# Alternative: interactive account creation, no default credentials.
bash dev.sh admin init

There is no default username/password. Normal startup serves a neutral informational page for an unconfigured address and does not seed sample vhosts. The self-signed bootstrap certificate is retained under data/certs and is not publicly trusted. Assign intended certificates explicitly. Public frontend TLS does not implicitly expose the separate administration listener. Administration remains loopback HTTP by default; enable its own HTTPS setting before choosing a remote bind. Existing databases are never reset by this default.

Use the protocol fixture only explicitly when running the documented test suites; RUST_PROXY_DEMO=1 bash dev.sh seeds a new disposable database. The separate bash dev.sh mock helper remains available for cache/backend testing.

For a system service, build as an ordinary user and follow the complete procedure in INSTALL.md:

bash dev.sh release
bash install.sh --plan --binary target/release/rust-proxy
sudo bash install.sh --binary target/release/rust-proxy
# A subsequent release:
sudo bash update.sh --binary target/release/rust-proxy

Copy .env.example to .env for startup defaults; it is trusted shell code. RUST_PROXY_DEMO=0 disables the demo. RUST_PROXY_TLS=0 removes the startup TLS default used when bootstrapping listeners.

The release checker reads its compile-time defaults from repository-root UPDATE_CHECK.toml, which is embedded in the final binary. It checks the configured Cargo.toml URL immediately on startup and then every 3600 seconds by default. A failed request is ignored until the next interval; it never blocks startup or proxy traffic and it never installs updates. On Unix-like systems the checker keeps its bundled public roots and also imports certificates from SSL_CERT_FILE, CURL_CA_BUNDLE and common Linux/BSD CA bundles, including the FreeBSD/OPNsense ca_root_nss path. It follows at most five credential-free HTTP(S) redirects and never downgrades an HTTPS check to HTTP. Failed checks stay invisible in the dashboard, preserving the existing footer behavior, but are emitted as application ERROR events so the optional error-file sink can capture them. Operators may override the embedded values with global CLI options or equivalent environment variables:

--update-repository-url         RUST_PROXY_UPDATE_REPOSITORY_URL
--update-version-url            RUST_PROXY_UPDATE_VERSION_URL
--update-check-interval-secs    RUST_PROXY_UPDATE_CHECK_INTERVAL_SECS

Set the interval to 0 to disable automatic checks. Debian/systemd installs may place the environment overrides in /etc/rustproxy/environment; source/development runs may use the normal process environment.

Persisted frontend sockets live only in listeners.endpoints and are edited under Settings -> Listeners. Listener address/TLS/H2/H3 capability changes need a process restart; vhost H2/H3 switches only narrow those capabilities and routing/pool policies otherwise use validated hot publication.

Private vhosts and backend pools

Use Settings -> Listeners to configure frontend sockets, then use Vhosts to select those named listeners and configure backend members. examples/vhosts-private.json binds its named listener only to 127.0.0.2 and uses two explicit loopback origins. Replace these with addresses assigned to your machine and your real services. examples/vhosts-balanced.json shows an opt-in public 80/443 deployment; do not apply it unintentionally.

A private vhost is selected by hostname and the accepted socket's actual local IP, port and TLS state. Sending its Host/X-Forwarded-* values to a different IP does not select it. A shared wildcard socket may accept TCP at multiple addresses, but each vhost keeps its original routing constraints. IP binding is not a firewall or authentication: NAT/another proxy can still forward traffic to that private IP. Keep network restrictions and application authentication.

New health-checked members remain in checking until the configured successes make them eligible. No healthy member means 503; a healthy backup may be selected. HTTP checks measure time to response headers, not whole-body throughput. Affinity cookies bypass shared cache; they contain only an opaque routing token and are not login sessions. Existing cookie sessions may finish on a draining healthy member. The keepalive controls are connection reuse, not Keepalived/VRRP.

Save pool/routing/security changes live. New/removed sockets and TLS ALPN changes need a process restart; active vs desired endpoints are visible in Settings/API. Old top-level routes are converted once into vhosts during validated startup/import. There is no separate legacy routing UI or dispatcher; path-to-origin ownership is retained. Read the migration guide before updating an existing database. See VHOSTS.md for algorithms, defaults, bounds, caveats and the complete configuration contract.

Standard ports without running as root

Settings -> Listeners accepts multiple named endpoints and socket addresses such as 0.0.0.0:80, 0.0.0.0:443, [::]:443 and custom ports. Presets change only the port: they do not silently replace a loopback interface with a public one. The admin listener is independent of these controls and stays loopback by default. In Settings -> Administration, enabling HTTPS permits a persisted non-loopback bind using either the application-managed certificate or a selected certificate. Plain remote HTTP remains available only with the explicit --allow-remote-admin startup flag.

Linux low-port binding can require CAP_NET_BIND_SERVICE. Use a dedicated service account and the example in deploy/README.md, not a root-run application. Port conflicts and OS permissions cannot be proved by merely saving configuration; bind failures are reported on restart. See settings, defaults and rollback.

Cache and response controls

The shared cache is enabled by default. Normal fresh 200 GET and cacheable HEAD requests use s-maxage, max-age or Expires; a configurable default TTL applies only to explicit public responses without an origin freshness lifetime and defaults to zero. GET/HEAD share the same frontend scheme + Host + exact URL object, response Vary selects representation variants, and unrelated request headers or the selected backend-pool member do not fragment that object. Since 0.19.1 HTTP and HTTPS use distinct primary cache identities so scheme-dependent origin responses cannot cross protocols. Non-GET/HEAD methods pass without globally purging unrelated cached objects.

Cookies (except the configured static-asset exception), authorization, ranges, If-Match/If-Unmodified-Since, private/no-store responses, Set-Cookie, Vary: *, streaming events and failed/incomplete bodies are not reused. If-None-Match and If-Modified-Since can be answered from a fresh cache entry as local conditional HITs. Request no-cache/freshness constraints revalidate a stored validator instead of blanket-bypassing the cache. Response no-cache with a usable validator may be retained, but must validate before every reuse. Request/response no-store remains a hard exclusion.

Default cache memory limits are 256 MiB HOT allocated/charged data, 16 MiB per object, 64 MiB transient RAM, a 32 MiB COLD writer queue, 4096 HOT entries and eight fill permits. The panel also offers memory-aware conservative/recommended/performance presets derived from the lower of detected host RAM and a finite cgroup memory limit; automatic presets are disabled below 256 MiB effective RAM. max_object_bytes remains an independent per-object ceiling and cannot exceed the global HOT budget. If it is configured above the transient budget, the panel warns that objects near that size will pass through uncached because a fill cannot reserve enough bounded transient RAM. Optional cold cache defaults to disabled; when enabled it uses a bounded disk path under the private data directory and asynchronously demotes LRU/idle hot objects. Cold objects survive process restart for the same configuration revision and are promoted to RAM on a fresh hit. Large fill buffers grow incrementally and consume multiple permits. Actual process RSS also includes in-flight responses, compression buffers and runtime allocations. HIT/MISS/BYPASS, hit count and standardized RFC 9211 Cache-Status diagnostics are independently optional; required Age remains on cache hits. Optional X-Cache-Resident-Age tracks RustProxy residency and now survives revalidation/tier movement. Purge is authenticated, CSRF-protected, audited and invalidates both tiers plus older fills. See cache behavior and limits and cache diagnostics.

Response compression is streamed and negotiated with Accept-Encoding; enabled algorithms are gzip, Brotli, Zstandard and deflate. Privacy-sensitive, ranged, upgraded, pre-encoded and no-transform responses are excluded. Strong ETags are weakened after transformation, and obsolete content digests are removed. Server policy is applied after generic response rules. Protected framing/identity headers cannot be edited through generic rules.

With the separate demo enabled, /cache-demo returns an explicitly public 30-second object. Enable cache and X-Cache in Settings, then issue this twice without cookies:

curl -i -H 'Host: localhost' http://127.0.0.1:8080/cache-demo

The first eligible request is MISS, then HIT while fresh. The origin timestamp stays unchanged on HIT. Unlike proxy/system measurements, this endpoint is explicitly demo content. The reserved admin cookie is removed before eligibility; application cookies still trigger privacy bypass. Use anonymous curl requests to check cache behavior.

Adding a language

Create langs/de_DE.json with metadata and string translations, then rebuild and restart. No JavaScript language switch or registry edits are needed:

{
  "meta": {
    "locale": "de-DE", "name": "German", "native_name": "Deutsch", "direction": "ltr"
  },
  "translations": { "settings": "Einstellungen", "overview": "Ubersicht" }
}

Missing keys fall back to en_US.json. Translation strings are rendered as text, not HTML; locale and direction metadata also control formatting/RTL. The two bundled languages are English and Polish. New packs are compile-time assets, not untrusted runtime plugins. See langs/README.md.

Updating an installed source tree

Read MIGRATION.md. Back up actual data/certificates, stop the old server and extract this archive into a separate, non-nested directory. From the new source directory:

rust-proxy --data-dir /actual/RustProxy/data backup create \
  --output /private/rustproxy-before-upgrade.rpb
bash upgrade.sh /actual/RustProxy --stopped
# Also pass --data-dir /actual/private/data when it is external.
cd /actual/RustProxy
bash dev.sh test
bash dev.sh

The supported operator backup is the password-encrypted rust-proxy backup create command. scripts/backup.py is retained only as an offline release-test helper and must not be used as a Secret Store backup. See Secret Store and encrypted backup/restore.

.env, data, unmanaged files and previous managed sources are preserved. Extra language JSON files survive the update; shipped files replace their old names, whose previous bytes remain in the backup. Unknown custom source edits are not merged. Do not overlay individual source files onto an old tree.

RustProxy 0.25.3 upgrades SQLite to schema version 8 by adding retained manual-configuration history. Existing schema-7 data is migrated automatically; the current configuration is seeded as the first retained history row and future successful saves keep the newest 512 revisions. Vhost/location method and access-policy fields are additive and default to disabled/inherited behavior when omitted. Once a 0.25.3 configuration containing the new fields is saved, an older binary may reject unknown configuration fields. Roll back using the pre-upgrade data backup as well as the old source, not just the binary.

Documentation and checks

documentation index, API, architecture, security, transport, vhosts and pools, Web Server, settings, cache, roadmap, changelog, verification.

# Rust-independent checks:
python3 scripts/check_source.py
python3 scripts/release_smoke.py
# Runs those checks first, then the Rust suite when Cargo is available:
bash dev.sh test
# Source-only package, with no Git checkout required:
python3 make_zip.py /tmp/RustProxy-0.25.4.zip

New management and test-origin guide

See MANAGEMENT.md for the certificate staging workflow, page/status behavior and logs; MOCK_SERVER.md provides a ready-to-use cache/health test setup. The example examples/mock-vhost.json is a complete minimal document for a fresh test installation, not a merge patch.

Authentication and mock development

In Vhosts -> Authentication, enable Tinyauth and supply its base URL (or the complete /api/auth/nginx endpoint) and public login URL. Private verifiers need the explicit private-network permission; optionally constrain allowed networks. Default identity headers are Remote-User and Remote-Email. No embedded provider password or disabled TLS verification is required. Do not protect the login host with itself. See INTEGRATIONS_040.md for exact status/redirect behavior and limits.

# Separate terminals, after building the real proxy on a toolchain-enabled host:
bash dev.sh mock --port 9001 --name origin-a --ttl 30
bash dev.sh mock-auth --port 9300

The fake-auth server accepts the intentionally insecure cookie mock_auth=allow; it is a loopback-only development peer, never an authentication product.

Docker vhosts operational controls

Docker vhosts now publish/unpublish immediately per service. Each Docker agent has a configurable inventory polling interval, while the optional Live view refreshes cached runtime state only. Docker-backed applications can use ACL profiles/inline ACLs and per-vhost proxy connect/header/read/send timeouts, matching the ordinary vhost proxy controls.