diff --git a/Cargo.toml b/Cargo.toml index df16fc5..fe92d32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "0.3.2" +version = "0.3.3" authors = ["Jun Kurihara"] homepage = "https://github.com/junkurihara/modoh-server" repository = "https://github.com/junkurihara/modoh-server" diff --git a/docker-otel/docker-compose.yml b/docker-otel/docker-compose.yml index e703815..8148d69 100644 --- a/docker-otel/docker-compose.yml +++ b/docker-otel/docker-compose.yml @@ -3,6 +3,7 @@ services: image: otel/opentelemetry-collector-contrib:latest container_name: otel-collector restart: unless-stopped + user: "0:0" # run as root to access docker socket for env detection ports: - 127.0.0.1:4317:4317 # gRPC - 127.0.0.1:13133:13133 # health check @@ -13,8 +14,8 @@ services: - 8889 # Prometheus exporter metrics - 8888 # Prometheus metrics exposed by the collector volumes: - - ./otel-config.yml:/etc/otel-config.yml - - /var/run/docker.sock:/var/run/docker.sock # docker env detector + - ./otel-config.yml:/etc/otel-config.yml:ro + - /var/run/docker.sock:/var/run/docker.sock:ro # docker env detector # - ./client_crt:/client_crt:ro # Needed to deploy on the internet command: ["--config=/etc/otel-config.yml"] logging: @@ -62,39 +63,39 @@ services: options: max-size: "10m" max-file: "3" - depends_on: - - rclone + # depends_on: + # - rclone networks: - net-otel - # Prometheus Remote Write Endpoint Storage (S3) - rclone: - image: rclone/rclone:latest - expose: - - 58080 - container_name: rclone - security_opt: - - apparmor:unconfined - cap_add: - - SYS_ADMIN - # devices: - # - /dev/fuse - env_file: .env - entrypoint: | - sh -c " - rclone serve s3 --auth-key ${ACCESS_KEY_ID},${SECRET_ACCESS_KEY} ${RCLONE_SERVER_NAME}:${S3_ROOT} --vfs-cache-mode writes --addr 0.0.0.0:58080 & \ - wait - " - restart: always - volumes: - - ~/.config/rclone/rclone.conf:/config/rclone/rclone.conf:ro - - /tmp/rclone-cache:/root/.cache/ - logging: - options: - max-size: "10m" - max-file: "3" - networks: - net-otel: + # # Prometheus Remote Write Endpoint Storage (S3) + # rclone: + # image: rclone/rclone:latest + # expose: + # - 58080 + # container_name: rclone + # security_opt: + # - apparmor:unconfined + # cap_add: + # - SYS_ADMIN + # # devices: + # # - /dev/fuse + # env_file: .env + # entrypoint: | + # sh -c " + # rclone serve s3 --auth-key ${ACCESS_KEY_ID},${SECRET_ACCESS_KEY} ${RCLONE_SERVER_NAME}:${S3_ROOT} --vfs-cache-mode writes --addr 0.0.0.0:58080 & \ + # wait + # " + # restart: always + # volumes: + # - ~/.config/rclone/rclone.conf:/config/rclone/rclone.conf:ro + # - /tmp/rclone-cache:/root/.cache/ + # logging: + # options: + # max-size: "10m" + # max-file: "3" + # networks: + # net-otel: # Visualize metrics grafana: diff --git a/docker-otel/mimir.yml b/docker-otel/mimir.yml index 819502c..7d3b2bc 100644 --- a/docker-otel/mimir.yml +++ b/docker-otel/mimir.yml @@ -5,14 +5,14 @@ target: all,alertmanager,overrides-exporter common: - storage: - backend: s3 - s3: - endpoint: rclone:58080 - secret_access_key: "${SECRET_ACCESS_KEY}" # This is a secret injected via an environment variable - access_key_id: "${ACCESS_KEY_ID}" # This is a secret injected via an environment variable - bucket_name: "${S3_BUCKET_NAME}" # This is a secret injected via an environment variable - insecure: true + # storage: + # backend: s3 + # s3: + # endpoint: rclone:58080 + # secret_access_key: "${SECRET_ACCESS_KEY}" # This is a secret injected via an environment variable + # access_key_id: "${ACCESS_KEY_ID}" # This is a secret injected via an environment variable + # bucket_name: "${S3_BUCKET_NAME}" # This is a secret injected via an environment variable + # insecure: true # Blocks storage requires a prefix when using a common object storage bucket. blocks_storage: diff --git a/docker/Dockerfile-slim b/docker/Dockerfile-slim index 79553d0..df2b0fd 100644 --- a/docker/Dockerfile-slim +++ b/docker/Dockerfile-slim @@ -2,7 +2,7 @@ FROM rust:alpine AS builder ARG CFLAGS=-Ofast -ARG BUILD_DEPS=musl-dev +ARG BUILD_DEPS="musl-dev pkgconfig openssl-dev openssl-libs-static" # ARG BUILD_DEPS="curl make ca-certificates build-essential pkg-config libssl-dev" diff --git a/httpsig-registry/Cargo.toml b/httpsig-registry/Cargo.toml index 19558d8..f578819 100644 --- a/httpsig-registry/Cargo.toml +++ b/httpsig-registry/Cargo.toml @@ -13,14 +13,14 @@ edition.workspace = true publish.workspace = true [dependencies] -anyhow = { version = "1.0.99" } -thiserror = { version = "2.0.16" } +anyhow = { version = "1.0.101" } +thiserror = { version = "2.0.18" } pulldown-cmark = { version = "0.13.0", default-features = false } -http = { version = "1.3.1" } -indexmap = { version = "2.11.1" } +http = { version = "1.4.0" } +indexmap = { version = "2.13.0" } minisign-verify = { version = "0.2.4" } -reqwest = { version = "0.12.23", default-features = false, features = [ - "rustls-tls", +reqwest = { version = "0.13.2", default-features = false, features = [ + "native-tls", "http2", "hickory-dns", ] } @@ -28,7 +28,7 @@ futures = { version = "0.3.31", default-features = false, features = [ "std", "async-await", ] } -tokio = { version = "1.47.1", features = [ +tokio = { version = "1.49.0", features = [ "net", "rt-multi-thread", "time", diff --git a/httpsig-wire-proto/Cargo.toml b/httpsig-wire-proto/Cargo.toml index 20c454b..508c0d5 100644 --- a/httpsig-wire-proto/Cargo.toml +++ b/httpsig-wire-proto/Cargo.toml @@ -13,16 +13,16 @@ edition.workspace = true publish.workspace = true [dependencies] -anyhow = "1.0.99" -thiserror = "2.0.16" +anyhow = "1.0.101" +thiserror = "2.0.18" rand = "0.9.2" hpke = "0.13.0" -bytes = "1.10.1" +bytes = "1.11.1" byteorder = "1.5.0" p256 = { version = "0.13.2" } elliptic-curve = { version = "0.13.8", features = ["ecdh"] } -ed25519-compact = { version = "2.1.1" } -digest = "0.10.7" +ed25519-compact = { version = "2.2.0" } +digest = "0.11.0" sha2 = "0.10.9" hkdf = "0.12.4" -httpsig = "0.0.19" +httpsig = "0.0.23" diff --git a/httpsig-wire-proto/src/dh.rs b/httpsig-wire-proto/src/dh.rs index 6ab3f0e..34828cc 100644 --- a/httpsig-wire-proto/src/dh.rs +++ b/httpsig-wire-proto/src/dh.rs @@ -7,10 +7,10 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::{Buf, BufMut, Bytes}; use elliptic_curve::ecdh; use hpke::{ + Serializable, generic_array::GenericArray, - kdf::{extract_and_expand, HkdfSha256, Kdf}, + kdf::{HkdfSha256, Kdf, extract_and_expand}, kem::{DhP256HkdfSha256, Kem, SharedSecret, X25519HkdfSha256}, - Serializable, }; use rand::{CryptoRng, RngCore}; diff --git a/httpsig-wire-proto/src/pk.rs b/httpsig-wire-proto/src/pk.rs index a0c4dd2..7b837a9 100644 --- a/httpsig-wire-proto/src/pk.rs +++ b/httpsig-wire-proto/src/pk.rs @@ -1,11 +1,11 @@ use crate::{ - common::{read_lengthed, to_u16, Deserialize, Serialize}, + common::{Deserialize, Serialize, read_lengthed, to_u16}, error::HttpSigError, }; use bytes::{Buf, BufMut, Bytes}; use hpke::{ - kem::{DhP256HkdfSha256, X25519HkdfSha256}, Kem, Serializable, + kem::{DhP256HkdfSha256, X25519HkdfSha256}, }; use httpsig::prelude::{AlgorithmName, PublicKey, SecretKey}; use rand::{CryptoRng, RngCore}; @@ -76,7 +76,7 @@ impl HttpSigPkKeyPair { /// export private key as `httpsig` crate's `SecretKey` pub fn try_export_sk(&self) -> Result { let alg_name = self.public_key.alg_name(); - let res = SecretKey::from_bytes(alg_name, self.private_key.to_vec().as_slice())?; + let res = SecretKey::from_bytes(&alg_name, self.private_key.to_vec().as_slice())?; Ok(res) } } @@ -92,7 +92,7 @@ impl HttpSigPkConfigContents { /// export public key as `httpsig` crate's `PublicKey` pub fn try_export(&self) -> Result { let alg_name = self.alg_name(); - let res = PublicKey::from_bytes(alg_name, self.public_key.to_vec().as_slice())?; + let res = PublicKey::from_bytes(&alg_name, self.public_key.to_vec().as_slice())?; Ok(res) } /// Get the algorithm name in `httpsig` crate diff --git a/modoh-bin/Cargo.toml b/modoh-bin/Cargo.toml index fdb9c03..99d0ee5 100644 --- a/modoh-bin/Cargo.toml +++ b/modoh-bin/Cargo.toml @@ -52,11 +52,11 @@ modoh-server-lib = { path = "../modoh-lib", default-features = false, features = "rustls", ] } -anyhow = "1.0.99" +anyhow = "1.0.101" mimalloc = { version = "*", default-features = false } -serde = { version = "1.0.219", default-features = false, features = ["derive"] } +serde = { version = "1.0.228", default-features = false, features = ["derive"] } derive_builder = "0.20.2" -tokio = { version = "1.47.1", default-features = false, features = [ +tokio = { version = "1.49.0", default-features = false, features = [ "net", "rt-multi-thread", "time", @@ -64,35 +64,35 @@ tokio = { version = "1.47.1", default-features = false, features = [ "macros", ] } async-trait = "0.1.89" -url = "2.5.7" +url = "2.5.8" # config -clap = { version = "4.5.47", features = ["std", "cargo", "wrap_help"] } -toml = { version = "0.9.5", default-features = false, features = [ +clap = { version = "4.5.58", features = ["std", "cargo", "wrap_help"] } +toml = { version = "1.0.1+spec-1.1.0", default-features = false, features = [ "parse", "serde", ] } -hot_reload = "0.2.0" +hot_reload = "0.3.5" # tracing and metrics -tracing = { version = "0.1.41" } -tracing-subscriber = { version = "0.3.20", features = ["fmt"] } -tracing-opentelemetry = { version = "0.31.0", optional = true } -opentelemetry = { version = "0.30.0", optional = true } -opentelemetry_sdk = { version = "0.30.0", features = [ +tracing = { version = "0.1.44" } +tracing-subscriber = { version = "0.3.22", features = ["fmt"] } +tracing-opentelemetry = { version = "0.32.1", optional = true } +opentelemetry = { version = "0.31.0", optional = true } +opentelemetry_sdk = { version = "0.31.0", features = [ "rt-tokio", "spec_unstable_metrics_views", ], optional = true } -opentelemetry-stdout = { version = "0.30.0", optional = true } -opentelemetry-otlp = { version = "0.30.0", optional = true, features = [ +opentelemetry-stdout = { version = "0.31.0", optional = true } +opentelemetry-otlp = { version = "0.31.0", optional = true, features = [ "grpc-tonic", ] } -opentelemetry-semantic-conventions = { version = "0.30.0", optional = true, features = [ +opentelemetry-semantic-conventions = { version = "0.31.0", optional = true, features = [ "semconv_experimental", ] } # add random otel service id whenever restarting -uuid = { version = "1.18.1", default-features = false, features = [ +uuid = { version = "1.21.0", default-features = false, features = [ "v4", "fast-rng", ], optional = true } diff --git a/modoh-lib/Cargo.toml b/modoh-lib/Cargo.toml index 6c28411..3aafeb0 100644 --- a/modoh-lib/Cargo.toml +++ b/modoh-lib/Cargo.toml @@ -32,25 +32,25 @@ futures = { version = "0.3.31", default-features = false, features = [ "std", "async-await", ] } -tokio = { version = "1.47.1", features = [ +tokio = { version = "1.49.0", features = [ "net", "rt-multi-thread", "time", "sync", "macros", ] } -anyhow = "1.0.99" -tracing = "0.1.41" -thiserror = "2.0.16" +anyhow = "1.0.101" +tracing = "0.1.44" +thiserror = "2.0.18" async-trait = "0.1.89" # http handling for both client and server -url = "2.5.7" +url = "2.5.8" ahash = "0.8.12" -hyper = { version = "1.7.0", default-features = false } -http = "1.3.1" +hyper = { version = "1.8.1", default-features = false } +http = "1.4.0" http-body-util = "0.1.3" -hyper-util = { version = "0.1.16", features = ["full"] } +hyper-util = { version = "0.1.20", features = ["full"] } # http handling for client hyper-tls = { version = "0.6.0", features = [ @@ -65,39 +65,39 @@ hyper-rustls = { version = "0.27.7", default-features = false, features = [ ], optional = true } # modoh target handling -odoh-rs = { git = "https://github.com/junkurihara/odoh-rs", rev = "789848b0e0fe77d409fb08fcbfffc32899a71697" } +odoh-rs = { git = "https://github.com/junkurihara/odoh-rs", rev = "f4d4c5d246d99fac4bd51559ee5826c9d491c114" } rand = "0.9.2" base64 = "0.22.1" byteorder = "1.5.0" # validation of id token -serde = { version = "1.0.219", default-features = false } +serde = { version = "1.0.228", default-features = false } auth-validator = { git = "https://github.com/junkurihara/rust-token-server", package = "rust-token-server-validator", branch = "develop", default-features = false, features = [ "blind-signatures", ] } -serde_json = { version = "1.0.143" } +serde_json = { version = "1.0.149" } # access control ipnet = { version = "2.11.0" } cedarwood = { version = "0.4.6" } -regex = { version = "1.11.2" } +regex = { version = "1.12.3" } # metrics -opentelemetry = { version = "0.30.0", optional = true } -opentelemetry_sdk = { version = "0.30.0", features = [ +opentelemetry = { version = "0.31.0", optional = true } +opentelemetry_sdk = { version = "0.31.0", features = [ "rt-tokio", ], optional = true } # tracing requests traveled among relays and targets # NOTE: DO NOT USE THIS IN PRODUCTION -tracing-opentelemetry = { version = "0.31.0", optional = true } +tracing-opentelemetry = { version = "0.32.1", optional = true } # httpsig httpsig-proto = { path = "../httpsig-wire-proto", default-features = false, package = "httpsig-proto" } httpsig-registry = { path = "../httpsig-registry", default-features = false, package = "httpsig-registry" } -indexmap = { version = "2.11.1" } -httpsig = { version = "0.0.19" } -httpsig-hyper = { version = "0.0.19" } +indexmap = { version = "2.13.0" } +httpsig = { version = "0.0.23" } +httpsig-hyper = { version = "0.0.23" } # logging crossbeam-channel = { version = "0.5.15", optional = true } diff --git a/modoh-lib/src/constants.rs b/modoh-lib/src/constants.rs index 929cbf7..1e6cfa6 100644 --- a/modoh-lib/src/constants.rs +++ b/modoh-lib/src/constants.rs @@ -18,8 +18,12 @@ pub const UPSTREAM: &str = "8.8.8.8:53"; pub const ERROR_TTL: u32 = 2; pub const MAX_TTL: u32 = 604800; pub const MIN_TTL: u32 = 10; -pub const STALE_IF_ERROR_SECS: u32 = 86400; -pub const STALE_WHILE_REVALIDATE_SECS: u32 = 60; +pub const STALE_IF_ERROR_SECS: u32 = 300; +pub const STALE_WHILE_REVALIDATE_SECS: u32 = 5; +/// ODoH Config cache is expired after at least 10 seconds +pub const MAX_ODOH_CONFIG_TTL_SECS: u64 = 10; +/// HTTPSig Config cache is expired after at least 10 seconds +pub const MAX_HTTPSIG_CONFIG_TTL_SECS: u64 = 10; /// Maximum ratio of TCP sessions to UDP sessions pub const TARGET_UDP_TCP_RATIO: usize = 8; diff --git a/modoh-lib/src/httpsig_handler/handler_main.rs b/modoh-lib/src/httpsig_handler/handler_main.rs index b1b4b58..3a75d44 100644 --- a/modoh-lib/src/httpsig_handler/handler_main.rs +++ b/modoh-lib/src/httpsig_handler/handler_main.rs @@ -45,7 +45,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { /// force to refetch notifier for the public keys pub(super) force_refetch_notify: Arc, diff --git a/modoh-lib/src/hyper_client.rs b/modoh-lib/src/hyper_client.rs index 6cf6989..9fdeb55 100644 --- a/modoh-lib/src/hyper_client.rs +++ b/modoh-lib/src/hyper_client.rs @@ -8,8 +8,8 @@ use crate::{ use http::{Request, Response}; use hyper::body::{Body, Incoming}; use hyper_util::client::legacy::{ - connect::{Connect, HttpConnector}, Client, + connect::{Connect, HttpConnector}, }; use tracing::instrument; @@ -20,7 +20,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { pub inner: Client, } @@ -30,7 +30,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { #[instrument(level = "debug", name = "http_request", skip_all)] /// wrapper request fn @@ -71,7 +71,7 @@ impl HttpClient where B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { /// Build inner client with http pub fn try_new(runtime_handle: tokio::runtime::Handle) -> Result { @@ -96,7 +96,7 @@ impl HttpClient, B> where B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { /// Build inner client with hyper-tls pub fn try_new(runtime_handle: tokio::runtime::Handle) -> Result { @@ -126,7 +126,7 @@ impl HttpClient, B> where B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { /// Build forwarder pub fn try_new(runtime_handle: tokio::runtime::Handle) -> Result { diff --git a/modoh-lib/src/relay/relay_handle_url.rs b/modoh-lib/src/relay/relay_handle_url.rs index 9a1a9a6..bf24cbc 100644 --- a/modoh-lib/src/relay/relay_handle_url.rs +++ b/modoh-lib/src/relay/relay_handle_url.rs @@ -16,11 +16,7 @@ fn is_looped(current_url: &Url) -> bool { let mut seen = vec![current_url.host_str().unwrap_or(HOSTNAME).to_ascii_lowercase()]; let hostnames = current_url.query_pairs().filter_map(|(k, v)| { // filter "targethost" or "relayhost" - if k.contains("host") { - Some(v) - } else { - None - } + if k.contains("host") { Some(v) } else { None } }); for h in hostnames { let hostname = h.to_ascii_lowercase(); @@ -37,7 +33,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { #[instrument(level = "debug", skip_all)] /// build next-hop url with loop detection and max subsequent nodes check @@ -118,7 +114,7 @@ where mod tests { use super::*; use crate::hyper_client::HttpClient; - use hyper::{body::Incoming, HeaderMap}; + use hyper::{HeaderMap, body::Incoming}; use std::sync::Arc; #[test] @@ -179,7 +175,10 @@ mod tests { let url = Url::parse("https://example1.com/proxy?targethost=example2.com&targetpath=/dns-query&relayhost[1]=example3.com&relaypath[1]=/proxy&relayhost[2]=example4.com&relaypath[2]=/proxy").unwrap(); let next_hop_url = inner.build_nexthop_url(&url).unwrap(); - assert_eq!(next_hop_url.as_str(), "https://example3.com/proxy?relayhost%5B1%5D=example4.com&relaypath%5B1%5D=%2Fproxy&targethost=example2.com&targetpath=%2Fdns-query"); + assert_eq!( + next_hop_url.as_str(), + "https://example3.com/proxy?relayhost%5B1%5D=example4.com&relaypath%5B1%5D=%2Fproxy&targethost=example2.com&targetpath=%2Fdns-query" + ); let url = Url::parse("https://example1.com/proxy?targethost=example2.com&targetpath=/dns-query&relayhost[1]=example3.com&relaypath[1]=/proxy&relayhost[2]=example4.com&relaypath[2]=/proxy&relayhost[3]=example5.com&relaypath[3]=/proxy").unwrap(); let next_hop_url = inner.build_nexthop_url(&url); diff --git a/modoh-lib/src/relay/relay_main.rs b/modoh-lib/src/relay/relay_main.rs index 3820833..54e33e8 100644 --- a/modoh-lib/src/relay/relay_main.rs +++ b/modoh-lib/src/relay/relay_main.rs @@ -5,14 +5,14 @@ use crate::{ httpsig_handler::HttpSigKeysHandler, hyper_body::{BoxBody, IncomingOr}, hyper_client::HttpClient, - message_util::{check_content_type, inspect_host, inspect_request_body, RequestType}, + message_util::{RequestType, check_content_type, inspect_host, inspect_request_body}, request_filter::RequestFilter, trace::*, }; use http::{ + Method, Request, Response, header::{self, HeaderMap, HeaderValue}, request::Parts, - Method, Request, Response, }; use hyper::body::{Body, Incoming}; use hyper_util::client::legacy::connect::Connect; @@ -26,7 +26,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, { /// hyper client pub(super) inner: Arc>, diff --git a/modoh-lib/src/router/router_main.rs b/modoh-lib/src/router/router_main.rs index 6c432d5..77b108f 100644 --- a/modoh-lib/src/router/router_main.rs +++ b/modoh-lib/src/router/router_main.rs @@ -13,10 +13,10 @@ use crate::{ validator::Validator, }; use hyper::{ + Request, body::Incoming, rt::{Read, Write}, service::service_fn, - Request, }; use hyper_util::{client::legacy::connect::Connect, rt::TokioIo, server::conn::auto::Builder as ConnectionBuilder}; use std::{net::SocketAddr, sync::Arc, time::Duration}; @@ -85,7 +85,7 @@ where let span_cx = span_cx.unwrap(); debug!(trace_id = span_cx.trace_id().to_string(), parent_span_id = span_cx.span_id().to_string(), "evil-trace enabled. received traceparent header."); let context = Context::new().with_remote_span_context(span_cx); - current_span.set_parent(context); + let _ = current_span.set_parent(context); current_span.context().span().span_context().trace_id(); debug!(trace_id = current_span.context().span().span_context().trace_id().to_string(), child_span_id = current_span.context().span().span_context().span_id().to_string(), "evil-trace enabled. get into child span"); } diff --git a/modoh-lib/src/target/target_main.rs b/modoh-lib/src/target/target_main.rs index 56b035c..ec8ccc8 100644 --- a/modoh-lib/src/target/target_main.rs +++ b/modoh-lib/src/target/target_main.rs @@ -1,18 +1,19 @@ use super::odoh::ODoHPublicKey; use crate::{ constants::{ - HTTPSIG_CONFIGS_PATH, ODOH_CONFIGS_PATH, ODOH_KEY_ROTATION_SECS, STALE_IF_ERROR_SECS, STALE_WHILE_REVALIDATE_SECS, + HTTPSIG_CONFIGS_PATH, MAX_HTTPSIG_CONFIG_TTL_SECS, MAX_ODOH_CONFIG_TTL_SECS, ODOH_CONFIGS_PATH, ODOH_KEY_ROTATION_SECS, + STALE_IF_ERROR_SECS, STALE_WHILE_REVALIDATE_SECS, }, count::RequestCount, error::*, globals::Globals, httpsig_handler::HttpSigKeyRotationState, - hyper_body::{full, BoxBody}, + hyper_body::{BoxBody, full}, message_util::inspect_host, trace::*, }; -use futures::{select, FutureExt}; -use http::{header, Method, Request, Response}; +use futures::{FutureExt, select}; +use http::{Method, Request, Response, header}; use hyper::body::Bytes; use std::{net::SocketAddr, sync::Arc, time::Duration}; use tokio::{ @@ -21,6 +22,23 @@ use tokio::{ }; use tracing::instrument; +#[instrument(level = "debug", skip_all)] +/// build http response from given packet +pub(super) fn build_http_response_no_store(packet: &[u8], content_type: &str, cors: bool) -> HttpResult> { + let packet_len = packet.len(); + let mut response_builder = Response::builder() + .header(header::CONTENT_LENGTH, packet_len) + .header(header::CONTENT_TYPE, content_type) + .header(header::CACHE_CONTROL, "no-store, no-cache, must-revalidate, max-age=0") + .header(header::PRAGMA, "no-cache") + .header(header::EXPIRES, "0"); + if cors { + response_builder = response_builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"); + } + let body = full(Bytes::copy_from_slice(packet)); + response_builder.body(body).map_err(|_| HttpError::InvalidODoHConfig) +} + #[instrument(level = "debug", skip_all)] /// build http response from given packet pub(super) fn build_http_response(packet: &[u8], ttl: u64, content_type: &str, cors: bool) -> HttpResult> { @@ -30,8 +48,7 @@ pub(super) fn build_http_response(packet: &[u8], ttl: u64, content_type: &str, c .header(header::CONTENT_TYPE, content_type) .header( header::CACHE_CONTROL, - format!("max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}") - .as_str(), + format!("max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}, must-revalidate").as_str(), ); if cors { response_builder = response_builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"); @@ -98,7 +115,12 @@ impl InnerTarget { let lock = self.odoh_configs.read().await; let configs = lock.as_config().to_owned(); drop(lock); - build_http_response(&configs, ODOH_KEY_ROTATION_SECS, "application/octet-stream", true) + build_http_response( + &configs, + ODOH_KEY_ROTATION_SECS.min(MAX_ODOH_CONFIG_TTL_SECS), + "application/octet-stream", + true, + ) } /// Serve httpsig config via GET method @@ -123,7 +145,12 @@ impl InnerTarget { let configs = lock.as_config().to_owned(); let rotation_period = httpsig_state.rotation_period.as_secs(); drop(lock); - build_http_response(&configs, rotation_period, "application/octet-stream", true) + build_http_response( + &configs, + rotation_period.min(MAX_HTTPSIG_CONFIG_TTL_SECS), + "application/octet-stream", + true, + ) } /// Start odoh config rotation service diff --git a/modoh-lib/src/target/target_serve_query.rs b/modoh-lib/src/target/target_serve_query.rs index 98f62dd..0c10fa8 100644 --- a/modoh-lib/src/target/target_serve_query.rs +++ b/modoh-lib/src/target/target_serve_query.rs @@ -1,4 +1,4 @@ -use super::{target_main::build_http_response, InnerTarget}; +use super::{InnerTarget, target_main::build_http_response}; use crate::{ constants::{ DNS_QUERY_PARAM, DOH_CONTENT_TYPE, MAX_DNS_QUESTION_LEN, MAX_DNS_RESPONSE_LEN, MIN_DNS_PACKET_LEN, ODOH_CONTENT_TYPE, @@ -7,10 +7,11 @@ use crate::{ dns, error::*, hyper_body::BoxBody, - message_util::{check_content_type, inspect_host, read_request_body, RequestType}, + message_util::{RequestType, check_content_type, inspect_host, read_request_body}, + target::target_main::build_http_response_no_store, trace::*, }; -use base64::{engine::general_purpose, Engine as _}; +use base64::{Engine as _, engine::general_purpose}; use byteorder::{BigEndian, ByteOrder}; use futures::TryFutureExt; use http::{Method, Request, Response}; @@ -21,7 +22,7 @@ use tokio::{ net::{TcpSocket, UdpSocket}, time::timeout, }; -use tracing::{instrument, Instrument as _}; +use tracing::{Instrument as _, instrument}; #[derive(Debug)] /// Dns response object @@ -96,7 +97,7 @@ impl InnerTarget { self.log_dns_message(_peer_addr, &res.packet, &req_headers); let encrypted_body = context.encrypt_response(res.packet)?; - let resp = build_http_response(&encrypted_body, 0u64, ODOH_CONTENT_TYPE, false)?; + let resp = build_http_response_no_store(&encrypted_body, ODOH_CONTENT_TYPE, false)?; Ok(resp) } } diff --git a/modoh-lib/src/validator/validator_jwks_service.rs b/modoh-lib/src/validator/validator_jwks_service.rs index a26decd..be7f93b 100644 --- a/modoh-lib/src/validator/validator_jwks_service.rs +++ b/modoh-lib/src/validator/validator_jwks_service.rs @@ -1,7 +1,7 @@ use super::validator_main::Validator; use crate::{constants::JWKS_REFETCH_DELAY_SEC, error::*, hyper_client::HttpClient, trace::*}; use auth_validator::JwksHttpClient; -use futures::{select, FutureExt}; +use futures::{FutureExt, select}; use hyper::body::Body; use hyper_util::client::legacy::connect::Connect; use std::{sync::Arc, time::Duration}; @@ -12,7 +12,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, HttpClient: JwksHttpClient, { /// Check token expiration every 60 secs, and refresh if the token is about to expire. diff --git a/modoh-lib/src/validator/validator_main.rs b/modoh-lib/src/validator/validator_main.rs index efa17ce..9e63b4e 100644 --- a/modoh-lib/src/validator/validator_main.rs +++ b/modoh-lib/src/validator/validator_main.rs @@ -6,8 +6,8 @@ use crate::{ hyper_client::HttpClient, }; use async_trait::async_trait; -use auth_validator::{reexports::Claims, JwksHttpClient, TokenValidator}; -use http::{header, HeaderValue, Method, Request}; +use auth_validator::{JwksHttpClient, TokenValidator, reexports::Claims}; +use http::{HeaderValue, Method, Request, header}; use http_body_util::{BodyExt, Empty}; use hyper::body::{Body, Buf, Bytes}; use hyper_util::client::legacy::connect::Connect; @@ -80,7 +80,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, HttpClient: JwksHttpClient, { pub(super) inner: TokenValidator>, @@ -91,7 +91,7 @@ where C: Send + Sync + Connect + Clone + 'static, B: Body + Send + Unpin + 'static, ::Data: Send, - ::Error: Into>, + ::Error: Into>, HttpClient: JwksHttpClient, { #[instrument(name = "validate_request", skip_all)]