74

I want to forward requests from 192.168.99.100:80 to 127.0.0.1:8000. This is how I'd do it in linux using iptables:

iptables -t nat -A OUTPUT -p tcp --dport 80 -d 192.168.99.100 -j DNAT --to-destination 127.0.0.1:8000

How do I do the same thing in MacOS X? I tried out a combination of ipfw commands without much success:

ipfw add fwd 127.0.0.1,8000 tcp from any to 192.168.99.100 80

(Success for me is pointing a browser at http://192.168.99.100 and getting a response back from a development server that I have running on localhost:8000)

4
  • Are we talking OSX Client or Server?
    – Scott Pack
    Jan 13, 2010 at 21:15
  • 1
    It's snow leopard.
    – nafe
    Jan 13, 2010 at 21:57
  • Is there some reason you can't just configure your web server to also listen on 192.168.99.100:80?
    – Zoredache
    Jan 13, 2010 at 23:28
  • 1
    @Zoredache Listening on port 80 needs sudo and some webservers don't drop privileges fast enough to safely run them with sudo.
    – Navin
    Dec 21, 2015 at 10:33

7 Answers 7

44

So I found out a way to do this. I'm not sure if it's the preferred way but it works! At your favourite shell:

sudo ifconfig lo0 10.0.0.1 alias
sudo ipfw add fwd 127.0.0.1,9090 tcp from me to 10.0.0.1 dst-port 80

(The alias to lo0 seems to be the missing part)

If you'd like a (fake) domain to point to this new alias then make sure /etc/hosts contains the line:

10.0.0.1 www.your-domain.com
1
  • 9
    ipfw won't work on Yosemite (MAC) and seems kinda deprecated Dec 14, 2016 at 10:20
38

I was able to get this working using the ifconfig and pfctl commands on Mac 10.10.2. With the following approach I'm successfully mapping 127.0.0.1:3000 to mydomain.com locally on my machine.

In your command line enter the following two commands to forward connections to 127.0.0.1:3000 to 10.0.0.1:

sudo ifconfig lo0 10.0.0.1 alias
echo "rdr pass on lo0 inet proto tcp from any to 10.0.0.1 port 80 -> 127.0.0.1 port 3000" | sudo pfctl -ef -

Then edit your /etc/hosts or /private/etc/hosts file and add the following line to map your domain to 10.0.0.1.

10.0.0.1 mydomain.com

After you save your hosts file flush your local DNS:

sudo discoveryutil udnsflushcaches

Now open mydomain.com in a browser and you'll be seeing the server hosted on your localhost port (i.e. 127.0.0.1:3000). Basically this process maps an <ip>:<port> to a new <ip> so that you can then map a host that IP.

7
  • 1
    Just a tad hacky, but gets the job done. Good show. Apr 24, 2015 at 13:57
  • 6
    Thank you. How would you undo the alias and the forwarding? Sep 7, 2015 at 19:22
  • 2
    Hi sudo discoveryutil udnsflushcaches gives me command not found but it still matches the entry correctly. But how can I map a second port? I tried to do steps 1+2 with id: 10.0.0.2 and another port, but it always takes only the last connection. So if I do last 10.0.0.1 & port 3000 it forwards 3000, if I do last 10.0.0.2 and port 4000 it forwards 4000. In my /private/etc/hosts I wrote both entries with the (according) different id.
    – Andi Giga
    Feb 3, 2016 at 10:18
  • 2
    Works, but I need to it again when I restart OSX.... Any thing to save this permanently?
    – Kasper
    Oct 5, 2017 at 10:11
  • 4
    Please update with sudo dscacheutil -flushcache
    – northtree
    Aug 16, 2018 at 5:15
9

I too had to do a similar thing recently, and in searching came upon this answer. Unfortunately, the answer of Nafe uses ipfw which is now deprecated and unavailable in OSX; and the answer of Kevin Leary is indeed a bit hackish. So I had to make something better (cleaner) and decided to share it here for posterity. This answer is largely based on the approach mentioned at this gist.

As OP mentions, pointing a browser at 192.168.99.100 should get a response from a server at localhost:8000. Adding an alias to ifconfig isn't really necessary, pfctl alone is sufficient: to achieve this the pf.conf file at /etc/pf.conf needs to be modified.

First we create (with sudo) a new anchor file (let's call it redirection) at: /etc/pf.anchors/redirection. This is basically a regular text file and contains the following line (just like in the answer of Kevin Leary): rdr pass on lo0 inet proto tcp from any to 192.168.99.100 port = 80 -> 127.0.0.1 port 8000. Once the new anchor file has been created, it needs to be referenced within the pf.conf file. Open the pf.conf file with sudo and add rdr-anchor "redirection" after the last rdr-anchor line (which is rdr-anchor "com.apple/*") and add load anchor "redirection" from "/etc/pf.anchors/redirection" at the end.

Ultimately, this is what the pf.conf file should look like:

scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
rdr-anchor "redirection"  #added for redirection/port forwarding
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
load anchor "pow" from "/etc/pf.anchors/redirection"  #added for redirection/port forwarding

And that's almost it. Just restart pfctl by issuing sudo pfctl -d to disable it first and then sudo pfctl -fe /etc/pf.conf to start it again.

Now, if you need this happen automatically after every restart, another tiny bit of work needs to be done: the launch daemon for pfctl needs to be updated (the referenced gist mentions that pf is enabled automatically on boot, however this does not seem to be the case from looking at the code). Open (with sudo) System/Library/LaunchDaemons/com.apple.pfctl.plist and look for this:

<array>
          <string>pfctl</string>
          <string>-f</string>
          <string>/etc/pf.conf</string>
</array>

and add the line <string>-e</string> to ultimately make it like this:

<array>
         <string>pfctl</string>
         <string>-e</string>
         <string>-f</string>
         <string>/etc/pf.conf</string>
</array>

That should do it.

Caveat: Apple no longer allows to change the launch demon files just like that (not with sudo, nor chmod, nor anything else). The only way is to tinker with the System Integrity Protection settings: boot into recovery mode and launch terminal. Check the SIP status with csrutil status, it should generally be enabled. Disable it with csrutil disable and reboot in normal mode and then do the changes to the plist file as discussed above. Once done, go back to recovery mode and re-enable the protection (it's in place for good reason) by issuing csrutil enable.

Explanation: One can check by issuing the ifconfig command that 127.0.0.1 is already the (default) alias for localhost lo0 - this fact is being used to avoid having to add an extra alias for localhost and to simply use the default address in the pf.conf file.

UPDATE: Unfortunately, it seems as though loading the file at startup does not work. I am still trying to get help to have it sorted. Until then, running sudo pfctl -f /etc/pf.conf after starting up does the trick.

3
  • 1
    Thanks for this, this worked for me in OS X Sierra. It is very important though that the redirection file ends with a newline.
    – kadrian
    Oct 28, 2016 at 8:40
  • 1
    It doesn't seem safe to overwrite system files like System/Library/LaunchDaemons/com.apple.pfctl.plist; this probably wouldn't survive an OS update?
    – Tom
    Jun 12, 2019 at 18:12
  • Yes, you're right, this sort of thing could easily get overwritten. Best to keep a back up of both the original (just in case) and the new one. "Safety" is relative, one would assume people on serverfault generally know what they're doing. But yeah, Apple added the SIP for good reason.
    – ahron
    Jan 23, 2020 at 14:15
4

This worked well for me:

1

From 10.5 on, OS X comes with a new, application-oriented firewall instead of ipfw. But ipfw is still installed. If you have trouble with its syntax, check out graphical frontends like WaterRoof or Flying Buttress.

HTH, PEra

1
  • What is the name of this "new application-oriented firewall"?
    – Mohsenasm
    May 27, 2023 at 9:44
1

order of rules is important, make sure there is no "deny all" before your allow rules, or something like that.

1
  • Thanks for the advice but I don't think I have that particular problem (ipfw list only shows my rule and 65535 allow ip from any to any).
    – nafe
    Jan 13, 2010 at 22:13
1

Your command seems to be missing a rule number; try:

ipfw add 100 fwd 127.0.0.1,8000 tcp from any to 192.168.99.100 80

(if you aren't running as root, you'll have to prefix it with sudo). Another thing to check is that the firewall is enabled:

sysctl net.inet.ip.fw.enable

If it comes back with the value 0 (off), turn it on with:

sysctl -w sysctl net.inet.ip.fw.enable=1

... and then arrange for it to get reenabled when the computer reboots. The "proper" way to do this is probably to create a launchd item (Lingon makes this fairly easy). Or just use one of the GUI tools PEra mentioned, and let it take care of the details.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .