Visualize this outcome:
site1.example.com
-> my single public IP -> Port forwarding 80 and 443 to LAN192.168.1.10
, a Mac mini running Apple macOS Server 5.3 on macOS 10.12 Sierra.site2.example.com
-> my single public IP -> LAN192.168.1.10
-> reverse proxy? -> :80 and :443 on LAN192.168.1.15
, another Mac mini running GitLab CE on Ubuntu 16.04.
This is on a network where I have no option of adding another public IP.
#1 is already up and running fine.
#2 is the hard part, mainly because I'm running macOS Server.app on #1, and the proxy setup does not seem to be the relatively straight-forward Apache one. Server has the Apache config files in odd places, and it likes to overwrite them with new changes made in the GUI, so it’s taken me a while to figure out the “right” way to do this under Server.app.
Enter Web Apps
I first had this question two years ago, and a friend of mine suggested there might be a way to do it with OS X Server's webappctl
command and writing an appropriate webapp.plist
, though the Work with web apps section of the OS X Server docs contains almost no detail. The docs have been updated to cover Server 5.3, but the web app documentation is still very sparse.
This new answer below is written as of macOS Server 5.3. Server 5 overhauls things in that every service in Server is now behind one master reverse proxy, so these instructions will not work with Server 4.1. However, these instructions are adapted and clarified from the instructions found at precursor.ca’s R.A.I.S page (scroll down to the “macOS Server 5 Reverse Proxy” tutorial link). Their tutorial also includes historical instructions for doing this with Server 4.1. However, since their tutorial focuses more on running multiple services from the same Mac instead of proxying to a separate machine, I’ve re-written the instructions to clarify that below.
Configuration Files
Note that in all of the files, you will need to replace the LAN IP addresses, and
site2
and the other example domain and file names with the names of your own proxied domain. There are several occurrences within each configuration file.
Make a web app configuration file on the macOS Server machine, in /Library/Server/Web/Config/apache2/httpd_site2webapp.conf
, pointing at the IP address of the site2
server.
1 2 3 4 5 |
ProxyPreserveHost On ProxyPassReverse / http://192.168.1.15:80/ ProxyPass / http://192.168.1.15:80/ ServerName site2.example.com |
Then in /Library/Server/Web/Config/apache2/webapps/com.example.site2webapp.plist
, add the following, referencing the location of the .conf
file above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!-- See man pages for webapp.plist(5) and webappctl(8) for information about this example webapp.plist --> <plist version="1.0"> <dict> <key>includeFiles</key> <array> <!-- Include files are activated in virtual host when webapp is started --> <string>/Library/Server/Web/Config/apache2/httpd_site2webapp.conf</string> </array> <key>name</key> <string>com.example.site2webapp</string> <key>displayName</key> <!-- Name shown in Server app --> <string>site2WebApp</string> <key>installationIndicatorFilePath</key> <!-- The presence of this file indicates web app is installed --> <string>/Library/Server/Web/Config/apache2/httpd_site2webapp.conf</string> <key>sslPolicy</key><!-- Determines webapp SSL behavior --> <integer>0</integer> <!-- 0: default, UseSSLWhenEnabled --> <!-- 1: UseSSLAlways --> <!-- 2: UseSSLOnlyWhenCertificateIsTrustable --> <!-- 3: UseSSLNever --> <!-- 4: UseSSLAndNonSSL --> </dict> </plist> |
If you will also need SSL, put the following in /Library/Server/Web/Config/apache2/httpd_site2SSLwebapp.conf
. The config differs in that LAN traffic between the servers will be unencrypted by default (this config essentially tells Server not to check if there is a valid cert), but the WAN traffic will be encrypted. I believe you can install a self-signed certificate on the site2
server for encrypted local traffic, but this config will still enable the reverse proxy without having to have matching certificates. (I grant there is likely a more correct way to secure the local traffic, but this worked for me.)
1 2 3 4 5 6 7 8 9 10 |
SSLProxyEngine on SSLProxyVerify none SSLProxyCheckPeerCN off SSLProxyCheckPeerName off SSLProxyCheckPeerExpire off ProxyPreserveHost On ProxyPassReverse / http://192.168.1.15:80/ ProxyPass / http://192.168.1.15:80/ ServerName site2.example.com |
And the corresponding SSL web app plist, /Library/Server/Web/Config/apache2/webapps/com.example.site2SSLwebapp.plist
, much the same as above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!-- See man pages for webapp.plist(5) and webappctl(8) for information about this example webapp.plist --> <plist version="1.0"> <dict> <key>includeFiles</key> <array> <!-- Include files are activated in virtual host when webapp is started --> <string>/Library/Server/Web/Config/apache2/httpd_site2SSLwebapp.conf</string> </array> <key>name</key> <string>com.example.site2SSLwebapp</string> <key>displayName</key> <!-- Name shown in Server app --> <string>site2SSLWebApp</string> <key>installationIndicatorFilePath</key> <!-- The presence of this file indicates web app is installed --> <string>/Library/Server/Web/Config/apache2/httpd_site2SSLwebapp.conf</string> <key>sslPolicy</key><!-- Determines webapp SSL behavior --> <integer>0</integer> <!-- 0: default, UseSSLWhenEnabled --> <!-- 1: UseSSLAlways --> <!-- 2: UseSSLOnlyWhenCertificateIsTrustable --> <!-- 3: UseSSLNever --> <!-- 4: UseSSLAndNonSSL --> </dict> </plist> |
For each of these four files, the permissions need to be owner: root and group: wheel, 644:
1 2 3 |
$ sudo chown -R root:wheel /path/to/file $ sudo chmod -R 644 /path/to/file |
Setting up Server.app
Add the web app to Websites
- In the Websites tab of the Server.app interface, click the + below the Websites listing to add a new site
- Enter
site2.example.com
for Domain Name - Leave everything else at the default settings
- Click Edit Advanced Settings…
- Under the section “Make these web apps available on this website:” check Enable for site2WebApp
- Click OK
- Click Create
SSL
If you need SSL on the WAN, install a certificate in Server that covers the new domain. I used Let’s Encrypt to create a single certificate that was good for both my site1
and site2
domains. (Here's my Let’s Encrypt instructions for getting a cert while using Server.app.)
- In the Certificates tab of Server.app, click the + at the bottom of the window, then Import a Certificate Identity…
- Drag-and-drop the
.pem
files you got back from Let's Encrypt (or whatever certificate files you have), and click Import - In the Websites tab, create the new site almost the same as before, except change the Port to
443
and under SSL Certificate, pick the cert you just imported - Under Edit Advanced Settings…, instead check Enable for site2SSLWebApp
It just works!1
- Finally. ↩