summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Kuhl <kuhl@mtu.edu>2021-05-30 21:09:32 -0400
committerScott Kuhl <kuhl@mtu.edu>2021-05-30 21:09:32 -0400
commit3f201095eabc34879ccc66cad47da73ccec3ce39 (patch)
tree53ebfe172e47844ab52af368ce1188daeeb6110b
parentc026a92cad92dea30fe1bbc4802e41a1fc0499ff (diff)
parent58c264ff1c28ca2f780c86ba79ba58cd21050617 (diff)
Merge branch 'master' into nat-ipv6
-rwxr-xr-xbin/sudoers-add14
-rw-r--r--docs/manpage.rst167
-rw-r--r--docs/tproxy.rst3
-rw-r--r--docs/usage.rst4
-rw-r--r--sshuttle/client.py16
-rw-r--r--sshuttle/cmdline.py9
-rw-r--r--sshuttle/firewall.py10
-rw-r--r--sshuttle/hostwatch.py103
-rw-r--r--sshuttle/methods/__init__.py2
-rw-r--r--sshuttle/methods/ipfw.py2
-rw-r--r--sshuttle/methods/nat.py2
-rw-r--r--sshuttle/methods/nft.py2
-rw-r--r--sshuttle/methods/pf.py2
-rw-r--r--sshuttle/methods/tproxy.py22
-rw-r--r--sshuttle/options.py9
-rw-r--r--tests/client/test_firewall.py6
-rw-r--r--tests/client/test_methods_nat.py6
-rw-r--r--tests/client/test_methods_pf.py18
-rw-r--r--tests/client/test_methods_tproxy.py34
19 files changed, 197 insertions, 234 deletions
diff --git a/bin/sudoers-add b/bin/sudoers-add
index 5bec3d1..e359d46 100755
--- a/bin/sudoers-add
+++ b/bin/sudoers-add
@@ -50,6 +50,14 @@ if [ "$FILE_NAME" == "" ]; then
exit 1
fi
+# Verify that the resulting file name begins with /etc/sudoers.d
+FILE_NAME="$(realpath "/etc/sudoers.d/$FILE_NAME")"
+if [[ "$FILE_NAME" != "/etc/sudoers.d/"* ]] ; then
+ echo -n "Invalid sudoers filename: Final sudoers file "
+ echo "location ($FILE_NAME) does not begin with /etc/sudoers.d"
+ exit 1
+fi
+
# Make a temp file to hold the sudoers config
umask 077
TEMP_FILE=$(mktemp)
@@ -62,9 +70,9 @@ visudo_code=$?
rm "$TEMP_FILE"
if [ $visudo_code -eq 0 ]; then
- echo "$CONTENT" > "/etc/sudoers.d/$FILE_NAME"
- chmod 0440 "/etc/sudoers.d/$FILE_NAME"
- echo "The sudoers file /etc/sudoers.d/$FILE_NAME has been successfully created!"
+ echo "$CONTENT" > "$FILE_NAME"
+ chmod 0440 "$FILE_NAME"
+ echo "The sudoers file $FILE_NAME has been successfully created!"
exit 0
else
diff --git a/docs/manpage.rst b/docs/manpage.rst
index 33e3373..ead9a16 100644
--- a/docs/manpage.rst
+++ b/docs/manpage.rst
@@ -10,8 +10,8 @@ Synopsis
Description
-----------
:program:`sshuttle` allows you to create a VPN connection from your
-machine to any remote server that you can connect to via
-ssh, as long as that server has python 3.6 or higher.
+machine to any remote server that you can connect to via ssh, as long
+as that server has a sufficiently new Python installation.
To work, you must have root access on the local machine,
but you can have a normal account on the server.
@@ -31,22 +31,23 @@ Options
.. option:: <subnets>
A list of subnets to route over the VPN, in the form
- ``a.b.c.d[/width][port[-port]]``. Valid examples are 1.2.3.4 (a
- single IP address), 1.2.3.4/32 (equivalent to 1.2.3.4),
- 1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0
- netmask), and 0/0 ('just route everything through the
- VPN'). Any of the previous examples are also valid if you append
- a port or a port range, so 1.2.3.4:8000 will only tunnel traffic
- that has as the destination port 8000 of 1.2.3.4 and
- 1.2.3.0/24:8000-9000 will tunnel traffic going to any port between
- 8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24 subnet.
- A hostname can be provided instead of an IP address. If the
- hostname resolves to multiple IPs, all of the IPs are included.
- If a width is provided with a hostname that the width is applied
- to all of the hostnames IPs (if they are all either IPv4 or IPv6).
- Widths cannot be supplied to hostnames that resolve to both IPv4
- and IPv6. Valid examples are example.com, example.com:8000,
- example.com/24, example.com/24:8000 and example.com:8000-9000.
+ ``a.b.c.d[/width][port[-port]]``. Valid examples are 1.2.3.4 (a
+ single IP address) and 1.2.3.4/32 (equivalent to 1.2.3.4),
+ 1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0 netmask).
+ Specify subnets 0/0 to match all IPv4 addresses and ::/0 to match
+ all IPv6 addresses. Any of the previous examples are also valid if
+ you append a port or a port range, so 1.2.3.4:8000 will only
+ tunnel traffic that has as the destination port 8000 of 1.2.3.4
+ and 1.2.3.0/24:8000-9000 will tunnel traffic going to any port
+ between 8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24
+ subnet. A hostname can be provided instead of an IP address. If
+ the hostname resolves to multiple IPs, all of the IPs are
+ included. If a width is provided with a hostname, the width is
+ applied to all of the hostnames IPs (if they are all either IPv4
+ or IPv6). Widths cannot be supplied to hostnames that resolve to
+ both IPv4 and IPv6. Valid examples are example.com,
+ example.com:8000, example.com/24, example.com/24:8000 and
+ example.com:8000-9000.
.. option:: --method <auto|nat|nft|tproxy|pf|ipfw>
@@ -141,7 +142,10 @@ Options
The remote hostname and optional username and ssh
port number to use for connecting to the remote server.
For example, example.com, testuser@example.com,
- testuser@example.com:2222, or example.com:2244.
+ testuser@example.com:2222, or example.com:2244. This
+ hostname is passed to ssh, so it will recognize any
+ aliases and settings you may have configured in
+ ~/.ssh/config.
.. option:: -x <subnet>, --exclude=<subnet>
@@ -274,9 +278,10 @@ Options
Set the file name for the sudoers.d file to be added. Default is
"sshuttle_auto". Only works with --sudoers.
-.. option:: -t, --tmark
+.. option:: -t <mark>, --tmark=<mark>
- Transproxy optional traffic mark with provided MARK value.
+ An option used by the tproxy method: Use the specified traffic
+ mark. The mark must be a hexadecimal value. Defaults to 0x01.
.. option:: --version
@@ -305,11 +310,8 @@ Arguments read from a file must be one per line, as shown below::
--option2
value2
-Comments in config file
-.......................
-
-It's possible to add comments in the configuration file. This allows annotating the
-various subnets with human-readable descriptions, like::
+The configuration file supports comments for human-readable
+annotations. For example::
# company-internal API
8.8.8.8/32
@@ -319,51 +321,96 @@ various subnets with human-readable descriptions, like::
Examples
--------
-Test locally by proxying all local connections, without using ssh::
-
- $ sshuttle -v 0/0
- Starting sshuttle proxy.
- Listening on ('0.0.0.0', 12300).
- [local sudo] Password:
- firewall manager ready.
- c : connecting to server...
- s: available routes:
- s: 192.168.42.0/24
- c : connected.
- firewall manager: starting transproxy.
- c : Accept: 192.168.42.106:50035 -> 192.168.42.121:139.
- c : Accept: 192.168.42.121:47523 -> 77.141.99.22:443.
- ...etc...
+Use the following command to route all IPv4 TCP traffic through remote
+(-r) host example.com (and possibly other traffic too, depending on
+the selected --method). The 0/0 subnet, short for 0.0.0.0/0, matches
+all IPv4 addresses. The ::/0 subnet, matching all IPv6 addresses could
+be added to the example. We also exclude (-x) example.com:22 so that
+we can establish ssh connections from our local machine to the remote
+host without them being routed through sshuttle. Excluding the remote
+host may be necessary on some machines for sshuttle to work properly.
+Press Ctrl+C to exit. To also route DNS queries through sshuttle, try
+adding --dns. Add or remove -v options to see more or less
+information::
+
+ $ sshuttle -r example.com -x example.com:22 0/0
+
+ Starting sshuttle proxy (version ...).
+ [local sudo] Password:
+ fw: Starting firewall with Python version 3.9.5
+ fw: ready method name nat.
+ c : IPv6 disabled since it isn't supported by method nat.
+ c : Method: nat
+ c : IPv4: on
+ c : IPv6: off (not available with nat method)
+ c : UDP : off (not available with nat method)
+ c : DNS : off (available)
+ c : User: off (available)
+ c : Subnets to forward through remote host (type, IP, cidr mask width, startPort, endPort):
+ c : (<AddressFamily.AF_INET: 2>, '0.0.0.0', 0, 0, 0)
+ c : Subnets to exclude from forwarding:
+ c : (<AddressFamily.AF_INET: 2>, '...', 32, 22, 22)
+ c : (<AddressFamily.AF_INET: 2>, '127.0.0.1', 32, 0, 0)
+ c : TCP redirector listening on ('127.0.0.1', 12299).
+ c : Starting client with Python version 3.9.5
+ c : Connecting to server...
+ user@example.com's password:
+ s: Starting server with Python version 3.6.8
+ s: latency control setting = True
+ s: auto-nets:False
+ c : Connected to server.
+ fw: setting up.
+ fw: iptables -w -t nat -N sshuttle-12299
+ fw: iptables -w -t nat -F sshuttle-12299
+ ...
+ Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
^C
- firewall manager: undoing changes.
- KeyboardInterrupt
c : Keyboard interrupt: exiting.
- c : SW#8:192.168.42.121:47523: deleting
- c : SW#6:192.168.42.106:50035: deleting
+ c : SW'unknown':Mux#1: deleting (1 remain)
+ c : SW#7:192.168.42.121:60554: deleting (0 remain)
-Test connection to a remote server, with automatic hostname
-and subnet guessing::
- $ sshuttle -vNHr example.org
+Connect to a remote server, with automatic hostname
+and subnet guessing::
- Starting sshuttle proxy.
- Listening on ('0.0.0.0', 12300).
- firewall manager ready.
- c : connecting to server...
+ $ sshuttle -vNHr example.com -x example.com:22
+ Starting sshuttle proxy (version ...).
+ [local sudo] Password:
+ fw: Starting firewall with Python version 3.9.5
+ fw: ready method name nat.
+ c : IPv6 disabled since it isn't supported by method nat.
+ c : Method: nat
+ c : IPv4: on
+ c : IPv6: off (not available with nat method)
+ c : UDP : off (not available with nat method)
+ c : DNS : off (available)
+ c : User: off (available)
+ c : Subnets to forward through remote host (type, IP, cidr mask width, startPort, endPort):
+ c : NOTE: Additional subnets to forward may be added below by --auto-nets.
+ c : Subnets to exclude from forwarding:
+ c : (<AddressFamily.AF_INET: 2>, '...', 32, 22, 22)
+ c : (<AddressFamily.AF_INET: 2>, '127.0.0.1', 32, 0, 0)
+ c : TCP redirector listening on ('127.0.0.1', 12300).
+ c : Starting client with Python version 3.9.5
+ c : Connecting to server...
+ user@example.com's password:
+ s: Starting server with Python version 3.6.8
+ s: latency control setting = True
+ s: auto-nets:True
+ c : Connected to server.
+ c : seed_hosts: []
s: available routes:
s: 77.141.99.0/24
- c : connected.
- c : seed_hosts: []
- firewall manager: starting transproxy.
- hostwatch: Found: testbox1: 1.2.3.4
- hostwatch: Found: mytest2: 5.6.7.8
- hostwatch: Found: domaincontroller: 99.1.2.3
+ fw: setting up.
+ fw: iptables -w -t nat -N sshuttle-12300
+ fw: iptables -w -t nat -F sshuttle-12300
+ ...
c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
^C
- firewall manager: undoing changes.
c : Keyboard interrupt: exiting.
- c : SW#6:192.168.42.121:60554: deleting
+ c : SW'unknown':Mux#1: deleting (1 remain)
+ c : SW#7:192.168.42.121:60554: deleting (0 remain)
Run :program:`sshuttle` with a `/etc/sshuttle.conf` configuration file::
diff --git a/docs/tproxy.rst b/docs/tproxy.rst
index 6a9dbc8..a805120 100644
--- a/docs/tproxy.rst
+++ b/docs/tproxy.rst
@@ -12,7 +12,8 @@ There are some things you need to consider for TPROXY to work:
ip -6 route add local default dev lo table 100
ip -6 rule add fwmark {TMARK} lookup 100
- where {TMARK} is the identifier mark passed with -t or --tmark flag (default value is 1).
+ where {TMARK} is the identifier mark passed with -t or --tmark flag
+ as a hexadecimal string (default value is '0x01').
- The ``--auto-nets`` feature does not detect IPv6 routes automatically. Add IPv6
routes manually. e.g. by adding ``'::/0'`` to the end of the command line.
diff --git a/docs/usage.rst b/docs/usage.rst
index 646c968..3fd63a1 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -11,6 +11,10 @@ Forward all traffic::
sshuttle -r username@sshserver 0.0.0.0/0
- Use the :option:`sshuttle -r` parameter to specify a remote server.
+ One some systems, you may also need to use the :option:`sshuttle -x`
+ parameter to exclude sshserver or sshserver:22 so that your local
+ machine can communicate directly to sshserver without it being
+ redirected by sshuttle.
- By default sshuttle will automatically choose a method to use. Override with
the :option:`sshuttle --method` parameter.
diff --git a/sshuttle/client.py b/sshuttle/client.py
index 647fb27..349ad86 100644
--- a/sshuttle/client.py
+++ b/sshuttle/client.py
@@ -201,12 +201,11 @@ class FirewallClient:
def __init__(self, method_name, sudo_pythonpath, ttl):
self.auto_nets = []
- python_path = os.path.dirname(os.path.dirname(__file__))
+
argvbase = ([sys.executable, sys.argv[0]] +
['-v'] * (helpers.verbose or 0) +
['--method', method_name] +
- ['--firewall'] +
- ['--ttl', str(ttl)])
+ ['--firewall'])
if ssyslog._p:
argvbase += ['--syslog']
@@ -224,7 +223,8 @@ class FirewallClient:
if sudo_pythonpath:
elev_prefix += ['/usr/bin/env',
- 'PYTHONPATH=%s' % python_path]
+ 'PYTHONPATH=%s' %
+ os.path.dirname(os.path.dirname(__file__))]
argv_tries = [elev_prefix + argvbase, argvbase]
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
@@ -261,7 +261,7 @@ class FirewallClient:
def setup(self, subnets_include, subnets_exclude, nslist,
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
- user, tmark, ttl):
+ user, ttl, tmark):
self.subnets_include = subnets_include
self.subnets_exclude = subnets_exclude
self.nslist = nslist
@@ -311,7 +311,9 @@ class FirewallClient:
else:
user = b'%d' % self.user
- self.pfile.write(b'GO %d %s\n' % (udp, user))
+ self.pfile.write(b'GO %d %s %d %s\n' %
+ (udp, user, self.ttl,
+ bytes(self.tmark, 'ascii')))
self.pfile.flush()
line = self.pfile.readline()
@@ -1003,7 +1005,7 @@ def main(listenip_v6, listenip_v4,
# start the firewall
fw.setup(subnets_include, subnets_exclude, nslist,
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
- required.udp, user, tmark, ttl)
+ required.udp, user, ttl, tmark)
# start the client process
try:
diff --git a/sshuttle/cmdline.py b/sshuttle/cmdline.py
index d935d9e..e792e22 100644
--- a/sshuttle/cmdline.py
+++ b/sshuttle/cmdline.py
@@ -20,7 +20,7 @@ def main():
return 1
if not opt.sudoers_filename:
- log('--sudoers-file must be set or omited.')
+ log('--sudoers-file must be set or omitted.')
return 1
sudoers(
@@ -85,6 +85,13 @@ def main():
ipport_v4 = "auto"
# parse_ipport6('[::1]:0')
ipport_v6 = "auto" if not opt.disable_ipv6 else None
+ try:
+ int(opt.tmark, 16)
+ except ValueError:
+ parser.error("--tmark must be a hexadecimal value")
+ opt.tmark = opt.tmark.lower() # make 'x' in 0x lowercase
+ if not opt.tmark.startswith("0x"): # accept without 0x prefix
+ opt.tmark = "0x%s" % opt.tmark
if opt.syslog:
ssyslog.start_syslog()
ssyslog.close_stdin()
diff --git a/sshuttle/firewall.py b/sshuttle/firewall.py
index 021ca96..031454c 100644
--- a/sshuttle/firewall.py
+++ b/sshuttle/firewall.py
@@ -223,11 +223,13 @@ def main(method_name, syslog, ttl):
raise Fatal('expected GO but got %r' % line)
_, _, args = line.partition(" ")
- udp, user = args.strip().split(" ", 1)
+ udp, user, ttl, tmark = args.strip().split(" ", 3)
udp = bool(int(udp))
if user == '-':
user = None
- debug2('Got udp: %r, user: %r' % (udp, user))
+ ttl = int(ttl)
+ debug2('Got udp: %r, user: %r, ttl: %s, tmark: %s' %
+ (udp, user, ttl, tmark))
subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
@@ -242,14 +244,14 @@ def main(method_name, syslog, ttl):
method.setup_firewall(
port_v6, dnsport_v6, nslist_v6,
socket.AF_INET6, subnets_v6, udp,
- user, ttl)
+ user, ttl, tmark)
if subnets_v4 or nslist_v4:
debug2('setting up IPv4.')
method.setup_firewall(
port_v4, dnsport_v4, nslist_v4,
socket.AF_INET, subnets_v4, udp,
- user, ttl)
+ user, ttl, tmark)
flush_systemd_dns_cache()
stdout.write('STARTED\n')
diff --git a/sshuttle/hostwatch.py b/sshuttle/hostwatch.py
index 683b6a7..7e4d3c5 100644
--- a/sshuttle/hostwatch.py
+++ b/sshuttle/hostwatch.py
@@ -16,8 +16,6 @@ NETSTAT_POLL_TIME = 30
CACHEFILE = os.path.expanduser('~/.sshuttle.hosts')
-_nmb_ok = True
-_smb_ok = True
hostnames = {}
queue = {}
try:
@@ -141,110 +139,11 @@ def _check_netstat():
check_host(ip)
-def _check_smb(hostname):
- return
- global _smb_ok
- if not _smb_ok:
- return
- debug2(' > smb: %s' % hostname)
- argv = ['smbclient', '-U', '%', '-L', hostname]
- try:
- p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null,
- env=get_env())
- lines = p.stdout.readlines()
- p.wait()
- except OSError:
- _, e = sys.exc_info()[:2]
- log('%r failed: %r' % (argv, e))
- _smb_ok = False
- return
-
- lines.reverse()
-
- # junk at top
- while lines:
- line = lines.pop().strip()
- if re.match(r'Server\s+', line):
- break
-
- # server list section:
- # Server Comment
- # ------ -------
- while lines:
- line = lines.pop().strip()
- if not line or re.match(r'-+\s+-+', line):
- continue
- if re.match(r'Workgroup\s+Master', line):
- break
- words = line.split()
- hostname = words[0].lower()
- debug3('< %s' % hostname)
- check_host(hostname)
-
- # workgroup list section:
- # Workgroup Master
- # --------- ------
- while lines:
- line = lines.pop().strip()
- if re.match(r'-+\s+', line):
- continue
- if not line:
- break
- words = line.split()
- (workgroup, hostname) = (words[0].lower(), words[1].lower())
- debug3('< group(%s) -> %s' % (workgroup, hostname))
- check_host(hostname)
- check_workgroup(workgroup)
-
- if lines:
- assert(0)
-
-
-def _check_nmb(hostname, is_workgroup, is_master):
- return
- global _nmb_ok
- if not _nmb_ok:
- return
- debug2(' > n%d%d: %s' % (is_workgroup, is_master, hostname))
- argv = ['nmblookup'] + ['-M'] * is_master + ['--', hostname]
- try:
- p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null,
- env=get_env)
- lines = p.stdout.readlines()
- rv = p.wait()
- except OSError:
- _, e = sys.exc_info()[:2]
- log('%r failed: %r' % (argv, e))
- _nmb_ok = False
- return
- if rv:
- log('%r returned %d' % (argv, rv))
- return
- for line in lines:
- m = re.match(r'(\d+\.\d+\.\d+\.\d+) (\w+)<\w\w>\n', line)
- if m:
- g = m.groups()
- (ip, name) = (g[0], g[1].lower())
- debug3('< %s -> %s' % (name, ip))
- if is_workgroup:
- _enqueue(_check_smb, ip)
- else:
- found_host(name, ip)
- check_host(name)
-
-
def check_host(hostname):
if _is_ip(hostname):
_enqueue(_check_revdns, hostname)
else:
_enqueue(_check_dns, hostname)
- _enqueue(_check_smb, hostname)
- _enqueue(_check_nmb, hostname, False, False)
-
-
-def check_workgroup(hostname):
- _enqueue(_check_nmb, hostname, True, False)
- _enqueue(_check_nmb, hostname, True, True)
def _enqueue(op, *args):
@@ -277,8 +176,6 @@ def hw_main(seed_hosts, auto_hosts):
_enqueue(_check_netstat)
check_host('localhost')
check_host(socket.gethostname())
- check_workgroup('workgroup')
- check_workgroup('-')
while 1:
now = time.time()
diff --git a/sshuttle/methods/__init__.py b/sshuttle/methods/__init__.py
index 1882c3a..0e4c49d 100644
--- a/sshuttle/methods/__init__.py
+++ b/sshuttle/methods/__init__.py
@@ -91,7 +91,7 @@ class BaseMethod(object):
(key, self.name))
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
- user):
+ user, ttl, tmark):
raise NotImplementedError()
def restore_firewall(self, port, family, udp, user):
diff --git a/sshuttle/methods/ipfw.py b/sshuttle/methods/ipfw.py
index f93bdf4..bda8968 100644
--- a/sshuttle/methods/ipfw.py
+++ b/sshuttle/methods/ipfw.py
@@ -189,7 +189,7 @@ class Method(BaseMethod):
# udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1)
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
- user, ttl):
+ user, ttl, tmark):
# IPv6 not supported
if family not in [socket.AF_INET]:
raise Exception(
diff --git a/sshuttle/methods/nat.py b/sshuttle/methods/nat.py
index baa9998..a7a661c 100644
--- a/sshuttle/methods/nat.py
+++ b/sshuttle/methods/nat.py
@@ -13,7 +13,7 @@ class Method(BaseMethod):
# 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, ttl):
+ user, ttl, tmark):
if family != socket.AF_INET and family != socket.AF_INET6:
raise Exception(
'Address family "%s" unsupported by nat method_name'
diff --git a/sshuttle/methods/nft.py b/sshuttle/methods/nft.py
index 775fa51..8f54c86 100644
--- a/sshuttle/methods/nft.py
+++ b/sshuttle/methods/nft.py
@@ -13,7 +13,7 @@ class Method(BaseMethod):
# 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, ttl):
+ user, ttl, tmark):
if udp:
raise Exception("UDP not supported by nft")
diff --git a/sshuttle/methods/pf.py b/sshuttle/methods/pf.py
index 1bc67e7..be46be7 100644
--- a/sshuttle/methods/pf.py
+++ b/sshuttle/methods/pf.py
@@ -444,7 +444,7 @@ class Method(BaseMethod):
return sock.getsockname()
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
- user, ttl):
+ user, ttl, tmark):
if family not in [socket.AF_INET, socket.AF_INET6]:
raise Exception(
'Address family "%s" unsupported by pf method_name'
diff --git a/sshuttle/methods/tproxy.py b/sshuttle/methods/tproxy.py
index c1cccd5..eb337fe 100644
--- a/sshuttle/methods/tproxy.py
+++ b/sshuttle/methods/tproxy.py
@@ -151,17 +151,7 @@ class Method(BaseMethod):
udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
- user, ttl):
- if self.firewall is None:
- tmark = '1'
- else:
- tmark = self.firewall.tmark
-
- self.setup_firewall_tproxy(port, dnsport, nslist, family, subnets, udp,
- user, tmark)
-
- def setup_firewall_tproxy(self, port, dnsport, nslist, family, subnets,
- udp, user, tmark):
+ user, ttl, tmark):
if family not in [socket.AF_INET, socket.AF_INET6]:
raise Exception(
'Address family "%s" unsupported by tproxy method'
@@ -192,8 +182,8 @@ class Method(BaseMethod):
_ipt('-F', divert_chain)
_ipt('-N', tproxy_chain)
_ipt('-F', tproxy_chain)
- _ipt('-I', 'OUTPUT', tmark, '-j', mark_chain)
- _ipt('-I', 'PREROUTING', tmark, '-j', tproxy_chain)
+ _ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
+ _ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
# Don't have packets sent to any of our local IP addresses go
# through the tproxy or mark chains.
@@ -224,7 +214,7 @@ class Method(BaseMethod):
'--dest', '%s/32' % ip,
'-m', 'udp', '-p', 'udp', '--dport', '53')
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
- '--tproxy-mark', '0x'+tmark+'/0x'+tmark,
+ '--tproxy-mark', tmark,
'--dest', '%s/32' % ip,
'-m', 'udp', '-p', 'udp', '--dport', '53',
'--on-port', str(dnsport))
@@ -249,7 +239,7 @@ class Method(BaseMethod):
'-m', 'tcp',
*tcp_ports)
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
- '--tproxy-mark', '0x'+tmark+'/0x'+tmark,
+ '--tproxy-mark', tmark,
'--dest', '%s/%s' % (snet, swidth),
'-m', 'tcp',
*(tcp_ports + ('--on-port', str(port))))
@@ -273,7 +263,7 @@ class Method(BaseMethod):
'-m', 'udp',
*udp_ports)
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
- '--tproxy-mark', '0x'+tmark+'/0x'+tmark,
+ '--tproxy-mark', tmark,
'--dest', '%s/%s' % (snet, swidth),
'-m', 'udp',
*(udp_ports + ('--on-port', str(port))))
diff --git a/sshuttle/options.py b/sshuttle/options.py
index 290e176..0a311aa 100644
--- a/sshuttle/options.py
+++ b/sshuttle/options.py
@@ -132,6 +132,7 @@ def parse_ipport(s):
def parse_list(lst):
+ """Parse a comma separated string into a list."""
return re.split(r'[\s,]+', lst.strip()) if lst else []
@@ -220,6 +221,7 @@ parser.add_argument(
type=parse_list,
help="""
capture and forward DNS requests made to the following servers
+ (comma separated)
"""
)
parser.add_argument(
@@ -280,7 +282,7 @@ parser.add_argument(
action="count",
default=0,
help="""
- increase debug message verbosity
+ increase debug message verbosity (can be used more than once)
"""
)
parser.add_argument(
@@ -445,8 +447,9 @@ parser.add_argument(
parser.add_argument(
"-t", "--tmark",
metavar="[MARK]",
- default="1",
+ default="0x01",
help="""
- transproxy optional traffic mark with provided MARK value
+ tproxy optional traffic mark with provided MARK value in
+ hexadecimal (default '0x01')
"""
)
diff --git a/tests/client/test_firewall.py b/tests/client/test_firewall.py
index 4a1d744..31618d9 100644
--- a/tests/client/test_firewall.py
+++ b/tests/client/test_firewall.py
@@ -15,7 +15,7 @@ NSLIST
{inet},1.2.3.33
{inet6},2404:6800:4004:80c::33
PORTS 1024,1025,1026,1027
-GO 1 -
+GO 1 - 63 0x01
HOST 1.2.3.3,existing
""".format(inet=AF_INET, inet6=AF_INET6))
stdout = Mock()
@@ -126,7 +126,7 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
True,
None,
- 63),
+ 63, '0x01'),
call().setup_firewall(
1025, 1027,
[(AF_INET, u'1.2.3.33')],
@@ -135,7 +135,7 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
True,
None,
- 63),
+ 63, '0x01'),
call().restore_firewall(1024, AF_INET6, True, None),
call().restore_firewall(1025, AF_INET, True, None),
]
diff --git a/tests/client/test_methods_nat.py b/tests/client/test_methods_nat.py
index 10b3ddb..0bd0ec2 100644
--- a/tests/client/test_methods_nat.py
+++ b/tests/client/test_methods_nat.py
@@ -103,7 +103,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
False,
None,
- 63)
+ 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET6, 'nat', 'sshuttle-1024')
@@ -150,7 +150,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
True,
None,
- 63)
+ 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by nat method_name'
assert mock_ipt_chain_exists.mock_calls == []
assert mock_ipt_ttl.mock_calls == []
@@ -164,7 +164,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
False,
None,
- 63)
+ 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET, 'nat', 'sshuttle-1025')
]
diff --git a/tests/client/test_methods_pf.py b/tests/client/test_methods_pf.py
index d807b11..83f7537 100644
--- a/tests/client/test_methods_pf.py
+++ b/tests/client/test_methods_pf.py
@@ -187,7 +187,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
False,
None,
- 63)
+ 63, '0x01')
assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xC4704433, ANY),
call(mock_pf_get_dev(), 0xCC20441A, ANY),
@@ -227,7 +227,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True,
None,
- 63)
+ 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by pf method_name'
assert mock_pf_get_dev.mock_calls == []
assert mock_ioctl.mock_calls == []
@@ -241,7 +241,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
False,
None,
- 63)
+ 63, '0x01')
assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xC4704433, ANY),
call(mock_pf_get_dev(), 0xCC20441A, ANY),
@@ -302,7 +302,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl,
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
False,
None,
- 63)
+ 63, '0x01')
assert mock_pfctl.mock_calls == [
call('-s all'),
@@ -335,7 +335,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl,
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True,
None,
- 63)
+ 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by pf method_name'
ass