Skip to content

Commit 2b23533

Browse files
committed
Split setup_firewall method.
* setup_firewall sets the firewall up. * restore_firewall restores the firewall to initial state.
1 parent 2eeea95 commit 2b23533

File tree

9 files changed

+187
-136
lines changed

9 files changed

+187
-136
lines changed

sshuttle/firewall.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def main(method_name, syslog):
232232
try:
233233
if port_v6:
234234
debug2('firewall manager: undoing IPv6 changes.\n')
235-
method.setup_firewall(port_v6, 0, [], socket.AF_INET6, [], udp)
235+
method.restore_firewall(port_v6, socket.AF_INET6, udp)
236236
except:
237237
try:
238238
debug1("firewall manager: "
@@ -245,7 +245,7 @@ def main(method_name, syslog):
245245
try:
246246
if port_v4:
247247
debug2('firewall manager: undoing IPv4 changes.\n')
248-
method.setup_firewall(port_v4, 0, [], socket.AF_INET, [], udp)
248+
method.restore_firewall(port_v4, socket.AF_INET, udp)
249249
except:
250250
try:
251251
debug1("firewall manager: "

sshuttle/methods/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def check_settings(self, udp, dns):
6868
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp):
6969
raise NotImplementedError()
7070

71+
def restore_firewall(self, port, family, udp):
72+
raise NotImplementedError()
73+
7174
def firewall_command(self, line):
7275
return False
7376

sshuttle/methods/nat.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,29 @@ def _ipt_ttl(*args):
3131
chain = 'sshuttle-%s' % port
3232

3333
# basic cleanup/setup of chains
34-
if ipt_chain_exists(family, table, chain):
35-
nonfatal(_ipt, '-D', 'OUTPUT', '-j', chain)
36-
nonfatal(_ipt, '-D', 'PREROUTING', '-j', chain)
37-
nonfatal(_ipt, '-F', chain)
38-
_ipt('-X', chain)
34+
self.restore_firewall(port, family, udp)
35+
36+
_ipt('-N', chain)
37+
_ipt('-F', chain)
38+
_ipt('-I', 'OUTPUT', '1', '-j', chain)
39+
_ipt('-I', 'PREROUTING', '1', '-j', chain)
3940

40-
if subnets or dnsport:
41-
_ipt('-N', chain)
42-
_ipt('-F', chain)
43-
_ipt('-I', 'OUTPUT', '1', '-j', chain)
44-
_ipt('-I', 'PREROUTING', '1', '-j', chain)
45-
46-
if subnets:
47-
# create new subnet entries. Note that we're sorting in a very
48-
# particular order: we need to go from most-specific (largest
49-
# swidth) to least-specific, and at any given level of specificity,
50-
# we want excludes to come first. That's why the columns are in
51-
# such a non- intuitive order.
52-
for f, swidth, sexclude, snet \
53-
in sorted(subnets, key=lambda s: s[1], reverse=True):
54-
if sexclude:
55-
_ipt('-A', chain, '-j', 'RETURN',
41+
# create new subnet entries. Note that we're sorting in a very
42+
# particular order: we need to go from most-specific (largest
43+
# swidth) to least-specific, and at any given level of specificity,
44+
# we want excludes to come first. That's why the columns are in
45+
# such a non- intuitive order.
46+
for f, swidth, sexclude, snet \
47+
in sorted(subnets, key=lambda s: s[1], reverse=True):
48+
if sexclude:
49+
_ipt('-A', chain, '-j', 'RETURN',
50+
'--dest', '%s/%s' % (snet, swidth),
51+
'-p', 'tcp')
52+
else:
53+
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
5654
'--dest', '%s/%s' % (snet, swidth),
57-
'-p', 'tcp')
58-
else:
59-
_ipt_ttl('-A', chain, '-j', 'REDIRECT',
60-
'--dest', '%s/%s' % (snet, swidth),
61-
'-p', 'tcp',
62-
'--to-ports', str(port))
55+
'-p', 'tcp',
56+
'--to-ports', str(port))
6357

6458
if dnsport:
6559
for f, ip in [i for i in nslist if i[0] == family]:
@@ -68,3 +62,29 @@ def _ipt_ttl(*args):
6862
'-p', 'udp',
6963
'--dport', '53',
7064
'--to-ports', str(dnsport))
65+
66+
def restore_firewall(self, port, family, udp):
67+
# only ipv4 supported with NAT
68+
if family != socket.AF_INET:
69+
raise Exception(
70+
'Address family "%s" unsupported by nat method_name'
71+
% family_to_string(family))
72+
if udp:
73+
raise Exception("UDP not supported by nat method_name")
74+
75+
table = "nat"
76+
77+
def _ipt(*args):
78+
return ipt(family, table, *args)
79+
80+
def _ipt_ttl(*args):
81+
return ipt_ttl(family, table, *args)
82+
83+
chain = 'sshuttle-%s' % port
84+
85+
# basic cleanup/setup of chains
86+
if ipt_chain_exists(family, table, chain):
87+
nonfatal(_ipt, '-D', 'OUTPUT', '-j', chain)
88+
nonfatal(_ipt, '-D', 'PREROUTING', '-j', chain)
89+
nonfatal(_ipt, '-F', chain)
90+
_ipt('-X', chain)

sshuttle/methods/pf.py

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -181,63 +181,70 @@ def setup_firewall(self, port, dnsport, nslist, family, subnets, udp):
181181
if udp:
182182
raise Exception("UDP not supported by pf method_name")
183183

184-
if subnets:
185-
includes = []
186-
# If a given subnet is both included and excluded, list the
187-
# exclusion first; the table will ignore the second, opposite
188-
# definition
189-
for f, swidth, sexclude, snet in sorted(
190-
subnets, key=lambda s: (s[1], s[2]), reverse=True):
191-
includes.append(b"%s%s/%d" %
192-
(b"!" if sexclude else b"",
193-
snet.encode("ASCII"),
194-
swidth))
195-
184+
includes = []
185+
# If a given subnet is both included and excluded, list the
186+
# exclusion first; the table will ignore the second, opposite
187+
# definition
188+
for f, swidth, sexclude, snet in sorted(
189+
subnets, key=lambda s: (s[1], s[2]), reverse=True):
190+
includes.append(b"%s%s/%d" %
191+
(b"!" if sexclude else b"",
192+
snet.encode("ASCII"),
193+
swidth))
194+
195+
tables.append(
196+
b'table <forward_subnets> {%s}' % b','.join(includes))
197+
translating_rules.append(
198+
b'rdr pass on lo0 proto tcp '
199+
b'to <forward_subnets> -> 127.0.0.1 port %r' % port)
200+
filtering_rules.append(
201+
b'pass out route-to lo0 inet proto tcp '
202+
b'to <forward_subnets> keep state')
203+
204+
if dnsport:
196205
tables.append(
197-
b'table <forward_subnets> {%s}' % b','.join(includes))
206+
b'table <dns_servers> {%s}' %
207+
b','.join([ns[1].encode("ASCII") for ns in nslist]))
198208
translating_rules.append(
199-
b'rdr pass on lo0 proto tcp '
200-
b'to <forward_subnets> -> 127.0.0.1 port %r' % port)
209+
b'rdr pass on lo0 proto udp to '
210+
b'<dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
201211
filtering_rules.append(
202-
b'pass out route-to lo0 inet proto tcp '
203-
b'to <forward_subnets> keep state')
204-
205-
if dnsport:
206-
tables.append(
207-
b'table <dns_servers> {%s}' %
208-
b','.join([ns[1].encode("ASCII") for ns in nslist]))
209-
translating_rules.append(
210-
b'rdr pass on lo0 proto udp to '
211-
b'<dns_servers> port 53 -> 127.0.0.1 port %r' % dnsport)
212-
filtering_rules.append(
213-
b'pass out route-to lo0 inet proto udp to '
214-
b'<dns_servers> port 53 keep state')
215-
216-
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
217-
+ b'\n'
218-
assert isinstance(rules, bytes)
219-
220-
pf_status = pfctl('-s all')[0]
221-
if b'\nrdr-anchor "sshuttle" all\n' not in pf_status:
222-
pf_add_anchor_rule(PF_RDR, "sshuttle")
223-
if b'\nanchor "sshuttle" all\n' not in pf_status:
224-
pf_add_anchor_rule(PF_PASS, "sshuttle")
225-
226-
pfctl('-a sshuttle -f /dev/stdin', rules)
227-
if sys.platform == "darwin":
228-
o = pfctl('-E')
229-
_pf_context['Xtoken'] = \
230-
re.search(b'Token : (.+)', o[1]).group(1)
231-
elif b'INFO:\nStatus: Disabled' in pf_status:
232-
pfctl('-e')
233-
_pf_context['started_by_sshuttle'] = True
234-
else:
235-
pfctl('-a sshuttle -F all')
236-
if sys.platform == "darwin":
237-
if _pf_context['Xtoken'] is not None:
238-
pfctl('-X %s' % _pf_context['Xtoken'].decode("ASCII"))
239-
elif _pf_context['started_by_sshuttle']:
240-
pfctl('-d')
212+
b'pass out route-to lo0 inet proto udp to '
213+
b'<dns_servers> port 53 keep state')
214+
215+
rules = b'\n'.join(tables + translating_rules + filtering_rules) \
216+
+ b'\n'
217+
assert isinstance(rules, bytes)
218+
219+
pf_status = pfctl('-s all')[0]
220+
if b'\nrdr-anchor "sshuttle" all\n' not in pf_status:
221+
pf_add_anchor_rule(PF_RDR, "sshuttle")
222+
if b'\nanchor "sshuttle" all\n' not in pf_status:
223+
pf_add_anchor_rule(PF_PASS, "sshuttle")
224+
225+
pfctl('-a sshuttle -f /dev/stdin', rules)
226+
if sys.platform == "darwin":
227+
o = pfctl('-E')
228+
_pf_context['Xtoken'] = \
229+
re.search(b'Token : (.+)', o[1]).group(1)
230+
elif b'INFO:\nStatus: Disabled' in pf_status:
231+
pfctl('-e')
232+
_pf_context['started_by_sshuttle'] = True
233+
234+
def restore_firewall(self, port, family, udp):
235+
if family != socket.AF_INET:
236+
raise Exception(
237+
'Address family "%s" unsupported by pf method_name'
238+
% family_to_string(family))
239+
if udp:
240+
raise Exception("UDP not supported by pf method_name")
241+
242+
pfctl('-a sshuttle -F all')
243+
if sys.platform == "darwin":
244+
if _pf_context['Xtoken'] is not None:
245+
pfctl('-X %s' % _pf_context['Xtoken'].decode("ASCII"))
246+
elif _pf_context['started_by_sshuttle']:
247+
pfctl('-d')
241248

242249
def firewall_command(self, line):
243250
if line.startswith('QUERY_PF_NAT '):

sshuttle/methods/tproxy.py

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -163,34 +163,22 @@ def _ipt_ttl(*args):
163163
divert_chain = 'sshuttle-d-%s' % port
164164

165165
# basic cleanup/setup of chains
166-
if ipt_chain_exists(family, table, mark_chain):
167-
_ipt('-D', 'OUTPUT', '-j', mark_chain)
168-
_ipt('-F', mark_chain)
169-
_ipt('-X', mark_chain)
166+
self.restore_firewall(port, family, udp)
170167

171-
if ipt_chain_exists(family, table, tproxy_chain):
172-
_ipt('-D', 'PREROUTING', '-j', tproxy_chain)
173-
_ipt('-F', tproxy_chain)
174-
_ipt('-X', tproxy_chain)
168+
_ipt('-N', mark_chain)
169+
_ipt('-F', mark_chain)
170+
_ipt('-N', divert_chain)
171+
_ipt('-F', divert_chain)
172+
_ipt('-N', tproxy_chain)
173+
_ipt('-F', tproxy_chain)
174+
_ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
175+
_ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
176+
_ipt('-A', divert_chain, '-j', 'MARK', '--set-mark', '1')
177+
_ipt('-A', divert_chain, '-j', 'ACCEPT')
178+
_ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
179+
'-m', 'tcp', '-p', 'tcp')
175180

176-
if ipt_chain_exists(family, table, divert_chain):
177-
_ipt('-F', divert_chain)
178-
_ipt('-X', divert_chain)
179-
180-
if subnets or dnsport:
181-
_ipt('-N', mark_chain)
182-
_ipt('-F', mark_chain)
183-
_ipt('-N', divert_chain)
184-
_ipt('-F', divert_chain)
185-
_ipt('-N', tproxy_chain)
186-
_ipt('-F', tproxy_chain)
187-
_ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
188-
_ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
189-
_ipt('-A', divert_chain, '-j', 'MARK', '--set-mark', '1')
190-
_ipt('-A', divert_chain, '-j', 'ACCEPT')
191-
_ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
192-
'-m', 'tcp', '-p', 'tcp')
193-
if subnets and udp:
181+
if udp:
194182
_ipt('-A', tproxy_chain, '-m', 'socket', '-j', divert_chain,
195183
'-m', 'udp', '-p', 'udp')
196184

@@ -205,34 +193,34 @@ def _ipt_ttl(*args):
205193
'-m', 'udp', '-p', 'udp', '--dport', '53',
206194
'--on-port', str(dnsport))
207195

208-
if subnets:
209-
for f, swidth, sexclude, snet \
210-
in sorted(subnets, key=lambda s: s[1], reverse=True):
211-
if sexclude:
212-
_ipt('-A', mark_chain, '-j', 'RETURN',
213-
'--dest', '%s/%s' % (snet, swidth),
214-
'-m', 'tcp', '-p', 'tcp')
215-
_ipt('-A', tproxy_chain, '-j', 'RETURN',
216-
'--dest', '%s/%s' % (snet, swidth),
217-
'-m', 'tcp', '-p', 'tcp')
218-
else:
219-
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
220-
'--dest', '%s/%s' % (snet, swidth),
221-
'-m', 'tcp', '-p', 'tcp')
222-
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
223-
'--tproxy-mark', '0x1/0x1',
224-
'--dest', '%s/%s' % (snet, swidth),
225-
'-m', 'tcp', '-p', 'tcp',
226-
'--on-port', str(port))
196+
for f, swidth, sexclude, snet \
197+
in sorted(subnets, key=lambda s: s[1], reverse=True):
198+
if sexclude:
199+
_ipt('-A', mark_chain, '-j', 'RETURN',
200+
'--dest', '%s/%s' % (snet, swidth),
201+
'-m', 'tcp', '-p', 'tcp')
202+
_ipt('-A', tproxy_chain, '-j', 'RETURN',
203+
'--dest', '%s/%s' % (snet, swidth),
204+
'-m', 'tcp', '-p', 'tcp')
205+
else:
206+
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
207+
'--dest', '%s/%s' % (snet, swidth),
208+
'-m', 'tcp', '-p', 'tcp')
209+
_ipt('-A', tproxy_chain, '-j', 'TPROXY',
210+
'--tproxy-mark', '0x1/0x1',
211+
'--dest', '%s/%s' % (snet, swidth),
212+
'-m', 'tcp', '-p', 'tcp',
213+
'--on-port', str(port))
227214

228-
if sexclude and udp:
215+
if udp:
216+
if sexclude:
229217
_ipt('-A', mark_chain, '-j', 'RETURN',
230218
'--dest', '%s/%s' % (snet, swidth),
231219
'-m', 'udp', '-p', 'udp')
232220
_ipt('-A', tproxy_chain, '-j', 'RETURN',
233221
'--dest', '%s/%s' % (snet, swidth),
234222
'-m', 'udp', '-p', 'udp')
235-
elif udp:
223+
else:
236224
_ipt('-A', mark_chain, '-j', 'MARK', '--set-mark', '1',
237225
'--dest', '%s/%s' % (snet, swidth),
238226
'-m', 'udp', '-p', 'udp')
@@ -242,6 +230,39 @@ def _ipt_ttl(*args):
242230
'-m', 'udp', '-p', 'udp',
243231
'--on-port', str(port))
244232

233+
def restore_firewall(self, port, family, udp):
234+
if family not in [socket.AF_INET, socket.AF_INET6]:
235+
raise Exception(
236+
'Address family "%s" unsupported by tproxy method'
237+
% family_to_string(family))
238+
239+
table = "mangle"
240+
241+
def _ipt(*args):
242+
return ipt(family, table, *args)
243+
244+
def _ipt_ttl(*args):
245+
return ipt_ttl(family, table, *args)
246+
247+
mark_chain = 'sshuttle-m-%s' % port
248+
tproxy_chain = 'sshuttle-t-%s' % port
249+
divert_chain = 'sshuttle-d-%s' % port
250+
251+
# basic cleanup/setup of chains
252+
if ipt_chain_exists(family, table, mark_chain):
253+
_ipt('-D', 'OUTPUT', '-j', mark_chain)
254+
_ipt('-F', mark_chain)
255+
_ipt('-X', mark_chain)
256+
257+
if ipt_chain_exists(family, table, tproxy_chain):
258+
_ipt('-D', 'PREROUTING', '-j', tproxy_chain)
259+
_ipt('-F', tproxy_chain)
260+
_ipt('-X', tproxy_chain)
261+
262+
if ipt_chain_exists(family, table, divert_chain):
263+
_ipt('-F', divert_chain)
264+
_ipt('-X', divert_chain)
265+
245266
def check_settings(self, udp, dns):
246267
if udp and recvmsg is None:
247268
Fatal("tproxy UDP support requires recvmsg function.\n")

sshuttle/tests/test_firewall.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,6 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
9797
2,
9898
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
9999
True),
100-
call().setup_firewall(1024, 0, [], 10, [], True),
101-
call().setup_firewall(1025, 0, [], 2, [], True),
100+
call().restore_firewall(1024, 10, True),
101+
call().restore_firewall(1025, 2, True),
102102
]

0 commit comments

Comments
 (0)