Commit 44ca58a1 authored by Łukasz Nowak's avatar Łukasz Nowak

Add flexibility to Certificate Authority.

Certificate authority is capable of getting "asynchronous" certificate
creation requests during buildout run, which are later processed one by
one.

Thanks to this recipes are able to request any amount of certificates,
the apache part will wait until certificates are really created.
parent 23fbc38a
......@@ -6,7 +6,7 @@ from setuptools import setup, find_packages
# "."], stdout=subprocess.PIPE).communicate()[0]))
name = "slapos.recipe.erp5"
version = '1.1-dev-178'
version = '1.1-dev-179'
def read(name):
return open(name).read()
......
......@@ -33,6 +33,7 @@ import hashlib
import sys
import zc.buildout
import zc.recipe.egg
import ConfigParser
# Taken from Zope2 egg
def write_inituser(fn, user, password):
......@@ -61,6 +62,7 @@ class Recipe(BaseSlapRecipe):
'killpidfromfile')], self.ws, sys.executable, self.bin_directory)[0]
self.path_list.append(self.killpidfromfile)
ca_conf = self.installCertificateAuthority()
memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
port=11000)
kumo_conf = self.installKumo(self.getLocalIPv4Address())
......@@ -76,10 +78,10 @@ class Recipe(BaseSlapRecipe):
zodb_configuration_string=self.substituteTemplate(
self.getTemplateFilename('zope-zodb-snippet.conf.in'),
dict(zodb_root_path=zodb_root_path)), with_timerservice=True)
key, certificate = self.requestCertificate('Login Based Access')
apache_conf = dict(
apache_login=self.installLoginApache(ip=self.getGlobalIPv6Address(),
port=13000, backend=zope_access, key=ca_conf['login_key'],
certificate=ca_conf['login_certificate']))
port=13000, backend=zope_access, key=key, certificate=certificate))
self.installERP5Site(user, password, zope_access, mysql_conf,
conversion_server_conf, memcached_conf, kumo_conf, self.site_id)
self.installTestRunner(ca_conf, mysql_conf, conversion_server_conf,
......@@ -320,64 +322,62 @@ class Recipe(BaseSlapRecipe):
self.path_list.append(wrapper)
return cron_d
def requestCertificate(self, name):
hash = hashlib.sha512(name).hexdigest()
key = os.path.join(self.ca_private, hash + self.ca_key_ext)
certificate = os.path.join(self.ca_certs, hash + self.ca_crt_ext)
parser = ConfigParser.RawConfigParser()
parser.add_section('certificate')
parser.set('certificate', 'name', name)
parser.set('certificate', 'key_file', key)
parser.set('certificate', 'certificate_file', certificate)
parser.write(open(os.path.join(self.ca_request_dir, hash), 'w'))
return key, certificate
def installCertificateAuthority(self, ca_country_code='XX',
ca_email='xx@example.com', ca_state='State', ca_city='City',
ca_company='Company'):
config = dict(
ca_dir=os.path.join(self.data_root_directory, 'ca'))
login_key = os.path.join(config['ca_dir'], 'private', 'login.key')
login_certificate = os.path.join(config['ca_dir'], 'certs', 'login.crt')
key_auth_key = os.path.join(config['ca_dir'], 'private', 'keyauth.key')
key_auth_certificate = os.path.join(config['ca_dir'], 'certs',
'keyauth.crt')
config.update(
ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
ca_key=os.path.join(config['ca_dir'], 'private', 'cakey.pem'),
ca_crl=os.path.join(config['ca_dir'], 'crl'),
login_key=login_key,
login_certificate=login_certificate,
key_auth_key=key_auth_key,
key_auth_certificate=key_auth_certificate,
)
self._createDirectory(config['ca_dir'])
for d in ['certs', 'crl', 'newcerts', 'private']:
self._createDirectory(os.path.join(config['ca_dir'], d))
self.ca_dir = os.path.join(self.data_root_directory, 'ca')
self._createDirectory(self.ca_dir)
self.ca_request_dir = os.path.join(self.ca_dir, 'requests')
self._createDirectory(self.ca_request_dir)
config = dict(ca_dir=self.ca_dir, request_dir=self.ca_request_dir)
self.ca_private = os.path.join(self.ca_dir, 'private')
self.ca_certs = os.path.join(self.ca_dir, 'certs')
self.ca_crl = os.path.join(self.ca_dir, 'crl')
self.ca_newcerts = os.path.join(self.ca_dir, 'newcerts')
self.ca_key_ext = '.key'
self.ca_crt_ext = '.crt'
for d in [self.ca_private, self.ca_crl, self.ca_newcerts, self.ca_certs]:
self._createDirectory(d)
for f in ['crlnumber', 'serial']:
if not os.path.exists(os.path.join(config['ca_dir'], f)):
open(os.path.join(config['ca_dir'], f), 'w').write('01')
if not os.path.exists(os.path.join(config['ca_dir'], 'index.txt')):
open(os.path.join(config['ca_dir'], 'index.txt'), 'w').write('')
config['openssl_configuration'] = os.path.join(config['ca_dir'],
'openssl.cnf')
if not os.path.exists(os.path.join(self.ca_dir, f)):
open(os.path.join(self.ca_dir, f), 'w').write('01')
if not os.path.exists(os.path.join(self.ca_dir, 'index.txt')):
open(os.path.join(self.ca_dir, 'index.txt'), 'w').write('')
openssl_configuration = os.path.join(self.ca_dir, 'openssl.cnf')
config.update(
working_directory=config['ca_dir'],
working_directory=self.ca_dir,
country_code=ca_country_code,
state=ca_state,
city=ca_city,
company=ca_company,
email_address=ca_email,
)
self._writeFile(config['openssl_configuration'],
pkg_resources.resource_string(__name__,
'template/openssl.cnf.ca.in') % config)
self._writeFile(openssl_configuration, pkg_resources.resource_string(
__name__, 'template/openssl.cnf.ca.in') % config)
self.path_list.extend(zc.buildout.easy_install.scripts([
('certificate_authority',
__name__ + '.certificate_authority', 'runCertificateAuthority')],
self.ws, sys.executable, self.wrapper_directory, arguments=[dict(
openssl_configuration=config['openssl_configuration'],
openssl_configuration=openssl_configuration,
openssl_binary=self.options['openssl_binary'],
ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
ca_key=os.path.join(config['ca_dir'], 'private', 'cakey.pem'),
ca_crl=os.path.join(config['ca_dir'], 'crl'),
login_key=os.path.join(config['ca_dir'], 'private', 'login.key'),
login_certificate=login_certificate,
key_auth_key=key_auth_key,
key_auth_certificate=key_auth_certificate,
certificate=os.path.join(self.ca_dir, 'cacert.pem'),
key=os.path.join(self.ca_private, 'cakey.pem'),
crl=os.path.join(self.ca_crl),
request_dir=self.ca_request_dir
)]))
return dict(
login_key=login_key, login_certificate=login_certificate,
key_auth_key=key_auth_key, key_auth_certificate=key_auth_certificate,
ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
ca_crl=os.path.join(config['ca_dir'], 'crl'),
certificate_authority_path=config['ca_dir']
......
import os
import subprocess
import time
import ConfigParser
def popenCommunicate(command_list, input=None):
......@@ -12,16 +13,22 @@ def popenCommunicate(command_list, input=None):
if popen.returncode is None:
popen.kill()
if popen.returncode != 0:
raise ValueError('Issue during calling %r, result was:\n%s' % (command_list,
result))
raise ValueError('Issue during calling %r, result was:\n%s' % (
command_list, result))
return result
def checkCertificateAuthority(ca_conf):
file_list = [
ca_conf['ca_key'],
ca_conf['ca_certificate'],
]
class CertificateAuthority:
def __init__(self, key, certificate, openssl_binary,
openssl_configuration, request_dir):
self.key = key
self.certificate = certificate
self.openssl_binary = openssl_binary
self.openssl_configuration = openssl_configuration
self.request_dir = request_dir
def checkAuthority(self):
file_list = [ self.key, self.certificate ]
ca_ready = True
for f in file_list:
if not os.path.exists(f):
......@@ -34,11 +41,10 @@ def checkCertificateAuthority(ca_conf):
os.unlink(f)
try:
# no CA, let us create new one
popenCommunicate([ca_conf['openssl_binary'], 'req', '-nodes', '-config',
ca_conf['openssl_configuration'], '-new', '-x509', '-extensions',
'v3_ca', '-keyout', ca_conf['ca_key'], '-out',
ca_conf['ca_certificate'], '-days',
'10950'], 'Automatic Certificate Authority\n')
popenCommunicate([self.openssl_binary, 'req', '-nodes', '-config',
self.openssl_configuration, '-new', '-x509', '-extensions',
'v3_ca', '-keyout', self.key, '-out', self.certificate,
'-days', '10950'], 'Automatic Certificate Authority\n')
except:
try:
for f in file_list:
......@@ -49,8 +55,7 @@ def checkCertificateAuthority(ca_conf):
pass
raise
def checkCertificate(common_name, key, certificate, ca_conf):
def _checkCertificate(self, common_name, key, certificate):
file_list = [key, certificate]
ready = True
for f in file_list:
......@@ -58,19 +63,19 @@ def checkCertificate(common_name, key, certificate, ca_conf):
ready = False
break
if ready:
return
return False
for f in file_list:
if os.path.exists(f):
os.unlink(f)
csr = certificate + '.csr'
try:
popenCommunicate([ca_conf['openssl_binary'], 'req', '-config',
ca_conf['openssl_configuration'], '-nodes', '-new', '-keyout',
popenCommunicate([self.openssl_binary, 'req', '-config',
self.openssl_configuration, '-nodes', '-new', '-keyout',
key, '-out', csr, '-days', '3650'],
common_name + '\n')
try:
popenCommunicate([ca_conf['openssl_binary'], 'ca', '-batch', '-config',
ca_conf['openssl_configuration'], '-out', certificate,
popenCommunicate([self.openssl_binary, 'ca', '-batch', '-config',
self.openssl_configuration, '-out', certificate,
'-infiles', csr])
finally:
if os.path.exists(csr):
......@@ -84,22 +89,24 @@ def checkCertificate(common_name, key, certificate, ca_conf):
# do not raise during cleanup
pass
raise
else:
return True
def checkLoginCertificate(ca_conf):
checkCertificate('Login Based Access', ca_conf['login_key'],
ca_conf['login_certificate'], ca_conf)
def checkKeyAuthCertificate(ca_conf):
checkCertificate('Key Based Access', ca_conf['key_auth_key'],
ca_conf['key_auth_certificate'], ca_conf)
def checkRequestDir(self):
for request_file in os.listdir(self.request_dir):
parser = ConfigParser.RawConfigParser()
parser.readfp(open(os.path.join(self.request_dir, request_file), 'r'))
if self._checkCertificate(parser.get('certificate', 'name'),
parser.get('certificate', 'key_file'), parser.get('certificate',
'certificate_file')):
print 'Created certificate %r' % parser.get('certificate', 'name')
def runCertificateAuthority(args):
ca_conf = args[0]
ca = CertificateAuthority(ca_conf['key'], ca_conf['certificate'],
ca_conf['openssl_binary'], ca_conf['openssl_configuration'],
ca_conf['request_dir'])
while True:
checkCertificateAuthority(ca_conf)
checkLoginCertificate(ca_conf)
checkKeyAuthCertificate(ca_conf)
ca.checkAuthority()
ca.checkRequestDir()
time.sleep(60)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment