summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFata Nugraha <fata.nugraha@grabtaxi.com>2023-08-04 18:49:58 +0700
committerBrian May <brian@linuxpenguins.xyz>2023-08-07 20:00:32 +1000
commit6b7cf80420e16ca58fbb6e28ab4adb030436636a (patch)
treeea5353cd206501e182db892ea7a539e01543a99f
parentac06e7968fde6773d9dfb0a8d79a0cf94e8014ec (diff)
Add support for group-based routing
-rw-r--r--sshuttle/client.py17
-rw-r--r--sshuttle/cmdline.py1
-rw-r--r--sshuttle/methods/__init__.py1
-rw-r--r--sshuttle/methods/ipfw.py4
-rw-r--r--sshuttle/methods/nat.py28
-rw-r--r--sshuttle/methods/nft.py4
-rw-r--r--sshuttle/methods/pf.py4
-rw-r--r--sshuttle/methods/tproxy.py4
-rw-r--r--sshuttle/options.py6
9 files changed, 51 insertions, 18 deletions
diff --git a/sshuttle/client.py b/sshuttle/client.py
index 49bb7e2..25b3440 100644
--- a/sshuttle/client.py
+++ b/sshuttle/client.py
@@ -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:
diff --git a/sshuttle/cmdline.py b/sshuttle/cmdline.py
index b7ea43f..eaca961 100644
--- a/sshuttle/cmdline.py
+++ b/sshuttle/cmdline.py
@@ -104,6 +104,7 @@ def main():
opt.to_ns,
opt.pidfile,
opt.user,
+ opt.group,
opt.sudo_pythonpath,
opt.tmark)
diff --git a/sshuttle/methods/__init__.py b/sshuttle/methods/__init__.py
index 9aaf62e..4a1abe6 100644
--- a/sshuttle/methods/__init__.py
+++ b/sshuttle/methods/__init__.py
@@ -50,6 +50,7 @@ class BaseMethod(object):
result.udp = False
result.dns = True
result.user = False
+ result.group = False
return result
@staticmethod
diff --git a/sshuttle/methods/ipfw.py b/sshuttle/methods/ipfw.py
index 74fd9f7..053ddf3 100644
--- a/sshuttle/methods/ipfw.py
+++ b/sshuttle/methods/ipfw.py
@@ -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'
diff --git a/sshuttle/methods/nat.py b/sshuttle/methods/nat.py
index 076d880..de95008 100644
--- a/sshuttle/methods/nat.py
+++ b/sshuttle/methods/nat.py
@@ -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):
diff --git a/sshuttle/methods/nft.py b/sshuttle/methods/nft.py
index 64ab3a6..59b6310 100644
--- a/sshuttle/methods/nft.py
+++ b/sshuttle/methods/nft.py
@@ -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")
diff --git a/sshuttle/methods/pf.py b/sshuttle/methods/pf.py
index ed56c51..d5ed06a 100644
--- a/sshuttle/methods/pf.py
+++ b/sshuttle/methods/pf.py
@@ -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'
diff --git a/sshuttle/methods/tproxy.py b/sshuttle/methods/tproxy.py
index 1d2ae29..e12943c 100644
--- a/sshuttle/methods/tproxy.py
+++ b/sshuttle/methods/tproxy.py
@@ -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'
diff --git a/sshuttle/options.py b/sshuttle/options.py
index 0ac7690..acd46da 100644
--- a/sshuttle/options.py
+++ b/sshuttle/options.py
@@ -383,6 +383,12 @@ parser.add_argument(
"""
)
parser.add_argument(
+ "--group",
+ help="""
+ apply all the rules only to this linux group
+ """
+)
+parser.add_argument(
"--firewall",
action="store_true",
help="""