diff --git a/bin/xymon-permissions.py b/bin/xymon-permissions.py new file mode 100755 index 0000000000000000000000000000000000000000..1f3f9c33a5b9fe783a273a2c68e65dd5dee0d0a0 --- /dev/null +++ b/bin/xymon-permissions.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +import os +import sys +import pwd +import grp +import stat +import glob +import posix1e +import pyxymon as pymon + +CHECK_NAME = 'permissions' +CHECK_VERSION = 2 +LIFETIME = 30 + +home_dir = '/export/home1/*' +owner = [] +permission = [] +acl = [] + + +class Home(object): + """ + Holds info about a home directory + """ + def __init__(self, name, path, st): + self.name = name + self.path = path + self.st = st + + @property + def uid(self): + return self.st.st_uid + + @property + def gid(self): + return self.st.st_gid + + @property + def uname(self): + return pwd.getpwuid(self.uid).pw_name + + @property + def gname(self): + return grp.getgrgid(self.gid).gr_name + + @property + def filemode(self): + return stat.filemode(self.st.st_mode) + + @property + def permission(self): + return ' '.join([self.filemode, self.path]) + + def __str__(self): + return ' '.join([self.filemode, self.uname, self.gname, self.path]) + + +def check_homes(top): + if not os.path.isdir(top): + return + for f in os.listdir(top): + pathname = os.path.join(top, f) + if not os.path.isdir(pathname): + continue + st = os.stat(pathname) + home = Home(f, pathname, st) + + if bad_owner(home): + owner.append(home) + + if bad_permission(home): + permission.append(home) + + if posix1e.has_extended(home.path): + acl.append(home) + + +def bad_owner(home): + if home.name == home.uname and home.name == home.gname: + return False + return True + + +def bad_permission(home): + # d--------- + if home.st.st_mode == stat.S_IFDIR: + return False + # drwx------ + elif home.st.st_mode == stat.S_IFDIR | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR: + return False + return True + + +def list_homes(homes): + for home in homes: + print(home) + + +def run_check(xymon): + for path in glob.glob(home_dir): + check_homes(path) + + if owner: + title = 'bad owner or group' + content = 'home must be owned by the respective user and the group his user-private-group<br/><br/>' + for home in owner: + content += ''.join([str(home), '<br/>']) + xymon.section(title, content) + xymon.color = pymon.STATUS_CRITICAL + + if permission: + title = 'bad permissions' + content = 'home permission not <code>drwx------</code> (active user) or <code>d---------</code> (blocked user)<br/><br/>' + for home in permission: + content += ''.join([str(home.permission), '<br/>']) + xymon.section(title, content) + xymon.color = pymon.STATUS_CRITICAL + + if acl: + title = 'bad acls' + content = 'home has posix.1e extended ACLs<br/>check acls using `getfacl`, which stands for `get fucking ACL`<br/><br/>' + for home in acl: + extacl = posix1e.ACL(file=home.path) + content += ''.join([home.path, '<br/>']) + content += ''.join([str(extacl), '<br/>']) + xymon.section(title, content) + xymon.color = pymon.STATUS_CRITICAL + +def main(): + """Run xymon check""" + xymon = pymon.XymonClient(CHECK_NAME) + check_script = os.path.basename(__file__) + # The default criticity is set to 'pymon.STATUS_OK' + xymon.lifetime = LIFETIME + xymon.title('home ownership and permissions') + + try: + run_check(xymon) + except Exception as e: + xymon.color = pymon.STATUS_WARNING + xymon.section('Exception', e) + + xymon.footer(check_script, CHECK_VERSION) + xymon.send() + + +if __name__ == '__main__': + main() + sys.exit(0)