[Freeipa-devel] Generate report of user access levels on each system

Jakub Hrozek jhrozek at redhat.com
Mon May 9 07:28:50 UTC 2016


On Sun, May 08, 2016 at 12:14:57PM -0400, Jerel Gilmer wrote:
> Hello all -
> 
> I've been using IdM and was tasked by my management with generating two
> system reports:
> 
> - List of what users have access to what services on each system
> - List of sudo rules for each system

The list of applicable HBAC rules and SUDO rules is resolved by SSSD.
This issue has come up before and we are tracking it in SSSD's tracker:
    https://fedorahosted.org/sssd/ticket/2840
but unfortunately the current milestone under development got already
quite big, so it was bumped to the next version.

> 
> I did a lot of research but couldn't find a simple way to do this so I
> created scripts using ldapsearch calls that created the reports I needed.

What you can do for the access control attestation is to run ipa
hbactest. We don't have a similar tool for sudo. But looking into sssd's
cache with ldbsearch on the target host itself might give some idea.

> 
> I'm sending these out to the community for comment and for anyone that has
> similar requirements.
> 
> I've pasted the scripts below. If there are any issues with formatting, you
> can download the scripts from my Github (github
> com/jerelgilmer/freeipa-scripts)
> 
> Jerel Gilmer
> 
> ----------> server-access-report.py
> #!/usr/bin/python
> '''
>  2016 - March - 2
>  Author: Jerel Gilmer
> '''
> 
> import os
> import sys
> import ldap
> 
> def print_usage():
>  print "Usage: server-access-report.py <hostname>"
>  print "This script generates the list of allowed users for each service
> defined in applicable HBAC rules.\n"
>  print "<hostname> - System hostname; This can be the short name\n"
>  print "For report of all systems, use \'.\'\n"
>  print "Make sure to set the below variables in the script:"
>  print "\tDOMAIN: Domain component"
>  print "\tLDAP_SERVER: LDAP server to be queried"
>  print "\tLDAP_USER: LDAP user to query server; preferable a read-only
> account"
>  print "\tLDAP_PW: LDAP user's password\n"
>  sys.exit(1)
> 
> try:
>  server = str(sys.argv[1])
> except:
>  print_usage()
> 
> ## LDAP Connection Info and bind to the LDAP server
> ## Uncomment and set these variables to the appropriate values
> ## Below are examples
> #DOMAIN = "dc=sub,dc=example,dc=com"
> #LDAP_SERVER = "ldap://ipaserver1.sub.example.com"
> #LDAP_USER = "uid=user1,cn=users,cn=compat," + DOMAIN
> #LDAP_PW = "Password123"
> 
> try:
>  DOMAIN
>  LDAP_SERVER
>  LDAP_USER
>  LDAP_PW
> except:
>  print_usage()
> 
> l = ldap.initialize(LDAP_SERVER)
> 
> l.simple_bind_s(LDAP_USER,LDAP_PW)
> 
> ## LDAP Search Variables
> ## Base DN for LDAP Searches
> baseComputerDN = "cn=computers,cn=accounts," + DOMAIN
> baseGroupDN =  "cn=groups,cn=accounts," + DOMAIN
> baseUserDN = "cn=users,cn=accounts," + DOMAIN
> baseHBACDN = "cn=hbac," + DOMAIN
> baseHBACServicesDN = "cn=hbacservices,cn=hbac," + DOMAIN
> baseHBACServiceGroupsDN = "cn=hbacservicegroups,cn=hbac," + DOMAIN
> 
> ## Default LDAP SCOPE
> scope = ldap.SCOPE_SUBTREE
> 
> ## Filter for LDAP Searches
> compFilter = "(&(objectclass=ipahost)(fqdn=*" + server + "*))"
> hbacFilter = "(objectclass=ipahbacrule)"
> userFilter = "(objectclass=person)"
> groupFilter = "(objectclass=ipausergroup)"
> hbacServiceFilter = "(objectclass=ipahbacservice)"
> hbacServiceGroupsFilter = "(objectclass=ipahbacservicegroup)"
> 
> ## Attributes from LDAP Searches
> compAttributes =  ['memberOf', 'fqdn']
> hbacAttributes = ['memberUser', 'memberService', 'serviceCategory']
> userAttributes = ['uid']
> groupAttributes = ['member']
> hbacServiceAttributes = ['cn' , 'ipaUniqueID']
> hbacServiceGroupsAttributes = ['cn' , 'member']
> 
> ## Perform LDAP searches and store results into array
> ALL_HOSTS = l.search_s(baseComputerDN, scope, compFilter, compAttributes)
> 
> ALL_USERS = l.search_s(baseUserDN, scope, userFilter, userAttributes)
> 
> ALL_GROUPS = l.search_s(baseGroupDN, scope, groupFilter, groupAttributes)
> 
> ALL_HBACRULES = l.search_s(baseHBACDN, scope, hbacFilter, hbacAttributes)
> 
> ALL_HBACSERVICES = l.search_s(baseHBACServicesDN, scope, hbacServiceFilter,
> hbacServiceAttributes)
> 
> ALL_HBACSERVICEGROUPS = l.search_s(baseHBACServiceGroupsDN, scope,
> hbacServiceGroupsFilter, hbacServiceGroupsAttributes)
> 
> # HBAC rules that apply to all servers
> hbacAllServersFilter = "(&(objectclass=ipahbacrule)(hostCategory=all))"
> HBACRULE_ALL_SERVERS = l.search_s(baseHBACDN, scope, hbacAllServersFilter,
> hbacAttributes)
> 
> ALL_HOSTS.sort()
> 
> def findUID(user):
>  uid = filter(lambda x: user in x, ALL_USERS)
>  return uid[0][1]['uid'][0]
> 
> def findGroupMembers(groupname):
>  if "cn=groups,cn" not in groupname:
>   pass
>  group = filter(lambda x: groupname in x, ALL_GROUPS)
>  try:
>   groupmembers = group[0][1]['member']
>  except:
>   groupmembers = ""
>  for user in groupmembers:
>   if "cn=groups,cn" in user:
>    for i in findGroupMembers(user):
>     yield i
>   else:
>    yield (findUID(user))
> 
> def findServiceName(service_name):
>  s = filter(lambda x: service_name in x, ALL_HBACSERVICES)
>  return s[0][1]['cn'][0]
> 
> def findServiceGroupMembers(service_group):
>  allServices = []
>  serviceGroup = filter(lambda x: service_group in x, ALL_HBACSERVICEGROUPS)
>  serviceGroupMembers = serviceGroup[0][1]['member']
>  for i in serviceGroupMembers:
>   allServices.append(findServiceName(i))
>  formattedAllServices = ', '.join(allServices)
>  return formattedAllServices
> 
> def accessToAllSystems():
>  allSystemsHBACRules = {}
> 
>  for hbacname in HBACRULE_ALL_SERVERS:
>   hbacrule = filter(lambda x: hbacname[0] in x, ALL_HBACRULES)
> 
>   for hbacuser in hbacrule:
>    services = []
>    allowedUsers = []
>    users = []
>    groups = []
> 
>    try:
>     users = filter(lambda x: "cn=users,cn" in x, hbacuser[1]['memberUser'])
>    except:
>     users = []
> 
>    try:
>     groups = filter(lambda x: "cn=groups,cn" in x,
> hbacuser[1]['memberUser'])
>    except:
>     groups = []
> 
>    try:
>     hbacservice = hbacuser[1]['memberService']
>     for i in hbacservice:
>      if "hbacservicegroups,cn" in i:
>       services.append(findServiceGroupMembers(i))
>      else:
>       services.append(findServiceName(i))
>    except:
>     try:
>      services = hbacuser[1]['serviceCategory'][0]
>     except:
>      services = ['None']
> 
>    for i in users:
>     allowedUsers.append(findUID(i))
> 
>    for i in groups:
>     allowedUsers += (findGroupMembers(i))
> 
>   allSystemsHBACRules[hbacrule[0][0]] = {'services': services,
> 'allowedUsers': allowedUsers}
> 
>  return allSystemsHBACRules
> 
> def mergeD(results,services):
>  for k in results:
>   if services == results[k]['services']:
>    return "MATCH!!", k
> 
> def nestedL(l):
>  if isinstance(l, str):
>   yield l
>  for k in l:
>   if isinstance(k, list):
>    for i in k:
>     yield i
>   if isinstance(k, str):
>    yield k
> 
> def main():
>  for entry in ALL_HOSTS:
>   systemWide = {}
>   HBACAllowedList = {}
>   results = {}
>   x = 1
> 
>   fqdn = entry[1]['fqdn'][0]
> 
>   print "HOSTNAME = ", fqdn
>   try:
>    membership = filter(lambda x: "hbac,dc" in x, entry[1]['memberOf'])
>   except:
>    membership = []
> 
>   for hbacname in membership:
>    hbacrule = filter(lambda x: hbacname in x, ALL_HBACRULES)
>    for hbacuser in hbacrule:
>     allowedUsers = []
>     allowedUsersLst = []
>     users = []
>     groups = []
>     services = []
>     try:
>      hbacservice = hbacuser[1]['memberService']
>      for i in hbacservice:
>       if "hbacservicegroups,cn" in i:
>        services.append(findServiceGroupMembers(i))
>       else:
>        services.append(findServiceName(i))
>     except:
>      try:
>       services = hbacuser[1]['serviceCategory'][0]
>      except:
>       services = []
>     try:
>      users = filter(lambda x: "cn=users,cn" in x, hbacuser[1]['memberUser'])
>     except:
>      users = []
> 
>     try:
>      groups = filter(lambda x: "cn=groups,cn" in x,
> hbacuser[1]['memberUser'])
>     except:
>      groups = []
> 
>     for i in users:
>      allowedUsers.append(findUID(i))
> 
>     for i in groups:
>      allowedUsers += findGroupMembers(i)
> 
>     HBACAllowedList[hbacrule[0][0]] = {'services': services,
> 'allowedUsers': allowedUsers}
> 
>   systemWide = accessToAllSystems()
>   HBACAllowedList.update(accessToAllSystems())
> 
>   for key, value in HBACAllowedList.iteritems():
> 
>    if isinstance(value['services'], list):
>     services = ', '.join(value['services'])
>    else:
>     services = value['services']
> 
>    allowedUsers = value['allowedUsers']
> 
>    try:
>     mark, key = mergeD(results,services)
>    except:
>     mark, key = (None, None)
> 
>    if mark == "MATCH!!":
>     results[key]['allowedUsers'].append(allowedUsers)
>    else:
>     results[x] = {'services': services, 'allowedUsers': allowedUsers}
>     x = x + 1
> 
>   for i in results:
>    results_services = results[i]['services']
> 
>    results_allowedUsers = list(nestedL(results[i]['allowedUsers']))
>    results_allowedUsersSet = set(results_allowedUsers)
>    results_allowedUsersLst = list(results_allowedUsersSet)
>    results_allowedUsersLst.sort()
>    formatted_allowedUsers = ' '.join(results_allowedUsersLst)
> 
>    if not results_services:
>     results_services = 'empty'
>    if not formatted_allowedUsers:
>     formatted_allowedUsers = 'empty'
>    print "SERVICES = ", results_services
>    print "ALLOWED USERS = ", formatted_allowedUsers, "\n"
> 
> main()
> 
> --------------------------------------------------
> 
> -------------------> sudo-report.py
> #!/usr/bin/python
> '''
>  2016 - March - 2
>  Author: Jerel Gilmer
> '''
> 
> import os
> import sys
> import ldap
> 
> def print_usage():
>  print "Usage: server-access-report.py <hostname>"
>  print "This script generates the list of sudo rules for each server.\n"
>  print "<hostname> - System hostname; This can be the short name\n"
>  print "For report of all systems, use \'.\'\n"
>  print "Make sure to set the below variables in the script:"
>  print "\tDOMAIN: Domain component"
>  print "\tLDAP_SERVER: LDAP server to be queried"
>  print "\tLDAP_USER: LDAP user to query server; preferable a read-only
> account"
>  print "\tLDAP_PW: LDAP user's password\n"
>  sys.exit(1)
> 
> try:
>  server = str(sys.argv[1])
> except:
>  print_usage()
> 
> ## LDAP Connection Info and bind to the LDAP server
> ## Uncomment and set these variables to the appropriate values
> ## Below are examples
> #DOMAIN = "dc=sub,dc=example,dc=com"
> #LDAP_SERVER = "ldap://ipaserver1"
> #LDAP_USER = "uid=user1,cn=users,cn=compat," + DOMAIN
> #LDAP_PW = "Password123"
> 
> try:
>  DOMAIN
>  LDAP_SERVER
>  LDAP_USER
>  LDAP_PW
> except:
>  print_usage()
> 
> l = ldap.initialize(LDAP_SERVER)
> 
> l.simple_bind_s(LDAP_USER,LDAP_PW)
> 
> ## LDAP Search Variables
> ## Base DN for LDAP Searches
> baseComputerDN = "cn=computers,cn=accounts," + DOMAIN
> baseGroupDN =  "cn=groups,cn=accounts," + DOMAIN
> baseUserDN = "cn=users,cn=accounts," + DOMAIN
> baseSudoDN = "cn=sudorules,cn=sudo," + DOMAIN
> baseSudoCmdDN = "cn=sudocmds,cn=sudo," + DOMAIN
> baseSudoCmdGroupDN = "cn=sudocmdgroups,cn=sudo," + DOMAIN
> 
> ## Default LDAP SCOPE
> scope = ldap.SCOPE_SUBTREE
> 
> ## Filter for LDAP Searches
> compFilter = "(&(objectclass=ipahost)(fqdn=*" + server + "*))"
> userFilter = "(objectclass=person)"
> groupFilter = "(objectclass=ipausergroup)"
> sudoFilter = "objectclass=ipasudorule"
> sudoCmdFilter = "objectclass=ipasudocmd"
> sudoCmdGroupFilter = "objectclass=ipasudocmdgrp"
> 
> ## Attributes from LDAP Searches
> compAttributes =  ['memberOf', 'fqdn']
> userAttributes = ['uid']
> groupAttributes = ['member']
> sudoAttributes = ['memberUser', 'ipaSudoOpt', 'memberAllowCmd',
> 'hostCategory', 'cmdCategory']
> sudoCmdAttributes = ['sudoCmd']
> sudoCmdGroupAttributes = ['member']
> 
> ## Perform LDAP searches and store results into array
> ALL_HOSTS = l.search_s(baseComputerDN, scope, compFilter, compAttributes)
> 
> ALL_USERS = l.search_s(baseUserDN, scope, userFilter, userAttributes)
> 
> ALL_GROUPS = l.search_s(baseGroupDN, scope, groupFilter, groupAttributes)
> 
> ALL_SUDORULES = l.search_s(baseSudoDN, scope, sudoFilter, sudoAttributes)
> 
> ALL_SUDOCMDS = l.search_s(baseSudoCmdDN, scope, sudoCmdFilter,
> sudoCmdAttributes)
> 
> ALL_SUDOCMDGROUPS = l.search_s(baseSudoCmdGroupDN, scope,
> sudoCmdGroupFilter, sudoCmdGroupAttributes)
> 
> # Sudo rules that apply to all servers
> sudoAllServersFilter = "(&(objectclass=ipasudorule)(hostCategory=all))"
> SUDORULE_ALL_SERVERS = l.search_s(baseSudoDN, scope, sudoAllServersFilter,
> sudoAttributes)
> 
> ALL_HOSTS.sort()
> 
> def findUID(user):
>  uid = filter(lambda x: user in x, ALL_USERS)
>  return uid[0][1]['uid'][0]
> 
> def findGroupMembers(groupname):
>  if "cn=groups,cn" not in groupname:
>   pass
>  group = filter(lambda x: groupname in x, ALL_GROUPS)
>  try:
>   groupmembers = group[0][1]['member']
>  except:
>   groupmembers = ""
>  for user in groupmembers:
>   if "cn=groups,cn" in user:
>    for i in findGroupMembers(user):
>     yield i
>   else:
>    yield (findUID(user))
> 
> def findSudoCmds(sudo_cmd):
>  s = filter(lambda x: sudo_cmd in x, ALL_SUDOCMDS)
>  return s[0][1]['sudoCmd'][0]
> 
> def findSudoCmdGroupMembers(sudo_cmd_group):
>  allSudoCmds = []
>  sudoGroup = filter(lambda x: sudo_cmd_group in x, ALL_SUDOCMDGROUPS)
>  sudoGroupMembers = sudoGroup[0][1]['member']
>  for i in sudoGroupMembers:
>   allSudoCmds.append(findSudoCmds(i))
>  formattedAllSudoCmds = ', '.join(allSudoCmds)
>  return formattedAllSudoCmds
> 
> def sudoOnAllSystems():
>  allSystemsSudoRules = {}
> 
>  for sudoname in SUDORULE_ALL_SERVERS:
>   sudorule = filter(lambda x: sudoname[0] in x, ALL_SUDORULES)
> 
>   for sudouser in sudorule:
>    sudocmds = []
>    allowedUsers = []
>    allowedSudoCmd = []
>    users = []
>    groups = []
> 
>    try:
>     sudoOptions = ['ipaSudoOpt']
>    except:
>     sudoOptions = []
> 
>    try:
>     sudocmds = sudouser[1]['memberAllowCmd']
>     for i in sudocmds:
>      if "cn=sudocmdgroups,cn" in i:
>       allowedSudoCmd.append(findSudoCmdGroupMembers(i))
>      else:
>       allowedSudoCmd.append(findSudoCmds(i))
>    except:
>     try:
>      if sudouser[1]['cmdCategory']:
>       allowedSudoCmd = sudouser[1]['cmdCategory'][0]
>     except:
>      allowedSudoCmd = ['None']
> 
>    try:
>     sudocmdgroup = filter(lambda x: "cn=sudocmdgroups" in x,
> sudouser[1]['memberAllowCmd'])
>    except:
>     sudocmdgroup = ''
> 
>    try:
>     users = filter(lambda x: "cn=users,cn" in x, sudouser[1]['memberUser'])
>    except:
>     users = []
> 
>    try:
>     groups = filter(lambda x: "cn=groups,cn" in x,
> sudouser[1]['memberUser'])
>    except:
>     groups = []
> 
>    for i in users:
>     allowedUsers.append(findUID(i))
> 
>    for i in groups:
>     allowedUsers += (findGroupMembers(i))
> 
>   allSystemsSudoRules[sudorule[0][0]] = {'sudoCommands': allowedSudoCmd,
> 'allowedUsers': allowedUsers}
> 
>  return allSystemsSudoRules
> 
> def mergeD(results,allowedSudoCmd):
>  for k in results:
>   if allowedSudoCmd == results[k]['sudoCommands']:
>    return "MATCH!!", k
> 
> def nestedL(l):
>  if isinstance(l, str):
>   yield l
>  for k in l:
>   if isinstance(k, list):
>    for i in k:
>     yield i
>   if isinstance(k, str):
>    yield k
> 
> def main():
>  for entry in ALL_HOSTS:
>   SudoAllowedList = {}
>   results = {}
>   x = 1
> 
>   fqdn = entry[1]['fqdn'][0]
> 
>   print "HOSTNAME = ", fqdn
> 
>   try:
>    membership = filter(lambda x: "sudo,dc" in x, entry[1]['memberOf'])
>   except:
>    membership = []
> 
>   for sudoname in membership:
>    sudorule = filter(lambda x: sudoname in x, ALL_SUDORULES)
>    for sudouser in sudorule:
>     allowedUsers = []
>     allowedUsersLst = []
>     allowedSudoLst = []
>     allowedSudoCmd = []
>     users = []
>     groups = []
> 
>     try:
>      sudoOptions = ['ipaSudoOpt']
>     except:
>      sudoOptions = []
> 
>     try:
>      sudocmds = sudouser[1]['memberAllowCmd']
>      for i in sudocmds:
>       if "cn=sudocmdgroups,cn" in i:
>        allowedSudoCmd.append(findSudoCmdGroupMembers(i))
>       else:
>        allowedSudoCmd.append(findSudoCmds(i))
>     except:
>      try:
>       if sudouser[1]['cmdCategory']:
>        allowedSudoCmd = sudouser[1]['cmdCategory'][0]
>      except:
>       allowedSudoCmd = ['None']
> 
>     try:
>      users = filter(lambda x: "cn=users,cn" in x, sudouser[1]['memberUser'])
>     except:
>      users = []
> 
>     try:
>      groups = filter(lambda x: "cn=groups,cn" in x,
> sudouser[1]['memberUser'])
>     except:
>      groups = []
> 
>     for i in users:
>      allowedUsers.append(findUID(i))
> 
>     for i in groups:
>      allowedUsers += findGroupMembers(i)
> 
>     SudoAllowedList[sudorule[0][0]] = {'sudoCommands': allowedSudoCmd,
> 'allowedUsers': allowedUsers}
> 
>   systemWide = sudoOnAllSystems()
>   SudoAllowedList.update(systemWide)
> 
>   for key, value in SudoAllowedList.iteritems():
> 
>    if isinstance(value['sudoCommands'], list):
>     allowedSudoCmd = ', '.join(value['sudoCommands'])
>    else:
>     allowedSudoCmd = value['sudoCommands']
> 
>    allowedUsers = value['allowedUsers']
> 
>    try:
>     mark, key = mergeD(results,allowedSudoCmd)
>    except:
>     mark, key = (None, None)
> 
>    if mark == "MATCH!!":
>     results[key]['allowedUsers'].append(allowedUsers)
>    else:
>     results[x] = {'sudoCommands': allowedSudoCmd, 'allowedUsers':
> allowedUsers}
>     x = x + 1
> 
>   for i in results:
>    results_allowedSudoCmd = results[i]['sudoCommands']
> 
>    results_allowedUsers = list(nestedL(results[i]['allowedUsers']))
>    results_allowedUsersSet = set(results_allowedUsers)
>    results_allowedUsersLst = list(results_allowedUsersSet)
>    results_allowedUsersLst.sort()
>    formatted_allowedUsers = ' '.join(results_allowedUsersLst)
> 
>    if not results_allowedSudoCmd:
>     results_services = 'empty'
>    if not formatted_allowedUsers:
>     formatted_allowedUsers = 'empty'
>    print "SUDO COMMANDS = ", results_allowedSudoCmd
>    print "ALLOWED USERS = ", formatted_allowedUsers, "\n"
> 
> main()
> 
> ------------------------------------------------

> -- 
> Manage your subscription for the Freeipa-devel mailing list:
> https://www.redhat.com/mailman/listinfo/freeipa-devel
> Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code




More information about the Freeipa-devel mailing list