summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeth Vidal <skvidal@fedoraproject.org>2012-10-26 17:19:47 +0000
committerSeth Vidal <skvidal@fedoraproject.org>2012-10-26 17:19:47 +0000
commit6b1bfa69175a4debdf5e8a0780338098ff9c37cd (patch)
treed7a1235ffc4af8774dc6eb09020e0bb434c609ac
parent820d0ef5ed643c26e8b422febb326ca05650c8db (diff)
downloadansible-6b1bfa69175a4debdf5e8a0780338098ff9c37cd.tar.gz
ansible-6b1bfa69175a4debdf5e8a0780338098ff9c37cd.tar.xz
ansible-6b1bfa69175a4debdf5e8a0780338098ff9c37cd.zip
- add toshio's auth-keys-from-fas script to ansible public
- modify how we specify users who should have root access to a cloud system - add authorized_key action to my test system
-rw-r--r--inventory/host_vars/209.132.184.1112
-rw-r--r--playbooks/hosts/jenkins.cloud.fedoraproject.org.yml12
-rwxr-xr-xscripts/auth-keys-from-fas207
3 files changed, 210 insertions, 11 deletions
diff --git a/inventory/host_vars/209.132.184.111 b/inventory/host_vars/209.132.184.111
index 8086adaa3..88f0e2ecf 100644
--- a/inventory/host_vars/209.132.184.111
+++ b/inventory/host_vars/209.132.184.111
@@ -6,5 +6,5 @@ security_group: webserver
zone: fedoracloud
hostbase: jenkins-master-
public_ip: 209.132.184.111
-root_auth_users: [ pingou, skvidal ]
+root_auth_users: pingou skvidal ralph
description: jenkins master instance
diff --git a/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml b/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml
index 7b14bbda6..a61158164 100644
--- a/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml
+++ b/playbooks/hosts/jenkins.cloud.fedoraproject.org.yml
@@ -22,16 +22,8 @@
tasks:
- include: $tasks/cloud_setup_basic.yml
- - name: install pkgs for jenkins
- action: yum state=installed pkg=$item
- with_items:
- - vim
- - java-1.7.0-openjdk
- - subversion
- - bzr
- - git
- tags:
- - packages
+ - name: add root keys for other allowed users
+ action: authorized_key user=root key=$PIPE(/srv/web/ansible/scripts/auth-keys-from-fas ${root_auth_users})
handlers:
- include: $handlers/restart_services.yml
diff --git a/scripts/auth-keys-from-fas b/scripts/auth-keys-from-fas
new file mode 100755
index 000000000..f49c66cce
--- /dev/null
+++ b/scripts/auth-keys-from-fas
@@ -0,0 +1,207 @@
+#!/usr/bin/python -tt
+#
+# Copyright 2012 Red Hat, Inc.
+# License: GPLv2+
+# Author: Toshio Kuratomi <toshio@fedoraproject.org>
+
+__version_info__ = ((0, 2),)
+
+import os
+import grp
+import sys
+import argparse
+
+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
+
+def parse_commandline(args):
+ parser = argparse.ArgumentParser(
+ description='Generate an ssh authorized_keys file using fas')
+ 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)
+
+ args_dict['users'] = users
+ args_dict['groups'] = groups
+
+ return args_dict
+
+def read_config_files(cfg_files):
+
+ config_spec = (
+ 'limit_from = ip_addr_list(default=list())',
+ 'fas_groups = boolean(default=False)',
+ 'username = string(default=%s)' % getuser(),
+ )
+
+ 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.im_self.username)
+ function.im_self.password = password
+ if retries >= MAX_RETRIES:
+ raise
+
+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'])
+
+ 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' % user
+ print '%s%s' % (from_string, key)