10 releases

0.3.7 Sep 22, 2024
0.3.5 Jul 14, 2024
0.3.0 Mar 22, 2024
0.1.0 Dec 24, 2023

#236 in HTTP server

MIT license

41KB
940 lines

Firewall   LICENSE crates.io Version Documentation

The Firewall trait is meant to be used by servers to abstract the logic of blocking incoming requests.

Its accept method is provided an ip address (v4 or v6) and if the connection is over TLS, access to the server name from the SNI extension, and the client supported protocols from the ALPN extension.

The ClientHello trait is used to make the Firewall trait agnostic over the TLS implementation.


For servers who only need/want those 2 traits, the default features should be disabled.

Cargo.toml

[dependencies.firewall]
version = "0.1"
default-features = false

The rustls feature provides an implementation of the ClientHello trait for rustls.

The openssl feature provides an implementation of the ClientHello trait for openssl.


The builder feature provides an implementation of the Firewall trait.

let firewall = Firewall::default()
    .require_sni()
    .allow_server_name("example.com")
    .allow_ip_range("1.2.3.4/30");

You can have a list of allowed ip ranges, and a list of denied ip ranges (both ipv4 and ipv6).

You can also add an exception based on the TLS ClientHello content.

A good use case for this is if you want to renew Let's Encrypt certificates with the TLS-ALPN-01 challenge. Let's Encrypt doesn't provide a list of ips that they use to validate the challenges. You can add an exception to bypass the allow list if the acme-tls/1 protocol is listed in the TLS ALPN extension.

struct AcmeTlsSni01Exception {}

impl TlsAccept for AcmeTlsSni01Exception {
    fn accept(&self, client_hello: impl ClientHello) -> AcceptDenyOverride {
        if client_hello.has_alpn(b"acme-tls/1") {
            AcceptDenyOverride::AcceptAndBypassAllowList
        } else if client_hello.has_alpn(b"http/1.1") {
            AcceptDenyOverride::Accept
        } else {
            AcceptDenyOverride::Deny
        }
    }
}

let firewall = firewall
    .with_exception(AcmeTlsSni01Exception {});

The cloudflare feature adds a method on Firewall to apply the official allow list for Cloudflare servers.

let firewall = Firewall::default()
    .try_allow_cloudflare_ips()
    .await
    .unwrap();

This is useful if your server is behind the Cloudflare CDN and you don't want to allow any other server to contact your origin server directly.

There's a public fetch_cloudflare_ip_ranges() function available if you want to make sure that the list is up to date.


The github_webhook feature adds a method on Firewall to apply the official allow list for Github webhook servers.

let firewall = Firewall::default()
    .try_allow_github_webhook_ips()
    .await
    .unwrap();

There's a public fetch_github_webhook_ip_ranges() function available if you want to make sure that the list is up to date.

Dependencies

~0–10MB
~111K SLoC