summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-08-18 22:02:03 +0200
committermetamuffin <metamuffin@disroot.org>2024-08-18 22:02:03 +0200
commit311d8dc828446d4cfd936706782d2d6b22ed5d86 (patch)
tree256d785ff19a2346f7db79e6ebeb0e5c2508e80a
parentf46b05a0afa776b39764bb3ddf8ecab6cde13a2c (diff)
downloadgnix-311d8dc828446d4cfd936706782d2d6b22ed5d86.tar
gnix-311d8dc828446d4cfd936706782d2d6b22ed5d86.tar.bz2
gnix-311d8dc828446d4cfd936706782d2d6b22ed5d86.tar.zst
new cert loading method
-rw-r--r--Cargo.lock3
-rw-r--r--Cargo.toml4
-rw-r--r--src/certs.rs86
-rw-r--r--src/config.rs4
-rw-r--r--src/main.rs35
5 files changed, 104 insertions, 28 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ff10378..5d0294d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -607,8 +607,10 @@ dependencies = [
"percent-encoding",
"pin-project",
"rand",
+ "ring",
"rustls",
"rustls-pemfile",
+ "rustls-webpki",
"serde",
"serde_yaml",
"thiserror",
@@ -1280,6 +1282,7 @@ dependencies = [
"aws-lc-rs",
"log",
"once_cell",
+ "ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
diff --git a/Cargo.toml b/Cargo.toml
index 92f45b2..a9b4586 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,8 +16,10 @@ hex = "0.4.3"
# TLS
rustls-pemfile = "2.1.3"
-rustls = "0.23.12"
+rustls = { version="0.23.12", default-features = false, features=["ring"] }
tokio-rustls = "0.26.0"
+rustls-webpki = "0.102.6"
+ring = "0.17.8"
# Async stuff
tokio = { version = "1.39.2", features = ["full"] }
diff --git a/src/certs.rs b/src/certs.rs
new file mode 100644
index 0000000..c1d0ad1
--- /dev/null
+++ b/src/certs.rs
@@ -0,0 +1,86 @@
+use anyhow::{anyhow, Context, Result};
+use log::debug;
+use rustls::{
+ crypto::CryptoProvider,
+ pki_types::{CertificateDer, PrivateKeyDer},
+ server::{ClientHello, ResolvesServerCert},
+ sign::CertifiedKey,
+};
+use std::{
+ collections::HashMap,
+ fs::File,
+ io::BufReader,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
+use webpki::EndEntityCert;
+
+#[derive(Debug)]
+pub struct CertPool {
+ provider: &'static Arc<CryptoProvider>,
+ domains: HashMap<String, Arc<CertifiedKey>>,
+}
+
+impl Default for CertPool {
+ fn default() -> Self {
+ Self {
+ provider: CryptoProvider::get_default().unwrap(),
+ domains: Default::default(),
+ }
+ }
+}
+
+impl CertPool {
+ pub fn load(roots: &[PathBuf]) -> Result<Self> {
+ let mut s = Self::default();
+ for r in roots {
+ s.load_recursive(&r)?;
+ }
+ Ok(s)
+ }
+
+ fn load_recursive(&mut self, path: &Path) -> Result<()> {
+ if !path.is_dir() {
+ return Ok(());
+ }
+ let keypath = path.join("privkey.pem");
+ let certpath = if path.join("fullchain.pem").exists() {
+ path.join("fullchain.pem")
+ } else {
+ path.join("cert.pem")
+ };
+ if keypath.exists() && certpath.exists() {
+ let certs = load_certs(&certpath)?;
+ let key = load_private_key(&keypath)?;
+ let skey = self.provider.key_provider.load_private_key(key)?;
+ for c in &certs {
+ let eec = EndEntityCert::try_from(c).unwrap();
+ for name in eec.valid_dns_names() {
+ debug!("loaded key for {name:?}");
+ let ck = CertifiedKey::new(certs.clone(), skey.clone());
+ self.domains.insert(name.to_owned(), Arc::new(ck));
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+impl ResolvesServerCert for CertPool {
+ fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
+ Some(self.domains.get(client_hello.server_name()?)?.clone())
+ }
+}
+
+fn load_certs(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
+ let mut reader = BufReader::new(File::open(path).context("reading tls certs")?);
+ let certs = rustls_pemfile::certs(&mut reader)
+ .try_collect::<Vec<_>>()
+ .context("parsing tls certs")?;
+ Ok(certs)
+}
+fn load_private_key(path: &Path) -> Result<PrivateKeyDer<'static>> {
+ let mut reader = BufReader::new(File::open(path).context("reading tls private key")?);
+ let keys = rustls_pemfile::private_key(&mut reader).context("parsing tls private key")?;
+ keys.ok_or(anyhow!("no private key found"))
+}
diff --git a/src/config.rs b/src/config.rs
index e3b0d3c..6ef392d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -59,8 +59,8 @@ pub struct HttpConfig {
pub struct HttpsConfig {
#[serde(deserialize_with = "string_or_seq")]
pub bind: Vec<SocketAddr>,
- pub tls_cert: PathBuf,
- pub tls_key: PathBuf,
+ #[serde(deserialize_with = "seq_or_not")]
+ pub cert_path: Vec<PathBuf>,
}
// try deser Vec<T> but fall back to deser T and putting that in Vec
diff --git a/src/main.rs b/src/main.rs
index eecb946..56a9b19 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,13 +2,15 @@
#![feature(slice_split_once)]
#![feature(iterator_try_collect)]
+pub mod certs;
pub mod config;
pub mod error;
pub mod helper;
pub mod modules;
use aes_gcm_siv::{aead::generic_array::GenericArray, Aes256GcmSiv, KeyInit};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result};
+use certs::CertPool;
use config::{setup_file_watch, Config, NODE_KINDS};
use error::ServiceError;
use futures::future::try_join_all;
@@ -24,15 +26,8 @@ use hyper::{
};
use log::{debug, error, info, warn, LevelFilter};
use modules::{NodeContext, MODULES};
-use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use std::{
- collections::HashMap,
- io::BufReader,
- net::SocketAddr,
- path::{Path, PathBuf},
- process::exit,
- str::FromStr,
- sync::Arc,
+ collections::HashMap, net::SocketAddr, path::PathBuf, process::exit, str::FromStr, sync::Arc,
};
use tokio::{
fs::File,
@@ -58,6 +53,10 @@ async fn main() -> anyhow::Result<()> {
.parse_env("LOG")
.init();
+ rustls::crypto::ring::default_provider()
+ .install_default()
+ .unwrap();
+
NODE_KINDS
.write()
.unwrap()
@@ -147,11 +146,10 @@ async fn serve_https(state: Arc<State>) -> Result<()> {
None => return Ok(()),
};
let tls_config = {
- let certs = load_certs(&https_config.tls_cert)?;
- let key = load_private_key(&https_config.tls_key)?;
+ let certs = CertPool::load(&https_config.cert_path)?;
let mut cfg = rustls::ServerConfig::builder()
.with_no_client_auth()
- .with_single_cert(certs, key)?;
+ .with_cert_resolver(Arc::new(certs));
cfg.alpn_protocols = vec![
// b"h2".to_vec(),
b"http/1.1".to_vec(),
@@ -222,19 +220,6 @@ pub async fn serve_stream<T: Unpin + Send + 'static + hyper::rt::Read + hyper::r
}
}
-fn load_certs(path: &Path) -> anyhow::Result<Vec<CertificateDer<'static>>> {
- let mut reader = BufReader::new(std::fs::File::open(path).context("reading tls certs")?);
- let certs = rustls_pemfile::certs(&mut reader)
- .try_collect::<Vec<_>>()
- .context("parsing tls certs")?;
- Ok(certs)
-}
-fn load_private_key(path: &Path) -> anyhow::Result<PrivateKeyDer<'static>> {
- let mut reader = BufReader::new(std::fs::File::open(path).context("reading tls private key")?);
- let keys = rustls_pemfile::private_key(&mut reader).context("parsing tls private key")?;
- keys.ok_or(anyhow!("no private key found"))
-}
-
async fn service(
state: Arc<State>,
config: Arc<Config>,