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

Implement EAB #551

Merged
merged 3 commits into from
Jan 17, 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.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions plane/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ data-encoding = "2.4.0"
futures-util = "0.3.29"
hyper = { version = "0.14.27", features = ["server"] }
lru = "0.12.1"
openssl = "0.10.62"
pem = "3.0.2"
rand = "0.8.5"
reqwest = { version = "0.11.22", features = ["json", "rustls-tls"], default-features = false }
Expand Down
49 changes: 47 additions & 2 deletions plane/plane-tests/tests/cert_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use crate::common::timeout::WithTimeout;
use common::test_env::TestEnvironment;
use plane::{
names::{Name, ProxyName},
proxy::{cert_manager::watcher_manager_pair, proxy_connection::ProxyConnection, AcmeConfig},
proxy::{
cert_manager::watcher_manager_pair, proxy_connection::ProxyConnection, AcmeConfig,
AcmeEabConfiguration,
},
};
use plane_test_macro::plane_test;

Expand All @@ -23,6 +26,7 @@ async fn cert_manager_does_refresh(env: TestEnvironment) {
endpoint: pebble.directory_url.clone(),
mailto_email: "test-cert@jamsocket.com".to_string(),
client: Pebble::client().unwrap(),
acme_eab_keypair: None,
};

let certs_dir = env.scratch_dir.join("certs");
Expand All @@ -31,7 +35,48 @@ async fn cert_manager_does_refresh(env: TestEnvironment) {
let (mut cert_watcher, cert_manager) = watcher_manager_pair(
env.cluster.clone(),
Some(&certs_dir.join("cert.json")),
Some(acme_config),
Some(acme_config.clone()),
)
.unwrap();

let _proxy_connection = ProxyConnection::new(
ProxyName::new_random(),
controller.client(),
env.cluster.clone(),
cert_manager,
);
let _cert = cert_watcher.next().with_timeout(60).await.unwrap().unwrap();
}

#[plane_test]
async fn cert_manager_does_refresh_eab(env: TestEnvironment) {
let controller = env.controller().await;

let dns = env.dns(&controller).await;

let eab_keypair = AcmeEabConfiguration::new(
"kid-1",
"zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W",
)
.unwrap();

let pebble = env.pebble_with_eab(dns.port, eab_keypair.clone()).await;
tracing::info!("Pebble: {}", pebble.directory_url);

let acme_config = AcmeConfig {
endpoint: pebble.directory_url.clone(),
mailto_email: "test-cert@jamsocket.com".to_string(),
client: Pebble::client().unwrap(),
acme_eab_keypair: Some(eab_keypair),
};

let certs_dir = env.scratch_dir.join("certs");
std::fs::create_dir_all(&certs_dir).unwrap();

let (mut cert_watcher, cert_manager) = watcher_manager_pair(
env.cluster.clone(),
Some(&certs_dir.join("cert.json")),
Some(acme_config.clone()),
)
.unwrap();

Expand Down
16 changes: 14 additions & 2 deletions plane/plane-tests/tests/common/resources/pebble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::common::test_env::TestEnvironment;
use anyhow::anyhow;
use anyhow::Result;
use bollard::{container::Config, Docker};
use plane::proxy::AcmeEabConfiguration;
use reqwest::Client;
use serde_json::json;
use std::os::unix::fs::PermissionsExt;
Expand Down Expand Up @@ -86,13 +87,17 @@ impl Pebble {
}
}

pub async fn new(env: &TestEnvironment, dns_port: u16) -> Result<Pebble> {
pub async fn new(
env: &TestEnvironment,
dns_port: u16,
eab_keypair: Option<AcmeEabConfiguration>,
) -> Result<Pebble> {
let scratch_dir = env.scratch_dir.clone();

let pebble_dir = scratch_dir.canonicalize()?.join("pebble");
std::fs::create_dir_all(&pebble_dir)?;

let pebble_config = json!({
let mut pebble_config = json!({
"pebble": {
"listenAddress": "0.0.0.0:14000",
"managementListenAddress": "0.0.0.0:15000",
Expand All @@ -111,6 +116,13 @@ impl Pebble {
}
});

if let Some(eab_keypair) = eab_keypair {
pebble_config["pebble"]["externalAccountBindingRequired"] = json!(true);
pebble_config["pebble"]["externalAccountMacKeys"] = json!({
eab_keypair.clone().key_id : eab_keypair.eab_key_b64()
});
}

std::fs::write(
pebble_dir.join("config.json"),
serde_json::to_string_pretty(&pebble_config)?,
Expand Down
17 changes: 16 additions & 1 deletion plane/plane-tests/tests/common/test_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use plane::{
dns::run_dns_with_listener,
drone::Drone,
names::{AcmeDnsServerName, ControllerName, DroneName, Name},
proxy::AcmeEabConfiguration,
types::ClusterName,
util::random_string,
};
Expand Down Expand Up @@ -137,7 +138,21 @@ impl TestEnvironment {
}

pub async fn pebble(&mut self, dns_port: u16) -> Arc<Pebble> {
let pebble = Arc::new(Pebble::new(&self, dns_port).await.unwrap());
let pebble = Arc::new(Pebble::new(&self, dns_port, None).await.unwrap());
self.drop_futures.lock().unwrap().push(pebble.clone());
pebble
}

pub async fn pebble_with_eab(
&mut self,
dns_port: u16,
eab_keypair: AcmeEabConfiguration,
) -> Arc<Pebble> {
let pebble = Arc::new(
Pebble::new(&self, dns_port, Some(eab_keypair))
.await
.unwrap(),
);
self.drop_futures.lock().unwrap().push(pebble.clone());
pebble
}
Expand Down
1 change: 1 addition & 0 deletions plane/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ async fn run(opts: Opts) -> Result<()> {
(Some(endpoint), Some(email)) => Some(AcmeConfig {
endpoint,
mailto_email: email,
acme_eab_keypair: None,
client: reqwest::Client::new(),
}),
(None, None) => None,
Expand Down
8 changes: 4 additions & 4 deletions plane/src/proxy/cert_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,10 @@ async fn get_certificate(
let mut builder = AccountBuilder::new(dir);
// builder.private_key(...);
builder.contact(vec![format!("mailto:{}", acme_config.mailto_email)]);
// if let Some(acme_eab_keypair) = acme_eab_keypair {
// let eab_key = PKey::hmac(&acme_eab_keypair.key)?;
// builder.external_account_binding(acme_eab_keypair.key_id.clone(), eab_key);
// }
if let Some(acme_eab_keypair) = acme_config.acme_eab_keypair.clone() {
let eab_key = openssl::pkey::PKey::hmac(&acme_eab_keypair.key)?;
builder.external_account_binding(acme_eab_keypair.key_id.clone(), eab_key);
}
builder.terms_of_service_agreed(true);
let account = builder.build().await.context("Building account")?;

Expand Down
22 changes: 21 additions & 1 deletion plane/src/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,32 @@ pub struct ServerPortConfig {
pub https_port: Option<u16>,
}

#[derive(Debug, Clone)]
pub struct AcmeEabConfiguration {
pub key_id: String,
pub key: Vec<u8>,
}

impl AcmeEabConfiguration {
pub fn eab_key_b64(&self) -> String {
data_encoding::BASE64URL_NOPAD.encode(&self.key)
}

pub fn new(key_id: &str, key_b64: &str) -> Result<Self> {
let key = data_encoding::BASE64URL_NOPAD.decode(key_b64.as_bytes())?;
Ok(Self {
key_id: key_id.into(),
key,
})
}
}

#[derive(Debug, Clone)]
pub struct AcmeConfig {
pub endpoint: Url,
pub mailto_email: String,
pub client: reqwest::Client,
// TODO: EAB credentials.
pub acme_eab_keypair: Option<AcmeEabConfiguration>,
}

pub async fn run_proxy(
Expand Down
Loading