How to use Apache as Reverse Proxy on Centos 7 with selinux

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 original Host 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 to http://media.gonscak.sk/example and return the response to the original client.
  • ProxyPassReverse should have the same configuration as ProxyPass. 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.

Total Page Visits: 176712 - Today Page Visits: 119