~soren/nova/ec2-soap-api

« back to all changes in this revision

Viewing changes to nova/crypto.py

  • Committer: Soren Hansen
  • Date: 2011-01-04 13:29:28 UTC
  • mfrom: (426.1.89 nova)
  • Revision ID: soren@linux2go.dk-20110104132928-49ersxj29drxqlms
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Wrappers around standard crypto data elements.
20
20
 
21
21
Includes root and intermediate CAs, SSH key_pairs and x509 certificates.
22
 
 
23
22
"""
24
23
 
25
24
import base64
 
25
import gettext
26
26
import hashlib
27
27
import logging
28
28
import os
34
34
 
35
35
import M2Crypto
36
36
 
37
 
from nova import exception
 
37
gettext.install('nova', unicode=1)
 
38
 
 
39
from nova import context
 
40
from nova import db
38
41
from nova import flags
39
42
 
40
43
 
41
44
FLAGS = flags.FLAGS
42
 
flags.DEFINE_string('ca_file', 'cacert.pem', 'Filename of root CA')
 
45
flags.DEFINE_string('ca_file', 'cacert.pem', _('Filename of root CA'))
 
46
flags.DEFINE_string('key_file',
 
47
                    os.path.join('private', 'cakey.pem'),
 
48
                    _('Filename of private key'))
 
49
flags.DEFINE_string('crl_file', 'crl.pem',
 
50
                    _('Filename of root Certificate Revokation List'))
43
51
flags.DEFINE_string('keys_path', '$state_path/keys',
44
 
                    'Where we keep our keys')
 
52
                    _('Where we keep our keys'))
45
53
flags.DEFINE_string('ca_path', '$state_path/CA',
46
 
                    'Where we keep our root CA')
47
 
flags.DEFINE_boolean('use_intermediate_ca', False,
48
 
                     'Should we use intermediate CAs for each project?')
49
 
 
50
 
 
51
 
def ca_path(project_id):
52
 
    if project_id:
53
 
        return "%s/INTER/%s/cacert.pem" % (FLAGS.ca_path, project_id)
54
 
    return "%s/cacert.pem" % (FLAGS.ca_path)
 
54
                    _('Where we keep our root CA'))
 
55
flags.DEFINE_boolean('use_project_ca', False,
 
56
                     _('Should we use a CA for each project?'))
 
57
flags.DEFINE_string('user_cert_subject',
 
58
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
59
                    'OU=NovaDev/CN=%s-%s-%s',
 
60
                    _('Subject for certificate for users, '
 
61
                    '%s for project, user, timestamp'))
 
62
flags.DEFINE_string('project_cert_subject',
 
63
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
64
                    'OU=NovaDev/CN=project-ca-%s-%s',
 
65
                    _('Subject for certificate for projects, '
 
66
                    '%s for project, timestamp'))
 
67
flags.DEFINE_string('vpn_cert_subject',
 
68
                    '/C=US/ST=California/L=MountainView/O=AnsoLabs/'
 
69
                    'OU=NovaDev/CN=project-vpn-%s-%s',
 
70
                    _('Subject for certificate for vpns, '
 
71
                    '%s for project, timestamp'))
 
72
 
 
73
 
 
74
def ca_folder(project_id=None):
 
75
    if FLAGS.use_project_ca and project_id:
 
76
        return os.path.join(FLAGS.ca_path, 'projects', project_id)
 
77
    return FLAGS.ca_path
 
78
 
 
79
 
 
80
def ca_path(project_id=None):
 
81
    return os.path.join(ca_folder(project_id), FLAGS.ca_file)
 
82
 
 
83
 
 
84
def key_path(project_id=None):
 
85
    return os.path.join(ca_folder(project_id), FLAGS.key_file)
55
86
 
56
87
 
57
88
def fetch_ca(project_id=None, chain=True):
58
 
    if not FLAGS.use_intermediate_ca:
 
89
    if not FLAGS.use_project_ca:
59
90
        project_id = None
60
91
    buffer = ""
61
92
    if project_id:
92
123
 
93
124
 
94
125
def ssl_pub_to_ssh_pub(ssl_public_key, name='root', suffix='nova'):
95
 
    pub_key_buffer = M2Crypto.BIO.MemoryBuffer(ssl_public_key)
96
 
    rsa_key = M2Crypto.RSA.load_pub_key_bio(pub_key_buffer)
 
126
    buf = M2Crypto.BIO.MemoryBuffer(ssl_public_key)
 
127
    rsa_key = M2Crypto.RSA.load_pub_key_bio(buf)
97
128
    e, n = rsa_key.pub()
98
129
 
99
130
    key_type = 'ssh-rsa'
106
137
    return '%s %s %s@%s\n' % (key_type, b64_blob, name, suffix)
107
138
 
108
139
 
109
 
def generate_x509_cert(subject, bits=1024):
 
140
def revoke_cert(project_id, file_name):
 
141
    """Revoke a cert by file name"""
 
142
    start = os.getcwd()
 
143
    os.chdir(ca_folder(project_id))
 
144
    # NOTE(vish): potential race condition here
 
145
    utils.execute("openssl ca -config ./openssl.cnf -revoke '%s'" % file_name)
 
146
    utils.execute("openssl ca -gencrl -config ./openssl.cnf -out '%s'" %
 
147
                  FLAGS.crl_file)
 
148
    os.chdir(start)
 
149
 
 
150
 
 
151
def revoke_certs_by_user(user_id):
 
152
    """Revoke all user certs"""
 
153
    admin = context.get_admin_context()
 
154
    for cert in db.certificate_get_all_by_user(admin, user_id):
 
155
        revoke_cert(cert['project_id'], cert['file_name'])
 
156
 
 
157
 
 
158
def revoke_certs_by_project(project_id):
 
159
    """Revoke all project certs"""
 
160
    # NOTE(vish): This is somewhat useless because we can just shut down
 
161
    #             the vpn.
 
162
    admin = context.get_admin_context()
 
163
    for cert in db.certificate_get_all_by_project(admin, project_id):
 
164
        revoke_cert(cert['project_id'], cert['file_name'])
 
165
 
 
166
 
 
167
def revoke_certs_by_user_and_project(user_id, project_id):
 
168
    """Revoke certs for user in project"""
 
169
    admin = context.get_admin_context()
 
170
    for cert in db.certificate_get_all_by_user(admin, user_id, project_id):
 
171
        revoke_cert(cert['project_id'], cert['file_name'])
 
172
 
 
173
 
 
174
def _project_cert_subject(project_id):
 
175
    """Helper to generate user cert subject"""
 
176
    return FLAGS.project_cert_subject % (project_id, utils.isotime())
 
177
 
 
178
 
 
179
def _vpn_cert_subject(project_id):
 
180
    """Helper to generate user cert subject"""
 
181
    return FLAGS.vpn_cert_subject % (project_id, utils.isotime())
 
182
 
 
183
 
 
184
def _user_cert_subject(user_id, project_id):
 
185
    """Helper to generate user cert subject"""
 
186
    return FLAGS.user_cert_subject % (project_id, user_id, utils.isotime())
 
187
 
 
188
 
 
189
def generate_x509_cert(user_id, project_id, bits=1024):
 
190
    """Generate and sign a cert for user in project"""
 
191
    subject = _user_cert_subject(user_id, project_id)
110
192
    tmpdir = tempfile.mkdtemp()
111
193
    keyfile = os.path.abspath(os.path.join(tmpdir, 'temp.key'))
112
194
    csrfile = os.path.join(tmpdir, 'temp.csr')
113
 
    logging.debug("openssl genrsa -out %s %s" % (keyfile, bits))
114
 
    utils.runthis("Generating private key: %s",
115
 
                  "openssl genrsa -out %s %s" % (keyfile, bits))
116
 
    utils.runthis("Generating CSR: %s",
117
 
                  "openssl req -new -key %s -out %s -batch -subj %s" %
 
195
    utils.execute("openssl genrsa -out %s %s" % (keyfile, bits))
 
196
    utils.execute("openssl req -new -key %s -out %s -batch -subj %s" %
118
197
                  (keyfile, csrfile, subject))
119
198
    private_key = open(keyfile).read()
120
199
    csr = open(csrfile).read()
121
200
    shutil.rmtree(tmpdir)
122
 
    return (private_key, csr)
123
 
 
124
 
 
125
 
def sign_csr(csr_text, intermediate=None):
126
 
    if not FLAGS.use_intermediate_ca:
127
 
        intermediate = None
128
 
    if not intermediate:
129
 
        return _sign_csr(csr_text, FLAGS.ca_path)
130
 
    user_ca = "%s/INTER/%s" % (FLAGS.ca_path, intermediate)
131
 
    if not os.path.exists(user_ca):
 
201
    (serial, signed_csr) = sign_csr(csr, project_id)
 
202
    fname = os.path.join(ca_folder(project_id), "newcerts/%s.pem" % serial)
 
203
    cert = {'user_id': user_id,
 
204
            'project_id': project_id,
 
205
            'file_name': fname}
 
206
    db.certificate_create(context.get_admin_context(), cert)
 
207
    return (private_key, signed_csr)
 
208
 
 
209
 
 
210
def _ensure_project_folder(project_id):
 
211
    if not os.path.exists(ca_path(project_id)):
132
212
        start = os.getcwd()
133
 
        os.chdir(FLAGS.ca_path)
134
 
        utils.runthis("Generating intermediate CA: %s",
135
 
                      "sh geninter.sh %s" % (intermediate))
 
213
        os.chdir(ca_folder())
 
214
        utils.execute("sh geninter.sh %s %s" %
 
215
                      (project_id, _project_cert_subject(project_id)))
136
216
        os.chdir(start)
137
 
    return _sign_csr(csr_text, user_ca)
 
217
 
 
218
 
 
219
def generate_vpn_files(project_id):
 
220
    project_folder = ca_folder(project_id)
 
221
    csr_fn = os.path.join(project_folder, "server.csr")
 
222
    crt_fn = os.path.join(project_folder, "server.crt")
 
223
 
 
224
    if os.path.exists(crt_fn):
 
225
        return
 
226
    _ensure_project_folder(project_id)
 
227
    start = os.getcwd()
 
228
    os.chdir(ca_folder())
 
229
    # TODO(vish): the shell scripts could all be done in python
 
230
    utils.execute("sh genvpn.sh %s %s" %
 
231
                  (project_id, _vpn_cert_subject(project_id)))
 
232
    with open(csr_fn, "r") as csrfile:
 
233
        csr_text = csrfile.read()
 
234
    (serial, signed_csr) = sign_csr(csr_text, project_id)
 
235
    with open(crt_fn, "w") as crtfile:
 
236
        crtfile.write(signed_csr)
 
237
    os.chdir(start)
 
238
 
 
239
 
 
240
def sign_csr(csr_text, project_id=None):
 
241
    if not FLAGS.use_project_ca:
 
242
        project_id = None
 
243
    if not project_id:
 
244
        return _sign_csr(csr_text, ca_folder())
 
245
    _ensure_project_folder(project_id)
 
246
    project_folder = ca_folder(project_id)
 
247
    return _sign_csr(csr_text, ca_folder(project_id))
138
248
 
139
249
 
140
250
def _sign_csr(csr_text, ca_folder):
141
251
    tmpfolder = tempfile.mkdtemp()
142
 
    csrfile = open("%s/inbound.csr" % (tmpfolder), "w")
 
252
    inbound = os.path.join(tmpfolder, "inbound.csr")
 
253
    outbound = os.path.join(tmpfolder, "outbound.csr")
 
254
    csrfile = open(inbound, "w")
143
255
    csrfile.write(csr_text)
144
256
    csrfile.close()
145
 
    logging.debug("Flags path: %s" % ca_folder)
 
257
    logging.debug(_("Flags path: %s") % ca_folder)
146
258
    start = os.getcwd()
147
259
    # Change working dir to CA
148
260
    os.chdir(ca_folder)
149
 
    utils.runthis("Signing cert: %s",
150
 
                  "openssl ca -batch -out %s/outbound.crt "
151
 
                  "-config ./openssl.cnf -infiles %s/inbound.csr" %
152
 
                  (tmpfolder, tmpfolder))
 
261
    utils.execute("openssl ca -batch -out %s -config "
 
262
                  "./openssl.cnf -infiles %s" % (outbound, inbound))
 
263
    out, _err = utils.execute("openssl x509 -in %s -serial -noout" % outbound)
 
264
    serial = out.rpartition("=")[2]
153
265
    os.chdir(start)
154
 
    with open("%s/outbound.crt" % (tmpfolder), "r") as crtfile:
155
 
        return crtfile.read()
 
266
    with open(outbound, "r") as crtfile:
 
267
        return (serial, crtfile.read())
156
268
 
157
269
 
158
270
def mkreq(bits, subject="foo", ca=0):
160
272
    req = M2Crypto.X509.Request()
161
273
    rsa = M2Crypto.RSA.gen_key(bits, 65537, callback=lambda: None)
162
274
    pk.assign_rsa(rsa)
163
 
    # Should not be freed here
164
 
    rsa = None
 
275
    rsa = None  # should not be freed here
165
276
    req.set_pubkey(pk)
166
277
    req.set_subject(subject)
167
278
    req.sign(pk, 'sha512')
225
336
# IN THE SOFTWARE.
226
337
# http://code.google.com/p/boto
227
338
 
228
 
 
229
339
def compute_md5(fp):
230
340
    """
231
341
    :type fp: file