~gandelman-a/charms/precise/keystone/ssh_user_fix

« back to all changes in this revision

Viewing changes to hooks/keystone_ssl.py

  • Committer: James Page
  • Date: 2013-03-11 09:10:47 UTC
  • mfrom: (50.1.10 keystone)
  • Revision ID: james.page@canonical.com-20130311091047-3t8xmo74fucxomie
SSH based hook syncing

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
import base64
 
4
import os
 
5
import shutil
 
6
import subprocess
 
7
import tarfile
 
8
import tempfile
 
9
from utils import *
 
10
 
 
11
CA_EXPIRY = '365'
 
12
ORG_NAME = 'Ubuntu'
 
13
ORG_UNIT = 'Ubuntu Cloud'
 
14
CA_BUNDLE='/usr/local/share/ca-certificates/juju_ca_cert.crt'
 
15
 
 
16
CA_CONFIG = """
 
17
[ ca ]
 
18
default_ca = CA_default
 
19
 
 
20
[ CA_default ]
 
21
dir                     = %(ca_dir)s
 
22
policy                  = policy_match
 
23
database                = $dir/index.txt
 
24
serial                  = $dir/serial
 
25
certs                   = $dir/certs
 
26
crl_dir                 = $dir/crl
 
27
new_certs_dir           = $dir/newcerts
 
28
certificate             = $dir/cacert.pem
 
29
private_key             = $dir/private/cacert.key
 
30
RANDFILE                = $dir/private/.rand
 
31
default_md              = default
 
32
 
 
33
[ req ]
 
34
default_bits            = 1024
 
35
default_md              = sha1
 
36
 
 
37
prompt                  = no
 
38
distinguished_name      = ca_distinguished_name
 
39
 
 
40
x509_extensions         = ca_extensions
 
41
 
 
42
[ ca_distinguished_name ]
 
43
organizationName        = %(org_name)s
 
44
organizationalUnitName  = %(org_unit_name)s Certificate Authority
 
45
commonName              = %(common_name)s
 
46
 
 
47
[ policy_match ]
 
48
countryName             = optional
 
49
stateOrProvinceName     = optional
 
50
organizationName        = match
 
51
organizationalUnitName  = optional
 
52
commonName              = supplied
 
53
 
 
54
[ ca_extensions ]
 
55
basicConstraints        = critical,CA:true
 
56
subjectKeyIdentifier    = hash
 
57
authorityKeyIdentifier  = keyid:always, issuer
 
58
keyUsage                = cRLSign, keyCertSign
 
59
"""
 
60
 
 
61
SIGNING_CONFIG="""
 
62
[ ca ]
 
63
default_ca = CA_default
 
64
 
 
65
[ CA_default ]
 
66
dir                     = %(ca_dir)s
 
67
policy                  = policy_match
 
68
database                = $dir/index.txt
 
69
serial                  = $dir/serial
 
70
certs                   = $dir/certs
 
71
crl_dir                 = $dir/crl
 
72
new_certs_dir           = $dir/newcerts
 
73
certificate             = $dir/cacert.pem
 
74
private_key             = $dir/private/cacert.key
 
75
RANDFILE                = $dir/private/.rand
 
76
default_md              = default
 
77
 
 
78
[ req ]
 
79
default_bits            = 1024
 
80
default_md              = sha1
 
81
 
 
82
prompt                  = no
 
83
distinguished_name      = req_distinguished_name
 
84
 
 
85
x509_extensions         = req_extensions
 
86
 
 
87
[ req_distinguished_name ]
 
88
organizationName        = %(org_name)s
 
89
organizationalUnitName  = %(org_unit_name)s Server Farm
 
90
 
 
91
[ policy_match ]
 
92
countryName             = optional
 
93
stateOrProvinceName     = optional
 
94
organizationName        = match
 
95
organizationalUnitName  = optional
 
96
commonName              = supplied
 
97
 
 
98
[ req_extensions ]
 
99
basicConstraints        = CA:false
 
100
subjectKeyIdentifier    = hash
 
101
authorityKeyIdentifier  = keyid:always, issuer
 
102
keyUsage                = digitalSignature, keyEncipherment, keyAgreement
 
103
extendedKeyUsage        = serverAuth, clientAuth
 
104
"""
 
105
 
 
106
 
 
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
 
111
        os.mkdir(ca_dir)
 
112
 
 
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
 
117
            os.mkdir(d)
 
118
    os.chmod(os.path.join(ca_dir, 'private'), 0710)
 
119
 
 
120
    if not os.path.isfile(os.path.join(ca_dir, 'serial')):
 
121
        with open(os.path.join(ca_dir, 'serial'), 'wb') as out:
 
122
            out.write('01\n')
 
123
 
 
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:
 
126
            out.write('')
 
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())
 
131
 
 
132
 
 
133
def root_ca_crt_key(ca_dir):
 
134
    init = False
 
135
    crt = os.path.join(ca_dir, 'cacert.pem')
 
136
    key = os.path.join(ca_dir, 'private', 'cacert.key')
 
137
    for f in [crt, key]:
 
138
        if not os.path.isfile(f):
 
139
            print 'Missing %s, will re-initialize cert+key.' % f
 
140
            init = True
 
141
        else:
 
142
            print 'Found %s.' % f
 
143
    if init:
 
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)
 
148
    return crt, key
 
149
 
 
150
 
 
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',
 
157
           'PEM']
 
158
    subprocess.check_call(cmd)
 
159
    return csr, key
 
160
 
 
161
 
 
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']
 
170
    print ' '.join(cmd)
 
171
    subprocess.check_call(cmd)
 
172
    return crt
 
173
 
 
174
 
 
175
def init_root_ca(ca_dir, common_name):
 
176
    init_ca(ca_dir, common_name)
 
177
    return root_ca_crt_key(ca_dir)
 
178
 
 
179
 
 
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'))
 
187
    else:
 
188
        print 'Intermediate CA certificate already exists.'
 
189
 
 
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())
 
194
 
 
195
 
 
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
 
206
    return
 
207
 
 
208
def update_bundle(bundle_file, new_bundle):
 
209
    return
 
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
 
214
            return
 
215
        else:
 
216
            print 'Updating CA bundle @ %s.' % bundle_file
 
217
 
 
218
    with open(bundle_file, 'wb') as out:
 
219
        out.write(new_bundle)
 
220
    subprocess.check_call(['update-ca-certificates'])
 
221
 
 
222
def tar_directory(path):
 
223
    cwd = os.getcwd()
 
224
    parent=os.path.dirname(path)
 
225
    directory=os.path.basename(path)
 
226
    tmp = tempfile.TemporaryFile()
 
227
    os.chdir(parent)
 
228
    tarball = tarfile.TarFile(fileobj=tmp, mode='w')
 
229
    tarball.add(directory)
 
230
    tarball.close()
 
231
    tmp.seek(0)
 
232
    out = tmp.read()
 
233
    tmp.close()
 
234
    os.chdir(cwd)
 
235
    return out
 
236
 
 
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,
 
243
                             root_ca_dir)
 
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)
 
248
        self.ca_dir = ca_dir
 
249
        self.root_ca_dir = root_ca_dir
 
250
        self.user = user
 
251
        self.group = group
 
252
        update_bundle(CA_BUNDLE, self.get_ca_bundle())
 
253
 
 
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)
 
262
        return crt
 
263
 
 
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
 
275
        return crt, key
 
276
 
 
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()
 
284
            try:
 
285
                key = open(key, 'r').read()
 
286
            except:
 
287
                print 'Could not load ssl private key for %s from %s' %\
 
288
                     (common_name, key)
 
289
                exit(1)
 
290
            return crt, key
 
291
        crt, key = self._create_certificate(common_name, common_name)
 
292
        return open(crt, 'r').read(), open(key, 'r').read()
 
293
 
 
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