Skip to content

Commit

Permalink
Merge pull request #401 from jpmcb/rustls-tls
Browse files Browse the repository at this point in the history
Remove `openssl` in favor of `rustls-tls`
  • Loading branch information
jpmcb committed Feb 7, 2023
2 parents a95c247 + b711beb commit 0899038
Show file tree
Hide file tree
Showing 12 changed files with 523 additions and 658 deletions.
1,045 changes: 427 additions & 618 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions Dockerfile
Expand Up @@ -4,11 +4,8 @@ FROM ${BUILDER_IMAGE} as build
ARG UNAME_ARCH
USER root

# We need these environment variables set for building the `openssl-sys` crate
ENV PKG_CONFIG_PATH=/${UNAME_ARCH}-bottlerocket-linux-musl/sys-root/usr/lib/pkgconfig
ENV PKG_CONFIG_ALLOW_CROSS=1
# Required to build in --offline mode
ENV CARGO_HOME=/src/.cargo
ENV OPENSSL_STATIC=true

ADD ./ /src/
RUN cargo install --offline --locked --target ${UNAME_ARCH}-bottlerocket-linux-musl --path /src/agent --root /src/agent && \
Expand Down Expand Up @@ -41,5 +38,4 @@ COPY --from=build /licenses /licenses
COPY --from=build \
/usr/share/licenses/bottlerocket-sdk-musl \
/usr/share/licenses/rust \
/usr/share/licenses/openssl \
/licenses/bottlerocket-sdk/
3 changes: 1 addition & 2 deletions agent/Cargo.toml
Expand Up @@ -18,13 +18,12 @@ tracing-opentelemetry = "0.18"

# k8s-openapi must match the version required by kube and enable a k8s version feature
k8s-openapi = { version = "0.17.0", default-features = false, features = ["v1_20"] }
kube = { version = "0.78.0", default-features = true, features = [ "derive", "runtime", "rustls-tls" ] }
kube = { version = "0.78.0", default-features = false, features = [ "derive", "runtime", "rustls-tls" ] }

semver = { version = "1.0", features = [ "serde" ] }
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
snafu = "0.7"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
reqwest = { version = "0.11", features = [ "json" ] }
chrono = { version = "0.4.23", features = [ "serde" ] }
tokio-retry = "0.3"
10 changes: 6 additions & 4 deletions apiserver/Cargo.toml
Expand Up @@ -15,10 +15,12 @@ server = []
models = { path = "../models", version = "0.1.0" }

# tracing-actix-web version must align with actix-web version
actix-web = { version = "4", features = ["openssl"] }
actix-web = { version = "4", features = ["rustls"] }
awc = "3"
actix-web-opentelemetry = { version = "0.13", features = ["metrics", "metrics-prometheus"] }
openssl = { version = "0.10" }
rustls = { version = "0.20" }
rustls-pemfile = { version = "1" }
webpki = { version = "0.22.0", features = ["std"] }
opentelemetry = { version = "0.18", features = ["rt-tokio-current-thread"]}
opentelemetry-prometheus = "0.11"
tracing = "0.1"
Expand All @@ -28,14 +30,14 @@ tracing-opentelemetry = "0.18"

# k8s-openapi must match the version required by kube and enable a k8s version feature
k8s-openapi = { version = "0.17.0", default-features = false, features = ["v1_20"] }
kube = { version = "0.78.0", default-features = true, features = [ "derive", "runtime", "rustls-tls" ] }
kube = { version = "0.78.0", default-features = false, features = [ "client", "derive", "runtime", "rustls-tls" ] }

async-trait = "0.1"
futures = "0.3"
lazy_static = "1.4"
log = "0.4"
mockall = { version = "0.11", optional = true }
reqwest = { version = "0.11", features = [ "json", "native-tls" ] }
reqwest = { version = "0.11", default-features = false, features = [ "json", "rustls-tls" ] }
schemars = "0.8.11"
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
Expand Down
17 changes: 14 additions & 3 deletions apiserver/src/api/error.rs
@@ -1,3 +1,5 @@
use std::io;

use models::node::{error, BottlerocketShadowClientError};

use actix_web::error::ResponseError;
Expand Down Expand Up @@ -44,10 +46,19 @@ pub enum Error {
#[snafu(display("Failed to reload certificate."))]
ReloadCertificateFailed {},

#[snafu(display("Failed to set up SslAcceptorBuilder : {:?}", source))]
SSLError { source: openssl::error::ErrorStack },
#[snafu(display("Failed to open file '{}': {}", path, source))]
FileOpen { path: String, source: io::Error },

#[snafu(display("Failed to extract TLS cert from file {}: {}", path, source))]
CertExtract { path: String, source: io::Error },

#[snafu(display("Failed to add CA to cert store: {}", source))]
CertStore { source: webpki::Error },

#[snafu(display("Failed to build TLS config from loaded certs: {}", source))]
TLSConfigBuild { source: rustls::Error },

#[snafu(display("Failed to serialize Webhook response: {:?}", source))]
#[snafu(display("Failed to serialize Webhook response: {}", source))]
WebhookError { source: serde_json::error::Error },
}

Expand Down
85 changes: 65 additions & 20 deletions apiserver/src/api/mod.rs
Expand Up @@ -34,10 +34,14 @@ use kube::{
runtime::{reflector, watcher::watcher, WatchStreamExt},
ResourceExt,
};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use opentelemetry::global::meter;
use rustls::{
server::AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore,
ServerConfig,
};
use rustls_pemfile::{certs, pkcs8_private_keys};
use snafu::{OptionExt, ResultExt};
use std::env;
use std::{env, fs::File, io::BufReader};
use tokio::time::{sleep, Duration};
use tracing::{event, Level};
use tracing_actix_web::TracingLogger;
Expand Down Expand Up @@ -158,23 +162,64 @@ pub async fn run_server<T: 'static + BottlerocketShadowClient>(

event!(Level::DEBUG, ?server_addr, "Server addr localhost.");

let mut builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls()).context(error::SSLSnafu)?;

builder
.set_certificate_chain_file(format!("{}/{}", TLS_KEY_MOUNT_PATH, CA_NAME))
.context(error::SSLSnafu)?;
builder
.set_certificate_file(
format!("{}/{}", TLS_KEY_MOUNT_PATH, PUBLIC_KEY_NAME),
SslFiletype::PEM,
)
.context(error::SSLSnafu)?;
builder
.set_private_key_file(
format!("{}/{}", TLS_KEY_MOUNT_PATH, PRIVATE_KEY_NAME),
SslFiletype::PEM,
)
.context(error::SSLSnafu)?;
// Server public certificate file
let cert_file_path = format!("{}/{}", TLS_KEY_MOUNT_PATH, PUBLIC_KEY_NAME);
let cert_file =
&mut BufReader::new(File::open(&cert_file_path).context(error::FileOpenSnafu {
path: cert_file_path.to_string(),
})?);

// Private key file
let key_file_path = format!("{}/{}", TLS_KEY_MOUNT_PATH, PRIVATE_KEY_NAME);
let key_file =
&mut BufReader::new(File::open(&key_file_path).context(error::FileOpenSnafu {
path: key_file_path.to_string(),
})?);

// Certificate authority file so a client can authenticate the server
let ca_file_path = format!("{}/{}", TLS_KEY_MOUNT_PATH, CA_NAME);
let ca_file = &mut BufReader::new(File::open(&ca_file_path).context(error::FileOpenSnafu {
path: ca_file_path.to_string(),
})?);

// convert files to key/cert objects
let cert_chain = certs(cert_file)
.context(error::CertExtractSnafu {
path: cert_file_path.to_string(),
})?
.into_iter()
.map(Certificate)
.collect();
let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file)
.context(error::CertExtractSnafu {
path: key_file_path.to_string(),
})?
.into_iter()
.map(PrivateKey)
.collect();
let cas: Vec<Certificate> = certs(ca_file)
.context(error::CertExtractSnafu {
path: ca_file_path.to_string(),
})?
.into_iter()
.map(Certificate)
.collect();

let mut cert_store = RootCertStore::empty();
for ca in cas {
cert_store.add(&ca).context(error::CertStoreSnafu)?;
}

let verifier = AllowAnyAnonymousOrAuthenticatedClient::new(cert_store);

let tls_config_builder = ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(verifier);

let tls_config = tls_config_builder
.with_single_cert(cert_chain, keys.remove(0))
.context(error::TLSConfigBuildSnafu)
.unwrap();

let server = HttpServer::new(move || {
App::new()
Expand Down Expand Up @@ -225,7 +270,7 @@ pub async fn run_server<T: 'static + BottlerocketShadowClient>(
web::get().to(ping::health_check),
)
})
.bind_openssl(server_addr, builder)
.bind_rustls(server_addr, tls_config)
.context(error::HttpServerSnafu)?
.run();

Expand Down
3 changes: 2 additions & 1 deletion clarify.toml
Expand Up @@ -69,10 +69,11 @@ license-files = [
#
# zstd's README file states:
# "Zstandard is dual-licensed under BSD and GPLv2."
expression = "(MIT OR Apache-2.0) AND (BSD-2-Clause OR GPL-2.0)"
expression = "(MIT OR Apache-2.0) AND (BSD-3-Clause OR GPL-2.0)"
license-files = [
{ path = "LICENSE", hash = 0x742401ae },
{ path = "LICENSE.Apache-2.0", hash = 0x7b466be4 },
{ path = "LICENSE.BSD-3-Clause", hash = 0xc9f5c4f6},
{ path = "LICENSE.Mit", hash = 0xa237d234 },
{ path = "zstd/COPYING", hash = 0x96841aa4 },
{ path = "zstd/LICENSE", hash = 0x79cda15 },
Expand Down
2 changes: 1 addition & 1 deletion controller/Cargo.toml
Expand Up @@ -14,7 +14,7 @@ maplit = "1.0"
semver = "1.0"
# k8s-openapi must match the version required by kube and enable a k8s version feature
k8s-openapi = { version = "0.17.0", default-features = false, features = ["v1_20"] }
kube = { version = "0.78.0", default-features = true, features = [ "derive", "runtime", "rustls-tls" ] }
kube = { version = "0.78.0", default-features = false, features = [ "derive", "runtime", "rustls-tls" ] }
models = { path = "../models", version = "0.1.0" }
opentelemetry = { version = "0.18", features = ["rt-tokio-current-thread"] }
opentelemetry-prometheus = "0.11"
Expand Down
2 changes: 2 additions & 0 deletions deny.toml
Expand Up @@ -23,6 +23,8 @@ allow = [
]

exceptions = [
# Explicitly allows MPL-2 being pulled in through reqwest's and actix's rustls dependency chain (which uses webpki)
{ name = "webpki-roots", allow = ["MPL-2.0"], version = "*" },
{ name = "unicode-ident", version = "1.0.2", allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016"] },
]

Expand Down
2 changes: 1 addition & 1 deletion integ/Cargo.toml
Expand Up @@ -35,7 +35,7 @@ uuid = { version = "0.8", default-features = false, features = ["serde", "v4"] }

# k8s-openapi must match the version required by kube and enable a k8s version feature
k8s-openapi = { version = "0.17.0", default-features = false, features = ["v1_20"] }
kube = { version = "0.78", default-features = true, features = [ "derive", "runtime" ] }
kube = { version = "0.78", default-features = false, features = [ "derive", "runtime" ] }


[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions models/Cargo.toml
Expand Up @@ -11,13 +11,13 @@ chrono = "0.4"
futures = "0.3"
# k8s-openapi must match the version required by kube and enable a k8s version feature
k8s-openapi = { version = "0.17.0", default-features = false, features = ["v1_20"] }
kube = { version = "0.78.0", default-features = true, features = [ "derive", "runtime" ] }
kube = { version = "0.78.0", default-features = false, features = [ "client", "derive", "runtime" ] }

lazy_static = "1.4"
maplit = "1.0"
mockall = { version = "0.11", optional = true }
regex = "1.7"
reqwest = "0.11"
reqwest = { version = "0.11", default-features = false, features = [ "json" ] }
schemars = "0.8.11"
semver = "1.0"
serde = { version = "1", features = [ "derive" ] }
Expand Down
2 changes: 1 addition & 1 deletion yamlgen/Cargo.toml
Expand Up @@ -8,5 +8,5 @@ license = "Apache-2.0 OR MIT"
[build-dependencies]
models = { path = "../models", version = "0.1.0" }
dotenv = "0.15"
kube = { version = "0.78.0", default-features = true, features = [ "derive", "runtime" ] }
kube = { version = "0.78.0", default-features = false, features = [ "derive", "runtime" ] }
serde_yaml = "0.9"

0 comments on commit 0899038

Please sign in to comment.