From b27b9d36de798bb18eaa95524f3900f9e17ce3e5 Mon Sep 17 00:00:00 2001 From: Csaba Henk Date: Tue, 20 Sep 2011 16:20:18 +0200 Subject: geo-rep: implement IP address based access control - gsyncd gets allow-network tunable which is expected to hold a comma-separated list of IP network addresses - for IP addess matching, bring in ipaddr module from Google (http://code.google.com/p/ipaddr-py/, rev. trunk@225) This will let users control master's access to slave's volumes until we implement unprivileged geo-rep (delayed due to some technical issues). It's also needed for the completeness of our hardening efforts, as plain file slaves won't be able to work with an unprivileged gsyncd. Change-Id: I58431cba6592f8672e93ea89a5eef478905b00b9 BUG: 2825 Reviewed-on: http://review.gluster.com/488 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur --- .../features/marker/utils/syncdaemon/Makefile.am | 3 +- .../marker/utils/syncdaemon/__codecheck.py | 61 ++++++++++++++-------- xlators/features/marker/utils/syncdaemon/gsyncd.py | 23 +++++++- 3 files changed, 64 insertions(+), 23 deletions(-) (limited to 'xlators/features/marker/utils') diff --git a/xlators/features/marker/utils/syncdaemon/Makefile.am b/xlators/features/marker/utils/syncdaemon/Makefile.am index ef2dc9aea2c..cc7cee102ea 100644 --- a/xlators/features/marker/utils/syncdaemon/Makefile.am +++ b/xlators/features/marker/utils/syncdaemon/Makefile.am @@ -1,5 +1,6 @@ syncdaemondir = $(libexecdir)/glusterfs/python/syncdaemon -syncdaemon_PYTHON = gconf.py gsyncd.py __init__.py master.py README.md repce.py resource.py configinterface.py syncdutils.py monitor.py libcxattr.py +syncdaemon_PYTHON = gconf.py gsyncd.py __init__.py master.py README.md repce.py resource.py configinterface.py syncdutils.py monitor.py libcxattr.py \ + $(top_builddir)/contrib/ipaddr-py/ipaddr.py CLEANFILES = diff --git a/xlators/features/marker/utils/syncdaemon/__codecheck.py b/xlators/features/marker/utils/syncdaemon/__codecheck.py index 832e75c444b..e3386afba8b 100644 --- a/xlators/features/marker/utils/syncdaemon/__codecheck.py +++ b/xlators/features/marker/utils/syncdaemon/__codecheck.py @@ -1,27 +1,46 @@ import os import os.path import sys +import tempfile +import shutil -fl = os.listdir(os.path.dirname(sys.argv[0]) or '.') -fl.sort() -for f in fl: - if f[-3:] != '.py' or f[0] == '_': - continue - m = f[:-3] - sys.stdout.write('importing %s ...' % m) - __import__(m) - print(' OK.') +ipd = tempfile.mkdtemp(prefix = 'codecheck-aux') -def sys_argv_set(a): - sys.argv = sys.argv[:1] + a +try: + # add a fake ipaddr module, we don't want to + # deal with the real one (just test our code) + f = open(os.path.join(ipd, 'ipaddr.py'), 'w') + f.write(""" +class IPAddress(object): + pass +class IPNetwork(list): + pass +""") + f.close() + sys.path.append(ipd) -gsyncd = sys.modules['gsyncd'] -for a in [['--help'], ['--version'], ['--canonicalize-escape-url', '/foo']]: - print('>>> invoking program with args: %s' % ' '.join(a)) - pid = os.fork() - if not pid: - sys_argv_set(a) - gsyncd.main() - _, r = os.waitpid(pid, 0) - if r: - raise RuntimeError('invocation failed') + fl = os.listdir(os.path.dirname(sys.argv[0]) or '.') + fl.sort() + for f in fl: + if f[-3:] != '.py' or f[0] == '_': + continue + m = f[:-3] + sys.stdout.write('importing %s ...' % m) + __import__(m) + print(' OK.') + + def sys_argv_set(a): + sys.argv = sys.argv[:1] + a + + gsyncd = sys.modules['gsyncd'] + for a in [['--help'], ['--version'], ['--canonicalize-escape-url', '/foo']]: + print('>>> invoking program with args: %s' % ' '.join(a)) + pid = os.fork() + if not pid: + sys_argv_set(a) + gsyncd.main() + _, r = os.waitpid(pid, 0) + if r: + raise RuntimeError('invocation failed') +finally: + shutil.rmtree(ipd) diff --git a/xlators/features/marker/utils/syncdaemon/gsyncd.py b/xlators/features/marker/utils/syncdaemon/gsyncd.py index 9771822dcea..f3b5988ade0 100644 --- a/xlators/features/marker/utils/syncdaemon/gsyncd.py +++ b/xlators/features/marker/utils/syncdaemon/gsyncd.py @@ -13,6 +13,8 @@ from optparse import OptionParser, SUPPRESS_HELP from logging import Logger from errno import EEXIST, ENOENT +from ipaddr import IPAddress, IPNetwork + from gconf import gconf from syncdutils import FreeObject, norm, grabpidfile, finalize, log_raise_exception from syncdutils import GsyncdError @@ -159,6 +161,7 @@ def main_i(): op.add_option('--timeout', metavar='SEC', type=int, default=120) op.add_option('--sync-jobs', metavar='N', type=int, default=3) op.add_option('--turns', metavar='N', type=int, default=0, help=SUPPRESS_HELP) + op.add_option('--allow-network', metavar='IPS', default='') op.add_option('-c', '--config-file', metavar='CONF', type=str, action='callback', callback=store_local) # duh. need to specify dest or value will be mapped to None :S @@ -208,7 +211,9 @@ def main_i(): sys.stderr.write(op.get_usage() + "\n") sys.exit(1) - if os.getenv('_GSYNCD_RESTRICTED_'): + restricted = os.getenv('_GSYNCD_RESTRICTED_') + + if restricted: allopts = {} allopts.update(opts.__dict__) allopts.update(rconf) @@ -288,6 +293,22 @@ def main_i(): gconf.__dict__.update(opts.__dict__) gconf.configinterface = gcnf + if restricted and gconf.allow_network: + ssh_conn = os.getenv('SSH_CONNECTION') + if not ssh_conn: + #legacy env var + ssh_conn = os.getenv('SSH_CLIENT') + if ssh_conn: + allowed_networks = [ IPNetwork(a) for a in gconf.allow_network.split(',') ] + client_ip = IPAddress(ssh_conn.split()[0]) + allowed = False + for nw in allowed_networks: + if client_ip in nw: + allowed = True + break + if not allowed: + raise GsyncdError("client IP address is not allowed") + ffd = rconf.get('feedback_fd') if ffd: fcntl.fcntl(ffd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) -- cgit