218 lines
14 KiB
Markdown
218 lines
14 KiB
Markdown
# Security model and limitations
|
|
|
|
This is a development source release with no executed Rust integration results in
|
|
the packaging environment. Do not expose it as a production security boundary until
|
|
the actual build, protocol tests, deployment review and an appropriate security
|
|
assessment have passed.
|
|
|
|
## Administration
|
|
|
|
Administration binds to loopback HTTP by default. Settings can enable a dedicated
|
|
administration HTTPS listener, choose the application-managed certificate or a
|
|
certificate from the inventory, persist a non-loopback bind and allow explicit DNS
|
|
Host/Origin names. Remote persisted binding is accepted only with administration TLS.
|
|
The normal installer, one-line bootstrap and reference service keep the plain-HTTP admin listener
|
|
on loopback by default. An explicit startup `--allow-remote-admin` can expose plain HTTP on a
|
|
non-loopback address; that path should remain behind a trusted firewall/private network boundary and
|
|
should be replaced by Administration HTTPS for direct remote use.
|
|
Passwords use salted Argon2id; password work has admission limits and runs off the
|
|
network I/O path. Setup requires local filesystem authority or the one-time random
|
|
capability. No default administrator/password is created.
|
|
|
|
Sessions have HttpOnly/SameSite=Strict cookies, idle and absolute expiries and
|
|
credential-generation revocation. Mutations require scheme-aware Host/Origin checks,
|
|
the request marker and a CSRF token. Local CLI reset changes the hash and invalidates
|
|
all existing sessions without editing SQLite manually. Administration HTTPS marks the
|
|
session cookie Secure; HTTP cannot. The admin certificate and bind changes are
|
|
restart-required. A dedicated metrics credential is separate from browser sessions.
|
|
|
|
The data directory and SQLite files are private. The local OS user with data-directory
|
|
access is a recovery authority and can create short-lived CLI sessions. Protect that
|
|
account, filesystem and backup copies; browser authentication does not defend against
|
|
a compromised process owner. `.env` is trusted shell code, never untrusted input.
|
|
|
|
## Backend and routing boundary
|
|
|
|
Only configured HTTP/HTTPS origins may be dialed. Absolute-form client targets and
|
|
CONNECT cannot turn the proxy into an unrestricted forward proxy. ACL is evaluated
|
|
before new TCP/TLS connections and before reuse of an existing upstream connection.
|
|
Host and route restrictions combine; route allow cannot override host deny.
|
|
Forwarded headers are rebuilt from socket identity by default. An explicit
|
|
trusted-proxy CIDR policy can accept a bounded existing chain and derive the client
|
|
IP for ACL/affinity; see INTEGRATIONS_040.md. Routing still uses the actual local
|
|
destination, never any forwarded address or host.
|
|
|
|
Private/loopback destinations require explicit approval. DNS answers pass the same
|
|
policy and optional per-backend CIDR restriction; an unsafe answer fails the entire
|
|
resolution. Validated results are used without a second DNS lookup. Special-address
|
|
blocks include common metadata/link-local targets and normalize mapped IPv6.
|
|
Listener self-target restrictions prevent simple loops and control-plane access by
|
|
default. A privileged administrator can deliberately set `accept_local_listener_risk=true`
|
|
on one backend; private/loopback destinations additionally require `allow_private=true`.
|
|
This is intended for consciously accepted local-listener use (including port 9090) and
|
|
can create a request loop or expose administration through a public vhost. It does not
|
|
relax metadata/link-local/special-address filtering or CIDR restrictions. Keep network
|
|
egress and frontend ACLs as defense in depth.
|
|
|
|
No backend `Location` redirect is followed internally. HTTPS transport encryption remains
|
|
available, but backend trust/hostname verification is off by default. Set `tls_verify: true`
|
|
for origins where certificate-chain and hostname authentication is required. With
|
|
verification disabled, a network attacker able to intercept that route may impersonate
|
|
the origin. Address/DNS restrictions still apply; the local-listener exception exists only
|
|
when explicitly accepted as above. Operator-supplied backend configuration is privileged
|
|
configuration, not a field that an unauthenticated caller can edit.
|
|
|
|
## TLS and tunnels
|
|
|
|
Private key files are size-bounded, regular files, owner-only on POSIX and opened
|
|
without following final-component symlinks. Named files cannot escape the certificate
|
|
directory by using path separators. Keep the entire directory tree private as well.
|
|
Keys and certificate name coverage are validated before publication. Generated
|
|
self-signed certificates are development material, not automatic public trust.
|
|
|
|
Existing TLS sessions/tunnels are not revoked by changing a certificate or ACL.
|
|
Policies apply to new requests and handshakes. Restart/stop the relevant process for
|
|
immediate termination until a scoped administrative close operation is implemented.
|
|
|
|
WebSocket payloads remain opaque byte streams. The proxy validates upgrade metadata,
|
|
not application-level authorization or message semantics. Frame/message limits on a
|
|
backend remain the backend's responsibility. Proxy buffers, tunnel/connection counts
|
|
and inactivity timers bound proxy-side resource usage.
|
|
|
|
Web Server PHP-FPM integration never forwards the client `Proxy` header as CGI `HTTP_PROXY`
|
|
(httpoxy protection), regardless of optional Web Service hardening. FastCGI response framing and
|
|
hop-by-hop headers are discarded and RustProxy owns downstream framing. PHP-FPM endpoints are
|
|
limited to Unix sockets or loopback TCP. Optional custom FastCGI parameters are validated static
|
|
operator values and cannot replace core CGI, `HTTP_*`, `PHP_VALUE` or `PHP_ADMIN_VALUE`; php.ini
|
|
overrides use a dedicated validated `PHP_VALUE` map. Configurable request-body, response-header
|
|
and stderr bounds keep the PHP connector resource usage explicit.
|
|
|
|
## Information and resource controls
|
|
|
|
Access logging does not collect credential headers, query values, cookies, bodies
|
|
or WebSocket payloads. Hostnames and paths are logged; never put secrets in them. Do not enable third-party verbose tracing or application logging without
|
|
reviewing their output separately. Access rings, viewer slots, auth work, updates,
|
|
DNS workers and request/connection admission are bounded. A slow log viewer does not
|
|
create an unbounded delivery queue.
|
|
|
|
Backups include TLS private keys and credential/session state. Restore matching
|
|
SQLite and certificate data together, then revoke restored sessions. Never commit
|
|
`.env`, the data directory, backups or generated certificates to a public repository.
|
|
|
|
## Not a completed security feature
|
|
|
|
Docker socket access controls, service-account metrics access,
|
|
2FA, roles, provider secret storage, certificate
|
|
expiry automation, a full special-address registry policy and independent fuzz/load
|
|
assessment remain roadmap work. Feature availability is not evidence of a completed
|
|
security audit or a performance guarantee.
|
|
|
|
|
|
## Cache, response transforms and ports (0.1.0)
|
|
|
|
Cache is enabled by default in 0.8.1 and ACLs precede lookup. Cookie (outside the
|
|
configured static-asset exception), Authorization, Range, unsafe preconditions and
|
|
request/response `no-store` remain hard shared-cache exclusions. Conditional validators
|
|
can be satisfied from a fresh object or revalidated against the origin. Response
|
|
`no-cache` with a usable validator may be retained only for mandatory validation before reuse. Origin privacy is
|
|
checked before custom header removal, and generated request IDs are not cached. Bounded
|
|
fill permits, object/entry/byte limits and generation checks protect cancellation,
|
|
update and purge paths. Purge is authenticated, CSRF-protected and audited, not a
|
|
public PURGE/BAN capability.
|
|
|
|
Compression is opt-in, with authenticated/private/no-transform exclusions. Response
|
|
rules cannot overwrite protected framing and identity fields. Required Age remains
|
|
on cache hits even when debug headers are disabled. Server hiding is cosmetic and
|
|
must not be considered an access-control or vulnerability mitigation substitute.
|
|
|
|
Standard public 80/443 ports are supported without globally forbidding remote
|
|
origins using those ports. Local listener targets remain guarded. Review SETTINGS.md
|
|
for interface-enumeration and restart semantics. A Linux service can grant only
|
|
CAP_NET_BIND_SERVICE to a dedicated unprivileged account; the application does not
|
|
acquire capabilities itself. Public listener controls do not implicitly expose the separate admin
|
|
API. The provided systemd unit is an unexecuted example, not an audit result.
|
|
|
|
Language packs are trusted build inputs with size/type/metadata bounds. The browser
|
|
renders strings as text and falls back per key. Translations are not executable
|
|
plugins, and no directory browsing or runtime arbitrary file loading is exposed.
|
|
|
|
## 0.2.0 frontend and pool boundaries
|
|
|
|
An explicit vhost binding is checked using the accepted stream's real local IP,
|
|
port and TLS state. Host, SNI and forwarded headers cannot assert a different local
|
|
address. A shared wildcard TCP listener does not grant every vhost access on every
|
|
accepted IP. Legacy routing does not inherit additional frontend sockets. Conflicting
|
|
hostname ownership is rejected, including disabled declarations.
|
|
|
|
Binding a private IP is not an authorization mechanism or firewall. NAT, port
|
|
forwarding or another proxy can make that destination reachable. Configure network
|
|
restrictions and application authentication as well; no trusted-proxy identity or
|
|
Keepalived/VRRP service is introduced. New unavailable IP bindings fail startup.
|
|
|
|
Health probes share literal/DNS/allowlist/loop protections, including actual old
|
|
listeners and desired new listeners, and cannot follow redirects to arbitrary
|
|
origins. They are bounded and do not use user cookies or credentials. Probes can
|
|
still hit an operator-selected GET path; select a side-effect-free health endpoint.
|
|
|
|
Affinity uses opaque random tokens but is not authentication, CSRF protection or
|
|
session replication. Tokens survive compatible edits, not process restarts. Cookie
|
|
mode bypasses cache; the dedicated cookie is stripped before origin forwarding and
|
|
cannot be overridden by an origin Set-Cookie. No failed request, especially a write,
|
|
is replayed on another member to obtain a successful response.
|
|
|
|
Dedicated per-vhost security headers also cover matching-host denials/errors and
|
|
cache hits. HSTS is removed on plaintext; includeSubDomains/preload remain explicit
|
|
opt-ins. Preload directives do not enroll a domain in a browser list. Arbitrary CSP
|
|
and Permissions-Policy values are encoding-validated, not semantically audited.
|
|
Old configurations retain origin headers unless an explicit policy overrides them.
|
|
|
|
## Certificate uploads and custom pages (0.3.0)
|
|
|
|
Certificate upload is an authenticated local-control-plane mutation protected by
|
|
Origin and CSRF validation, body/per-file bounds and two-worker admission. PEM pairs
|
|
are parsed, key-matched and checked against supplied DNS names. Private immutable
|
|
files use generated simple names, mode 600 and the existing no-symlink/directory
|
|
lock protections. Public API responses/config/audit contain metadata, not key PEM.
|
|
JavaScript clears file controls after use, but does not claim secure erasure of
|
|
browser memory. Only configuration publication activates an inventory selection.
|
|
No new listener or public certificate-upload endpoint is created.
|
|
|
|
Custom HTML is fully trusted administrator-authored content served on the chosen
|
|
public vhost. It may contain executable HTML/scripts there; it is never previewed
|
|
or inserted as HTML in the admin DOM. Do not upload untrusted markup. The fixed
|
|
welcome page interpolates only the software version, not Host, URL or user input.
|
|
HTML is bounded to 32 KiB per page and by the overall 256 KiB configuration limit.
|
|
Responses use no-store; old representation encoding/length/cookies are discarded
|
|
when replacing an origin error. Authentication challenges, Allow and Retry-After
|
|
are retained, then administrator response/security rules are applied.
|
|
|
|
ACLs run before static/fallback/direct-IP pages. Even a custom status 200 for a
|
|
blocked request never authorizes an upstream fetch. Such overrides can confuse
|
|
clients and monitoring; retaining the original 401/403/5xx status is recommended.
|
|
Direct-IP HTTPS still needs a completed TLS handshake and a suitable certificate;
|
|
an HTTP welcome page cannot remove a TLS certificate-name error.
|
|
|
|
## Integration boundaries in 0.4.0
|
|
|
|
Tinyauth runs after host/path ACLs and before any cached/static/origin response or
|
|
WebSocket upgrade. Incoming identity headers are stripped, including after generic
|
|
header rules, and only configured fields from a successful verifier are copied.
|
|
The verifier gets no request body or admin session cookie. Its DNS/private-network
|
|
and TLS checks match the origin safety boundary. Authentication failure cannot be
|
|
turned into a 200 by custom pages. Denials and protected responses are no-store;
|
|
there is no auth-result or protected-content shared cache. Existing upgraded
|
|
sessions are not reauthenticated continuously.
|
|
|
|
Only configured login-origin redirects are accepted. Do not expose the fake-auth
|
|
helper or treat its test cookie as an actual login. H3 uses the same routing,
|
|
auth, request limits and response policies; 0-RTT is disabled. Transport resource
|
|
limits do not replace rate limiting, firewalls or independent security/conformance
|
|
testing. H3 is uncompiled/unexecuted in this packaging environment.
|
|
|
|
Diagnostic response fields are hidden by default, but origin secrets still need
|
|
explicit response rules. In 0.5.0 the dedicated controls expose request ID and
|
|
revision independently; the obsolete hide_mock_origin field is not accepted. X-Mock-Origin can be
|
|
removed using ordinary response-header rules. Scope labels on listeners never
|
|
enforce network restrictions. See ACME_RFC2136.md for private TSIG/account storage
|
|
and the separate backup requirement for manual DNS-01 issuance.
|