mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-05-18 23:20:52 +02:00
Fix conflicts in localhost detection after removal of ttl code.
Since sshuttle's TTL code was removed in a recent commit, this code will cause sshuttle to exit when the host specified with -r is actually the localhost.
This commit is contained in:
commit
2538beb711
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: pip
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
10
.github/workflows/pythonpackage.yml
vendored
10
.github/workflows/pythonpackage.yml
vendored
@ -5,11 +5,11 @@ name: Python package
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, tproxy_mark_param ]
|
branches: [ master ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master, tproxy_mark_param ]
|
branches: [ master ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
branches: [ tproxy_mark_param ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -20,9 +20,9 @@ jobs:
|
|||||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2.3.4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2.2.2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
@ -46,6 +46,10 @@ Obtaining sshuttle
|
|||||||
|
|
||||||
dnf install sshuttle
|
dnf install sshuttle
|
||||||
|
|
||||||
|
- Gentoo::
|
||||||
|
|
||||||
|
emerge -av net-proxy/sshuttle
|
||||||
|
|
||||||
- NixOS::
|
- NixOS::
|
||||||
|
|
||||||
nix-env -iA nixos.sshuttle
|
nix-env -iA nixos.sshuttle
|
||||||
|
@ -50,6 +50,14 @@ if [ "$FILE_NAME" == "" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
# Make a temp file to hold the sudoers config
|
||||||
umask 077
|
umask 077
|
||||||
TEMP_FILE=$(mktemp)
|
TEMP_FILE=$(mktemp)
|
||||||
@ -62,9 +70,9 @@ visudo_code=$?
|
|||||||
rm "$TEMP_FILE"
|
rm "$TEMP_FILE"
|
||||||
|
|
||||||
if [ $visudo_code -eq 0 ]; then
|
if [ $visudo_code -eq 0 ]; then
|
||||||
echo "$CONTENT" > "/etc/sudoers.d/$FILE_NAME"
|
echo "$CONTENT" > "$FILE_NAME"
|
||||||
chmod 0440 "/etc/sudoers.d/$FILE_NAME"
|
chmod 0440 "$FILE_NAME"
|
||||||
echo "The sudoers file /etc/sudoers.d/$FILE_NAME has been successfully created!"
|
echo "The sudoers file $FILE_NAME has been successfully created!"
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
178
docs/manpage.rst
178
docs/manpage.rst
@ -4,14 +4,14 @@ sshuttle
|
|||||||
|
|
||||||
Synopsis
|
Synopsis
|
||||||
--------
|
--------
|
||||||
**sshuttle** [*options*] [**-r** *[username@]sshserver[:port]*] \<*subnets* ...\>
|
**sshuttle** [*options*] **-r** *[username@]sshserver[:port]* \<*subnets* ...\>
|
||||||
|
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
:program:`sshuttle` allows you to create a VPN connection from your
|
:program:`sshuttle` allows you to create a VPN connection from your
|
||||||
machine to any remote server that you can connect to via
|
machine to any remote server that you can connect to via ssh, as long
|
||||||
ssh, as long as that server has python 3.6 or higher.
|
as that server has a sufficiently new Python installation.
|
||||||
|
|
||||||
To work, you must have root access on the local machine,
|
To work, you must have root access on the local machine,
|
||||||
but you can have a normal account on the server.
|
but you can have a normal account on the server.
|
||||||
@ -31,22 +31,23 @@ Options
|
|||||||
.. option:: <subnets>
|
.. option:: <subnets>
|
||||||
|
|
||||||
A list of subnets to route over the VPN, in the form
|
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
|
``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),
|
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
|
1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0 netmask).
|
||||||
netmask), and 0/0 ('just route everything through the
|
Specify subnets 0/0 to match all IPv4 addresses and ::/0 to match
|
||||||
VPN'). Any of the previous examples are also valid if you append
|
all IPv6 addresses. Any of the previous examples are also valid if
|
||||||
a port or a port range, so 1.2.3.4:8000 will only tunnel traffic
|
you append a port or a port range, so 1.2.3.4:8000 will only
|
||||||
that has as the destination port 8000 of 1.2.3.4 and
|
tunnel traffic that has as the destination port 8000 of 1.2.3.4
|
||||||
1.2.3.0/24:8000-9000 will tunnel traffic going to any port between
|
and 1.2.3.0/24:8000-9000 will tunnel traffic going to any port
|
||||||
8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24 subnet.
|
between 8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24
|
||||||
A hostname can be provided instead of an IP address. If the
|
subnet. A hostname can be provided instead of an IP address. If
|
||||||
hostname resolves to multiple IPs, all of the IPs are included.
|
the hostname resolves to multiple IPs, all of the IPs are
|
||||||
If a width is provided with a hostname that the width is applied
|
included. If a width is provided with a hostname, the width is
|
||||||
to all of the hostnames IPs (if they are all either IPv4 or IPv6).
|
applied to all of the hostnames IPs (if they are all either IPv4
|
||||||
Widths cannot be supplied to hostnames that resolve to both IPv4
|
or IPv6). Widths cannot be supplied to hostnames that resolve to
|
||||||
and IPv6. Valid examples are example.com, example.com:8000,
|
both IPv4 and IPv6. Valid examples are example.com,
|
||||||
example.com/24, example.com/24:8000 and example.com:8000-9000.
|
example.com:8000, example.com/24, example.com/24:8000 and
|
||||||
|
example.com:8000-9000.
|
||||||
|
|
||||||
.. option:: --method <auto|nat|nft|tproxy|pf|ipfw>
|
.. option:: --method <auto|nat|nft|tproxy|pf|ipfw>
|
||||||
|
|
||||||
@ -88,6 +89,13 @@ Options
|
|||||||
few subnets over the VPN, you probably would prefer to
|
few subnets over the VPN, you probably would prefer to
|
||||||
keep using your local DNS server for everything else.
|
keep using your local DNS server for everything else.
|
||||||
|
|
||||||
|
:program:`sshuttle` tries to store a cache of the hostnames in
|
||||||
|
~/.sshuttle.hosts on the remote host. Similarly, it tries to read
|
||||||
|
the file when you later reconnect to the host with --auto-hosts
|
||||||
|
enabled to quickly populate the host list. When troubleshooting
|
||||||
|
this feature, try removing this file on the remote host when
|
||||||
|
sshuttle is not running.
|
||||||
|
|
||||||
.. option:: -N, --auto-nets
|
.. option:: -N, --auto-nets
|
||||||
|
|
||||||
In addition to the subnets provided on the command
|
In addition to the subnets provided on the command
|
||||||
@ -141,7 +149,10 @@ Options
|
|||||||
The remote hostname and optional username and ssh
|
The remote hostname and optional username and ssh
|
||||||
port number to use for connecting to the remote server.
|
port number to use for connecting to the remote server.
|
||||||
For example, example.com, testuser@example.com,
|
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>
|
.. option:: -x <subnet>, --exclude=<subnet>
|
||||||
|
|
||||||
@ -174,7 +185,7 @@ Options
|
|||||||
|
|
||||||
A comma-separated list of hostnames to use to
|
A comma-separated list of hostnames to use to
|
||||||
initialize the :option:`--auto-hosts` scan algorithm.
|
initialize the :option:`--auto-hosts` scan algorithm.
|
||||||
:option:`--auto-hosts` does things like poll local SMB servers
|
:option:`--auto-hosts` does things like poll netstat output
|
||||||
for lists of local hostnames, but can speed things up
|
for lists of local hostnames, but can speed things up
|
||||||
if you use this option to give it a few names to start
|
if you use this option to give it a few names to start
|
||||||
from.
|
from.
|
||||||
@ -274,9 +285,10 @@ Options
|
|||||||
Set the file name for the sudoers.d file to be added. Default is
|
Set the file name for the sudoers.d file to be added. Default is
|
||||||
"sshuttle_auto". Only works with --sudoers.
|
"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
|
.. option:: --version
|
||||||
|
|
||||||
@ -305,11 +317,8 @@ Arguments read from a file must be one per line, as shown below::
|
|||||||
--option2
|
--option2
|
||||||
value2
|
value2
|
||||||
|
|
||||||
Comments in config file
|
The configuration file supports comments for human-readable
|
||||||
.......................
|
annotations. For example::
|
||||||
|
|
||||||
It's possible to add comments in the configuration file. This allows annotating the
|
|
||||||
various subnets with human-readable descriptions, like::
|
|
||||||
|
|
||||||
# company-internal API
|
# company-internal API
|
||||||
8.8.8.8/32
|
8.8.8.8/32
|
||||||
@ -319,51 +328,96 @@ various subnets with human-readable descriptions, like::
|
|||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
Test locally by proxying all local connections, without using ssh::
|
|
||||||
|
|
||||||
$ sshuttle -v 0/0
|
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::
|
||||||
|
|
||||||
Starting sshuttle proxy.
|
$ sshuttle -r example.com -x example.com:22 0/0
|
||||||
Listening on ('0.0.0.0', 12300).
|
|
||||||
|
Starting sshuttle proxy (version ...).
|
||||||
[local sudo] Password:
|
[local sudo] Password:
|
||||||
firewall manager ready.
|
fw: Starting firewall with Python version 3.9.5
|
||||||
c : connecting to server...
|
fw: ready method name nat.
|
||||||
s: available routes:
|
c : IPv6 disabled since it isn't supported by method nat.
|
||||||
s: 192.168.42.0/24
|
c : Method: nat
|
||||||
c : connected.
|
c : IPv4: on
|
||||||
firewall manager: starting transproxy.
|
c : IPv6: off (not available with nat method)
|
||||||
c : Accept: 192.168.42.106:50035 -> 192.168.42.121:139.
|
c : UDP : off (not available with nat method)
|
||||||
c : Accept: 192.168.42.121:47523 -> 77.141.99.22:443.
|
c : DNS : off (available)
|
||||||
...etc...
|
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
|
^C
|
||||||
firewall manager: undoing changes.
|
|
||||||
KeyboardInterrupt
|
|
||||||
c : Keyboard interrupt: exiting.
|
c : Keyboard interrupt: exiting.
|
||||||
c : SW#8:192.168.42.121:47523: deleting
|
c : SW'unknown':Mux#1: deleting (1 remain)
|
||||||
c : SW#6:192.168.42.106:50035: deleting
|
c : SW#7:192.168.42.121:60554: deleting (0 remain)
|
||||||
|
|
||||||
Test connection to a remote server, with automatic hostname
|
|
||||||
|
Connect to a remote server, with automatic hostname
|
||||||
and subnet guessing::
|
and subnet guessing::
|
||||||
|
|
||||||
$ sshuttle -vNHr example.org
|
$ sshuttle -vNHr example.com -x example.com:22
|
||||||
|
Starting sshuttle proxy (version ...).
|
||||||
Starting sshuttle proxy.
|
[local sudo] Password:
|
||||||
Listening on ('0.0.0.0', 12300).
|
fw: Starting firewall with Python version 3.9.5
|
||||||
firewall manager ready.
|
fw: ready method name nat.
|
||||||
c : connecting to server...
|
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: available routes:
|
||||||
s: 77.141.99.0/24
|
s: 77.141.99.0/24
|
||||||
c : connected.
|
fw: setting up.
|
||||||
c : seed_hosts: []
|
fw: iptables -w -t nat -N sshuttle-12300
|
||||||
firewall manager: starting transproxy.
|
fw: iptables -w -t nat -F sshuttle-12300
|
||||||
hostwatch: Found: testbox1: 1.2.3.4
|
...
|
||||||
hostwatch: Found: mytest2: 5.6.7.8
|
|
||||||
hostwatch: Found: domaincontroller: 99.1.2.3
|
|
||||||
c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
|
c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
|
||||||
^C
|
^C
|
||||||
firewall manager: undoing changes.
|
|
||||||
c : Keyboard interrupt: exiting.
|
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::
|
Run :program:`sshuttle` with a `/etc/sshuttle.conf` configuration file::
|
||||||
|
|
||||||
@ -387,9 +441,7 @@ Example configuration file::
|
|||||||
Discussion
|
Discussion
|
||||||
----------
|
----------
|
||||||
When it starts, :program:`sshuttle` creates an ssh session to the
|
When it starts, :program:`sshuttle` creates an ssh session to the
|
||||||
server specified by the ``-r`` option. If ``-r`` is omitted,
|
server specified by the ``-r`` option.
|
||||||
it will start both its client and server locally, which is
|
|
||||||
sometimes useful for testing.
|
|
||||||
|
|
||||||
After connecting to the remote server, :program:`sshuttle` uploads its
|
After connecting to the remote server, :program:`sshuttle` uploads its
|
||||||
(python) source code to the remote end and executes it
|
(python) source code to the remote end and executes it
|
||||||
|
@ -15,10 +15,12 @@ Supports:
|
|||||||
|
|
||||||
* IPv4 TCP
|
* IPv4 TCP
|
||||||
* IPv4 DNS
|
* IPv4 DNS
|
||||||
|
* IPv6 TCP
|
||||||
|
* IPv6 DNS
|
||||||
|
|
||||||
Requires:
|
Requires:
|
||||||
|
|
||||||
* iptables DNAT, REDIRECT, and ttl modules.
|
* iptables DNAT and REDIRECT modules. ip6tables for IPv6.
|
||||||
|
|
||||||
Linux with nft method
|
Linux with nft method
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -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 route add local default dev lo table 100
|
||||||
ip -6 rule add fwmark {TMARK} lookup 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
|
- 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.
|
routes manually. e.g. by adding ``'::/0'`` to the end of the command line.
|
||||||
|
@ -11,6 +11,10 @@ Forward all traffic::
|
|||||||
sshuttle -r username@sshserver 0.0.0.0/0
|
sshuttle -r username@sshserver 0.0.0.0/0
|
||||||
|
|
||||||
- Use the :option:`sshuttle -r` parameter to specify a remote server.
|
- 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
|
- By default sshuttle will automatically choose a method to use. Override with
|
||||||
the :option:`sshuttle --method` parameter.
|
the :option:`sshuttle --method` parameter.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
pytest==6.2.2
|
pytest==6.2.4
|
||||||
pytest-cov==2.11.1
|
pytest-cov==2.12.1
|
||||||
flake8==3.8.4
|
flake8==3.9.2
|
||||||
pyflakes==2.2.0
|
pyflakes==2.3.1
|
||||||
|
@ -1,2 +1 @@
|
|||||||
setuptools-scm==5.0.2
|
setuptools-scm==6.0.1
|
||||||
psutil
|
|
||||||
|
1
setup.py
1
setup.py
@ -63,7 +63,6 @@ setup(
|
|||||||
},
|
},
|
||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'psutil',
|
|
||||||
],
|
],
|
||||||
tests_require=[
|
tests_require=[
|
||||||
'pytest',
|
'pytest',
|
||||||
|
@ -42,4 +42,4 @@ import sshuttle.cmdline_options as options # noqa: E402
|
|||||||
from sshuttle.server import main # noqa: E402
|
from sshuttle.server import main # noqa: E402
|
||||||
main(options.latency_control, options.latency_buffer_size,
|
main(options.latency_control, options.latency_buffer_size,
|
||||||
options.auto_hosts, options.to_nameserver,
|
options.auto_hosts, options.to_nameserver,
|
||||||
options.auto_nets, options.ttl, options.localhost_detector)
|
options.auto_nets, options.localhost_detector)
|
||||||
|
@ -7,7 +7,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
import tempfile
|
import tempfile
|
||||||
import psutil
|
|
||||||
|
|
||||||
import sshuttle.helpers as helpers
|
import sshuttle.helpers as helpers
|
||||||
import sshuttle.ssnet as ssnet
|
import sshuttle.ssnet as ssnet
|
||||||
@ -46,6 +45,7 @@ def got_signal(signum, frame):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
# Filename of the pidfile created by the sshuttle client.
|
||||||
_pidname = None
|
_pidname = None
|
||||||
|
|
||||||
# This variable is set to true if the client and the server appear to
|
# This variable is set to true if the client and the server appear to
|
||||||
@ -204,14 +204,13 @@ class MultiListener:
|
|||||||
|
|
||||||
class FirewallClient:
|
class FirewallClient:
|
||||||
|
|
||||||
def __init__(self, method_name, sudo_pythonpath, ttl):
|
def __init__(self, method_name, sudo_pythonpath):
|
||||||
self.auto_nets = []
|
self.auto_nets = []
|
||||||
python_path = os.path.dirname(os.path.dirname(__file__))
|
|
||||||
argvbase = ([sys.executable, sys.argv[0]] +
|
argvbase = ([sys.executable, sys.argv[0]] +
|
||||||
['-v'] * (helpers.verbose or 0) +
|
['-v'] * (helpers.verbose or 0) +
|
||||||
['--method', method_name] +
|
['--method', method_name] +
|
||||||
['--firewall'] +
|
['--firewall'])
|
||||||
['--ttl', ttl])
|
|
||||||
if ssyslog._p:
|
if ssyslog._p:
|
||||||
argvbase += ['--syslog']
|
argvbase += ['--syslog']
|
||||||
|
|
||||||
@ -229,7 +228,8 @@ class FirewallClient:
|
|||||||
|
|
||||||
if sudo_pythonpath:
|
if sudo_pythonpath:
|
||||||
elev_prefix += ['/usr/bin/env',
|
elev_prefix += ['/usr/bin/env',
|
||||||
'PYTHONPATH=%s' % python_path]
|
'PYTHONPATH=%s' %
|
||||||
|
os.path.dirname(os.path.dirname(__file__))]
|
||||||
argv_tries = [elev_prefix + argvbase, argvbase]
|
argv_tries = [elev_prefix + argvbase, argvbase]
|
||||||
|
|
||||||
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
|
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
|
||||||
@ -266,7 +266,7 @@ class FirewallClient:
|
|||||||
|
|
||||||
def setup(self, subnets_include, subnets_exclude, nslist,
|
def setup(self, subnets_include, subnets_exclude, nslist,
|
||||||
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
|
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
|
||||||
user, tmark, ttl):
|
user, tmark):
|
||||||
self.subnets_include = subnets_include
|
self.subnets_include = subnets_include
|
||||||
self.subnets_exclude = subnets_exclude
|
self.subnets_exclude = subnets_exclude
|
||||||
self.nslist = nslist
|
self.nslist = nslist
|
||||||
@ -277,7 +277,6 @@ class FirewallClient:
|
|||||||
self.udp = udp
|
self.udp = udp
|
||||||
self.user = user
|
self.user = user
|
||||||
self.tmark = tmark
|
self.tmark = tmark
|
||||||
self.ttl = ttl
|
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
rv = self.p.poll()
|
rv = self.p.poll()
|
||||||
@ -316,7 +315,8 @@ class FirewallClient:
|
|||||||
else:
|
else:
|
||||||
user = b'%d' % self.user
|
user = b'%d' % self.user
|
||||||
|
|
||||||
self.pfile.write(b'GO %d %s\n' % (udp, user))
|
self.pfile.write(b'GO %d %s %s\n' %
|
||||||
|
(udp, user, bytes(self.tmark, 'ascii')))
|
||||||
self.pfile.flush()
|
self.pfile.flush()
|
||||||
|
|
||||||
line = self.pfile.readline()
|
line = self.pfile.readline()
|
||||||
@ -461,7 +461,7 @@ def ondns(listener, method, mux, handlers):
|
|||||||
def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
||||||
python, latency_control, latency_buffer_size,
|
python, latency_control, latency_buffer_size,
|
||||||
dns_listener, seed_hosts, auto_hosts, auto_nets, daemon,
|
dns_listener, seed_hosts, auto_hosts, auto_nets, daemon,
|
||||||
to_nameserver, ttl):
|
to_nameserver):
|
||||||
|
|
||||||
helpers.logprefix = 'c : '
|
helpers.logprefix = 'c : '
|
||||||
debug1('Starting client with Python version %s'
|
debug1('Starting client with Python version %s'
|
||||||
@ -491,7 +491,6 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
auto_hosts=auto_hosts,
|
auto_hosts=auto_hosts,
|
||||||
to_nameserver=to_nameserver,
|
to_nameserver=to_nameserver,
|
||||||
auto_nets=auto_nets,
|
auto_nets=auto_nets,
|
||||||
ttl=ttl,
|
|
||||||
localhost_detector=localhost_detector))
|
localhost_detector=localhost_detector))
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
if e.args[0] == errno.EPIPE:
|
if e.args[0] == errno.EPIPE:
|
||||||
@ -676,7 +675,9 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
# poll() won't tell us when process exited since the
|
# poll() won't tell us when process exited since the
|
||||||
# process is no longer our child (it returns 0 all the
|
# process is no longer our child (it returns 0 all the
|
||||||
# time).
|
# time).
|
||||||
if not psutil.pid_exists(serverproc.pid):
|
try:
|
||||||
|
os.kill(serverproc.pid, 0)
|
||||||
|
except OSError:
|
||||||
raise Fatal('ssh connection to server (pid %d) exited.' %
|
raise Fatal('ssh connection to server (pid %d) exited.' %
|
||||||
serverproc.pid)
|
serverproc.pid)
|
||||||
else:
|
else:
|
||||||
@ -698,12 +699,11 @@ def main(listenip_v6, listenip_v4,
|
|||||||
latency_buffer_size, dns, nslist,
|
latency_buffer_size, dns, nslist,
|
||||||
method_name, seed_hosts, auto_hosts, auto_nets,
|
method_name, seed_hosts, auto_hosts, auto_nets,
|
||||||
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
|
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
|
||||||
user, sudo_pythonpath, tmark, ttl):
|
user, sudo_pythonpath, tmark):
|
||||||
|
|
||||||
if not remotename:
|
if not remotename:
|
||||||
print("WARNING: You must specify -r/--remote to securely route "
|
raise Fatal("You must use -r/--remote to specify a remote "
|
||||||
"traffic to a remote machine. Running without -r/--remote "
|
"host to route traffic through.")
|
||||||
"is only recommended for testing.")
|
|
||||||
|
|
||||||
if daemon:
|
if daemon:
|
||||||
try:
|
try:
|
||||||
@ -714,21 +714,28 @@ def main(listenip_v6, listenip_v4,
|
|||||||
debug1('Starting sshuttle proxy (version %s).' % __version__)
|
debug1('Starting sshuttle proxy (version %s).' % __version__)
|
||||||
helpers.logprefix = 'c : '
|
helpers.logprefix = 'c : '
|
||||||
|
|
||||||
fw = FirewallClient(method_name, sudo_pythonpath, ttl)
|
fw = FirewallClient(method_name, sudo_pythonpath)
|
||||||
|
|
||||||
# If --dns is used, store the IP addresses that the client
|
# nslist is the list of name severs to intercept. If --dns is
|
||||||
# normally uses for DNS lookups in nslist. The firewall needs to
|
# used, we add all DNS servers in resolv.conf. Otherwise, the list
|
||||||
# redirect packets outgoing to this server to the remote host
|
# can be populated with the --ns-hosts option (which is already
|
||||||
|
# stored in nslist). This list is used to setup the firewall so it
|
||||||
|
# can redirect packets outgoing to this server to the remote host
|
||||||
# instead.
|
# instead.
|
||||||
if dns:
|
if dns:
|
||||||
nslist += resolvconf_nameservers(True)
|
nslist += resolvconf_nameservers(True)
|
||||||
|
|
||||||
|
# If we are intercepting DNS requests, we tell the remote host
|
||||||
|
# where it should send the DNS requests to with the --to-ns
|
||||||
|
# option.
|
||||||
|
if len(nslist) > 0:
|
||||||
if to_nameserver is not None:
|
if to_nameserver is not None:
|
||||||
to_nameserver = "%s@%s" % tuple(to_nameserver[1:])
|
to_nameserver = "%s@%s" % tuple(to_nameserver[1:])
|
||||||
else:
|
else: # if we are not intercepting DNS traffic
|
||||||
# option doesn't make sense if we aren't proxying dns
|
# ...and the user specified a server to send DNS traffic to.
|
||||||
if to_nameserver and len(to_nameserver) > 0:
|
if to_nameserver and len(to_nameserver) > 0:
|
||||||
print("WARNING: --to-ns option is ignored because --dns was not "
|
print("WARNING: --to-ns option is ignored unless "
|
||||||
"used.")
|
"--dns or --ns-hosts is used.")
|
||||||
to_nameserver = None
|
to_nameserver = None
|
||||||
|
|
||||||
# Get family specific subnet lists. Also, the user may not specify
|
# Get family specific subnet lists. Also, the user may not specify
|
||||||
@ -1024,14 +1031,14 @@ def main(listenip_v6, listenip_v4,
|
|||||||
# start the firewall
|
# start the firewall
|
||||||
fw.setup(subnets_include, subnets_exclude, nslist,
|
fw.setup(subnets_include, subnets_exclude, nslist,
|
||||||
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
|
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
|
||||||
required.udp, user, tmark, ttl)
|
required.udp, user, tmark)
|
||||||
|
|
||||||
# start the client process
|
# start the client process
|
||||||
try:
|
try:
|
||||||
return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
return _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
||||||
python, latency_control, latency_buffer_size,
|
python, latency_control, latency_buffer_size,
|
||||||
dns_listener, seed_hosts, auto_hosts, auto_nets,
|
dns_listener, seed_hosts, auto_hosts, auto_nets,
|
||||||
daemon, to_nameserver, ttl)
|
daemon, to_nameserver)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
if daemon:
|
if daemon:
|
||||||
|
@ -20,7 +20,7 @@ def main():
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
if not opt.sudoers_filename:
|
if not opt.sudoers_filename:
|
||||||
log('--sudoers-file must be set or omited.')
|
log('--sudoers-file must be set or omitted.')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
sudoers(
|
sudoers(
|
||||||
@ -43,7 +43,7 @@ def main():
|
|||||||
if opt.firewall:
|
if opt.firewall:
|
||||||
if opt.subnets or opt.subnets_file:
|
if opt.subnets or opt.subnets_file:
|
||||||
parser.error('exactly zero arguments expected')
|
parser.error('exactly zero arguments expected')
|
||||||
return firewall.main(opt.method, opt.syslog, opt.ttl)
|
return firewall.main(opt.method, opt.syslog)
|
||||||
elif opt.hostwatch:
|
elif opt.hostwatch:
|
||||||
return hostwatch.hw_main(opt.subnets, opt.auto_hosts)
|
return hostwatch.hw_main(opt.subnets, opt.auto_hosts)
|
||||||
else:
|
else:
|
||||||
@ -85,6 +85,13 @@ def main():
|
|||||||
ipport_v4 = "auto"
|
ipport_v4 = "auto"
|
||||||
# parse_ipport6('[::1]:0')
|
# parse_ipport6('[::1]:0')
|
||||||
ipport_v6 = "auto" if not opt.disable_ipv6 else None
|
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:
|
if opt.syslog:
|
||||||
ssyslog.start_syslog()
|
ssyslog.start_syslog()
|
||||||
ssyslog.close_stdin()
|
ssyslog.close_stdin()
|
||||||
@ -109,8 +116,7 @@ def main():
|
|||||||
opt.pidfile,
|
opt.pidfile,
|
||||||
opt.user,
|
opt.user,
|
||||||
opt.sudo_pythonpath,
|
opt.sudo_pythonpath,
|
||||||
opt.tmark,
|
opt.tmark)
|
||||||
opt.ttl)
|
|
||||||
|
|
||||||
if return_code == 0:
|
if return_code == 0:
|
||||||
log('Normal exit code, exiting...')
|
log('Normal exit code, exiting...')
|
||||||
|
@ -5,6 +5,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import traceback
|
import traceback
|
||||||
|
import subprocess as ssubprocess
|
||||||
|
|
||||||
import sshuttle.ssyslog as ssyslog
|
import sshuttle.ssyslog as ssyslog
|
||||||
import sshuttle.helpers as helpers
|
import sshuttle.helpers as helpers
|
||||||
@ -89,6 +90,29 @@ def subnet_weight(s):
|
|||||||
return (-s[-1] + (s[-2] or -65535), s[1], s[2])
|
return (-s[-1] + (s[-2] or -65535), s[1], s[2])
|
||||||
|
|
||||||
|
|
||||||
|
def flush_systemd_dns_cache():
|
||||||
|
# If the user is using systemd-resolve for DNS resolution, it is
|
||||||
|
# possible for the request to go through systemd-resolve before we
|
||||||
|
# see it...and it may use a cached result instead of sending a
|
||||||
|
# request that we can intercept. When sshuttle starts and stops,
|
||||||
|
# this means that we should clear the cache!
|
||||||
|
#
|
||||||
|
# The command to do this was named systemd-resolve, but changed to
|
||||||
|
# resolvectl in systemd 239.
|
||||||
|
# https://github.com/systemd/systemd/blob/f8eb41003df1a4eab59ff9bec67b2787c9368dbd/NEWS#L3816
|
||||||
|
|
||||||
|
if helpers.which("resolvectl"):
|
||||||
|
debug2("Flushing systemd's DNS resolver cache: "
|
||||||
|
"resolvectl flush-caches")
|
||||||
|
ssubprocess.Popen(["resolvectl", "flush-caches"],
|
||||||
|
stdout=ssubprocess.PIPE, env=helpers.get_env())
|
||||||
|
elif helpers.which("systemd-resolve"):
|
||||||
|
debug2("Flushing systemd's DNS resolver cache: "
|
||||||
|
"systemd-resolve --flush-caches")
|
||||||
|
ssubprocess.Popen(["systemd-resolve", "--flush-caches"],
|
||||||
|
stdout=ssubprocess.PIPE, env=helpers.get_env())
|
||||||
|
|
||||||
|
|
||||||
# This is some voodoo for setting up the kernel's transparent
|
# This is some voodoo for setting up the kernel's transparent
|
||||||
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
|
# proxying stuff. If subnets is empty, we just delete our sshuttle rules;
|
||||||
# otherwise we delete it, then make them from scratch.
|
# otherwise we delete it, then make them from scratch.
|
||||||
@ -97,7 +121,7 @@ def subnet_weight(s):
|
|||||||
# exit. In case that fails, it's not the end of the world; future runs will
|
# exit. In case that fails, it's not the end of the world; future runs will
|
||||||
# supercede it in the transproxy list, at least, so the leftover rules
|
# supercede it in the transproxy list, at least, so the leftover rules
|
||||||
# are hopefully harmless.
|
# are hopefully harmless.
|
||||||
def main(method_name, syslog, ttl):
|
def main(method_name, syslog):
|
||||||
helpers.logprefix = 'fw: '
|
helpers.logprefix = 'fw: '
|
||||||
stdin, stdout = setup_daemon()
|
stdin, stdout = setup_daemon()
|
||||||
hostmap = {}
|
hostmap = {}
|
||||||
@ -199,11 +223,12 @@ def main(method_name, syslog, ttl):
|
|||||||
raise Fatal('expected GO but got %r' % line)
|
raise Fatal('expected GO but got %r' % line)
|
||||||
|
|
||||||
_, _, args = line.partition(" ")
|
_, _, args = line.partition(" ")
|
||||||
udp, user = args.strip().split(" ", 1)
|
udp, user, tmark = args.strip().split(" ", 2)
|
||||||
udp = bool(int(udp))
|
udp = bool(int(udp))
|
||||||
if user == '-':
|
if user == '-':
|
||||||
user = None
|
user = None
|
||||||
debug2('Got udp: %r, user: %r' % (udp, user))
|
debug2('Got udp: %r, user: %r, tmark: %s' %
|
||||||
|
(udp, user, tmark))
|
||||||
|
|
||||||
subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
|
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]
|
nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
|
||||||
@ -218,15 +243,16 @@ def main(method_name, syslog, ttl):
|
|||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
port_v6, dnsport_v6, nslist_v6,
|
port_v6, dnsport_v6, nslist_v6,
|
||||||
socket.AF_INET6, subnets_v6, udp,
|
socket.AF_INET6, subnets_v6, udp,
|
||||||
user, ttl)
|
user, tmark)
|
||||||
|
|
||||||
if subnets_v4 or nslist_v4:
|
if subnets_v4 or nslist_v4:
|
||||||
debug2('setting up IPv4.')
|
debug2('setting up IPv4.')
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
port_v4, dnsport_v4, nslist_v4,
|
port_v4, dnsport_v4, nslist_v4,
|
||||||
socket.AF_INET, subnets_v4, udp,
|
socket.AF_INET, subnets_v4, udp,
|
||||||
user, ttl)
|
user, tmark)
|
||||||
|
|
||||||
|
flush_systemd_dns_cache()
|
||||||
stdout.write('STARTED\n')
|
stdout.write('STARTED\n')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -288,3 +314,12 @@ def main(method_name, syslog, ttl):
|
|||||||
debug1(traceback.format_exc())
|
debug1(traceback.format_exc())
|
||||||
except BaseException:
|
except BaseException:
|
||||||
debug2('An error occurred, ignoring it.')
|
debug2('An error occurred, ignoring it.')
|
||||||
|
|
||||||
|
try:
|
||||||
|
flush_systemd_dns_cache()
|
||||||
|
except BaseException:
|
||||||
|
try:
|
||||||
|
debug1("Error trying to flush systemd dns cache.")
|
||||||
|
debug1(traceback.format_exc())
|
||||||
|
except BaseException:
|
||||||
|
debug2("An error occurred, ignoring it.")
|
||||||
|
@ -15,9 +15,9 @@ POLL_TIME = 60 * 15
|
|||||||
NETSTAT_POLL_TIME = 30
|
NETSTAT_POLL_TIME = 30
|
||||||
CACHEFILE = os.path.expanduser('~/.sshuttle.hosts')
|
CACHEFILE = os.path.expanduser('~/.sshuttle.hosts')
|
||||||
|
|
||||||
|
# Have we already failed to write CACHEFILE?
|
||||||
|
CACHE_WRITE_FAILED = False
|
||||||
|
|
||||||
_nmb_ok = True
|
|
||||||
_smb_ok = True
|
|
||||||
hostnames = {}
|
hostnames = {}
|
||||||
queue = {}
|
queue = {}
|
||||||
try:
|
try:
|
||||||
@ -33,7 +33,10 @@ def _is_ip(s):
|
|||||||
|
|
||||||
|
|
||||||
def write_host_cache():
|
def write_host_cache():
|
||||||
|
"""If possible, write our hosts file to disk so future connections
|
||||||
|
can reuse the hosts that we already found."""
|
||||||
tmpname = '%s.%d.tmp' % (CACHEFILE, os.getpid())
|
tmpname = '%s.%d.tmp' % (CACHEFILE, os.getpid())
|
||||||
|
global CACHE_WRITE_FAILED
|
||||||
try:
|
try:
|
||||||
f = open(tmpname, 'wb')
|
f = open(tmpname, 'wb')
|
||||||
for name, ip in sorted(hostnames.items()):
|
for name, ip in sorted(hostnames.items()):
|
||||||
@ -41,7 +44,15 @@ def write_host_cache():
|
|||||||
f.close()
|
f.close()
|
||||||
os.chmod(tmpname, 384) # 600 in octal, 'rw-------'
|
os.chmod(tmpname, 384) # 600 in octal, 'rw-------'
|
||||||
os.rename(tmpname, CACHEFILE)
|
os.rename(tmpname, CACHEFILE)
|
||||||
finally:
|
CACHE_WRITE_FAILED = False
|
||||||
|
except (OSError, IOError):
|
||||||
|
# Write message if we haven't yet or if we get a failure after
|
||||||
|
# a previous success.
|
||||||
|
if not CACHE_WRITE_FAILED:
|
||||||
|
log("Failed to write host cache to temporary file "
|
||||||
|
"%s and rename it to %s" % (tmpname, CACHEFILE))
|
||||||
|
CACHE_WRITE_FAILED = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.unlink(tmpname)
|
os.unlink(tmpname)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
@ -49,25 +60,34 @@ def write_host_cache():
|
|||||||
|
|
||||||
|
|
||||||
def read_host_cache():
|
def read_host_cache():
|
||||||
|
"""If possible, read the cache file from disk to populate hosts that
|
||||||
|
were found in a previous sshuttle run."""
|
||||||
try:
|
try:
|
||||||
f = open(CACHEFILE)
|
f = open(CACHEFILE)
|
||||||
except IOError:
|
except (OSError, IOError):
|
||||||
_, e = sys.exc_info()[:2]
|
_, e = sys.exc_info()[:2]
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise
|
log("Failed to read existing host cache file %s on remote host"
|
||||||
|
% CACHEFILE)
|
||||||
|
return
|
||||||
for line in f:
|
for line in f:
|
||||||
words = line.strip().split(',')
|
words = line.strip().split(',')
|
||||||
if len(words) == 2:
|
if len(words) == 2:
|
||||||
(name, ip) = words
|
(name, ip) = words
|
||||||
name = re.sub(r'[^-\w\.]', '-', name).strip()
|
name = re.sub(r'[^-\w\.]', '-', name).strip()
|
||||||
|
# Remove characters that shouldn't be in IP
|
||||||
ip = re.sub(r'[^0-9.]', '', ip).strip()
|
ip = re.sub(r'[^0-9.]', '', ip).strip()
|
||||||
if name and ip:
|
if name and ip:
|
||||||
found_host(name, ip)
|
found_host(name, ip)
|
||||||
|
|
||||||
|
|
||||||
def found_host(name, ip):
|
def found_host(name, ip):
|
||||||
|
"""The provided name maps to the given IP. Add the host to the
|
||||||
|
hostnames list, send the host to the sshuttle client via
|
||||||
|
stdout, and write the host to the cache file.
|
||||||
|
"""
|
||||||
hostname = re.sub(r'\..*', '', name)
|
hostname = re.sub(r'\..*', '', name)
|
||||||
hostname = re.sub(r'[^-\w\.]', '_', hostname)
|
hostname = re.sub(r'[^-\w\.]', '_', hostname)
|
||||||
if (ip.startswith('127.') or ip.startswith('255.') or
|
if (ip.startswith('127.') or ip.startswith('255.') or
|
||||||
@ -86,29 +106,37 @@ def found_host(name, ip):
|
|||||||
|
|
||||||
|
|
||||||
def _check_etc_hosts():
|
def _check_etc_hosts():
|
||||||
debug2(' > hosts')
|
"""If possible, read /etc/hosts to find hosts."""
|
||||||
for line in open('/etc/hosts'):
|
filename = '/etc/hosts'
|
||||||
line = re.sub(r'#.*', '', line)
|
debug2(' > Reading %s on remote host' % filename)
|
||||||
words = line.strip().split()
|
try:
|
||||||
if not words:
|
for line in open(filename):
|
||||||
continue
|
line = re.sub(r'#.*', '', line) # remove comments
|
||||||
ip = words[0]
|
words = line.strip().split()
|
||||||
names = words[1:]
|
if not words:
|
||||||
if _is_ip(ip):
|
continue
|
||||||
debug3('< %s %r' % (ip, names))
|
ip = words[0]
|
||||||
for n in names:
|
if _is_ip(ip):
|
||||||
check_host(n)
|
names = words[1:]
|
||||||
found_host(n, ip)
|
debug3('< %s %r' % (ip, names))
|
||||||
|
for n in names:
|
||||||
|
check_host(n)
|
||||||
|
found_host(n, ip)
|
||||||
|
except (OSError, IOError):
|
||||||
|
debug1("Failed to read %s on remote host" % filename)
|
||||||
|
|
||||||
|
|
||||||
def _check_revdns(ip):
|
def _check_revdns(ip):
|
||||||
|
"""Use reverse DNS to try to get hostnames from an IP addresses."""
|
||||||
debug2(' > rev: %s' % ip)
|
debug2(' > rev: %s' % ip)
|
||||||
try:
|
try:
|
||||||
r = socket.gethostbyaddr(ip)
|
r = socket.gethostbyaddr(ip)
|
||||||
debug3('< %s' % r[0])
|
debug3('< %s' % r[0])
|
||||||
check_host(r[0])
|
check_host(r[0])
|
||||||
found_host(r[0], ip)
|
found_host(r[0], ip)
|
||||||
except (socket.herror, UnicodeError):
|
except (OSError, socket.error, UnicodeError):
|
||||||
|
# This case is expected to occur regularly.
|
||||||
|
# debug3('< %s gethostbyaddr failed on remote host' % ip)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -136,115 +164,23 @@ def _check_netstat():
|
|||||||
log('%r failed: %r' % (argv, e))
|
log('%r failed: %r' % (argv, e))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# The same IPs may appear multiple times. Consolidate them so the
|
||||||
|
# debug message doesn't print the same IP repeatedly.
|
||||||
|
ip_list = []
|
||||||
for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content):
|
for ip in re.findall(r'\d+\.\d+\.\d+\.\d+', content):
|
||||||
|
if ip not in ip_list:
|
||||||
|
ip_list.append(ip)
|
||||||
|
|
||||||
|
for ip in sorted(ip_list):
|
||||||
debug3('< %s' % ip)
|
debug3('< %s' % ip)
|
||||||
check_host(ip)
|
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):
|
def check_host(hostname):
|
||||||
if _is_ip(hostname):
|
if _is_ip(hostname):
|
||||||
_enqueue(_check_revdns, hostname)
|
_enqueue(_check_revdns, hostname)
|
||||||
else:
|
else:
|
||||||
_enqueue(_check_dns, hostname)
|
_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):
|
def _enqueue(op, *args):
|
||||||
@ -277,18 +213,22 @@ def hw_main(seed_hosts, auto_hosts):
|
|||||||
_enqueue(_check_netstat)
|
_enqueue(_check_netstat)
|
||||||
check_host('localhost')
|
check_host('localhost')
|
||||||
check_host(socket.gethostname())
|
check_host(socket.gethostname())
|
||||||
check_workgroup('workgroup')
|
|
||||||
check_workgroup('-')
|
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
# For each item in the queue
|
||||||
for t, last_polled in list(queue.items()):
|
for t, last_polled in list(queue.items()):
|
||||||
(op, args) = t
|
(op, args) = t
|
||||||
if not _stdin_still_ok(0):
|
if not _stdin_still_ok(0):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Determine if we need to run.
|
||||||
maxtime = POLL_TIME
|
maxtime = POLL_TIME
|
||||||
|
# netstat runs more often than other jobs
|
||||||
if op == _check_netstat:
|
if op == _check_netstat:
|
||||||
maxtime = NETSTAT_POLL_TIME
|
maxtime = NETSTAT_POLL_TIME
|
||||||
|
|
||||||
|
# Check if this jobs needs to run.
|
||||||
if now - last_polled > maxtime:
|
if now - last_polled > maxtime:
|
||||||
queue[t] = time.time()
|
queue[t] = time.time()
|
||||||
op(*args)
|
op(*args)
|
||||||
@ -298,5 +238,5 @@ def hw_main(seed_hosts, auto_hosts):
|
|||||||
break
|
break
|
||||||
|
|
||||||
# FIXME: use a smarter timeout based on oldest last_polled
|
# FIXME: use a smarter timeout based on oldest last_polled
|
||||||
if not _stdin_still_ok(1):
|
if not _stdin_still_ok(1): # sleeps for up to 1 second
|
||||||
break
|
break
|
||||||
|
@ -17,7 +17,7 @@ def ipt_chain_exists(family, table, name):
|
|||||||
cmd = 'iptables'
|
cmd = 'iptables'
|
||||||
else:
|
else:
|
||||||
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
||||||
argv = [cmd, '-t', table, '-nL']
|
argv = [cmd, '-w', '-t', table, '-nL']
|
||||||
try:
|
try:
|
||||||
output = ssubprocess.check_output(argv, env=get_env())
|
output = ssubprocess.check_output(argv, env=get_env())
|
||||||
for line in output.decode('ASCII').split('\n'):
|
for line in output.decode('ASCII').split('\n'):
|
||||||
@ -29,9 +29,9 @@ def ipt_chain_exists(family, table, name):
|
|||||||
|
|
||||||
def ipt(family, table, *args):
|
def ipt(family, table, *args):
|
||||||
if family == socket.AF_INET6:
|
if family == socket.AF_INET6:
|
||||||
argv = ['ip6tables', '-t', table] + list(args)
|
argv = ['ip6tables', '-w', '-t', table] + list(args)
|
||||||
elif family == socket.AF_INET:
|
elif family == socket.AF_INET:
|
||||||
argv = ['iptables', '-t', table] + list(args)
|
argv = ['iptables', '-w', '-t', table] + list(args)
|
||||||
else:
|
else:
|
||||||
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
raise Exception('Unsupported family "%s"' % family_to_string(family))
|
||||||
debug1('%s' % ' '.join(argv))
|
debug1('%s' % ' '.join(argv))
|
||||||
@ -49,25 +49,3 @@ def nft(family, table, action, *args):
|
|||||||
rv = ssubprocess.call(argv, env=get_env())
|
rv = ssubprocess.call(argv, env=get_env())
|
||||||
if rv:
|
if rv:
|
||||||
raise Fatal('%r returned %d' % (argv, rv))
|
raise Fatal('%r returned %d' % (argv, rv))
|
||||||
|
|
||||||
|
|
||||||
_no_ttl_module = False
|
|
||||||
|
|
||||||
|
|
||||||
def ipt_ttl(family, *args):
|
|
||||||
global _no_ttl_module
|
|
||||||
if not _no_ttl_module:
|
|
||||||
# we avoid infinite loops by generating server-side connections
|
|
||||||
# with ttl 63. This makes the client side not recapture those
|
|
||||||
# connections, in case client == server.
|
|
||||||
try:
|
|
||||||
argsplus = list(args)
|
|
||||||
ipt(family, *argsplus)
|
|
||||||
except Fatal:
|
|
||||||
ipt(family, *args)
|
|
||||||
# we only get here if the non-ttl attempt succeeds
|
|
||||||
log('WARNING: your iptables is missing '
|
|
||||||
'the ttl module.')
|
|
||||||
_no_ttl_module = True
|
|
||||||
else:
|
|
||||||
ipt(family, *args)
|
|
||||||
|
@ -91,7 +91,7 @@ class BaseMethod(object):
|
|||||||
(key, self.name))
|
(key, self.name))
|
||||||
|
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user):
|
user, tmark):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def restore_firewall(self, port, family, udp, user):
|
def restore_firewall(self, port, family, udp, user):
|
||||||
|
@ -177,7 +177,6 @@ class Method(BaseMethod):
|
|||||||
sender.setsockopt(socket.SOL_IP, IP_BINDANY, 1)
|
sender.setsockopt(socket.SOL_IP, IP_BINDANY, 1)
|
||||||
sender.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
sender.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
sender.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
sender.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||||
sender.setsockopt(socket.SOL_IP, socket.IP_TTL, 63)
|
|
||||||
sender.bind(srcip)
|
sender.bind(srcip)
|
||||||
sender.sendto(data, dstip)
|
sender.sendto(data, dstip)
|
||||||
sender.close()
|
sender.close()
|
||||||
@ -189,7 +188,12 @@ class Method(BaseMethod):
|
|||||||
# udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1)
|
# udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1)
|
||||||
|
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user, ttl):
|
user, tmark):
|
||||||
|
# TODO: The ttl hack to allow the host and server to run on
|
||||||
|
# the same machine has been removed but this method hasn't
|
||||||
|
# been updated yet.
|
||||||
|
ttl = 63
|
||||||
|
|
||||||
# IPv6 not supported
|
# IPv6 not supported
|
||||||
if family not in [socket.AF_INET]:
|
if family not in [socket.AF_INET]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import socket
|
import socket
|
||||||
from sshuttle.firewall import subnet_weight
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import family_to_string, which, debug2
|
from sshuttle.helpers import family_to_string, which, debug2
|
||||||
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists, nonfatal
|
from sshuttle.linux import ipt, ipt_chain_exists, nonfatal
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
|
|
||||||
|
|
||||||
@ -13,23 +13,18 @@ class Method(BaseMethod):
|
|||||||
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
||||||
# "-A OUTPUT").
|
# "-A OUTPUT").
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user, ttl):
|
user, tmark):
|
||||||
# only ipv4 supported with NAT
|
if family != socket.AF_INET and family != socket.AF_INET6:
|
||||||
if family != socket.AF_INET:
|
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by nat method_name'
|
'Address family "%s" unsupported by nat method_name'
|
||||||
% family_to_string(family))
|
% family_to_string(family))
|
||||||
if udp:
|
if udp:
|
||||||
raise Exception("UDP not supported by nat method_name")
|
raise Exception("UDP not supported by nat method_name")
|
||||||
|
|
||||||
table = "nat"
|
table = "nat"
|
||||||
|
|
||||||
def _ipt(*args):
|
def _ipt(*args):
|
||||||
return ipt(family, table, *args)
|
return ipt(family, table, *args)
|
||||||
|
|
||||||
def _ipt_ttl(*args):
|
|
||||||
return ipt_ttl(family, table, *args)
|
|
||||||
|
|
||||||
def _ipm(*args):
|
def _ipm(*args):
|
||||||
return ipt(family, "mangle", *args)
|
return ipt(family, "mangle", *args)
|
||||||
|
|
||||||
@ -50,16 +45,11 @@ class Method(BaseMethod):
|
|||||||
_ipt('-I', 'OUTPUT', '1', *args)
|
_ipt('-I', 'OUTPUT', '1', *args)
|
||||||
_ipt('-I', 'PREROUTING', '1', *args)
|
_ipt('-I', 'PREROUTING', '1', *args)
|
||||||
|
|
||||||
# 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.
|
|
||||||
_ipt_ttl('-A', chain, '-j', 'RETURN', '-m', 'ttl', '--ttl', '%s' % ttl)
|
|
||||||
|
|
||||||
# Redirect DNS traffic as requested. This includes routing traffic
|
# Redirect DNS traffic as requested. This includes routing traffic
|
||||||
# to localhost DNS servers through sshuttle.
|
# to localhost DNS servers through sshuttle.
|
||||||
for _, ip in [i for i in nslist if i[0] == family]:
|
for _, ip in [i for i in nslist if i[0] == family]:
|
||||||
_ipt('-A', chain, '-j', 'REDIRECT',
|
_ipt('-A', chain, '-j', 'REDIRECT',
|
||||||
'--dest', '%s/32' % ip,
|
'--dest', '%s' % ip,
|
||||||
'-p', 'udp',
|
'-p', 'udp',
|
||||||
'--dport', '53',
|
'--dport', '53',
|
||||||
'--to-ports', str(dnsport))
|
'--to-ports', str(dnsport))
|
||||||
@ -87,7 +77,7 @@ class Method(BaseMethod):
|
|||||||
|
|
||||||
def restore_firewall(self, port, family, udp, user):
|
def restore_firewall(self, port, family, udp, user):
|
||||||
# only ipv4 supported with NAT
|
# only ipv4 supported with NAT
|
||||||
if family != socket.AF_INET:
|
if family != socket.AF_INET and family != socket.AF_INET6:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by nat method_name'
|
'Address family "%s" unsupported by nat method_name'
|
||||||
% family_to_string(family))
|
% family_to_string(family))
|
||||||
@ -99,9 +89,6 @@ class Method(BaseMethod):
|
|||||||
def _ipt(*args):
|
def _ipt(*args):
|
||||||
return ipt(family, table, *args)
|
return ipt(family, table, *args)
|
||||||
|
|
||||||
def _ipt_ttl(*args):
|
|
||||||
return ipt_ttl(family, table, *args)
|
|
||||||
|
|
||||||
def _ipm(*args):
|
def _ipm(*args):
|
||||||
return ipt(family, "mangle", *args)
|
return ipt(family, "mangle", *args)
|
||||||
|
|
||||||
@ -123,6 +110,7 @@ class Method(BaseMethod):
|
|||||||
def get_supported_features(self):
|
def get_supported_features(self):
|
||||||
result = super(Method, self).get_supported_features()
|
result = super(Method, self).get_supported_features()
|
||||||
result.user = True
|
result.user = True
|
||||||
|
result.ipv6 = True
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def is_supported(self):
|
def is_supported(self):
|
||||||
|
@ -13,7 +13,7 @@ class Method(BaseMethod):
|
|||||||
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
||||||
# "-A OUTPUT").
|
# "-A OUTPUT").
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user, ttl):
|
user, tmark):
|
||||||
if udp:
|
if udp:
|
||||||
raise Exception("UDP not supported by nft")
|
raise Exception("UDP not supported by nft")
|
||||||
|
|
||||||
@ -45,14 +45,6 @@ class Method(BaseMethod):
|
|||||||
else:
|
else:
|
||||||
_nft('add rule', chain, 'meta', 'nfproto', '!=', 'ipv6', 'return')
|
_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.
|
|
||||||
if family == socket.AF_INET:
|
|
||||||
_nft('add rule', chain, 'ip ttl == 63 return')
|
|
||||||
elif family == socket.AF_INET6:
|
|
||||||
_nft('add rule', chain, 'ip6 hoplimit == 63 return')
|
|
||||||
|
|
||||||
# Strings to use below to simplify our code
|
# Strings to use below to simplify our code
|
||||||
if family == socket.AF_INET:
|
if family == socket.AF_INET:
|
||||||
ip_version_l = 'ipv4'
|
ip_version_l = 'ipv4'
|
||||||
|
@ -11,8 +11,8 @@ from fcntl import ioctl
|
|||||||
from ctypes import c_char, c_uint8, c_uint16, c_uint32, Union, Structure, \
|
from ctypes import c_char, c_uint8, c_uint16, c_uint32, Union, Structure, \
|
||||||
sizeof, addressof, memmove
|
sizeof, addressof, memmove
|
||||||
from sshuttle.firewall import subnet_weight
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import debug1, debug2, debug3, Fatal, family_to_string, \
|
from sshuttle.helpers import log, debug1, debug2, debug3, Fatal, \
|
||||||
get_env, which
|
family_to_string, get_env, which
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
|
|
||||||
|
|
||||||
@ -393,6 +393,10 @@ def pfctl(args, stdin=None):
|
|||||||
env=get_env())
|
env=get_env())
|
||||||
o = p.communicate(stdin)
|
o = p.communicate(stdin)
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
|
log('%r returned %d, stdout and stderr follows: ' %
|
||||||
|
(argv, p.returncode))
|
||||||
|
log("stdout:\n%s" % o[0].decode("ascii"))
|
||||||
|
log("stderr:\n%s" % o[1].decode("ascii"))
|
||||||
raise Fatal('%r returned %d' % (argv, p.returncode))
|
raise Fatal('%r returned %d' % (argv, p.returncode))
|
||||||
|
|
||||||
return o
|
return o
|
||||||
@ -444,7 +448,7 @@ class Method(BaseMethod):
|
|||||||
return sock.getsockname()
|
return sock.getsockname()
|
||||||
|
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user, ttl):
|
user, tmark):
|
||||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by pf method_name'
|
'Address family "%s" unsupported by pf method_name'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import struct
|
import struct
|
||||||
from sshuttle.firewall import subnet_weight
|
from sshuttle.firewall import subnet_weight
|
||||||
from sshuttle.helpers import family_to_string
|
from sshuttle.helpers import family_to_string
|
||||||
from sshuttle.linux import ipt, ipt_ttl, ipt_chain_exists
|
from sshuttle.linux import ipt, ipt_chain_exists
|
||||||
from sshuttle.methods import BaseMethod
|
from sshuttle.methods import BaseMethod
|
||||||
from sshuttle.helpers import debug1, debug2, debug3, Fatal, which
|
from sshuttle.helpers import debug1, debug2, debug3, Fatal, which
|
||||||
|
|
||||||
@ -151,17 +151,7 @@ class Method(BaseMethod):
|
|||||||
udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
|
udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
|
||||||
|
|
||||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||||
user, ttl):
|
user, tmark):
|
||||||
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):
|
|
||||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by tproxy method'
|
'Address family "%s" unsupported by tproxy method'
|
||||||
@ -172,9 +162,6 @@ class Method(BaseMethod):
|
|||||||
def _ipt(*args):
|
def _ipt(*args):
|
||||||
return ipt(family, table, *args)
|
return ipt(family, table, *args)
|
||||||
|
|
||||||
def _ipt_ttl(*args):
|
|
||||||
return ipt_ttl(family, table, *args)
|
|
||||||
|
|
||||||
def _ipt_proto_ports(proto, fport, lport):
|
def _ipt_proto_ports(proto, fport, lport):
|
||||||
return proto + ('--dport', '%d:%d' % (fport, lport)) \
|
return proto + ('--dport', '%d:%d' % (fport, lport)) \
|
||||||
if fport else proto
|
if fport else proto
|
||||||
@ -192,8 +179,8 @@ class Method(BaseMethod):
|
|||||||
_ipt('-F', divert_chain)
|
_ipt('-F', divert_chain)
|
||||||
_ipt('-N', tproxy_chain)
|
_ipt('-N', tproxy_chain)
|
||||||
_ipt('-F', tproxy_chain)
|
_ipt('-F', tproxy_chain)
|
||||||
_ipt('-I', 'OUTPUT', tmark, '-j', mark_chain)
|
_ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
|
||||||
_ipt('-I', 'PREROUTING', tmark, '-j', tproxy_chain)
|
_ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
|
||||||
|
|
||||||
# Don't have packets sent to any of our local IP addresses go
|
# Don't have packets sent to any of our local IP addresses go
|
||||||
# through the tproxy or mark chains.
|
# through the tproxy or mark chains.
|
||||||
@ -224,7 +211,7 @@ class Method(BaseMethod):
|
|||||||
'--dest', '%s/32' % ip,
|
'--dest', '%s/32' % ip,
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53')
|
'-m', 'udp', '-p', 'udp', '--dport', '53')
|
||||||
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x'+tmark+'/0x'+tmark,
|
'--tproxy-mark', tmark,
|
||||||
'--dest', '%s/32' % ip,
|
'--dest', '%s/32' % ip,
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53',
|
'-m', 'udp', '-p', 'udp', '--dport', '53',
|
||||||
'--on-port', str(dnsport))
|
'--on-port', str(dnsport))
|
||||||
@ -249,7 +236,7 @@ class Method(BaseMethod):
|
|||||||
'-m', 'tcp',
|
'-m', 'tcp',
|
||||||
*tcp_ports)
|
*tcp_ports)
|
||||||
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x'+tmark+'/0x'+tmark,
|
'--tproxy-mark', tmark,
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'tcp',
|
'-m', 'tcp',
|
||||||
*(tcp_ports + ('--on-port', str(port))))
|
*(tcp_ports + ('--on-port', str(port))))
|
||||||
@ -273,7 +260,7 @@ class Method(BaseMethod):
|
|||||||
'-m', 'udp',
|
'-m', 'udp',
|
||||||
*udp_ports)
|
*udp_ports)
|
||||||
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x'+tmark+'/0x'+tmark,
|
'--tproxy-mark', tmark,
|
||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
'-m', 'udp',
|
'-m', 'udp',
|
||||||
*(udp_ports + ('--on-port', str(port))))
|
*(udp_ports + ('--on-port', str(port))))
|
||||||
@ -289,9 +276,6 @@ class Method(BaseMethod):
|
|||||||
def _ipt(*args):
|
def _ipt(*args):
|
||||||
return ipt(family, table, *args)
|
return ipt(family, table, *args)
|
||||||
|
|
||||||
def _ipt_ttl(*args):
|
|
||||||
return ipt_ttl(family, table, *args)
|
|
||||||
|
|
||||||
mark_chain = 'sshuttle-m-%s' % port
|
mark_chain = 'sshuttle-m-%s' % port
|
||||||
tproxy_chain = 'sshuttle-t-%s' % port
|
tproxy_chain = 'sshuttle-t-%s' % port
|
||||||
divert_chain = 'sshuttle-d-%s' % port
|
divert_chain = 'sshuttle-d-%s' % port
|
||||||
|
@ -132,6 +132,7 @@ def parse_ipport(s):
|
|||||||
|
|
||||||
|
|
||||||
def parse_list(lst):
|
def parse_list(lst):
|
||||||
|
"""Parse a comma separated string into a list."""
|
||||||
return re.split(r'[\s,]+', lst.strip()) if lst else []
|
return re.split(r'[\s,]+', lst.strip()) if lst else []
|
||||||
|
|
||||||
|
|
||||||
@ -172,7 +173,7 @@ class MyArgumentParser(ArgumentParser):
|
|||||||
|
|
||||||
parser = MyArgumentParser(
|
parser = MyArgumentParser(
|
||||||
prog="sshuttle",
|
prog="sshuttle",
|
||||||
usage="%(prog)s [-l [ip:]port] [-r [user@]sshserver[:port]] <subnets...>",
|
usage="%(prog)s [-l [ip:]port] -r [user@]sshserver[:port] <subnets...>",
|
||||||
fromfile_prefix_chars="@"
|
fromfile_prefix_chars="@"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -220,6 +221,7 @@ parser.add_argument(
|
|||||||
type=parse_list,
|
type=parse_list,
|
||||||
help="""
|
help="""
|
||||||
capture and forward DNS requests made to the following servers
|
capture and forward DNS requests made to the following servers
|
||||||
|
(comma separated)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -280,7 +282,7 @@ parser.add_argument(
|
|||||||
action="count",
|
action="count",
|
||||||
default=0,
|
default=0,
|
||||||
help="""
|
help="""
|
||||||
increase debug message verbosity
|
increase debug message verbosity (can be used more than once)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -387,14 +389,6 @@ parser.add_argument(
|
|||||||
(internal use only)
|
(internal use only)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--ttl",
|
|
||||||
default="63",
|
|
||||||
help="""
|
|
||||||
Override the TTL for the connections made by the sshuttle server.
|
|
||||||
Default is 63.
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--hostwatch",
|
"--hostwatch",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@ -444,8 +438,9 @@ parser.add_argument(
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-t", "--tmark",
|
"-t", "--tmark",
|
||||||
metavar="[MARK]",
|
metavar="[MARK]",
|
||||||
default="1",
|
default="0x01",
|
||||||
help="""
|
help="""
|
||||||
transproxy optional traffic mark with provided MARK value
|
tproxy optional traffic mark with provided MARK value in
|
||||||
|
hexadecimal (default '0x01')
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -154,7 +154,7 @@ class Hostwatch:
|
|||||||
|
|
||||||
class DnsProxy(Handler):
|
class DnsProxy(Handler):
|
||||||
|
|
||||||
def __init__(self, mux, chan, request, to_nameserver, ttl):
|
def __init__(self, mux, chan, request, to_nameserver):
|
||||||
Handler.__init__(self, [])
|
Handler.__init__(self, [])
|
||||||
self.timeout = time.time() + 30
|
self.timeout = time.time() + 30
|
||||||
self.mux = mux
|
self.mux = mux
|
||||||
@ -164,7 +164,6 @@ class DnsProxy(Handler):
|
|||||||
self.peers = {}
|
self.peers = {}
|
||||||
self.to_ns_peer = None
|
self.to_ns_peer = None
|
||||||
self.to_ns_port = None
|
self.to_ns_port = None
|
||||||
self.ttl = ttl
|
|
||||||
if to_nameserver is None:
|
if to_nameserver is None:
|
||||||
self.to_nameserver = None
|
self.to_nameserver = None
|
||||||
else:
|
else:
|
||||||
@ -194,7 +193,6 @@ class DnsProxy(Handler):
|
|||||||
|
|
||||||
family, sockaddr = self._addrinfo(peer, port)
|
family, sockaddr = self._addrinfo(peer, port)
|
||||||
sock = socket.socket(family, socket.SOCK_DGRAM)
|
sock = socket.socket(family, socket.SOCK_DGRAM)
|
||||||
sock.setsockopt(socket.SOL_IP, socket.IP_TTL, self.ttl)
|
|
||||||
sock.connect(sockaddr)
|
sock.connect(sockaddr)
|
||||||
|
|
||||||
self.peers[sock] = peer
|
self.peers[sock] = peer
|
||||||
@ -243,15 +241,13 @@ class DnsProxy(Handler):
|
|||||||
|
|
||||||
class UdpProxy(Handler):
|
class UdpProxy(Handler):
|
||||||
|
|
||||||
def __init__(self, mux, chan, family, ttl):
|
def __init__(self, mux, chan, family):
|
||||||
sock = socket.socket(family, socket.SOCK_DGRAM)
|
sock = socket.socket(family, socket.SOCK_DGRAM)
|
||||||
Handler.__init__(self, [sock])
|
Handler.__init__(self, [sock])
|
||||||
self.timeout = time.time() + 30
|
self.timeout = time.time() + 30
|
||||||
self.mux = mux
|
self.mux = mux
|
||||||
self.chan = chan
|
self.chan = chan
|
||||||
self.sock = sock
|
self.sock = sock
|
||||||
if family == socket.AF_INET:
|
|
||||||
self.sock.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
|
|
||||||
|
|
||||||
def send(self, dstip, data):
|
def send(self, dstip, data):
|
||||||
debug2('UDP: sending to %r port %d' % dstip)
|
debug2('UDP: sending to %r port %d' % dstip)
|
||||||
@ -275,7 +271,7 @@ class UdpProxy(Handler):
|
|||||||
|
|
||||||
|
|
||||||
def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver,
|
def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver,
|
||||||
auto_nets, ttl, localhost_detector):
|
auto_nets, localhost_detector):
|
||||||
try:
|
try:
|
||||||
helpers.logprefix = ' s: '
|
helpers.logprefix = ' s: '
|
||||||
debug1('Starting server with Python version %s'
|
debug1('Starting server with Python version %s'
|
||||||
@ -362,7 +358,7 @@ def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver,
|
|||||||
|
|
||||||
def dns_req(channel, data):
|
def dns_req(channel, data):
|
||||||
debug2('Incoming DNS request channel=%d.' % channel)
|
debug2('Incoming DNS request channel=%d.' % channel)
|
||||||
h = DnsProxy(mux, channel, data, to_nameserver, ttl)
|
h = DnsProxy(mux, channel, data, to_nameserver)
|
||||||
handlers.append(h)
|
handlers.append(h)
|
||||||
dnshandlers[channel] = h
|
dnshandlers[channel] = h
|
||||||
mux.got_dns_req = dns_req
|
mux.got_dns_req = dns_req
|
||||||
@ -393,7 +389,7 @@ def main(latency_control, latency_buffer_size, auto_hosts, to_nameserver,
|
|||||||
raise Fatal('UDP connection channel %d already open' %
|
raise Fatal('UDP connection channel %d already open' %
|
||||||
channel)
|
channel)
|
||||||
else:
|
else:
|
||||||
h = UdpProxy(mux, channel, family, ttl)
|
h = UdpProxy(mux, channel, family)
|
||||||
handlers.append(h)
|
handlers.append(h)
|
||||||
udphandlers[channel] = h
|
udphandlers[channel] = h
|
||||||
mux.got_udp_open = udp_open
|
mux.got_udp_open = udp_open
|
||||||
|
@ -586,7 +586,7 @@ class MuxWrapper(SockWrapper):
|
|||||||
def connect_dst(family, ip, port):
|
def connect_dst(family, ip, port):
|
||||||
debug2('Connecting to %s:%d' % (ip, port))
|
debug2('Connecting to %s:%d' % (ip, port))
|
||||||
outsock = socket.socket(family)
|
outsock = socket.socket(family)
|
||||||
outsock.setsockopt(socket.SOL_IP, socket.IP_TTL, 63)
|
|
||||||
return SockWrapper(outsock, outsock,
|
return SockWrapper(outsock, outsock,
|
||||||
connect_to=(ip, port),
|
connect_to=(ip, port),
|
||||||
peername='%s:%d' % (ip, port))
|
peername='%s:%d' % (ip, port))
|
||||||
|
@ -19,9 +19,14 @@ Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
|||||||
%(user_name)s ALL=NOPASSWD: %(ca)s
|
%(user_name)s ALL=NOPASSWD: %(ca)s
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
warning_msg = "# WARNING: When you allow a user to run sshuttle as root,\n" \
|
||||||
|
"# they can then use sshuttle's --ssh-cmd option to run any\n" \
|
||||||
|
"# command as root.\n"
|
||||||
|
|
||||||
|
|
||||||
def build_config(user_name):
|
def build_config(user_name):
|
||||||
content = template % {
|
content = warning_msg
|
||||||
|
content += template % {
|
||||||
'ca': command_alias,
|
'ca': command_alias,
|
||||||
'dist_packages': path_to_dist_packages,
|
'dist_packages': path_to_dist_packages,
|
||||||
'py': sys.executable,
|
'py': sys.executable,
|
||||||
@ -42,6 +47,7 @@ def save_config(content, file_name):
|
|||||||
process.stdin.write(content.encode())
|
process.stdin.write(content.encode())
|
||||||
|
|
||||||
streamdata = process.communicate()[0]
|
streamdata = process.communicate()[0]
|
||||||
|
sys.stdout.write(streamdata.decode("ASCII"))
|
||||||
returncode = process.returncode
|
returncode = process.returncode
|
||||||
|
|
||||||
if returncode:
|
if returncode:
|
||||||
@ -61,4 +67,5 @@ def sudoers(user_name=None, no_modify=None, file_name=None):
|
|||||||
sys.stdout.write(content)
|
sys.stdout.write(content)
|
||||||
exit(0)
|
exit(0)
|
||||||
else:
|
else:
|
||||||
|
sys.stdout.write(warning_msg)
|
||||||
save_config(content, file_name)
|
save_config(content, file_name)
|
||||||
|
@ -15,7 +15,7 @@ NSLIST
|
|||||||
{inet},1.2.3.33
|
{inet},1.2.3.33
|
||||||
{inet6},2404:6800:4004:80c::33
|
{inet6},2404:6800:4004:80c::33
|
||||||
PORTS 1024,1025,1026,1027
|
PORTS 1024,1025,1026,1027
|
||||||
GO 1 -
|
GO 1 - 0x01
|
||||||
HOST 1.2.3.3,existing
|
HOST 1.2.3.3,existing
|
||||||
""".format(inet=AF_INET, inet6=AF_INET6))
|
""".format(inet=AF_INET, inet6=AF_INET6))
|
||||||
stdout = Mock()
|
stdout = Mock()
|
||||||
@ -100,7 +100,7 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
|
|||||||
mock_get_method("not_auto").name = "test"
|
mock_get_method("not_auto").name = "test"
|
||||||
mock_get_method.reset_mock()
|
mock_get_method.reset_mock()
|
||||||
|
|
||||||
sshuttle.firewall.main("not_auto", False, 63)
|
sshuttle.firewall.main("not_auto", False)
|
||||||
|
|
||||||
assert mock_rewrite_etc_hosts.mock_calls == [
|
assert mock_rewrite_etc_hosts.mock_calls == [
|
||||||
call({'1.2.3.3': 'existing'}, 1024),
|
call({'1.2.3.3': 'existing'}, 1024),
|
||||||
@ -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)],
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63),
|
'0x01'),
|
||||||
call().setup_firewall(
|
call().setup_firewall(
|
||||||
1025, 1027,
|
1025, 1027,
|
||||||
[(AF_INET, u'1.2.3.33')],
|
[(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)],
|
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63),
|
'0x01'),
|
||||||
call().restore_firewall(1024, AF_INET6, True, None),
|
call().restore_firewall(1024, AF_INET6, True, None),
|
||||||
call().restore_firewall(1025, AF_INET, True, None),
|
call().restore_firewall(1025, AF_INET, True, None),
|
||||||
]
|
]
|
||||||
|
@ -11,7 +11,7 @@ from sshuttle.methods import get_method
|
|||||||
def test_get_supported_features():
|
def test_get_supported_features():
|
||||||
method = get_method('nat')
|
method = get_method('nat')
|
||||||
features = method.get_supported_features()
|
features = method.get_supported_features()
|
||||||
assert not features.ipv6
|
assert features.ipv6
|
||||||
assert not features.udp
|
assert not features.udp
|
||||||
assert features.dns
|
assert features.dns
|
||||||
|
|
||||||
@ -85,27 +85,52 @@ def test_firewall_command():
|
|||||||
|
|
||||||
|
|
||||||
@patch('sshuttle.methods.nat.ipt')
|
@patch('sshuttle.methods.nat.ipt')
|
||||||
@patch('sshuttle.methods.nat.ipt_ttl')
|
|
||||||
@patch('sshuttle.methods.nat.ipt_chain_exists')
|
@patch('sshuttle.methods.nat.ipt_chain_exists')
|
||||||
def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
def test_setup_firewall(mock_ipt_chain_exists, mock_ipt):
|
||||||
mock_ipt_chain_exists.return_value = True
|
mock_ipt_chain_exists.return_value = True
|
||||||
method = get_method('nat')
|
method = get_method('nat')
|
||||||
assert method.name == 'nat'
|
assert method.name == 'nat'
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
|
||||||
method.setup_firewall(
|
|
||||||
1024, 1026,
|
|
||||||
[(AF_INET6, u'2404:6800:4004:80c::33')],
|
|
||||||
AF_INET6,
|
|
||||||
[(AF_INET6, 64, False, u'2404:6800:4004:80c::', 0, 0),
|
|
||||||
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
|
|
||||||
True,
|
|
||||||
None,
|
|
||||||
63)
|
|
||||||
assert str(excinfo.value) \
|
|
||||||
== 'Address family "AF_INET6" unsupported by nat method_name'
|
|
||||||
assert mock_ipt_chain_exists.mock_calls == []
|
assert mock_ipt_chain_exists.mock_calls == []
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
assert mock_ipt.mock_calls == []
|
||||||
|
method.setup_firewall(
|
||||||
|
1024, 1026,
|
||||||
|
[(AF_INET6, u'2404:6800:4004:80c::33')],
|
||||||
|
AF_INET6,
|
||||||
|
[(AF_INET6, 64, False, u'2404:6800:4004:80c::', 0, 0),
|
||||||
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
|
||||||
|
False,
|
||||||
|
None,
|
||||||
|
'0x01')
|
||||||
|
|
||||||
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
|
call(AF_INET6, 'nat', 'sshuttle-1024')
|
||||||
|
]
|
||||||
|
assert mock_ipt.mock_calls == [
|
||||||
|
call(AF_INET6, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-F', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-X', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-N', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-F', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1024'),
|
||||||
|
call(AF_INET6, 'nat', '-A', 'sshuttle-1024', '-j', 'REDIRECT',
|
||||||
|
'--dest', u'2404:6800:4004:80c::33', '-p', 'udp',
|
||||||
|
'--dport', '53', '--to-ports', '1026'),
|
||||||
|
call(AF_INET6, 'nat', '-A', 'sshuttle-1024', '-j', 'RETURN',
|
||||||
|
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
||||||
|
call(AF_INET6, 'nat', '-A', 'sshuttle-1024', '-j', 'RETURN',
|
||||||
|
'--dest', u'2404:6800:4004:80c::101f/128', '-p', 'tcp',
|
||||||
|
'--dport', '80:80'),
|
||||||
|
call(AF_INET6, 'nat', '-A', 'sshuttle-1024', '-j', 'REDIRECT',
|
||||||
|
'--dest', u'2404:6800:4004:80c::/64', '-p', 'tcp',
|
||||||
|
'--to-ports', '1024')
|
||||||
|
]
|
||||||
|
mock_ipt_chain_exists.reset_mock()
|
||||||
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
|
assert mock_ipt_chain_exists.mock_calls == []
|
||||||
assert mock_ipt.mock_calls == []
|
assert mock_ipt.mock_calls == []
|
||||||
|
|
||||||
with pytest.raises(Exception) as excinfo:
|
with pytest.raises(Exception) as excinfo:
|
||||||
@ -117,10 +142,9 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
|
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert str(excinfo.value) == 'UDP not supported by nat method_name'
|
assert str(excinfo.value) == 'UDP not supported by nat method_name'
|
||||||
assert mock_ipt_chain_exists.mock_calls == []
|
assert mock_ipt_chain_exists.mock_calls == []
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == []
|
assert mock_ipt.mock_calls == []
|
||||||
|
|
||||||
method.setup_firewall(
|
method.setup_firewall(
|
||||||
@ -131,14 +155,10 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
|
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(AF_INET, 'nat', 'sshuttle-1025')
|
call(AF_INET, 'nat', 'sshuttle-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == [
|
|
||||||
call(AF_INET, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
|
|
||||||
'-m', 'ttl', '--ttl', '63')
|
|
||||||
]
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
|
||||||
call(AF_INET, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1025'),
|
||||||
@ -149,7 +169,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-I', 'OUTPUT', '1', '-j', 'sshuttle-1025'),
|
||||||
call(AF_INET, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-I', 'PREROUTING', '1', '-j', 'sshuttle-1025'),
|
||||||
call(AF_INET, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
call(AF_INET, 'nat', '-A', 'sshuttle-1025', '-j', 'REDIRECT',
|
||||||
'--dest', u'1.2.3.33/32', '-p', 'udp',
|
'--dest', u'1.2.3.33', '-p', 'udp',
|
||||||
'--dport', '53', '--to-ports', '1027'),
|
'--dport', '53', '--to-ports', '1027'),
|
||||||
call(AF_INET, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
|
call(AF_INET, 'nat', '-A', 'sshuttle-1025', '-j', 'RETURN',
|
||||||
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
||||||
@ -160,20 +180,33 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
'--to-ports', '1025')
|
'--to-ports', '1025')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
method.restore_firewall(1025, AF_INET, False, None)
|
method.restore_firewall(1025, AF_INET, False, None)
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(AF_INET, 'nat', 'sshuttle-1025')
|
call(AF_INET, 'nat', 'sshuttle-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-D', 'OUTPUT', '-j',
|
||||||
call(AF_INET, 'nat', '-D', 'PREROUTING', '-j', 'sshuttle-1025'),
|
'sshuttle-1025'),
|
||||||
|
call(AF_INET, 'nat', '-D', 'PREROUTING', '-j',
|
||||||
|
'sshuttle-1025'),
|
||||||
call(AF_INET, 'nat', '-F', 'sshuttle-1025'),
|
call(AF_INET, 'nat', '-F', 'sshuttle-1025'),
|
||||||
call(AF_INET, 'nat', '-X', 'sshuttle-1025')
|
call(AF_INET, 'nat', '-X', 'sshuttle-1025')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
|
method.restore_firewall(1025, AF_INET6, False, None)
|
||||||
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
|
call(AF_INET6, 'nat', 'sshuttle-1025')
|
||||||
|
]
|
||||||
|
assert mock_ipt.mock_calls == [
|
||||||
|
call(AF_INET6, 'nat', '-D', 'OUTPUT', '-j', 'sshuttle-1025'),
|
||||||
|
call(AF_INET6, 'nat', '-D', 'PREROUTING', '-j',
|
||||||
|
'sshuttle-1025'),
|
||||||
|
call(AF_INET6, 'nat', '-F', 'sshuttle-1025'),
|
||||||
|
call(AF_INET6, 'nat', '-X', 'sshuttle-1025')
|
||||||
|
]
|
||||||
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
@ -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)],
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
call(mock_pf_get_dev(), 0xCC20441A, 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)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
assert mock_ioctl.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)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
call(mock_pf_get_dev(), 0xCC20441A, 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)],
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
|
|
||||||
assert mock_pfctl.mock_calls == [
|
assert mock_pfctl.mock_calls == [
|
||||||
call('-s all'),
|
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)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
assert mock_ioctl.mock_calls == []
|
assert mock_ioctl.mock_calls == []
|
||||||
@ -349,7 +349,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)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
call(mock_pf_get_dev(), 0xC4704433, ANY),
|
||||||
call(mock_pf_get_dev(), 0xCBE0441A, ANY),
|
call(mock_pf_get_dev(), 0xCBE0441A, ANY),
|
||||||
@ -408,7 +408,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
|
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
||||||
@ -445,7 +445,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
assert str(excinfo.value) == 'UDP not supported by pf method_name'
|
||||||
assert mock_pf_get_dev.mock_calls == []
|
assert mock_pf_get_dev.mock_calls == []
|
||||||
assert mock_ioctl.mock_calls == []
|
assert mock_ioctl.mock_calls == []
|
||||||
@ -459,7 +459,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
|
|||||||
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
False,
|
False,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ioctl.mock_calls == [
|
assert mock_ioctl.mock_calls == [
|
||||||
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
||||||
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
call(mock_pf_get_dev(), 0xcd60441a, ANY),
|
||||||
|
@ -92,9 +92,8 @@ def test_firewall_command():
|
|||||||
|
|
||||||
|
|
||||||
@patch('sshuttle.methods.tproxy.ipt')
|
@patch('sshuttle.methods.tproxy.ipt')
|
||||||
@patch('sshuttle.methods.tproxy.ipt_ttl')
|
|
||||||
@patch('sshuttle.methods.tproxy.ipt_chain_exists')
|
@patch('sshuttle.methods.tproxy.ipt_chain_exists')
|
||||||
def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
def test_setup_firewall(mock_ipt_chain_exists, mock_ipt):
|
||||||
mock_ipt_chain_exists.return_value = True
|
mock_ipt_chain_exists.return_value = True
|
||||||
method = get_method('tproxy')
|
method = get_method('tproxy')
|
||||||
assert method.name == 'tproxy'
|
assert method.name == 'tproxy'
|
||||||
@ -109,13 +108,12 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(AF_INET6, 'mangle', 'sshuttle-m-1024'),
|
call(AF_INET6, 'mangle', 'sshuttle-m-1024'),
|
||||||
call(AF_INET6, 'mangle', 'sshuttle-t-1024'),
|
call(AF_INET6, 'mangle', 'sshuttle-t-1024'),
|
||||||
call(AF_INET6, 'mangle', 'sshuttle-d-1024')
|
call(AF_INET6, 'mangle', 'sshuttle-d-1024')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET6, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1024'),
|
call(AF_INET6, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1024'),
|
||||||
call(AF_INET6, 'mangle', '-F', 'sshuttle-m-1024'),
|
call(AF_INET6, 'mangle', '-F', 'sshuttle-m-1024'),
|
||||||
@ -139,17 +137,17 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
||||||
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'MARK',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1'),
|
'--set-mark', '0x01'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'ACCEPT'),
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'ACCEPT'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
|
||||||
'-j', 'sshuttle-d-1024', '-m', 'tcp', '-p', 'tcp'),
|
'-j', 'sshuttle-d-1024', '-m', 'tcp', '-p', 'tcp'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
|
||||||
'-j', 'sshuttle-d-1024', '-m', 'udp', '-p', 'udp'),
|
'-j', 'sshuttle-d-1024', '-m', 'udp', '-p', 'udp'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::33/32',
|
'--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::33/32',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1',
|
'--tproxy-mark', '0x01',
|
||||||
'--dest', u'2404:6800:4004:80c::33/32',
|
'--dest', u'2404:6800:4004:80c::33/32',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
|
||||||
@ -165,22 +163,23 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
'--dest', u'2404:6800:4004:80c::101f/128',
|
'--dest', u'2404:6800:4004:80c::101f/128',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '8080:8080'),
|
'-m', 'udp', '-p', 'udp', '--dport', '8080:8080'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
'--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000'),
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
'--tproxy-mark', '0x01', '--dest',
|
||||||
|
u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000',
|
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000',
|
||||||
'--on-port', '1024'),
|
'--on-port', '1024'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64',
|
'--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000'),
|
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000'),
|
||||||
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64',
|
'--tproxy-mark', '0x01', '--dest',
|
||||||
|
u'2404:6800:4004:80c::/64',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000',
|
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000',
|
||||||
'--on-port', '1024')
|
'--on-port', '1024')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
method.restore_firewall(1025, AF_INET6, True, None)
|
method.restore_firewall(1025, AF_INET6, True, None)
|
||||||
@ -189,7 +188,6 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET6, 'mangle', 'sshuttle-t-1025'),
|
call(AF_INET6, 'mangle', 'sshuttle-t-1025'),
|
||||||
call(AF_INET6, 'mangle', 'sshuttle-d-1025')
|
call(AF_INET6, 'mangle', 'sshuttle-d-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET6, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
call(AF_INET6, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
||||||
call(AF_INET6, 'mangle', '-F', 'sshuttle-m-1025'),
|
call(AF_INET6, 'mangle', '-F', 'sshuttle-m-1025'),
|
||||||
@ -201,7 +199,6 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET6, 'mangle', '-X', 'sshuttle-d-1025')
|
call(AF_INET6, 'mangle', '-X', 'sshuttle-d-1025')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
# IPV4
|
# IPV4
|
||||||
@ -214,13 +211,12 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
(AF_INET, 32, True, u'1.2.3.66', 80, 80)],
|
||||||
True,
|
True,
|
||||||
None,
|
None,
|
||||||
63)
|
'0x01')
|
||||||
assert mock_ipt_chain_exists.mock_calls == [
|
assert mock_ipt_chain_exists.mock_calls == [
|
||||||
call(AF_INET, 'mangle', 'sshuttle-m-1025'),
|
call(AF_INET, 'mangle', 'sshuttle-m-1025'),
|
||||||
call(AF_INET, 'mangle', 'sshuttle-t-1025'),
|
call(AF_INET, 'mangle', 'sshuttle-t-1025'),
|
||||||
call(AF_INET, 'mangle', 'sshuttle-d-1025')
|
call(AF_INET, 'mangle', 'sshuttle-d-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
call(AF_INET, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
||||||
call(AF_INET, 'mangle', '-F', 'sshuttle-m-1025'),
|
call(AF_INET, 'mangle', '-F', 'sshuttle-m-1025'),
|
||||||
@ -244,17 +240,17 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
||||||
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
'-m', 'addrtype', '--dst-type', 'LOCAL'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025',
|
||||||
'-j', 'MARK', '--set-mark', '1'),
|
'-j', 'MARK', '--set-mark', '0x01'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025', '-j', 'ACCEPT'),
|
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025', '-j', 'ACCEPT'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
|
||||||
'-j', 'sshuttle-d-1025', '-m', 'tcp', '-p', 'tcp'),
|
'-j', 'sshuttle-d-1025', '-m', 'tcp', '-p', 'tcp'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
|
||||||
'-j', 'sshuttle-d-1025', '-m', 'udp', '-p', 'udp'),
|
'-j', 'sshuttle-d-1025', '-m', 'udp', '-p', 'udp'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'1.2.3.33/32',
|
'--set-mark', '0x01', '--dest', u'1.2.3.33/32',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.33/32',
|
'--tproxy-mark', '0x01', '--dest', u'1.2.3.33/32',
|
||||||
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
|
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
|
||||||
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp',
|
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp',
|
||||||
@ -269,20 +265,19 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp',
|
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp',
|
||||||
'--dport', '80:80'),
|
'--dport', '80:80'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'1.2.3.0/24',
|
'--set-mark', '0x01', '--dest', u'1.2.3.0/24',
|
||||||
'-m', 'tcp', '-p', 'tcp'),
|
'-m', 'tcp', '-p', 'tcp'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24',
|
'--tproxy-mark', '0x01', '--dest', u'1.2.3.0/24',
|
||||||
'-m', 'tcp', '-p', 'tcp', '--on-port', '1025'),
|
'-m', 'tcp', '-p', 'tcp', '--on-port', '1025'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
|
||||||
'--set-mark', '1', '--dest', u'1.2.3.0/24',
|
'--set-mark', '0x01', '--dest', u'1.2.3.0/24',
|
||||||
'-m', 'udp', '-p', 'udp'),
|
'-m', 'udp', '-p', 'udp'),
|
||||||
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
|
||||||
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24',
|
'--tproxy-mark', '0x01', '--dest', u'1.2.3.0/24',
|
||||||
'-m', 'udp', '-p', 'udp', '--on-port', '1025')
|
'-m', 'udp', '-p', 'udp', '--on-port', '1025')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
|
||||||
method.restore_firewall(1025, AF_INET, True, None)
|
method.restore_firewall(1025, AF_INET, True, None)
|
||||||
@ -291,7 +286,6 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET, 'mangle', 'sshuttle-t-1025'),
|
call(AF_INET, 'mangle', 'sshuttle-t-1025'),
|
||||||
call(AF_INET, 'mangle', 'sshuttle-d-1025')
|
call(AF_INET, 'mangle', 'sshuttle-d-1025')
|
||||||
]
|
]
|
||||||
assert mock_ipt_ttl.mock_calls == []
|
|
||||||
assert mock_ipt.mock_calls == [
|
assert mock_ipt.mock_calls == [
|
||||||
call(AF_INET, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
call(AF_INET, 'mangle', '-D', 'OUTPUT', '-j', 'sshuttle-m-1025'),
|
||||||
call(AF_INET, 'mangle', '-F', 'sshuttle-m-1025'),
|
call(AF_INET, 'mangle', '-F', 'sshuttle-m-1025'),
|
||||||
@ -303,5 +297,4 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
|
|||||||
call(AF_INET, 'mangle', '-X', 'sshuttle-d-1025')
|
call(AF_INET, 'mangle', '-X', 'sshuttle-d-1025')
|
||||||
]
|
]
|
||||||
mock_ipt_chain_exists.reset_mock()
|
mock_ipt_chain_exists.reset_mock()
|
||||||
mock_ipt_ttl.reset_mock()
|
|
||||||
mock_ipt.reset_mock()
|
mock_ipt.reset_mock()
|
||||||
|
Loading…
Reference in New Issue
Block a user