# Firewall   [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![crates.io Version](https://img.shields.io/crates/v/firewall.svg)](https://crates.io/crates/firewall) [![Documentation](https://docs.rs/firewall/badge.svg)](https://docs.rs/firewall) 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](https://en.wikipedia.org/wiki/Server_Name_Indication), and the client supported protocols from the [ALPN extension](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation). 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` ```toml [dependencies.firewall] version = "0.1" default-features = false ``` --- The `rustls` feature provides an implementation of the `ClientHello` trait for [rustls](https://crates.io/crates/rustls). The `openssl` feature provides an implementation of the `ClientHello` trait for [openssl](https://crates.io/crates/openssl). --- The `builder` feature provides an implementation of the `Firewall` trait. ```rust 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](https://letsencrypt.org/) 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](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation). ```rust 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. ```rust 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. ```rust 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.