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

Add FIPS support to Hyper 1.0 Client #3539

Merged
merged 8 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .cargo-deny-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ confidence-threshold = 1.0
exceptions = [
{ allow = ["OpenSSL"], name = "ring", version = "*" },
{ allow = ["OpenSSL"], name = "aws-lc-sys", version = "*" },
{ allow = ["OpenSSL"], name = "aws-lc-fips-sys", version = "*" },
]

[[licenses.clarify]]
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,24 +232,24 @@ jobs:
- target: i686-unknown-linux-gnu
build_smithy_rs_features: --all-features
build_aws_exclude: ''
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
test_smithy_rs_features: --all-features
test_aws_exclude: ''
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
- target: powerpc-unknown-linux-gnu
build_smithy_rs_features: ''
build_aws_exclude: --exclude aws-inlineable
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
test_smithy_rs_features: ''
test_aws_exclude: --exclude aws-inlineable
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
- target: powerpc64-unknown-linux-gnu
build_smithy_rs_features: ''
build_aws_exclude: --exclude aws-inlineable
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
test_smithy_rs_features: ''
test_aws_exclude: --exclude aws-inlineable
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript
test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript --exclude aws-smithy-experimental
env:
CROSS_CONFIG: Cross.toml
steps:
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,25 @@ message = "Users may now set service-specific configuration in the environment.
references = ["smithy-rs#3493"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "Velfi"

[[smithy-rs]]
message = "Fix bug in Hyper 1.0 support where https URLs returned an error"
references = ["smithy-rs#3539"]
meta = { "breaking" = false, "tada" = false, "bug" = false }
author = "rcoh"

[[smithy-rs]]
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).

Please note that support for Hyper 1.0 remains experimental."""
references = ["smithy-rs#3539"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "rcoh"

[[aws-sdk-rust]]
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).

Please note that support for Hyper 1.0 remains experimental."""
references = ["smithy-rs#3539"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "rcoh"
12 changes: 6 additions & 6 deletions rust-runtime/aws-smithy-experimental/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-smithy-experimental"
version = "0.1.0"
version = "0.1.1"
authors = ["AWS Rust SDK Team <[email protected]>"]
description = "Experiments for the smithy-rs ecosystem"
edition = "2021"
Expand All @@ -9,7 +9,8 @@ repository = "https://github.com/smithy-lang/smithy-rs"

[features]
crypto-ring = ["rustls/ring"]
crypto-aws-lc = ["rustls/aws_lc_rs", "dep:fs_extra"]
crypto-aws-lc = ["rustls/aws_lc_rs"]
crypto-aws-lc-fips = ["rustls/fips"]

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

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

[[example]]
name = "client-aws-lc"
required-features = ["crypto-aws-lc"]
required-features = ["crypto-aws-lc", "crypto-aws-lc-fips"]
doc-scrape-examples = true

[[example]]
Expand Down
10 changes: 6 additions & 4 deletions rust-runtime/aws-smithy-experimental/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
Staging ground for experimental new features in the smithy-rs ecosystem.

### Hyper 1.0 Support
This crate adds support for Hyper 1.0 (see [examples](./examples)). There a few blockers before stablization:
1. Moving to `rustls` 0.23 to take advantage of FIPS support. This is blocked on `hyper-rustls` being upgraded.
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.
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.
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.

## Crate Stabilization

This crate adds support for Hyper 1.0 (see [examples](./examples)). There a few blockers before stablization:
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.
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.

<!-- anchor_start:footer -->
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
*/

use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder};
#[tokio::main]

fn main() {
async fn main() {
// feature = crypto-aws-lc
let _client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::AwsLc)
.build_https();

// feature = crypto-aws-lc-fips
// A FIPS client can also be created. Note that this has a more complex build environment required.
let _client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::AwsLcFips)
.build_https();
}
26 changes: 25 additions & 1 deletion rust-runtime/aws-smithy-experimental/src/hyper_1_0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,14 @@ use aws_smithy_types::retry::ErrorKind;

use crate::hyper_1_0::timeout_middleware::{ConnectTimeout, HttpTimeoutError};
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[non_exhaustive]
pub enum CryptoMode {
#[cfg(feature = "crypto-ring")]
Ring,
#[cfg(feature = "crypto-aws-lc")]
AwsLc,
#[cfg(feature = "crypto-aws-lc-fips")]
AwsLcFips,
}

impl CryptoMode {
Expand All @@ -60,6 +63,16 @@ impl CryptoMode {

#[cfg(feature = "crypto-ring")]
CryptoMode::Ring => rustls::crypto::ring::default_provider(),

#[cfg(feature = "crypto-aws-lc-fips")]
CryptoMode::AwsLcFips => {
let provider = rustls::crypto::default_fips_provider();
assert!(
provider.fips(),
"FIPS was requested but the provider did not support FIPS"
);
provider
}
}
}
}
Expand Down Expand Up @@ -113,12 +126,21 @@ mod cached_connectors {
HttpsConnector<HttpConnector>,
> = once_cell::sync::Lazy::new(|| make_tls(GaiResolver::new(), CryptoMode::AwsLc.provider()));

#[cfg(feature = "crypto-aws-lc-fips")]
pub(crate) static HTTPS_NATIVE_ROOTS_AWS_LC_FIPS: once_cell::sync::Lazy<
HttpsConnector<HttpConnector>,
> = once_cell::sync::Lazy::new(|| {
make_tls(GaiResolver::new(), CryptoMode::AwsLcFips.provider())
});

pub(super) fn cached_https(mode: Inner) -> hyper_rustls::HttpsConnector<HttpConnector> {
match mode {
#[cfg(feature = "crypto-ring")]
Inner::Standard(CryptoMode::Ring) => HTTPS_NATIVE_ROOTS_RING.clone(),
#[cfg(feature = "crypto-aws-lc")]
Inner::Standard(CryptoMode::AwsLc) => HTTPS_NATIVE_ROOTS_AWS_LC.clone(),
#[cfg(feature = "crypto-aws-lc-fips")]
Inner::Standard(CryptoMode::AwsLcFips) => HTTPS_NATIVE_ROOTS_AWS_LC_FIPS.clone(),
#[allow(unreachable_patterns)]
Inner::Standard(_) => unreachable!("unexpected mode"),
Inner::Custom(provider) => make_tls(GaiResolver::new(), provider),
Expand Down Expand Up @@ -169,6 +191,8 @@ mod build_connector {
crypto_provider: CryptoProvider,
) -> HttpsConnector<HttpConnector<R>> {
use hyper_rustls::ConfigBuilderExt;
let mut base_connector = HttpConnector::new_with_resolver(resolver);
base_connector.enforce_http(false);
hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(
rustls::ClientConfig::builder_with_provider(Arc::new(restrict_ciphers(crypto_provider)))
Expand All @@ -180,7 +204,7 @@ mod build_connector {
.https_or_http()
.enable_http1()
.enable_http2()
.wrap_connector(HttpConnector::new_with_resolver(resolver))
.wrap_connector(base_connector)
}

pub(super) fn https_with_resolver<R: ResolveDns>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

use aws_smithy_async::time::SystemTimeSource;
use aws_smithy_experimental::hyper_1_0::{CryptoMode, HyperClientBuilder};
use aws_smithy_runtime_api::client::dns::{DnsFuture, ResolveDns, ResolveDnsError};
use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnector, HttpConnectorSettings};
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
use hyper_util::client::legacy::connect::dns::{GaiResolver, Name};
use std::error::Error;
use std::str::FromStr;
use std::sync::Arc;
use tower::Service;

#[cfg(feature = "crypto-ring")]
#[tokio::test]
async fn ring_client() {
let client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::Ring)
.build_https();
smoke_test_client(&client).await.unwrap();
}

#[cfg(feature = "crypto-aws-lc-fips")]
#[tokio::test]
async fn aws_lc_fips_client() {
let client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::AwsLcFips)
.build_https();
smoke_test_client(&client).await.unwrap();
}

#[cfg(feature = "crypto-aws-lc")]
#[tokio::test]
async fn aws_lc_client() {
let client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::AwsLc)
.build_https();
smoke_test_client(&client).await.unwrap();
}

#[cfg(feature = "crypto-ring")]
#[tokio::test]
async fn custom_dns_client() {
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug, Clone)]
struct PassThroughResolver {
inner: GaiResolver,
count: Arc<AtomicUsize>,
}
impl ResolveDns for PassThroughResolver {
fn resolve_dns<'a>(&'a self, _name: &'a str) -> DnsFuture<'a> {
let mut inner = self.inner.clone();
let name = Name::from_str(_name).unwrap();
let count = self.count.clone();
DnsFuture::new(async move {
count.fetch_add(1, Ordering::Relaxed);
let result = inner
.call(name)
.await
.map_err(|err| ResolveDnsError::new(err))?;
Ok(result.map(|addr| addr.ip()).collect::<Vec<_>>())
})
}
}
let resolver = PassThroughResolver {
inner: GaiResolver::new(),
count: Default::default(),
};
let client = HyperClientBuilder::new()
.crypto_mode(CryptoMode::Ring)
.build_with_resolver(resolver.clone());
smoke_test_client(&client).await.unwrap();
assert_eq!(resolver.count.load(Ordering::Relaxed), 1);
}

async fn smoke_test_client(client: &dyn HttpClient) -> Result<(), Box<dyn Error>> {
let connector_settings = HttpConnectorSettings::builder().build();
let runtime_components = RuntimeComponentsBuilder::for_tests()
.with_time_source(Some(SystemTimeSource::new()))
.build()
.unwrap();
let connector = client.http_connector(&connector_settings, &runtime_components);
let _response = connector
.call(HttpRequest::get("https://amazon.com").unwrap())
.await?;
Ok(())
}
1 change: 1 addition & 0 deletions tools/ci-build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ RUN set -eux; \
gcc \
git \
glibc-langpack-en \
go \
java-11-amazon-corretto-headless \
make \
openssl-devel \
Expand Down
2 changes: 2 additions & 0 deletions tools/ci-scripts/test-windows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set -eu -o pipefail

exclusions=("--exclude" "aws-smithy-http-server-python" "--exclude" "aws-smithy-http-server-typescript" "--exclude" "aws-smithy-experimental")
for runtime_path in "rust-runtime" "aws/rust-runtime"; do
echo "testing $runtime_path"
pushd "${runtime_path}" &>/dev/null
# aws-smithy-http-server-python cannot be compiled on Windows since it uses the `signal-hook` crate
# which is not really yet fully supported on the platform.
Expand All @@ -18,4 +19,5 @@ for runtime_path in "rust-runtime" "aws/rust-runtime"; do
done
# 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
# (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
echo "Testing isolated features of aws-smithy-experimental"
(cd rust-runtime && cargo test -p aws-smithy-experimental --features crypto-ring) # only ring works on windows