mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-07-06 09:40:32 +02:00
Compare commits
10 Commits
sshuttle-0
...
sshuttle-0
Author | SHA1 | Date | |
---|---|---|---|
cfb2592346 | |||
2e8381ecda | |||
7d35690e41 | |||
141d9760b9 | |||
0658c85ffe | |||
90a55a33a2 | |||
c3399595d2 | |||
6ef9ae1796 | |||
1ca8aa5b89 | |||
a62975e0ce |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
*.8
|
*.8
|
||||||
|
/.do_built
|
||||||
|
/.do_built.dir
|
||||||
|
/.redo
|
||||||
|
21
Makefile
21
Makefile
@ -1,19 +1,10 @@
|
|||||||
PANDOC:=$(shell \
|
all:
|
||||||
if pandoc </dev/null 2>/dev/null; then \
|
|
||||||
echo pandoc; \
|
|
||||||
else \
|
|
||||||
echo "Warning: pandoc not installed; can't generate manpages." >&2; \
|
|
||||||
echo '@echo Skipping: pandoc'; \
|
|
||||||
fi)
|
|
||||||
|
|
||||||
default: all
|
Makefile:
|
||||||
|
@
|
||||||
|
|
||||||
all: sshuttle.8
|
%: FORCE
|
||||||
|
+./do $@
|
||||||
|
|
||||||
sshuttle.8: sshuttle.md
|
.PHONY: FORCE
|
||||||
|
|
||||||
%.8: %.md
|
|
||||||
$(PANDOC) -s -r markdown -w man -o $@ $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
|
||||||
|
11
README.md
11
README.md
@ -1,3 +1,14 @@
|
|||||||
|
|
||||||
|
WARNING:
|
||||||
|
On MacOS 10.6 (at least up to 10.6.6), your network will
|
||||||
|
stop responding about 10 minutes after the first time you
|
||||||
|
start sshuttle, because of a MacOS kernel bug relating to
|
||||||
|
arp and the net.inet.ip.scopedroute sysctl. To fix it,
|
||||||
|
just switch your wireless off and on. Sshuttle makes the
|
||||||
|
kernel setting it changes permanent, so this won't happen
|
||||||
|
again, even after a reboot.
|
||||||
|
|
||||||
|
|
||||||
sshuttle: where transparent proxy meets VPN meets ssh
|
sshuttle: where transparent proxy meets VPN meets ssh
|
||||||
=====================================================
|
=====================================================
|
||||||
|
|
||||||
|
11
all.do
Normal file
11
all.do
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
exec >&2
|
||||||
|
UI=
|
||||||
|
[ "$(uname)" = "Darwin" ] && UI=ui-macos/all
|
||||||
|
redo-ifchange sshuttle.8 $UI
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "What now?"
|
||||||
|
[ -z "$UI" ] || echo "- Try the MacOS GUI: open ui-macos/Sshuttle*.app"
|
||||||
|
echo "- Run sshuttle: ./sshuttle --dns -r HOSTNAME 0/0"
|
||||||
|
echo "- Read the README: less README.md"
|
||||||
|
echo "- Read the man page: less sshuttle.md"
|
2
clean.do
Normal file
2
clean.do
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
redo ui-macos/clean
|
||||||
|
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
@ -102,7 +102,7 @@ class FirewallClient:
|
|||||||
self.subnets_include = subnets_include
|
self.subnets_include = subnets_include
|
||||||
self.subnets_exclude = subnets_exclude
|
self.subnets_exclude = subnets_exclude
|
||||||
self.dnsport = dnsport
|
self.dnsport = dnsport
|
||||||
argvbase = ([sys.argv[0]] +
|
argvbase = ([sys.argv[1], sys.argv[0], sys.argv[1]] +
|
||||||
['-v'] * (helpers.verbose or 0) +
|
['-v'] * (helpers.verbose or 0) +
|
||||||
['--firewall', str(port), str(dnsport)])
|
['--firewall', str(port), str(dnsport)])
|
||||||
if ssyslog._p:
|
if ssyslog._p:
|
||||||
|
7
default.8.do
Normal file
7
default.8.do
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
exec >&2
|
||||||
|
if pandoc </dev/null 2>/dev/null; then
|
||||||
|
pandoc -s -r markdown -w man -o $3 $1.md
|
||||||
|
else
|
||||||
|
echo "Warning: pandoc not installed; can't generate manpages."
|
||||||
|
redo-always
|
||||||
|
fi
|
150
do
Executable file
150
do
Executable file
@ -0,0 +1,150 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# A minimal alternative to djb redo that doesn't support incremental builds.
|
||||||
|
# For the full version, visit http://github.com/apenwarr/redo
|
||||||
|
#
|
||||||
|
# The author disclaims copyright to this source file and hereby places it in
|
||||||
|
# the public domain. (2010 12 14)
|
||||||
|
#
|
||||||
|
|
||||||
|
# By default, no output coloring.
|
||||||
|
GREEN=""
|
||||||
|
BOLD=""
|
||||||
|
PLAIN=""
|
||||||
|
|
||||||
|
if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then
|
||||||
|
GREEN="$(printf '\033[32m')"
|
||||||
|
BOLD="$(printf '\033[1m')"
|
||||||
|
PLAIN="$(printf '\033[m')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_dirsplit()
|
||||||
|
{
|
||||||
|
base=${1##*/}
|
||||||
|
dir=${1%$base}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dirsplit "$0"
|
||||||
|
export REDO=$(cd "${dir:-.}" && echo "$PWD/$base")
|
||||||
|
|
||||||
|
DO_TOP=
|
||||||
|
if [ -z "$DO_BUILT" ]; then
|
||||||
|
DO_TOP=1
|
||||||
|
[ -n "$*" ] || set all # only toplevel redo has a default target
|
||||||
|
export DO_BUILT=$PWD/.do_built
|
||||||
|
: >>"$DO_BUILT"
|
||||||
|
echo "Removing previously built files..." >&2
|
||||||
|
sort -u "$DO_BUILT" | tee "$DO_BUILT.new" |
|
||||||
|
while read f; do printf "%s\0%s.did\0" "$f" "$f"; done |
|
||||||
|
xargs -0 rm -f 2>/dev/null
|
||||||
|
mv "$DO_BUILT.new" "$DO_BUILT"
|
||||||
|
DO_PATH=$DO_BUILT.dir
|
||||||
|
export PATH=$DO_PATH:$PATH
|
||||||
|
rm -rf "$DO_PATH"
|
||||||
|
mkdir "$DO_PATH"
|
||||||
|
for d in redo redo-ifchange; do
|
||||||
|
ln -s "$REDO" "$DO_PATH/$d";
|
||||||
|
done
|
||||||
|
[ -e /bin/true ] && TRUE=/bin/true || TRUE=/usr/bin/true
|
||||||
|
for d in redo-ifcreate redo-stamp redo-always; do
|
||||||
|
ln -s $TRUE "$DO_PATH/$d";
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
_find_dofile_pwd()
|
||||||
|
{
|
||||||
|
DOFILE=default.$1.do
|
||||||
|
while :; do
|
||||||
|
DOFILE=default.${DOFILE#default.*.}
|
||||||
|
[ -e "$DOFILE" -o "$DOFILE" = default.do ] && break
|
||||||
|
done
|
||||||
|
EXT=${DOFILE#default}
|
||||||
|
EXT=${EXT%.do}
|
||||||
|
BASE=${1%$EXT}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_find_dofile()
|
||||||
|
{
|
||||||
|
PREFIX=
|
||||||
|
while :; do
|
||||||
|
_find_dofile_pwd "$1"
|
||||||
|
[ -e "$DOFILE" ] && break
|
||||||
|
[ "$PWD" = "/" ] && break
|
||||||
|
TARGET=${PWD##*/}/$TARGET
|
||||||
|
PREFIX=${PWD##*/}/$PREFIX
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
BASE=$PREFIX$BASE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_run_dofile()
|
||||||
|
{
|
||||||
|
export DO_DEPTH="$DO_DEPTH "
|
||||||
|
export REDO_TARGET=$PWD/$TARGET
|
||||||
|
set -e
|
||||||
|
read line1 <"$PWD/$DOFILE"
|
||||||
|
cmd=${line1#"#!/"}
|
||||||
|
if [ "$cmd" != "$line1" ]; then
|
||||||
|
/$cmd "$PWD/$DOFILE" "$@" >"$TARGET.tmp2"
|
||||||
|
else
|
||||||
|
. "$PWD/$DOFILE" >"$TARGET.tmp2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_do()
|
||||||
|
{
|
||||||
|
DIR=$1
|
||||||
|
TARGET=$2
|
||||||
|
if [ ! -e "$TARGET" ] || [ -e "$TARGET/." -a ! -e "$TARGET.did" ]; then
|
||||||
|
printf '%sdo %s%s%s%s\n' \
|
||||||
|
"$GREEN" "$DO_DEPTH" "$BOLD" "$DIR$TARGET" "$PLAIN" >&2
|
||||||
|
echo "$PWD/$TARGET" >>"$DO_BUILT"
|
||||||
|
DOFILE=$TARGET.do
|
||||||
|
BASE=$TARGET
|
||||||
|
EXT=
|
||||||
|
[ -e "$TARGET.do" ] || _find_dofile "$TARGET"
|
||||||
|
if [ ! -e "$DOFILE" ]; then
|
||||||
|
echo "do: $TARGET: no .do file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
[ ! -e "$DO_BUILD" ] || : >>"$TARGET.did"
|
||||||
|
( _run_dofile "$BASE" "$EXT" "$TARGET.tmp" )
|
||||||
|
RV=$?
|
||||||
|
if [ $RV != 0 ]; then
|
||||||
|
printf "do: %s%s\n" "$DO_DEPTH" \
|
||||||
|
"$DIR$TARGET: got exit code $RV" >&2
|
||||||
|
rm -f "$TARGET.tmp" "$TARGET.tmp2"
|
||||||
|
return $RV
|
||||||
|
fi
|
||||||
|
mv "$TARGET.tmp" "$TARGET" 2>/dev/null ||
|
||||||
|
! test -s "$TARGET.tmp2" ||
|
||||||
|
mv "$TARGET.tmp2" "$TARGET" 2>/dev/null
|
||||||
|
rm -f "$TARGET.tmp2"
|
||||||
|
else
|
||||||
|
echo "do $DO_DEPTH$TARGET exists." >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
redo()
|
||||||
|
{
|
||||||
|
for i in "$@"; do
|
||||||
|
_dirsplit "$i"
|
||||||
|
( cd "$dir" && _do "$dir" "$base" ) || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set -e
|
||||||
|
redo "$@"
|
||||||
|
|
||||||
|
if [ -n "$DO_TOP" ]; then
|
||||||
|
echo "Removing stamp files..." >&2
|
||||||
|
[ ! -e "$DO_BUILT" ] ||
|
||||||
|
while read f; do printf "%s.did\0" "$f"; done <"$DO_BUILT" |
|
||||||
|
xargs -0 rm -f 2>/dev/null
|
||||||
|
fi
|
29
firewall.py
29
firewall.py
@ -7,6 +7,13 @@ from helpers import *
|
|||||||
IPPROTO_DIVERT = 254
|
IPPROTO_DIVERT = 254
|
||||||
|
|
||||||
|
|
||||||
|
def nonfatal(func, *args):
|
||||||
|
try:
|
||||||
|
func(*args)
|
||||||
|
except Fatal, e:
|
||||||
|
log('error: %s\n' % e)
|
||||||
|
|
||||||
|
|
||||||
def ipt_chain_exists(name):
|
def ipt_chain_exists(name):
|
||||||
argv = ['iptables', '-t', 'nat', '-nL']
|
argv = ['iptables', '-t', 'nat', '-nL']
|
||||||
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
|
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
|
||||||
@ -57,9 +64,9 @@ def do_iptables(port, dnsport, subnets):
|
|||||||
|
|
||||||
# basic cleanup/setup of chains
|
# basic cleanup/setup of chains
|
||||||
if ipt_chain_exists(chain):
|
if ipt_chain_exists(chain):
|
||||||
ipt('-D', 'OUTPUT', '-j', chain)
|
nonfatal(ipt, '-D', 'OUTPUT', '-j', chain)
|
||||||
ipt('-D', 'PREROUTING', '-j', chain)
|
nonfatal(ipt, '-D', 'PREROUTING', '-j', chain)
|
||||||
ipt('-F', chain)
|
nonfatal(ipt, '-F', chain)
|
||||||
ipt('-X', chain)
|
ipt('-X', chain)
|
||||||
|
|
||||||
if subnets or dnsport:
|
if subnets or dnsport:
|
||||||
@ -143,7 +150,7 @@ def sysctl_set(name, val, permanent=False):
|
|||||||
_fill_oldctls(PREFIX)
|
_fill_oldctls(PREFIX)
|
||||||
if not (name in _oldctls):
|
if not (name in _oldctls):
|
||||||
debug1('>> No such sysctl: %r\n' % name)
|
debug1('>> No such sysctl: %r\n' % name)
|
||||||
return
|
return False
|
||||||
oldval = _oldctls[name]
|
oldval = _oldctls[name]
|
||||||
if val != oldval:
|
if val != oldval:
|
||||||
rv = _sysctl_set(name, val)
|
rv = _sysctl_set(name, val)
|
||||||
@ -156,6 +163,7 @@ def sysctl_set(name, val, permanent=False):
|
|||||||
f.close()
|
f.close()
|
||||||
else:
|
else:
|
||||||
_changedctls.append(name)
|
_changedctls.append(name)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _udp_unpack(p):
|
def _udp_unpack(p):
|
||||||
@ -214,7 +222,18 @@ def do_ipfw(port, dnsport, subnets):
|
|||||||
|
|
||||||
if subnets or dnsport:
|
if subnets or dnsport:
|
||||||
sysctl_set('net.inet.ip.fw.enable', 1)
|
sysctl_set('net.inet.ip.fw.enable', 1)
|
||||||
sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
|
changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
|
||||||
|
if changed:
|
||||||
|
log("\n"
|
||||||
|
" WARNING: ONE-TIME NETWORK DISRUPTION:\n"
|
||||||
|
" =====================================\n"
|
||||||
|
"sshuttle has changed a MacOS kernel setting to work around\n"
|
||||||
|
"a bug in MacOS 10.6. This will cause your network to drop\n"
|
||||||
|
"within 5-10 minutes unless you restart your network\n"
|
||||||
|
"interface (change wireless networks or unplug/plug the\n"
|
||||||
|
"ethernet port) NOW, then restart sshuttle. The fix is\n"
|
||||||
|
"permanent; you only have to do this once.\n\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
ipfw('add', sport, 'check-state', 'ip',
|
ipfw('add', sport, 'check-state', 'ip',
|
||||||
'from', 'any', 'to', 'any')
|
'from', 'any', 'to', 'any')
|
||||||
|
@ -13,7 +13,11 @@ _nmb_ok = True
|
|||||||
_smb_ok = True
|
_smb_ok = True
|
||||||
hostnames = {}
|
hostnames = {}
|
||||||
queue = {}
|
queue = {}
|
||||||
null = open('/dev/null', 'rb+')
|
try:
|
||||||
|
null = open('/dev/null', 'wb')
|
||||||
|
except IOError, e:
|
||||||
|
log('warning: %s\n' % e)
|
||||||
|
null = os.popen("sh -c 'while read x; do :; done'", 'wb', 4096)
|
||||||
|
|
||||||
|
|
||||||
def _is_ip(s):
|
def _is_ip(s):
|
||||||
|
5
main.py
Executable file → Normal file
5
main.py
Executable file → Normal file
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys, os, re
|
import sys, os, re
|
||||||
import helpers, options, client, server, firewall, hostwatch
|
import helpers, options, client, server, firewall, hostwatch
|
||||||
import compat.ssubprocess as ssubprocess
|
import compat.ssubprocess as ssubprocess
|
||||||
@ -55,7 +54,7 @@ l,listen= transproxy to this ip address and port number [127.0.0.1:0]
|
|||||||
H,auto-hosts scan for remote hostnames and update local /etc/hosts
|
H,auto-hosts scan for remote hostnames and update local /etc/hosts
|
||||||
N,auto-nets automatically determine subnets to route
|
N,auto-nets automatically determine subnets to route
|
||||||
dns capture local DNS requests and forward to the remote DNS server
|
dns capture local DNS requests and forward to the remote DNS server
|
||||||
python= path to python interpreter on the remote server [python]
|
python= path to python interpreter on the remote server
|
||||||
r,remote= ssh hostname (and optional username) of remote sshuttle server
|
r,remote= ssh hostname (and optional username) of remote sshuttle server
|
||||||
x,exclude= exclude this subnet (can be used more than once)
|
x,exclude= exclude this subnet (can be used more than once)
|
||||||
v,verbose increase debug message verbosity
|
v,verbose increase debug message verbosity
|
||||||
@ -71,7 +70,7 @@ firewall (internal use only)
|
|||||||
hostwatch (internal use only)
|
hostwatch (internal use only)
|
||||||
"""
|
"""
|
||||||
o = options.Options(optspec)
|
o = options.Options(optspec)
|
||||||
(opt, flags, extra) = o.parse(sys.argv[1:])
|
(opt, flags, extra) = o.parse(sys.argv[2:])
|
||||||
|
|
||||||
if opt.daemon:
|
if opt.daemon:
|
||||||
opt.syslog = 1
|
opt.syslog = 1
|
||||||
|
@ -119,7 +119,14 @@ class DnsProxy(Handler):
|
|||||||
self.sock.send(request)
|
self.sock.send(request)
|
||||||
|
|
||||||
def callback(self):
|
def callback(self):
|
||||||
|
try:
|
||||||
data = self.sock.recv(4096)
|
data = self.sock.recv(4096)
|
||||||
|
except socket.error, e:
|
||||||
|
if e.args[0] == errno.ECONNREFUSED:
|
||||||
|
debug2('DNS response: ignoring ECONNREFUSED.\n')
|
||||||
|
return # might have been spurious; wait for a real answer
|
||||||
|
else:
|
||||||
|
raise
|
||||||
debug2('DNS response: %d bytes\n' % len(data))
|
debug2('DNS response: %d bytes\n' % len(data))
|
||||||
self.mux.send(self.chan, ssnet.CMD_DNS_RESPONSE, data)
|
self.mux.send(self.chan, ssnet.CMD_DNS_RESPONSE, data)
|
||||||
self.ok = False
|
self.ok = False
|
||||||
|
11
ssh.py
11
ssh.py
@ -73,16 +73,23 @@ def connect(ssh_cmd, rhostport, python, stderr, options):
|
|||||||
|
|
||||||
|
|
||||||
if not rhost:
|
if not rhost:
|
||||||
argv = [python, '-c', pyscript]
|
# ignore the --python argument when running locally; we already know
|
||||||
|
# which python version works.
|
||||||
|
argv = [sys.argv[1], '-c', pyscript]
|
||||||
else:
|
else:
|
||||||
if ssh_cmd:
|
if ssh_cmd:
|
||||||
sshl = ssh_cmd.split(' ')
|
sshl = ssh_cmd.split(' ')
|
||||||
else:
|
else:
|
||||||
sshl = ['ssh']
|
sshl = ['ssh']
|
||||||
|
if python:
|
||||||
|
pycmd = "'%s' -c '%s'" % (python, pyscript)
|
||||||
|
else:
|
||||||
|
pycmd = ("P=python2; $P -V 2>/dev/null || P=python; "
|
||||||
|
"\"$P\" -c '%s'") % pyscript
|
||||||
argv = (sshl +
|
argv = (sshl +
|
||||||
portl +
|
portl +
|
||||||
ipv6flag +
|
ipv6flag +
|
||||||
[rhost, '--', "'%s' -c '%s'" % (python, pyscript)])
|
[rhost, '--', pycmd])
|
||||||
(s1,s2) = socket.socketpair()
|
(s1,s2) = socket.socketpair()
|
||||||
def setup():
|
def setup():
|
||||||
# runs in the child process
|
# runs in the child process
|
||||||
|
7
sshuttle
Executable file
7
sshuttle
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
DIR=$(dirname "$0")
|
||||||
|
if python2 -V 2>/dev/null; then
|
||||||
|
exec python2 "$DIR/main.py" python2 "$@"
|
||||||
|
else
|
||||||
|
exec python "$DIR/main.py" python "$@"
|
||||||
|
fi
|
11
sshuttle.md
11
sshuttle.md
@ -253,6 +253,17 @@ between the two separate streams, so a tcp-based tunnel is
|
|||||||
fine.
|
fine.
|
||||||
|
|
||||||
|
|
||||||
|
# BUGS
|
||||||
|
|
||||||
|
On MacOS 10.6 (at least up to 10.6.6), your network will
|
||||||
|
stop responding about 10 minutes after the first time you
|
||||||
|
start sshuttle, because of a MacOS kernel bug relating to
|
||||||
|
arp and the net.inet.ip.scopedroute sysctl. To fix it,
|
||||||
|
just switch your wireless off and on. Sshuttle makes the
|
||||||
|
kernel setting it changes permanent, so this won't happen
|
||||||
|
again, even after a reboot.
|
||||||
|
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
`ssh`(1), `python`(1)
|
`ssh`(1), `python`(1)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
import sys, os, socket, select, struct, time
|
import sys, os, socket, select, struct, time
|
||||||
|
|
||||||
listener = socket.socket()
|
listener = socket.socket()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
exec >&2
|
exec >&2
|
||||||
find -name '*~' | xargs rm -f
|
find . -name '*~' | xargs rm -f
|
||||||
rm -rf *.app *.zip *.tar.gz
|
rm -rf *.app *.zip *.tar.gz
|
||||||
rm -f bits/runpython *.nib sources.list
|
rm -f bits/runpython *.nib sources.list
|
||||||
|
Reference in New Issue
Block a user