lighthouse/common/warp_utils/src/cors.rs
Mac L 41b5af9b16 Support IPv6 in BN and VC HTTP APIs (#3104)
## Issue Addressed

#3103

## Proposed Changes

Parse `http-address` and `metrics-address` as `IpAddr` for both the beacon node and validator client to support IPv6 addresses.
Also adjusts parsing of CORS origins to allow for IPv6 addresses.

## Usage
You can now set  `http-address` and/or `metrics-address`  flags to IPv6 addresses.
For example, the following:
`lighthouse bn --http --http-address :: --metrics --metrics-address ::1`
will expose the beacon node HTTP server on `[::]` (equivalent of `0.0.0.0` in IPv4) and the metrics HTTP server on `localhost` (the equivalent of `127.0.0.1` in IPv4) 

The beacon node API can then be accessed by:
`curl "http://[server-ipv6-address]:5052/eth/v1/some_endpoint"`

And the metrics server api can be accessed by:
`curl "http://localhost:5054/metrics"` or by `curl "http://[::1]:5054/metrics"`

## Additional Info
On most Linux distributions the `v6only` flag is set to `false` by default (see the section for the `IPV6_V6ONLY` flag in https://www.man7.org/linux/man-pages/man7/ipv6.7.html) which means IPv4 connections will continue to function on a IPv6 address (providing it is appropriately mapped). This means that even if the Lighthouse API is running on `::` it is also possible to accept IPv4 connections.

However on Windows, this is not the case. The `v6only` flag is set to `true` so binding to `::` will only allow IPv6 connections.
2022-03-24 00:04:49 +00:00

83 lines
2.7 KiB
Rust

use std::net::IpAddr;
use warp::filters::cors::Builder;
/// Configure a `cors::Builder`.
///
/// If `allow_origin.is_none()` the `default_origin` is used.
pub fn set_builder_origins(
builder: Builder,
allow_origin: Option<&str>,
default_origin: (IpAddr, u16),
) -> Result<Builder, String> {
if let Some(allow_origin) = allow_origin {
let origins = allow_origin
.split(',')
.map(|s| verify_cors_origin_str(s).map(|_| s))
.collect::<Result<Vec<_>, _>>()?;
Ok(builder.allow_origins(origins))
} else {
let origin = match default_origin.0 {
IpAddr::V4(_) => format!("http://{}:{}", default_origin.0, default_origin.1),
IpAddr::V6(_) => format!("http://[{}]:{}", default_origin.0, default_origin.1),
};
verify_cors_origin_str(&origin)?;
Ok(builder.allow_origin(origin.as_str()))
}
}
/// Verify that `s` can be used as a CORS origin.
///
/// ## Notes
///
/// We need this function since `warp` will panic if provided an invalid origin. The verification
/// code is taken from here:
///
/// https://github.com/seanmonstar/warp/blob/3d1760c6ca35ce2d03dee0562259d0320e9face3/src/filters/cors.rs#L616
///
/// Ideally we should make a PR to `warp` to expose this behaviour, however we defer this for a
/// later time. The impact of a false-positive on this function is fairly limited, since only
/// trusted users should be setting CORS origins.
fn verify_cors_origin_str(s: &str) -> Result<(), String> {
// Always the wildcard origin.
if s == "*" {
return Ok(());
}
let mut parts = s.splitn(2, "://");
let scheme = parts
.next()
.ok_or_else(|| format!("{} is missing a scheme", s))?;
let rest = parts
.next()
.ok_or_else(|| format!("{} is missing the part following the scheme", s))?;
headers::Origin::try_from_parts(scheme, rest, None)
.map_err(|e| format!("Unable to parse {}: {}", s, e))
.map(|_| ())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn valid_origins() {
verify_cors_origin_str("*").unwrap();
verify_cors_origin_str("http://127.0.0.1").unwrap();
verify_cors_origin_str("http://localhost").unwrap();
verify_cors_origin_str("http://127.0.0.1:8000").unwrap();
verify_cors_origin_str("http://localhost:8000").unwrap();
verify_cors_origin_str("http://[::1]").unwrap();
verify_cors_origin_str("http://[::1]:8000").unwrap();
}
#[test]
fn invalid_origins() {
verify_cors_origin_str(".*").unwrap_err();
verify_cors_origin_str("127.0.0.1").unwrap_err();
verify_cors_origin_str("localhost").unwrap_err();
verify_cors_origin_str("[::1]").unwrap_err();
}
}