[
https://issues.apache.org/jira/browse/CAMEL-23070?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen updated CAMEL-23070:
--------------------------------
Priority: Minor (was: Major)
> AS2AsyncMDNServerConnection uses strict RequestValidateHost — async MDNs
> rejected with HTTP 421
> -----------------------------------------------------------------------------------------------
>
> Key: CAMEL-23070
> URL: https://issues.apache.org/jira/browse/CAMEL-23070
> Project: Camel
> Issue Type: Bug
> Components: camel-as2
> Affects Versions: 4.18.0
> Reporter: Reto Peter
> Priority: Minor
>
> The async MDN receiver ({{{}AS2AsyncMDNServerConnection{}}}) uses
> {{RequestValidateHost}} in its HttpProcessor, which enforces strict host
> header validation per HttpCore 5.3+. This causes incoming async MDN requests
> to be rejected with HTTP 421 "Not authoritative"
> when the {{Host}} header doesn't exactly match the server's expected
> hostname.
> The main AS2 receiver ({{{}AS2ServerConnection{}}}) does not use
> {{{}RequestValidateHost{}}}, so it accepts any {{Host}} header. This is an
> inconsistency — async MDNs are rejected while regular AS2 messages are
> accepted.
> \{panel}
> h3. Root Cause
> In {{AS2AsyncMDNServerConnection.RequestListenerThread}} constructor (line
> ~105):
> \{code:java|title=AS2AsyncMDNServerConnection.java (Camel 4.18.0)}
> public RequestListenerThread(int port, SSLContext sslContext) throws
> IOException
> { setName(REQUEST_LISTENER_THREAD_NAME_PREFIX + port); // ...
> HttpProcessor httpProcessor = new DefaultHttpProcessor(new
> RequestValidateHost()); // <-- STRICT registry = new
> RequestHandlerRegistry<>(); handler = new
> BasicHttpServerRequestHandler(registry); httpService = new
> HttpService(httpProcessor, handler); }
> \{code}
> But in {{AS2ServerConnection.initProtocolProcessor()}} (line ~655):
> \{code:java|title=AS2ServerConnection.java (Camel 4.18.0)}
> protected HttpProcessor initProtocolProcessor(
> String as2Version, String originServer, String serverFqdn, String
> mdnMessageTemplate)
> { return HttpProcessorBuilder.create() .addFirst(new
> AS2ConsumerConfigInterceptor()) .add(new ResponseContent(true))
> .add(new ResponseServer(originServer)) .add(new
> ResponseDate()) .add(new ResponseConnControl())
> .add(new ResponseMDN(as2Version, serverFqdn, mdnMessageTemplate))
> .build(); // <--
> NO RequestValidateHost }
> \{code}
> The main AS2 server uses {{HttpProcessorBuilder}} with response
> interceptors only — no {{{}RequestValidateHost{}}}. The async MDN server uses
> {{DefaultHttpProcessor}} with only {{RequestValidateHost}} and no response
> interceptors at all.
> h3. Impact
> - Partners that send async MDNs with a {{Host}} header that doesn't match
> the server's configured hostname get HTTP 421 "Not authoritative"
> - This is common in production environments with load balancers, reverse
> proxies, or NAT where the external hostname differs from the internal one
> - The sender thinks the MDN delivery failed and may retry or flag the
> transmission as unconfirmed
> - Meanwhile, the original AS2 message was received successfully (because
> {{AS2ServerConnection}} has no host validation)
> h3. Steps to Reproduce
> Configure a Camel AS2 server with async MDN enabled on a separate port
> Have a partner send an AS2 message requesting async MDN delivery
> Partner sends the async MDN to the callback URL
> If the {{Host}} header in the MDN request doesn't exactly match the
> server's hostname: HTTP 421 "Not authoritative"
> This is easy to trigger in environments with:
> - Load balancers or reverse proxies (external hostname differs from
> internal)
> - Docker/Kubernetes (container hostname differs from service hostname)
> - NAT configurations
> - Partners that set {{Host}} header to the IP address instead of the
> hostname
> h3. Proposed Fix
> Remove {{RequestValidateHost}} from the async MDN server and add the same
> response interceptors as the main AS2 server:
> \{code:diff|title=Fix in AS2AsyncMDNServerConnection.java}
> public RequestListenerThread(int port, SSLContext sslContext) throws
> IOException
> { setName(REQUEST_LISTENER_THREAD_NAME_PREFIX + port); // ...
> - HttpProcessor httpProcessor = new DefaultHttpProcessor(new
> RequestValidateHost()); - HttpProcessor httpProcessor =
> HttpProcessorBuilder.create() .add(new ResponseContent(true))
> .add(new ResponseDate()) .add(new ResponseServer(null))
> .add(new ResponseConnControl()) .build(); - registry = new
> RequestHandlerRegistry<>(); handler = new
> BasicHttpServerRequestHandler(registry); httpService = new
> HttpService(httpProcessor, handler); }
> \{code}
> This makes the async MDN receiver consistent with the main AS2 receiver:
> - No strict host validation (same as {{{}AS2ServerConnection{}}})
> - Standard response interceptors are added ({{{}ResponseContent{}}},
> {{{}ResponseDate{}}}, etc.) — the current code has none, which means async
> MDN responses may also be missing standard HTTP headers
> h3. Additional Issue: Missing Response Interceptors
> Note that the current {{AS2AsyncMDNServerConnection}} creates the
> HttpProcessor with only {{RequestValidateHost}} and no response interceptors:
> \{code:java}
> HttpProcessor httpProcessor = new DefaultHttpProcessor(new
> RequestValidateHost());
> \{code}
> This means HTTP responses from the async MDN receiver may be missing
> standard headers like {{{}Content-Length{}}}, {{{}Date{}}}, and
> {{{}Connection{}}}. The proposed fix addresses both issues.
> h3. Our Workaround
> We use reflection after endpoint initialization to replace the
> {{HttpProcessor}} on the async MDN server's {{HttpService}} with a permissive
> processor built via {{HttpProcessorBuilder}} (with standard response
> interceptors but no {{{}RequestValidateHost{}}}).
> h3. Test Scenario
> Tested with OpenAS2 sending async MDNs.
> Before fix: Async MDNs rejected with HTTP 421 when Host header doesn't
> match.
> After replacing HttpProcessor: Async MDNs accepted regardless of Host
> header value.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)