Security Hardening Guide
On this page
Security is a primary motivation of Praxis, not an afterthought. This guide covers the secure defaults and operational hardening for production deployments.
Default Security Posture
Praxis ships secure by default and fails closed on ambiguous configuration:
- A listener
addressis required; there is no implicit default, so a listener never binds to an interface you did not name. Bind to127.0.0.1rather than0.0.0.0when a listener should not be externally reachable. - TLS certificate verification is enabled by default for upstream connections.
- Admin endpoints are restricted to loopback; non-loopback
binding is a validation error unless
insecure_options.allow_public_adminis set. - A loopback admin listener rejects requests whose
Hostis not a loopback name, blocking DNS rebinding from the operator’s browser (see Admin DNS Rebinding). unsafe_code = "deny"in workspace lints; no unsafe Rust in the Praxis codebase.- Rustls for TLS (no OpenSSL, no C FFI in the TLS path).
- TLS certificate and key paths reject directory
traversal (
..). - Health check targets reject loopback, link-local, and cloud metadata addresses (SSRF protection).
- Upstream hostnames that resolve to private or
reserved addresses are refused at connection time on
both the TCP and HTTP data planes, so a DNS record
that rebinds after startup cannot steer traffic to
loopback, RFC 1918, or
169.254.169.254. Setinsecure_options.allow_private_upstreamswhen upstream DNS names legitimately resolve into private space. - Policy engine outbound calls (JWKS, token exchange,
CIBA backchannel) share the proxy’s sub-request
connector. Private DNS answers (loopback, RFC 1918,
link-local, cloud metadata, and CGNAT) are skipped;
calls with no public answer are refused. Resolution
happens once to prevent rebinding. Use
allow_private_idpfor an in-cluster provider. - Policy engine TLS verifies against the platform
trust store, including
SSL_CERT_FILEandSSL_CERT_DIR; certificate and hostname verification are always on. Clustertlssettings do not apply, so private-CA and mTLS providers are unsupported and cannot share cluster-TLS connections. - Root execution (UID 0) rejected by default.
- Supply chain audited via
cargo auditandcargo deny. - Reserved internal headers (
x-praxis-*and AI extension prefixesx-ext-protocol-*,x-ext-agent-*) are rejected from client requests, stripped before forwarding to backends, and stripped from backend responses before reaching clients. --dumpredacts credential injection literal values as[REDACTED]to prevent accidental secret exposure in config dumps.
Network Security
- Bind public-facing listeners to specific interfaces
rather than
0.0.0.0. - Place Praxis behind a firewall. Expose only the ports your listeners require.
- Use separate listeners for public traffic and internal admin or health-check endpoints.
- Restrict admin and metrics endpoints to internal networks or loopback addresses.
- Restrict admin endpoints (including KV store API) to internal networks or loopback addresses. The KV admin API allows runtime modification of routing and transformation data.
Admin DNS Rebinding
The admin API has no authentication; loopback binding is
its access control. A web page the operator visits can
rebind its own DNS name to 127.0.0.1, after which the
browser treats the admin API as same-origin and can read
/api/pipelines, /api/stats, and KV values, or send
PUT/DELETE to /api/log-level and /api/kv/*.
Every such request still carries the attacker’s name in
Host. When admin.address is a loopback address, every
admin route (including /healthy, /ready, and
/metrics) answers 421 Misdirected Request with
{"error":"misdirected request"} unless each Host
header (and any absolute-form request authority) is one
of:
- a loopback IP literal:
127.0.0.0/8,[::1], or IPv4-mapped loopback, with or without a port localhost, in any case, optionally with a trailing dot and a port
A request with no Host at all (HTTP/1.0) is served:
browsers always send Host, so only a client that
already reaches the socket directly can omit it. Probe
and scrape the admin port as 127.0.0.1, [::1], or
localhost; a local DNS alias for the admin address is
rejected.
The check is skipped when the admin listener binds a
non-loopback address (allow_public_admin), because
operators may legitimately reach it by DNS name. Such a
listener is still reachable through loopback on the same
host, so protect it with network controls rather than
relying on the bind address.
TLS Best Practices
- Set certificate and key file permissions to
0600, owned by the Praxis process user. - Use
min_version: tls13in TLS configuration. TLS 1.2 can be used if required, but TLS 1.0 and 1.1 are deprecated and Praxis will not negotiate them. - Rotate certificates before expiration. Single-cert listeners hot-reload certificates automatically (see tls.md). Multi-cert listeners require a restart.
- Use separate certificate entries with
server_namesfor multi-domain deployments (SNI routing). - Enable CRL checking for mTLS listeners by adding
crl_pathsto theclient_cablock. CRL paths reject directory traversal (..). See tls.md for configuration details.
Access Control
- IP ACLs: Use the
ip_aclfilter to restrict access by source IP. Use eitherallowordeny, not both (mutually exclusive). An allow-list implicitly denies all non-matching IPs. - Rate Limiting: Configure
rate_limitfilters to bound request volume per client or globally. Tune limits based on expected traffic patterns. - CORS: Use the
corsfilter with explicitallow_originsrather than wildcards. Restrictallow_methodsandallow_headersto what your application requires. - CSRF: Use the
csrffilter with explicittrusted_origins. Theenforce_percentagefield enables gradual rollout; enforcement sampling is randomized per-request to prevent attackers from predicting unenforced windows. - Connection limits: Set
max_connectionson listeners to cap concurrent connections. HTTP listeners reject excess requests with 503 andRetry-After; TCP listeners close immediately. - Path-based gating is not a boundary against
normalizing upstreams: filter conditions
(
when: { path_prefix: … }) androuterroute matches compare the raw request path. Praxis does not resolve dot-segments (/./,/../), collapse duplicate slashes (//), or percent-decode the path before matching, and it forwards the path to the upstream verbatim (this is deliberate — see the%2f///passthrough behavior). A request such as//adminor/%2e/adminwill therefore not match apath_prefix: /admingate, yet an upstream that normalizes the path may still treat it as/admin. Do not rely on path-prefix gating ofbasic_auth,ip_acl, orcsrfas the sole access control in front of a backend that normalizes paths. Prefer gating on a classifier-promotedx-praxis-*header (the “classify → route → branch” pattern), or terminate sensitive paths at the proxy.
Resource Limits
- Memory pressure: Set
runtime.max_memory_bytesto a process RSS ceiling. When exceeded, the proxy rejects new requests with 503 to prevent OOM. See configuration.md for details. - File descriptors: Praxis raises its open file
limit at startup and sheds requests with 503 before
descriptors run out. Set
downstream_keepalive_timeout_mson listeners so idle clients cannot pin descriptors, and see capacity-planning.md for sizing the limit and raising the hard limit. - Payload size: Set
body_limits.max_request_bytesandbody_limits.max_response_bytesto bound buffered payload sizes. Requests exceeding the limit receive 413.
Deployment
Container Security
- Run the container as a non-root user. The official
image uses a dedicated
praxisuser. - Mount the filesystem read-only where possible. Configuration and TLS materials can be mounted as read-only volumes.
- Drop all Linux capabilities except those required for binding to privileged ports (if needed).
- Use a minimal base image to reduce attack surface.
Kubernetes
- Set
runAsNonRoot: trueandreadOnlyRootFilesystem: truein the pod security context. - Use
NetworkPolicyto restrict traffic. - Store TLS certificates in Kubernetes
Secretobjects and mount them read-only. - Set resource limits to prevent resource exhaustion.
Insecure Configuration Options
The following options weaken security. Use them only in development:
verify: falseon upstream TLS: Disables certificate verification. Acceptable only for local development with self-signed certs.- Binding to
0.0.0.0: Exposes the listener on all interfaces. Use specific addresses in production. - Wildcard CORS origins (
"*"): Allows any origin. Use explicit origin lists in production. - Empty IP ACL allowlists: An empty allowlist permits all traffic. When possible, use the principle of least privilege and only allow access from the networks that require it.