aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dockerignore2
-rw-r--r--Cargo.lock33
-rw-r--r--Cargo.toml1
-rw-r--r--Dockerfile23
-rw-r--r--src/cert_store.rs47
-rw-r--r--src/proxy_config.rs8
6 files changed, 106 insertions, 8 deletions
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..d913617
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+target
+
diff --git a/Cargo.lock b/Cargo.lock
index 66da4cc..38e8c70 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -994,6 +994,17 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1147,6 +1158,18 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
+]
diff --git a/Cargo.toml b/Cargo.toml
index bd40fe6..5dca10f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,3 +32,4 @@ unicase = "2"
lazy_static = "1.4"
structopt = "0.3"
glob = "0.3"
+rcgen = "0.8"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..ebda1fc
--- /dev/null
+++ b/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"]
diff --git a/src/cert_store.rs b/src/cert_store.rs
index 8d45df4..4cc2fc0 100644
--- a/src/cert_store.rs
+++ b/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
+ }
+ }
}
}
diff --git a/src/proxy_config.rs b/src/proxy_config.rs
index 2807a3b..1b46305 100644
--- a/src/proxy_config.rs
+++ b/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)]