-
Notifications
You must be signed in to change notification settings - Fork 83
chore(proxy): print request safety in proxy #1715
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
base: main
Are you sure you want to change the base?
Changes from all commits
44887f7
21bee66
861bce1
a71be63
1ee3d71
16a938f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,7 +50,7 @@ use lazy_static::lazy_static; | |
| use rcgen::Certificate; | ||
| use rustls::{RootCertStore, ServerConfig}; | ||
| use rustls_pki_types::CertificateDer; | ||
| use std::collections::HashMap; | ||
| use std::collections::{HashMap, HashSet}; | ||
| use std::net::SocketAddr; | ||
| use std::sync::Arc; | ||
| use tokio::io::{AsyncWriteExt, BufReader, BufWriter}; | ||
|
|
@@ -420,17 +420,11 @@ pub async fn http_handler( | |
| } | ||
|
|
||
| if request.uri().scheme().cloned() == Some(http::uri::Scheme::HTTPS) { | ||
| info!( | ||
| "proxy HTTPS request directly to remote server: {:?}", | ||
| request | ||
| ); | ||
| log_request(&request, "proxy HTTPS request directly to remote server:"); | ||
| return proxy_via_https(request, registry_cert).await; | ||
| } | ||
|
|
||
| info!( | ||
| "proxy HTTP request directly to remote server: {:?}", | ||
| request | ||
| ); | ||
| log_request(&request, "proxy HTTP request directly to remote server:"); | ||
| return proxy_via_http(request).await; | ||
| } | ||
|
|
||
|
|
@@ -445,7 +439,7 @@ pub async fn https_handler( | |
| registry_cert: Arc<Option<Vec<CertificateDer<'static>>>>, | ||
| server_ca_cert: Arc<Option<Certificate>>, | ||
| ) -> ClientResult<Response> { | ||
| info!("handle HTTPS request: {:?}", request); | ||
| log_request(&request, "handle HTTPS request:"); | ||
|
|
||
| // Proxy the request directly to the remote server. | ||
| if let Some(host) = request.uri().host() { | ||
|
|
@@ -622,10 +616,7 @@ pub async fn upgraded_handler( | |
| config.proxy.rules.as_deref(), | ||
| url::Url::parse(&request_uri.to_string()).or_err(ErrorType::ParseError)?, | ||
| ) { | ||
| info!( | ||
| "proxy HTTPS request via dfdaemon by rule config: {:?}", | ||
| request, | ||
| ); | ||
| log_request(&request, "proxy HTTPS request via dfdaemon by rule config:"); | ||
| return proxy_via_dfdaemon( | ||
| config, | ||
| task, | ||
|
|
@@ -640,9 +631,9 @@ pub async fn upgraded_handler( | |
| // If the request header contains the X-Dragonfly-Use-P2P header, proxy the request via the | ||
| // dfdaemon. | ||
| if header::get_use_p2p(request.headers()) { | ||
| info!( | ||
| "proxy HTTP request via dfdaemon by X-Dragonfly-Use-P2P header: {:?}", | ||
| request, | ||
| log_request( | ||
| &request, | ||
| "proxy HTTP request via dfdaemon by X-Dragonfly-Use-P2P header:", | ||
| ); | ||
| return proxy_via_dfdaemon( | ||
| config, | ||
|
|
@@ -656,17 +647,11 @@ pub async fn upgraded_handler( | |
| } | ||
|
|
||
| if request.uri().scheme().cloned() == Some(http::uri::Scheme::HTTPS) { | ||
| info!( | ||
| "proxy HTTPS request directly to remote server: {:?}", | ||
| request, | ||
| ); | ||
| log_request(&request, "proxy HTTPS request directly to remote server:"); | ||
| return proxy_via_https(request, registry_cert).await; | ||
| } | ||
|
|
||
| info!( | ||
| "proxy HTTP request directly to remote server: {:?}", | ||
| request, | ||
| ); | ||
| log_request(&request, "proxy HTTP request directly to remote server:"); | ||
| return proxy_via_http(request).await; | ||
| } | ||
|
|
||
|
|
@@ -1327,3 +1312,42 @@ fn empty() -> BoxBody<Bytes, ClientError> { | |
| .map_err(|never| match never {}) | ||
| .boxed() | ||
| } | ||
|
|
||
| /// log_request safely output information from the request through logs | ||
| fn log_request(request: &Request<hyper::body::Incoming>, log_info: &str) { | ||
| const HEADER_BLACKLIST: &[&str] = &[ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing commonly sensitive headers:
Consider whether an allowlist approach (log only known-safe headers) would be more secure than a blocklist, which requires ongoing maintenance. |
||
| "authorization", | ||
| "cookie", | ||
| "set-cookie", | ||
| "proxy-authorization", | ||
| "x-access-token", | ||
| "x-auth-token", | ||
| ]; | ||
|
|
||
| let blacklist_set: HashSet<&str> = HEADER_BLACKLIST.iter().cloned().collect(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allocates a new With only 6 entries, a linear scan over the |
||
|
|
||
| let (mut safe_headers, mut sensitive_headers) = (Vec::new(), Vec::new()); | ||
| for (name, value) in request.headers().iter() { | ||
| if blacklist_set.contains(&name.as_str()) { | ||
| sensitive_headers.push((name, value)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to allocate a let sensitive_names: Vec<&str> = request.headers().keys()
.filter(|name| SENSITIVE_HEADERS.contains(&name.as_str()))
.map(|name| name.as_str())
.collect();
if !sensitive_names.is_empty() {
debug!("{} | redacted sensitive headers: {:?}", log_info, sensitive_names);
} |
||
| } else { | ||
| safe_headers.push((name, value)); | ||
| } | ||
| } | ||
|
|
||
| info!( | ||
| "{} | method={}, uri={}, version={:?}, safe_headers={:?}", | ||
| log_info, | ||
| request.method(), | ||
| request.uri(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Query strings often carry tokens ( |
||
| request.version(), | ||
| safe_headers, | ||
| ); | ||
|
|
||
| if !sensitive_headers.is_empty() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logs the actual values of Recommendation: Log only the header names, not their values. Or redact values to something like |
||
| debug!( | ||
| "{} | BLACKLISTED_HEADERS (SENSITIVE): {:?}", | ||
| log_info, sensitive_headers, | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
capitalize first word