13
ORG_UNIT = 'Ubuntu Cloud'
14
CA_BUNDLE='/usr/local/share/ca-certificates/juju_ca_cert.crt'
18
default_ca = CA_default
23
database = $dir/index.txt
27
new_certs_dir = $dir/newcerts
28
certificate = $dir/cacert.pem
29
private_key = $dir/private/cacert.key
30
RANDFILE = $dir/private/.rand
38
distinguished_name = ca_distinguished_name
40
x509_extensions = ca_extensions
42
[ ca_distinguished_name ]
43
organizationName = %(org_name)s
44
organizationalUnitName = %(org_unit_name)s Certificate Authority
45
commonName = %(common_name)s
48
countryName = optional
49
stateOrProvinceName = optional
50
organizationName = match
51
organizationalUnitName = optional
55
basicConstraints = critical,CA:true
56
subjectKeyIdentifier = hash
57
authorityKeyIdentifier = keyid:always, issuer
58
keyUsage = cRLSign, keyCertSign
63
default_ca = CA_default
68
database = $dir/index.txt
72
new_certs_dir = $dir/newcerts
73
certificate = $dir/cacert.pem
74
private_key = $dir/private/cacert.key
75
RANDFILE = $dir/private/.rand
83
distinguished_name = req_distinguished_name
85
x509_extensions = req_extensions
87
[ req_distinguished_name ]
88
organizationName = %(org_name)s
89
organizationalUnitName = %(org_unit_name)s Server Farm
92
countryName = optional
93
stateOrProvinceName = optional
94
organizationName = match
95
organizationalUnitName = optional
99
basicConstraints = CA:false
100
subjectKeyIdentifier = hash
101
authorityKeyIdentifier = keyid:always, issuer
102
keyUsage = digitalSignature, keyEncipherment, keyAgreement
103
extendedKeyUsage = serverAuth, clientAuth
107
def init_ca(ca_dir, common_name, org_name=ORG_NAME, org_unit_name=ORG_UNIT):
108
print 'Ensuring certificate authority exists at %s.' % ca_dir
109
if not os.path.exists(ca_dir):
110
print 'Initializing new certificate authority at %s' % ca_dir
113
for i in ['certs', 'crl', 'newcerts', 'private']:
114
d = os.path.join(ca_dir, i)
115
if not os.path.exists(d):
116
print 'Creating %s.' % d
118
os.chmod(os.path.join(ca_dir, 'private'), 0710)
120
if not os.path.isfile(os.path.join(ca_dir, 'serial')):
121
with open(os.path.join(ca_dir, 'serial'), 'wb') as out:
124
if not os.path.isfile(os.path.join(ca_dir, 'index.txt')):
125
with open(os.path.join(ca_dir, 'index.txt'), 'wb') as out:
127
if not os.path.isfile(os.path.join(ca_dir, 'ca.cnf')):
128
print 'Creating new CA config in %s' % ca_dir
129
with open(os.path.join(ca_dir, 'ca.cnf'), 'wb') as out:
130
out.write(CA_CONFIG % locals())
133
def root_ca_crt_key(ca_dir):
135
crt = os.path.join(ca_dir, 'cacert.pem')
136
key = os.path.join(ca_dir, 'private', 'cacert.key')
138
if not os.path.isfile(f):
139
print 'Missing %s, will re-initialize cert+key.' % f
142
print 'Found %s.' % f
144
cmd = ['openssl', 'req', '-config', os.path.join(ca_dir, 'ca.cnf'), '-x509',
145
'-nodes', '-newkey', 'rsa', '-days', '21360', '-keyout', key,
146
'-out', crt, '-outform', 'PEM']
147
subprocess.check_call(cmd)
151
def intermediate_ca_csr_key(ca_dir):
152
print 'Creating new intermediate CSR.'
153
key = os.path.join(ca_dir, 'private', 'cacert.key')
154
csr = os.path.join(ca_dir, 'cacert.csr')
155
cmd = ['openssl', 'req', '-config', os.path.join(ca_dir, 'ca.cnf'), '-sha1',
156
'-newkey', 'rsa', '-nodes', '-keyout', key, '-out', csr, '-outform',
158
subprocess.check_call(cmd)
162
def sign_int_csr(ca_dir, csr, common_name):
163
print 'Signing certificate request %s.' % csr
164
crt = os.path.join(ca_dir, 'certs',
165
'%s.crt' % os.path.basename(csr).split('.')[0])
166
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
167
cmd = ['openssl', 'ca', '-batch', '-config', os.path.join(ca_dir, 'ca.cnf'),
168
'-extensions', 'ca_extensions', '-days', CA_EXPIRY, '-notext',
169
'-in', csr, '-out', crt, '-subj', subj, '-batch']
171
subprocess.check_call(cmd)
175
def init_root_ca(ca_dir, common_name):
176
init_ca(ca_dir, common_name)
177
return root_ca_crt_key(ca_dir)
180
def init_intermediate_ca(ca_dir, common_name, root_ca_dir,
181
org_name=ORG_NAME, org_unit_name=ORG_UNIT):
182
init_ca(ca_dir, common_name)
183
if not os.path.isfile(os.path.join(ca_dir, 'cacert.pem')):
184
csr, key = intermediate_ca_csr_key(ca_dir)
185
crt = sign_int_csr(root_ca_dir, csr, common_name)
186
shutil.copy(crt, os.path.join(ca_dir, 'cacert.pem'))
188
print 'Intermediate CA certificate already exists.'
190
if not os.path.isfile(os.path.join(ca_dir, 'signing.cnf')):
191
print 'Creating new signing config in %s' % ca_dir
192
with open(os.path.join(ca_dir, 'signing.cnf'), 'wb') as out:
193
out.write(SIGNING_CONFIG % locals())
196
def create_certificate(ca_dir, service):
197
common_name = service
198
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
199
csr = os.path.join(ca_dir, 'certs', '%s.csr' % service)
200
key = os.path.join(ca_dir, 'certs', '%s.key' % service)
201
cmd = ['openssl', 'req', '-sha1', '-newkey', 'rsa', '-nodes', '-keyout',
202
key, '-out', csr, '-subj', subj]
203
subprocess.check_call(cmd)
204
crt = sign_csr(ca_dir, csr, common_name)
205
print 'Signed new CSR, crt @ %s' % crt
208
def update_bundle(bundle_file, new_bundle):
210
if os.path.isfile(bundle_file):
211
current = open(bundle_file, 'r').read().strip()
212
if new_bundle == current:
213
print 'CA Bundle @ %s is up to date.' % bundle_file
216
print 'Updating CA bundle @ %s.' % bundle_file
218
with open(bundle_file, 'wb') as out:
219
out.write(new_bundle)
220
subprocess.check_call(['update-ca-certificates'])
222
def tar_directory(path):
224
parent=os.path.dirname(path)
225
directory=os.path.basename(path)
226
tmp = tempfile.TemporaryFile()
228
tarball = tarfile.TarFile(fileobj=tmp, mode='w')
229
tarball.add(directory)
237
class JujuCA(object):
238
def __init__(self, name, ca_dir, root_ca_dir, user, group):
239
root_crt, root_key = init_root_ca(root_ca_dir,
240
'%s Certificate Authority' % name)
241
init_intermediate_ca(ca_dir,
242
'%s Intermediate Certificate Authority' % name,
244
cmd = ['chown', '-R', '%s.%s' % (user, group), ca_dir]
245
subprocess.check_call(cmd)
246
cmd = ['chown', '-R', '%s.%s' % (user, group), root_ca_dir]
247
subprocess.check_call(cmd)
249
self.root_ca_dir = root_ca_dir
252
update_bundle(CA_BUNDLE, self.get_ca_bundle())
254
def _sign_csr(self, csr, service, common_name):
255
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
256
crt = os.path.join(self.ca_dir, 'certs', '%s.crt' % common_name)
257
cmd = ['openssl', 'ca', '-config',
258
os.path.join(self.ca_dir, 'signing.cnf'), '-extensions',
259
'req_extensions', '-days', '365', '-notext', '-in', csr,
260
'-out', crt, '-batch', '-subj', subj]
261
subprocess.check_call(cmd)
264
def _create_certificate(self, service, common_name):
265
subj = '/O=%s/OU=%s/CN=%s' % (ORG_NAME, ORG_UNIT, common_name)
266
csr = os.path.join(self.ca_dir, 'certs', '%s.csr' % service)
267
key = os.path.join(self.ca_dir, 'certs', '%s.key' % service)
268
cmd = ['openssl', 'req', '-sha1', '-newkey', 'rsa', '-nodes', '-keyout',
269
key, '-out', csr, '-subj', subj]
270
subprocess.check_call(cmd)
271
crt = self._sign_csr(csr, service, common_name)
272
cmd = ['chown', '-R', '%s.%s' % (self.user, self.group), self.ca_dir]
273
subprocess.check_call(cmd)
274
print 'Signed new CSR, crt @ %s' % crt
277
def get_cert_and_key(self, common_name):
278
print 'Getting certificate and key for %s.' % common_name
279
key = os.path.join(self.ca_dir, 'certs', '%s.key' % common_name)
280
crt = os.path.join(self.ca_dir, 'certs', '%s.crt' % common_name)
281
if os.path.isfile(crt):
282
print 'Found existing certificate for %s.' % common_name
283
crt = open(crt, 'r').read()
285
key = open(key, 'r').read()
287
print 'Could not load ssl private key for %s from %s' %\
291
crt, key = self._create_certificate(common_name, common_name)
292
return open(crt, 'r').read(), open(key, 'r').read()
294
def get_ca_bundle(self):
295
int_cert = open(os.path.join(self.ca_dir, 'cacert.pem')).read()
296
root_cert = open(os.path.join(self.root_ca_dir, 'cacert.pem')).read()
297
# NOTE: ordering of certs in bundle matters!
298
return int_cert + root_cert