Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add evil trace feature for development #7

Merged
merged 1 commit into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ ENV RUSTFLAGS "-C link-arg=-s"

RUN update-ca-certificates 2> /dev/null || true

# TODO: do not enable otel-evil-trace for production
RUN apt-get update && apt-get install -qy --no-install-recommends $BUILD_DEPS && \
curl -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain stable && \
export PATH="$HOME/.cargo/bin:$PATH" && \
echo "Building Mutualized Oblivious DNS relay and target from source" && \
cargo build --release --no-default-features --features=otel-full --package modoh-server && \
cargo build --release --no-default-features --features=otel-full,otel-evil-trace --package modoh-server && \
strip --strip-all /tmp/target/release/modoh-server

########################################
Expand Down
5 changes: 4 additions & 1 deletion modoh-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["otel-full"]
default = ["otel-full", "otel-evil-trace"]
otel-full = ["otel-trace", "otel-metrics", "otel-instance-id"]
otel-trace = [
"opentelemetry/trace",
Expand All @@ -56,6 +56,9 @@ otel-base = [
]
otel-instance-id = ["dep:uuid"]

# DO NOT USE THIS IN PRODUCTION
otel-evil-trace = ["modoh-server-lib/evil-trace"]

[dependencies]
modoh-server-lib = { path = "../modoh-lib", default-features = false, features = [
"native-tls",
Expand Down
5 changes: 5 additions & 0 deletions modoh-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ publish = false
[features]
default = ["native-tls"]
metrics = ["dep:opentelemetry_sdk", "dep:opentelemetry"]
evil-trace = ["dep:tracing-opentelemetry"]
native-tls = ["dep:hyper-tls"]
rustls = []

Expand Down Expand Up @@ -102,3 +103,7 @@ opentelemetry_sdk = { version = "0.21.1", features = [
"metrics",
"rt-tokio",
], optional = true }

# tracing requests traveled among relays and targets
# NOTE: DO NOT USE THIS IN PRODUCTION
tracing-opentelemetry = { version = "0.22.0", optional = true }
7 changes: 7 additions & 0 deletions modoh-lib/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,10 @@ pub const JWKS_REFETCH_DELAY_SEC: u64 = 300;
pub const JWKS_REFETCH_TIMEOUT_SEC: u64 = 3;
/// Expected maximum size of JWKS in bytes
pub const EXPECTED_MAX_JWKS_SIZE: u64 = 1024 * 64;

#[cfg(feature = "evil-trace")]
pub const EVIL_TRACE_HEADER_NAME: &str = "traceparent";
#[cfg(feature = "evil-trace")]
pub const EVIL_TRACE_FLAGS: &str = "01";
#[cfg(feature = "evil-trace")]
pub const EVIL_TRACE_VERSION: &str = "00";
92 changes: 92 additions & 0 deletions modoh-lib/src/evil_trace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use crate::constants::EVIL_TRACE_HEADER_NAME;
use anyhow::anyhow;
use http::Request;
use hyper::header::HeaderName;
use opentelemetry::trace::{SpanContext, SpanId, TraceFlags, TraceId};

#[allow(unused)]
/// Trace context
pub(crate) struct TraceCX {
pub(crate) version: String, // 00
pub(crate) trace_id: String,
pub(crate) span_id: String,
pub(crate) trace_flags: String,
}

impl TryFrom<String> for TraceCX {
type Error = anyhow::Error;

fn try_from(value: String) -> Result<Self, Self::Error> {
let mut iter = value.split('-');
let version = iter.next().ok_or_else(|| anyhow!("Invalid trace context"))?;
let trace_id = iter.next().ok_or_else(|| anyhow!("Invalid trace context"))?;
let span_id = iter.next().ok_or_else(|| anyhow!("Invalid trace context"))?;
let trace_flags = iter.next().ok_or_else(|| anyhow!("Invalid trace context"))?;
Ok(TraceCX {
version: version.to_string(),
trace_id: trace_id.to_string(),
span_id: span_id.to_string(),
trace_flags: trace_flags.to_string(),
})
}
}

impl From<TraceCX> for SpanContext {
fn from(val: TraceCX) -> Self {
let trace_flags = match val.trace_flags.as_str() {
"00" => TraceFlags::NOT_SAMPLED,
"01" => TraceFlags::SAMPLED,
_ => TraceFlags::default(),
};
SpanContext::new(
TraceId::from_hex(&val.trace_id).unwrap(),
SpanId::from_hex(&val.span_id).unwrap(),
trace_flags,
false,
opentelemetry::trace::TraceState::default(),
)
}
}

/// Get span context built from trace header from http request
/// Definition of http header for trace
/// https://uptrace.dev/opentelemetry/opentelemetry-traceparent.html
/// ```test:
/// # {version}-{trace_id}-{span_id}-{trace_flags}
/// traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01
/// ```
pub(crate) fn get_span_cx_from_request<B>(req: &Request<B>) -> Option<SpanContext> {
let header_name = HeaderName::from_static(EVIL_TRACE_HEADER_NAME);
let header_value = req.headers().get(&header_name)?;
let header_value = header_value.to_str().ok()?;
TraceCX::try_from(header_value.to_string()).ok().map(SpanContext::from)
}

#[cfg(test)]
mod tests {
use super::*;
use http::header::HeaderValue;
use opentelemetry::trace::TraceState;

#[test]
fn test_get_trace_cx_from_request() {
let req = Request::builder()
.header(
HeaderName::from_static(EVIL_TRACE_HEADER_NAME),
HeaderValue::from_static("00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01"),
)
.body(http_body_util::Empty::<hyper::body::Bytes>::new())
.unwrap();
let span_context = get_span_cx_from_request(&req).unwrap();
assert_eq!(
span_context,
SpanContext::new(
TraceId::from_hex("80e1afed08e019fc1110464cfa66635c").unwrap(),
SpanId::from_hex("7a085853722dc6d2").unwrap(),
TraceFlags::SAMPLED,
false,
TraceState::default(),
)
);
}
}
24 changes: 24 additions & 0 deletions modoh-lib/src/hyper_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ where
&self,
req: Request<B>,
) -> std::result::Result<Response<Incoming>, hyper_util::client::legacy::Error> {
#[cfg(feature = "evil-trace")]
{
use crate::constants::{EVIL_TRACE_FLAGS, EVIL_TRACE_HEADER_NAME, EVIL_TRACE_VERSION};
use opentelemetry::trace::TraceContextExt;
use tracing_opentelemetry::OpenTelemetrySpanExt;
let current_span_context = tracing::Span::current().context().span().span_context().clone();
let header_value = format!(
"{}-{}-{}-{}",
EVIL_TRACE_VERSION,
current_span_context.trace_id(),
current_span_context.span_id(),
EVIL_TRACE_FLAGS
);
let mut req = req;
let headers = req.headers_mut();
headers.insert(
http::HeaderName::from_static(EVIL_TRACE_HEADER_NAME),
http::HeaderValue::from_str(&header_value).unwrap_or(http::HeaderValue::from_static("")),
);
println!("evil-trace enabled. header: {:?}", req.headers());
self.inner.request(req).await
}

#[cfg(not(feature = "evil-trace"))]
self.inner.request(req).await
}
}
Expand Down
3 changes: 3 additions & 0 deletions modoh-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ mod validator;
#[cfg(feature = "metrics")]
mod metrics;

#[cfg(feature = "evil-trace")]
mod evil_trace;

use crate::{count::RequestCount, error::*, globals::Globals, router::Router, trace::*};
use hyper_client::HttpClient;
use hyper_executor::LocalExecutor;
Expand Down
19 changes: 18 additions & 1 deletion modoh-lib/src/router/router_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use hyper::{
use hyper_util::{client::legacy::connect::Connect, rt::TokioIo, server::conn::auto::Builder as ConnectionBuilder};
use std::{net::SocketAddr, sync::Arc, time::Duration};
use tokio::time::timeout;
use tracing::Instrument as _;

#[derive(Clone)]
/// (M)ODoH Router main object
Expand Down Expand Up @@ -61,7 +62,23 @@ where
timeout_sec + Duration::from_secs(1),
server_clone.serve_connection(
stream,
service_fn(move |req: Request<Incoming>| serve_request_with_validation(req, peer_addr, self_clone.clone())),
service_fn(move |req: Request<Incoming>| {
let current_span = tracing::info_span!("router_serve", method = ?req.method(), uri = ?req.uri(), peer_addr = ?peer_addr, xff = ?req.headers().get("x-forwarded-for"), forwarded = ?req.headers().get("forwarded"));

#[cfg(feature = "evil-trace")]
{
use opentelemetry::trace::TraceContextExt;
use tracing_opentelemetry::OpenTelemetrySpanExt;
let span_cx = crate::evil_trace::get_span_cx_from_request(&req);
if span_cx.is_some() {
debug!("evil-trace enabled. parente span context: {:?}", span_cx);
let span_cx = span_cx.unwrap();
let context = current_span.context().with_remote_span_context(span_cx);
current_span.set_parent(context);
}
}
serve_request_with_validation(req, peer_addr, self_clone.clone()).instrument(current_span)
}),
),
)
.await
Expand Down
2 changes: 0 additions & 2 deletions modoh-lib/src/router/router_serve_req.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ use crate::{
use hyper::{body::Incoming, header, Request, StatusCode};
use hyper_util::client::legacy::connect::Connect;
use std::net::SocketAddr;
use tracing::instrument;

#[instrument(name = "router_serve", skip_all, fields(method = ?req.method(), uri = ?req.uri(), peer_addr = ?peer_addr, xff = ?req.headers().get("x-forwarded-for"), forwarded = ?req.headers().get("forwarded")))]
/// Service wrapper with validation
pub async fn serve_request_with_validation<C>(
req: Request<Incoming>,
Expand Down