Security model
ReverseDock sits between the internet and your applications, so it treats every party differently. This page lists who is trusted with what, the defaults that hold each boundary, and what ReverseDock does not protect against. To report a vulnerability, follow the security policy; do not open a public issue.
Trust boundaries
Section titled “Trust boundaries”| Party | Trusted with | Not trusted with |
|---|---|---|
| Clients | nothing | their own forwarding headers, request IDs, framing, sizes, speed |
Trusted proxies (security.trusted_proxies) |
reporting the client address and the request ID | anything else in the request |
| Backends | the responses they produce | connection-level headers, a valid certificate unless verified |
| Admin API callers | reading state and asking for a reload, with the token | changing the configuration (there is no write API in v0.1) |
Whoever can write rdock.yaml and the files it names |
everything | — |
Clients
Section titled “Clients”Every client is untrusted. The defaults that protect the proxy and the backends:
- Forwarding headers are replaced. From a peer that is not a trusted proxy,
X-Forwarded-*,X-Real-IP,ForwardedandCF-Connecting-IPare removed, including spellings with_that some backends treat like-, and then written again from what ReverseDock saw. A client cannot claim another IP or scheme. - Request IDs are new.
X-Request-IDfrom a client is replaced by a fresh UUIDv7. - Request smuggling. Pingora’s parser rejects conflicting
Content-Length, invalidTransfer-Encodingand malformed authorities and re-frames every body it forwards. ReverseDock also refuses HTTP/1.1 requests withoutHost(400),Expectvalues other than100-continue(417) and bare-LF line endings in request headers. The end-to-end suite replays a catalogue of smuggling vectors, and a differential fuzz target checks forwarded framing. - Size limits. 64 KiB of request line and headers and 100 headers (431), 8 KiB request target (414). There is no body limit by default; set
security.limits.max_body_bytes(413, also for chunked bodies). - Slow clients. The request headers must arrive within 10 s of their first byte (
listeners.<name>.timeouts.request_header), which a client sending one byte at a time cannot stretch. Reads and writes wait at most 60 s, idle keep-alive connections close after 60 s, and the TLS handshake has the same 10 s deadline. - Paths. The path is normalised before routing and the normalised path is forwarded, so the backend sees what was routed;
%2Fnever acts as a separator. Path middlewares refuse results that normalisation would change (400). - Regular expressions in rules use the Rust
regexcrate, which runs in linear time, with a size limit when compiled. A request cannot make a rule slow. - Bounded memory.
rate_limitkeeps at most 65,536 keys per middleware, so many client addresses cannot grow it without limit.
Trusted proxies
Section titled “Trusted proxies”List in security.trusted_proxies only the load balancers, CDNs or tunnels you control. For requests from them, ReverseDock keeps the forwarding headers, appends the proxy to X-Forwarded-For, accepts a valid X-Request-ID, and takes the client IP from X-Forwarded-For by walking it from the right and skipping trusted addresses (at most 32 entries), from X-Real-IP with client_ip_header: x-real-ip, or from CF-Connecting-IP (see below).
That client IP is what ClientIP, ip_allow, ip_deny, rate_limit, maintenance.allow and the access log use. A trusted range that is too wide lets anyone inside it choose their own client IP, and with it pass an ip_allow list. trusted_proxies: [cloudflare] since v0.3 trusts Cloudflare’s published edge ranges, refreshed daily with a cached and a built-in fallback; CF-Connecting-IP is believed only from a trusted peer (with auto, only from a Cloudflare address) and removed otherwise. See Behind a proxy.
Backends
Section titled “Backends”- TLS to backends is verified when
scheme: https: the certificate must chain to a trusted root, or totls.ca_filewhen it is set, and match the backend’s name ortls.server_name.verify: falseturns this off for that service only, and you have to write it. - Connection headers are cleaned in both directions: Pingora removes hop-by-hop request headers, and ReverseDock removes them from responses.
- Errors do not leak. Proxy errors get ReverseDock’s own page, which shows the status and the request ID, not backend addresses or messages; the cause is in the access log’s
errorfield. forward_authfails closed. If the auth service cannot be reached the request is refused with 502, or 504 on timeout, never let through. Headers listed incopy_headersare removed from the client’s request before the auth service’s values are copied, so a client cannot forge an identity header.basic_authaccepts argon2id and bcrypt; Apache MD5 and SHA-1 hashes load with a warning, and plain-text passwords are refused.rdock hashcreates argon2id hashes.
Admin API
Section titled “Admin API”- It listens on its own socket,
127.0.0.1:2080by default, never on a proxy listener.rdock validatewarns whenadmin.listeneris not a loopback address and reports an error when it overlaps a listener. /healthz,/readyzand/metricsanswer without a token. Metrics include your route, service and certificate names, so keep the listener private./api/v1/statusand/api/v1/config/reloadneedAuthorization: Bearer <token>; anything else gets 401. The token comes fromRDOCK_ADMIN_TOKEN, or from$RDOCK_DATA_DIR/admin/token, which rdock creates with a random value on first start, readable by the owner only. The comparison runs in constant time.- The v0.1 API cannot change the configuration; it can only reload the file that is already there. Users, scoped tokens, audit logs and the dashboard are planned for v0.5.
Set admin.enabled: false if you do not need the admin listener; rdock reload, rdock status and rdock healthcheck then have nothing to talk to, and SIGHUP still reloads.
The configuration and its secrets
Section titled “The configuration and its secrets”Whoever can write rdock.yaml, or a certificate, key, user list or page it names, controls the proxy. Keep these files writable only by the operator, and mount them read-only in containers.
- Secrets stay references. Write
${file:/run/secrets/name}or${env:NAME}instead of a value. Files are read up to 64 KiB. Secret fields (certificates.<name>.key_passwordand the ACME tokens of v0.3) keep the reference in the loaded configuration and are resolved only by the component that uses them. See Secrets. - History never holds resolved values. The history entries and the last-known-good copy store the document as written, with references unresolved, in files readable by the owner only. When a value from a reference fails to parse, the error message does not quote it.
- Unknown keys are errors, so a misspelt security setting is reported instead of ignored.
- Logs redact. The access log replaces
Authorization,Proxy-Authorization,CookieandSet-Cookiewith***by default (observability.access_log.redact), logs only the length of the query string, and logs theRefererwithout its query. - Settings that cannot be honoured are refused. A setting planned for a later version makes
rdock runrefuse the configuration instead of running without it.
The process
Section titled “The process”- The official image runs as a non-root user on a distroless base, and the Compose example adds a read-only root filesystem,
cap_drop: [ALL]andno-new-privileges. The systemd unit usesDynamicUser=yeswith onlyCAP_NET_BIND_SERVICEand the systemd sandboxing options. - ReverseDock’s crates forbid
unsafecode. TLS uses rustls with the ring provider. - Release artifacts are signed and come with checksums, an SBOM and build provenance; see Verify a download.
Out of scope
Section titled “Out of scope”ReverseDock does not protect against these in v0.1. Plan for them elsewhere.
- Volumetric attacks. Limits and
rate_limitprotect against single abusive clients, not floods that fill the network link. Use your provider’s or a CDN’s protection. - Application attacks. There is no web application firewall: SQL injection, XSS and similar attacks reach the backend if its route allows the request.
- A compromised host or operator. Anyone with access to the configuration files, the data directory (admin token, history) or the process can control the proxy.
- A compromised trusted proxy. Its forwarding headers are believed by design.
- Backends you disabled verification for.
verify: falseaccepts any certificate. - Client-certificate authentication of users is not available, and client certificates to backends are planned for v0.4.