Author: challngr Date: Tue May 5 18:27:36 2015 New Revision: 1677876 URL: http://svn.apache.org/r1677876 Log: UIMA-4358 Remove single-user mode. Do permission checking in post-install and abort if it looks wrong.
Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/check_ducc uima/sandbox/uima-ducc/trunk/src/main/admin/ducc.py uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_post_install uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_util.py uima/sandbox/uima-ducc/trunk/src/main/admin/start_ducc Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/check_ducc URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/src/main/admin/check_ducc?rev=1677876&r1=1677875&r2=1677876&view=diff ============================================================================== --- uima/sandbox/uima-ducc/trunk/src/main/admin/check_ducc (original) +++ uima/sandbox/uima-ducc/trunk/src/main/admin/check_ducc Tue May 5 18:27:36 2015 @@ -44,7 +44,11 @@ class CheckDucc(DuccUtil): self.check_clock_skew(checkdate) self.verify_jvm() self.verify_limits() - self.verify_duccling(self.single_user) + (viable, elevated, safe) = self.verify_duccling() + self.duccling_ok(viable, elevated, safe) + if ( not safe or not viable ): + print 'NOTOK ducc_ling is not installed correctly.' + return def verify_activemq(self): @@ -103,10 +107,7 @@ class CheckDucc(DuccUtil): if ( self.kill_signal == None ): response = "Node health checks return." - if ( self.single_user ) : - lines = self.ssh(node, True, self.DUCC_HOME + "/admin/check_ducc", "-s", "-x", str(int(time()))) - else: - lines = self.ssh(node, True, self.DUCC_HOME + "/admin/check_ducc", "-x", str(int(time()))) + lines = self.ssh(node, True, self.DUCC_HOME + "/admin/check_ducc", "-x", str(int(time()))) while 1: line = lines.readline() if ( 'signal' in line ): @@ -171,9 +172,6 @@ class CheckDucc(DuccUtil): print " the PID file needs rebuilding. This option causes the file to be rebuilt regardless of" print " changes." print "" - print " -s --singleuser" - print " Bypasses the multi-user ducc_ling checks." - print "" print " -x localdate" print " Validate the local installation, called via ssh usually. The date is the dat on the calling machine." print "" @@ -189,7 +187,7 @@ class CheckDucc(DuccUtil): def main(self, argv): try: - opts, args = getopt.getopt(argv, 'cikn:opqsx:h?v', ['configuration', 'nodelist=', 'int', 'quit', 'kill', 'pids', 'verbose', 'nothreading', 'singleuser']) + opts, args = getopt.getopt(argv, 'cikn:opqx:h?v', ['configuration', 'nodelist=', 'int', 'quit', 'kill', 'pids', 'verbose', 'nothreading', ]) except: self.usage("Invalid arguments " + ' '.join(argv)) @@ -202,7 +200,6 @@ class CheckDucc(DuccUtil): checkdate = 0 config_only = False verbose = False - self.single_user = False for ( o, a ) in opts: if o in ('-c', '--configuration'): @@ -226,8 +223,6 @@ class CheckDucc(DuccUtil): self.kill_signal = '-KILL' elif o in ( '--nothreading' ): self.disable_threading() - elif o in ('-s', '--singleuser' ): - self.single_user = True elif o in ('-p', '--pids'): redo_pids = True elif o in ('-x'): Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/ducc.py URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/src/main/admin/ducc.py?rev=1677876&r1=1677875&r2=1677876&view=diff ============================================================================== --- uima/sandbox/uima-ducc/trunk/src/main/admin/ducc.py (original) +++ uima/sandbox/uima-ducc/trunk/src/main/admin/ducc.py Tue May 5 18:27:36 2015 @@ -65,7 +65,7 @@ class Ducc(DuccUtil): def add_to_classpath(self, lib): os.environ['CLASSPATH'] = os.environ['CLASSPATH'] + ":" + lib - def run_component(self, component, or_parms, numagents, rmoverride, background, nodup, localdate, single_user): + def run_component(self, component, or_parms, numagents, rmoverride, background, nodup, localdate): if ( component == 'all' ): component = 'rm,sm,pm,ws,orchestrator' @@ -101,14 +101,10 @@ class Ducc(DuccUtil): if ( not self.verify_limits() ): return - if ( not single_user ) : - dok = self.verify_duccling(single_user) - if ( not dok ): - print 'NOTOK ducc_ling is not set up correctly on node', self.localhost - print dok - return - else: - print 'Single user mode: bypassing ducc_ling checks.' + (viable, elevated, safe) = self.verify_duccling() + if ( not self.duccling_ok(viable, elevated, safe) ): + print 'NOT_OK Cannot proceed because of ducc_ling problems.' + return if ( not verify_slave_node(localdate, self.ducc_properties) ): # we assume that verify_local_node is spewing a line of the form @@ -219,8 +215,7 @@ class Ducc(DuccUtil): pid = self.nohup(cmd) else: pid = self.spawn(' '.join(cmd)) - print 'PID ' + str(pid) # nohup will print this from the (twice) forked process if background - # hard for us to access it here in nohup + print 'PID ' + str(pid) if ( c == 'ws' ): os.chdir(here) @@ -243,7 +238,6 @@ class Ducc(DuccUtil): print ' -n <numagents> if > 1, multiple agents are started (testing mode)' print ' -o <mem-in-GB> rm memory override for use on small machines' print ' -k causes the entire DUCC system to shutdown' - print ' -s start in single-user mode (inhibit some sanity checks)' print ' --nodup If specified, do not start a process if it appears to be already started.' print ' --or_parms [cold|warm|hot]' print ' --ducc_head nodename the name of the "ducc head" where ducc is started from' @@ -259,7 +253,6 @@ class Ducc(DuccUtil): shutdown = False background = False or_parms = None - single_user = False nodup = False # we allow duplicates unless asked not to localdate = time.time() @@ -283,8 +276,6 @@ class Ducc(DuccUtil): rmoverride = a elif ( o == '-k'): shutdown = True - elif ( o == '-s'): - single_user = True elif ( o == '--or_parms' ): or_parms = a elif ( o == '--nodup' ): @@ -308,7 +299,7 @@ class Ducc(DuccUtil): if ( component == 'broker' ): self.run_broker(background) else: - self.run_component(component, or_parms, numagents, rmoverride, background, nodup, localdate, single_user) + self.run_component(component, or_parms, numagents, rmoverride, background, nodup, localdate) return def __call__(self, *args): Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_post_install URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_post_install?rev=1677876&r1=1677875&r2=1677876&view=diff ============================================================================== --- uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_post_install (original) +++ uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_post_install Tue May 5 18:27:36 2015 @@ -25,6 +25,7 @@ import getopt import shutil import subprocess +from stat import * from ducc_util import DuccUtil from ducc_base import Properties @@ -243,8 +244,69 @@ class PostInstall(): shutil.move(fn, bak) print 'Existing', fn, 'moved to', bak + def verify_permissions(self): + # should have 755 permissions + spot_checked_directories = ['../bin', '../lib', '../resources' ] + # should have 755 permissions + spot_checked_execs = ['../bin/ducc_submit'] + # should have 644 permissions + spot_checked_data = ['../lib/uima-ducc-cli.jar', '../resources/default.ducc.properties'] + + ret = True + for f in spot_checked_directories: + if ( not os.path.exists(f) ): + print 'ERROR: Directory', f, 'cannot be found.' + ret = false + continue + + stat = os.stat(f) + mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + expected = oct(0755) + if ( mode != expected ): + print 'ERROR: Directory', f, 'has permissions', mode, 'expected', expected + ret = False + + for f in spot_checked_execs: + if ( not os.path.exists(f) ): + print 'ERROR: File', f, 'cannot be found.' + ret = False + continue + + stat = os.stat(f) + mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + expected = oct(0755) + if ( mode != expected ): + print 'ERROR: File', f, 'has permissions', mode, 'expected', expected + ret = False + + for f in spot_checked_data: + if ( not os.path.exists(f) ): + print 'ERROR: File', f, 'cannot be found.' + ret = False + continue + + stat = os.stat(f) + mode = oct(stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + expected = oct(0644) + if ( mode != expected ): + print 'ERROR: File', f, 'has permissions', mode, 'expected', expected + ret = False + + return ret + def main(self, argv): + if ( not self.verify_permissions() ): + print '--------------------------------------------------------------------------------' + print 'Package verificaiton fails. Most likely cause is an unexpected UMASK unpacking the distribution.' + print 'To unpack the distribution your UMASK must be set to 022.' + print '' + print 'Example:' + print '' + print 'umask 022; tar -xf [distribution]' + print '--------------------------------------------------------------------------------' + sys.exit(1) + self.ducc_head = None self.keystore_pw = None self.path_to_java = None Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_util.py URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_util.py?rev=1677876&r1=1677875&r2=1677876&view=diff ============================================================================== --- uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_util.py (original) +++ uima/sandbox/uima-ducc/trunk/src/main/admin/ducc_util.py Tue May 5 18:27:36 2015 @@ -338,59 +338,89 @@ class DuccUtil(DuccBase): return False return True - def verify_duccling(self, single_user): + # + # Verify the viability of ducc_ling. + # Returns a tuple (viable, elevated, safe) + # where 'viable' is a boolean indicating whether the ducc_ling is viable (exists and is correct version) + # If this is false, the other two values are meaningless + # + # 'elevated' indicates whether priveleges are at least partially elevated + # 'safe' indicates whether ducc_ling is safe (elevated privileges and correct permissions) + # The caller will evaluate these and take appriopriate action + # + def verify_duccling(self): + viable = True + elevated = False + safe = False + + dl = self.duccling + path = os.path.dirname(os.path.abspath(dl)) + - check_permission = True # if we're not ducc we don't care about permissions - user = os.environ['LOGNAME'] - if ( (user != 'ducc') or ( single_user) ): - check_permission = False - - if ( check_permission ) : # only care about ducc_ling setup if we're ducc - path = os.path.dirname(os.path.abspath(self.duccling)) - dl = path + '/ducc_ling' - - sstat = os.stat(path) - mode = sstat.st_mode - if ( not S_ISDIR(mode) ): - print 'ducc_ling path', path, ': Not a directory.' - return False - - dirown = mode & (S_IRWXU | S_IRWXG | S_IRWXO) - #print 'Directory perms', oct(dirown) - if ( dirown != S_IRWXU ): - print 'ducc_ling path', path, ': Invalid directory permissions', oct(dirown), 'should be', oct(S_IRWXU) - return False - - sstat = os.stat(dl) - mode = sstat.st_mode - expected = (S_IRWXU | S_IRGRP | S_IXGRP) - pathown = mode & (S_IRWXU | S_IRWXG | S_IRWXO) - #print 'Duccling perms', oct(pathown) - if ( pathown != expected ): - print 'ducc_ling module', dl, ': Invalid permissions', oct(pathown), 'Should be', oct(expected) - return False - - if ( (mode & S_ISUID) != S_ISUID): - print 'ducc_ling module', dl, ': setuid bit is not set' - return False - + if ( not (os.path.exists(dl) and os.access(dl, os.X_OK)) ): + print dl, 'does not exist or is not executable.' + viable = False + + path = os.path.dirname(os.path.abspath(dl)) + dl = path + '/ducc_ling' + + dl_stat = os.stat(dl) # dl_stat is stat for ducc_ling + dl_mode = dl_stat.st_mode + + if ( (dl_mode & S_ISUID) != S_ISUID): + if ( os.environ['LOGNAME'] == 'ducc' ): + print 'ducc_ling module', dl, ': setuid bit is not set. Processes will run as user ducc' + elevated = False + file_safe = True + dir_safe = True + own_safe = True + else: + elevated = True + file_safe = False + dir_safe = False + own_safe = False + + + if ( elevated ): + + # + # if setuid bit is set, all this MUST be true or we won't mark ducc_ling safe: + # file permissions are 750 + # dir permissions are 700 + # owenership is root.ducc + # + dl_perm = oct(dl_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + expected = oct(0750) + if ( dl_perm != expected ): + print dl, ': Invalid execution bits', dl_perm, 'should be', expected + else: + file_safe = True + + dir_stat = os.stat(path) # dir_stat is stat for ducc_ling + dir_mode = dir_stat.st_mode + + dir_perm = oct(dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) + expected = oct(0700) + if ( dir_perm != expected ): + print 'ducc_ling', path, ': Invalid directory permissions', dir_perm, 'should be', expected + else: + dir_safe = True + try: grpinfo = grp.getgrnam('ducc') + duccgid = grpinfo.gr_gid + + if ( (dl_stat.st_uid != 0) or (dl_stat.st_gid != duccgid) ): + print 'ducc_ling module', dl, ': Invalid ownership. Should be root.ducc' + else: + own_safe = True except: print 'ducc_ling group "ducc" cannot be found.' - return False - duccgid = grpinfo.gr_gid - #print 'UID', sstat.st_uid, 'GID', duccgid - if ( (sstat.st_uid != 0) or (sstat.st_gid != duccgid) ): - print 'ducc_ling module', dl, ': Invalid ownership. Should be root.ducc' - return False - else: - if ( not os.path.exists(self.duccling) ): - print "Missing ducc_ling" - return False - - # now make sure the version matches that on the master node + safe = file_safe and dir_safe and own_safe + + # A last viability check, do versions match? This also runs it, proving it + # can execute in this environment. lines = self.popen(self.duccling + ' -v') version_from_head = lines.readline().strip(); toks = version_from_head.split() @@ -407,13 +437,40 @@ class DuccUtil(DuccBase): print "Mismatched ducc_ling versions:" print "ALERT: Version on Agent Node:", version_from_head print "ALERT: Version on Ducc Head:", line - return False + viable = False verfile.close() else: - print "ducc_ling version file missing, cannot verify version." - return False; + print "NOTE: ducc_ling version file missing, cannot verify version." + + # leave the decisions to the caller + return (viable, elevated, safe) + + # Apply these rules to determine if ducc_ling is installed ok + # + # Caller Elevated Protected (safe) Action + # -------- -------- --------- ------ + # ducc Y Y OK + # Y N Fail + # + # N Y (by def) OK + # + # ~ducc Y N (by def) Fail + # N Y (by def) OK, Note + def duccling_ok(self, viable, elevated, safe): + + if ( not viable ): + return False + + user = os.environ['LOGNAME'] + + if ( user == 'ducc' ): + if ( elevated ): + return safe + else: + if ( elevated ): + return False + print 'Note: Running unprivileged ducc_ling. Process will run as user', user - print 'ducc_ling OK' return True def ssh_ok(self, node, line): Modified: uima/sandbox/uima-ducc/trunk/src/main/admin/start_ducc URL: http://svn.apache.org/viewvc/uima/sandbox/uima-ducc/trunk/src/main/admin/start_ducc?rev=1677876&r1=1677875&r2=1677876&view=diff ============================================================================== --- uima/sandbox/uima-ducc/trunk/src/main/admin/start_ducc (original) +++ uima/sandbox/uima-ducc/trunk/src/main/admin/start_ducc Tue May 5 18:27:36 2015 @@ -63,7 +63,7 @@ class StartDucc(DuccUtil): def start_component(self, args): - ducc, component, or_parms, single_user = args + ducc, component, or_parms = args msgs = [] node = self.ducc_properties.get('ducc.head') @@ -91,10 +91,7 @@ class StartDucc(DuccUtil): if ( node == 'local' ): node = self.localhost - if ( single_user ): - lines = self.ssh(node, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c', com, '-s', '-b', or_parms, '-d', str(time.time()), '--nodup', "'") - else: - lines = self.ssh(node, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c', com, '-b', or_parms, '-d', str(time.time()), '--nodup', "'") + lines = self.ssh(node, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c', com, '-b', or_parms, '-d', str(time.time()), '--nodup', "'") # we'll capture anything that the python shell spews because it may be useful, and then drop the # pipe when we see a PID message @@ -121,18 +118,15 @@ class StartDucc(DuccUtil): def start_one_agent(self, args): - host, single_user = args + host = args[0] msgs = [] spacer = ' ' msgs.append((host, "")) - if (single_user): - lines = self.ssh(host, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c' 'agent', '-s', '-b', '-d', str(time.time()), '--nodup', "'") - else: - lines = self.ssh(host, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c' 'agent', '-b', '-d', str(time.time()), '--nodup', "'") - - while 1: - line = lines.readline().strip() - #msgs.append(('[l]', line)) + lines = self.ssh(host, True, "'", self.DUCC_HOME + '/admin/ducc.py', '-c' 'agent', '-b', '-d', str(time.time()), '--nodup', "'") + for line in lines: + line = line.strip() + # print '[]', host, line + # msgs.append(('[l]', line)) if ( line.startswith('PID') ): toks = line.split(' ') pid = toks[1] @@ -144,12 +138,16 @@ class StartDucc(DuccUtil): msgs.append((spacer, 'DUCC Agent started PID', pid)) break - if ( not line ): - break + if ( 'tty' in line ): + # ssh junk if mesg is set + continue + toks = line.split() - if ( not self.ssh_ok(host, line ) ): - break; + sshmsgs = self.ssh_ok(host, line ) + if ( sshmsgs != None ): + for m in sshmsgs: + print '[S]', m if ( toks[0] == 'NOTOK' ): msgs.append((spacer, 'NOTOK Not started:', ' '.join(toks[1:]))) @@ -180,10 +178,6 @@ class StartDucc(DuccUtil): print "" print " start_ducc -n foo.nodes -n bar.nodes -n baz.nodes" print "" - print " -s --singleuser" - print " Start ducc in 'single user mode'. This bypasses some checking required for multi-user" - print " mode and not required for single-user mode." - print "" print " -c, --component component" print " Start a specific DUCC component, optionally on a specific node. If the component name" print " is qualified with a nodename, the component is started on that node. To qualify a" @@ -233,13 +227,12 @@ class StartDucc(DuccUtil): nodefiles = [] components = [] - single_user = False or_parms = self.ducc_properties.get('ducc.orchestrator.start.type') self.pids = Properties() self.pids.load_if_exists(self.pid_file) try: - opts, args = getopt.getopt(argv, 'c:mn:sh?v', ['component=', 'components=', 'help', 'nodelist=', 'singleuser', 'cold', 'warm', 'hot', 'nothreading']) + opts, args = getopt.getopt(argv, 'c:mn:sh?v', ['component=', 'components=', 'help', 'nodelist=', 'cold', 'warm', 'hot', 'nothreading']) except: self.invalid('Invalid arguments', ' '.join(argv)) @@ -248,8 +241,6 @@ class StartDucc(DuccUtil): components.append(a) elif o in ( '-n', '--nodelist' ): nodefiles.append(a) - elif o in ( '-s', '--singleuser' ): - single_user = True elif o in ( '--nothreading' ): self.disable_threading() elif o in ( '--cold', '--warm', '--hot' ): @@ -335,7 +326,7 @@ class StartDucc(DuccUtil): print '********** Starting agents from file', nodefile try: for node in nodelist: - self.threadpool.invoke(self.start_one_agent, node, single_user) + self.threadpool.invoke(self.start_one_agent, node) except: self.threadpool.quit() print sys.exc_info()[0], "DUCC may not be started correctly." @@ -349,8 +340,8 @@ class StartDucc(DuccUtil): pass # already started else: try: - self.threadpool.invoke(self.start_component, ducc, com, or_parms, single_user) - #self.start_component(ducc, com, or_parms, single_user) + self.threadpool.invoke(self.start_component, ducc, com, or_parms) + #self.start_component(ducc, com, or_parms) except: self.threadpool.quit() print sys.exc_info()[0], "DUCC may not be started correctly."