Implement glob pattern hostnames

no wildcard certificates: one certificate per matching hostname that
actually recieves requests
This commit is contained in:
Alex 2021-12-08 11:11:22 +01:00
parent 11c6f0b1c2
commit 098a6cf2cd
No known key found for this signature in database
GPG key ID: EDABF9711E244EB1
5 changed files with 48 additions and 7 deletions

7
Cargo.lock generated
View file

@ -472,6 +472,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.1.26" version = "0.1.26"
@ -1880,6 +1886,7 @@ dependencies = [
"envy", "envy",
"futures 0.3.18", "futures 0.3.18",
"futures-util", "futures-util",
"glob",
"http 0.2.5", "http 0.2.5",
"hyper 0.14.15", "hyper 0.14.15",
"hyper-reverse-proxy", "hyper-reverse-proxy",

View file

@ -31,3 +31,4 @@ hyper-reverse-proxy = "0.4"
unicase = "2" unicase = "2"
lazy_static = "1.4" lazy_static = "1.4"
structopt = "0.3" structopt = "0.3"
glob = "0.3"

View file

@ -14,7 +14,7 @@ use rustls::sign::CertifiedKey;
use crate::cert::{Cert, CertSer}; use crate::cert::{Cert, CertSer};
use crate::consul::*; use crate::consul::*;
use crate::proxy_config::ProxyConfig; use crate::proxy_config::*;
pub struct CertStore { pub struct CertStore {
consul: Consul, consul: Consul,
@ -39,11 +39,13 @@ impl CertStore {
let proxy_config: Arc<ProxyConfig> = rx_proxy_config.borrow().clone(); let proxy_config: Arc<ProxyConfig> = rx_proxy_config.borrow().clone();
for ent in proxy_config.entries.iter() { for ent in proxy_config.entries.iter() {
domains.insert(ent.host.clone()); if let HostDescription::Hostname(domain) = &ent.host {
domains.insert(domain.clone());
}
} }
info!("Ensuring we have certs for domains: {:?}", domains);
for dom in domains.iter() { for dom in domains.iter() {
info!("Ensuring we have certs for domains: {:?}", domains);
if let Err(e) = self.get_cert(dom).await { if let Err(e) = self.get_cert(dom).await {
warn!("Error get_cert {}: {}", dom, e); warn!("Error get_cert {}: {}", dom, e);
} }
@ -58,7 +60,7 @@ impl CertStore {
.borrow() .borrow()
.entries .entries
.iter() .iter()
.any(|ent| ent.host == domain) .any(|ent| ent.host.matches(domain))
{ {
bail!("Domain {} should not have a TLS certificate.", domain); bail!("Domain {} should not have a TLS certificate.", domain);
} }

View file

@ -93,7 +93,7 @@ async fn handle(
.entries .entries
.iter() .iter()
.filter(|ent| { .filter(|ent| {
ent.host == host ent.host.matches(host)
&& ent && ent
.path_prefix .path_prefix
.as_ref() .as_ref()

View file

@ -15,11 +15,34 @@ use crate::consul::*;
// ---- Extract proxy config from Consul catalog ---- // ---- Extract proxy config from Consul catalog ----
#[derive(Debug)]
pub enum HostDescription {
Hostname(String),
Pattern(glob::Pattern),
}
impl HostDescription {
fn new(desc: &str) -> Result<Self> {
if desc.chars().any(|x| matches!(x, '*' | '?' | '[' | ']')) {
Ok(Self::Pattern(glob::Pattern::new(desc)?))
} else {
Ok(Self::Hostname(desc.to_string()))
}
}
pub fn matches(&self, v: &str) -> bool {
match self {
Self::Pattern(p) => p.matches(v),
Self::Hostname(s) => s == v,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct ProxyEntry { pub struct ProxyEntry {
pub target_addr: SocketAddr, pub target_addr: SocketAddr,
pub host: String, pub host: HostDescription,
pub path_prefix: Option<String>, pub path_prefix: Option<String>,
pub priority: u32, pub priority: u32,
pub add_headers: Vec<(String, String)>, pub add_headers: Vec<(String, String)>,
@ -65,9 +88,17 @@ fn parse_tricot_tag(
_ => 100, _ => 100,
}; };
let host = match HostDescription::new(host) {
Ok(h) => h,
Err(e) => {
warn!("Invalid hostname pattern {}: {}", host, e);
return None;
}
};
Some(ProxyEntry { Some(ProxyEntry {
target_addr, target_addr,
host: host.to_string(), host,
path_prefix, path_prefix,
priority, priority,
add_headers: add_headers.to_vec(), add_headers: add_headers.to_vec(),