diff options
author | Scott Kuhl <kuhl@mtu.edu> | 2020-11-03 20:14:56 -0500 |
---|---|---|
committer | Scott Kuhl <kuhl@mtu.edu> | 2020-11-03 20:14:56 -0500 |
commit | d3700f09da7742f581b2e8ed6e526b67f0535646 (patch) | |
tree | 0a3ea5f34bac31949932afdefa803f6dceca092b /sshuttle/methods | |
parent | b5aaeda2a8bcf640e2c4307fc5cf37eb27b621f6 (diff) |
Improve nft IPv6 support.
This commit makes two fixes:
1. If an IPv6 DNS server is used, an nft rule had "ip6 protocol" in it
which is invalid and caused sshuttle to exit.
2. I modified detection of udp vs tcp to follow the recommendation at
https://superuser.com/questions/1560376/match-ipv6-protocol-using-nftables
I also re-arranged the code slightly to reduce the number of
if-statements.
Diffstat (limited to 'sshuttle/methods')
-rw-r--r-- | sshuttle/methods/nft.py | 62 |
1 files changed, 32 insertions, 30 deletions
diff --git a/sshuttle/methods/nft.py b/sshuttle/methods/nft.py index 058baa9..1b127ef 100644 --- a/sshuttle/methods/nft.py +++ b/sshuttle/methods/nft.py @@ -37,6 +37,13 @@ class Method(BaseMethod): _nft('add rule', 'output jump %s' % chain) _nft('add rule', 'prerouting jump %s' % chain) + # setup_firewall() gets called separately for ipv4 and ipv6. Make sure + # we only handle the version that we expect to. + if family == socket.AF_INET: + _nft('add rule', chain, 'meta', 'nfproto', '!=', 'ipv4', 'return') + else: + _nft('add rule', chain, 'meta', 'nfproto', '!=', 'ipv6', 'return') + # This TTL hack allows the client and server to run on the # same host. The connections the sshuttle server makes will # have TTL set to 63. @@ -45,17 +52,20 @@ class Method(BaseMethod): elif family == socket.AF_INET6: _nft('add rule', chain, 'ip6 hoplimit == 63 return') + # Strings to use below to simplify our code + if family == socket.AF_INET: + ip_version_l = 'ipv4' + ip_version = 'ip' + elif family == socket.AF_INET6: + ip_version_l = 'ipv6' + ip_version = 'ip6' + # Redirect DNS traffic as requested. This includes routing traffic # to localhost DNS servers through sshuttle. 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 }', - ('redirect to :' + str(dnsport))) - elif family == socket.AF_INET6: - _nft('add rule', chain, 'ip6 protocol udp ip6 daddr %s' % ip, - 'udp dport { 53 }', - ('redirect to :' + str(dnsport))) + _nft('add rule', chain, ip_version, + 'daddr %s' % ip, 'udp dport 53', + ('redirect to :' + str(dnsport))) # Don't route any remaining local traffic through sshuttle _nft('add rule', chain, 'fib daddr type local return') @@ -63,34 +73,26 @@ class Method(BaseMethod): # create new subnet entries. for _, swidth, sexclude, snet, fport, lport \ in sorted(subnets, key=subnet_weight, reverse=True): - if family == socket.AF_INET: - tcp_ports = ('ip', 'protocol', 'tcp') - elif family == socket.AF_INET6: - tcp_ports = ('ip6', 'nexthdr', 'tcp') + # match using nfproto as described at + # https://superuser.com/questions/1560376/match-ipv6-protocol-using-nftables if fport and fport != lport: - tcp_ports = \ - tcp_ports + \ - ('tcp', 'dport', '{ %d-%d }' % (fport, lport)) + tcp_ports = ('meta', 'nfproto', ip_version_l, 'tcp', + 'dport', '{ %d-%d }' % (fport, lport)) elif fport and fport == lport: - tcp_ports = tcp_ports + ('tcp', 'dport', '%d' % (fport)) + tcp_ports = ('meta', 'nfproto', ip_version_l, 'tcp', + 'dport', '%d' % (fport)) + else: + tcp_ports = ('meta', 'nfproto', ip_version_l, + 'meta', 'l4proto', 'tcp') if sexclude: - if family == socket.AF_INET: - _nft('add rule', chain, *(tcp_ports + ( - 'ip daddr %s/%s' % (snet, swidth), 'return'))) - elif family == socket.AF_INET6: - _nft('add rule', chain, *(tcp_ports + ( - 'ip6 daddr %s/%s' % (snet, swidth), 'return'))) + _nft('add rule', chain, *(tcp_ports + ( + ip_version, 'daddr %s/%s' % (snet, swidth), 'return'))) else: - if family == socket.AF_INET: - _nft('add rule', chain, *(tcp_ports + ( - 'ip daddr %s/%s' % (snet, swidth), - ('redirect to :' + str(port))))) - elif family == socket.AF_INET6: - _nft('add rule', chain, *(tcp_ports + ( - 'ip6 daddr %s/%s' % (snet, swidth), - ('redirect to :' + str(port))))) + _nft('add rule', chain, *(tcp_ports + ( + ip_version, 'daddr %s/%s' % (snet, swidth), + ('redirect to :' + str(port))))) def restore_firewall(self, port, family, udp, user): if udp: |