Updated script per 10509 to remove fas-clientsetc

This commit is contained in:
Andrew Heath 2022-05-03 13:12:39 -04:00 committed by kevin
parent 848ce2c8f7
commit 4e1394f7b2
1 changed files with 22 additions and 173 deletions

View File

@ -1,206 +1,55 @@
#!/usr/bin/python3
#
# Copyright 2012 Red Hat, Inc.
# License: GPLv2+
# Author: Toshio Kuratomi <toshio@fedoraproject.org>
# Author: Andrew Heath <aheath1992@gmail.com>
__version_info__ = ((0, 2),)
import os
import grp
import sys
import subprocess
import argparse
import sys
from getpass import getpass, getuser
from configobj import ConfigObj, flatten_errors
from fedora.client import AccountSystem, AuthError
from validate import Validator
retries = 0
MAX_RETRIES = 3
# Dictionary that holds the list of users and groups.
args_dict = {}
def parse_commandline(args):
parser = argparse.ArgumentParser(
description='Generate an ssh authorized_keys file using fas')
description='Generate an ssh authorized_keys file')
parser.add_argument('users', metavar='USERS', type=str, nargs='+',
help='FAS usernames and FAS group names preceeded by "@".'
' For example: toshio skvidal @gitfas')
parser.add_argument('--with-fas-conf', dest='fas_conf',
action='store_true', default=True,
help='Look for credentials in /etc/fas.conf. On hosts that run'
' fasClient to set up users and groups, fas.conf will have'
' a username and password that will work for us. This can only be'
' read if the user is root. Otherwise a different method of'
' getting credentials will be used.')
parser.add_argument('--without-fas-conf', dest='fas_conf',
action='store_false', default=True,
help='Look for credentials in /etc/fas.conf. On hosts that run'
' fasClient to set up users and groups, fas.conf will have'
' a username and password that will work for us. This can only be'
' read if the user is root. Otherwise a different method of'
' getting credentials will be used.')
# The following can be set in the config files so the default value is
# taken from there
parser.add_argument('--limit-from', '-l', action='append', default=None,
help='IP addresses that limit where the keys can be used to'
' ssh from')
parser.add_argument('--with-fas-groups', dest='fas_groups',
action='store_true', default=None,
help='Query fas for members of groups. The default is to use the'
' groups information on the local machine. Querying fas can be'
' used on machines that do not get their group information from'
' fas or if you need group information that has been updated'
' recently but it is slower.')
parser.add_argument('--without-fas-groups', dest='fas_groups',
action='store_false', default=None,
help='Query fas for members of groups. The default is to use the'
' groups information on the local machine. Querying fas can be'
' used on machines that do not get their group information from'
' fas or if you need group information that has been updated'
' recently but it is slower.')
parser.add_argument('--username', '-u', type=str, action='store',
default=None,
help='Use this username when contacting fas. This will not be'
' used if a username and password can be read from /etc/fas.conf')
parsed = parser.parse_args(args)
args_dict = {}
args_dict['fas_conf'] = parsed.fas_conf
if parsed.limit_from is not None:
args_dict['limit_from'] = parsed.limit_from
if parsed.fas_groups is not None:
args_dict['fas_groups'] = parsed.fas_groups
if parsed.username is not None:
args_dict['username'] = parsed.username
users = set()
groups = set()
for entry in parsed.users:
if entry.startswith('@'):
groups.add(entry[1:])
elif entry == sys.argv[0]:
# argparse hasn't already subtracted the program name here....
continue
else:
users.add(entry)
# adding User list and group list to the Dictionary.
args_dict['users'] = users
args_dict['groups'] = groups
return args_dict
def read_config_files(cfg_files):
# Function looks up group(s) provided and gets a list of the users
# and appends the them to the users list.
config_spec = (
'limit_from = ip_addr_list(default=list())',
'fas_groups = boolean(default=False)',
'username = string(default=%s)' % getuser(),
)
def group_users():
for group in args_dict['groups']:
list_users = subprocess.check_output(['getent', 'group', group ])
splitlines = list_users.decode().split(':')[3]
users = splitlines.strip()
users = users.split(',')
args_dict['users'].update(users)
options = ConfigObj(configspec=config_spec)
validator = Validator()
for cfg_file in cfg_files:
cfg_file = os.path.abspath(os.path.expanduser(cfg_file))
cfg = ConfigObj(cfg_file, configspec=config_spec)
options.merge(cfg)
results = options.validate(validator)
if results != True:
for (section_list, key, unused_) in flatten_errors(options, results):
if key is not None:
print('The "%s" key in the section "%s" failed validation' % (
key, ', '.join(section_list)))
else:
print('The following section was missing:%s ' % ', '.join(
section_list))
sys.exit(1)
return options
def read_fas_conf():
'''Read username and password from /etc/fas.conf
This function will trhow an exception if it can't read the information.
otherwise it returns username, password
'''
cfgfile = open('/etc/fas.conf', 'r')
# Turn comment markers ";" into "#"
change_comment = lambda line: (line.startswith(';')
and line.replace(';', '#', 1) ) or line
lines = [change_comment(l) for l in cfgfile]
cfg = ConfigObj(lines)
return cfg['global']['login'], cfg['global']['password']
def members_of_group(group, use_fas=False):
if use_fas:
members = retry_fas(fas.group_members, group)
members = [m.username for m in members]
else:
group_info = grp.getgrnam(group)
members = group_info[3]
return set(members)
def iterate_ssh_keys(user):
person = retry_fas(fas.people_by_key, search=user, fields=['ssh_key'])[user]
if person.ssh_key:
for key in person.ssh_key.split('\n'):
if key:
yield key
def retry_fas(function, *args, **kwargs):
global retries
# Try the function at least once
while True:
try:
return function(*args, **kwargs)
except AuthError:
retries += 1
password = getpass('FAS Password for %s:' % function.__self__.username)
function.__self__.password = password
if retries >= MAX_RETRIES:
raise
# Takes all users in the Dictionary and gets their ssh-keys
if __name__ == '__main__':
args = parse_commandline(sys.argv)
conf = read_config_files(['/etc/fas-auth-keys.conf',
'~/.fedora/fas-auth-keys.conf'])
# Merge args and conf. Note that this requires that we've already parsed
# our args so that they're in a dict and won't overwrite pieces of conf if
# they weren't specified on the commandline
conf.merge(args)
# Merge in fas.conf
if conf['fas_conf']:
try:
username, password = read_fas_conf()
except:
# We don't care -- this is a nicety
pass
else:
conf['username'] = username
conf['password'] = password
fas = AccountSystem(username=conf['username'], password=conf['password'])
from_string = ''
if conf['limit_from']:
from_string = 'from="%s" ' % ','.join(conf['limit_from'])
for group in conf['groups']:
conf['users'].update(members_of_group(group, use_fas=conf['fas_groups']))
ssh_keys = dict()
for user in conf['users']:
ssh_keys[user] = set()
for key in iterate_ssh_keys(user):
ssh_keys[user].add(key)
for user in sorted(ssh_keys.keys()):
for key in ssh_keys[user]:
print('%s%s' % (from_string, key))
group_users()
for i in args_dict['users']:
keys = subprocess.check_output(['sss_ssh_authorizedkeys', i])
keys = keys.decode().strip()
print(keys)