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,16 +13,22 @@ def popenCommunicate(command_list, input=None): ...@@ -12,16 +13,22 @@ 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
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 ca_ready = True
for f in file_list: for f in file_list:
if not os.path.exists(f): if not os.path.exists(f):
...@@ -34,11 +41,10 @@ def checkCertificateAuthority(ca_conf): ...@@ -34,11 +41,10 @@ def checkCertificateAuthority(ca_conf):
os.unlink(f) os.unlink(f)
try: try:
# no CA, let us create new one # no CA, let us create new one
popenCommunicate([ca_conf['openssl_binary'], 'req', '-nodes', '-config', popenCommunicate([self.openssl_binary, 'req', '-nodes', '-config',
ca_conf['openssl_configuration'], '-new', '-x509', '-extensions', self.openssl_configuration, '-new', '-x509', '-extensions',
'v3_ca', '-keyout', ca_conf['ca_key'], '-out', 'v3_ca', '-keyout', self.key, '-out', self.certificate,
ca_conf['ca_certificate'], '-days', '-days', '10950'], 'Automatic Certificate Authority\n')
'10950'], 'Automatic Certificate Authority\n')
except: except:
try: try:
for f in file_list: for f in file_list:
...@@ -49,8 +55,7 @@ def checkCertificateAuthority(ca_conf): ...@@ -49,8 +55,7 @@ def checkCertificateAuthority(ca_conf):
pass pass
raise raise
def _checkCertificate(self, common_name, key, certificate):
def checkCertificate(common_name, key, certificate, ca_conf):
file_list = [key, certificate] file_list = [key, certificate]
ready = True ready = True
for f in file_list: for f in file_list:
...@@ -58,19 +63,19 @@ def checkCertificate(common_name, key, certificate, ca_conf): ...@@ -58,19 +63,19 @@ def checkCertificate(common_name, key, certificate, ca_conf):
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: try:
popenCommunicate([ca_conf['openssl_binary'], 'req', '-config', popenCommunicate([self.openssl_binary, 'req', '-config',
ca_conf['openssl_configuration'], '-nodes', '-new', '-keyout', self.openssl_configuration, '-nodes', '-new', '-keyout',
key, '-out', csr, '-days', '3650'], key, '-out', csr, '-days', '3650'],
common_name + '\n') common_name + '\n')
try: try:
popenCommunicate([ca_conf['openssl_binary'], 'ca', '-batch', '-config', popenCommunicate([self.openssl_binary, 'ca', '-batch', '-config',
ca_conf['openssl_configuration'], '-out', certificate, self.openssl_configuration, '-out', certificate,
'-infiles', csr]) '-infiles', csr])
finally: finally:
if os.path.exists(csr): if os.path.exists(csr):
...@@ -84,22 +89,24 @@ def checkCertificate(common_name, key, certificate, ca_conf): ...@@ -84,22 +89,24 @@ def checkCertificate(common_name, key, certificate, ca_conf):
# do not raise during cleanup # do not raise during cleanup
pass pass
raise raise
else:
return True
def checkRequestDir(self):
def checkLoginCertificate(ca_conf): for request_file in os.listdir(self.request_dir):
checkCertificate('Login Based Access', ca_conf['login_key'], parser = ConfigParser.RawConfigParser()
ca_conf['login_certificate'], ca_conf) 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',
def checkKeyAuthCertificate(ca_conf): 'certificate_file')):
checkCertificate('Key Based Access', ca_conf['key_auth_key'], print 'Created certificate %r' % parser.get('certificate', 'name')
ca_conf['key_auth_certificate'], ca_conf)
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