Port Forwarding on Mac Pro with macOS Sierra

I would like to port forward from my macOS Sierra computer with IP address of 152.1.2.3 to my NAS which is 192.168.2.3. Specifically I would like to access the HTTP web server on my NAS at 192.168.2.3/Photos. The NAS has all traffic that comes in over HTTP directed to the correct folder (Photos).

I have Internet Sharing turned on and share my Ethernet connection of my macOS computer (152.1.2.3 on Ethernet 2) to my NAS (192.168.2.3 on Ethernet 1)

My NAS can see the internet and get updates.

I can access the webserver locally from my computer by entering 192.168.2.3 and it directs me to the webserver page at 192.168.2.3/Photos.

I just cannot access it from outside the local network.

The easy solution is to connect the NAS directly to the network but I cannot because of local policies.

In Summary: I would like to type in the following and be redirected to the web server on the NAS --> 152.1.2.3:9999

would the terminal command be:

rdr pass on en2 inet proto tcp from any to any port 9999 -> 198.168.2.3 port 80

Solution 1:

Internet Sharing in macOS is (internally) done by creating a bridge device containing two or more interfaces, activating a DHCP server, setting up various pf rules and enabling pf.

To print all rules the following shell script has to be executed:

pfdump.sh:

#!/bin/bash

function pfprint() {
  if [ -n "$1" ];then
    sudo pfctl -a "$2" -s"$1" 2>/dev/null
  else
    sudo pfctl -s"$1" 2>/dev/null
  fi
}

function print_all() {

  local p=$(printf "%-40s" $1)
  (
    pfprint r "$1" | sed "s,^,r     ,"
    pfprint n "$1" | sed "s,^,n     ,"
    pfprint A "$1" | sed "s,^,A     ,"
  ) | sed "s,^,$p,"

  for a in `pfprint A "$1"`; do
    print_all "$a"
  done
}

print_all

By default pf is disabled with the following pf dump:

                                        r     scrub-anchor "com.apple/*" all fragment reassemble
                                        r     anchor "com.apple/*" all
                                        n     nat-anchor "com.apple/*" all
                                        n     rdr-anchor "com.apple/*" all
                                        A       com.apple
com.apple                               r     anchor "200.AirDrop/*" all
com.apple                               r     anchor "250.ApplicationFirewall/*" all
com.apple                               A       com.apple/200.AirDrop
com.apple                               A       com.apple/250.ApplicationFirewall

After activating Internet Sharing pf is enabled and a dump looks like this:

                                        r     scrub-anchor "com.apple/*" all fragment reassemble
                                        r     scrub-anchor "com.apple.internet-sharing" all fragment reassemble
                                        r     anchor "com.apple/*" all
                                        r     anchor "com.apple.internet-sharing" all
                                        n     nat-anchor "com.apple/*" all
                                        n     nat-anchor "com.apple.internet-sharing" all
                                        n     rdr-anchor "com.apple/*" all
                                        n     rdr-anchor "com.apple.internet-sharing" all
                                        A       com.apple
                                        A       com.apple.internet-sharing
com.apple                               r     anchor "200.AirDrop/*" all
com.apple                               r     anchor "250.ApplicationFirewall/*" all
com.apple                               A       com.apple/200.AirDrop
com.apple                               A       com.apple/250.ApplicationFirewall
com.apple.internet-sharing              r     scrub-anchor "base_v4" all fragment reassemble
com.apple.internet-sharing              r     anchor "base_v4" all
com.apple.internet-sharing              n     nat-anchor "base_v4" all
com.apple.internet-sharing              n     rdr-anchor "base_v4" all
com.apple.internet-sharing              A       com.apple.internet-sharing/base_v4
com.apple.internet-sharing/base_v4      r     scrub on en1 all no-df fragment reassemble
com.apple.internet-sharing/base_v4      r     scrub on bridge100 all no-df max-mss 1460 fragment reassemble
com.apple.internet-sharing/base_v4      r     scrub on bridge100 proto esp all no-df fragment reassemble
com.apple.internet-sharing/base_v4      r     pass on en1 all flags any keep state
com.apple.internet-sharing/base_v4      r     pass on en1 proto esp all no state
com.apple.internet-sharing/base_v4      r     pass on bridge100 all flags any keep state rtable 6
com.apple.internet-sharing/base_v4      n     nat on en1 inet from 192.168.2.0/24 to any -> (en1:0) extfilter ei
com.apple.internet-sharing/base_v4      n     no nat on bridge100 inet from 192.168.2.1 to 192.168.2.0/24
com.apple.internet-sharing/base_v4      n     rdr on bridge100 inet proto tcp from 192.168.2.0/24 to any port = 21 -> 127.0.0.1 port 8021

Here en1 is the external interface (the one sharing to your internal network) and 192.168.2.0/24 the internal network provided by the DHCP server. Both could differ in your environment.

To expose an internal server to the public with Internet Sharing enabled, you have to forward a port of the external interface to the IP and the service port of the server:

Create rdr rule on the router (your Mac Pro) by entering in Terminal.app:

sudo nano /private/etc/pf-rdr.rule

and the content:

rdr on en1 inet proto tcp from any to 152.1.2.3 port = 9999 -> 192.168.2.3 port 80

assuming en1 is the external interface, 152.1.2.3:9999 its IP:port and 192.168.2.3:80 the IP:port of the internal server. To get the device identifier (and IP) of your external interface enter ifconfig on your Mac Pro.

Then create a launch daemon:

sudo nano /Library/LaunchDaemons/org.user.pfrdr.plist

with the content:

<?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">
<plist version="1.0">
<dict>
    <key>Disabled</key>
    <false/>
    <key>Label</key>
    <string>org.user.pfrdr</string>
    <key>ProgramArguments</key>
    <array>
        <string>/sbin/pfctl</string>
        <string>-a</string>
        <string>com.apple/portforwarding</string>
        <string>-f</string>
        <string>/private/etc/pf-rdr.rule</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/Library/Logs/org.usr.pfrdr.log</string>
    <key>StandardOutPath</key>
    <string>/Library/Logs/org.usr.pfrdr.log</string>
</dict>
</plist>

Launch the daemon with:

sudo launchctl load /Library/LaunchDaemons/org.user.pfrdr.plist

This will add a second rdr rule in the com.apple anchor to pf and forward the external port to the internal host.

Since DHCP assigns IP addresses dynamically it's also useful to define a fixed IP for the internal server:

Create a file bootptab on the router (your Mac Pro):

sudo nano /etc/bootptab

with the content:

# Bootptab file
# Section 1 -- ignored
%%
# Section 2 -- used
# Hardware types: 1=Ethernet, 6=Wireless
#                 See http://www.ietf.org/rfc/rfc1700.txt
#
# machine entries have the following format:
#
# hostname         hwtype     hwaddr         ipaddr   bootfile
your-nas-ethernet     1 c4:2c:03:3a:7f:65 192.168.2.3 boot
a-wireless-machine    6 60:33:4b:27:6c:62 192.168.2.4 boot
another-ethernet      1 00:0d:93:72:e7:96 192.168.2.2 boot

Replace the MAC addresses (hwaddr) and the hardware types (hwtype) by the proper ones found in your environment here. You can get the MAC addresses by entering ifconfig on the respective host. Reboot your router (Mac Pro) afterwards.


Security implications:

By using your Mac Pro as a router it is exposed to the Internet and vulnerable as such. Pf as it runs after enabling Internet Sharing has absolutely no blocking rule. You have to add a whole bunch of additional rules to make it more secure (and working).


The IP you have published belongs to 152.1.0.0/16 - an IP block assigned to the North Carolina State University (NCSU). Your university may have applied security measures in this network which will prevent access from other networks (or the "whole" Internet).