mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-02-11 16:09:14 +01:00
Add support for group-based routing
This commit is contained in:
parent
ac06e7968f
commit
6b7cf80420
@ -21,6 +21,10 @@ try:
|
||||
from pwd import getpwnam
|
||||
except ImportError:
|
||||
getpwnam = None
|
||||
try:
|
||||
from grp import getgrnam
|
||||
except ImportError:
|
||||
getgrnam = None
|
||||
|
||||
import socket
|
||||
|
||||
@ -726,7 +730,7 @@ def main(listenip_v6, listenip_v4,
|
||||
latency_buffer_size, dns, nslist,
|
||||
method_name, seed_hosts, auto_hosts, auto_nets,
|
||||
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
|
||||
user, sudo_pythonpath, tmark):
|
||||
user, group, sudo_pythonpath, tmark):
|
||||
|
||||
if not remotename:
|
||||
raise Fatal("You must use -r/--remote to specify a remote "
|
||||
@ -829,6 +833,15 @@ def main(listenip_v6, listenip_v4,
|
||||
raise Fatal("User %s does not exist." % user)
|
||||
required.user = False if user is None else True
|
||||
|
||||
if group is not None:
|
||||
if getgrnam is None:
|
||||
raise Fatal("Routing by group not available on this system.")
|
||||
try:
|
||||
group = getgrnam(group).gr_gid
|
||||
except KeyError:
|
||||
raise Fatal("User %s does not exist." % user)
|
||||
required.group = False if group is None else True
|
||||
|
||||
if not required.ipv6 and len(subnets_v6) > 0:
|
||||
print("WARNING: IPv6 subnets were ignored because IPv6 is disabled "
|
||||
"in sshuttle.")
|
||||
@ -1058,7 +1071,7 @@ def main(listenip_v6, listenip_v4,
|
||||
# start the firewall
|
||||
fw.setup(subnets_include, subnets_exclude, nslist,
|
||||
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
|
||||
required.udp, user, tmark)
|
||||
required.udp, user, group, tmark)
|
||||
|
||||
# start the client process
|
||||
try:
|
||||
|
@ -104,6 +104,7 @@ def main():
|
||||
opt.to_ns,
|
||||
opt.pidfile,
|
||||
opt.user,
|
||||
opt.group,
|
||||
opt.sudo_pythonpath,
|
||||
opt.tmark)
|
||||
|
||||
|
@ -50,6 +50,7 @@ class BaseMethod(object):
|
||||
result.udp = False
|
||||
result.dns = True
|
||||
result.user = False
|
||||
result.group = False
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
@ -156,7 +156,7 @@ class Method(BaseMethod):
|
||||
# udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1)
|
||||
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||
user, tmark):
|
||||
user, group, tmark):
|
||||
# IPv6 not supported
|
||||
if family not in [socket.AF_INET]:
|
||||
raise Exception(
|
||||
@ -207,7 +207,7 @@ class Method(BaseMethod):
|
||||
else:
|
||||
ipfw('table', '126', 'add', '%s/%s' % (snet, swidth))
|
||||
|
||||
def restore_firewall(self, port, family, udp, user):
|
||||
def restore_firewall(self, port, family, udp, user, group):
|
||||
if family not in [socket.AF_INET]:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by ipfw method'
|
||||
|
@ -13,7 +13,7 @@ class Method(BaseMethod):
|
||||
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
||||
# "-A OUTPUT").
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||
user, tmark):
|
||||
user, group, tmark):
|
||||
if family != socket.AF_INET and family != socket.AF_INET6:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by nat method_name'
|
||||
@ -35,9 +35,14 @@ class Method(BaseMethod):
|
||||
|
||||
_ipt('-N', chain)
|
||||
_ipt('-F', chain)
|
||||
if user is not None:
|
||||
_ipm('-I', 'OUTPUT', '1', '-m', 'owner', '--uid-owner', str(user),
|
||||
'-j', 'MARK', '--set-mark', str(port))
|
||||
if user is not None or group is not None:
|
||||
margs = ['-I', 'OUTPUT', '1', '-m', 'owner']
|
||||
if user is not None:
|
||||
margs.append('--uid-owner', str(user))
|
||||
if group is not None:
|
||||
margs.append('--gid-owner', str(group))
|
||||
margs = args.append('-j', 'MARK', '--set-mark', str(port))
|
||||
nonfatal(_ipm, *margs)
|
||||
args = '-m', 'mark', '--mark', str(port), '-j', chain
|
||||
else:
|
||||
args = '-j', chain
|
||||
@ -75,7 +80,7 @@ class Method(BaseMethod):
|
||||
'--dest', '%s/%s' % (snet, swidth),
|
||||
*(tcp_ports + ('--to-ports', str(port))))
|
||||
|
||||
def restore_firewall(self, port, family, udp, user):
|
||||
def restore_firewall(self, port, family, udp, user, group):
|
||||
# only ipv4 supported with NAT
|
||||
if family != socket.AF_INET and family != socket.AF_INET6:
|
||||
raise Exception(
|
||||
@ -96,9 +101,15 @@ class Method(BaseMethod):
|
||||
|
||||
# basic cleanup/setup of chains
|
||||
if ipt_chain_exists(family, table, chain):
|
||||
if user is not None:
|
||||
nonfatal(_ipm, '-D', 'OUTPUT', '-m', 'owner', '--uid-owner',
|
||||
str(user), '-j', 'MARK', '--set-mark', str(port))
|
||||
if user is not None or group is not None:
|
||||
margs = ['-D', 'OUTPUT', '-m', 'owner']
|
||||
if user is not None:
|
||||
margs.append('--uid-owner', str(user))
|
||||
if group is not None:
|
||||
margs.append('--gid-owner', str(group))
|
||||
margs = args.append('-j', 'MARK', '--set-mark', str(port))
|
||||
nonfatal(_ipm, *margs)
|
||||
|
||||
args = '-m', 'mark', '--mark', str(port), '-j', chain
|
||||
else:
|
||||
args = '-j', chain
|
||||
@ -111,6 +122,7 @@ class Method(BaseMethod):
|
||||
result = super(Method, self).get_supported_features()
|
||||
result.user = True
|
||||
result.ipv6 = True
|
||||
result.group = True
|
||||
return result
|
||||
|
||||
def is_supported(self):
|
||||
|
@ -13,7 +13,7 @@ class Method(BaseMethod):
|
||||
# recently-started one will win (because we use "-I OUTPUT 1" instead of
|
||||
# "-A OUTPUT").
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||
user, tmark):
|
||||
user, group, tmark):
|
||||
if udp:
|
||||
raise Exception("UDP not supported by nft")
|
||||
|
||||
@ -87,7 +87,7 @@ class Method(BaseMethod):
|
||||
ip_version, 'daddr %s/%s' % (snet, swidth),
|
||||
('redirect to :' + str(port)))))
|
||||
|
||||
def restore_firewall(self, port, family, udp, user):
|
||||
def restore_firewall(self, port, family, udp, user, group):
|
||||
if udp:
|
||||
raise Exception("UDP not supported by nft method_name")
|
||||
|
||||
|
@ -448,7 +448,7 @@ class Method(BaseMethod):
|
||||
return sock.getsockname()
|
||||
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||
user, tmark):
|
||||
user, group, tmark):
|
||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by pf method_name'
|
||||
@ -473,7 +473,7 @@ class Method(BaseMethod):
|
||||
pf.add_rules(anchor, includes, port, dnsport, nslist, family)
|
||||
pf.enable()
|
||||
|
||||
def restore_firewall(self, port, family, udp, user):
|
||||
def restore_firewall(self, port, family, udp, user, group):
|
||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by pf method_name'
|
||||
|
@ -114,7 +114,7 @@ class Method(BaseMethod):
|
||||
udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
|
||||
|
||||
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
|
||||
user, tmark):
|
||||
user, group, tmark):
|
||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by tproxy method'
|
||||
@ -228,7 +228,7 @@ class Method(BaseMethod):
|
||||
'-m', 'udp',
|
||||
*(udp_ports + ('--on-port', str(port))))
|
||||
|
||||
def restore_firewall(self, port, family, udp, user):
|
||||
def restore_firewall(self, port, family, udp, user, group):
|
||||
if family not in [socket.AF_INET, socket.AF_INET6]:
|
||||
raise Exception(
|
||||
'Address family "%s" unsupported by tproxy method'
|
||||
|
@ -382,6 +382,12 @@ parser.add_argument(
|
||||
apply all the rules only to this linux user
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
"--group",
|
||||
help="""
|
||||
apply all the rules only to this linux group
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
"--firewall",
|
||||
action="store_true",
|
||||
|
Loading…
Reference in New Issue
Block a user