Add support for group-based routing

This commit is contained in:
Fata Nugraha 2023-08-04 18:49:58 +07:00 committed by Brian May
parent ac06e7968f
commit 6b7cf80420
9 changed files with 51 additions and 18 deletions

View File

@ -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:

View File

@ -104,6 +104,7 @@ def main():
opt.to_ns,
opt.pidfile,
opt.user,
opt.group,
opt.sudo_pythonpath,
opt.tmark)

View File

@ -50,6 +50,7 @@ class BaseMethod(object):
result.udp = False
result.dns = True
result.user = False
result.group = False
return result
@staticmethod

View File

@ -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'

View File

@ -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):

View File

@ -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")

View File

@ -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'

View File

@ -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'

View File

@ -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",