~openstack-gd/nova/nova-avail-zones

« back to all changes in this revision

Viewing changes to nova/crypto.py

  • Committer: Tarmac
  • Author(s): Vishvananda Ishaya
  • Date: 2010-12-22 18:24:00 UTC
  • mfrom: (396.6.14 project-vpns)
  • Revision ID: tarmac-20101222182400-vh0j8lwp72auht1r
Fixes per-project vpns (cloudpipe) and adds manage commands and support for certificate revocation.

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