mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-02-14 09:30:59 +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
|
from pwd import getpwnam
|
||||||
except ImportError:
|
except ImportError:
|
||||||
getpwnam = None
|
getpwnam = None
|
||||||
|
try:
|
||||||
|
from grp import getgrnam
|
||||||
|
except ImportError:
|
||||||
|
getgrnam = None
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
@ -726,7 +730,7 @@ 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):
|
user, group, sudo_pythonpath, tmark):
|
||||||
|
|
||||||
if not remotename:
|
if not remotename:
|
||||||
raise Fatal("You must use -r/--remote to specify a remote "
|
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)
|
raise Fatal("User %s does not exist." % user)
|
||||||
required.user = False if user is None else True
|
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:
|
if not required.ipv6 and len(subnets_v6) > 0:
|
||||||
print("WARNING: IPv6 subnets were ignored because IPv6 is disabled "
|
print("WARNING: IPv6 subnets were ignored because IPv6 is disabled "
|
||||||
"in sshuttle.")
|
"in sshuttle.")
|
||||||
@ -1058,7 +1071,7 @@ 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)
|
required.udp, user, group, tmark)
|
||||||
|
|
||||||
# start the client process
|
# start the client process
|
||||||
try:
|
try:
|
||||||
|
@ -104,6 +104,7 @@ def main():
|
|||||||
opt.to_ns,
|
opt.to_ns,
|
||||||
opt.pidfile,
|
opt.pidfile,
|
||||||
opt.user,
|
opt.user,
|
||||||
|
opt.group,
|
||||||
opt.sudo_pythonpath,
|
opt.sudo_pythonpath,
|
||||||
opt.tmark)
|
opt.tmark)
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ class BaseMethod(object):
|
|||||||
result.udp = False
|
result.udp = False
|
||||||
result.dns = True
|
result.dns = True
|
||||||
result.user = False
|
result.user = False
|
||||||
|
result.group = False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -156,7 +156,7 @@ 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, tmark):
|
user, group, tmark):
|
||||||
# IPv6 not supported
|
# IPv6 not supported
|
||||||
if family not in [socket.AF_INET]:
|
if family not in [socket.AF_INET]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
@ -207,7 +207,7 @@ class Method(BaseMethod):
|
|||||||
else:
|
else:
|
||||||
ipfw('table', '126', 'add', '%s/%s' % (snet, swidth))
|
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]:
|
if family not in [socket.AF_INET]:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Address family "%s" unsupported by ipfw method'
|
'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
|
# 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, tmark):
|
user, group, tmark):
|
||||||
if family != socket.AF_INET and family != socket.AF_INET6:
|
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'
|
||||||
@ -35,9 +35,14 @@ class Method(BaseMethod):
|
|||||||
|
|
||||||
_ipt('-N', chain)
|
_ipt('-N', chain)
|
||||||
_ipt('-F', chain)
|
_ipt('-F', chain)
|
||||||
if user is not None:
|
if user is not None or group is not None:
|
||||||
_ipm('-I', 'OUTPUT', '1', '-m', 'owner', '--uid-owner', str(user),
|
margs = ['-I', 'OUTPUT', '1', '-m', 'owner']
|
||||||
'-j', 'MARK', '--set-mark', str(port))
|
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
|
args = '-m', 'mark', '--mark', str(port), '-j', chain
|
||||||
else:
|
else:
|
||||||
args = '-j', chain
|
args = '-j', chain
|
||||||
@ -75,7 +80,7 @@ class Method(BaseMethod):
|
|||||||
'--dest', '%s/%s' % (snet, swidth),
|
'--dest', '%s/%s' % (snet, swidth),
|
||||||
*(tcp_ports + ('--to-ports', str(port))))
|
*(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
|
# only ipv4 supported with NAT
|
||||||
if family != socket.AF_INET and family != socket.AF_INET6:
|
if family != socket.AF_INET and family != socket.AF_INET6:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
@ -96,9 +101,15 @@ class Method(BaseMethod):
|
|||||||
|
|
||||||
# basic cleanup/setup of chains
|
# basic cleanup/setup of chains
|
||||||
if ipt_chain_exists(family, table, chain):
|
if ipt_chain_exists(family, table, chain):
|
||||||
if user is not None:
|
if user is not None or group is not None:
|
||||||
nonfatal(_ipm, '-D', 'OUTPUT', '-m', 'owner', '--uid-owner',
|
margs = ['-D', 'OUTPUT', '-m', 'owner']
|
||||||
str(user), '-j', 'MARK', '--set-mark', str(port))
|
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
|
args = '-m', 'mark', '--mark', str(port), '-j', chain
|
||||||
else:
|
else:
|
||||||
args = '-j', chain
|
args = '-j', chain
|
||||||
@ -111,6 +122,7 @@ class Method(BaseMethod):
|
|||||||
result = super(Method, self).get_supported_features()
|
result = super(Method, self).get_supported_features()
|
||||||
result.user = True
|
result.user = True
|
||||||
result.ipv6 = True
|
result.ipv6 = True
|
||||||
|
result.group = 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, tmark):
|
user, group, tmark):
|
||||||
if udp:
|
if udp:
|
||||||
raise Exception("UDP not supported by nft")
|
raise Exception("UDP not supported by nft")
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class Method(BaseMethod):
|
|||||||
ip_version, 'daddr %s/%s' % (snet, swidth),
|
ip_version, 'daddr %s/%s' % (snet, swidth),
|
||||||
('redirect to :' + str(port)))))
|
('redirect to :' + str(port)))))
|
||||||
|
|
||||||
def restore_firewall(self, port, family, udp, user):
|
def restore_firewall(self, port, family, udp, user, group):
|
||||||
if udp:
|
if udp:
|
||||||
raise Exception("UDP not supported by nft method_name")
|
raise Exception("UDP not supported by nft method_name")
|
||||||
|
|
||||||
|
@ -448,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, tmark):
|
user, group, 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'
|
||||||
@ -473,7 +473,7 @@ class Method(BaseMethod):
|
|||||||
pf.add_rules(anchor, includes, port, dnsport, nslist, family)
|
pf.add_rules(anchor, includes, port, dnsport, nslist, family)
|
||||||
pf.enable()
|
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]:
|
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'
|
||||||
|
@ -114,7 +114,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, tmark):
|
user, group, 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'
|
||||||
@ -228,7 +228,7 @@ class Method(BaseMethod):
|
|||||||
'-m', 'udp',
|
'-m', 'udp',
|
||||||
*(udp_ports + ('--on-port', str(port))))
|
*(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]:
|
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'
|
||||||
|
@ -382,6 +382,12 @@ parser.add_argument(
|
|||||||
apply all the rules only to this linux user
|
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(
|
parser.add_argument(
|
||||||
"--firewall",
|
"--firewall",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
Loading…
Reference in New Issue
Block a user