Skip to content

Commit 09ba40e

Browse files
authored
Add FIPS support to Hyper 1.0 Client (smithy-lang#3539)
## Description This does several things: 1. Upgrade to RusTLS 0.23 which enables FIPS support 2. Add smoke test of the clients. This revealed a bug where https URLs were not supported. This is technically a breaking change because I added `non_exhaustive` to the CryptoMode enum. <!--- Describe your changes in detail --> ## Testing New integration tests. I expect this to fail in CI since I'll need to update the build image to match. ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent d37ac94 commit 09ba40e

File tree

10 files changed

+168
-17
lines changed

10 files changed

+168
-17
lines changed

.cargo-deny-config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ confidence-threshold = 1.0
2323
exceptions = [
2424
{ allow = ["OpenSSL"], name = "ring", version = "*" },
2525
{ allow = ["OpenSSL"], name = "aws-lc-sys", version = "*" },
26+
{ allow = ["OpenSSL"], name = "aws-lc-fips-sys", version = "*" },
2627
]
2728

2829
[[licenses.clarify]]

.github/workflows/ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,24 +232,24 @@ jobs:
232232
- target: i686-unknown-linux-gnu
233233
build_smithy_rs_features: --all-features
234234
build_aws_exclude: ''
235-
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
235+
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
236236
test_smithy_rs_features: --all-features
237237
test_aws_exclude: ''
238-
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
238+
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
239239
- target: powerpc-unknown-linux-gnu
240240
build_smithy_rs_features: ''
241241
build_aws_exclude: --exclude aws-inlineable
242-
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
242+
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
243243
test_smithy_rs_features: ''
244244
test_aws_exclude: --exclude aws-inlineable
245245
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
246246
- target: powerpc64-unknown-linux-gnu
247247
build_smithy_rs_features: ''
248248
build_aws_exclude: --exclude aws-inlineable
249-
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
249+
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
250250
test_smithy_rs_features: ''
251251
test_aws_exclude: --exclude aws-inlineable
252-
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
252+
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
253253
env:
254254
CROSS_CONFIG: Cross.toml
255255
steps:

CHANGELOG.next.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,25 @@ message = "Users may now set service-specific configuration in the environment.
7070
references = ["smithy-rs#3493"]
7171
meta = { "breaking" = false, "tada" = true, "bug" = false }
7272
author = "Velfi"
73+
74+
[[smithy-rs]]
75+
message = "Fix bug in Hyper 1.0 support where https URLs returned an error"
76+
references = ["smithy-rs#3539"]
77+
meta = { "breaking" = false, "tada" = false, "bug" = false }
78+
author = "rcoh"
79+
80+
[[smithy-rs]]
81+
message = """Add FIPS support to our Hyper 1.0-based client. Customers can enable this mode by enabling the `crypto-aws-lc-fips` on `aws-smithy-experimental`. To construct a client using the new client, consult this [example](https://github.com/awslabs/aws-sdk-rust/blob/release-2024-03-29/sdk/s3/tests/hyper-10.rs).
82+
83+
Please note that support for Hyper 1.0 remains experimental."""
84+
references = ["smithy-rs#3539"]
85+
meta = { "breaking" = false, "tada" = true, "bug" = false }
86+
author = "rcoh"
87+
88+
[[aws-sdk-rust]]
89+
message = """Add FIPS support to our Hyper 1.0-based client. Customers can enable this mode by enabling the `crypto-aws-lc-fips` on `aws-smithy-experimental`. To construct a client using the new client, consult this [example](https://github.com/awslabs/aws-sdk-rust/blob/release-2024-03-29/sdk/s3/tests/hyper-10.rs).
90+
91+
Please note that support for Hyper 1.0 remains experimental."""
92+
references = ["smithy-rs#3539"]
93+
meta = { "breaking" = false, "tada" = true, "bug" = false }
94+
author = "rcoh"

rust-runtime/aws-smithy-experimental/Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-smithy-experimental"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
authors = ["AWS Rust SDK Team <[email protected]>"]
55
description = "Experiments for the smithy-rs ecosystem"
66
edition = "2021"
@@ -9,7 +9,8 @@ repository = "https://github.com/smithy-lang/smithy-rs"
99

1010
[features]
1111
crypto-ring = ["rustls/ring"]
12-
crypto-aws-lc = ["rustls/aws_lc_rs", "dep:fs_extra"]
12+
crypto-aws-lc = ["rustls/aws_lc_rs"]
13+
crypto-aws-lc-fips = ["rustls/fips"]
1314

1415
[dependencies]
1516
aws-smithy-types = { path = "../aws-smithy-types", features = ["http-body-1-x"] }
@@ -20,13 +21,12 @@ pin-project-lite = "0.2.13"
2021
hyper-util = "0.1.3"
2122
http = "1"
2223
tokio = "1"
23-
hyper-rustls = { version = "0.26", features = ["http2", "http1"] }
24-
rustls = { version = "0.22.2", default-features = false }
24+
hyper-rustls = { version = "0.27", features = ["http2", "http1", "native-tokio", "tls12"], default-features = false }
25+
rustls = { version = "0.23", default-features = false }
2526
h2 = "0.4"
2627
once_cell = "1.18.0"
2728
tracing = "0.1.40"
2829
tower = "0.4.1"
29-
fs_extra = { version = "1.3.0", optional = true } # hack for cargo-minimal-versions
3030

3131
[dev-dependencies]
3232
aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] }
@@ -40,7 +40,7 @@ doc-scrape-examples = true
4040

4141
[[example]]
4242
name = "client-aws-lc"
43-
required-features = ["crypto-aws-lc"]
43+
required-features = ["crypto-aws-lc", "crypto-aws-lc-fips"]
4444
doc-scrape-examples = true
4545

4646
[[example]]

rust-runtime/aws-smithy-experimental/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
Staging ground for experimental new features in the smithy-rs ecosystem.
44

55
### Hyper 1.0 Support
6-
This crate adds support for Hyper 1.0 (see [examples](./examples)). There a few blockers before stablization:
7-
1. Moving to `rustls` 0.23 to take advantage of FIPS support. This is blocked on `hyper-rustls` being upgraded.
8-
2. Expose an API for providing a custom connector. Currently that API is not exposed because a shim layer is needed to avoid taking a hard dependency on hyper-util.
9-
3. Add support for poisoning connections in the connection pool. This API needs to be either backported into hyper-util or we need to establish our own client.
6+
This crate allows customers to use Hyper 1.0. A valuable consequence of this is access to aws-lc-rs and its `FIPS` compliant crypto. This is available behind the `crypto-aws-lc-fips` feature. **Note**: FIPS support has somewhat [complex build requirements](https://github.com/aws/aws-lc/blob/main/BUILDING.md), namely CMake and Go.
7+
8+
## Crate Stabilization
109

10+
This crate adds support for Hyper 1.0 (see [examples](./examples)). There a few blockers before stablization:
11+
1. Expose an API for providing a custom connector. Currently, that API is not exposed because a shim layer is needed to avoid taking a hard dependency on hyper-util.
12+
2. Add support for poisoning connections in the connection pool. This API needs to be either backported into hyper-util or we need to establish our own client.
1113

1214
<!-- anchor_start:footer -->
1315
This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/smithy-lang/smithy-rs) code generator.

rust-runtime/aws-smithy-experimental/examples/client-aws-lc.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@
44
*/
55

66
use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder};
7+
#[tokio::main]
78

8-
fn main() {
9+
async fn main() {
10+
// feature = crypto-aws-lc
911
let _client = HyperClientBuilder::new()
1012
.crypto_mode(CryptoMode::AwsLc)
1113
.build_https();
14+
15+
// feature = crypto-aws-lc-fips
16+
// A FIPS client can also be created. Note that this has a more complex build environment required.
17+
let _client = HyperClientBuilder::new()
18+
.crypto_mode(CryptoMode::AwsLcFips)
19+
.build_https();
1220
}

rust-runtime/aws-smithy-experimental/src/hyper_1_0.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,14 @@ use aws_smithy_types::retry::ErrorKind;
4545

4646
use crate::hyper_1_0::timeout_middleware::{ConnectTimeout, HttpTimeoutError};
4747
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
48+
#[non_exhaustive]
4849
pub enum CryptoMode {
4950
#[cfg(feature = "crypto-ring")]
5051
Ring,
5152
#[cfg(feature = "crypto-aws-lc")]
5253
AwsLc,
54+
#[cfg(feature = "crypto-aws-lc-fips")]
55+
AwsLcFips,
5356
}
5457

5558
impl CryptoMode {
@@ -60,6 +63,16 @@ impl CryptoMode {
6063

6164
#[cfg(feature = "crypto-ring")]
6265
CryptoMode::Ring => rustls::crypto::ring::default_provider(),
66+
67+
#[cfg(feature = "crypto-aws-lc-fips")]
68+
CryptoMode::AwsLcFips => {
69+
let provider = rustls::crypto::default_fips_provider();
70+
assert!(
71+
provider.fips(),
72+
"FIPS was requested but the provider did not support FIPS"
73+
);
74+
provider
75+
}
6376
}
6477
}
6578
}
@@ -113,12 +126,21 @@ mod cached_connectors {
113126
HttpsConnector<HttpConnector>,
114127
> = once_cell::sync::Lazy::new(|| make_tls(GaiResolver::new(), CryptoMode::AwsLc.provider()));
115128

129+
#[cfg(feature = "crypto-aws-lc-fips")]
130+
pub(crate) static HTTPS_NATIVE_ROOTS_AWS_LC_FIPS: once_cell::sync::Lazy<
131+
HttpsConnector<HttpConnector>,
132+
> = once_cell::sync::Lazy::new(|| {
133+
make_tls(GaiResolver::new(), CryptoMode::AwsLcFips.provider())
134+
});
135+
116136
pub(super) fn cached_https(mode: Inner) -> hyper_rustls::HttpsConnector<HttpConnector> {
117137
match mode {
118138
#[cfg(feature = "crypto-ring")]
119139
Inner::Standard(CryptoMode::Ring) => HTTPS_NATIVE_ROOTS_RING.clone(),
120140
#[cfg(feature = "crypto-aws-lc")]
121141
Inner::Standard(CryptoMode::AwsLc) => HTTPS_NATIVE_ROOTS_AWS_LC.clone(),
142+
#[cfg(feature = "crypto-aws-lc-fips")]
143+
Inner::Standard(CryptoMode::AwsLcFips) => HTTPS_NATIVE_ROOTS_AWS_LC_FIPS.clone(),
122144
#[allow(unreachable_patterns)]
123145
Inner::Standard(_) => unreachable!("unexpected mode"),
124146
Inner::Custom(provider) => make_tls(GaiResolver::new(), provider),
@@ -169,6 +191,8 @@ mod build_connector {
169191
crypto_provider: CryptoProvider,
170192
) -> HttpsConnector<HttpConnector<R>> {
171193
use hyper_rustls::ConfigBuilderExt;
194+
let mut base_connector = HttpConnector::new_with_resolver(resolver);
195+
base_connector.enforce_http(false);
172196
hyper_rustls::HttpsConnectorBuilder::new()
173197
.with_tls_config(
174198
rustls::ClientConfig::builder_with_provider(Arc::new(restrict_ciphers(crypto_provider)))
@@ -180,7 +204,7 @@ mod build_connector {
180204
.https_or_http()
181205
.enable_http1()
182206
.enable_http2()
183-
.wrap_connector(HttpConnector::new_with_resolver(resolver))
207+
.wrap_connector(base_connector)
184208
}
185209

186210
pub(super) fn https_with_resolver<R: ResolveDns>(
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
use aws_smithy_async::time::SystemTimeSource;
7+
use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder};
8+
use aws_smithy_runtime_api::client::dns::{DnsFuture, ResolveDns, ResolveDnsError};
9+
use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnector, HttpConnectorSettings};
10+
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
11+
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
12+
use hyper_util::client::legacy::connect::dns::{GaiResolver, Name};
13+
use std::error::Error;
14+
use std::str::FromStr;
15+
use std::sync::Arc;
16+
use tower::Service;
17+
18+
#[cfg(feature = "crypto-ring")]
19+
#[tokio::test]
20+
async fn ring_client() {
21+
let client = HyperClientBuilder::new()
22+
.crypto_mode(CryptoMode::Ring)
23+
.build_https();
24+
smoke_test_client(&client).await.unwrap();
25+
}
26+
27+
#[cfg(feature = "crypto-aws-lc-fips")]
28+
#[tokio::test]
29+
async fn aws_lc_fips_client() {
30+
let client = HyperClientBuilder::new()
31+
.crypto_mode(CryptoMode::AwsLcFips)
32+
.build_https();
33+
smoke_test_client(&client).await.unwrap();
34+
}
35+
36+
#[cfg(feature = "crypto-aws-lc")]
37+
#[tokio::test]
38+
async fn aws_lc_client() {
39+
let client = HyperClientBuilder::new()
40+
.crypto_mode(CryptoMode::AwsLc)
41+
.build_https();
42+
smoke_test_client(&client).await.unwrap();
43+
}
44+
45+
#[cfg(feature = "crypto-ring")]
46+
#[tokio::test]
47+
async fn custom_dns_client() {
48+
use std::sync::atomic::{AtomicUsize, Ordering};
49+
#[derive(Debug, Clone)]
50+
struct PassThroughResolver {
51+
inner: GaiResolver,
52+
count: Arc<AtomicUsize>,
53+
}
54+
impl ResolveDns for PassThroughResolver {
55+
fn resolve_dns<'a>(&'a self, _name: &'a str) -> DnsFuture<'a> {
56+
let mut inner = self.inner.clone();
57+
let name = Name::from_str(_name).unwrap();
58+
let count = self.count.clone();
59+
DnsFuture::new(async move {
60+
count.fetch_add(1, Ordering::Relaxed);
61+
let result = inner
62+
.call(name)
63+
.await
64+
.map_err(|err| ResolveDnsError::new(err))?;
65+
Ok(result.map(|addr| addr.ip()).collect::<Vec<_>>())
66+
})
67+
}
68+
}
69+
let resolver = PassThroughResolver {
70+
inner: GaiResolver::new(),
71+
count: Default::default(),
72+
};
73+
let client = HyperClientBuilder::new()
74+
.crypto_mode(CryptoMode::Ring)
75+
.build_with_resolver(resolver.clone());
76+
smoke_test_client(&client).await.unwrap();
77+
assert_eq!(resolver.count.load(Ordering::Relaxed), 1);
78+
}
79+
80+
async fn smoke_test_client(client: &dyn HttpClient) -> Result<(), Box<dyn Error>> {
81+
let connector_settings = HttpConnectorSettings::builder().build();
82+
let runtime_components = RuntimeComponentsBuilder::for_tests()
83+
.with_time_source(Some(SystemTimeSource::new()))
84+
.build()
85+
.unwrap();
86+
let connector = client.http_connector(&connector_settings, &runtime_components);
87+
let _response = connector
88+
.call(HttpRequest::get("https://amazon.com").unwrap())
89+
.await?;
90+
Ok(())
91+
}

tools/ci-build/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ RUN set -eux; \
157157
gcc \
158158
git \
159159
glibc-langpack-en \
160+
go \
160161
java-11-amazon-corretto-headless \
161162
make \
162163
openssl-devel \

tools/ci-scripts/test-windows.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set -eu -o pipefail
88

99
exclusions=("--exclude" "aws-smithy-http-server-python" "--exclude" "aws-smithy-http-server-typescript" "--exclude" "aws-smithy-experimental")
1010
for runtime_path in "rust-runtime" "aws/rust-runtime"; do
11+
echo "testing $runtime_path"
1112
pushd "${runtime_path}" &>/dev/null
1213
# aws-smithy-http-server-python cannot be compiled on Windows since it uses the `signal-hook` crate
1314
# which is not really yet fully supported on the platform.
@@ -18,4 +19,5 @@ for runtime_path in "rust-runtime" "aws/rust-runtime"; do
1819
done
1920
# TODO(https://github.com/awslabs/aws-sdk-rust/issues/1117) We don't have a way to codegen the deps needed by the aws-config crate
2021
# (cd aws/rust-runtime/aws-config && cargo test --all-features) # aws-config is not part of the workspace so we have to test it separately
22+
echo "Testing isolated features of aws-smithy-experimental"
2123
(cd rust-runtime && cargo test -p aws-smithy-experimental --features crypto-ring) # only ring works on windows

0 commit comments

Comments
 (0)