Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(s2n-tls-hyper): Add proper IPv6 address formatting #4938

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-----
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certificate content:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4f:58:57:a5:76:45:86:96:de:ab:a5:24:6e:a9:67:38:a0:1e:52:8a
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=MA, L=Boston, O=Amazon, OU=AmazonWebServices, CN=localhost
        Validity
            Not Before: Nov 26 04:52:47 2024 GMT
            Not After : Nov  2 04:52:47 2124 GMT
        Subject: C=US, ST=MA, L=Boston, O=Amazon, OU=AmazonWebServices, CN=localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ...
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                4E:BE:C7:F2:CF:FC:A3:6C:A4:A4:A3:B8:E6:3B:EC:A1:BE:BB:F7:80
            X509v3 Authority Key Identifier: 
                4E:BE:C7:F2:CF:FC:A3:6C:A4:A4:A3:B8:E6:3B:EC:A1:BE:BB:F7:80
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Alternative Name: 
                IP Address:0:0:0:0:0:0:0:1
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        ...

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.
goatgoose marked this conversation as resolved.
Show resolved Hide resolved
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.
goatgoose marked this conversation as resolved.
Show resolved Hide resolved
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(())
}
Loading