Set REMOTE_ADDR to X-Forwarded-For in apache - apache

In a situation where Apache is sitting behind a reverse proxy (such as Squid), the cgi environment variable REMOTE_ADDR gets the address of the proxy rather than the client.
However, the proxy will set a header called X-Forwarded-For to contain the original IP address of the client so that Apache can see it.
The question is, how do we get Apache to replace REMOTE_ADDR with the value in the X-Forwarded-For header so that all of the web applications will transparently see the correct address?

You can use mod_rpaf for that. http://stderr.net/apache/rpaf/

Currently apache module mod_remoteip is the recommended way to do this; rpaf hasn't been reliably maintained, and can cause problems.

Note that the X-Forwarded-For header may contain a list of IP addresses if the request has traversed more than one proxy. In this case, you usually want the leftmost IP. You can extract this with a SetEnvIf:
SetEnvIf X-Forwarded-For "^(\d{1,3}+\.\d{1,3}+\.\d{1,3}+\.\d{1,3}+).*" XFFCLIENTIP=$1
Note the use of $1 to set the XFFCLIENTIP environment variable to hold the contents of the first group in the regex (in the parentheses).
Then you can use the value of the environment variable to set headers (or use it in Apache log formats so that the logs contain the actual client IP).

In addition to mod_rpaf as mentioned before, it appears that mod_extract_forwarded will perform this function as well.
One advantage to mod_extract_forwarded is that it is available from EPEL for RHEL/CentOS servers whereas mod_rpaf is not.
It appears that neither of these two modules allow you to whitelist an entire subnet of proxy servers, which is why the CloudFlare folks created their own plugin: mod_cloudflare which, it should be noted, is not a general-purpose tool like the other two; it contains a hardcoded list of CloudFlare subnets.

Yes, we can do this.
Just add a auto_prepend_file in your PHP.ini like auto_prepend_file = "c:/prepend.php"
and in this file add this:
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
You need the MOD_REMOTEIP in apache width RemoteIPHeader X-Real-IP.
Cheers,
Guiremach

Since Apache 2.4 there is mod_remoteip built-in module that does this.
Enable mod_remoteip
(e.g. a2enmod remoteip)
Create a list of trusted IP ranges (the IPs from which you accept the remote IP header). You can put them in a file like conf/trusted-ranges.txt
Add this line to the Apache config:
RemoteIPTrustedProxyList conf/trusted-ranges.txt
Change your log file formats to use %a instead of %h for logging the client IP.
For Cloudflare you need to trust all their IP ranges and use a custom header CF-Connecting-IP:
RemoteIPHeader CF-Connecting-IP
You can get Cloudflare ranges like this:
curl https://www.cloudflare.com/ips-v4 > trusted-ranges.txt
curl https://www.cloudflare.com/ips-v6 >> trusted-ranges.txt

Unfortunately,
at the time of this writing, none of the backports and forks at freshports.org, people.apache.org or gist.github.com worked. They were all based on an early alpha version of apache httpd 2.3 which was neither compatible with current versions of 2.2 nor 2.4.
So after hours of wasting time while trying to adjust the backports to create a real working one for httpd 2.2, I decided to move to httpd 2.4. Within httpd 2.4, mod_remoteip works smoothly, even if a load balancer has permanent keepalive connections which it uses to proxy requests from different actual client ip addresses to the backend. I'm not sure if the other modules can handle this situation (changing client ip addresses on each request within the same connection).

Remember that this value can be spoofed. See http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/ for a real-life example with Cross-site Scripting consequences.

You can install the module mod_extract_forwarded and set MEFaccept parameter to all.

Related

Apache Config variables

I have some difficulties to understand the apache variables described in here:
https://httpd.apache.org/docs/2.4/expr.html#vars
What I need to use in my directive:
The IP of the webserver the apache is running on
The IP of the client trying to access the server
The URL (string) the client is trying to access
Alternatively, a boolean telling me whether the current request is coming from the server itself
As far as I understand, REMOTE_ADDR must be the client's IP. But where is the Servers ID hidden? SERVER_ADDR does not exist...
SERVER_ADDR depending if your server provide it, else try HTTP_HOST
REMOTE_ADDR
REQUEST_FILENAME/REQUEST_URI
There is no native function provided by Apache doing that but I let you check this topic you may find what you looking for.

WebSphere reverse proxy plugin - skipping domains

Hoping someone can give me some advice if possible.
We have a Linux box in our DMZ with the WebSphere plugin. This points to a Windows box running WebSphere Application Server.
httpd config only contains the default virtualhost with no ServerAlias specified. There is a redirect set up in the virtualhost in httpd.conf to forward any requests to service.domain.com to service.domain.com/wascontext1. Plugin-cfg.xml is set up with two uri groups, wascontext1 and wascontext2, but only 1 is actively used.
I want to use the Linux box as a reverse proxy for another application totally separate to WAS. It would have a different domain (i.e. dimsim.domain.com) but point to the same IP.
I was going to add another virtualhost for this but am unsure exactly how the WebSphere plugin will behave with it. From what I understand if I set this up and went to dimsim.domain.com/wascontext1 it would serve the WebSphere content as httpd forwards all requests to the plugin.
Is there a way to tell httpd to not send requests to the WebSphere plugin based on domain name or virtualhost? Or would doing a rewrite on any requests to dimsim.domain.com/wascontext be considered ok?
thanks
jc
EDIT: Thanks for the responses! I'll test changing the virtualhost name in plugin-cfg.xml on our second unused context and let you know how it goes.
A solution that doesn't require plugin-cfg.xml changes: If you use an Apache-based HTTP server, you can conditionally set the per-request variable "skipwas" to short-circuit the WAS Plugin processing.
e.g.
SetEnvIf Host ^dimsim\.domain\.com$ skipwas=1
If you look at the plugin-cfg.xml file, in the first part of the file you will find virtualhostgroup section similar to this:
<VirtualHostGroup Name="default_host">
<VirtualHost Name="*:9080"/>
<VirtualHost Name="*:9443"/>
<VirtualHost Name="*:443"/>
<VirtualHost Name="*:80"/>
</VirtualHostGroup>
just change the Name from * to the required domain name e.g. service.domain.com and then plugin will forward only requests for the service.domain.com hostname.
So something like:
<VirtualHost Name="service.domain.com:80"/>
should work for you.
When a request comes into the web server, it is passed to the WebSphere plugin and then plugin examines the request based on its configuration to determine if it should forward to WebSphere or pass back to the web server for further processing.
The "route" clauses in the plugin-cfg.xml are key to determining what will be forwarded and what will not. A request must match all the values in the route to be forwarded. A route contains virtual hosts, uris and clusters. The request must match one a virtual host from the VirtualHostGroup in the route, a URI from the UriGroup in the route and there must be an available server in the ServerCluster value of the route for the request to be sent to WebSphere.
Note-If you manipulate your plugin-cfg.xml for your setup, be aware that plugin is very sensitive about the format of this configuration and incorrect or invalid entries could cause a crash of the webserver. Be sure to backup the file and test before using in production. Also, if you modify your WebSphere configuration, it could overwrite this file and wipe out your changes.
Sorry for the late response.
covener's answer of setting the following does what I need.
SetEnvIf Host ^dimsim\.domain\.com$ skipwas=1

Shibboleth Errors When Authenticating Through a Reverse Proxy on port 8000

Trying to get shibboleth working on a Docker container, using Apache as a reverse proxy. Problems occur because I am using port 80 for the public Apache instance and port 8000 for the internal instance, which confuses shibboleth:
2017-10-03 07:34:23 ERROR OpenSAML.MessageDecoder.SAML2POST [5]: POST targeted at (https://dashboard.hpc.unimelb.edu.au/Shibboleth.sso/SAML2/POST), but delivered to (https://dashboard.hpc.unimelb.edu.au:8000/Shibboleth.sso/SAML2/POST)
The first URL is the external URL which the end user sees. The second URL is what the docker container sees when it gets the proxied request (with the HTTP host name forwarded).
Note I used "ShibURLScheme https" on the internal apache instance to ensure it believes https is being used, as the reverse proxy ensures all requests are https.
Is there anyway I call tell Shibboleth that this is OK, the URLs really are the same? Maybe by rewriting the URL shibboleth sees or something?
Thanks
There are two things I would check:
Make sure the ServerName directive in your Apache conf file is set to
https://dashboard.hpc.unimelb.edu.au:80. Notice the :80. Omitting
the port number completely may work too. You want Apache and vis-a-vis, shibd to see the ServerName that the client is using. You should also make sure you have a UseCanonicalName On directive as part of this.
This is likely less of a problem, but make sure your sp-metadata given to the IdP is correct. You should add entries for the FQDN(s) that the client sees. Please note that most of that documentation page isn't applicable to your scenario, but adding the correct metadata entries is vital.
It looks like your ServerName in Apache isn't correct, so I'd start there.

rewrite subdomain using .htaccess

My main domain is subdomain.domain.tld, and I want to rewrite all the traffic from subdomain1.domain.tld to the first one. Meaning if someone accesses subdomain1.domain.tld/whatever.php, he actually accesses subdomain.domain.tld/whatever.php, however, he's still shown subdomain1 in the browser's navigation bar.
I did some research, but I couldn't find something too promising.
You don't need rewriting for this, in fact internal rewriting is not possible between separate hosts...
Assuming that both "subdomains" (those are actually hostnames) are served by the same http server you can simply configure the same DocumentRoot for both hosts. That way they serve exactly the same file system which obviously means that the same scripts will be called.
Maybe you can get away even more simple if you just use the ServerAlias command for your virtual host. This obviously is only possible if you do not need separate configurations for both hosts.
Just take a look into the documentation of the apache http server. This is explained and good examples are offered:
https://httpd.apache.org/docs/2.4/vhosts/examples.html
In case those two hosts are not served by the same http server you could use an internal proxy setup: subdomain1.domain.tld acts as a front end proxy for subdomain.domain.tld, so it just relays all incoming requests and also the outgoing responses. That is easily done with a combination of the ProxyPass and the ProxyPassReverse rules offered by apaches proxy module: https://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypassreverse
This setup can even be used if the two http servers operate on different IP addresses or even completely separate systems.

problem disabling mod_authz_host to avoid dns lookups

i'm using Debian 4.3.2-1 and Apache 2 in my production server. Watching the logs I noticed apache is resolving client's hostnames with 'HostnameLookups Off' in apache2.conf. I want to avoid these lookups so i'm guessing apache is making this dns queries because i have mod_authz_host enabled. When i try to unlink this module i get several modules complaining because they use "Order" directive.
How is the clean way to go? Should I comment all Order directives like
Order allow,deny
Deny from all
Is this the only way to stop apache making dns requests ?
thank you!
One option is to have the DNS server quickly answer any requests to convert an IP in to a hostname with a "this IP has no hostname" responses. MaraDNS 1.x supports this via "reject_ptr"; I could add this capability to Deadwood (MaraDNS 2.0) in about 10 minutes if there was demand for it.