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 ...@@ -6,7 +6,7 @@ from setuptools import setup, find_packages
# "."], stdout=subprocess.PIPE).communicate()[0])) # "."], stdout=subprocess.PIPE).communicate()[0]))
name = "slapos.recipe.erp5" name = "slapos.recipe.erp5"
version = '1.1-dev-178' version = '1.1-dev-179'
def read(name): def read(name):
return open(name).read() return open(name).read()
......
...@@ -33,6 +33,7 @@ import hashlib ...@@ -33,6 +33,7 @@ import hashlib
import sys import sys
import zc.buildout import zc.buildout
import zc.recipe.egg import zc.recipe.egg
import ConfigParser
# Taken from Zope2 egg # Taken from Zope2 egg
def write_inituser(fn, user, password): def write_inituser(fn, user, password):
...@@ -61,6 +62,7 @@ class Recipe(BaseSlapRecipe): ...@@ -61,6 +62,7 @@ class Recipe(BaseSlapRecipe):
'killpidfromfile')], self.ws, sys.executable, self.bin_directory)[0] 'killpidfromfile')], self.ws, sys.executable, self.bin_directory)[0]
self.path_list.append(self.killpidfromfile) self.path_list.append(self.killpidfromfile)
ca_conf = self.installCertificateAuthority() ca_conf = self.installCertificateAuthority()
memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(), memcached_conf = self.installMemcached(ip=self.getLocalIPv4Address(),
port=11000) port=11000)
kumo_conf = self.installKumo(self.getLocalIPv4Address()) kumo_conf = self.installKumo(self.getLocalIPv4Address())
...@@ -76,10 +78,10 @@ class Recipe(BaseSlapRecipe): ...@@ -76,10 +78,10 @@ class Recipe(BaseSlapRecipe):
zodb_configuration_string=self.substituteTemplate( zodb_configuration_string=self.substituteTemplate(
self.getTemplateFilename('zope-zodb-snippet.conf.in'), self.getTemplateFilename('zope-zodb-snippet.conf.in'),
dict(zodb_root_path=zodb_root_path)), with_timerservice=True) dict(zodb_root_path=zodb_root_path)), with_timerservice=True)
key, certificate = self.requestCertificate('Login Based Access')
apache_conf = dict( apache_conf = dict(
apache_login=self.installLoginApache(ip=self.getGlobalIPv6Address(), apache_login=self.installLoginApache(ip=self.getGlobalIPv6Address(),
port=13000, backend=zope_access, key=ca_conf['login_key'], port=13000, backend=zope_access, key=key, certificate=certificate))
certificate=ca_conf['login_certificate']))
self.installERP5Site(user, password, zope_access, mysql_conf, self.installERP5Site(user, password, zope_access, mysql_conf,
conversion_server_conf, memcached_conf, kumo_conf, self.site_id) conversion_server_conf, memcached_conf, kumo_conf, self.site_id)
self.installTestRunner(ca_conf, mysql_conf, conversion_server_conf, self.installTestRunner(ca_conf, mysql_conf, conversion_server_conf,
...@@ -320,64 +322,62 @@ class Recipe(BaseSlapRecipe): ...@@ -320,64 +322,62 @@ class Recipe(BaseSlapRecipe):
self.path_list.append(wrapper) self.path_list.append(wrapper)
return cron_d 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', def installCertificateAuthority(self, ca_country_code='XX',
ca_email='xx@example.com', ca_state='State', ca_city='City', ca_email='xx@example.com', ca_state='State', ca_city='City',
ca_company='Company'): ca_company='Company'):
config = dict( self.ca_dir = os.path.join(self.data_root_directory, 'ca')
ca_dir=os.path.join(self.data_root_directory, 'ca')) self._createDirectory(self.ca_dir)
login_key = os.path.join(config['ca_dir'], 'private', 'login.key') self.ca_request_dir = os.path.join(self.ca_dir, 'requests')
login_certificate = os.path.join(config['ca_dir'], 'certs', 'login.crt') self._createDirectory(self.ca_request_dir)
key_auth_key = os.path.join(config['ca_dir'], 'private', 'keyauth.key') config = dict(ca_dir=self.ca_dir, request_dir=self.ca_request_dir)
key_auth_certificate = os.path.join(config['ca_dir'], 'certs', self.ca_private = os.path.join(self.ca_dir, 'private')
'keyauth.crt') self.ca_certs = os.path.join(self.ca_dir, 'certs')
self.ca_crl = os.path.join(self.ca_dir, 'crl')
config.update( self.ca_newcerts = os.path.join(self.ca_dir, 'newcerts')
ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'), self.ca_key_ext = '.key'
ca_key=os.path.join(config['ca_dir'], 'private', 'cakey.pem'), self.ca_crt_ext = '.crt'
ca_crl=os.path.join(config['ca_dir'], 'crl'), for d in [self.ca_private, self.ca_crl, self.ca_newcerts, self.ca_certs]:
login_key=login_key, self._createDirectory(d)
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))
for f in ['crlnumber', 'serial']: for f in ['crlnumber', 'serial']:
if not os.path.exists(os.path.join(config['ca_dir'], f)): if not os.path.exists(os.path.join(self.ca_dir, f)):
open(os.path.join(config['ca_dir'], f), 'w').write('01') open(os.path.join(self.ca_dir, f), 'w').write('01')
if not os.path.exists(os.path.join(config['ca_dir'], 'index.txt')): if not os.path.exists(os.path.join(self.ca_dir, 'index.txt')):
open(os.path.join(config['ca_dir'], 'index.txt'), 'w').write('') open(os.path.join(self.ca_dir, 'index.txt'), 'w').write('')
config['openssl_configuration'] = os.path.join(config['ca_dir'], openssl_configuration = os.path.join(self.ca_dir, 'openssl.cnf')
'openssl.cnf')
config.update( config.update(
working_directory=config['ca_dir'], working_directory=self.ca_dir,
country_code=ca_country_code, country_code=ca_country_code,
state=ca_state, state=ca_state,
city=ca_city, city=ca_city,
company=ca_company, company=ca_company,
email_address=ca_email, email_address=ca_email,
) )
self._writeFile(config['openssl_configuration'], self._writeFile(openssl_configuration, pkg_resources.resource_string(
pkg_resources.resource_string(__name__, __name__, 'template/openssl.cnf.ca.in') % config)
'template/openssl.cnf.ca.in') % config)
self.path_list.extend(zc.buildout.easy_install.scripts([ self.path_list.extend(zc.buildout.easy_install.scripts([
('certificate_authority', ('certificate_authority',
__name__ + '.certificate_authority', 'runCertificateAuthority')], __name__ + '.certificate_authority', 'runCertificateAuthority')],
self.ws, sys.executable, self.wrapper_directory, arguments=[dict( self.ws, sys.executable, self.wrapper_directory, arguments=[dict(
openssl_configuration=config['openssl_configuration'], openssl_configuration=openssl_configuration,
openssl_binary=self.options['openssl_binary'], openssl_binary=self.options['openssl_binary'],
ca_certificate=os.path.join(config['ca_dir'], 'cacert.pem'), certificate=os.path.join(self.ca_dir, 'cacert.pem'),
ca_key=os.path.join(config['ca_dir'], 'private', 'cakey.pem'), key=os.path.join(self.ca_private, 'cakey.pem'),
ca_crl=os.path.join(config['ca_dir'], 'crl'), crl=os.path.join(self.ca_crl),
login_key=os.path.join(config['ca_dir'], 'private', 'login.key'), request_dir=self.ca_request_dir
login_certificate=login_certificate,
key_auth_key=key_auth_key,
key_auth_certificate=key_auth_certificate,
)])) )]))
return dict( 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_certificate=os.path.join(config['ca_dir'], 'cacert.pem'),
ca_crl=os.path.join(config['ca_dir'], 'crl'), ca_crl=os.path.join(config['ca_dir'], 'crl'),
certificate_authority_path=config['ca_dir'] certificate_authority_path=config['ca_dir']
......
import os import os
import subprocess import subprocess
import time import time
import ConfigParser
def popenCommunicate(command_list, input=None): def popenCommunicate(command_list, input=None):
...@@ -12,94 +13,100 @@ def popenCommunicate(command_list, input=None): ...@@ -12,94 +13,100 @@ def popenCommunicate(command_list, input=None):
if popen.returncode is None: if popen.returncode is None:
popen.kill() popen.kill()
if popen.returncode != 0: if popen.returncode != 0:
raise ValueError('Issue during calling %r, result was:\n%s' % (command_list, raise ValueError('Issue during calling %r, result was:\n%s' % (
result)) command_list, result))
return result return result
def checkCertificateAuthority(ca_conf): class CertificateAuthority:
file_list = [ def __init__(self, key, certificate, openssl_binary,
ca_conf['ca_key'], openssl_configuration, request_dir):
ca_conf['ca_certificate'], self.key = key
] self.certificate = certificate
ca_ready = True self.openssl_binary = openssl_binary
for f in file_list: self.openssl_configuration = openssl_configuration
if not os.path.exists(f): self.request_dir = request_dir
ca_ready = False
break def checkAuthority(self):
if ca_ready: file_list = [ self.key, self.certificate ]
return ca_ready = True
for f in file_list: for f in file_list:
if os.path.exists(f): if not os.path.exists(f):
os.unlink(f) ca_ready = False
try: break
# no CA, let us create new one if ca_ready:
popenCommunicate([ca_conf['openssl_binary'], 'req', '-nodes', '-config', return
ca_conf['openssl_configuration'], '-new', '-x509', '-extensions', for f in file_list:
'v3_ca', '-keyout', ca_conf['ca_key'], '-out', if os.path.exists(f):
ca_conf['ca_certificate'], '-days', os.unlink(f)
'10950'], 'Automatic Certificate Authority\n')
except:
try: try:
for f in file_list: # no CA, let us create new one
if os.path.exists(f): popenCommunicate([self.openssl_binary, 'req', '-nodes', '-config',
os.unlink(f) self.openssl_configuration, '-new', '-x509', '-extensions',
'v3_ca', '-keyout', self.key, '-out', self.certificate,
'-days', '10950'], 'Automatic Certificate Authority\n')
except: except:
# do not raise during cleanup try:
pass for f in file_list:
raise if os.path.exists(f):
os.unlink(f)
except:
# do not raise during cleanup
pass
raise
def checkCertificate(common_name, key, certificate, ca_conf): def _checkCertificate(self, common_name, key, certificate):
file_list = [key, certificate] file_list = [key, certificate]
ready = True ready = True
for f in file_list: for f in file_list:
if not os.path.exists(f): if not os.path.exists(f):
ready = False ready = False
break break
if ready: if ready:
return return False
for f in file_list: for f in file_list:
if os.path.exists(f): if os.path.exists(f):
os.unlink(f) os.unlink(f)
csr = certificate + '.csr' csr = certificate + '.csr'
try:
popenCommunicate([ca_conf['openssl_binary'], 'req', '-config',
ca_conf['openssl_configuration'], '-nodes', '-new', '-keyout',
key, '-out', csr, '-days', '3650'],
common_name + '\n')
try: try:
popenCommunicate([ca_conf['openssl_binary'], 'ca', '-batch', '-config', popenCommunicate([self.openssl_binary, 'req', '-config',
ca_conf['openssl_configuration'], '-out', certificate, self.openssl_configuration, '-nodes', '-new', '-keyout',
'-infiles', csr]) key, '-out', csr, '-days', '3650'],
finally: common_name + '\n')
if os.path.exists(csr): try:
os.unlink(csr) popenCommunicate([self.openssl_binary, 'ca', '-batch', '-config',
except: self.openssl_configuration, '-out', certificate,
try: '-infiles', csr])
for f in file_list: finally:
if os.path.exists(f): if os.path.exists(csr):
os.unlink(f) os.unlink(csr)
except: except:
# do not raise during cleanup try:
pass for f in file_list:
raise if os.path.exists(f):
os.unlink(f)
except:
def checkLoginCertificate(ca_conf): # do not raise during cleanup
checkCertificate('Login Based Access', ca_conf['login_key'], pass
ca_conf['login_certificate'], ca_conf) raise
else:
return True
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): def runCertificateAuthority(args):
ca_conf = args[0] 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: while True:
checkCertificateAuthority(ca_conf) ca.checkAuthority()
checkLoginCertificate(ca_conf) ca.checkRequestDir()
checkKeyAuthCertificate(ca_conf)
time.sleep(60) 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