Using Apache as a proxy/reverse proxy

We ran into an issue at work recently, we needed to add SSL certificates to a service that did not allow it. Using httpd we were able to set it up quickly, and reliably.

These steps are for Centos 7, as that is what I use in production.

$ sudo yum install -y httpd openssl

after install, I usually rename the welcome.conf to welcome.conf.old, and userdir.conf to userdir.conf.old.

cd /etc/httpd/conf.d/
mv welcome.conf welcome.conf.old
mv userdir.conf userdir.conf.old

SSL Certs:

self-signed:

Self signed certificates are super easy to generate, and work just fine for internal encrypted communication between two services. just add your respective SSLCertificateFile and SSLCertificateKeyFile entries to your apache virtual host config.

openssl req -x509 -nodes -days 2000 -newkey rsa:2048 -keyout mydomain.key -out mydomain.crt

letsencrypt:

My site uses letsencrypt certificates, and installing them is very easy

$ sudo yum update && sudo yum upgrade
$ sudo yum install -y git
$ sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
$ cd /opt/letsencrypt

Creating your certs:

 $ sudo letsencrypt-auto certonly --standalone -d example.com -d www.example.com

enter your email when asked, and read and agree to the TOS.

Certificates are stored in /etc/letsencrypt/archive with symlinks to originals located in /etc/letsencrypt/live with each domain in its own folder under the folder root.

  • cert.pem: server certificate only.

  • chain.pem: root and intermediate certificates only.

  • fullchain.pem: combined server, root, and intermediate certificates (replaces cert.pem and chain.pem).

  • privkey.pem: private key (keep this key private).

configuring apache as a reverse proxy/proxy:

let's say I have 4 servers running, on ports 5001-5005.

# file name /etc/http/conf.d/api-ssl.conf
ServerName api.domain.com

# adding keys for SSL and enabling
SSLEngine on  
SSLCertificateFile /etc/ssl/certs/api.domain.com.crt  
SSLCertificateKeyFile /etc/ssl/certs/api.domain.com.key

# proxy configs below, notice how we have both forward (ProxyPass) and reverse configured.

ProxyPass /inv http://api.domain.com:5000/ retry=1 acquire=3000 timeout=600 Keepalive=On  
ProxyPassReverse /inv http://api.domain.com:5000/  
ProxyPass /hng http://api.domain.com:5001/ retry=1 acquire=3000 timeout=600 Keepalive=On  
ProxyPassReverse /hlng http://api.domain.com:5001/  
ProxyPass /autoresponse http://api.domain.com:5002/ retry=1 acquire=3000 timeout=600 Keepalive=On  
ProxyPassReverse /autoresponse http://api.domain.com:5002/  
ProxyPass /poop http://api.domain.com:5003/ retry=1 acquire=3000 timeout=600 Keepalive=On  
ProxyPassReverse /phl http://api.domain.com:5003/  
ProxyPass /oldinv http://api.domain.com:5004/ retry=1 acquire=3000 timeout=600 Keepalive=On  
ProxyPassReverse /oldinv http://api.domain.com:5004/

#general logging, need to make sure we get the client IP logged.

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined  
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy  
SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded  
CustomLog "logs/access_log" combined env=!forwarded  
CustomLog "logs/access_log" proxy env=forwarded  

` Now instead of hitting api.domain.com:5000 or api.domain.com:5001 we hit https://api.domain.com/inv/ or https://api.domain.com/hng to access the API's. It really cleans up the API being able to take advantage of 1 port (443) instead of 4+.