diff options
author | metamuffin <metamuffin@disroot.org> | 2024-08-19 02:52:14 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-08-19 02:52:14 +0200 |
commit | 6c3524c381467483a025eda5e7e5f0ded53094fa (patch) | |
tree | 0d081ed4bc18f0a950783a5fe8e1fe4e08c888d1 | |
parent | b005bbca6d8c1adb9c12e10d79028717b9d963c5 (diff) | |
download | gnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar gnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar.bz2 gnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar.zst |
paths module
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | readme.md | 23 | ||||
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/modules/mod.rs | 2 | ||||
-rw-r--r-- | src/modules/paths.rs | 54 |
6 files changed, 83 insertions, 12 deletions
@@ -607,6 +607,7 @@ dependencies = [ "percent-encoding", "pin-project", "rand", + "regex", "ring", "rustls", "rustls-pemfile", @@ -16,7 +16,7 @@ hex = "0.4.3" # TLS rustls-pemfile = "2.1.3" -rustls = { version="0.23.12", default-features = false, features=["ring"] } +rustls = { version = "0.23.12", default-features = false, features = ["ring"] } tokio-rustls = "0.26.0" rustls-webpki = "0.102.6" ring = "0.17.8" @@ -42,13 +42,14 @@ markup = "0.15.0" humansize = "2.1.3" mime_guess = "2.0.5" -bytes = "1.7.1" -anyhow = "1.0.86" -thiserror = "1.0.63" - # Crypto for authentificating clients aes-gcm-siv = "0.11.1" argon2 = "0.6.0-pre.1" rand = "0.9.0-alpha.2" -users = "0.11.0" +# Other helpers and stuff +bytes = "1.7.1" +anyhow = "1.0.86" +thiserror = "1.0.63" +regex = "1.10.6" +users = "0.11.0"
\ No newline at end of file @@ -33,8 +33,9 @@ http: https: bind: "[::1]:8443" - tls_cert: "ssl/cert.pem" - tls_key: "ssl/key.pem" # only accepts pkcs8 + cert_path: "/etc/letsencrypt/live" # Automatically scans and selects certificates + # tls_cert: "certs/fullchain.pem" + # tls_key: "certs/privkey.pem" # !hosts multiplexes requests for different hostnames. handler: !hosts @@ -60,12 +61,17 @@ The configuration uses YAML formatting. When the configuration file is changed, it will automatically be loaded and applied if valid. - **section `http`** - - `bind`: string or list of strings with addresses to listen on. + - Optional section. Omit to disable unencrypted http. + - `bind`: Addresses to accept http requests on (string or list of strings). - **section `https`** - - `bind`: string or list of strings with addresses to listen on. - - `tls_cert`: path to the SSL certificate. (Sometimes called `fullchain.pem`) - - `tls_key`: path to the SSL key. (Often called `key.pem` or `privkey.pem`) + - Optional section. Omit to disable https. + - `bind`: Addresses to accept https requests on (string or list of strings). + - `cert_path`: Path to a directory structure that certificates are loaded + from. The hierachy should contain directories containing corresponding + `fullchain.pem` and `privkey.pem` files. The correct certificate is selected + automatically by subject (`CN`). Pointing this directly at + `/etc/letsencrypt/live` is possible. (string or list of strings) - **section `limits`** - Note: Make sure you do not exceed the maximum file descriptor limit on your @@ -92,6 +98,11 @@ themselves; in that case the request is passed on. - Hands over the requests to different modules depending on the `host` header. - Takes a map from hostname (string) to handler (module) +- **module `paths`** + - Routes requests by matching the path against regexes. + - Takes a map from a global regex that is applied to the path (string) to + handler (module) + - **module `proxy`** - Forwards the request as-is to some other server. the `x-real-ip` header is injected into the request. Connection upgrades are handled by direct diff --git a/src/error.rs b/src/error.rs index 14b2842..e6f7646 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,8 @@ pub enum ServiceError { NoHost, #[error("unknown host")] UnknownHost, + #[error("unknown path")] + UnknownPath, #[error("can't connect to the backend")] CantConnect, #[error("not found")] diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 987646f..dfb1f02 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -15,6 +15,7 @@ pub mod file; pub mod files; pub mod headers; pub mod hosts; +pub mod paths; pub mod proxy; pub mod redirect; pub mod switch; @@ -27,6 +28,7 @@ pub static MODULES: &[&dyn NodeKind] = &[ &auth::cookie::CookieAuthKind, &proxy::ProxyKind, &hosts::HostsKind, + &paths::PathsKind, &files::FilesKind, &file::FileKind, &accesslog::AccessLogKind, diff --git a/src/modules/paths.rs b/src/modules/paths.rs new file mode 100644 index 0000000..b994d48 --- /dev/null +++ b/src/modules/paths.rs @@ -0,0 +1,54 @@ +use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; +use crate::{config::DynNode, error::ServiceError}; +use futures::Future; +use regex::RegexSet; +use serde_yaml::Value; +use std::{collections::BTreeMap, pin::Pin, sync::Arc}; + +pub struct PathsKind; + +struct Paths { + matcher: RegexSet, + handlers: Vec<DynNode>, +} + +impl NodeKind for PathsKind { + fn name(&self) -> &'static str { + "paths" + } + fn instanciate(&self, config: Value) -> anyhow::Result<Arc<dyn Node>> { + let routes = serde_yaml::from_value::<BTreeMap<String, DynNode>>(config)? + .into_iter() + .collect::<Vec<(String, DynNode)>>(); + + let mut handlers = Vec::new(); + let mut patterns = Vec::new(); + for (k, v) in routes { + handlers.push(v); + patterns.push(format!("^{}$", k)); + } + let matcher = RegexSet::new(&patterns)?; + + Ok(Arc::new(Paths { handlers, matcher })) + } +} +impl Node for Paths { + fn handle<'a>( + &'a self, + context: &'a mut NodeContext, + request: NodeRequest, + ) -> Pin<Box<dyn Future<Output = Result<NodeResponse, ServiceError>> + Send + Sync + 'a>> { + Box::pin(async move { + let path = request.uri().path(); + + let index = self + .matcher + .matches(path) + .iter() + .next() + .ok_or(ServiceError::UnknownPath)?; + + self.handlers[index].handle(context, request).await + }) + } +} |