rpxy

repository·develop·Indexed 20 days ago

https://github.com/junkurihara/rust-rpxy

A high-performance, lightweight Layer 7 reverse-proxy written in Rust. It supports HTTP/1.1, HTTP/2, HTTP/3, and gRPC, featuring TLS termination for multiple domains, Mutual TLS (mTLS), and automated certificate management via the TLS-ALPN-01 ACME protocol. Key capabilities include post-quantum key exchange (X25519MLKEM768), load balancing (round-robin, random, sticky), path-based routing with URL rewriting, and support for HAProxy PROXY Protocol (v1/v2).

Tokens
29.1K
Snippets
81
Records
134
Agent score
72%

What's inside rpxy

  1. Overview of rpxy features

    develop

    rpxy is a lightweight Layer 7 (HTTP) reverse-proxy written in Rust. It is designed to route multiple hostnames to backend application servers while handling TLS termination.

    Key features include:

    • Protocol Support: HTTP/1.1, HTTP/2, and HTTP/3 (via quinn or s2n-quic).
    • gRPC Support: Native support for gRPC traffic.
    • TLS/SSL: TLS termination for multiple domains, Mutual TLS (mTLS) with client certificates, and automated certificate management via the TLS-ALPN-01 ACME protocol.
    • Security: Post-quantum key exchange (X25519MLKEM768) and TLS connection sanitization to prevent domain fronting (ensuring SNI and HTTP HOST headers match).
    • Load Balancing: Round-robin, random, and sticky sessions.
    • Observability: Support for HAProxy PROXY Protocol (v1/v2) to recover client IPs behind L4 proxies.
  2. How rpxy-trusted-proxies resolves trusted proxies

    develop

    The rpxy-trusted-proxies crate is a helper used by rpxy to resolve trusted_forwarded_proxies entries. It accepts user-facing entries like CIDRs or built-in alias names and expands them into a resolved Vec<IpNet> for the main configuration parser.

    Key Behaviors:

    • Built-in Aliases: Supports aliases like cloudflare, fastly, and cloudfront which are expanded into concrete CIDR sets.
    • Deterministic Resolution: It uses static snapshots checked into the source code. It does not perform network fetches during configuration parsing or proxy startup, ensuring startup is deterministic and does not depend on external network reachability.
    • Separation of Concerns: Alias resolution is kept separate from the main proxy runtime to prevent provider-specific metadata from leaking into request handling code.
  3. Understand rpxy access logging behavior

    develop

    Access logging behavior depends on whether you are logging to a file or to stdout:

    ModeCondition for Access LogsBehavior
    File Mode (log_dir_path is Some)Always enabledAccess logs are written to a dedicated file (defined by ACCESS_LOG_FILE) independently of the RUST_LOG level.
    Stdio Mode (log_dir_path is None)RUST_LOG $\ge$ INFOAccess logs are interleaved with system logs on stdout. If RUST_LOG is set to WARN or ERROR, access logs will not be emitted.

    Access Log Format: In file mode, access logs use a high-performance minimal formatter with the shape: {timestamp} {message} .

  4. Perform path-based routing and URL rewriting

    develop

    You can route requests to different backends based on the URL path using the [[apps.<app_name>.reverse_proxy]] array. rpxy uses longest-prefix-matching to select the correct rule.

    • Path Matching: Use the path key to define the prefix to match.
    • URL Rewriting: Use the replace_path key to rewrite the matched portion of the path before sending it to the upstream.
    • Default Destination: A reverse_proxy entry without a path key acts as the default destination for that application. If no such entry exists, requests that don't match any defined paths are rejected.
    [[apps.app1.reverse_proxy]]
    path = '/path/another'
    replace_path = '/path'
    upstream = [{ location = 'another.backend.local' }]
  5. Update trusted proxy snapshots

    develop

    Because the crate uses static snapshots for provider IP ranges, you must explicitly refresh these snapshots to include the latest provider data. This is done using a dedicated binary that fetches IP lists from official provider endpoints and updates the source file src/snapshots.rs.

    Note: To run the updater, you must enable the update-snapshots feature, which pulls in additional dependencies (reqwest, chrono, serde, serde_json) not required by the standard runtime.

    Command:

    cargo run -p rpxy-trusted-proxies --features update-snapshots --bin update-snapshots -- [FLAGS]
    cargo run -p rpxy-trusted-proxies --features update-snapshots --bin update-snapshots --
  6. Convert Let's Encrypt private keys to PKCS8 format

    develop

    rpxy requires private keys to be in PKCS8 format. If you are using certificates and private keys obtained directly from Let's Encrypt (which are typically PKCS1-formatted), you must convert them using openssl before use.

    % openssl pkcs8 -topk8 -nocrypt \
        -in your_domain_from_le.key \
        -inform PEM \
        -out your_domain_pkcs8.key.pem \
        -outform PEM
  7. Terminate TLS for HTTPS traffic

    develop

    To serve HTTPS, specify a listen_port_tls (e.g., 443) globally. For each application, provide the paths to your PEM-encoded certificate and private key in the tls table.

    Requirements:

    • The private key must be in PKCS8 format.
    • On Unix-like systems, it is recommended to set private key file permissions to 0600. rpxy will emit a warn! log if permissions are too open.
    listen_port = 80
    listen_port_tls = 443
    
    [apps."app_name"]
    server_name = 'app1.example.com'
    tls = { tls_cert_path = 'server.crt',  tls_cert_key_path = 'server.key' }
    reverse_proxy = [{ upstream = [{ location = 'app1.local:8080' }] }]
  8. Manage rpxy via a web interface

    develop

    You can use the third-party project Gamerboy59/rpxy-webui to manage rpxy through a web interface.

    Warning: rpxy-webui is maintained independently. Recent rpxy releases may include breaking configuration changes (such as removed keys) that can cause rpxy-webui generated configurations to fail. Always verify compatibility before use.

  9. Automated Certificate Issuance via ACME (TLS-ALPN-01)

    develop

    You can automate certificate issuance and renewal using the ACME tls-alpn-01 protocol via the rustls-acme library. This requires the HTTPS port to be open to the public.

    Setup Steps:

    1. In your application config, set tls.acme = true and do not specify tls_cert_path or tls_cert_key_path.
    2. Define global ACME settings in the [experimental.acme] section.

    Security Note: On Unix-like systems, ACME-managed files (account keys, certificates, private keys) are created with mode 0600, and the cache directory is created with mode 0700.

    # Application configuration
    [apps.localhost_with_acme]
    server_name = 'example.org'
    reverse_proxy = [{ upstream = [{ location = 'example.com', tls = true }] }]
    tls = { https_redirection = true, acme = true }
    
    # Global ACME settings
    [experimental.acme]
    dir_url = "https://localhost:14000/dir"
    email = "test@example.com"
    registry_path = "./acme_registry"
  10. Build rpxy from source

    develop

    To build the rpxy executable binary, you must clone the repository, initialize submodules, and use cargo build.

    Note: Currently, building from source is supported on Linux, macOS, and some Unix-like OSes. For Windows, use the official Docker image.

    Default Build (HTTP/3 via quinn)

    This is the standard build which enables QUIC and HTTP/3 using the quinn library.

    Alternative Build (HTTP/3 via s2n-quic)

    If you prefer s2n-quic, you must disable default features and enable the http3-s2n feature. This build requires openssl to be installed on your system for the build process.

    # Cloning the git repository
    % git clone https://github.com/junkurihara/rust-rpxy
    % cd rust-rpxy
    
    # Update submodules
    % git submodule update --init
    
    # Build (default: QUIC and HTTP/3 is enabled using `quinn`)
    % cargo build --release
    
    # If you want to use `s2n-quic`, build as follows.
    % cargo build --no-default-features --features http3-s2n --release
  11. Configure a basic cleartext HTTP reverse proxy

    develop

    To set up a basic HTTP reverse proxy, configure listen_port globally and define applications under the [apps.<app_name>] table. rpxy routes requests based on the server_name matching the HOST header or the URL's request line.

    To host multiple domains on a single IP/port, create multiple [apps.<app_name>] entries. You can also define a default_app to handle requests that do not match any specific server_name. Note that default_app only works for HTTP; HTTPS requests for unknown server names are rejected.

    listen_port = 80
    
    [apps.app1]
    server_name = 'app1.example.com'
    reverse_proxy = [{ upstream = [{ location = 'app1.local:8080' }] }]