mirror of
https://github.com/zerotier/coyote.git
synced 2024-11-24 12:18:02 +00:00
Split examples into TLS and non-TLS editions, for easier testing
Signed-off-by: Erik Hollensbe <git@hollensbe.org>
This commit is contained in:
parent
84b609ab7d
commit
b76ac0ae8c
4 changed files with 163 additions and 83 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1236,12 +1236,11 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ratpack"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a13f3016fde9fbad50dd0f7a2cef95b421224908dbe68125a4f5b447bdecb7"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"http",
|
||||
"hyper",
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -19,11 +19,11 @@ base64 = "^0.13"
|
|||
serde_json = "^1.0"
|
||||
serde = "^1.0"
|
||||
tokio = { version = "^1.16", features = ["full"] }
|
||||
ratpack = "^0.1" # { git = "https://github.com/zerotier/ratpack", branch = "main" }
|
||||
hyper = "^0.14"
|
||||
http = "^0.2"
|
||||
url = { version = "^2.2", features = [ "serde" ] }
|
||||
deadpool-postgres = { version = "^0.10", features = ["serde"] }
|
||||
ratpack = { version = "^0.1", path = "../ratpack", features = ["logging"] }
|
||||
log = "^0.4"
|
||||
trust-dns-client = "^0.20"
|
||||
openssl = "^0.10"
|
||||
|
@ -35,18 +35,26 @@ futures = "^0.3"
|
|||
futures-core = "^0.3"
|
||||
chrono = { version = "^0.4", features = [ "serde" ] }
|
||||
x509-parser = { version = "^0.12", features = [ "ring", "verify", "validate" ] }
|
||||
rustls = { version = "^0.20", optional = true }
|
||||
rustls-pemfile = { version = "^0.3", optional = true }
|
||||
webpki-roots = { version = "^0.22", optional = true }
|
||||
|
||||
[lib]
|
||||
|
||||
[[example]]
|
||||
name = "acmed-tls"
|
||||
path = "examples/acmed-tls.rs"
|
||||
required-features = ["tls"]
|
||||
|
||||
[[example]]
|
||||
name = "acmed"
|
||||
path = "examples/acmed.rs"
|
||||
|
||||
[features]
|
||||
tls = ["rustls", "rustls-pemfile", "webpki-roots", "ratpack/tls"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "^0.9"
|
||||
rustls = "^0.20"
|
||||
rustls-pemfile = "^0.3"
|
||||
webpki-roots = "^0.22"
|
||||
eggshell = "^0.1" # { path = "../eggshell" }
|
||||
bollard = "^0.11"
|
||||
tempfile = "^3.3"
|
||||
|
|
146
examples/acmed-tls.rs
Normal file
146
examples/acmed-tls.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
ops::Add,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use openssl::{
|
||||
error::ErrorStack,
|
||||
pkey::{PKey, Private},
|
||||
rsa::Rsa,
|
||||
x509::{X509Extension, X509Name, X509Req},
|
||||
};
|
||||
|
||||
use coyote::{
|
||||
acme::{
|
||||
ca::{CACollector, CA},
|
||||
challenge::Challenger,
|
||||
handlers::{configure_routes, ServiceState},
|
||||
PostgresNonceValidator,
|
||||
},
|
||||
models::Postgres,
|
||||
};
|
||||
|
||||
use ratpack::prelude::*;
|
||||
|
||||
const CHALLENGE_EXPIRATION: i64 = 600;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ServerError> {
|
||||
// set HOSTNAME in your environment to something your webserver or certbot can hit; otherwise
|
||||
// it will be 'localhost'. a cert will be generated with this name to serve the service with.
|
||||
// This is really important.
|
||||
let dnsname = &std::env::var("HOSTNAME").unwrap_or("localhost".to_string());
|
||||
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
//
|
||||
// to start a database to work with me:
|
||||
//
|
||||
// make postgres
|
||||
//
|
||||
let pg = Postgres::new("host=localhost dbname=coyote user=postgres", 10)
|
||||
.await
|
||||
.unwrap();
|
||||
pg.migrate().await.unwrap();
|
||||
|
||||
let c = Challenger::new(Some(chrono::Duration::seconds(CHALLENGE_EXPIRATION)));
|
||||
let ca = CACollector::new(Duration::MAX);
|
||||
|
||||
let pg2 = pg.clone();
|
||||
let c2 = c.clone();
|
||||
|
||||
// FIXME probably need something magical with signals here to manage shutdown that I don't want to think about yet
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
// FIXME whitelist all challenge requests. This is not how ACME is supposed to work. You have to write this.
|
||||
c2.tick(|_c| Some(())).await;
|
||||
// NOTE this will explode violently if it unwraps to error, e.g. if the db goes down.
|
||||
c2.reconcile(pg2.clone()).await.unwrap();
|
||||
|
||||
tokio::time::sleep(Duration::new(1, 0)).await;
|
||||
}
|
||||
});
|
||||
|
||||
let mut ca2 = ca.clone();
|
||||
let (csr, key) = generate_csr(dnsname)?;
|
||||
|
||||
let test_ca = CA::new_test_ca().unwrap();
|
||||
let cert = test_ca.generate_and_sign_cert(
|
||||
csr,
|
||||
SystemTime::now(),
|
||||
SystemTime::now().add(Duration::from_secs(365 * 24 * 60 * 60)),
|
||||
)?;
|
||||
|
||||
let test_ca2 = test_ca.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// after CA generation, write out the key and certificate
|
||||
let mut buf = std::fs::File::create("ca.key").unwrap();
|
||||
let private = test_ca
|
||||
.clone()
|
||||
.private_key()
|
||||
.private_key_to_pem_pkcs8()
|
||||
.unwrap();
|
||||
buf.write(&private).unwrap();
|
||||
|
||||
let mut buf = std::fs::File::create("ca.pem").unwrap();
|
||||
let cert = test_ca.clone().certificate().to_pem().unwrap();
|
||||
buf.write(&cert).unwrap();
|
||||
|
||||
ca2.spawn_collector(|| -> Result<CA, ErrorStack> { Ok(test_ca.clone()) })
|
||||
.await
|
||||
});
|
||||
|
||||
let validator = PostgresNonceValidator::new(pg.clone());
|
||||
let ss = ServiceState::new(
|
||||
format!("https://{}:8000", dnsname),
|
||||
pg.clone(),
|
||||
c,
|
||||
ca,
|
||||
validator,
|
||||
)?;
|
||||
let mut app = App::with_state(ss);
|
||||
|
||||
configure_routes(&mut app, None);
|
||||
|
||||
let key = key.private_key_to_der()?;
|
||||
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
vec![
|
||||
rustls::Certificate(cert.to_der()?),
|
||||
rustls::Certificate(test_ca2.certificate().to_der()?),
|
||||
],
|
||||
rustls::PrivateKey(key),
|
||||
)?;
|
||||
|
||||
Ok(app.serve_tls("0.0.0.0:8000", config).await?)
|
||||
}
|
||||
|
||||
fn generate_csr(dnsname: &str) -> Result<(X509Req, Rsa<Private>), ErrorStack> {
|
||||
log::info!("hostname: {}", dnsname);
|
||||
let mut namebuilder = X509Name::builder().unwrap();
|
||||
namebuilder.append_entry_by_text("CN", dnsname).unwrap();
|
||||
let mut req = X509Req::builder().unwrap();
|
||||
req.set_subject_name(&namebuilder.build()).unwrap();
|
||||
let mut extensions = openssl::stack::Stack::new()?;
|
||||
extensions.push(X509Extension::new(
|
||||
None,
|
||||
Some(&req.x509v3_context(None)),
|
||||
"subjectAltName",
|
||||
&format!("DNS:{}", dnsname),
|
||||
)?)?;
|
||||
req.add_extensions(&extensions)?;
|
||||
req.set_version(2)?;
|
||||
|
||||
let key = Rsa::generate(4096).unwrap();
|
||||
// FIXME there has to be a much better way of doing this!
|
||||
let pubkey = PKey::public_key_from_pem(&key.public_key_to_pem().unwrap()).unwrap();
|
||||
|
||||
req.set_pubkey(&pubkey).unwrap();
|
||||
Ok((req.build(), key))
|
||||
}
|
|
@ -1,15 +1,6 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
ops::Add,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use openssl::{
|
||||
error::ErrorStack,
|
||||
pkey::{PKey, Private},
|
||||
rsa::Rsa,
|
||||
x509::{X509Extension, X509Name, X509Req},
|
||||
};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
use coyote::{
|
||||
acme::{
|
||||
|
@ -27,11 +18,6 @@ const CHALLENGE_EXPIRATION: i64 = 600;
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ServerError> {
|
||||
// set HOSTNAME in your environment to something your webserver or certbot can hit; otherwise
|
||||
// it will be 'localhost'. a cert will be generated with this name to serve the service with.
|
||||
// This is really important.
|
||||
let dnsname = &std::env::var("HOSTNAME").unwrap_or("localhost".to_string());
|
||||
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Info)
|
||||
.init();
|
||||
|
@ -64,38 +50,16 @@ async fn main() -> Result<(), ServerError> {
|
|||
});
|
||||
|
||||
let mut ca2 = ca.clone();
|
||||
let (csr, key) = generate_csr(dnsname)?;
|
||||
|
||||
let test_ca = CA::new_test_ca().unwrap();
|
||||
let cert = test_ca.generate_and_sign_cert(
|
||||
csr,
|
||||
SystemTime::now(),
|
||||
SystemTime::now().add(Duration::from_secs(365 * 24 * 60 * 60)),
|
||||
)?;
|
||||
|
||||
let test_ca2 = test_ca.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// after CA generation, write out the key and certificate
|
||||
let mut buf = std::fs::File::create("ca.key").unwrap();
|
||||
let private = test_ca
|
||||
.clone()
|
||||
.private_key()
|
||||
.private_key_to_pem_pkcs8()
|
||||
.unwrap();
|
||||
buf.write(&private).unwrap();
|
||||
|
||||
let mut buf = std::fs::File::create("ca.pem").unwrap();
|
||||
let cert = test_ca.clone().certificate().to_pem().unwrap();
|
||||
buf.write(&cert).unwrap();
|
||||
|
||||
ca2.spawn_collector(|| -> Result<CA, ErrorStack> { Ok(test_ca.clone()) })
|
||||
.await
|
||||
});
|
||||
|
||||
let validator = PostgresNonceValidator::new(pg.clone());
|
||||
let ss = ServiceState::new(
|
||||
format!("https://{}:8000", dnsname),
|
||||
"http://127.0.0.1:8000".to_string(),
|
||||
pg.clone(),
|
||||
c,
|
||||
ca,
|
||||
|
@ -105,42 +69,5 @@ async fn main() -> Result<(), ServerError> {
|
|||
|
||||
configure_routes(&mut app, None);
|
||||
|
||||
let key = key.private_key_to_der()?;
|
||||
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
vec![
|
||||
rustls::Certificate(cert.to_der()?),
|
||||
rustls::Certificate(test_ca2.certificate().to_der()?),
|
||||
],
|
||||
rustls::PrivateKey(key),
|
||||
)?;
|
||||
|
||||
Ok(app.serve_tls("0.0.0.0:8000", config).await?)
|
||||
}
|
||||
|
||||
fn generate_csr(dnsname: &str) -> Result<(X509Req, Rsa<Private>), ErrorStack> {
|
||||
log::info!("hostname: {}", dnsname);
|
||||
let mut namebuilder = X509Name::builder().unwrap();
|
||||
namebuilder.append_entry_by_text("CN", dnsname).unwrap();
|
||||
let mut req = X509Req::builder().unwrap();
|
||||
req.set_subject_name(&namebuilder.build()).unwrap();
|
||||
let mut extensions = openssl::stack::Stack::new()?;
|
||||
extensions.push(X509Extension::new(
|
||||
None,
|
||||
Some(&req.x509v3_context(None)),
|
||||
"subjectAltName",
|
||||
&format!("DNS:{}", dnsname),
|
||||
)?)?;
|
||||
req.add_extensions(&extensions)?;
|
||||
req.set_version(2)?;
|
||||
|
||||
let key = Rsa::generate(4096).unwrap();
|
||||
// FIXME there has to be a much better way of doing this!
|
||||
let pubkey = PKey::public_key_from_pem(&key.public_key_to_pem().unwrap()).unwrap();
|
||||
|
||||
req.set_pubkey(&pubkey).unwrap();
|
||||
Ok((req.build(), key))
|
||||
Ok(app.serve("127.0.0.1:8000").await?)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue