diff --git a/rust/.gitignore b/rust/.gitignore index a2ef7f4edf..d4661763be 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1,2 +1,4 @@ target/ libsrc/ +*.rsa +*.cert diff --git a/rust/examples/openvasd/config.example.toml b/rust/examples/openvasd/config.example.toml index 4a8df6d761..1b5920a52b 100644 --- a/rust/examples/openvasd/config.example.toml +++ b/rust/examples/openvasd/config.example.toml @@ -1,6 +1,8 @@ [feed] # path to the openvas feed. This is required for the /vts endpoint. path = "/var/lib/openvas/plugins" +# disables or enables the signnature check +signature_check = true [feed.check_interval] # how often the feed should be checked for updates diff --git a/rust/examples/tls/Self-Signed mTLS Method/server_certificates.sh b/rust/examples/tls/Self-Signed mTLS Method/server_certificates.sh index 3da90b334c..dab9756e0a 100644 --- a/rust/examples/tls/Self-Signed mTLS Method/server_certificates.sh +++ b/rust/examples/tls/Self-Signed mTLS Method/server_certificates.sh @@ -3,57 +3,71 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -set -xe - -openssl req -nodes \ - -x509 \ - -days 3650 \ - -newkey rsa:4096 \ - -keyout ca.key \ - -out ca.cert \ - -sha256 \ - -batch \ - -subj "/CN=ponytown RSA CA" - -openssl req -nodes \ - -newkey rsa:3072 \ - -keyout inter.key \ - -out inter.req \ - -sha256 \ - -batch \ - -subj "/CN=ponytown RSA level 2 intermediate" - -openssl req -nodes \ - -newkey rsa:2048 \ - -keyout end.key \ - -out end.req \ - -sha256 \ - -batch \ - -subj "/CN=testserver.com" - -openssl rsa \ - -in end.key \ - -out server.rsa - -openssl x509 -req \ - -in inter.req \ - -out inter.cert \ - -CA ca.cert \ - -CAkey ca.key \ - -sha256 \ - -days 3650 \ - -set_serial 123 \ - -extensions v3_inter -extfile ../openssl.cnf - -openssl x509 -req \ - -in end.req \ - -out end.cert \ - -CA inter.cert \ - -CAkey inter.key \ - -sha256 \ - -days 2000 \ - -set_serial 456 \ - -extensions v3_end -extfile ../openssl.cnf - -cat end.cert inter.cert ca.cert > server.pem -rm *.key *.cert *.req +set -e +generate_certificates() +{ + out="$1" + name="$2" + printf "generating $out for $name\t" + openssl req -nodes \ + -x509 \ + -days 3650 \ + -newkey rsa:4096 \ + -keyout ca.key \ + -out ca.cert \ + -sha256 \ + -batch \ + -subj "/CN=$name RSA CA" + + openssl req -nodes \ + -newkey rsa:3072 \ + -keyout inter.key \ + -out inter.req \ + -sha256 \ + -batch \ + -subj "/CN=$name RSA level 2 intermediate" + + openssl req -nodes \ + -newkey rsa:2048 \ + -keyout end.key \ + -out end.req \ + -sha256 \ + -batch \ + -subj "/CN=testserver.com" + + openssl rsa \ + -in end.key \ + -out $out.rsa + + openssl x509 -req \ + -in inter.req \ + -out inter.cert \ + -CA ca.cert \ + -CAkey ca.key \ + -sha256 \ + -days 3650 \ + -set_serial 123 \ + -extensions v3_inter -extfile openssl.cnf + + openssl x509 -req \ + -in end.req \ + -out end.cert \ + -CA inter.cert \ + -CAkey inter.key \ + -sha256 \ + -days 2000 \ + -set_serial 456 \ + -extensions v3_end -extfile openssl.cnf + + cat end.cert inter.cert ca.cert > $out.pem + rm *.key *.cert *.req + printf "done\n" +} + +generate_certificates "server" "ponytown" +generate_certificates "client1" "onehause" +generate_certificates "client2" "zweidorf" +mkdir -p clients/certificates +mkdir -p clients/keys +mv client*.pem clients/certificates/ +mv client*.rsa clients/keys/ diff --git a/rust/openvasd/Cargo.toml b/rust/openvasd/Cargo.toml index 40fdd9ccec..11cec1e0ff 100644 --- a/rust/openvasd/Cargo.toml +++ b/rust/openvasd/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1.0.96" serde = { version = "1.0.163", features = ["derive"] } uuid = {version = "1", features = ["v4", "fast-rng", "serde"]} hyper-rustls = "0.24.0" -rustls = "0.21.1" +rustls = {version = "0.21.1", features = ["secret_extraction", "dangerous_configuration"]} tokio-rustls = "0.24.0" futures-util = "0.3.28" rustls-pemfile = "1.0.2" diff --git a/rust/openvasd/src/config.rs b/rust/openvasd/src/config.rs index 1547d687db..5b2fe2265d 100644 --- a/rust/openvasd/src/config.rs +++ b/rust/openvasd/src/config.rs @@ -188,8 +188,9 @@ impl Config { where P: AsRef + std::fmt::Display, { - let config = std::fs::read_to_string(path).unwrap_or_default(); - toml::from_str(&config).unwrap_or_default() + println!("loading {path} ..."); + let config = std::fs::read_to_string(path).unwrap(); + toml::from_str(&config).unwrap() } pub fn load() -> Self { @@ -330,6 +331,7 @@ impl Config { let mut config = match cmds.get_one::("config") { Some(path) => Self::from_file(path), None => { + println!("no config provided"); if let Some(config) = Self::load_user() { config } else { diff --git a/rust/openvasd/src/controller/context.rs b/rust/openvasd/src/controller/context.rs index 50a1e389df..a68e64aa95 100644 --- a/rust/openvasd/src/controller/context.rs +++ b/rust/openvasd/src/controller/context.rs @@ -100,7 +100,8 @@ impl ContextBuilder { if let Some(fp) = self.feed_config.as_ref() { let loader = nasl_interpreter::FSPluginLoader::new(fp.path.clone()); let dispatcher: DefaultDispatcher = DefaultDispatcher::default(); - let version = feed::version(&loader, &dispatcher).unwrap(); + let version = + feed::version(&loader, &dispatcher).unwrap_or_else(|_| String::from("UNDEFINED")); self.response.set_feed_version(&version); } self diff --git a/rust/openvasd/src/controller/entry.rs b/rust/openvasd/src/controller/entry.rs index 4ba9a1faa9..86414cce65 100644 --- a/rust/openvasd/src/controller/entry.rs +++ b/rust/openvasd/src/controller/entry.rs @@ -6,12 +6,18 @@ //! //! All known paths must be handled in the entrypoint function. -use std::{fmt::Display, sync::Arc}; +use std::{ + fmt::Display, + sync::{Arc, RwLock}, +}; -use super::context::Context; +use super::{context::Context, ClientIdentifier}; use hyper::{Body, Method, Request, Response}; -use crate::scan::{self, Error, ScanDeleter, ScanStarter, ScanStopper}; +use crate::{ + scan::{self, Error, ScanDeleter, ScanStarter, ScanStopper}, + tls::TlsStream, +}; enum HealthOpts { /// Ready @@ -60,7 +66,7 @@ impl KnownPaths { Some("alive") => KnownPaths::Health(HealthOpts::Alive), Some("started") => KnownPaths::Health(HealthOpts::Started), _ => KnownPaths::Unknown, - } + }, _ => { tracing::trace!("Unknown path: {path}"); KnownPaths::Unknown @@ -95,6 +101,7 @@ impl Display for KnownPaths { pub async fn entrypoint<'a, S, DB>( req: Request, ctx: Arc>, + num: Arc>, ) -> Result, Error> where S: ScanStarter @@ -108,11 +115,12 @@ where { use KnownPaths::*; // on head requests we just return an empty response without checking the api key - tracing::trace!( - "{} {}:{:?}", + tracing::warn!( + "{} {}:{:?} -> {:?}", req.method(), req.uri().path(), - req.uri().query() + req.uri().query(), + num.read().unwrap(), ); if req.method() == Method::HEAD { return Ok(ctx.response.empty(hyper::StatusCode::OK)); @@ -133,9 +141,9 @@ where } match (req.method(), kp) { - (&Method::GET, Health(HealthOpts::Alive)) | - (&Method::GET, Health(HealthOpts::Started)) => - Ok(ctx.response.empty(hyper::StatusCode::OK)), + (&Method::GET, Health(HealthOpts::Alive)) | (&Method::GET, Health(HealthOpts::Started)) => { + Ok(ctx.response.empty(hyper::StatusCode::OK)) + } (&Method::GET, Health(HealthOpts::Ready)) => { let oids = ctx.db.oids().await?; if oids.count() == 0 { diff --git a/rust/openvasd/src/controller/mod.rs b/rust/openvasd/src/controller/mod.rs index 6e9ddb7fe7..0b9fa3dc01 100644 --- a/rust/openvasd/src/controller/mod.rs +++ b/rust/openvasd/src/controller/mod.rs @@ -7,6 +7,8 @@ mod entry; pub mod feed; pub mod results; +use std::default; + use crate::scan::{ScanDeleter, ScanResultFetcher, ScanStarter, ScanStopper}; pub use context::{Context, ContextBuilder, NoOpScanner}; pub use entry::entrypoint; @@ -20,29 +22,28 @@ pub(crate) fn quit_on_poison() -> T { /// Combines all traits needed for a scanner. pub trait Scanner: ScanStarter + ScanStopper + ScanDeleter + ScanResultFetcher {} -impl Scanner for T where T: ScanStarter + ScanStopper + ScanDeleter + ScanResultFetcher {} - -macro_rules! make_svc { - ($controller:expr) => {{ - // start background service - use std::sync::Arc; - - tokio::spawn(crate::controller::results::fetch(Arc::clone(&$controller))); - tokio::spawn(crate::controller::feed::fetch(Arc::clone(&$controller))); - - use hyper::service::{make_service_fn, service_fn}; - make_service_fn(|_conn| { - let controller = Arc::clone($controller); - async { - Ok::<_, crate::scan::Error>(service_fn(move |req| { - crate::controller::entrypoint(req, Arc::clone(&controller)) - })) - } - }) - }}; +/// Contains information about an authorization model of a connection (e.g. mtls) +#[derive(Default, Debug)] +pub enum ClientIdentifier { + /// When there in no information available + #[default] + Unknown, + /// Contains a hashed number of an identifier + /// + /// openvasd uses the identifier as a key for results. This key is usually calculated by an + /// subject of a known client certificate. Based on that we don't need more information. + Known(u64), +} +/// Is used to transfer the information if there is an identifier present within the connection +pub trait ClientInformationRetriever { + /// Gets the identifier + /// + /// Based on the concurrent nature, the actual information is boxed within a Arc and locked for + /// concurrent read write accesses. + fn client_identifier(&self) -> &std::sync::Arc>; } -pub(crate) use make_svc; +impl Scanner for T where T: ScanStarter + ScanStopper + ScanDeleter + ScanResultFetcher {} #[cfg(test)] mod tests { diff --git a/rust/openvasd/src/main.rs b/rust/openvasd/src/main.rs index ac8cd86c66..1e4059e08c 100644 --- a/rust/openvasd/src/main.rs +++ b/rust/openvasd/src/main.rs @@ -12,6 +12,55 @@ mod scan; mod storage; mod tls; +async fn serve<'a, DB, I>( + db: DB, + config: config::Config, + inc: I, +) -> Result<(), Box> +where + I: hyper::server::accept::Accept, + I::Error: Into>, + I::Conn: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static, + DB: crate::storage::Storage + std::marker::Send + 'static + std::marker::Sync, +{ + // let scanner = scan::OSPDWrapper::new(config.ospd.socket.clone(), config.ospd.read_timeout); + // let rc = config.ospd.result_check_interval; + // let fc = ( + // config.feed.path.clone(), + // config.feed.check_interval, + // config.feed.signature_check, + // ); + // let ctx = controller::ContextBuilder::new() + // .result_config(rc) + // .feed_config(fc) + // .scanner(scanner) + // .api_key(config.endpoints.key.clone()) + // .enable_get_scans(config.endpoints.enable_get_scans) + // .storage(db) + // .build(); + // let controller = std::sync::Arc::new(ctx); + // let make_svc = { + // use std::sync::Arc; + + // tokio::spawn(crate::controller::results::fetch(Arc::clone(&controller))); + // tokio::spawn(crate::controller::feed::fetch(Arc::clone(&controller))); + + // use hyper::service::{make_service_fn, service_fn}; + // make_service_fn(|_conn| { + // let controller = Arc::clone(&controller); + // async { + // Ok::<_, crate::scan::Error>(service_fn(move |req| { + // crate::controller::entrypoint(req, Arc::clone(&controller)) + // })) + // } + // }) + // }; + // // TODO: change to return server + // let server = hyper::Server::builder(inc).serve(make_svc); + // server.await?; + Ok(()) +} + pub async fn run<'a, DB>( db: DB, config: config::Config, @@ -19,37 +68,56 @@ pub async fn run<'a, DB>( where DB: crate::storage::Storage + std::marker::Send + 'static + std::marker::Sync, { - let scanner = scan::OSPDWrapper::new(config.ospd.socket.clone(), config.ospd.read_timeout); - let rc = config.ospd.result_check_interval; - let fc = ( - config.feed.path.clone(), - config.feed.check_interval, - config.feed.signature_check, - ); - let ctx = controller::ContextBuilder::new() - .result_config(rc) - .feed_config(fc) - .scanner(scanner) - .api_key(config.endpoints.key.clone()) - .enable_get_scans(config.endpoints.enable_get_scans) - .storage(db) - .build(); - let controller = std::sync::Arc::new(ctx); let addr = config.listener.address; let incoming = hyper::server::conn::AddrIncoming::bind(&addr)?; let addr = incoming.local_addr(); - - if let Some(tlsc) = tls::tls_config(&config)? { - tracing::trace!("TLS enabled"); - let make_svc = crate::controller::make_svc!(&controller); - let server = hyper::Server::builder(tls::TlsAcceptor::new(tlsc, incoming)).serve(make_svc); + if let Some((roots, certs, key)) = tls::tls_config(&config)? { tracing::info!("listening on https://{}", addr); + let inc = tls::TlsAcceptor::new(roots, certs, key, incoming); + let scanner = scan::OSPDWrapper::new(config.ospd.socket.clone(), config.ospd.read_timeout); + let rc = config.ospd.result_check_interval; + let fc = ( + config.feed.path.clone(), + config.feed.check_interval, + config.feed.signature_check, + ); + let ctx = controller::ContextBuilder::new() + .result_config(rc) + .feed_config(fc) + .scanner(scanner) + .api_key(config.endpoints.key.clone()) + .enable_get_scans(config.endpoints.enable_get_scans) + .storage(db) + .build(); + let controller = std::sync::Arc::new(ctx); + let make_svc = { + use std::sync::Arc; + + tokio::spawn(crate::controller::results::fetch(Arc::clone(&controller))); + tokio::spawn(crate::controller::feed::fetch(Arc::clone(&controller))); + + use hyper::service::{make_service_fn, service_fn}; + make_service_fn(|conn: &crate::tls::TlsStream| { + let controller = Arc::clone(&controller); + let num = Arc::clone(&conn.client_identifier); + tracing::warn!("mmmmmooooeeeepppp23"); + async { + Ok::<_, crate::scan::Error>(service_fn(move |req| { + crate::controller::entrypoint( + req, + Arc::clone(&controller), + Arc::clone(&num), + ) + })) + } + }) + }; + // TODO: change to return server + let server = hyper::Server::builder(inc).serve(make_svc); server.await?; } else { - let make_svc = crate::controller::make_svc!(&controller); - let server = hyper::Server::builder(incoming).serve(make_svc); tracing::info!("listening on http://{}", addr); - server.await?; + serve(db, config, incoming).await?; } Ok(()) } @@ -60,6 +128,7 @@ async fn main() -> Result<(), Box> { let filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::metadata::LevelFilter::INFO.into()) .parse_lossy(config.log.level.clone()); + tracing::debug!("config: {:?}", config); tracing_subscriber::fmt().with_env_filter(filter).init(); if !config.ospd.socket.exists() { tracing::warn!("OSPD socket {} does not exist. Some commands will not work until the socket is created!", config.ospd.socket.display()); diff --git a/rust/openvasd/src/tls.rs b/rust/openvasd/src/tls.rs index 46e0663758..7f7de4c325 100644 --- a/rust/openvasd/src/tls.rs +++ b/rust/openvasd/src/tls.rs @@ -29,17 +29,22 @@ use core::task::{Context, Poll}; use futures_util::{ready, Future}; use hyper::server::accept::Accept; use hyper::server::conn::{AddrIncoming, AddrStream}; -use rustls::server::{AllowAnyAuthenticatedClient, NoClientAuth}; +use rustls::server::{ + AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, ClientCertVerifier, + NoClientAuth, +}; use rustls::RootCertStore; use rustls_pemfile::{read_one, Item}; use std::path::{Path, PathBuf}; use std::pin::Pin; -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use std::{fs, io}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio_rustls::rustls::ServerConfig; +use crate::controller::{ClientIdentifier, ClientInformationRetriever}; + enum State { Handshaking(tokio_rustls::Accept), Streaming(tokio_rustls::server::TlsStream), @@ -53,17 +58,38 @@ enum State { /// On streaming the connection will be read/written. pub struct TlsStream { state: State, + pub client_identifier: Arc>, } impl TlsStream { - fn new(stream: AddrStream, config: Arc) -> TlsStream { + fn new( + stream: AddrStream, + + roots: RootCertStore, + certs: Vec, + key: rustls::PrivateKey, + ) -> TlsStream { + let client_identifier = Arc::new(RwLock::new(ClientIdentifier::default())); + let inner = AllowAnyAuthenticatedClient::new(roots); + let verifier = SnitchAuthenticatedClients::new(inner, client_identifier.clone()).boxed(); + let config: Arc = Arc::new(server_config(verifier, certs, key).unwrap()); + let accept = tokio_rustls::TlsAcceptor::from(config).accept(stream); TlsStream { state: State::Handshaking(accept), + client_identifier, } } } +impl ClientInformationRetriever for TlsStream { + fn client_identifier( + &self, + ) -> &std::sync::Arc> { + &self.client_identifier + } +} + impl AsyncRead for TlsStream { fn poll_read( self: Pin<&mut Self>, @@ -74,7 +100,10 @@ impl AsyncRead for TlsStream { match pin.state { State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) { Ok(mut stream) => { + tracing::warn!("mmmmmooooeeeepppp"); let result = Pin::new(&mut stream).poll_read(cx, buf); + // let mut num = pin.num.write().unwrap(); + // *num = 42; pin.state = State::Streaming(stream); result } @@ -122,13 +151,25 @@ impl AsyncWrite for TlsStream { /// Handles the actual tls connection based on the given address and config. pub struct TlsAcceptor { - config: Arc, + roots: RootCertStore, + certs: Vec, + key: rustls::PrivateKey, incoming: AddrIncoming, } impl TlsAcceptor { - pub fn new(config: Arc, incoming: AddrIncoming) -> TlsAcceptor { - TlsAcceptor { config, incoming } + pub fn new( + roots: RootCertStore, + certs: Vec, + key: rustls::PrivateKey, + incoming: AddrIncoming, + ) -> TlsAcceptor { + TlsAcceptor { + roots, + certs, + key, + incoming, + } } } @@ -142,13 +183,99 @@ impl Accept for TlsAcceptor { ) -> Poll>> { let pin = self.get_mut(); match ready!(Pin::new(&mut pin.incoming).poll_accept(cx)) { - Some(Ok(sock)) => Poll::Ready(Some(Ok(TlsStream::new(sock, pin.config.clone())))), + Some(Ok(sock)) => Poll::Ready(Some(Ok(TlsStream::new( + sock, + pin.roots.clone(), + pin.certs.clone(), + pin.key.clone(), + )))), Some(Err(e)) => Poll::Ready(Some(Err(e))), None => Poll::Ready(None), } } } +struct SnitchAuthenticatedClients { + inner: AllowAnyAuthenticatedClient, + client_identifier: Arc>, +} + +impl SnitchAuthenticatedClients { + /// Construct a new `AllowAnyAnonymousOrAuthenticatedClient`. + /// + /// `roots` is the list of trust anchors to use for certificate validation. + pub fn new( + inner: AllowAnyAuthenticatedClient, + client_identifier: Arc>, + ) -> Self { + Self { + inner, + client_identifier, + } + } + + /// Update the verifier to validate client certificates against the provided DER format + /// unparsed certificate revocation lists (CRLs). + pub fn with_crls( + self, + crls: impl IntoIterator, + client_identifier: Arc>, + ) -> Result { + Ok(Self { + inner: self.inner.with_crls(crls)?, + client_identifier, + }) + } + + /// Wrap this verifier in an [`Arc`] and coerce it to `dyn ClientCertVerifier` + #[inline(always)] + pub fn boxed(self) -> Arc { + // This function is needed because `ClientCertVerifier` is only reachable if the + // `dangerous_configuration` feature is enabled, which makes coercing hard to outside users + Arc::new(self) + } +} + +impl rustls::server::ClientCertVerifier for SnitchAuthenticatedClients { + fn offer_client_auth(&self) -> bool { + self.inner.offer_client_auth() + } + + fn client_auth_mandatory(&self) -> bool { + false + } + + fn client_auth_root_subjects(&self) -> &[rustls::DistinguishedName] { + self.inner.client_auth_root_subjects() + } + + fn verify_client_cert( + &self, + end_entity: &rustls::Certificate, + intermediates: &[rustls::Certificate], + now: std::time::SystemTime, + ) -> Result { + tracing::warn!("verify client cerrrrrrrrttttt"); + match self + .inner + .verify_client_cert(end_entity, intermediates, now) + { + Ok(r) => { + let mut ci = self.client_identifier.write().unwrap(); + let ehm: u64 = end_entity + .0 + .iter() + .map(|x| *x as u64) + .reduce(|a, b| a + b) + .unwrap_or_default(); + tracing::warn!("narg: {ehm}"); + *ci = ClientIdentifier::Known(ehm); + Ok(r) + } + Err(_) => todo!(), + } + } +} /// Creates a rustls ServerConfig based on the given config. /// /// When the tls certificate cannot be loaded it will return None. @@ -157,15 +284,23 @@ impl Accept for TlsAcceptor { /// client authentication. pub fn tls_config( config: &crate::config::Config, -) -> Result>, Box> { +) -> Result< + Option<(RootCertStore, Vec, rustls::PrivateKey)>, + Box, +> { if let Some(certs_path) = &config.tls.certs { match load_certs(certs_path) { Ok(certs) => { if let Some(key_path) = &config.tls.key { let key = load_private_key(key_path)?; - let verifier = { - if let Some(client_certs_dir) = &config.tls.client_certs { - let client_certs: Vec = std::fs::read_dir(client_certs_dir)? + let client_certs = config + .clone() + .tls + .client_certs + .map(|p| { + let client_certs: Vec = std::fs::read_dir(p) + // TODO create proper return statement + .unwrap() .filter_map(|entry| { let entry = entry.ok()?; let file_type = entry.file_type().ok()?; @@ -178,39 +313,16 @@ pub fn tls_config( } }) .collect(); - if client_certs.is_empty() { - tracing::info!( - "no client certs found, starting without certificate based client auth" - ); - NoClientAuth::boxed() - } else { - tracing::info!( - "client certs found, starting with certificate based client auth" - ); - let mut client_auth_roots = RootCertStore::empty(); - for root in client_certs.iter().flat_map(load_certs).flatten() { - client_auth_roots.add(&root)?; - } - AllowAnyAuthenticatedClient::new(client_auth_roots).boxed() - } - } else { - tracing::info!( - "no client certs found, starting without certificate based client auth" - ); - NoClientAuth::boxed() - } - }; - - let mut cfg = rustls::ServerConfig::builder() - .with_safe_defaults() - //.with_client_cert_verifier() - .with_client_cert_verifier(verifier) - .with_single_cert(certs, key) - .map_err(|e| error(format!("{}", e)))?; - // Configure ALPN to accept HTTP/2, HTTP/1.1, and HTTP/1.0 in that order. - cfg.alpn_protocols = - vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; - Ok(Some(std::sync::Arc::new(cfg))) + client_certs + }) + .unwrap_or_default(); + + let mut roots = RootCertStore::empty(); + for root in client_certs.iter().flat_map(load_certs).flatten() { + roots.add(&root)?; + } + + Ok(Some((roots, certs, key))) } else { Err(error("TLS enabled, but private key is missing".to_string()).into()) } @@ -223,6 +335,20 @@ pub fn tls_config( } } +fn server_config( + verifier: Arc, + certs: Vec, + key: rustls::PrivateKey, +) -> Result> { + let mut cfg = rustls::ServerConfig::builder() + .with_safe_defaults() + .with_client_cert_verifier(verifier) + .with_single_cert(certs, key) + .map_err(|e| error(format!("{}", e)))?; + cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; + Ok(cfg) +} + fn error(err: String) -> io::Error { io::Error::new(io::ErrorKind::Other, err) } diff --git a/rust/openvasd/tests/authorization.rs b/rust/openvasd/tests/authorization.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rust/openvasd/tests/data/generate-test-keys.sh b/rust/openvasd/tests/data/generate-test-keys.sh new file mode 100644 index 0000000000..35a6849384 --- /dev/null +++ b/rust/openvasd/tests/data/generate-test-keys.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2023 Greenbone AG +# +# SPDX-License-Identifier: GPL-2.0-or-later + +set -e +generate_certificates() +{ + out="$1" + name="$2" + openssl req -nodes \ + -x509 \ + -days 3650 \ + -newkey rsa:4096 \ + -keyout ca.key \ + -out ca.cert \ + -sha256 \ + -batch \ + -subj "/CN=$name RSA CA" + + openssl req -nodes \ + -newkey rsa:3072 \ + -keyout inter.key \ + -out inter.req \ + -sha256 \ + -batch \ + -subj "/CN=$name RSA level 2 intermediate" + + openssl req -nodes \ + -newkey rsa:2048 \ + -keyout end.key \ + -out end.req \ + -sha256 \ + -batch \ + -subj "/CN=testserver.com" + + openssl rsa \ + -in end.key \ + -out $out.rsa + + openssl x509 -req \ + -in inter.req \ + -out inter.cert \ + -CA ca.cert \ + -CAkey ca.key \ + -sha256 \ + -days 3650 \ + -set_serial 123 \ + -extensions v3_inter -extfile openssl.cnf + + openssl x509 -req \ + -in end.req \ + -out end.cert \ + -CA inter.cert \ + -CAkey inter.key \ + -sha256 \ + -days 2000 \ + -set_serial 456 \ + -extensions v3_end -extfile openssl.cnf + + cat end.cert inter.cert ca.cert > $out.pem + rm *.key *.cert *.req + printf "generated $out\n" +} + +generate_certificates "server" "ponytown" +generate_certificates "client1" "onehause" +generate_certificates "client2" "zweidorf" +mkdir -p clients/certificates +mkdir -p clients/keys +mv client*.pem clients/certificates/ +mv client*.rsa clients/keys/ + diff --git a/rust/openvasd/tests/data/openssl.cnf b/rust/openvasd/tests/data/openssl.cnf new file mode 100644 index 0000000000..d0701c4328 --- /dev/null +++ b/rust/openvasd/tests/data/openssl.cnf @@ -0,0 +1,25 @@ + +[ v3_end ] +basicConstraints = critical,CA:false +keyUsage = nonRepudiation, digitalSignature +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +subjectAltName = @alt_names + +[ v3_client ] +basicConstraints = critical,CA:false +keyUsage = nonRepudiation, digitalSignature +extendedKeyUsage = critical, clientAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always + +[ v3_inter ] +subjectKeyIdentifier = hash +extendedKeyUsage = critical, serverAuth, clientAuth +basicConstraints = CA:true +keyUsage = cRLSign, keyCertSign, digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign + +[ alt_names ] +DNS.1 = localhost.localdomain +DNS.2 = localhost4.localdomain4 +DNS.3 = localhost