commit 2056defcc9f50cc7f76dc36a374471ff0218e69e
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sat Oct 1 23:03:33 2016 +0200
Initial import of fas_server
roles/fas_server/files/Makefile.fedora-ca | 70 ++++
roles/fas_server/files/certhelper.py | 283 ++++++++++++++
roles/fas_server/files/controllers.py | 263 +++++++++++++
roles/fas_server/files/export-bugzilla | 153 ++++++++
roles/fas_server/files/fas-log.cfg | 29 ++
.../fas_server/files/fedora-ca-client-openssl.cnf | 317 +++++++++++++++
roles/fas_server/files/ipa.staging.pem | 23 ++
roles/fas_server/tasks/main.yml | 405 ++++++++++++++++++++
roles/fas_server/templates/export-bugzilla.cfg.j2 | 20 +
roles/fas_server/templates/fas-app.conf.j2 | 29 ++
roles/fas_server/templates/fas.cfg.j2 | 295 ++++++++++++++
roles/fas_server/templates/krb5.conf.j2 | 31 ++
roles/fas_server/templates/yubikey-remove.py.j2 | 62 +++
13 files changed, 1980 insertions(+), 0 deletions(-)
---
diff --git a/roles/fas_server/files/Makefile.fedora-ca
b/roles/fas_server/files/Makefile.fedora-ca
new file mode 100644
index 0000000..5da1ea9
--- /dev/null
+++ b/roles/fas_server/files/Makefile.fedora-ca
@@ -0,0 +1,70 @@
+# $Id: Makefile,v 1.4 2006/06/20 18:55:37 jmates Exp $
+#
+# NOTE If running OpenSSL 0.9.8a or higher, see -newkey, below.
+#
+# Automates the setup of a custom Certificate Authority and provides
+# routines for signing and revocation of certificates. To use, first
+# customize the commands in this file and the settings in openssl.cnf,
+# then run:
+#
+# make init
+#
+# Then, copy in certificate signing requests, and ensure their suffix is
+# .csr before signing them with the following command:
+#
+# make sign
+#
+# To revoke a key, name the certificate file with the cert option
+# as shown below:
+#
+# make revoke cert=foo.cert
+#
+# This will revoke the certificate and call gencrl; the revocation list
+# will then need to be copied somehow to the various systems that use
+# your CA cert.
+
+requests = *.csr
+
+# remove -batch option if want chance to not certify a particular request
+sign: FORCE
+ @openssl ca -batch -config openssl.cnf -days 180 -in $(req) -out $(cert)
+
+revoke:
+ @test $${cert:?"usage: make revoke cert=certificate"}
+ @openssl ca -config openssl.cnf -revoke $(cert)
+ @$(MAKE) gencrl
+
+gencrl:
+ @openssl ca -config openssl.cnf -gencrl -out crl/crl.pem
+
+clean:
+ -rm ${requests}
+
+# creates required supporting files, CA key and certificate
+init:
+ @test ! -f serial
+ @mkdir crl newcerts private
+ @chmod go-rwx private
+ @echo '01' > serial
+ @touch index
+ # NOTE use "-newkey rsa:2048" if running OpenSSL 0.9.8a or higher
+ @openssl req -nodes -config openssl.cnf -days 1825 -x509 -newkey rsa:2048 -out
ca-cert.pem -outform PEM
+
+help:
+ @echo make sign req=in.csr cert=out.cert
+ @echo ' - signs in.csr, outputting to out.cert'
+ @echo
+ @echo make revoke cert=filename
+ @echo ' - revokes certificate in named file and calls gencrl'
+ @echo
+ @echo make gencrl
+ @echo ' - updates Certificate Revocation List (CRL)'
+ @echo
+ @echo make clean
+ @echo ' - removes all *.csr files in this directory'
+ @echo
+ @echo make init
+ @echo ' - required initial setup command for new CA'
+
+# for legacy make support
+FORCE:
diff --git a/roles/fas_server/files/certhelper.py b/roles/fas_server/files/certhelper.py
new file mode 100755
index 0000000..caa0519
--- /dev/null
+++ b/roles/fas_server/files/certhelper.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+#
+# 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 2 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 Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Copyright 2005 Dan Williams <dcbw(a)redhat.com> and Red Hat, Inc.
+
+
+import sys, os, tempfile
+
+OPENSSL_PROG = '/usr/bin/openssl'
+
+def print_usage(prog):
+ print "\nUsage:\n"
+ print " %s ca --outdir=<outdir> --name=<name>\n" % prog
+ print " %s normal --outdir=<outdir> --name=<name>
--cadir=<cadir> --caname=<ca-name>" % prog
+ print ""
+ print " Types:"
+ print " ca - Build system Certificate Authority key &
certificate"
+ print " normal - Key & certificate that works with the build
server and builders"
+ print ""
+ print "Examples:\n"
+ print " %s ca --outdir=/etc/plague/ca --name=my_ca" % prog
+ print " %s normal --outdir=/etc/plague/server/certs --name=server
--cadir=/etc/plague/ca --caname=my_ca" % prog
+ print " %s normal --outdir=/etc/plague/builder/certs --name=builder1
--cadir=/etc/plague/ca --caname=my_ca" % prog
+ print "\n"
+
+
+class CertHelperException:
+ def __init__(self, message):
+ self.message = message
+
+
+class CertHelper:
+ def __init__(self, prog, outdir, name):
+ self._prog = prog
+ self._outdir = outdir
+ self._name = name
+
+ def dispatch(self, cmd, argslist):
+ if cmd.lower() == 'ca':
+ self._gencert_ca(argslist)
+ elif cmd.lower() == 'normal':
+ self._gencert_normal(argslist)
+ else:
+ print_usage(self._prog)
+
+ def _gencert_ca(self, args):
+ # Set up CA directory
+ if not os.path.exists(self._outdir):
+ os.makedirs(self._outdir)
+ try:
+ os.makedirs(os.path.join(self._outdir, 'certs'))
+ os.makedirs(os.path.join(self._outdir, 'crl'))
+ os.makedirs(os.path.join(self._outdir, 'newcerts'))
+ os.makedirs(os.path.join(self._outdir, 'private'))
+ except:
+ pass
+ cert_db = os.path.join(self._outdir, "index.txt")
+ os.system("/bin/touch %s" % cert_db)
+ serial = os.path.join(self._outdir, "serial")
+ if not os.path.exists(serial):
+ os.system("/bin/echo '01' > %s" % serial)
+
+ cnf = write_openssl_cnf(self._outdir, self._name, {})
+
+ # Create the CA key
+ key_file = os.path.join(self._outdir, "private",
"cakey.pem")
+ cmd = "%s genrsa -out %s 4096" % (OPENSSL_PROG, key_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+
+ # Make the self-signed CA certificate
+ cert_file = os.path.join(self._outdir, "%s_ca_cert.pem" % self._name)
+ cmd = "%s req -config %s -new -x509 -days 3650 -key %s -out %s -extensions
v3_ca" % (OPENSSL_PROG, cnf, key_file, cert_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+
+ os.remove(cnf)
+ print "Success. Your Certificate Authority directory is: %s\n" %
self._outdir
+
+ def _gencert_normal(self, args):
+ cadir = argfind(args, 'cadir')
+ if not cadir:
+ print_usage(self._prog)
+ sys.exit(1)
+ caname = argfind(args, 'caname')
+ if not caname:
+ print_usage(self._prog)
+ sys.exit(1)
+
+ cnf = write_openssl_cnf(cadir, caname, self._name, {})
+
+ # Generate key
+ key_file = os.path.join(self._outdir, "%s_key.pem" % self._name)
+ cmd = "%s genrsa -out %s 4096" % (OPENSSL_PROG, key_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+ print ""
+
+ # Generate the certificate request
+ req_file = os.path.join(self._outdir, "%s_req.pem" % self._name)
+ cmd = '%s req -config %s -new -nodes -out %s -key %s' % (OPENSSL_PROG,
cnf, req_file, key_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+ print ""
+
+ # Sign the request with the CA's certificate and key
+ cert_file = os.path.join(self._outdir, "%s_cert.pem" % self._name)
+ cmd = '%s ca -config %s -days 3650 -out %s -infiles %s' % (OPENSSL_PROG,
cnf, cert_file, req_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+ print ""
+
+ # Cat the normal cert and key together
+ key_and_cert = os.path.join(self._outdir, "%s_key_and_cert.pem" %
self._name)
+ cmd = '/bin/cat %s %s > %s' % (key_file, cert_file, key_and_cert)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+
+ # Cleanup: remove the cert, key, and request files
+ cmd = "/bin/rm -f %s %s %s" % (key_file, req_file, cert_file)
+ if os.system(cmd) != 0:
+ raise CertHelperException("\n\nERROR: Command '%s' was not
successful.\n" % cmd)
+
+ os.remove(cnf)
+ print "Success. Your certificate and key file is: %s\n" %
key_and_cert
+
+
+def write_openssl_cnf(home, ca_name, commonname, opt_dict):
+ (fd, name) = tempfile.mkstemp('', 'openssl_cnf_', dir=None,
text=True)
+ os.write(fd, """
+##############################
+HOME = %s
+RANDFILE = .rand
+
+##############################
+[ ca ]
+default_ca = CA_default\n
+
+##############################
+[ CA_default ]
+
+dir = $HOME
+certs = $dir/certs
+crl_dir = $dir/crl
+database = $dir/index.txt
+new_certs_dir = $dir/newcerts
+
+certificate = $dir/cacert.pem
+private_key = $dir/private/cakey.pem
+serial = $dir/serial
+crl = $dir/crl.pem
+
+x509_extensions = usr_cert
+
+name_opt = ca_default
+cert_opt = ca_default
+
+default_days = 3650
+default_crl_days= 30
+default_md = sha256
+preserve = no
+
+policy = policy_match
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+##############################
+[ req ]
+default_bits = 4096
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+string_mask = MASK:0x2002
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FR
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = IDF
+
+localityName = Locality Name (eg, city)
+localityName_default = Paris
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = RPM Fusion Project
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+organizationalUnitName_default = RPM Fusion Builders
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_default = %s
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+emailAddress_default = buildsys(a)rpmfusion.org
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+##############################
+[ usr_cert ]
+
+basicConstraints=CA:FALSE
+nsComment = "OpenSSL Generated Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+
+##############################
+[ v3_ca ]
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints = CA:true
+
+""" % (home, commonname ))
+
+ return name
+
+def argfind(arglist, prefix):
+ val = None
+ for arg in arglist:
+ if arg.startswith('--%s=' % prefix):
+ val = arg
+ break
+ if not val:
+ return None
+ val = val.replace('--%s=' % prefix, '')
+ return val
+
+if __name__ == '__main__':
+ prog = sys.argv[0]
+ if len(sys.argv) < 3:
+ print_usage(prog)
+ sys.exit(1)
+
+ outdir = argfind(sys.argv, 'outdir')
+ if not outdir:
+ print_usage(prog)
+ sys.exit(1)
+
+ name = argfind(sys.argv, 'name')
+ if not name:
+ print_usage(prog)
+ sys.exit(1)
+
+ ch = CertHelper(prog, outdir, name)
+ try:
+ ch.dispatch(sys.argv[1], sys.argv)
+ except CertHelperException, e:
+ print e.message
+ sys.exit(1)
+
+ sys.exit(0)
+
diff --git a/roles/fas_server/files/controllers.py
b/roles/fas_server/files/controllers.py
new file mode 100644
index 0000000..6b15a46
--- /dev/null
+++ b/roles/fas_server/files/controllers.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2008 Ricky Zhou
+# Copyright © 2008-2014 Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties 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, write to the Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
+# incorporated in the source code or documentation are not subject to the GNU
+# General Public License and may only be used or replicated with the express
+# permission of Red Hat, Inc.
+#
+# Author(s): Ricky Zhou <ricky(a)fedoraproject.org>
+# Mike McGrath <mmcgrath(a)redhat.com>
+# Toshio Kuratomi <toshio(a)redhat.com>
+#
+from bunch import Bunch
+
+from turbogears import expose, config, identity, redirect
+from turbogears.database import session
+from cherrypy import request
+
+import turbogears
+import cherrypy
+import time
+
+from fedora.tg import controllers as f_ctrlers
+from fedora.tg.utils import request_format
+
+from fas import release
+from fas.user import User
+from fas.group import Group
+from fas.configs import Config
+from fas.fpca import FPCA
+from fas.json_request import JsonRequest
+from fas.help import Help
+from fas.model import Session, People
+from fas.model import SessionTable
+
+
+from fas.auth import undeprecated_cla_done
+from fas.util import available_languages
+
+from fas import plugin
+
+import os
+
+import datetime
+
+import socket
+
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
+class SQLAlchemyStorage:
+ def __init__(self):
+ pass
+
+ def load(self, session_id):
+ s = Session.query.get(session_id)
+ if not s:
+ return None
+ expiration_time = s.expiration_time
+ pickled_data = s.data
+ data = pickle.loads(pickled_data.encode('utf-8'))
+ return (data, expiration_time)
+
+ # This is an iffy one. CherryPy's built in session
+ # storage classes use delete(self, id=None), but it
+ # isn't called from anywhere in cherrypy. I think we
+ # can do this as long as we're careful about how we call it.
+ def delete(self, session_id=None):
+ if session_id is None:
+ session_id = cherrypy.session.id
+ s = Session.query.get(session_id)
+ session.delete(s)
+ session.flush()
+
+ def save(self, session_id, data, expiration_time):
+ pickled_data = pickle.dumps(data)
+ s = Session.query.get(session_id)
+ if not s:
+ s = Session()
+ s.id = session_id
+ s.data = pickled_data
+ s.expiration_time = expiration_time
+ session.flush()
+
+ def acquire_lock(self):
+ pass
+
+ def release_lock(self):
+ pass
+
+ def clean_up(self, sess):
+ # This is to make sure that only one server cleans up sessions
+ if socket.gethostname() != 'fas01.phx2.fedoraproject.org':
+ return
+ result = SessionTable.delete(
+ SessionTable.c.expiration_time.__lt__(datetime.datetime.now())
+ ).execute()
+
+config.update({'session_filter.storage_class': SQLAlchemyStorage})
+
+def get_locale(locale=None):
+ if locale:
+ return locale
+ try:
+ return turbogears.identity.current.user.locale
+ except AttributeError:
+ pass
+ try:
+ return cherrypy.request.simple_cookie['fas_locale'].value
+ except KeyError:
+ pass
+
+ default_language = config.get('default_language',
+ turbogears.i18n.utils._get_locale())
+ return default_language
+
+config.update({'i18n.get_locale': get_locale})
+
+
+def add_custom_stdvars(variables):
+ return variables.update({'gettext': _, "lang": get_locale(),
+ 'available_languages': available_languages(),
+ 'fas_version': release.VERSION,
+ 'webmaster_email': config.get('webmaster_email')})
+turbogears.view.variable_providers.append(add_custom_stdvars)
+
+# from fas import json
+# import logging
+# log = logging.getLogger("fas.controllers")
+
+#TODO: Appropriate flash icons for errors, etc.
+# mmcgrath wonders if it will be handy to expose an encrypted mailer with fas
+# over json for our apps
+
+class Root(plugin.RootController):
+
+ user = User()
+ group = Group()
+ fpca = FPCA()
+ json = JsonRequest()
+ config = Config()
+ help = Help()
+
+ def __init__(self):
+ # TODO: Find a better place for this.
+ os.environ['GNUPGHOME'] = config.get('gpghome')
+ plugin.RootController.__init__(self)
+
+ def getpluginident(self):
+ return 'fas'
+
+ @expose(template="fas.templates.welcome", allow_json=True)
+ def index(self):
+ if turbogears.identity.not_anonymous():
+ if request_format() == 'json':
+ # redirects don't work with JSON calls. This is a bit of a
+ # hack until we can figure out something better.
+ return dict()
+ turbogears.redirect('/home')
+ return dict(now=time.ctime())
+
+ @identity.require(identity.not_anonymous())
+ @expose(template="fas.templates.home", allow_json=True)
+ def home(self):
+ user_name = turbogears.identity.current.user_name
+ person = People.by_username(user_name)
+ (cla_done, undeprecated_cla) = undeprecated_cla_done(person)
+
+ person = person.filter_private()
+ return dict(person=person, memberships=person['memberships'],
cla=undeprecated_cla)
+
+ @expose(template="fas.templates.about")
+ def about(self):
+ return dict()
+
+ @expose(template="fas.templates.login", allow_json=True)
+ def login(self, forward_url=None, *args, **kwargs):
+ '''Page to become authenticated to the Account System.
+
+ This shows a small login box to type in your username and password
+ from the Fedora Account System.
+
+ :kwarg forward_url: The url to send to once authentication succeeds
+ '''
+ actual_login_dict = f_ctrlers.login(forward_url=forward_url, *args, **kwargs)
+
+ try:
+ login_dict = Bunch()
+ login_dict['user'] = Bunch()
+ for field in People.allow_fields['complete']:
+ login_dict['user'][field] = None
+ for field in People.allow_fields['self']:
+ login_dict['user'][field] =
getattr(actual_login_dict['user'], field)
+ # Strip out things that the user shouldn't see about their own
+ # login
+ login_dict['user']['internal_comments'] = None
+ login_dict['user']['emailtoken'] = None
+ login_dict['user']['security_answer'] = None
+ login_dict['user']['alias_enabled'] = None
+ login_dict['user']['passwordtoken'] = None
+
+ # Add things that are needed by some other apps
+ login_dict['user'].approved_memberships = list(
+ actual_login_dict['user'].approved_memberships)
+ login_dict['user'].memberships =
list(actual_login_dict['user'].memberships)
+ login_dict['user'].unapproved_memberships = list(
+ actual_login_dict['user'].unapproved_memberships)
+ login_dict['user'].group_roles =
list(actual_login_dict['user'].group_roles)
+ login_dict['user'].roles =
list(actual_login_dict['user'].roles)
+ login_dict['user'].groups = [g.name for g in
actual_login_dict['user'].approved_memberships]
+ return login_dict
+ except KeyError, e:
+ # No problem, this usually means that we failed to login and
+ # therefore we don't have a user field.
+ login_dict = actual_login_dict
+
+ if not identity.current.anonymous and identity.was_login_attempted() \
+ and not identity.get_identity_errors():
+ # Success that needs to be passed back via json
+ return login_dict
+
+ if identity.was_login_attempted() and request.fas_provided_username:
+ if request.fas_identity_failure_reason == 'status_inactive':
+ turbogears.flash(_('Your old password has expired. Please'
+ ' reset your password below.'))
+ if request_format() != 'json':
+ redirect('/user/resetpass')
+ if request.fas_identity_failure_reason == 'status_account_disabled':
+ turbogears.flash(_('Your account is currently disabled. For'
+ ' more information, please contact %(admin_email)s' %
+ {'admin_email': config.get('accounts_email')}))
+ if request_format() != 'json':
+ redirect('/login')
+
+ return login_dict
+
+ @expose(allow_json=True)
+ def logout(self):
+ return f_ctrlers.logout()
+
+ @expose()
+ def language(self, locale):
+ if locale not in available_languages():
+ turbogears.flash(_('The language \'%s\' is not available.') %
locale)
+ redirect(request.headers.get("Referer", "/"))
+ return dict()
+ #turbogears.i18n.set_session_locale(locale)
+ cherrypy.response.simple_cookie['fas_locale'] = locale
+ redirect(request.headers.get("Referer", "/"))
+ return dict()
+
diff --git a/roles/fas_server/files/export-bugzilla
b/roles/fas_server/files/export-bugzilla
new file mode 100644
index 0000000..25c3e1c
--- /dev/null
+++ b/roles/fas_server/files/export-bugzilla
@@ -0,0 +1,153 @@
+#!/usr/bin/python -t
+__requires__ = 'TurboGears'
+import pkg_resources
+pkg_resources.require('CherryPy >= 2.0, < 3.0alpha')
+
+import logging
+logging.basicConfig()
+
+import os
+import sys
+import getopt
+import xmlrpclib
+import smtplib
+from email.Message import Message
+import warnings
+
+# Ignore DeprecationWarnings. This allows us to stop getting email
+# from the cron job. We'll see the same warnings from the server starting up
+warnings.simplefilter('ignore', DeprecationWarning)
+
+import turbogears
+import bugzilla
+from turbogears import config
+cfgfile = '/etc/export-bugzilla.cfg'
+if os.access('./export-bugzilla.cfg', os.R_OK):
+ cfgfile = './export-bugzilla.cfg'
+turbogears.update_config(configfile=cfgfile)
+from turbogears.database import session
+from fas.model import BugzillaQueue
+
+BZSERVER = config.get('bugzilla.url',
'https://bugzilla.rpmfusion.org/bugzilla-cvs/xmlrpc.cgi')
+BZUSER = config.get('bugzilla.username')
+BZPASS = config.get('bugzilla.password')
+MAILSERVER = config.get('mail.server', 'localhost')
+ADMINEMAIL = config.get('mail.admin_email',
'sysadmin(a)lists.rpmfusion.org')
+NOTIFYEMAIL = config.get('mail.notify_email',
['sysadmin(a)lists.rpmfusion.org'])
+
+if __name__ == '__main__':
+ opts, args = getopt.getopt(sys.argv[1:], '', ('usage',
'help'))
+ if len(args) != 2 or ('--usage','') in opts or
('--help','') in opts:
+ print """
+ Usage: export-bugzilla.py GROUP BUGZILLA_GROUP
+ """
+ sys.exit(1)
+ ourGroup = args[0]
+ bzGroup = args[1]
+
+ server = bugzilla.Bugzilla(url=BZSERVER, user=BZUSER, password=BZPASS,
+ cookiefile=None, tokenfile=None)
+ bugzilla_queue = BugzillaQueue.query.join('group').filter_by(
+ name=ourGroup)
+
+ no_bz_account = []
+ for entry in bugzilla_queue:
+ # Make sure we have a record for this user in bugzilla
+ if entry.action == 'r':
+ # Remove the user's bugzilla group
+ try:
+ server.updateperms(entry.email, 'rem', bzGroup)
+ except xmlrpclib.Fault, e:
+ if e.faultCode == 51:
+ # It's okay, not having this user is equivalent to setting
+ # them to not have this group.
+ pass
+ else:
+ raise
+
+ elif entry.action == 'a':
+ # Make sure the user exists
+ try:
+ server.getuser(entry.email)
+ except xmlrpclib.Fault, e:
+ if e.faultCode == 51:
+ # This user doesn't have a bugzilla account yet
+ # add them to a list and we'll let them know.
+ no_bz_account.append(entry)
+ continue
+ else:
+ print 'Error:', e, entry.email, entry.person.human_name
+ raise
+ server.updateperms(entry.email, 'add', bzGroup)
+ else:
+ print 'Unrecognized action code: %s %s %s %s %s' % (entry.action,
+ entry.email, entry.person.human_name, entry.person.username,
entry.group.name)
+ continue
+
+ # Remove them from the queue
+ session.delete(entry)
+ session.flush()
+
+# Mail the people without bugzilla accounts
+ if '$USER' in NOTIFYEMAIL:
+ for person in no_bz_account:
+ smtplib.SMTP(MAILSERVER)
+ msg = Message()
+ message = '''Hello %(name)s,
+
+ As a RPM Fusion packager, we grant you permissions to make changes to bugs in
+ bugzilla to all RPM Fusion bugs. This lets you work together with other RPM Fusion
+ developers in an easier fashion. However, to enable this functionality, we
+ need to have your bugzilla email address stored in the RPM Fusion Account System.
+ At the moment you have:
+
+ %(email)s
+
+ which bugzilla is telling us is not an account in bugzilla. If you could
+ please set up an account in bugzilla with this address or change your email
+ address on your RPM Fusion Account to match an existing bugzilla account this would
+ let us go forward.
+
+ Note: this message is being generated by an automated script. You'll continue
+ getting this message until the problem is resolved. Sorry for the
+ inconvenience.
+
+ Thank you,
+ The RPM Fusion Account System
+ %(admin_email)s
+ ''' % {'name': person.person.human_name, 'email':
person.email,
+ 'admin_email': ADMINEMAIL}
+
+ msg.add_header('To', person.email)
+ msg.add_header('From', ADMINEMAIL)
+ msg.add_header('Subject', 'RPM Fusion Account System and Bugzilla
Mismatch')
+ msg.set_payload(message)
+ smtp = smtplib.SMTP(MAILSERVER)
+ smtp.sendmail(ADMINEMAIL, [person.email], msg.as_string())
+ smtp.quit()
+ recipients = [e for e in NOTIFYEMAIL if e != '$USER']
+ if recipients and no_bz_account:
+ smtplib.SMTP(MAILSERVER)
+ msg = Message()
+ people = []
+ for person in no_bz_account:
+ if person.person.status == 'Active':
+ people.append(' %(user)s -- %(name)s -- %(email)s' %
+ {'name': person.person.human_name, 'email':
person.email,
+ 'user': person.person.username})
+ if people:
+ people = '\n'.join(people)
+ message = '''
+The following people are in the packager group but do not have email addresses
+that are valid in bugzilla:
+%s
+
+''' % people
+
+ msg.add_header('From', ADMINEMAIL)
+ msg.add_header('To', ', '.join(recipients))
+ msg.add_header('Subject', 'RPM Fusion Account System and Bugzilla
Mismatch')
+ msg.set_payload(message)
+ smtp = smtplib.SMTP(MAILSERVER)
+ smtp.sendmail(ADMINEMAIL, recipients, msg.as_string())
+ smtp.quit()
diff --git a/roles/fas_server/files/fas-log.cfg b/roles/fas_server/files/fas-log.cfg
new file mode 100644
index 0000000..a1ed30c
--- /dev/null
+++ b/roles/fas_server/files/fas-log.cfg
@@ -0,0 +1,29 @@
+# LOGGING
+# Logging is often deployment specific, but some handlers and
+# formatters can be defined here.
+
+[logging]
+[[formatters]]
+[[[message_only]]]
+format='*(message)s'
+
+[[[full_content]]]
+format='*(name)s *(levelname)s *(message)s'
+
+[[handlers]]
+[[[debug_out]]]
+class='StreamHandler'
+level='DEBUG'
+args='(sys.stdout,)'
+formatter='full_content'
+
+[[[access_out]]]
+class='StreamHandler'
+level='WARN'
+args='(sys.stdout,)'
+formatter='message_only'
+
+[[[error_out]]]
+class='StreamHandler'
+level='ERROR'
+args='(sys.stdout,)'
diff --git a/roles/fas_server/files/fedora-ca-client-openssl.cnf
b/roles/fas_server/files/fedora-ca-client-openssl.cnf
new file mode 100644
index 0000000..2adf8d3
--- /dev/null
+++ b/roles/fas_server/files/fedora-ca-client-openssl.cnf
@@ -0,0 +1,317 @@
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = /var/lib/rpmfusion-ca/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = . # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several ctificates with same subject.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem # The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha256 # which md to use.
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 4096
+default_md = sha256
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+# we use PrintableString+UTF8String mask so if pure ASCII texts are used
+# the resulting certificates are compatible with Netscape
+string_mask = MASK:0x2002
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FR
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = IDF
+
+localityName = Locality Name (eg, city)
+localityName_default = Paris
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = RPM Fusion Project
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+#challengePassword = A challenge password
+#challengePassword_min = 0
+#challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl =
http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl =
http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
diff --git a/roles/fas_server/files/ipa.staging.pem
b/roles/fas_server/files/ipa.staging.pem
new file mode 100644
index 0000000..b4f721f
--- /dev/null
+++ b/roles/fas_server/files/ipa.staging.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMR4wHAYDVQQKDBVTVEcu
+RkVET1JBUFJPSkVDVC5PUkcxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0
+eTAeFw0xNjA4MDQxNzI3NTlaFw0zNjA4MDQxNzI3NTlaMEAxHjAcBgNVBAoMFVNU
+Ry5GRURPUkFQUk9KRUNULk9SRzEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9y
+aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5HiQvnHPP+3AEJPR
+wlizXKhaxPhwVoO68r9VEcEDUOkRo78LQ0ZLEcwaAZBX64uTeStPd5azU6pEN0Gi
+124djqJZpBs3v9YNsvt+R4Au7SQhAdBu370VcKEKjj79UYc7e70E04ycv3jJP6hi
+7+RD+BeOwPHmMwEUXF2JrKytNOmRCfxoZ7LnQfH80a+YZA1MmpAEGIo8+pRuvGth
+cORUTtyEWsaBgpek6wnPjs7lDQG1LJyi0K2L/YQPYAisZCMBoM/ck5SAHSd4F6+P
+BcHMhQd2DhsxRhIb5Se4Zi8LUxAvkVdRlCsIk+6bdIM9SpzVd9+RtBnE3LOKu1TH
+bxCW2QIDAQABo4G0MIGxMB8GA1UdIwQYMBaAFFfHodJF0pk5OgP9sgMqtPOdOaqC
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBRXx6HS
+RdKZOToD/bIDKrTznTmqgjBOBggrBgEFBQcBAQRCMEAwPgYIKwYBBQUHMAGGMmh0
+dHA6Ly9pcGEwMS5zdGcucGh4Mi5mZWRvcmFwcm9qZWN0Lm9yZzo4MC9jYS9vY3Nw
+MA0GCSqGSIb3DQEBCwUAA4IBAQAnBIll/83TixgIu6JByImWWK7Ew++33heW+rDQ
+GQhol1Bp7Gk4wsLpGLATDI+ur25kREnzPfwXLcptO/5GvMEe8rwwvo1b6zkl5VEq
+vCA5dQimBTKTlTX2JFZze/KkiKa7WKZAopnSQVkPsSnAZXClTbjALXHwdQ0bDEUU
+old29skK0Xvf+WGmE3/SvQmEcueDeDJcV7Jckj45ZuqegklBG6y+fG5ELV0B4u9l
+p0ySWPVoaWSRR+izB8Kq9gCP0a5HsO3u5qJ+HRWr+Md7KboMGX29pQehakvtcnta
+jr+txnKWhel7c7bEwa6JVRFoOO7jcOHEMohPbKl3Ef/n0uCQ
+-----END CERTIFICATE-----
+
diff --git a/roles/fas_server/tasks/main.yml b/roles/fas_server/tasks/main.yml
new file mode 100644
index 0000000..6120681
--- /dev/null
+++ b/roles/fas_server/tasks/main.yml
@@ -0,0 +1,405 @@
+---
+# Tasks to set up fas_server
+
+- name: install needed packages
+ yum: pkg={{ item }} state=installed
+ with_items:
+ - fas
+ - fas-plugin-yubikey
+ - python-turbojson
+ - python-tgcaptcha
+ - httpd
+ - mod_wsgi
+ - python-bunch
+ - python-requests-kerberos
+ tags:
+ - packages
+ - fas
+
+- name: install needed packages
+ when: env == "staging"
+ yum: pkg={{ item }} state=installed
+ with_items:
+ - krb5-workstation
+ - python-requests-kerberos
+ tags:
+ - packages
+ - fas
+
+- name: configure krb5
+ when: env == "staging"
+ template: src=krb5.conf.j2 dest=/etc/krb5.conf owner=root group=root mode=0644
+ tags:
+ - config
+ - fas
+
+- name: install IPA keytab
+ when: env == "staging"
+ copy: >
+ src="{{ private }}/files/keytabs/{{env}}/fas_sync"
+ dest="/etc/fas_sync_keytab"
+ owner=fas
+ group=fas
+ mode=0600
+ tags:
+ - config
+ - fas
+
+- name: install ipa public cert
+ when: env == "staging"
+ copy: src=ipa.{{env}}.pem dest=/etc/fas_sync_cert.pem owner=root group=root mode=0644
+ tags:
+ - config
+ - fas
+
+- name: enable httpd_can_network_connect selinux boolean
+ seboolean: name={{ item }} state=yes persistent=yes
+ with_items:
+ - httpd_can_network_connect
+ - allow_ypbind
+ tags:
+ - config
+ - fas
+
+- name: setup /var/www/.python-eggs directory
+ file: path=/var/www/.python-eggs owner=apache group=apache mode=0700 state=directory
+ tags:
+ - config
+ - fas
+
+- name: setup /etc/fas-gpg directory
+ file: path=/etc/fas-gpg owner=fas group=fas mode=0700 state=directory
setype=httpd_sys_rw_content_t
+ tags:
+ - config
+ - fas
+
+- name: install /etc/httpd/conf.d/accounts.conf file
+ template: >
+ src="fas-app.conf.j2"
+ dest="/etc/httpd/conf.d/accounts.conf"
+ owner=root
+ group=root
+ mode=0644
+ notify:
+ - reload httpd
+ tags:
+ - config
+ - fas
+
+- name: setup /etc/pki/fas directory
+ file: path=/etc/pki/fas owner=fas group=fas mode=0755 state=directory
+ tags:
+ - config
+ - fas
+
+- name: install pythonsitelib/fas/config/log.cfg
+ copy: >
+ src="fas-log.cfg"
+ dest="/usr/lib/python2.6/site-packages/fas/config/log.cfg"
+ owner=root
+ group=root
+ mode=0644
+ notify:
+ - reload httpd
+ tags:
+ - config
+ - fas
+
+# $bugzillaUser = "fedora-admin-xmlrpc(a)redhat.com"
+
+- name: install /etc/fas-gpg/pubring.gpg file
+ copy: >
+ src="{{ private }}/files/fas-gpg/pubring.gpg"
+ dest="/etc/fas-gpg/pubring.gpg"
+ owner=fas
+ group=fas
+ mode=0600
+ tags:
+ - config
+ - fas
+
+- name: install /etc/pki/fas/rpmfusion-server-ca.cert file
+ copy: >
+ src="{{ private }}/files/rpmfusion-ca.cert"
+ dest="/etc/pki/fas/rpmfusion-server-ca.cert"
+ owner=fas
+ group=fas
+ mode=0644
+ tags:
+ - config
+ - fas
+
+- name: install /etc/pki/fas/rpmfusion-upload-ca.cert file
+ copy: >
+ src="{{ private }}/files/rpmfusion-ca.cert"
+ dest="/etc/pki/fas/rpmfusion-upload-ca.cert"
+ owner=fas
+ group=fas
+ mode=0644
+ tags:
+ - config
+ - fas
+
+- name: install /usr/share/fas/static/rpmfusion-server-ca.cert file
+ copy: >
+ src="{{ private }}/files/rpmfusion-ca.cert"
+ dest="/usr/share/fas/static/rpmfusion-server-ca.cert"
+ owner=root
+ group=root
+ mode=0644
+ tags:
+ - config
+ - fas
+
+- name: install /usr/share/fas/static/rpmfusion-upload-ca.cert file
+ copy: >
+ src="{{ private }}/files/rpmfusion-ca.cert"
+ dest="/usr/share/fas/static/rpmfusion-upload-ca.cert"
+ owner=root
+ group=root
+ mode=0644
+ tags:
+ - config
+ - fas
+
+- name: install /etc/fas.cfg file
+ template: >
+ src="fas.cfg.j2"
+ dest="/etc/fas.cfg"
+ owner=fas
+ group=apache
+ mode=0640
+ notify:
+ - reload httpd
+ tags:
+ - config
+ - fas
+
+- name: install /usr/local/bin/yubikey-remove.py file
+ when: env == "staging"
+ template: >
+ src="yubikey-remove.py.j2"
+ dest="/usr/local/bin/yubikey-remove.py"
+ owner=fas
+ group=fas
+ mode=0750
+ tags:
+ - config
+ - fas
+
+# $gen_cert = "True"
+
+- name: install /etc/fas.cfg file
+ template: >
+ src="fas.cfg.j2"
+ dest="/etc/fas.cfg"
+ owner=fas
+ group=apache
+ mode=0640
+ when: master_fas_node == True
+ notify:
+ - reload httpd
+ tags:
+ - config
+ - fas
+
+- name: setup /var/lock/rpmfusion-ca directory
+ file: path=/var/lock/rpmfusion-ca owner=fas group=fas mode=0700 state=directory
setype=var_lock_t
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: setup /var/lib/rpmfusion-ca directory
+ file: path=/var/lib/rpmfusion-ca owner=fas group=fas mode=0771 state=directory
setype=httpd_sys_content_t
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+#- name: install /var/lib/rpmfusion-ca/.rnd file
+# file: path=/var/lib/rpmfusion-ca/.rnd owner=fas group=fas mode=0600
setype=httpd_sys_content_t
+# when: master_fas_node == True
+# tags:
+# - config
+# - fas
+
+- name: setup /var/lib/rpmfusion-ca/newcerts directory
+ file: path=/var/lib/rpmfusion-ca/newcerts owner=fas group=fas mode=0700
state=directory
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: setup /var/lib/rpmfusion-ca/private directory
+ file: path=/var/lib/rpmfusion-ca/private owner=fas group=fas mode=0700 state=directory
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /var/lib/rpmfusion-ca/private/cakey.pem file
+ copy: >
+ src="{{ private }}/files/cakey.pem"
+ dest="/var/lib/rpmfusion-ca/private/cakey.pem"
+ owner=fas
+ group=fas
+ mode=0400
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /var/lib/rpmfusion-ca/Makefile file
+ copy: >
+ src="Makefile.rpmfusion-ca"
+ dest="/var/lib/rpmfusion-ca/Makefile"
+ owner=root
+ group=root
+ mode=0644
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /var/lib/rpmfusion-ca/openssl.cnf file
+ copy: >
+ src="rpmfusion-ca-client-openssl.cnf"
+ dest="/var/lib/rpmfusion-ca/openssl.cnf"
+ owner=root
+ group=root
+ mode=0644
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /var/lib/rpmfusion-ca/certhelper.py file
+ copy: >
+ src="certhelper.py"
+ dest="/var/lib/rpmfusion-ca/certhelper.py"
+ owner=root
+ group=root
+ mode=0755
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /var/lib/rpmfusion-ca/cacert.pem file
+ copy: >
+ src="{{ private }}/files/rpmfusion-ca.cert"
+ dest="/var/lib/rpmfusion-ca/cacert.pem"
+ owner=root
+ group=root
+ mode=0644
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+#For publishing the crl
+- name: setup /srv/web/ca directory
+ file: path=/srv/web/ca owner=apache group=apache mode=0755 state=directory
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: twice every month, force a new crl to be created
+ cron: >
+ name="gen-crl"
+ job="cd /var/lib/rpmfusion-ca ; /usr/bin/make gencrl &> /dev/null"
+ user="fas"
+ minute="0"
+ hour="0"
+ day="1,15"
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: create directory /var/lib/rpmfusion-ca/crl/
+ file: path={{ item }} state=directory
+ with_items:
+ - /var/lib/rpmfusion-ca/crl/
+ tags:
+ - fas
+
+- name: touch /var/lib/rpmfusion-ca/crl/crl.pem and /var/lib/rpmfusion-ca/cacert.pem
+ command: touch /var/lib/rpmfusion-ca/cacert.pem /var/lib/rpmfusion-ca/crl/crl.pem
+ when: master_fas_node == True
+ changed_when: "1 != 1"
+ tags:
+ - config
+ - fas
+
+- name: create /srv/web/ca/crl.pem link
+ file: path="/srv/web/ca/crl.pem" state=link
src="/var/lib/rpmfusion-ca/crl/crl.pem"
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: create /srv/web/ca/cacert.pem link
+ file: path="/srv/web/ca/cacert.pem" state=link
src="/var/lib/rpmfusion-ca/cacert.pem"
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: install /etc/export-bugzilla.cfg file
+ template: >
+ src="export-bugzilla.cfg.j2"
+ dest="/etc/export-bugzilla.cfg"
+ owner=fas
+ group=fas
+ mode=0600
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+
+- name: HOTFIX fix the export-bugzilla cron to not store bugzilla token
+ copy: >
+ src="export-bugzilla"
+ dest="/usr/sbin/export-bugzilla"
+ owner=root
+ group=root
+ mode=0755
+ tags:
+ - config
+ - hotfix
+ - fas
+
+
+- name: run export-bugzilla program
+ cron: >
+ name="export-bugzilla"
+ job="cd /etc; MAILTO=root; /usr/sbin/export-bugzilla fedorabugs
fedora_contrib"
+ user="fas"
+ minute="10"
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+
+- name: HOTFIX fas2.py in python-bugzilla to add a bugzilla override for ticket 4827
+ copy: src={{ roles }}/pkgdb2/files/fas2.py
+ dest=/usr/lib/python2.6/site-packages/fedora/client/fas2.py
+ mode=644 owner=root group=root
+ when: master_fas_node == True
+ tags:
+ - config
+ - fas
+ - hotfixfas
+
+- name: HOTFIX make sure only fas01 cleans up sessions
+ copy: src={{ roles }}/fas_server/files/controllers.py
+ dest=/usr/lib/python2.6/site-packages/fas/controllers.py
+ mode=644 owner=root group=root
+ tags:
+ - config
+ - fas
+ - hotfixfas
diff --git a/roles/fas_server/templates/export-bugzilla.cfg.j2
b/roles/fas_server/templates/export-bugzilla.cfg.j2
new file mode 100644
index 0000000..a337b4c
--- /dev/null
+++ b/roles/fas_server/templates/export-bugzilla.cfg.j2
@@ -0,0 +1,20 @@
+[global]
+# bugzilla.url =
https://bugdev.devel.redhat.com/bugzilla-cvs/xmlrpc.cgi
+# Running from fas1 so we need the PHX available address.
+bugzilla.url = "https://bugzilla.rpmfusion.org/xmlrpc.cgi"
+# bugzilla.url = "https://bugzilla.redhat.com/xmlrpc.cgi"
+bugzilla.username = "{{ bugzilla_user }}"
+bugzilla.password = "{{ bugzilla_password }}"
+
+# Mail server for sending invalid bugzilla account messages
+mail.server = 'localhost'
+mail.admin_email = 'root(a)rpmfusion.org'
+
+# This is a list (*must* have a comma) of email addresses to send messages about
+# invalid bugzilla accounts to. The strin '$USER' is special. If present in
the
+# list, it will send an email to the user whose email address is not in bugzilla.
+mail.notify_email = 'root(a)rpmfusion.org',
+
+# At the moment, we have to extract this information directly from the fas2
+# database. We can build a json interface for it at a later date.
+sqlalchemy.dburi = "postgres://fas:{{ fasDbPassword }}@db02/fas2"
diff --git a/roles/fas_server/templates/fas-app.conf.j2
b/roles/fas_server/templates/fas-app.conf.j2
new file mode 100644
index 0000000..e210248
--- /dev/null
+++ b/roles/fas_server/templates/fas-app.conf.j2
@@ -0,0 +1,29 @@
+Alias /accounts/static /usr/share/fas/static
+Alias /favicon.ico /usr/share/fas/static/favicon.ico
+
+Alias /accounts/rpmfusion-server-ca.cert /usr/share/fas/static/rpmfusion-server-ca.cert
+Alias /accounts/rpmfusion-upload-ca.cert /usr/share/fas/static/rpmfusion-upload-ca.cert
+
+# For serving the crl
+Alias /ca /srv/web/ca
+
+AddType application/x-x509-ca-cert cacert.pem
+AddType application/x-x509-crl crl.pem
+
+WSGISocketPrefix run/wsgi
+
+# TG implements its own signal handler.
+WSGIRestrictSignal Off
+
+# These are the real tunables
+WSGIDaemonProcess fas processes={{wsgi_procs}} threads={{wsgi_threads}}
maximum-requests=100 user=fas group=fas display-name=fas inactivity-timeout=300
shutdown-timeout=10
+WSGIPythonOptimize 1
+
+WSGIScriptAlias /accounts /usr/sbin/fas.wsgi/accounts
+
+<Location /accounts>
+ WSGIProcessGroup fas
+ Order deny,allow
+ Allow from all
+</Location>
+
diff --git a/roles/fas_server/templates/fas.cfg.j2
b/roles/fas_server/templates/fas.cfg.j2
new file mode 100644
index 0000000..c85f917
--- /dev/null
+++ b/roles/fas_server/templates/fas.cfg.j2
@@ -0,0 +1,295 @@
+[global]
+
+#
+# Deployment type
+# Determines which color of the header is being used
+# Valid options:
+# - "dev": Development
+# - "stg": Staging
+# - "prod": Production
+#
+{% if env == "staging" %}
+deployment_type = "stg"
+{% else %}
+deployment_type = "prod"
+{% endif %}
+
+# TODO: better namespacing (maybe a [fas] section)
+# admingroup is for humans that can see and do anything
+
+###
+### OpenID Support
+###
+{% if env == "staging" %}
+samadhi.baseurl = 'https://admin.stg.rpmfusion.org/'
+{% else %}
+samadhi.baseurl = 'https://admin.rpmfusion.org/'
+{% endif %}
+openidstore = "/var/tmp/fas/openid"
+
+###
+### GPG Keys for specific operations
+###
+# This is the GPG Key ID used to encrypt the answer to the user's security question.
+# The private key should be known to the admins to verify that the user supplied the
correct answer.
+key_securityquestion = 'D1E6AA0A'
+
+###
+### UI
+###
+
+theme = 'fas'
+
+# Personal Info / Form availability
+# Select/deselect items in the form
+show_postal_address = 0
+
+# Language support
+available_languages = ['en', 'en_GB', 'ar', 'ast',
'bg', 'bn', 'bn_IN', 'bs', 'ca', 'cs',
'da', 'de', 'el', 'es', 'eu', 'fa',
'fi', 'fr', 'ga', 'gl', 'he', 'hi',
'hu', 'id', 'is', 'it', 'ja', 'ko',
'lv', 'mai', 'ml', 'mr', 'nb', 'nl',
'pa', 'pl', 'pt_BR', 'pt', 'ru', 'si',
'sk', 'sq', 'sr', 'sv', 'ta', 'te',
'tg', 'tr', 'uk', 'vi', 'zh_CN', 'zh_HK',
'zh_TW']
+
+default_language = 'en'
+
+# Country codes from GEOIP that we don't want to display in
+# country selection boxes
+country_blacklist = ["--", "A1", "A2", "AN",
"AS", "AX", "BI", "BL", "BV",
"CC", "CU", "CV", "CX", "DM",
"FK", "FO", "GF", "GG", "GP",
"GS", "GW", "HM", "IO", "IR",
"IQ", "JE", "KI", "KP", "MF",
"MP", "MS", "MW", "NF", "NR",
"NU", "PM", "PN", "RE", "SB",
"SD", "SH", "SJ", "SY", "TC",
"TF", "TK", "TL", "TV", "UM",
"VC", "VG", "WF", "YT"]
+
+# Captcha
+tgcaptcha2.key = '{{ fasCaptchaSecret }}'
+tgcaptcha2.jpeg_generator = 'vanasco_dowty'
+
+{% if env == "staging" %}
+###
+### IPA Sync settings
+###
+ipa_sync_enabled = True
+ipa_sync_server = 'ipa01.stg.online.rpmfusion.net'
+ipa_sync_principal = 'fas_sync(a)STG.RPMFUSION.ORG'
+ipa_sync_keytab = '/etc/fas_sync_keytab'
+ipa_sync_certfile = '/etc/fas_sync_cert.pem'
+
+{% else %}
+ipa_sync_enabled = False
+{% endif %}
+###
+### Administrative settings
+###
+
+# Usernames that are unavailable for fas allocation
+username_blacklist =
"abuse,accounts,adm,admin,amanda,apache,askfedora,asterisk,bin,board,bodhi,bodhi2,canna,census,chair,chairman,containerbuild,cvsdirsec,cvsdocs,cvseclipse,cvsextras,cvsfont,daemon,dbus,decode,desktop,dgilmore,directors,dovecot,dumper,fama,famsco,fas,fax,fedora,fedorarewards,fesco,freemedia,ftbfs,ftp,ftpadm,ftpadmin,ftpsync,games,gdm,gnomebackup,gopher,gregdek,halt,hostmaster,hotness,ident,info,ingres,jaboutboul,jan,keys,kojiadmin,ldap,legal,logo,lp,mail,mailnull,manager,marketing,masher,masta,mirrormanager,mysql,nagios,named,netdump,news,newsadm,newsadmin,nfsnobody,nobody,noc,notifications,nrpe,nscd,ntp,nut,openvideo,operator,packager,patrick,pcap,pkgdb,pkgsigner,postfix,postgres,postmaster,press,privoxy,pvm,quagga,radiusd,radvd,relnotes,relrod,rel-eng,root,rpc,rpcuser,rpm,rsc,s3-mirror,sales,scholarship,secalert,secondary-signer,security,server-wg,shutdown,smmsp,spevack,squid,sshd,support,sync,system,tickets,toor,updates,usenet,uucp,vcsa,vendors,vendor-s
upport,voting,webalizer,webmaster,wikiadmin,wnn,www,xfs,zabbix"
+email_domain_blacklist = "{{ fas_blocked_emails }}"
+
+# Valid SSH Key
+valid_ssh_key = "rsa,ssh-rsa,ecdsa,ecdsa-sha2"
+
+# admingroup has powers to change anything in the fas UI
+admingroup = 'accounts'
+# systemgroup is for automated systems that can read any info from the FAS db
+systemgroup = 'fas-system'
+# Moderator group provides its members restricted admin power
+# allowed by defined action below.
+# Valid action :
+# modo.allow.update_status, allow approved member to do related action.
+modo.group = 'accounts-moderators'
+modo.allow.update_status = True
+
+# thirdpartygroup is for thirdparties that also need group management
+# via fas, but maintain their own actual account systems
+thirdpartygroup = 'thirdparty'
+
+# Placing a group into privileged_view_group protects the information in it
+# only admins of the group can view the group
+privileged_view_groups = "(^fas-.*)"
+
+# Who should we say is sending email from fas and get email
+# when fas sends a message about something?
+accounts_email = "accounts(a)rpmfusion.org"
+# Who should be listed as the legal contact for the Contributor Agreement?
+legal_cla_email = "noreply(a)rpmfusion.org"
+# Who should be listed as the webmaster contact for the site?
+webmaster_email = "webmaster(a)rpmfusion.org"
+
+# All groups and some users get email aliases created for them via a cron
+# job. This setting is appended to group names when sending email to members
+# of a group. Be sure to set up a cron job for your site for this to work
+email_host = "rpmfusion.org" # as in, web-members@email_host
+
+# Settings for Contributor Agreements
+# Meta group for anyone who's satisfied the contributor agreement requirement
+cla_done_group = "cla_done"
+# The standard group is what you're placed in when you sign the contributor
+# agreement via fas
+cla_standard_group = "cla_fpca"
+# If you have a contributor agreement that you're getting rid of but want
+# to give people a transition period to sign a new one, you can put the
+# deprecated group in here for now.
+cla_deprecated_groups = ['cla_rpmfusion']
+
+# Groups that automatically grant membership to other groups
+# Format: 'group1:a,b,c|group2:d,e,f'
+auto_approve_groups =
'packager:fedorabugs|qa:fedorabugs|security-team:fedorabugs|qa-beaker-user:qa-automation-shell|docs:fedorabugs|cla_fpca:cla_done|cla_redhat:cla_done|cla_dell:cla_done|cla_ibm:cla_done|cla_intel:cla_done'
+
+# Anti-spam approval check script, which injects in both registration and CLA steps
+# In Fedora, this is provided by the Basset service
+{% if env == "staging" %}
+antispam.api.url = 'http://basset01.stg.phx2.rpmfusion.org/basset'
+antispam.api.username = '{{ basset_stg_frontend_user }}'
+antispam.api.password = '{{ basset_stg_frontend_pass }}'
+antispam.registration.autoaccept = False
+antispam.cla.autoaccept = False
+{% endif %}
+
+# Some server parameters that you may want to tweak
+server.socket_port=8088
+server.thread_pool=50
+server.socket_queue_size=30
+
+# Needed for translations
+### Q for ricky: Should this move to app.cfg?
+session_filter.on = True
+
+# Set to True if you'd like to abort execution if a controller gets an
+# unexpected parameter. False by default
+tg.strict_parameters = True
+
+server.webpath='/accounts'
+base_url_filter.on = True
+base_url_filter.use_x_forwarded_host = False
+{% if env == "staging" %}
+base_url_filter.base_url = "https://admin.stg.rpmfusion.org"
+fas.url = "https://admin.stg.rpmfusion.org/accounts/"
+{% else %}
+base_url_filter.base_url = "https://admin.rpmfusion.org"
+fas.url = "https://admin.rpmfusion.org/accounts/"
+{% endif %}
+# Knobs to tweak for debugging
+
+# Enable the debug output at the end on pages.
+# log_debug_info_filter.on = False
+debug = 'off'
+server.environment="production"
+autoreload.package="fas"
+autoreload.on = False
+server.throw_errors = False
+server.log_to_screen = False
+
+# Make the session cookie only return to the host over an SSL link
+visit.cookie.secure = True
+session_filter.cookie_secure = True
+visit.cookie.httponly = True
+
+###
+### Communicating to other services
+###
+
+# Database
+{% if env == "staging" %}
+sqlalchemy.dburi="postgres://fas:{{ fasDbPassword }}@db02/fas2"
+{% else %}
+sqlalchemy.dburi="postgres://fas:{{ fasDbPassword }}@db02/fas2"
+{% endif %}
+sqlalchemy.echo=False
+# When using wsgi, we want the pool to be very low (as a separate instance is
+# run in each apache mod_wsgi thread. So each one is going to have very few
+# concurrent db connections.
+sqlalchemy.pool_size=1
+sqlalchemy.max_overflow=2
+
+# If you're serving standalone (cherrypy), since FAS2 is much busier than
+# other servers due to serving visit and auth via JSON you want higher values
+#sqlalchemy.pool_size=10
+#sqlalchemy.max_overflow=25
+
+memcached_server = "localhost:11211"
+
+# Sending of email via TurboMail
+mail.on = True
+mail.smtp.server = 'localhost'
+#mail.testmode = True
+mail.smtp.debug = False
+mail.encoding = 'utf-8'
+mail.transport = 'smtp'
+mail.manager = 'demand'
+
+# Enable yubikeys
+yubi_server_prefix='http://localhost/yk-val/verify?id='
+{% if env == "staging" %}
+ykksm_db="postgres://ykksmimporter:{{ ykksmimporterPassword
}}(a)db-fas01.stg/ykksm"
+ykval_db="postgres://ykval_verifier:{{ ykval_verifierPassword
}}(a)db-fas01.stg/ykval"
+{% else %}
+ykksm_db="postgres://ykksmimporter:{{ ykksmimporterPassword }}@db-ykksm/ykksm"
+ykval_db="postgres://ykval_verifier:{{ ykval_verifierPassword
}}@db-ykval/ykval"
+{% endif %}
+
+# Enable or disable generation of SSL certificates for users
+gencert = "{{ gen_cert }}"
+
+makeexec = "/usr/bin/make"
+openssl_lockdir = "/var/lock/rpmfusion-ca"
+openssl_digest = "sha256"
+openssl_expire = 15552000 # 60*60*24*180 = 6 months
+openssl_ca_dir = "/var/lib/rpmfusion-ca"
+openssl_ca_newcerts = "/var/lib/rpmfusion-ca/newcerts"
+openssl_ca_index = "/var/lib/rpmfusion-ca/index.txt"
+openssl_c = "FR"
+openssl_st = "IDF"
+openssl_l = "Paris"
+openssl_o = "RPM Fusion Project"
+openssl_ou = "RPM Fusion User Cert"
+
+# Source of entrophy for salts, tokens, passwords
+# os.urandom will be used if this is false.
+use_openssl_rand_bytes = True
+
+
+# These determine where FAS will read the public keyring from used in all GPG operations
+gpgexec = "/usr/bin/gpg"
+gpghome = "/etc/fas-gpg"
+# Note: gpg_fingerprint and gpg_passphrase are for encrypting password reset mail if the
user has
+# a gpg key registered. It's currently broken
+gpg_fingerprint = "C199 1E25 D00A D200 2D2E 54D1 BF7F 1647 C54E 8410"
+gpg_passphrase = "{{ fasGpgPassphrase }}"
+gpg_keyserver = "hkp://subkeys.pgp.net"
+
+[/rpmfusion-server-ca.cert]
+static_filter.on = True
+static_filter.file = "/etc/pki/fas/rpmfusion-server-ca.cert"
+
+[/rpmfusion-upload-ca.cert]
+static_filter.on = True
+static_filter.file = "/etc/pki/fas/rpmfusion-upload-ca.cert"
+
+# LOGGING
+# Logging configuration generally follows the style of the standard
+# Python logging module configuration. Note that when specifying
+# log format messages, you need to use *() for formatting variables.
+# Deployment independent log configuration is in fas/config/log.cfg
+[logging]
+
+[[loggers]]
+[[[fas]]]
+level='DEBUG'
+qualname='fas'
+handlers=['debug_out']
+
+[[[allinfo]]]
+level='INFO'
+handlers=['debug_out']
+
+#[[[access]]]
+#level='INFO'
+#qualname='turbogears.access'
+#handlers=['access_out']
+#propagate=0
+
+[[[identity]]]
+level='WARN'
+qualname='turbogears.identity'
+handlers=['access_out']
+propagate=0
+
+[[[database]]]
+# Set to INFO to make SQLAlchemy display SQL commands
+level='ERROR'
+qualname='sqlalchemy.engine'
+handlers=['debug_out']
+propagate=0
diff --git a/roles/fas_server/templates/krb5.conf.j2
b/roles/fas_server/templates/krb5.conf.j2
new file mode 100644
index 0000000..eb5806e
--- /dev/null
+++ b/roles/fas_server/templates/krb5.conf.j2
@@ -0,0 +1,31 @@
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+[libdefaults]
+{% if env == "production" %}
+ default_realm =
RPMFUSION.ORG
+{% else %}
+ default_realm =
STG.RPMFUSION.ORG
+{% endif %}
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ ticket_lifetime = 24h
+ renew_lifetime = 7d
+ forwardable = true
+
+[realms]
+{% if env == "production" %}
+
RPMFUSION.ORG = {
+ kdc =
ipa01.online.rpmfusion.net
+ admin_server =
ipa01.online.rpmfusion.net
+ }
+{% else %}
+
STG.RPMFUSION.ORG = {
+ kdc =
ipa01.stg.online.rpmfusion.net
+ admin_server =
ipa01.stg.online.rpmfusion.net
+ }
+{% endif %}
+
+[domain_realm]
diff --git a/roles/fas_server/templates/yubikey-remove.py.j2
b/roles/fas_server/templates/yubikey-remove.py.j2
new file mode 100755
index 0000000..698947f
--- /dev/null
+++ b/roles/fas_server/templates/yubikey-remove.py.j2
@@ -0,0 +1,62 @@
+#!/usr/bin/python -tt
+
+import sys
+
+import psycopg2
+
+
+FAS_HOST = "db02"
+YKKSM_HOST = "db02"
+YKVAL_HOST = "db02"
+FAS_USER = "fas"
+FAS_PASS = "{{ fasDbPassword }}"
+YKKSM_USER = "ykksmimporter"
+YKKSM_PASS = "{{ ykksmimporterPassword }}"
+YKVAL_USER = "ykval_verifier"
+YKVAL_PASS = "{{ ykval_verifierPassword }}"
+
+fasdb = None
+yk_ksmdb = None
+yk_valdb = None
+
+def usage():
+ usage = '''
+ fas-yubiremove [USERNAME1 [USERNAME2 [...]]]
+
+ Remove existing yubikey for the listed USERNAMEs.
+ '''.strip()
+ print usage
+
+
+def init():
+ global fasdb, yk_ksmdb, yk_valdb
+ fasdb = psycopg2.connect("user='%s' password='%s'
host='%s' dbname='fas2'" % (FAS_USER, FAS_PASS, FAS_HOST))
+ yk_ksmdb = psycopg2.connect("user='%s' password='%s'
host='%s' dbname='ykksm'" % (YKKSM_USER, YKKSM_PASS, YKKSM_HOST))
+ yk_valdb = psycopg2.connect("user='%s' password='%s'
host='%s' dbname='ykval'" % (YKVAL_USER, YKVAL_PASS, YKVAL_HOST))
+
+
+def main():
+ init()
+ # Get username from commandline
+ usernames = sys.argv[1:]
+ # get the yubikey for the user from the fas configs db
+ for username in usernames:
+
+ fas = fasdb.cursor()
+ fas.execute("select value from configs, people where people.id =
configs.person_id and username=%s and application = 'yubikey' and attribute =
'prefix'", (username,))
+ prefix = fas.fetchall()[0]
+ # Remove the keys from the yubikey database
+ yk_ksm = yk_ksmdb.cursor()
+ yk_ksm.execute('delete from yubikeys where publicname=%s', (prefix[0],))
+ yk_val = yk_valdb.cursor()
+ yk_val.execute('delete from yubikeys where yk_publicname=%s',
(prefix[0],))
+
+ # Remove the key from fas
+ fas.execute("delete from configs where person_id = (select id from people
where username=%s) and application = 'yubikey'", (username,))
+
+ yk_valdb.commit()
+ yk_ksmdb.commit()
+ fasdb.commit()
+
+if __name__ == '__main__':
+ sys.exit(main())