summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-08-19 02:52:14 +0200
committermetamuffin <metamuffin@disroot.org>2024-08-19 02:52:14 +0200
commit6c3524c381467483a025eda5e7e5f0ded53094fa (patch)
tree0d081ed4bc18f0a950783a5fe8e1fe4e08c888d1
parentb005bbca6d8c1adb9c12e10d79028717b9d963c5 (diff)
downloadgnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar
gnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar.bz2
gnix-6c3524c381467483a025eda5e7e5f0ded53094fa.tar.zst
paths module
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml13
-rw-r--r--readme.md23
-rw-r--r--src/error.rs2
-rw-r--r--src/modules/mod.rs2
-rw-r--r--src/modules/paths.rs54
6 files changed, 83 insertions, 12 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5d0294d..11cf932 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -607,6 +607,7 @@ dependencies = [
"percent-encoding",
"pin-project",
"rand",
+ "regex",
"ring",
"rustls",
"rustls-pemfile",
diff --git a/Cargo.toml b/Cargo.toml
index a9b4586..bc18ba9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/readme.md b/readme.md
index 609d420..5cda4d6 100644
--- a/readme.md
+++ b/readme.md
@@ -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
+ })
+ }
+}