Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
sshuttle
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
pub
sshuttle
Commits
34f538ff
Commit
34f538ff
authored
Oct 26, 2020
by
Scott Kuhl
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into which-fix to resolve merge conflict.
parents
68c9c9bb
5c8c7072
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
97 additions
and
29 deletions
+97
-29
manpage.rst
docs/manpage.rst
+21
-12
requirements.rst
docs/requirements.rst
+12
-0
__init__.py
sshuttle/methods/__init__.py
+18
-8
nft.py
sshuttle/methods/nft.py
+36
-9
test_methods_nat.py
tests/client/test_methods_nat.py
+10
-0
No files found.
docs/manpage.rst
View file @
34f538ff
...
...
@@ -40,11 +40,15 @@ Options
that has as the destination port 8000 of 1.2.3.4 and
1.2.3.0/24:8000-9000 will tunnel traffic going to any port between
8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24 subnet.
It is also possible to use a name in which case the first IP it resolves
to during startup will be routed over the VPN. Valid examples are
example.com, example.com:8000 and example.com:8000-9000.
A hostname can be provided instead of an IP address. If the
hostname resolves to multiple IPs, all of the IPs are included.
If a width is provided with a hostname that the width is applied
to all of the hostnames IPs (if they are all either IPv4 or IPv6).
Widths cannot be supplied to hostnames that resolve to both IPv4
and IPv6. Valid examples are example.com, example.com:8000,
example.com/24, example.com/24:8000 and example.com:8000-9000.
.. option:: --method <auto|nat|nft|tproxy|pf>
.. option:: --method <auto|nat|nft|tproxy|pf
|ipfw
>
Which firewall method should sshuttle use? For auto, sshuttle attempts to
guess the appropriate method depending on what it can find in PATH. The
...
...
@@ -64,9 +68,9 @@ Options
You can use any name resolving to an IP address of the machine running
:program:`sshuttle`, e.g. ``--listen localhost``.
For the
tproxy and pf methods this can be an IPv6 address. Use this option
with comma separated values if required, to provide both IPv4 and IPv6
addresses, e.g. ``--listen 127.0.0.1:0,[::1]:0``.
For the
nft, tproxy and pf methods this can be an IPv6 address. Use
this option with comma separated values if required, to provide both
IPv4 and IPv6
addresses, e.g. ``--listen 127.0.0.1:0,[::1]:0``.
.. option:: -H, --auto-hosts
...
...
@@ -92,6 +96,10 @@ Options
are taken automatically from the server's routing
table.
This feature does not detect IPv6 routes. Specify IPv6 subnets
manually. For example, specify the ``::/0`` subnet on the command
line to route all IPv6 traffic.
.. option:: --dns
Capture local DNS requests and forward to the remote DNS
...
...
@@ -122,9 +130,9 @@ Options
.. option:: --python
Specify the name/path of the remote python interpreter.
The default is just ``python``, which means to use the
default python interpreter o
n the remote system's PATH.
Specify the name/path of the remote python interpreter.
The
default is to use ``python3`` (or ``python``, if ``python3``
fails) i
n the remote system's PATH.
.. option:: -r <[username@]sshserver[:port]>, --remote=<[username@]sshserver[:port]>
...
...
@@ -221,7 +229,8 @@ Options
.. option:: --disable-ipv6
If using tproxy or pf methods, this will disable IPv6 support.
Disable IPv6 support for methods that support it (nft, tproxy, and
pf).
.. option:: --firewall
...
...
docs/requirements.rst
View file @
34f538ff
...
...
@@ -20,6 +20,18 @@ Requires:
* iptables DNAT, REDIRECT, and ttl modules.
Linux with nft method
~~~~~~~~~~~~~~~~~~~~~
Supports
* IPv4 TCP
* IPv4 DNS
* IPv6 TCP
* IPv6 DNS
Requires:
* nftables
Linux with TPROXY method
~~~~~~~~~~~~~~~~~~~~~~~~
...
...
sshuttle/methods/__init__.py
View file @
34f538ff
...
...
@@ -2,24 +2,34 @@ import importlib
import
socket
import
struct
import
errno
import
ipaddress
from
sshuttle.helpers
import
Fatal
,
debug3
,
which
def
original_dst
(
sock
):
ip
=
"0.0.0.0"
port
=
-
1
try
:
family
=
sock
.
family
SO_ORIGINAL_DST
=
80
if
family
==
socket
.
AF_INET
:
SOCKADDR_MIN
=
16
sockaddr_in
=
sock
.
getsockopt
(
socket
.
SOL_IP
,
SO_ORIGINAL_DST
,
SOCKADDR_MIN
)
(
proto
,
port
,
a
,
b
,
c
,
d
)
=
struct
.
unpack
(
'!HHBBBB'
,
sockaddr_in
[:
8
])
# FIXME: decoding is IPv4 only.
assert
(
socket
.
htons
(
proto
)
==
socket
.
AF_INET
)
ip
=
'
%
d.
%
d.
%
d.
%
d'
%
(
a
,
b
,
c
,
d
)
return
(
ip
,
port
)
port
,
raw_ip
=
struct
.
unpack_from
(
'!2xH4s'
,
sockaddr_in
[:
8
])
ip
=
str
(
ipaddress
.
IPv4Address
(
raw_ip
))
elif
family
==
socket
.
AF_INET6
:
sockaddr_in
=
sock
.
getsockopt
(
41
,
SO_ORIGINAL_DST
,
64
)
port
,
raw_ip
=
struct
.
unpack_from
(
"!2xH4x16s"
,
sockaddr_in
)
ip
=
str
(
ipaddress
.
IPv6Address
(
raw_ip
))
else
:
raise
Fatal
(
"fw: Unknown family type."
)
except
socket
.
error
as
e
:
if
e
.
args
[
0
]
==
errno
.
ENOPROTOOPT
:
return
sock
.
getsockname
()
raise
return
(
ip
,
port
)
class
Features
(
object
):
...
...
sshuttle/methods/nft.py
View file @
34f538ff
...
...
@@ -16,7 +16,10 @@ class Method(BaseMethod):
if
udp
:
raise
Exception
(
"UDP not supported by nft"
)
table
=
'sshuttle-
%
s'
%
port
if
family
==
socket
.
AF_INET
:
table
=
'sshuttle-ipv4-
%
s'
%
port
if
family
==
socket
.
AF_INET6
:
table
=
'sshuttle-ipv6-
%
s'
%
port
def
_nft
(
action
,
*
args
):
return
nft
(
family
,
table
,
action
,
*
args
)
...
...
@@ -37,7 +40,10 @@ class Method(BaseMethod):
# 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'
)
# Redirect DNS traffic as requested. This includes routing traffic
# to localhost DNS servers through sshuttle.
...
...
@@ -57,7 +63,11 @@ class Method(BaseMethod):
# create new subnet entries.
for
_
,
swidth
,
sexclude
,
snet
,
fport
,
lport
\
in
sorted
(
subnets
,
key
=
subnet_weight
,
reverse
=
True
):
if
family
==
socket
.
AF_INET
:
tcp_ports
=
(
'ip'
,
'protocol'
,
'tcp'
)
elif
family
==
socket
.
AF_INET6
:
tcp_ports
=
(
'ip6'
,
'nexthdr'
,
'tcp'
)
if
fport
and
fport
!=
lport
:
tcp_ports
=
\
tcp_ports
+
\
...
...
@@ -66,21 +76,38 @@ class Method(BaseMethod):
tcp_ports
=
tcp_ports
+
(
'tcp'
,
'dport'
,
'
%
d'
%
(
fport
))
if
sexclude
:
if
family
==
socket
.
AF_INET
:
_nft
(
'add rule'
,
chain
,
*
(
tcp_ports
+
(
'ip daddr
%
s/
%
s'
%
(
snet
,
swidth
),
'return'
)))
elif
family
==
socket
.
AF_INET6
:
_nft
(
'add rule'
,
chain
,
*
(
tcp_ports
+
(
'ip6 daddr
%
s/
%
s'
%
(
snet
,
swidth
),
'return'
)))
else
:
if
family
==
socket
.
AF_INET
:
_nft
(
'add rule'
,
chain
,
*
(
tcp_ports
+
(
'ip daddr
%
s/
%
s'
%
(
snet
,
swidth
),
(
'redirect to :'
+
str
(
port
)))))
elif
family
==
socket
.
AF_INET6
:
_nft
(
'add rule'
,
chain
,
*
(
tcp_ports
+
(
'ip6 daddr
%
s/
%
s'
%
(
snet
,
swidth
),
(
'redirect to :'
+
str
(
port
)))))
def
restore_firewall
(
self
,
port
,
family
,
udp
,
user
):
if
udp
:
raise
Exception
(
"UDP not supported by nft method_name"
)
table
=
'sshuttle-
%
s'
%
port
if
family
==
socket
.
AF_INET
:
table
=
'sshuttle-ipv4-
%
s'
%
port
if
family
==
socket
.
AF_INET6
:
table
=
'sshuttle-ipv6-
%
s'
%
port
def
_nft
(
action
,
*
args
):
return
nft
(
family
,
table
,
action
,
*
args
)
# basic cleanup/setup of chains
nonfatal
(
_nft
,
'delete table'
,
''
)
def
get_supported_features
(
self
):
result
=
super
(
Method
,
self
)
.
get_supported_features
()
result
.
ipv6
=
True
return
result
tests/client/test_methods_nat.py
View file @
34f538ff
...
...
@@ -18,12 +18,22 @@ def test_get_supported_features():
def
test_get_tcp_dstip
():
sock
=
Mock
()
sock
.
family
=
AF_INET
sock
.
getsockopt
.
return_value
=
struct
.
pack
(
'!HHBBBB'
,
socket
.
ntohs
(
AF_INET
),
1024
,
127
,
0
,
0
,
1
)
method
=
get_method
(
'nat'
)
assert
method
.
get_tcp_dstip
(
sock
)
==
(
'127.0.0.1'
,
1024
)
assert
sock
.
mock_calls
==
[
call
.
getsockopt
(
0
,
80
,
16
)]
sock
=
Mock
()
sock
.
family
=
AF_INET6
sock
.
getsockopt
.
return_value
=
struct
.
pack
(
'!HH4xBBBBBBBBBBBBBBBB'
,
socket
.
ntohs
(
AF_INET6
),
1024
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
)
method
=
get_method
(
'nft'
)
assert
method
.
get_tcp_dstip
(
sock
)
==
(
'::1'
,
1024
)
assert
sock
.
mock_calls
==
[
call
.
getsockopt
(
41
,
80
,
64
)]
def
test_recv_udp
():
sock
=
Mock
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment