Skip to content

Commit

Permalink
fix(s2n-tls-hyper): Add proper IPv6 address formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed Nov 26, 2024
1 parent 9877437 commit 8b8c2c2
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 2 deletions.
23 changes: 23 additions & 0 deletions bindings/rust/certs/cert_localhost_ipv6.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID2DCCAsCgAwIBAgIUT1hXpXZFhpbeq6UkbqlnOKAeUoowDQYJKoZIhvcNAQEL
BQAwbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24x
DzANBgNVBAoMBkFtYXpvbjEaMBgGA1UECwwRQW1hem9uV2ViU2VydmljZXMxEjAQ
BgNVBAMMCWxvY2FsaG9zdDAgFw0yNDExMjYwNDUyNDdaGA8yMTI0MTEwMjA0NTI0
N1owbDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24x
DzANBgNVBAoMBkFtYXpvbjEaMBgGA1UECwwRQW1hem9uV2ViU2VydmljZXMxEjAQ
BgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKE6vnPsBDz69ET25yWSikTchned2PvOzZ9zOd7jtQM7A504jHGMOoyXsZaLF+R2
6M4Fo/7sWQfPImbrfDELIGoZo9V/DbJoETgmEsE6rSj2poEF46ZbNWa0THBNko0O
q3zgpwPmUPtPTF3QHH0JC/Kbm7V9S4yVx4Qit7GSPUr9Cgj3MJF6MAZqi29LpgSK
uMQ1bf6T9t6F5hNH3A4T5dfjtjcQczDhbnkKCUFa8DbOR+5vecQQjI6vJoK3suIU
xS4U6rNRhLYL+grswpqVh2AXI3qsl12jhcer7hbRovRM8MQkDCPnLDb9Chm2zVWW
5HgkUbtsZrpldrWoVdNihqUCAwEAAaNwMG4wHQYDVR0OBBYEFE6+x/LP/KNspKSj
uOY77KG+u/eAMB8GA1UdIwQYMBaAFE6+x/LP/KNspKSjuOY77KG+u/eAMA8GA1Ud
EwEB/wQFMAMBAf8wGwYDVR0RBBQwEocQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
9w0BAQsFAAOCAQEAE//QpOTdTJfc2OM+/kilicGx/PiV3WjiqoaeQo6uUd7cD7wD
6JuXABtUSndfkxryeQzKQmq2KBedbQCOKnt/OeY0yMqCxbws3l3H+uwPwwAACIUn
3e5+RtotiQQBreSiHJJ6omFpd+cyvluZwZ20t3dhlgGU5NUN3zcHkdc8hGpaxtfJ
AaunssA40QcjQFQYI8ADTiQHW20rZcsVRKkwRkNVps/vMDpLBCyBp96xhTAtkoDH
Xs/Zi1bJiJ8xw3TkDeJFShpP+cQPYHI36qWqNjTei9eHrNX8sNFAdNZVyitoZ3/W
FrPdms/ivlvgQbWWB3EKxD+PsQXoYvjkGhMNmg==
-----END CERTIFICATE-----
13 changes: 13 additions & 0 deletions bindings/rust/certs/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ openssl req -x509 -newkey rsa:4096 -keyout key_rsa.pem -out cert_rsa.pem -sha256
# used for TLS 1.3 connections
echo "generating ec key and cert"
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -keyout key.pem -out cert.pem -sha256 -days 36500 -nodes -subj "/C=US/ST=AZ/L=Tempe/O=Amazon/OU=AmazonWebServices/CN=localhost"


# used for testing IPv6. includes the localhost IPV6 address as a SAN.
echo "generating localhost IPv6 key and cert"
openssl req -x509 \
-newkey rsa:2048 \
-keyout key_localhost_ipv6.pem \
-out cert_localhost_ipv6.pem \
-sha256 \
-days 36500 \
-nodes \
-subj "/C=US/ST=MA/L=Boston/O=Amazon/OU=AmazonWebServices/CN=localhost" \
-addext "subjectAltName = IP:0:0:0:0:0:0:0:1"
28 changes: 28 additions & 0 deletions bindings/rust/certs/key_localhost_ipv6.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQChOr5z7AQ8+vRE
9uclkopE3IZ3ndj7zs2fczne47UDOwOdOIxxjDqMl7GWixfkdujOBaP+7FkHzyJm
63wxCyBqGaPVfw2yaBE4JhLBOq0o9qaBBeOmWzVmtExwTZKNDqt84KcD5lD7T0xd
0Bx9CQvym5u1fUuMlceEIrexkj1K/QoI9zCRejAGaotvS6YEirjENW3+k/beheYT
R9wOE+XX47Y3EHMw4W55CglBWvA2zkfub3nEEIyOryaCt7LiFMUuFOqzUYS2C/oK
7MKalYdgFyN6rJddo4XHq+4W0aL0TPDEJAwj5yw2/QoZts1VluR4JFG7bGa6ZXa1
qFXTYoalAgMBAAECggEAOmyjnDUv+fsEbkM8UrCt+zMgZRMlkGYJvBiQpXTFYNTP
Q/c8aV8jzlOf7kocD9WJGjMQEO4LexlzwXDe8ZSzG8+Lv29JgtdUOhEN5ciB/CCZ
CJMeQee2S6/VLTLnAseIm/l6fB7HRLIhHbOuxx5ynmkF/TfYmyqhgH/mKeow3M2O
hhSigu1sIfVWAwMq7pNJ1+Dr6z4LzGVjJOBFHFt/UCSngT9NJehwF+VNU7UW2g98
buUEwT6qgsDQCWEjLORppWMQTvZOQnPrE/fBf7t/QCLZ42I0y155jrMYXJIx92Q4
dqlgWmAFliLayNd1+9lJPDMG9JmA+JAv4bfSHqLMRwKBgQDXv5yA29KWVtCkzF83
QHECOV5Fv5KE3eLP6CnZjOI7G3OqrTc/EEtvl+bXZBQa/97IAYpYVO2KDY/tVbrr
La1ArR7s/zN1OxHNcuSuL9ghD61nHTgQg9psPqvXqVD/lsMiUNu64GtU0Bw6yce5
yf508Y8vXtm0A+riEv2H5dKtEwKBgQC/Tz5cpYCLi1riL6IthQrp22ILS7AxhPXb
h6EBBAUR3P92Bq27ArFuBOqLobF1fAeBichC2oCxWc+BPwqauJM75PNSKsXgFb/N
W2oDVjXIBk1PRc8d6QUHpl4nxphzL1W1xur/f1bIDixZUltOQjMOx1lgLQ/wwRWT
ekYU0LYMZwKBgQDLT68tI3o04ITn+Av2ZkzYmrVDJz/s46g84nylnYUHzFvYyDja
vgFInS4VZiMoOl13vzPe/9GFmjg6oOJvg3DUFRCip++XFt407IOhvkZ/CWYQWNGf
hpGMFhccOVuyMCGdMfOPDLM4jpE7uTD03Oxkycp0Cn8/i72J4/l1WleJbwKBgQCE
QkiezFx+HK2MSdoZFi1hV6YEoSMCWSWPy8hnZ1wJ6XtDIYLiEw6PPR7ZwcNpsYGO
8K5eaakm8ywd8nNmW8yOT85YM/Hw5ZhgZJ56CBPOYWz5LQ3vY7VygHX/kbC7kTH0
Jb05PdPFIudOKT2ucN3TjcYgU4b9rr834gSpR1FUaQKBgQC4hCEX1kO/NIlJ1fBz
kHm0LKQT1ggMDRvQ+M5If7a8BqdvXdFN5V9No3DQseXMB1q3OxxbWtJwYiLOskjI
F2TC98TE9XnPaMABLB3nLBnPW2DHN5DS6A9ool48fV7nZeSUnuytgLPR3s1FrqEf
yc2ARE24uiZT4QhFoZY2TG2Vpw==
-----END PRIVATE KEY-----
17 changes: 15 additions & 2 deletions bindings/rust/s2n-tls-hyper/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,21 @@ where
return Box::pin(async move { Err(Error::InvalidScheme) });
}

// IPv6 addresses are enclosed in square brackets within the host of a URI:
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2
// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
//
// These square brackets aren't part of the domain itself, so they are trimmed off to ensure
// that the proper server name is provided to s2n-tls-tokio.
let mut domain = req.host().unwrap_or("");
if let Some(trimmed) = domain.strip_prefix('[') {
if let Some(trimmed) = trimmed.strip_suffix(']') {
domain = trimmed;
}
}
let domain = domain.to_owned();

let builder = self.conn_builder.clone();
let host = req.host().unwrap_or("").to_owned();
let call = self.http.call(req);
Box::pin(async move {
// `HttpsConnector` wraps an HTTP connector that also implements `Service<Uri>`.
Expand All @@ -130,7 +143,7 @@ where

let connector = TlsConnector::new(builder);
let tls = connector
.connect(&host, tcp)
.connect(&domain, tcp)
.await
.map_err(Error::TlsError)?;

Expand Down
49 changes: 49 additions & 0 deletions bindings/rust/s2n-tls-hyper/tests/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,52 @@ async fn error_matching() -> Result<(), Box<dyn Error + Send + Sync>> {
server_task.abort();
Ok(())
}

#[tokio::test]
async fn ipv6() -> Result<(), Box<dyn Error + Send + Sync>> {
let config = {
// The localhost IPv6 certificate contains ::1 in the SAN extension. s2n-tls will not
// successfully validate the certificate unless the sever name is properly formatted, and
// matches this identity.
let localhost_ipv6_cert: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../certs/cert_localhost_ipv6.pem"
));
let localhost_ipv6_key: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../certs/key_localhost_ipv6.pem"
));

let mut builder = config::Config::builder();
builder.load_pem(localhost_ipv6_cert, localhost_ipv6_key)?;
builder.trust_pem(localhost_ipv6_cert)?;
builder.build()?
};

// Listen for IPv6 connections.
let listener = TcpListener::bind("[::1]:0").await?;
let addr = listener.local_addr()?;

let mut tasks = tokio::task::JoinSet::new();
tasks.spawn(serve_echo(listener, config.clone()));

tasks.spawn(async move {
let connector = HttpsConnector::new(config);
let client: Client<_, Empty<Bytes>> =
Client::builder(TokioExecutor::new()).build(connector);

// Connect to the localhost IPv6 address. s2n-tls hostname verification should ensure that
// the certificate contains the `::1` identity (without square brackets).
let uri = Uri::from_str(format!("https://[::1]:{}", addr.port()).as_str())?;
let response = client.get(uri).await?;
assert_eq!(response.status(), 200);

Ok(())
});

while let Some(res) = tasks.join_next().await {
res.unwrap()?;
}

Ok(())
}

0 comments on commit 8b8c2c2

Please sign in to comment.