Introduction
In addition to being a “basic” web server, and providing static and dynamic content to end-users, Apache httpd (as well as most other web servers) can also act as a reverse proxy server, also-known-as a “gateway” server.
In such scenarios, httpd itself does not generate or host the data, but rather the content is obtained by one or several backend servers, which normally have no direct connection to the external network. As httpd receives a request from a client, the request itself is proxied to one of these backend servers, which then handles the request, generates the content and then sends this content back to httpd, which then generates the actual HTTP response back to the client.
There are numerous reasons for such an implementation, but generally the typical rationales are due to security, high-availability, load-balancing and centralized authentication/authorization.
It is critical in these implementations that the layout, design and architecture of the backend infrastructure (those servers which actually handle the requests) are insulated and protected from the outside; as far as the client is concerned, the reverse proxy server is the sole source of all content.
More is here.
Typical implemetation is below:
In this tutorial, we will set up Apache as a basic reverse proxy using the mod_proxy extension to redirect incoming connections to one or several backend servers running on the same network. This Apache Proxy Server also creates and manages security (ssl engine, https). Conection to the backend servers from this Proxy Server is not encrypted (only http). Next, we will use https (ssl certificates from Let’s Encrypt for ours conections from outside world, but not to backend.
Installation
For a minimum HTTP server instalation install apache itself:
yum install httpd -y
Make sure, that the “/etc/hosts” file contain references for the loopback address and the hostname
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 192.168.3.3 edge-proxy-e edge-proxy-e.gonscak.sk
Turn on the HTTP server, and make sure it starts automatically on reboot. Next, add http port to the firewalld.
systemctl start httpd.service systemctl enable httpd.service firewall-cmd --add-service=http --permanent firewall-cmd --reload
Now, we can test our apache test web page on http address. This page is there fer testing and informational purposes:
http://edge-proxy-e.gonscak.sk
If you see the test page above, then your server is now correctly installed.
Example – Reverse Proxying a Single Backend Server
Create a first configuration file for our test backend server (I assume, that you already have one).
vim /etc/httpd/conf.d/test-vhost.conf <VirtualHost *:80> ServerName edge-proxy-e.gonscak.sk ProxyPreserveHost On ProxyPass / http://media.gonscak.sk/ ProxyPassReverse / http://media.gonscak.sk/ </VirtualHost>
There are three directives here:
ProxyPreserveHost
makes Apache pass the originalHost
header to the backend server. This is useful, as it makes the backend server aware of the address used to access the application.ProxyPass
is the main proxy configuration directive. In this case, it specifies that everything under the root URL (/
) should be mapped to the backend server at the given address. For example, if Apache gets a request for/example
, it will connect tohttp://media.gonscak.sk/example
and return the response to the original client.ProxyPassReverse
should have the same configuration asProxyPass
. It tells Apache to modify the response headers from backend server. This makes sure that if the backend server returns a location redirect header, the client’s browser will be redirected to the proxy address and not the backend server address, which would not work as intended.
Now, we can test out configuration with the first command below. It runs a configuration file syntax test and report OK or error. And with second command we gracefully restarts Apache httpd daemon. If the daemon is not running, it is not started. Currently open connections are not aborted:
apachectl configtest apachectl graceful
And now, if everything is OK, we can open out web page now (http://192.168.3.3). We now not see the default page of apache, but the content of backend server media.gonscak.sk. We are not connected directly to the media.gonscak.sk, but only to the “edge” server with Apache.
Enabling SSL support, set certificates from LetsEcnrypt
First, we must install package mod_ssl for Apache to support SSL:
yum install mod_ssl.x86_64
Now, we must open port 443 for Apache in firewall:
firewall-cmd --add-service=https --permanent firewall-cmd --reload
Now, we create o text file, where we set up some directives for vhost. And then we can simple change som SSL directives for all vhosts in Apache. I use some Mozilla recommendations via https://mozilla.github.io/server-side-tls/ssl-config-generator:
SSLEngine on SSLCertificateFile /etc/pki/tls/certs/newclient.crt SSLCertificateKeyFile /etc/pki/tls/private/newclient.key SSLCACertificateFile /etc/pki/tls/certs/ca.crt Header always set Strict-Transport-Security "max-age=15768000" SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 SSLHonorCipherOrder on SSLCompression off
Next, I create an empty directory for DocumentRoot. There will be no content:
mkdir -p /var/www/vhosts/sk.gonscak.media
I edit config file for “/etc/httpd/conf.d/test-vhost.conf” and add virtualhost for ssl. And add link for log files.
<VirtualHost *:80> ServerAdmin webmaster@gonscak.sk ServerName edge-proxy-e.gonscak.sk AddDefaultCharset UTF-8 RedirectPermanent / https://edge-proxy-e.gonscak.sk/ </VirtualHost> <VirtualHost *:443> ServerAdmin webmaster@gonscak.sk DocumentRoot "/var/www/vhosts/sk.gonscak.media" AddDefaultCharset UTF-8 ServerName edge-proxy-e.gonscak.sk ErrorLog /var/log/httpd/sk.gonscak.media-error_log CustomLog /var/log/httpd/sk.gonscak.media-access_log common Include /etc/httpd/conf.d/modern-ssl-template.txt <IfModule mod_proxy.c> ProxyRequests Off ProxyPass /.well-known/ ! ProxyPass / http://media.gonscak.sk/ ProxyPassReverse / http://media.gonscak.sk/ SSLProxyEngine Off ProxyPreserveHost Off </IfModule> </VirtualHost>
Now, I hide some information, which world can get from our Apache server. Add this directives to Apache configuration. Detailes can be read here.
vim /etc/httpd/conf/httpd.conf ServerSignature Off ServerTokens Prod
Some nice explanations of Proxy and WordPress behind it is here: https://community.pivotal.io/s/article/Purpose-of-the-X-Forwarded-Proto-HTTP-Header
Selinux problem
If we have enabled selinux (check like this):
# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 31
And we see some problems in our error log and the page is not loaded:
[Tue Aug 25 12:25:37.344072 2020] [proxy_http:error] [client xxx:xxx] AH01114: HTTP: failed to make connection to backend: 192.168.88.5
We can see this error at log:
sealert -a /var/log/audit/audit.log
Just allow selinux policy to Apache to can network connect via setsebool:
setsebool -P httpd_can_network_connect on
And that is.