Add destination domain ACL rules
This commit is contained in:
+69
-5
@@ -28,7 +28,7 @@ use tracing_opentelemetry::OpenTelemetrySpanExt;
|
||||
|
||||
use crate::{
|
||||
BoxError,
|
||||
acl::{Acl, Decision},
|
||||
acl::{Acl, Decision, DestinationHost},
|
||||
config::Config,
|
||||
metrics::Metrics,
|
||||
telemetry::{HeaderExtractor, HeaderInjector, remove_hop_by_hop_headers},
|
||||
@@ -177,7 +177,16 @@ fn authorize_request(
|
||||
peer: IpAddr,
|
||||
state: &ProxyState,
|
||||
) -> Result<Option<ProxyResponse>, ProxyError> {
|
||||
let outcome = state.acl.authorize(peer, request.headers());
|
||||
let destination = request.uri().authority().and_then(|authority| {
|
||||
if authority.as_str().contains('@') {
|
||||
None
|
||||
} else {
|
||||
DestinationHost::parse(authority.host())
|
||||
}
|
||||
});
|
||||
let outcome = state
|
||||
.acl
|
||||
.authorize(peer, request.headers(), destination.as_ref());
|
||||
|
||||
state
|
||||
.metrics
|
||||
@@ -671,7 +680,10 @@ mod tests {
|
||||
let (mut sender, connection_task) = connect_client(proxy_addr).await;
|
||||
let request = Request::builder()
|
||||
.method(Method::GET)
|
||||
.uri(format!("http://{upstream_addr}/hello?q=1"))
|
||||
.uri(format!(
|
||||
"http://localhost:{}/hello?q=1",
|
||||
upstream_addr.port()
|
||||
))
|
||||
.header(HOST, "wrong.invalid")
|
||||
.header(PROXY_AUTHORIZATION, basic_authorization())
|
||||
.body(Full::new(Bytes::new()))
|
||||
@@ -685,7 +697,7 @@ mod tests {
|
||||
upstream_task.abort();
|
||||
let observed = observed.lock().unwrap();
|
||||
assert_eq!(observed.target, "/hello?q=1");
|
||||
assert_eq!(observed.host, upstream_addr.to_string());
|
||||
assert_eq!(observed.host, format!("localhost:{}", upstream_addr.port()));
|
||||
assert!(!observed.forwarded_proxy_authorization);
|
||||
drop(observed);
|
||||
|
||||
@@ -710,7 +722,7 @@ mod tests {
|
||||
let (mut sender, connection_task) = connect_client(proxy_addr).await;
|
||||
let request = Request::builder()
|
||||
.method(Method::CONNECT)
|
||||
.uri(echo_addr.to_string())
|
||||
.uri(format!("localhost:{}", echo_addr.port()))
|
||||
.body(Full::new(Bytes::new()))
|
||||
.unwrap();
|
||||
|
||||
@@ -759,6 +771,56 @@ mod tests {
|
||||
connection_task.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn denies_blacklisted_http_domain_before_upstream_connection() {
|
||||
let acl = Acl::from_config(AclConfig {
|
||||
default_action: Action::Allow,
|
||||
users: BTreeMap::new(),
|
||||
rules: vec![RuleConfig {
|
||||
name: "blocked-domain".to_owned(),
|
||||
action: Action::Deny,
|
||||
users: vec![],
|
||||
networks: vec![],
|
||||
domains: vec!["blocked.example".to_owned()],
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
let (proxy_addr, proxy_task) = spawn_proxy_with_acl(acl).await;
|
||||
let (mut sender, connection_task) = connect_client(proxy_addr).await;
|
||||
let request = Request::builder()
|
||||
.method(Method::GET)
|
||||
.uri("http://blocked.example/")
|
||||
.body(Full::new(Bytes::new()))
|
||||
.unwrap();
|
||||
|
||||
let response = sender.send_request(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||
response.into_body().collect().await.unwrap();
|
||||
|
||||
proxy_task.abort();
|
||||
connection_task.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn domain_whitelist_rejects_ip_literal_connect() {
|
||||
let echo_listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let echo_addr = echo_listener.local_addr().unwrap();
|
||||
let (proxy_addr, proxy_task) = spawn_proxy_with_acl(localhost_acl()).await;
|
||||
let (mut sender, connection_task) = connect_client(proxy_addr).await;
|
||||
let request = Request::builder()
|
||||
.method(Method::CONNECT)
|
||||
.uri(echo_addr.to_string())
|
||||
.body(Full::new(Bytes::new()))
|
||||
.unwrap();
|
||||
|
||||
let response = sender.send_request(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
||||
response.into_body().collect().await.unwrap();
|
||||
|
||||
proxy_task.abort();
|
||||
connection_task.abort();
|
||||
}
|
||||
|
||||
fn basic_authorization() -> String {
|
||||
format!(
|
||||
"Basic {}",
|
||||
@@ -781,6 +843,7 @@ mod tests {
|
||||
action: Action::Allow,
|
||||
users: vec!["alice".to_owned()],
|
||||
networks: vec![],
|
||||
domains: vec!["localhost".to_owned()],
|
||||
}],
|
||||
})
|
||||
.unwrap()
|
||||
@@ -795,6 +858,7 @@ mod tests {
|
||||
action: Action::Allow,
|
||||
users: vec![],
|
||||
networks: vec!["127.0.0.1/32".to_owned(), "::1/128".to_owned()],
|
||||
domains: vec!["localhost".to_owned()],
|
||||
}],
|
||||
})
|
||||
.unwrap()
|
||||
|
||||
Reference in New Issue
Block a user