Tunnel all traffic out on port with pfctl

So, I've tried google, I've tried FreeBSD forums, I can't find anyway to do this with pf or ipfw. I know its possible with pf and its easy with Linux. I know that the pf rule in /etc/pfctl.conf would need to be something like the following to tunnel out all traffic on a specific port:

pass out on proto tcp from any to port 9050  

Obviously this doesn't work and I'm missing something:

$ doas pfctl -nf /etc/pf.conf
/etc/pf.conf:15: syntax error 

Would anyone know how to do this?

Hi @Azrael,

the interface name like any or igb0 is missing after the on keyword. Or remove on.

I removed the on keyword like this:

pass out proto tcp from any to port 9050

That is syntactically correct, but doesn't tunnel all tcp traffic on port 9050.

I think I'm still missing more, no?

Thanks @bendingrodriguez

What do you mean by tunnel all traffic? That rule simply allows outbound resp. outgoing traffic to that port on all ifaces. Is 9050 bound to a VPN application like openvpn? If so, in which direction connections are initiated?

Tunnel all traffic - To force all outbound traffic over a particular port like 9050 for tor.

No, this does not have to do with a VPN.

@Azrael you don't mention basics - os / versions etc ... supply details - paint a picture , don't assume we know your environment ....

  • show /etc/pf.conf file
  • have you stopped/restarted networking services ?

links below have details wrt pf configs ... might be of help

https://elatov.github.io/2011/05/freebsd-firewall-and-nat-with-pf/

https://robert-chalmers.uk/2018/10/03/protect-your-mac-with-pf-the-all-powerful-firewall/

....

well, Tor is another thing, @Azrael. It creates a socks proxy on port 9050. So all your applications have to be configured to use that proxy.
Maybe this could help a bit:

Solene'% : OpenBSD full Tor setup (OpenBSD, but should work as well)

This seems like a description of port forwarding not tunneling.

2 Likes

@munkeHoller

I'm using FreeBSD 13, which is the current. The pf rules should be the same for any version of BSD that's not super old. I looked at the links you provided, but none of them seemed to show information or an example of tunneling all traffic over a port. Just using one ip as a gateway for another.

Thanks

@Neo - You might be correct. I understood "Tunneling all traffic" to be the same a "Forwarding all traffic". Perhaps I am wrong. Would you know how to do this?

Thank you

1 Like

there's boatloads of details online ... below for starters

OpenBSD - Setup IP Forwarding - YouTube

without being cute ... do you actually know what it is you want to do , and why you need to do it ....

@munkeHoller Yes, I am trying to do what this code does for Linux, but in BSD:

Also, the port of sshuttle seem to be not working correctly in FreeBSD and I assume learning how this works would help me to fix that.

There are many examples on the net on how to set up IP Port Forwarding, but I did a quick check for Linux only.

Not sure if Google will yield any useful results for FreeBSD.

Sorry not to be more helpful. I'm busy writing and testing some Ruby code to perform an arbitrage check between buying THB with USD and then buying TRX-THB with Thai Baht on one crypto exchange, moving it to another exchange and then trading the TRX for BTC, and running all the checks with fees, and converting back to USD to see the differences, using APIs to both crypto exchanges.

Anyway, FreeBSD is not something I have used in the past two decades or more. Sorry about that.

2 Likes

Well, I haven't solved this yet, but I feel I'm at least getting closer. This rule seems to at least read in English-like what I'm trying to do. Obviously lo0 is localhost.

pass in on lo0 proto tcp from any to 127.0.0.1 port 9050 

There are no errors when I re-load pf, but it also doesn't seem to forward anything, or my curl requests through tor when I try 'curl -4 canhazip.com'. I just get my home ip or VPN. I don't know, I just thought posting this might help someone see what I'm missing as it wasn't clear before. Any thoughts welcome.

if you want to set up a transparent proxy, this might help: http://0xfeedface.org/2013/06/25/tor-transparent-proxy-on-freebsd.html. The difference to a normal proxy is that it works on the network layer, not on the application layer.

1 Like

@bendingrodriguez - Took me a bit to reply. I got busy with some python work. I like the author of that article. Shawn Webb is an awesome person who created HardenedBSD and did other good work. I tried that article twice. The second time I even tried it in a vm of HardenedBSD in case he designed it for that and copy/pasted everything. It did not work for forwarding all traffic. In the defense of the author, these instructions were dated as written in 2013. So its likely something may have changed with pf or the other configurations mentioned. I appreciate anything to forward this search. For now I'm still looking again.

Thanks

Hi @Azrael,

sorry for the late reply, I was busy too - and this topic is not that trivial.

First I have to say that I'm not a pf specialist, but mainly work with iptables/nftables i.e. netfilter. Their syntax, logic and semantic differ significantly. I played around with fwbuilder, trying to migrate iptables to pf rules, but that wasn't really productive, so I tested the rules on my own. In the end, the following rule set turned out to be working:

int_if = vtnet0
# local net(s)
non_tor = "{ 192.168.0.0/24 }"
trans_port = 9040
dns_port = 1053

scrub in

rdr pass on { lo1 $int_if } inet proto tcp to !($int_if) -> 127.0.0.1 port $trans_port
rdr pass on lo0 inet proto udp to port domain -> 127.0.0.2 port $dns_port

# don't lock me out
pass in quick inet proto tcp to self port ssh
pass out quick inet proto tcp to self port ssh keep state

block return out

pass quick on { lo0 lo1 } keep state
pass out quick inet proto tcp user _tor flags S/SA modulate state
pass out quick route-to lo1 inet proto udp to port $dns_port keep state
pass out quick inet to $non_tor keep state
pass out route-to lo1 inet proto tcp all flags S/SA modulate state

I haven't tested if that set is really minimal. And I'm much too lazy to exlpain it in detail :slight_smile: Note that the set is adapted for a single client working as a tor host, not for a router. In that case you would have to configure additional rules resp. change some rules.

The most important rules relate to NAT (rdr pass) and PBR (route-to), which in effect do the redirection of the traffic. Installing a separate dns server (I used bind916) isn't really necessary, I just took over that (as the other configs and rules) from Shawn's excellent guide. But there is an inaccuracy, probably a typo:
rdr pass on lo0 inet proto udp to port domain -> 127.0.0.1 port 1053
This has to be 127.0.0.2, see the rule set above.

There are of course some system modifications needed:

# rc.conf
sshd_enable="YES"
pf_enable="YES"
named_enable="YES"
tor_enable="YES"
cloned_interfaces="lo1"
ifconfig_lo1="127.0.0.2"

# torrc
VirtualAddrNetwork 10.192.0.0/10
AutomapHostsOnResolve 1
TransPort 9040
DNSPort 127.0.0.2:1053

$ echo "supersede domain-name-servers 127.0.0.1;" > /etc/dhclient.conf

# configuring google's dns server as a forwarder isn't needed

After reboot, check if all is running properly:

$ sockstat -4l | egrep 'tor|bind'
# 9050: socks listener, 9040: transparent pf listener
_tor     tor        1091  5  tcp4   127.0.0.1:9050        *:*
_tor     tor        1091  6  udp4   127.0.0.2:1053        *:*
_tor     tor        1091  7  tcp4   127.0.0.1:9040        *:*
bind     named      815   26 udp4   127.0.0.1:53          *:*

Check if it's working as desired:

  • $ host juhanurmihxlp77nkq76byazcldy2hlmovfu2epvl5ankdibsot4csyd.onion
    $ host juhanurmihxlp77nkq76byazcldy2hlmovfu2epvl5ankdibsot4csyd.onion 8.8.8.8
    (compare the outputs on the tor host and on a non-tor host)
  • If you have an external server under your control:
    On the server: $ tcpdump port 42042
    On the tor host: $ echo | telnet server 42042 (or use netcat)
    Then on the server you should see a line like this:
    09:50:22.309110 IP tor-exit-62.for-privacy.net.21814 > server.fq.dn.42042: Flags [S] ...
    (if port 42042 is blocked somewhere, try 443 instead)

For dumping traffic resp. checking for proper redirections, tcpdump is an essential (magic) tool. As an admin, you can't miss it.

1 Like

@bendingrodriguez - I'm excited you found something that works. I might need a little more help to get this going. I didn't have bind set up. I tried using this link, but I did not have a remote server to make a master/slave with. For /usr/local/etc/namedb/named.conf I just used this:

include "/usr/local/etc/namedb/rndc.key";

controls {
inet 127.0.0.1 allow { localhost; } keys { "rndc-key"; };
};

I restarted named and tried 127.0.0.1, 127.0.0.1:53, 127.0.0.2 and 127.0.0.1:1053 in /etc/resolv.conf. Each time I just got an error that the domain was unable to be resolved:

# doas -u user curl -IL google.com
curl: (6) Could not resolve host: google.com

Everything is set up in /etc/rc.conf, /etc/pf.conf and /usr/local/tor/torrc as you specified. I haven't set up or ran bind in about 8 years, so I'm sure I could be missing something there. Also, I do not have a device called 'vtnet0'. Just em0, lo0 and lo1. I tried using these in place of vtnet0 and reloading pf.conf, but I still ran into the same DNS problem. Would you or anyone know what I'm missing?

Thanks again for helping me with this.

1 Like

Hi @Azrael,

did you run rndc-confgen -a as specified when bind was installed:

BIND requires configuration of rndc, including a "secret"
key.  The easiest, and most secure way to configure rndc is
to run 'rndc-confgen -a' to generate the proper conf file,
with a new random key, and appropriate file permissions.

You don't need to change /usr/local/etc/namedb/named.conf, the default settings are working out of the box. You can restore them via

$ cp /usr/local/etc/namedb/named.conf{.sample,}

Don't forget to restart named.

/etc/resolv.conf just needs to contain one line:

nameserver 127.0.0.1

Since I was testing on a VM, my internal iface is vtnet0, yours is em0. And of course you have to adapt non_tor.

Here's another simple test for dns:

$ host x.org 
x.org has address 131.252.210.176
Host x.org not found: 3(NXDOMAIN)
# ^^ ignore NXDOMAIN
$ host x.org 8.8.8.8
;; connection timed out; no servers could be reached
# ^^ correct, due to the `block return out` rule

Hey Guys and @Azrael

Sorry, I've been busy and not paying close attention to this topic.

However, let me give you by thoughts.

This is an easy task. Basically, if I were doing this, I would just write a simple python script which does what you want and not try to use tools written by others which are not easy to configure or may not do as you desire.

@Azrael has good Python skills as I recall. It would only take about a hour (at the most) to write some Python code which listens, receives and retransmits, on both side of the TCP send/receive ends.

It seems to me, looking at this topic, that what you @Azrael have spent days and weeks on could have been done just starting from scratch and writing a page or so of Python networking code, and saving a lot of time and frustration in the process.

That's my 0.2 cents.

HTH

Kindly Note: I have written a lot of networking code in my days, including some Python code to send and receive packets from an IOT device I was tinkering around with a few years ago. It took very little time, as I recall, to write this Python code from scratch, less than a hour (as I recall) including testing.

Often, as has been my experience, it's easier to write code like this than to spend weeks trying to find "other people's code" or "utilities" or "commands" to accomplish basic networking (and other) tasks. The Python networking libs are quite mature and robust.

2 Likes