Use self-signed certificates if necessary
This commit is contained in:
parent
d7f3c13fa4
commit
7599dfc0ef
6 changed files with 106 additions and 8 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
|
33
Cargo.lock
generated
33
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -32,3 +32,4 @@ unicase = "2"
|
|||
lazy_static = "1.4"
|
||||
structopt = "0.3"
|
||||
glob = "0.3"
|
||||
rcgen = "0.8"
|
||||
|
|
23
Dockerfile
Normal file
23
Dockerfile
Normal file
|
@ -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"]
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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…
Reference in a new issue