From 51a383c5d59ca0005d38e5c99cef426f599f670b Mon Sep 17 00:00:00 2001 From: Luke Macken Date: Sat, 2 May 2009 16:30:52 -0400 Subject: [PATCH] Add an selinux-overlord.py func script --- scripts/selinux/selinux-overlord.py | 158 ++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 scripts/selinux/selinux-overlord.py diff --git a/scripts/selinux/selinux-overlord.py b/scripts/selinux/selinux-overlord.py new file mode 100644 index 0000000..893926a --- /dev/null +++ b/scripts/selinux/selinux-overlord.py @@ -0,0 +1,158 @@ +#!/usr/bin/python -tt +# A tool to help monitor & manage SELinux using func +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authors: Luke Macken + +import time + +from pprint import pprint +from optparse import OptionParser +from func import jobthing +from func.overlord.client import Overlord + +status, stdout, stderr = range(3) + +class SELinuxOverlord(Overlord): + selinux_status = {'Enforcing': [], 'Permissive': [], 'Disabled': []} + selinux_minions = {} + + def __init__(self, minions): + super(SELinuxOverlord, self).__init__(minions) + self.minion_glob = minions + + def get_selinux_status(self): + """ Get the SELinux status of all minions """ + results = self.command.run('/usr/sbin/getenforce') + + for minion, result in results.iteritems(): + if result[status]: + print "[%s] Error: %s" % (minion, result) + else: + self.selinux_status[result[stdout].strip()].append(minion) + + for key in self.selinux_status: + self.selinux_status[key].sort() + + return self.selinux_status + + def get_selinux_denials(self): + """ Return all AVC denials from this week """ + if len(self.selinux_minions): + for minion, result in self.selinux_minions.iteritems(): + yield minion, result + else: + results = self.command.run('ausearch -m AVC -ts this-week --input-logs') + for minion, result in results.iteritems(): + self.selinux_minions[minion] = result + yield minion, result + + def dump_selinux_denials(self): + """ Write out all SELinux denials for all minions """ + for minion, result in self.get_selinux_denials(): + if not result[status]: + out = file(minion, 'w') + out.write(result[stdout]) + out.close() + print "[%s] Successfully collected this weeks AVCs" % minion + else: + if '\n' in result: + print "[%s] No AVCs Found" % minion + out = file(minion, 'w') + out.close() + else: + print "[%s] Problem running ausearch: %r" % (minion, result) + + def get_enforced_denials(self): + """ Print all denials from SELinux-enforcing minions """ + for minion, result in self.get_selinux_denials(): + if minion not in self.selinux_status['Enforcing']: + continue + if not result[status]: + overlord = Overlord(minion) + audit2allow = overlord.command.run('audit2allow -la') + for m, r in audit2allow.iteritems(): + if r[stdout].strip(): + print "[%s]\n%s\n" % (m, r[stdout]) + + def upgrade_policy(self): + """ Update the SELinux policy across the given minions """ + print "Cleaning yum metadata..." + results = self.command.run('yum clean metadata') + for minion, result in results.items(): + if result[0]: + print "[%s] Problem cleaning yum cache: %s" % (minion, result[1]) + + async_client = Overlord(self.minion_glob, nforks=10, async=True) + + print "Upgrading SELinux policy..." + job_id = async_client.command.run('yum -y update selinux*') + + running = True + + while running: + time.sleep(20) + return_code, results = async_client.job_status(job_id) + if return_code == jobthing.JOB_ID_RUNNING: + continue + elif return_code in (jobthing.JOB_ID_FINISHED, jobthing.JOB_ID_PARTIAL): + for minion, result in results.items(): + if result[0]: + print '[%s] Problem upgrading policy: %s' % (minion, result[1]) + if 'Updated: selinux-policy' in result[1]: + ver = result[1].split('Updated: ')[-1].split()[1].split(':')[1] + print "[%s] selinux-policy successfully upgraded to %s" % (minion, ver) + else: + print "selinux-policy *not* upgraded on %s: %s" % (minion, result[1]) + if return_code == jobthing.JOB_ID_FINISHED: + running = False + elif return_code == jobthing.JOB_ID_LOST_IN_SPACE: + print "Job %s lost in space: %s" % (job_id, results) + else: + print "Unknown return code %s: %s" % (return_code, results) + + print "SELinux policy upgrade complete!" + + +if __name__ == '__main__': + parser = OptionParser('usage: %prog [options] [minion1[;minion2]]') + parser.add_option('-s', '--status', action='store_true', dest='status', + help='Display the SELinux status of all minions') + parser.add_option('-e', '--enforced-denials', action='store_true', + dest='enforced_denials', help='Display enforced denials') + parser.add_option('-d', '--dump-avcs', action='store_true', + dest='dump_avcs', help='Dump AVCs to disk') + parser.add_option('-u', '--upgrade-policy', action='store_true', + dest='upgrade', help='Upgrade SELinux policy') + opts, args = parser.parse_args() + + minions = len(args) > 0 and ';'.join(args) or '*' + overlord = SELinuxOverlord(minions) + + if opts.status or opts.enforced_denials: + print "Determining SELinux status on minions: %s" % minions + pprint(overlord.get_selinux_status()) + if opts.enforced_denials: + print "Finding enforced SELinux denials..." + overlord.get_enforced_denials() + if opts.dump_avcs: + print "Dumping SELinux denials to disk..." + overlord.dump_selinux_denials() + if opts.upgrade: + overlord.upgrade_policy() + +# vim: ts=4 sw=4 expandtab ai