Compare commits

...
Author SHA1 Message Date
Ian 13b0995b05 Forward plain-HTTP upstreams as asked
The wrapper's forward proxy upgraded every request to HTTPS and rewrote
links in index responses. Only an HTTPS registry is downgraded to reach
us, so only that should be upgraded back and rewritten; anything else
now goes out exactly as the client sent it, port included.
2026-10-09 16:53:43 -04:00
3 changed files with 32 additions and 2 deletions

No files matched your search

+5 -1
View File
@@ -66,12 +66,16 @@ async fn handle(req: Request, firewall: &Firewall) -> Result<Response, Infallibl
} }
info!(method = %req.method(), %url, "forwarding"); info!(method = %req.method(), %url, "forwarding");
let index = firewall.is_index_url(&url); // Only an HTTPS registry was downgraded to reach us, so only it is upgraded back and rewritten.
let tls = firewall.is_https_url(&url);
let index = tls && firewall.is_index_url(&url);
let (mut parts, body) = req.into_parts(); let (mut parts, body) = req.into_parts();
if tls {
let Ok(uri) = url.parse() else { let Ok(uri) = url.parse() else {
return Ok(text(400, "firewall: expected an absolute URL\n")); return Ok(text(400, "firewall: expected an absolute URL\n"));
}; };
parts.uri = uri; parts.uri = uri;
}
let client = DecompressionLayer::new().into_layer(EasyHttpWebClient::default()); let client = DecompressionLayer::new().into_layer(EasyHttpWebClient::default());
let response = match client.serve(Request::from_parts(parts, body)).await { let response = match client.serve(Request::from_parts(parts, body)).await {
+5
View File
@@ -50,6 +50,11 @@ impl Firewall {
hosts hosts
} }
pub fn is_https_url(&self, url: &str) -> bool {
let host = host_of(url);
self.config.registries.iter().any(|r| r.upstream.starts_with("https://") && r.host() == host)
}
pub fn is_index_url(&self, url: &str) -> bool { pub fn is_index_url(&self, url: &str) -> bool {
self.config.registries.iter().any(|r| r.index && r.host() == host_of(url)) self.config.registries.iter().any(|r| r.index && r.host() == host_of(url))
} }
+22 -1
View File
@@ -1,7 +1,9 @@
use firewall::aliasing::forward::downgrade; use firewall::aliasing::forward::downgrade;
use firewall::http::https_url; use firewall::http::https_url;
use firewall::Firewall; use firewall::{aliasing, Firewall, FirewallConfig};
use rama::http::{Body, Request}; use rama::http::{Body, Request};
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
fn request(target: &str, host: Option<&str>) -> Request { fn request(target: &str, host: Option<&str>) -> Request {
let mut builder = Request::builder().method("GET").uri(target); let mut builder = Request::builder().method("GET").uri(target);
@@ -35,6 +37,25 @@ fn requests_always_go_out_over_tls() {
assert_eq!(https_url(&request("/six", None)), None); assert_eq!(https_url(&request("/six", None)), None);
} }
#[test]
fn a_plain_http_upstream_is_not_upgraded() {
let upstream = TcpListener::bind("127.0.0.1:0").expect("bind");
let addr = upstream.local_addr().expect("address");
std::thread::spawn(move || {
let (mut stream, _) = upstream.accept().expect("accept");
let _ = stream.read(&mut [0; 4096]);
let _ = stream.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok");
});
let port = aliasing::spawn(0, FirewallConfig::default()).expect("spawn");
let mut proxy = TcpStream::connect(("127.0.0.1", port)).expect("connect");
let request = format!("GET http://{addr}/x HTTP/1.1\r\nHost: {addr}\r\nConnection: close\r\n\r\n");
proxy.write_all(request.as_bytes()).expect("write");
let mut response = String::new();
proxy.read_to_string(&mut response).expect("read");
assert!(response.starts_with("HTTP/1.1 200") && response.ends_with("ok"), "{response}");
}
#[tokio::test] #[tokio::test]
async fn downgrade_touches_known_hosts_in_text_only() { async fn downgrade_touches_known_hosts_in_text_only() {
let text = b"https://pypi.org/x https://example.com/y".to_vec(); let text = b"https://pypi.org/x https://example.com/y".to_vec();