Skip to content

Commit d9cffe4

Browse files
committed
Fixed port handling for certificate verification in the backend and proxy components. Updated related settings and documentation.
1 parent 4631ffe commit d9cffe4

7 files changed

Lines changed: 89 additions & 41 deletions

File tree

.env.dev

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ TRUSTED_SERVER__PUBLISHER__ORIGIN_URL=http://localhost:9090
44
# [synthetic]
55
TRUSTED_SERVER__SYNTHETIC__COUNTER_STORE=counter_store
66
TRUSTED_SERVER__SYNTHETIC__OPID_STORE=opid_store
7+
8+
# [proxy]
9+
# Disable TLS certificate verification for local dev with self-signed certs
10+
# TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK=false

crates/common/src/backend.rs

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ use url::Url;
66

77
use crate::error::TrustedServerError;
88

9+
/// Returns the default port for the given scheme (443 for HTTPS, 80 for HTTP).
10+
#[inline]
11+
fn default_port_for_scheme(scheme: &str) -> u16 {
12+
if scheme.eq_ignore_ascii_case("https") {
13+
443
14+
} else {
15+
80
16+
}
17+
}
18+
919
/// Compute the Host header value for a backend request.
1020
///
1121
/// For standard ports (443 for HTTPS, 80 for HTTP), returns just the hostname.
@@ -16,9 +26,7 @@ use crate::error::TrustedServerError;
1626
/// would generate URLs without the port when the Host header didn't include it.
1727
#[inline]
1828
fn compute_host_header(scheme: &str, host: &str, port: u16) -> String {
19-
let is_https = scheme.eq_ignore_ascii_case("https");
20-
let default_port = if is_https { 443 } else { 80 };
21-
if port != default_port {
29+
if port != default_port_for_scheme(scheme) {
2230
format!("{}:{}", host, port)
2331
} else {
2432
host.to_string()
@@ -37,6 +45,11 @@ fn compute_host_header(scheme: &str, host: &str, port: u16) -> String {
3745
/// * `host` - The hostname
3846
/// * `port` - Optional port number
3947
/// * `certificate_check` - If true, enables TLS certificate verification (default for production)
48+
///
49+
/// # Errors
50+
///
51+
/// Returns an error if the host is empty or if backend creation fails
52+
/// (except for `NameInUse` which reuses the existing backend).
4053
pub fn ensure_origin_backend(
4154
scheme: &str,
4255
host: &str,
@@ -49,12 +62,7 @@ pub fn ensure_origin_backend(
4962
}));
5063
}
5164

52-
let is_https = scheme.eq_ignore_ascii_case("https");
53-
let target_port = match (port, is_https) {
54-
(Some(p), _) => p,
55-
(None, true) => 443,
56-
(None, false) => 80,
57-
};
65+
let target_port = port.unwrap_or_else(|| default_port_for_scheme(scheme));
5866

5967
let host_with_port = format!("{}:{}", host, target_port);
6068

@@ -115,6 +123,13 @@ pub fn ensure_origin_backend(
115123
}
116124
}
117125

126+
/// Ensures a dynamic backend exists for the given origin URL.
127+
///
128+
/// Parses the URL and delegates to [`ensure_origin_backend`] to create or reuse a backend.
129+
///
130+
/// # Errors
131+
///
132+
/// Returns an error if the URL cannot be parsed or lacks a host, or if backend creation fails.
118133
pub fn ensure_backend_from_url(
119134
origin_url: &str,
120135
certificate_check: bool,
@@ -141,83 +156,97 @@ mod tests {
141156
// Tests for compute_host_header - the fix for port preservation in Host header
142157
#[test]
143158
fn host_header_includes_port_for_non_standard_https() {
144-
// Non-standard port 9443 should be included in Host header
145159
assert_eq!(
146160
compute_host_header("https", "cdn.example.com", 9443),
147-
"cdn.example.com:9443"
161+
"cdn.example.com:9443",
162+
"should include non-standard HTTPS port 9443 in Host header"
148163
);
149164
assert_eq!(
150165
compute_host_header("https", "cdn.example.com", 8443),
151-
"cdn.example.com:8443"
166+
"cdn.example.com:8443",
167+
"should include non-standard HTTPS port 8443 in Host header"
152168
);
153169
}
154170

155171
#[test]
156172
fn host_header_excludes_port_for_standard_https() {
157-
// Standard port 443 should NOT be included
158173
assert_eq!(
159174
compute_host_header("https", "cdn.example.com", 443),
160-
"cdn.example.com"
175+
"cdn.example.com",
176+
"should omit standard HTTPS port 443 from Host header"
161177
);
162178
}
163179

164180
#[test]
165181
fn host_header_includes_port_for_non_standard_http() {
166-
// Non-standard port 8080 should be included
167182
assert_eq!(
168183
compute_host_header("http", "cdn.example.com", 8080),
169-
"cdn.example.com:8080"
184+
"cdn.example.com:8080",
185+
"should include non-standard HTTP port 8080 in Host header"
170186
);
171187
}
172188

173189
#[test]
174190
fn host_header_excludes_port_for_standard_http() {
175-
// Standard port 80 should NOT be included
176191
assert_eq!(
177192
compute_host_header("http", "cdn.example.com", 80),
178-
"cdn.example.com"
193+
"cdn.example.com",
194+
"should omit standard HTTP port 80 from Host header"
179195
);
180196
}
181197

182198
#[test]
183199
fn returns_name_for_https_with_cert_check() {
184-
let name = ensure_origin_backend("https", "origin.example.com", None, true).unwrap();
200+
let name = ensure_origin_backend("https", "origin.example.com", None, true)
201+
.expect("should create backend for valid HTTPS origin");
185202
assert_eq!(name, "backend_https_origin_example_com_443");
186203
}
187204

188205
#[test]
189206
fn returns_name_for_https_without_cert_check() {
190-
let name = ensure_origin_backend("https", "origin.example.com", None, false).unwrap();
207+
let name = ensure_origin_backend("https", "origin.example.com", None, false)
208+
.expect("should create backend with cert check disabled");
191209
assert_eq!(name, "backend_https_origin_example_com_443_nocert");
192210
}
193211

194212
#[test]
195213
fn returns_name_for_http_with_port_and_sanitizes() {
196-
let name = ensure_origin_backend("http", "api.test-site.org", Some(8080), true).unwrap();
214+
let name = ensure_origin_backend("http", "api.test-site.org", Some(8080), true)
215+
.expect("should create backend for HTTP origin with explicit port");
197216
assert_eq!(name, "backend_http_api_test-site_org_8080");
198-
// Explicitly check that ':' was replaced with '_'
199-
assert!(name.ends_with("_8080"));
217+
assert!(
218+
name.ends_with("_8080"),
219+
"should sanitize ':' to '_' in backend name"
220+
);
200221
}
201222

202223
#[test]
203224
fn returns_name_for_http_without_port_defaults_to_80() {
204-
let name = ensure_origin_backend("http", "example.org", None, true).unwrap();
225+
let name = ensure_origin_backend("http", "example.org", None, true)
226+
.expect("should create backend defaulting to port 80 for HTTP");
205227
assert_eq!(name, "backend_http_example_org_80");
206228
}
207229

208230
#[test]
209231
fn error_on_missing_host() {
210-
let err = ensure_origin_backend("https", "", None, true)
211-
.err()
212-
.unwrap();
232+
let err =
233+
ensure_origin_backend("https", "", None, true).expect_err("should reject empty host");
213234
let msg = err.to_string();
214-
assert!(msg.contains("missing host"));
235+
assert!(
236+
msg.contains("missing host"),
237+
"should report missing host in error message"
238+
);
215239
}
216240

217241
#[test]
218242
fn second_call_reuses_existing_backend() {
219-
let first = ensure_origin_backend("https", "reuse.example.com", None, true).unwrap();
220-
let second = ensure_origin_backend("https", "reuse.example.com", None, true).unwrap();
221-
assert_eq!(first, second);
243+
let first = ensure_origin_backend("https", "reuse.example.com", None, true)
244+
.expect("should create backend on first call");
245+
let second = ensure_origin_backend("https", "reuse.example.com", None, true)
246+
.expect("should reuse backend on second call");
247+
assert_eq!(
248+
first, second,
249+
"should return same backend name on repeat call"
250+
);
222251
}
223252
}

crates/common/src/creative.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,7 @@ pub fn rewrite_creative_html(settings: &Settings, markup: &str) -> String {
332332
// Image src + data-src
333333
element!("img", |el| {
334334
if let Some(src) = el.get_attribute("src") {
335-
log::debug!("creative rewrite: img src input = {}", src);
336335
if let Some(p) = proxy_if_abs(settings, &src) {
337-
log::debug!("creative rewrite: img src output = {}", p);
338336
let _ = el.set_attribute("src", &p);
339337
}
340338
}
@@ -645,14 +643,15 @@ mod tests {
645643
#[test]
646644
fn to_abs_preserves_port_in_protocol_relative() {
647645
let settings = crate::test_support::tests::create_test_settings();
648-
// Protocol-relative URL with explicit port should preserve the port
649646
assert_eq!(
650647
to_abs(&settings, "//cdn.example.com:8080/asset.js"),
651-
Some("https://cdn.example.com:8080/asset.js".to_string())
648+
Some("https://cdn.example.com:8080/asset.js".to_string()),
649+
"should preserve port 8080 in protocol-relative URL"
652650
);
653651
assert_eq!(
654652
to_abs(&settings, "//cdn.example.com:9443/img.png"),
655-
Some("https://cdn.example.com:9443/img.png".to_string())
653+
Some("https://cdn.example.com:9443/img.png".to_string()),
654+
"should preserve port 9443 in protocol-relative URL"
656655
);
657656
}
658657

crates/common/src/proxy.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,16 +1183,19 @@ mod tests {
11831183
#[tokio::test]
11841184
async fn proxy_sign_preserves_non_standard_port() {
11851185
let settings = create_test_settings();
1186-
// Test with non-standard port (e.g., 9443)
11871186
let body = serde_json::json!({
11881187
"url": "https://cdn.example.com:9443/img/300x250.svg",
11891188
});
11901189
let mut req = Request::new(Method::POST, "https://edge.example/first-party/sign");
11911190
req.set_body(body.to_string());
11921191
let mut resp = handle_first_party_proxy_sign(&settings, req)
11931192
.await
1194-
.expect("sign ok");
1195-
assert_eq!(resp.get_status(), StatusCode::OK);
1193+
.expect("should sign URL with non-standard port");
1194+
assert_eq!(
1195+
resp.get_status(),
1196+
StatusCode::OK,
1197+
"should return 200 for valid sign request"
1198+
);
11961199
let json = resp.take_body_str();
11971200
// Port 9443 should be preserved (URL-encoded as %3A9443)
11981201
assert!(
@@ -1607,7 +1610,7 @@ mod tests {
16071610
"https://cdn.example.com:9443/creatives/300x250.html",
16081611
beresp,
16091612
)
1610-
.expect("finalize should succeed");
1613+
.expect("should finalize HTML response with non-standard port URL");
16111614

16121615
let body = out.take_body_str();
16131616

crates/common/src/publisher.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ pub fn handle_publisher_request(
216216
has_synthetic_cookie
217217
);
218218

219-
let backend_name = ensure_backend_from_url(&settings.publisher.origin_url, true)?;
219+
let backend_name = ensure_backend_from_url(
220+
&settings.publisher.origin_url,
221+
settings.proxy.certificate_check,
222+
)?;
220223
let origin_host = settings.publisher.origin_host();
221224

222225
log::debug!(

crates/common/src/settings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ impl Settings {
348348
return Err(Report::new(TrustedServerError::InsecureSecretKey));
349349
}
350350

351+
if !settings.proxy.certificate_check {
352+
log::warn!("INSECURE: proxy.certificate_check is disabled — TLS certificates will NOT be verified");
353+
}
354+
351355
Ok(settings)
352356
}
353357

trusted-server.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ rewrite_sdk = true
8484
# ]
8585

8686

87+
# Proxy configuration
88+
# [proxy]
89+
# Enable TLS certificate verification when proxying to HTTPS origins.
90+
# Defaults to true. Set to false only for local development with self-signed certificates.
91+
# certificate_check = true
92+
8793
[auction]
8894
enabled = true
8995
providers = ["prebid"]

0 commit comments

Comments
 (0)