Browse Source

Use self-signed certificates if necessary

pull/2/head
Alex 10 months ago
parent
commit
7599dfc0ef
Signed by: lx GPG Key ID: EDABF9711E244EB1
  1. 2
      .dockerignore
  2. 33
      Cargo.lock
  3. 1
      Cargo.toml
  4. 23
      Dockerfile
  5. 47
      src/cert_store.rs
  6. 8
      src/proxy_config.rs

2
.dockerignore

@ -0,0 +1,2 @@
target

33
Cargo.lock

@ -993,6 +993,17 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "pem"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06673860db84d02a63942fa69cd9543f2624a5df3aea7f33173048fa7ad5cf1a"
dependencies = [
"base64",
"once_cell",
"regex",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1146,6 +1157,18 @@ dependencies = [
"rand_core",
]
[[package]]
name = "rcgen"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5911d1403f4143c9d56a702069d593e8d0f3fab880a85e103604d0893ea31ba7"
dependencies = [
"chrono",
"pem",
"ring",
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
@ -1893,6 +1916,7 @@ dependencies = [
"lazy_static",
"log",
"pretty_env_logger",
"rcgen",
"regex",
"reqwest",
"rustls 0.20.2",
@ -2201,3 +2225,12 @@ dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "yasna"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75"
dependencies = [
"chrono",
]

1
Cargo.toml

@ -32,3 +32,4 @@ unicase = "2"
lazy_static = "1.4"
structopt = "0.3"
glob = "0.3"
rcgen = "0.8"

23
Dockerfile

@ -0,0 +1,23 @@
FROM rust:1.57-bullseye as builder
RUN apt-get update && \
apt-get install -y libssl-dev pkg-config
WORKDIR /srv
# Build dependencies and cache them
COPY Cargo.* ./
RUN mkdir -p src && \
echo "fn main() {println!(\"if you see this, the build broke\")}" > src/main.rs && \
cargo build --release && \
rm -r src && \
rm target/release/deps/tricot*
# Build final app
COPY ./src ./src
RUN cargo build --release
FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y libssl1.1 iptables
COPY --from=builder /srv/target/release/tricot /usr/local/sbin/tricot
CMD ["/usr/local/sbin/tricot"]

47
src/cert_store.rs

@ -19,6 +19,7 @@ use crate::proxy_config::*;
pub struct CertStore {
consul: Consul,
certs: RwLock<HashMap<String, Arc<Cert>>>,
self_signed_certs: RwLock<HashMap<String, Arc<Cert>>>,
rx_proxy_config: watch::Receiver<Arc<ProxyConfig>>,
}
@ -27,6 +28,7 @@ impl CertStore {
Arc::new(Self {
consul,
certs: RwLock::new(HashMap::new()),
self_signed_certs: RwLock::new(HashMap::new()),
rx_proxy_config,
})
}
@ -66,16 +68,23 @@ impl CertStore {
}
// Check in local memory if it exists
let certs = self.certs.read().unwrap();
if let Some(cert) = certs.get(domain) {
if let Some(cert) = self.certs.read().unwrap().get(domain) {
if !cert.is_old() {
return Ok(cert.clone());
}
}
// Not found in local memory
// Not found in local memory, try to get it in background
tokio::spawn(self.clone().get_cert_task(domain.to_string()));
bail!("Certificate not found (will try to get it in background)");
// In the meantime, use a self-signed certificate
if let Some(cert) = self.self_signed_certs.read().unwrap().get(domain) {
if !cert.is_old() {
return Ok(cert.clone());
}
}
self.gen_self_signed_certificate(domain)
}
pub async fn get_cert_task(self: Arc<Self>, domain: String) -> Result<Arc<Cert>> {
@ -221,6 +230,27 @@ impl CertStore {
info!("Cert successfully renewed: {}", domain);
Ok(cert)
}
fn gen_self_signed_certificate(&self, domain: &str) -> Result<Arc<Cert>> {
let subject_alt_names = vec![domain.to_string(), "localhost".to_string()];
let cert = rcgen::generate_simple_self_signed(subject_alt_names)?;
let certser = CertSer {
hostname: domain.to_string(),
date: Utc::today().naive_utc(),
valid_days: 1024,
key_pem: cert.serialize_private_key_pem(),
cert_pem: cert.serialize_pem()?,
};
let cert = Arc::new(Cert::new(certser)?);
self.self_signed_certs
.write()
.unwrap()
.insert(domain.to_string(), cert.clone());
info!("Added self-signed certificate for {}", domain);
Ok(cert)
}
}
pub struct StoreResolver(pub Arc<CertStore>);
@ -228,7 +258,12 @@ pub struct StoreResolver(pub Arc<CertStore>);
impl rustls::server::ResolvesServerCert for StoreResolver {
fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
let domain = client_hello.server_name()?;
let cert = self.0.get_cert_for_https(domain).ok()?;
Some(cert.certkey.clone())
match self.0.get_cert_for_https(domain) {
Ok(cert) => Some(cert.certkey.clone()),
Err(e) => {
warn!("Could not get certificate for {}: {}", domain, e);
None
}
}
}
}

8
src/proxy_config.rs

@ -60,13 +60,17 @@ impl std::fmt::Display for ProxyEntry {
HostDescription::Hostname(h) => write!(f, "{}", h)?,
HostDescription::Pattern(p) => write!(f, "Pattern('{}')", p.as_str())?,
}
write!(f, "{} {}", self.path_prefix.as_ref().unwrap_or(&String::new()), self.priority)?;
write!(
f,
"{} {}",
self.path_prefix.as_ref().unwrap_or(&String::new()),
self.priority
)?;
if !self.add_headers.is_empty() {
write!(f, "+Headers: {:?}", self.add_headers)?;
}
Ok(())
}
}
#[derive(Debug)]

Loading…
Cancel
Save