Apache HTTP Server as a Basic Forward/Reverse Proxy


Nitin Venkatesh's Gravatar

Nitin Venkatesh
published Sept. 14, 2016, 9:35 a.m.


Forward vs Reverse Proxies - Intro

image-forward-reverse-proxy

Source: Quora.com

Forward Proxy

A forward proxy brokerages the request of the client and forwards the request to the destination of the client's choice contingent on the configuration of the forward proxy.

Most common uses are to

  • Bypass firewall restrictions.
  • Cache static resources and save bandwidth.
  • Control what resources clients can access.

Reverse Proxy

A reverse proxy brokerages the request of the client and forwards the request to the destination purely based on the configuration of the reverse proxy.

Most common uses of a reverse proxy are to

  • Redirect traffic to machines on the internal network.
  • Keep the client unaware of which machine they are accessing.
  • Perform load balancing.
  • A central point for Web Application Firewall monitoring.

Environment Setup

Warning: Enabling proxies, either forward or reverse, could pose a huge security risk. It is highly recommended that you harden the server and take the necessary security precautions required before enabling proxies in your network.

The following article is just to demo how the Apache HTTP Server could act as a forward and reverse proxy. The machines used in the demo were entirely run on a private isolated network.

Okay, now that we have the disclaimers out of the way, let's see how our environment setup will be like. We will use four machines - 2 proxies, 1 web server and a client.

  • Web Server - 192.168.33.11
  • Client - 192.168.33.1
  • Proxy Server 1 - 192.168.33.10
  • Proxy Server 2 - 192.168.33.12 (This will be rarely used - only to demo proxy chains)

image-network-diag-demo-setup

Our Web Server also runs the Apache HTTP Server with mod_php5 and so has support for PHP5. We have a PHP script on it called test.php which echoes back the Request Headers of any incoming requests.

## test.php
<?php
    print_r(apache_request_headers());
?>

On the Proxy Server machines we enable the mod_proxy and mod_proxy_http modules for Proxying support. This is done with the following commands on Ubuntu or other Debian-based machines:

$ sudo a2enmod proxy
$ sudo a2enmod proxy_http

A server restart is required on addition of the two modules - $ sudo service apache2 restart

Reverse Proxy

Adding the following Directives in the VirtualHost configuration of our Proxy Server 1 (192.168.33.10) allows for any incoming traffic on that particular VirtualHost to be forwarded to our Web Server (192.168.33.11).

## Proxy Server 1 -> Web Server

ProxyPass / http://192.168.33.11/
ProxyPassReverse / http://192.168.33.11/

A configuration reload is required after adding the two lines - $ sudo service apache2 restart

A request made to 192.168.33.10/test.php from our Client machine (192.168.33.1) looks so,

$ curl http://192.168.33.10/test.php
Array
(
    [Host] => 192.168.33.11
    [User-Agent] => curl/7.47.0
    [Accept] => */*
    [X-Forwarded-For] => 192.168.33.1
    [X-Forwarded-Host] => 192.168.33.10
    [X-Forwarded-Server] => 10.0.2.15
    [Connection] => Keep-Alive
)

ProxyChains

Let's configure the Proxy Server 2 to act as a reverse proxy, forwarding all traffic to Proxy Server 1 and then study the effect. So add the following lines to the VirtualHost configuration of Proxy Server 2 (192.168.33.12)

## Proxy Server 2 -> Proxy Server 1

ProxyPass / http://192.168.33.10/
ProxyPassReverse / http://192.168.33.10/

So now, let's make a request from the client to Proxy Server 2. The route would be as follows: Client -> Proxy Server 2 -> Proxy Server 1 -> Web Server

$ curl http://192.168.33.12/test.php
Array
(
    [Host] => 192.168.33.11
    [User-Agent] => curl/7.47.0
    [Accept] => */*
    [X-Forwarded-For] => 192.168.33.1, 192.168.33.12
    [X-Forwarded-Host] => 192.168.33.12, 192.168.33.10
    [X-Forwarded-Server] => 10.0.2.15, 10.0.2.15
    [Connection] => Keep-Alive
)

Adding ProxyPreserveHost

You could add an additional directive to the VirtualHost config of Proxy Server 1, so that it now looks like

ProxyPass / http://192.168.33.11/
ProxyPassReverse / http://192.168.33.11/

ProxyPreserveHost On

Now, when the same request is made from our client machine, it looks like so,

$ curl http://192.168.33.10/test.php
Array
(
    [Host] => 192.168.33.10
    [User-Agent] => curl/7.47.0
    [Accept] => */*
    [X-Forwarded-For] => 192.168.33.1
    [X-Forwarded-Host] => 192.168.33.10
    [X-Forwarded-Server] => 10.0.2.15
    [Connection] => Keep-Alive
)

The Host header gets preserved from the original request instead of being overwritten by Proxy Server 1.

Proxy Chains with ProxyPreserveHost

With ProxyPreserveHost On on both Proxy Server 1 and Proxy Server 2,

# Client -> Proxy Server 2 -> Proxy Server 1 -> Web Server

$ curl http://192.168.33.12/test.php
Array
(
    [Host] => 192.168.33.12
    [User-Agent] => curl/7.47.0
    [Accept] => */*
    [X-Forwarded-For] => 192.168.33.1, 192.168.33.12
    [X-Forwarded-Host] => 192.168.33.12, 192.168.33.12
    [X-Forwarded-Server] => 10.0.2.15, 10.0.2.15
    [Connection] => Keep-Alive
)

With ProxyPreserveHost On on Proxy Server 1 and ProxyPreserveHost Off on Proxy Server 2,

# Client -> Proxy Server 2 -> Proxy Server 1 -> Web Server

$ curl http://192.168.33.12/test.php
Array
(
    [Host] => 192.168.33.10
    [User-Agent] => curl/7.47.0
    [Accept] => */*
    [X-Forwarded-For] => 192.168.33.1, 192.168.33.12
    [X-Forwarded-Host] => 192.168.33.12, 192.168.33.10
    [X-Forwarded-Server] => 10.0.2.15, 10.0.2.15
    [Connection] => Keep-Alive
)

Forward Proxy

To configure my Proxy Server 1 (192.168.33.10) as my Forward Proxy, I add the following two Directives to my VirtualHost configuration of Proxy Server 1.

ProxyRequests On
ProxyVia On

Here's the client trying to access http://google.com using Proxy Server 1,

Using HTTP 1.0:

$ netcat 192.168.33.10 80
GET http://google.com HTTP/1.0

HTTP/1.1 302 Found
Date: Mon, 05 Sep 2016 19:30:15 GMT
Server: Apache/2.4.7 (Ubuntu)
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&ei=x8fNV-OIBcWL8QfHwoDYAQ
Content-Length: 261
Via: 1.0 10.0.2.15
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.in/?gfe_rd=cr&amp;ei=x8fNV-OIBcWL8QfHwoDYAQ">here</A>.
</BODY></HTML>

Using HTTP 1.1:

$ netcat 192.168.33.10 80
GET http://google.com HTTP/1.1
Host: google.com

HTTP/1.1 302 Found
Date: Mon, 05 Sep 2016 19:28:44 GMT
Server: Apache/2.4.7 (Ubuntu)
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&ei=bMfNV9DkKK_v8wervYu4Bw
Content-Length: 261
Via: 1.1 10.0.2.15

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.in/?gfe_rd=cr&amp;ei=bMfNV9DkKK_v8wervYu4Bw">here</A>.
</BODY></HTML>

Another way using HTTP 1.1: (note the Host header)

$ netcat 192.168.33.10 80
GET http://google.com HTTP/1.1
Host: 192.168.33.10

HTTP/1.1 302 Found
Date: Mon, 05 Sep 2016 19:30:55 GMT
Server: Apache/2.4.7 (Ubuntu)
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&ei=78fNV9KIF9jpugTU0oWwAw
Content-Length: 261
Via: 1.1 10.0.2.15

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.in/?gfe_rd=cr&amp;ei=78fNV9KIF9jpugTU0oWwAw">here</A>.
</BODY></HTML>

And now, back to our test.php script sitting on our Web Server (192.168.33.11),

$ netcat 192.168.33.10 80
GET http://192.168.33.11/test.php HTTP/1.0

HTTP/1.1 200 OK
Date: Mon, 05 Sep 2016 19:33:19 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.19
Vary: Accept-Encoding
Content-Length: 91
Content-Type: text/html
Via: 1.0 10.0.2.15
Connection: close

Array
(
    [Host] => 192.168.33.11
    [Via] => 1.0 10.0.2.15
    [Connection] => close
)

And now for a twist, Proxy Server 1 (192.168.33.10) acting as a reverse proxy to Web Server (192.168.33.11) and Proxy Server 2 (192.168.33.12) acting as a Forward Proxy.

$ netcat 192.168.33.12 80
GET http://192.168.33.10/test.php
Array
(
    [Host] => 192.168.33.10
    [Via] => 0.9 10.0.2.15
    [X-Forwarded-For] => 192.168.33.12
    [X-Forwarded-Host] => 192.168.33.10
    [X-Forwarded-Server] => 10.0.2.15
    [Connection] => Keep-Alive
)

Restricting access to your Forward Proxy

For this section, you're going to need mod_authz_host which comes enabled by default.

So, in this scenario, we will be using our client machine (192.168.33.1) as the one authorized to use the Forward Proxy, and our 192.168.33.12 as another client but one that is not authorized to use the Forward Proxy.

Now we add the following lines to our VirtualHost configuration on our Proxy Server 1 (192.168.33.10) that is acting as our Forward Proxy. This will allow only 192.168.33.1 to access our Forward Proxy and anyone else trying to access it will be blocked.

<Proxy "*">
Require ip 192.168.33.1
</Proxy>

And reload for the configuration to take effect - $ sudo service apache2 reload

And now, accessing from our authorized client (192.168.33.1),

$ netcat 192.168.33.10 80
GET http://google.com HTTP/1.0

HTTP/1.1 302 Found
Date: Sat, 10 Sep 2016 16:24:14 GMT
Server: Apache/2.4.7 (Ubuntu)
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Location: http://www.google.co.in/?gfe_rd=cr&ei=rjPUV8f3MseL8QfxsZq4DA
Content-Length: 261
Via: 1.0 10.0.2.15
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.in/?gfe_rd=cr&amp;ei=rjPUV8f3MseL8QfxsZq4DA">here</A>.
</BODY></HTML>

But whereas, trying to access from an unauthorized client,

$ netcat 192.168.33.10 80
GET http://google.com HTTP/1.0

HTTP/1.1 403 Forbidden
Date: Sat, 10 Sep 2016 16:25:48 GMT
Server: Apache/2.4.7 (Ubuntu)
Content-Length: 293
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access http://google.com
on this server.</p>
<hr>
<address>Apache/2.4.7 (Ubuntu) Server at google.com Port 80</address>
</body></html>

And there you have it, restricted access to your Forward Proxy based on IP addresses.

Footnote:

Again, as a reminder, please do go through the official documentation and harden your server before using it as a Forward / Reverse Proxy.

References: