Nat – OS X 10.8: redirecting locally initiated ssh connections to localhost:22

appleipfwnat;pfrouting

I need to hack the OS X pf to redirect all ssh connections from an user to this machine. I want, when doing

$ ssh google.com

to get the same results as with

$ ssh localhost

ie a connection to my locally running sshd.

Under a recent Linux, this would simply be:

# iptables -t nat -A OUTPUT -p tcp --dport 22 -m owner --uid-owner theuser -j REDIRECT

Under OS X 10.8, there appear to be 2 methods – ipfw and pf. Neither works.
Ipfw:

# ipfw flush
# ipfw add 50 fwd 127.0.0.1,22 tcp from any to any 22 uid theuser

If I drop the uid theuser part, the redirect works, minus the user thingie. If I leave the uid directive there, the network stack dies and the system shortly becomes unusable; no more ipfw, no more ps, no more kill.

According to the man pages, ipfw is deprecated, so packet filter should be used instead:

# sysctl -w net.inet.ip.forwarding=1

Then I added

anchor "910.Custom/*"
load anchor "910.Custom" from "/etc/pf.anchors/910.Custom"

in /etc/pf.anchors/com.apple and

rdr on en1 proto TCP from any to any port 22 -> 127.0.0.1 port 22

in /etc/pf.anchors/910.Custom (notice how I'm not mentioning anything about a user here, since the pf docs don't list such an option for rdr rules).

After I run # pfctl -ef /etc/pf.anchors/com.apple nothing happens. If I add garbage to /etc/pf.anchors/910.Custom or even if I dare add user theuser after the rdr rule, the firewall fitfully complains of the bad syntax.

Can the OS X kernel even perform NAT routing anymore, or did Apple yank out that functionality? If it can, am I missing anything?

LE. fixed iptables syntax

Best Answer

You can do it with PF as well. However, rdr only accepts incoming packets. Thus, you have to first route those packets to lo0, then add a rdr-rule there (which will catch them as they will be routed in from "somewhere") to send them to your local SSH-server.

The order is necessarily: rdr stuff, then filter stuff (like pass), but chronologically the 2nd rule will hit first (on $Out), which will then activate the first rule (on lo0).

# Output interface
Out = en0

# A macro to shorten rules below
Packets = "proto tcp from" $Out "to any port 22"

# Rule 1: Redirect those connections _after_ they were routed to lo0 below
rdr pass log on lo0 $Packets -> 127.0.0.1

# Rule 2: Route _first_ new IPv4 TCP connections leaving $Out to lo0
pass out on $Out route-to lo0 inet $Packets
Related Topic