Removing duplicate headers from HTTP requests - apache

I am using an Apache 2.4 server with mod_proxy as an HTTP reverse proxy for Tomcat server. The reverse proxy works on a Split-DNS configuration where "server.com" might point either to the actual HTTP server or to my reverse proxy depending on where the client is.
The problem that I'm having is that our client application had a problem where sometimes it would include an header more than once. For example, an HTTP request could end up looking like this:
POST server.com HTTP/1.1
Some-Header: foo
Authorization: BASIC abc123
Authorization: BASIC abc123
Other-Headers: ...
This works fine if the client is talking directly to Tomcat but if it goes through the reverse proxy then the duplicated headers seem to get mangled and Tomcat ends up receiving a request that looks like this:
POST server.com HTTP/1.1
Some-Header: foo
Authorization: BASIC abc123, BASIC abc123
Other-Headers: ...
I used Wireshark to inspect the HTTP requests as they are sent/received in the Client->Proxy->Tomcat chain and Apache is definitely the component that is "collapsing" the two headers into one.
Is there a way to configure this behavior in a way where it either sends both headers or just one? What I don't want is this "collapsing" taking place...

You can use mod_headers to remove the duplicate header. See their official docs for information on how to enable it.
Then you can add a line like this to your configuration file so that the first part of header disappears:
RequestHeader edit Authorization "^BASIC\ abc123\\,\ " ""
Let me know if that works for you.

Related

Bearer token for upstream server with NGINX reverse proxy. Is the header being stripped?

I have a Tomcat server that is behind an NGINX reverse proxy applying SSL. There is a bearer token in place for API calls on the Tomcat server, but I am getting a 401 error when I send this token to an endpoint in Postman. The proxy otherwise works flawlessly.
I've spent way too long troubleshooting this, but I've only looked at my proxy settings. I discovered last night that the proxy should be forwarding Authentication headers to the upstream Tomcat server, so now I'm lost as to how to troubleshoot this. Has anyone encountered this before or can point me in the right direction? This is outside of my normal scope so I'm a little out of my element.
EDIT - Even when I force the header with the Bearer token using "proxy_set_header Authorization "Bearer $ID_TOKEN";" it still returns the 401 error. Is it maybe adding something it shouldn't like a second Authorization header, or appending the Authorization header?
EDIT2 - Tomcat error logs show:
[{"time":"2021-05-14 19:01:10.069","description":"Request header did not include a token."}]
If you are not using the auth_request module for NGINX then it should be fairly easy to simply pass the Authorization headers as followed:
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
If this doesn't work i will really need to see more of your NGINX configuration and I would strongly suggest to use the NGINX auth_request module to handle all oAuth on the NGINX server itself.

Caddy as reverse proxy to rewrite a http redirect url from an upstream response

I am having a backend that is not able when running behind a reverse proxy since I cannot configure a custom base URL.
For the login process the backend makes heavy use of HTTP redirects but due to the fact that is behind a reverse proxy it sends redirection URL that are not reachable by the client.
So I was wondering if there is a way to rewrite the upstream HTTP HEADER Location
If the backend responses
HTTP/1.1 301
Location: http://backend-hostname/auth/login
Caddy should rewrite the Location header to
HTTP/1.1 301
Location: http://www.my-super-site.com/service/a/auth/login
Is something like this possible?
I've that we can remove headers by declaring
header / {
- Location
}
but it possible to replace the header and rewrite the URL?
I was also looking for answer for this question and unfortunately I've found this responses:
https://caddy.community/t/v2-reverse-proxy-but-upstream-server-redirects-to-nonexistent-path/8566
https://caddy.community/t/proxy-url-not-loading-site/5393/7
TLDR:
You need to use sub-domains rather than sub-paths for services that are not design for being after proxy (or at least configure base URL). :(

Apache configuration: effect of explicit :80 in http header field (host)

We have a server running Apache providing services via a simple API. We now stumbled upon the problem that we cannot access the API using a third-party library, altough the resulting HTTP request are ALMOST the same. The only difference - as far as we can tell from Wireshark - is the presence or absence of the explicit information about port 80. For example:
curl -d "..." http://www.example.com/foo/bar/
curl -d "..." http://www.example.com:80/foo/bar/
Both work, and Wireshark shows Host: www.example.com, i.e., without the port 80. As far as I understand cURL as well as browser or most other clients remove port 80. So far, all fine.
Now, a third-party library to make requests requires to set a port, and we need to set it to 80. If the library makes a request, Wiresharks now shows Host: www.example.com:80 - note the additional port information. This request fails, and as far as we can see in Wiresharks, this failing request only differs with respect to the host field.
Can this be a configuration issue of Apache? We currently have no direct access to the server to check the conf files. Or are we missing something completely different here.
From rfc 2616:
Host = "Host" ":" host [ ":" port ] ; Section 3.2.2
So "Host: www.example.com:80" is perfectly legitimate. But I have never seen port 80 (or 443 in the case of HTTPS) in the host field of a HTTP request. It is obviously required where the request is routed via a proxy to a non-standard port.
This would give me some concerns as to the quality of the "third-party library". My first of port of call in resolving this would be to speak to the providers of the component - they have presumably come across the problem before.
You did not mention what access you have to the library - did you check that this is not a configurable option? Do you have access to the source code, and the permission to modify it? (if not, that would imply it is commercial, paid-for software - which should give you the right to some support).
I don't know what the solution is, but some obvious things to try would be:
configure the URL at the default vhost for webserver rather than explicitly for www.example.com
or use mod_headers to rewrite the host field
or put a forward proxy in front of the webserver e.g. squid and add a url rewriter (if squid does not automatically strip the port from the host field)
Apache performs string matching with the Host field. So when the :80 is attached, the string matching will fail and Apache will consider it a URL it does not handle and reject it. That is why curl stripped it.
You can read more about the ServerName field here, which is the setting in which Apache matches against Host.
Update
So the :80 has no effect and the string matching still works.
On my production server, I did not change Apache's configuration. I wrote some quick PHP to send out the GET request on a socket, and Apache still responded correctly with the :80 attached to the Host: field.
I also checked on the server itself and see the request come in with the errant :80 attached to it and Apache answers with the status of 200 and presents the HTML.
There is something else wrong with the third party software's request.

Can the Host Header be different from the URL

We run a website which is hosted using WCF.
The website is hosted on: https://foo.com and the ssl certicate is registered using the following command:
netsh http add sslcert hostnameport=foo.com:443
When we browse the website on the server, all is fine, and the certificate is valid.
There is a loadbalance in front of the server which listens to bar.com and then redirects the request to our server.
The loadbalancer doesn't rewrite the get URL, but only the Host Header.
The rewritten header looks like this:
GET https://foo.com/ HTTP/1.1
Host: bar.com
Connection: keep-alive
Now we have some issues which indicates that the ssl certificate is invalid in this case.
The Loadbalancer itself has a certificate registered listening to https://bar.com
Questions:
Is it ok/allowed that the get URL and the Host in the http header are different?
If it is ok to have different values in the header, under which url should we run the site? get URL or Host url?
Well, referencing the RFC2616:
If Request-URI is an absolute URI, the host is part of the
Request-URI. Any Host header field value in the request MUST be
ignored.
So, back to your questions:
It is allowed but a bad idea as it will create confusion, better to use relative path. i.e.
GET /path HTTP/1.1
instead of
GET https://foo.com/path HTTP/1.1.
Modify the loadbalance configuration to do so. Or make the both values the same.
If Host header has a value different than the request URI, then the URI is taking priority over the Hosts header.

Force ssl certificate to be included in CORS pre-flight request

I am trying to make a (non-simple) CORS GET request (AJAX) made by my client server (running on Apache # port 443) to a 3rd party endpoint server (running on Tomcat # port 8443), which fails to trigger when tried over HTTPS.
When SSL is not enabled it works just fine indicating that the CORS is set up properly.
The problem is that since the GET is (not-simple) it sends a pre-flight OPTIONS request.
According to this:
Pre-flight OPTIONS request failing over HTTPS
Pre-flighted requests don't include the client certificate. He states this is in the CORS spec however I was unable to find this specifically listed in the spec:
http://www.w3.org/TR/cors/
The third party cannot enable
SSLVerifyClient optional
as they require all communication be sent with SSL.
However they do have their CORS setup right and they have
access-control-allow-credentials: "true"
In our AJAX call we included in the xhrFields
withCredentials: true
So we are telling it to pass withCredentials (which includes cert / cookie / etc)
And on our APACHE we have
SSLOptions +ExportCertData
Somehow when we make the call though, they are still seeing the error "key/cert was not included "
Am I missing something? Is there a way to force this in Apache?
At the moment I'm getting ready to create a man the middle script to attach the cert to the initial request but it seems like there has to be a better way.
Any suggestions?