diff options
Diffstat (limited to 'sshuttle/methods/nft.py')
-rw-r--r-- | sshuttle/methods/nft.py | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/sshuttle/methods/nft.py b/sshuttle/methods/nft.py new file mode 100644 index 0000000..cd28a5b --- /dev/null +++ b/sshuttle/methods/nft.py @@ -0,0 +1,80 @@ +import socket +from sshuttle.firewall import subnet_weight +from sshuttle.linux import nft, nft_get_handle, nonfatal +from sshuttle.methods import BaseMethod + + +class Method(BaseMethod): + + # We name the chain based on the transproxy port number so that it's + # possible to run multiple copies of sshuttle at the same time. Of course, + # the multiple copies shouldn't have overlapping subnets, or only the most- + # recently-started one will win (because we use "-I OUTPUT 1" instead of + # "-A OUTPUT"). + def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, + user): + if udp: + raise Exception("UDP not supported by nft") + + table = "nat" + + def _nft(action, *args): + return nft(family, table, action, *args) + + chain = 'sshuttle-%s' % port + + # basic cleanup/setup of chains + _nft('add table', '') + _nft('add chain', 'prerouting', + '{ type nat hook prerouting priority -100; policy accept; }') + _nft('add chain', 'postrouting', + '{ type nat hook postrouting priority 100; policy accept; }') + _nft('add chain', 'output', + '{ type nat hook output priority -100; policy accept; }') + _nft('add chain', chain) + _nft('flush chain', chain) + _nft('add rule', 'output jump %s' % chain) + _nft('add rule', 'prerouting jump %s' % chain) + + # create new subnet entries. + for _, swidth, sexclude, snet, fport, lport \ + in sorted(subnets, key=subnet_weight, reverse=True): + tcp_ports = ('ip', 'protocol', 'tcp') + if fport: + tcp_ports = tcp_ports + ('dport { %d-%d }' % (fport, lport)) + + if sexclude: + _nft('add rule', chain, *(tcp_ports + ( + 'ip daddr %s/%s' % (snet, swidth), 'return'))) + else: + _nft('add rule', chain, *(tcp_ports + ( + 'ip daddr %s/%s' % (snet, swidth), 'ip ttl != 42', + ('redirect to :' + str(port))))) + + for _, ip in [i for i in nslist if i[0] == family]: + if family == socket.AF_INET: + _nft('add rule', chain, 'ip protocol udp ip daddr %s' % ip, + 'udp dport { 53 }', 'ip ttl != 42', + ('redirect to :' + str(dnsport))) + elif family == socket.AF_INET6: + _nft('add rule', chain, 'ip6 protocol udp ip6 daddr %s' % ip, + 'udp dport { 53 }', 'ip ttl != 42', + ('redirect to :' + str(dnsport))) + + def restore_firewall(self, port, family, udp, user): + if udp: + raise Exception("UDP not supported by nft method_name") + + table = "nat" + + def _nft(action, *args): + return nft(family, table, action, *args) + + chain = 'sshuttle-%s' % port + + # basic cleanup/setup of chains + handle = nft_get_handle('chain ip nat output', chain) + nonfatal(_nft, 'delete rule', 'output', handle) + handle = nft_get_handle('chain ip nat prerouting', chain) + nonfatal(_nft, 'delete rule', 'prerouting', handle) + nonfatal(_nft, 'delete chain', chain) |