A simple reverse proxy for use with Hyper and Tokio
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyper-reverse-proxy/README.md

166 lines
6.4 KiB

5 years ago
7 years ago
# hyper-reverse-proxy
2 years ago
[![License][license-img]](LICENSE)
[![CI][ci-img]][ci-url]
[![docs][docs-img]][docs-url]
[![version][version-img]][version-url]
[license-img]: https://img.shields.io/crates/l/hyper-reverse-proxy.svg
[ci-img]: https://github.com/felipenoris/hyper-reverse-proxy/workflows/CI/badge.svg
2 years ago
[ci-url]: https://github.com/felipenoris/hyper-reverse-proxy/actions/workflows/main.yml
2 years ago
[docs-img]: https://docs.rs/hyper-reverse-proxy/badge.svg
[docs-url]: https://docs.rs/hyper-reverse-proxy
[version-img]: https://img.shields.io/crates/v/hyper-reverse-proxy.svg
[version-url]: https://crates.io/crates/hyper-reverse-proxy
7 years ago
5 years ago
A simple reverse proxy, to be used with [Hyper].
The implementation ensures that [Hop-by-hop headers] are stripped correctly in both directions,
and adds the client's IP address to a comma-space-separated list of forwarding addresses in the
`X-Forwarded-For` header.
7 years ago
5 years ago
The implementation is based on Go's [`httputil.ReverseProxy`].
7 years ago
[Hyper]: http://hyper.rs/
5 years ago
[Hop-by-hop headers]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
7 years ago
[`httputil.ReverseProxy`]: https://golang.org/pkg/net/http/httputil/#ReverseProxy
5 years ago
# Example
Add these dependencies to your `Cargo.toml` file.
5 years ago
```toml
5 years ago
[dependencies]
hyper-reverse-proxy = "?"
hyper = { version = "?", features = ["full"] }
tokio = { version = "?", features = ["full"] }
lazy_static = "?"
hyper-trust-dns = { version = "?", features = [
"rustls-http2",
"dnssec-ring",
"dns-over-https-rustls",
"rustls-webpki",
"https-only"
] }
5 years ago
```
The following example will set up a reverse proxy listening on `127.0.0.1:13900`,
and will proxy these calls:
* `"/target/first"` will be proxied to `http://127.0.0.1:13901`
2 years ago
5 years ago
* `"/target/second"` will be proxied to `http://127.0.0.1:13902`
2 years ago
5 years ago
* All other URLs will be handled by `debug_request` function, that will display request information.
```rust
5 years ago
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
2 years ago
use hyper::{Body, Request, Response, Server, StatusCode};
use hyper_reverse_proxy::ReverseProxy;
use hyper_trust_dns::{RustlsHttpsConnector, TrustDnsResolver};
2 years ago
use std::net::IpAddr;
use std::{convert::Infallible, net::SocketAddr};
5 years ago
lazy_static::lazy_static! {
static ref PROXY_CLIENT: ReverseProxy<RustlsHttpsConnector> = {
ReverseProxy::new(
hyper::Client::builder().build::<_, hyper::Body>(TrustDnsResolver::default().into_rustls_webpki_https_connector()),
)
};
}
fn debug_request(req: &Request<Body>) -> Result<Response<Body>, Infallible> {
5 years ago
let body_str = format!("{:?}", req);
2 years ago
Ok(Response::new(Body::from(body_str)))
5 years ago
}
2 years ago
async fn handle(client_ip: IpAddr, req: Request<Body>) -> Result<Response<Body>, Infallible> {
if req.uri().path().starts_with("/target/first") {
match PROXY_CLIENT.call(client_ip, "http://127.0.0.1:13901", req)
.await
{
Ok(response) => {
Ok(response)
},
Err(_error) => {
Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::empty())
.unwrap())},
2 years ago
}
} else if req.uri().path().starts_with("/target/second") {
match PROXY_CLIENT.call(client_ip, "http://127.0.0.1:13902", req)
.await
{
Ok(response) => Ok(response),
Err(_error) => Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::empty())
.unwrap()),
2 years ago
}
} else {
debug_request(&req)
2 years ago
}
}
2 years ago
#[tokio::main]
2 years ago
async fn main() {
let bind_addr = "127.0.0.1:8000";
let addr: SocketAddr = bind_addr.parse().expect("Could not parse ip:port.");
2 years ago
2 years ago
let make_svc = make_service_fn(|conn: &AddrStream| {
let remote_addr = conn.remote_addr().ip();
async move { Ok::<_, Infallible>(service_fn(move |req| handle(remote_addr, req))) }
5 years ago
});
2 years ago
let server = Server::bind(&addr).serve(make_svc);
2 years ago
5 years ago
println!("Running server on {:?}", addr);
2 years ago
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
5 years ago
}
```
2 years ago
### A word about Security
2 years ago
Handling outgoing requests can be a security nightmare. This crate does not control the client for the outgoing requests, as it needs to be supplied to the proxy call. The following chapters may give you an overview on how you can secure your client using the `hyper-trust-dns` crate.
> You can see them being used in the example.
2 years ago
#### HTTPS
You should use a secure transport in order to know who you are talking to and so you can trust the connection. By default `hyper-trust-dns` enables the feature flag `https-only` which will panic if you supply a transport scheme which isn't `https`. It is a healthy default as it's not only you needing to trust the source but also everyone else seeing the content on unsecure connections.
> ATTENTION: if you are running on a host with added certificates in your cert store, make sure to audit them in a interval, so neither old certificates nor malicious certificates are considered as valid by your client.
2 years ago
#### TLS 1.2
By default `tls 1.2` is disabled in favor of `tls 1.3`, because many parts of `tls 1.2` can be considered as attach friendly. As not yet all services support it `tls 1.2` can be enabled via the `rustls-tls-12` feature.
> ATTENTION: make sure to audit the services you connect to on an interval
2 years ago
#### DNSSEC
As dns queries and entries aren't "trustworthy" by default from a security standpoint. `DNSSEC` adds a new cryptographic layer for verification. To enable it use the `dnssec-ring` feature.
2 years ago
#### HTTP/2
By default only rustlss `http1` feature is enabled for dns queries. While `http/3` might be just around the corner. `http/2` support can be enabled using the `rustls-http2` feature.
2 years ago
#### DoT & DoH
DoT and DoH provide you with a secure transport between you and your dns.
2 years ago
By default none of them are enabled. If you would like to enabled them, you can do so using the features `doh` and `dot`.
Recommendations:
- If you need to monitor network activities in relation to accessed ports, use dot with the `dns-over-rustls` feature flag
- If you are out in the wild and have no need to monitor based on ports, doh with the `dns-over-https-rustls` feature flag as it will blend in with other `https` traffic
2 years ago
It is highly recommended to use one of them.
> Currently only includes dns queries as `esni` or `ech` is still in draft by the `ietf`