mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-22 16:03:57 +01:00
Add nat-like method using nftables instead of iptables
This commit is contained in:
parent
d11f5b9d16
commit
1940b524f1
@ -1,3 +1,4 @@
|
|||||||
|
import re
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import subprocess as ssubprocess
|
import subprocess as ssubprocess
|
||||||
@ -49,6 +50,39 @@ def ipt(family, table, *args):
|
|||||||
raise Fatal('%r returned %d' % (argv, rv))
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
|
def nft(family, table, action, *args):
|
||||||
|
if family == socket.AF_INET:
|
||||||
|
argv = ['nft', action, 'ip', table] + list(args)
|
||||||
|
elif family == socket.AF_INET6:
|
||||||
|
argv = ['nft', action, 'ip6', table] + list(args)
|
||||||
|
else:
|
||||||
|
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
||||||
|
debug1('>> %s\n' % ' '.join(argv))
|
||||||
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
|
rv = ssubprocess.call(argv, env=env)
|
||||||
|
if rv:
|
||||||
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
|
def nft_get_handle(expression, chain):
|
||||||
|
cmd = 'nft'
|
||||||
|
argv = [cmd, 'list', expression, '-a']
|
||||||
|
env = {
|
||||||
|
'PATH': os.environ['PATH'],
|
||||||
|
'LC_ALL': "C",
|
||||||
|
}
|
||||||
|
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, env=env)
|
||||||
|
for line in p.stdout:
|
||||||
|
if (b'jump %s' % chain.encode('utf-8')) in line:
|
||||||
|
return re.sub('.*# ', '', line.decode('utf-8'))
|
||||||
|
rv = p.wait()
|
||||||
|
if rv:
|
||||||
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
_no_ttl_module = False
|
_no_ttl_module = False
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,12 +102,14 @@ def get_method(method_name):
|
|||||||
def get_auto_method():
|
def get_auto_method():
|
||||||
if _program_exists('iptables'):
|
if _program_exists('iptables'):
|
||||||
method_name = "nat"
|
method_name = "nat"
|
||||||
|
elif _program_exists('nft'):
|
||||||
|
method_name = "nft"
|
||||||
elif _program_exists('pfctl'):
|
elif _program_exists('pfctl'):
|
||||||
method_name = "pf"
|
method_name = "pf"
|
||||||
elif _program_exists('ipfw'):
|
elif _program_exists('ipfw'):
|
||||||
method_name = "ipfw"
|
method_name = "ipfw"
|
||||||
else:
|
else:
|
||||||
raise Fatal(
|
raise Fatal(
|
||||||
"can't find either iptables or pfctl; check your PATH")
|
"can't find either iptables, nft or pfctl; check your PATH")
|
||||||
|
|
||||||
return get_method(method_name)
|
return get_method(method_name)
|
||||||
|
80
sshuttle/methods/nft.py
Normal file
80
sshuttle/methods/nft.py
Normal file
@ -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)
|
@ -160,7 +160,7 @@ parser.add_argument(
|
|||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--method",
|
"--method",
|
||||||
choices=["auto", "nat", "tproxy", "pf", "ipfw"],
|
choices=["auto", "nat", "nft", "tproxy", "pf", "ipfw"],
|
||||||
metavar="TYPE",
|
metavar="TYPE",
|
||||||
default="auto",
|
default="auto",
|
||||||
help="""
|
help="""
|
||||||
|
Loading…
Reference in New Issue
Block a user