mirror of https://github.com/smithy-lang/smithy-rs
TLS tests in CI (#2886)
## Motivation and Context This PR adds a CI workflow to verify the TLS configuration of the smithy-rs client. ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Signed-off-by: Daniele Ahmed <ahmeddan@amazon.de> Co-authored-by: Daniele Ahmed <ahmeddan@amazon.de>
This commit is contained in:
parent
5675a69d72
commit
61b675c00b
|
@ -0,0 +1,81 @@
|
|||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This workflow tests the TLS configuration of the smithy-rs client
|
||||
# To run on an Ubuntu machine, run each step in this order.
|
||||
# Each script can be run on your Ubuntu host.
|
||||
# You will have to install Docker and rustc/cargo manually.
|
||||
|
||||
env:
|
||||
rust_version: 1.68.2
|
||||
|
||||
name: Verify client TLS configuration
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
verify-tls-config:
|
||||
name: Verify TLS configuration
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc make python3-pip nginx git ruby openjdk-17-jre pkg-config libssl-dev faketime
|
||||
pip3 install certbuilder crlbuilder
|
||||
- name: Stop nginx
|
||||
run: sudo systemctl stop nginx
|
||||
- name: Checkout smithy-rs
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ./smithy-rs
|
||||
- name: Checkout trytls
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ouspg/trytls
|
||||
path: ./trytls
|
||||
- name: Checkout badtls
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: wbond/badtls.io
|
||||
path: ./badtls.io
|
||||
- name: Checkout badssl
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: chromium/badssl.com
|
||||
path: ./badssl.com
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ env.rust_version }}
|
||||
- name: Build badssl.com
|
||||
shell: bash
|
||||
working-directory: badssl.com
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
run: ../smithy-rs/tools/ci-scripts/configure-tls/configure-badssl
|
||||
- name: Build SDK
|
||||
working-directory: smithy-rs
|
||||
run: ./gradlew :aws:sdk:assemble -Paws.services=+sts,+sso
|
||||
- name: Build trytls
|
||||
shell: bash
|
||||
working-directory: trytls
|
||||
run: ../smithy-rs/tools/ci-scripts/configure-tls/configure-trytls
|
||||
- name: Build badtls.io
|
||||
working-directory: badtls.io
|
||||
shell: bash
|
||||
run: ../smithy-rs/tools/ci-scripts/configure-tls/configure-badtls
|
||||
- name: Update TLS configuration
|
||||
shell: bash
|
||||
run: smithy-rs/tools/ci-scripts/configure-tls/update-certs
|
||||
- name: Build TLS stub
|
||||
working-directory: smithy-rs/tools/ci-resources/tls-stub
|
||||
shell: bash
|
||||
run: cargo build
|
||||
- name: Test TLS configuration
|
||||
working-directory: smithy-rs/tools
|
||||
shell: bash
|
||||
run: trytls https target/debug/stub
|
4
ci.mk
4
ci.mk
|
@ -131,3 +131,7 @@ check-semver:
|
|||
.PHONY: generate-smithy-rs-release
|
||||
generate-smithy-rs-release:
|
||||
$(CI_ACTION) $@ $(ARGS)
|
||||
|
||||
.PHONY: verify-tls-config
|
||||
verify-tls-config:
|
||||
$(CI_ACTION) $@ $(ARGS)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
[package]
|
||||
name = "stub"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
aws-config = {path = "../../../aws/sdk/build/aws-sdk/sdk/aws-config", features = ["client-hyper"] }
|
||||
aws-credential-types = { path = "../../../aws/sdk/build/aws-sdk/sdk/aws-credential-types", features = ["hardcoded-credentials"] }
|
||||
aws-sdk-sts = { path = "../../../aws/sdk/build/aws-sdk/sdk/sts" }
|
||||
aws-smithy-client = { path = "../../../aws/sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["client-hyper", "rustls"] }
|
||||
exitcode = "1"
|
||||
hyper-rustls = { version = "0.24", features = ["rustls-native-certs", "http2"] }
|
||||
rustls = "0.21"
|
||||
rustls-native-certs = "0.6"
|
||||
rustls-pemfile = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
x509-parser = "0.15"
|
|
@ -0,0 +1,8 @@
|
|||
# TLS Stub
|
||||
|
||||
This package is used to verify the client's TLS configuration.
|
||||
|
||||
It is used in a CI test. See `ci-tls.yml`, "Verify client TLS configuration".
|
||||
|
||||
The stub loads a root certificate authority and uses it to connect to a supplied port on localhost.
|
||||
`trytls` reads the output on the console and uses the exit code of the stub to pass or fail a test case.
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::time::Duration;
|
||||
|
||||
use aws_config::timeout::TimeoutConfig;
|
||||
use aws_credential_types::Credentials;
|
||||
use aws_sdk_sts::error::SdkError;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use x509_parser::prelude::*;
|
||||
|
||||
const OPERATION_TIMEOUT: u64 = 5;
|
||||
|
||||
fn unsupported() {
|
||||
println!("UNSUPPORTED");
|
||||
std::process::exit(exitcode::OK);
|
||||
}
|
||||
|
||||
fn get_credentials() -> Credentials {
|
||||
Credentials::from_keys(
|
||||
"AKIAIOSFODNN7EXAMPLE",
|
||||
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn debug_cert(cert: &[u8]) {
|
||||
let x509 = X509Certificate::from_der(cert).unwrap();
|
||||
let subject = x509.1.subject();
|
||||
let serial = x509.1.raw_serial_as_string();
|
||||
println!("Adding root CA: {subject} ({serial})");
|
||||
}
|
||||
|
||||
fn add_cert_to_store(cert: &[u8], store: &mut rustls::RootCertStore) {
|
||||
let cert = rustls::Certificate(cert.to_vec());
|
||||
#[cfg(debug_assertions)]
|
||||
debug_cert(&cert.0);
|
||||
if let Err(e) = store.add(&cert) {
|
||||
println!("Error adding root certificate: {e}");
|
||||
unsupported();
|
||||
}
|
||||
}
|
||||
|
||||
fn load_ca_bundle(filename: &String, roots: &mut rustls::RootCertStore) {
|
||||
match File::open(filename) {
|
||||
Ok(f) => {
|
||||
let mut f = BufReader::new(f);
|
||||
match rustls_pemfile::certs(&mut f) {
|
||||
Ok(certs) => {
|
||||
for cert in certs {
|
||||
add_cert_to_store(&cert, roots);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error reading PEM file: {e}");
|
||||
unsupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error opening file '{filename}': {e}");
|
||||
unsupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_native_certs(roots: &mut rustls::RootCertStore) {
|
||||
let certs = rustls_native_certs::load_native_certs();
|
||||
if let Err(ref e) = certs {
|
||||
println!("Error reading native certificates: {e}");
|
||||
unsupported();
|
||||
}
|
||||
for cert in certs.unwrap() {
|
||||
add_cert_to_store(&cert.0, roots);
|
||||
}
|
||||
let mut pem_ca_cert = b"\
|
||||
-----BEGIN CERTIFICATE-----
|
||||
-----END CERTIFICATE-----\
|
||||
" as &[u8];
|
||||
let certs = rustls_pemfile::certs(&mut pem_ca_cert).unwrap();
|
||||
for cert in certs {
|
||||
add_cert_to_store(&cert, roots);
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_client(
|
||||
roots: rustls::RootCertStore,
|
||||
host: &String,
|
||||
port: &String,
|
||||
) -> aws_sdk_sts::Client {
|
||||
let credentials = get_credentials();
|
||||
let tls_config = rustls::client::ClientConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_safe_default_protocol_versions()
|
||||
.unwrap()
|
||||
.with_root_certificates(roots)
|
||||
.with_no_client_auth();
|
||||
let https_connector = hyper_rustls::HttpsConnectorBuilder::new()
|
||||
.with_tls_config(tls_config)
|
||||
.https_only()
|
||||
.enable_http1()
|
||||
.enable_http2()
|
||||
.build();
|
||||
let smithy_connector = aws_smithy_client::hyper_ext::Adapter::builder().build(https_connector);
|
||||
let sdk_config = aws_config::from_env()
|
||||
.http_connector(smithy_connector)
|
||||
.credentials_provider(credentials)
|
||||
.region("us-nether-1")
|
||||
.endpoint_url(format!("https://{host}:{port}"))
|
||||
.timeout_config(
|
||||
TimeoutConfig::builder()
|
||||
.operation_timeout(Duration::from_secs(OPERATION_TIMEOUT))
|
||||
.build(),
|
||||
)
|
||||
.load()
|
||||
.await;
|
||||
aws_sdk_sts::Client::new(&sdk_config)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), aws_sdk_sts::Error> {
|
||||
let argv: Vec<String> = env::args().collect();
|
||||
if argv.len() < 3 || argv.len() > 4 {
|
||||
eprintln!("Syntax: {} <hostname> <port> [ca-file]", argv[0]);
|
||||
std::process::exit(exitcode::USAGE);
|
||||
}
|
||||
let mut roots = rustls::RootCertStore::empty();
|
||||
if argv.len() == 4 {
|
||||
print!(
|
||||
"Connecting to https://{}:{} with root CA bundle from {}: ",
|
||||
&argv[1], &argv[2], &argv[3]
|
||||
);
|
||||
load_ca_bundle(&argv[3], &mut roots);
|
||||
} else {
|
||||
print!(
|
||||
"Connecting to https://{}:{} with native roots: ",
|
||||
&argv[1], &argv[2]
|
||||
);
|
||||
load_native_certs(&mut roots);
|
||||
}
|
||||
let sts_client = create_client(roots, &argv[1], &argv[2]).await;
|
||||
match sts_client.get_caller_identity().send().await {
|
||||
Ok(_) => println!("\nACCEPT"),
|
||||
Err(SdkError::DispatchFailure(e)) => println!("{e:?}\nREJECT"),
|
||||
Err(SdkError::ServiceError(e)) => println!("{e:?}\nACCEPT"),
|
||||
Err(e) => {
|
||||
println!("Unexpected error: {e:#?}");
|
||||
std::process::exit(exitcode::SOFTWARE);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
perl -p -i -e 's/ruby2\.4/ruby2.6/' Dockerfile
|
||||
grep -q 'start of badssl\.test hosts' /etc/hosts || make list-hosts | sudo tee -a /etc/hosts
|
||||
# badssl fails to create dh480.pem on our Ubuntu host.
|
||||
# Create it manually inside the docker container.
|
||||
sed -i '/CMD /i \
|
||||
RUN echo "-----BEGIN DH PARAMETERS-----" >/var/www/badssl/_site/certs/sets/current/gen/dhparam/dh480.pem \
|
||||
RUN echo "MEICPQDZ/YFp3iEs3/k9iRGoC/5/To2+5pUF/C6GkO6VjXHHyRVy68I0rI0q7IAq" >>/var/www/badssl/_site/certs/sets/current/gen/dhparam/dh480.pem \
|
||||
RUN echo "VyyGQ7/5Q/Iu0QQnHT4X9uMCAQI=" >>/var/www/badssl/_site/certs/sets/current/gen/dhparam/dh480.pem \
|
||||
RUN echo "-----END DH PARAMETERS-----" >>/var/www/badssl/_site/certs/sets/current/gen/dhparam/dh480.pem \
|
||||
' Dockerfile
|
||||
sed -i '/ 480/c \\ttrue' certs/Makefile
|
||||
# badssl does not create an expired certificate;
|
||||
# it creates a certificate that expires after 1 day and waits for 1 day to run the "expired certificate" test.
|
||||
# This command patches this behavior to run the test immediately.
|
||||
# See: https://github.com/chromium/badssl.com/blob/df8d5a9d062f4b99fc19d8aacdea5333b399d624/certs/Makefile#L177
|
||||
sed -i 's%./tool sign $@ $(D) 1 sha256 req_v3_usr $^%faketime -f "-2d" ./tool sign $@ $(D) 1 sha256 req_v3_usr $^%' certs/Makefile
|
||||
screen -dmS badssl sudo make serve
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
python3 scripts/generate.py badtls.test
|
||||
sudo mkdir /etc/nginx/tls || true
|
||||
sudo mkdir /var/www || true
|
||||
sudo python3 scripts/install.py /etc/nginx/conf.d /etc/nginx/tls /var/www
|
||||
sudo rm /etc/nginx/sites-enabled/default
|
||||
echo '#### start of badtls.test hosts ####' | sudo tee -a /etc/hosts
|
||||
echo '127.0.0.1 domain-match.badtls.test wildcard-match.badtls.test san-match.badtls.test dh1024.badtls.test expired-1963.badtls.test future.badtls.test domain-mismatch.badtls.test san-mismatch.badtls.test bad-key-usage.badtls.test expired.badtls.test wildcard.mismatch.badtls.test rc4.badtls.test weak-sig.badtls.test rc4-md5.badtls.test' | sudo tee -a /etc/hosts
|
||||
echo '#### end of badtls.test hosts ####' | sudo tee -a /etc/hosts
|
||||
screen -dmS badtls sudo bash ./scripts/local.sh
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
perl -p -i -e 's!\./runners!runners!' setup.py
|
||||
sed -i '/import platform/a import distro' runners/trytls/utils.py
|
||||
sed -i 's/platform.linux_distribution()/distro.name(), distro.version(), distro.id()/' runners/trytls/utils.py
|
||||
sed -i 's/break//' runners/trytls/bundles/https.py
|
||||
perl -p -i -e 's/badssl\.com/badssl.test/g; s/badtls\.io/badtls.test/g;' runners/trytls/bundles/https.py
|
||||
pip3 install -e .
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
sed -i -e '/BEGIN CERTIFICATE/,/CERTIFICATE/!b' -e '/END CERTIFICATE/!d;r badtls.io/certs/ca.crt' -e 'd' trytls/runners/trytls/bundles/https.py
|
||||
sed -i -e '/BEGIN CERTIFICATE/,/CERTIFICATE/!b' -e '/END CERTIFICATE/!d;r badssl.com/certs/sets/test/gen/crt/ca-root.crt' -e 'd' smithy-rs/tools/ci-resources/tls-stub/src/main.rs
|
Loading…
Reference in New Issue