- add ssl_email function for extracting the emailAddress from an X.509 cert
[myplc.git] / api-config
1 #!/usr/bin/python
2 #
3 # Bootstraps the PLC database with a default administrator account and
4 # a default site. Also generates the MA/SA API certificate.
5 #
6 # Mark Huang <mlhuang@cs.princeton.edu>
7 # Copyright (C) 2006 The Trustees of Princeton University
8 #
9 # $Id: api-config,v 1.12 2006/05/30 15:06:20 mlhuang Exp $
10 #
11
12 from plc_config import PLCConfiguration
13 import os
14 import re
15 import xml
16 import CertOps, Certificate
17 import Certificate
18 import commands
19
20
21 def main():
22     cfg = PLCConfiguration()
23     cfg.load()
24     variables = cfg.variables()
25
26     # Load variables into dictionaries
27     for category_id, (category, variablelist) in variables.iteritems():
28         globals()[category_id] = dict(zip(variablelist.keys(),
29                                        [variable['value'] for variable in variablelist.values()]))
30
31     # Get the issuer e-mail address of the root CA certificate
32     root_ca_email = commands.getoutput("openssl x509 -in %s -noout -email" % \
33                                        plc['root_ca_ssl_crt'])
34
35     # Verify API certificate
36     if os.path.exists(plc_ma_sa['api_crt']):
37         print "Verifying API certificate '%s'" % plc_ma_sa['api_crt']
38         try:
39             cert_xml = file(plc_ma_sa['api_crt']).read().strip()
40             # Verify root CA signature
41             CertOps.authenticate_cert(cert_xml,
42                                       {root_ca_email:
43                                        file(plc['root_ca_ssl_key_pub']).read().strip()})
44             # Check if MA/SA e-mail address has changed
45             dom = xml.dom.minidom.parseString(cert_xml)
46             for issuer in dom.getElementsByTagName('issuer'):
47                 if issuer.getAttribute('email') != plc_mail['support_address']:
48                     raise Exception, "E-mail address '%s' in certificate '%s' does not match support address '%s'" % \
49                           (issuer.getAttribute('email'), plc_ma_sa['api_crt'], plc_mail['support_address'])
50         except Exception, e:
51             # Delete invalid API certificate
52             print "Warning: ", e
53             os.unlink(plc_ma_sa['api_crt'])
54
55     # Generate API certificate
56     if not os.path.exists(plc_ma_sa['api_crt']):
57         print "Generating new API certificate"
58         try:
59             cert = Certificate.Certificate('ticket-cert-0')
60             ma_sa_ssl_key_pub = file(plc_ma_sa['ssl_key_pub']).read().strip()
61             cert.add_subject_pubkey(pubkey = ma_sa_ssl_key_pub, email = plc_mail['support_address'])
62             root_ca_subject = commands.getoutput("openssl x509 -in %s -noout -subject" % \
63                                                  plc['root_ca_ssl_crt'])
64             m = re.search('/CN=([^/]*).*', root_ca_subject)
65             if m is None:
66                 root_ca_cn = plc['name'] + " Root CA"
67             else:
68                 root_ca_cn = m.group(1)
69             cert.set_issuer(email = root_ca_email, cn = root_ca_cn)
70             cert_xml = cert.sign(plc['root_ca_ssl_key'])
71             ma_sa_api_crt = file(plc_ma_sa['api_crt'], "w")
72             ma_sa_api_crt.write(cert_xml)
73             ma_sa_api_crt.close()
74         except Exception, e:
75             print "Warning: Could not generate API certificate: ", e
76
77     # For backward compatibility, until we can convert all code to use
78     # the now standardized variable names.
79
80     # API expects root SSH public key to be at /etc/planetlab/node_root_key
81     if not os.path.exists("/etc/planetlab/node_root_key"):
82         os.symlink(plc['root_ssh_key_pub'], "/etc/planetlab/node_root_key")
83
84     # Old variable names littered throughout the API
85     old_variables = {'PL_API_SERVER': plc_api['host'],
86                      'PL_API_PATH': plc_api['path'],
87                      'PL_API_PORT': plc_api['port'],
88                      'PL_API_CAPABILITY_AUTH_METHOD': "capability",
89                      'PL_API_CAPABILITY_PASS': plc_api['maintenance_password'],
90                      'PL_API_CAPABILITY_USERNAME': plc_api['maintenance_user'],
91                      'PLANETLAB_SUPPORT_EMAIL': plc_mail['support_address'],
92                      'BOOT_MESSAGES_EMAIL': plc_mail['boot_address'],
93                      'WWW_BASE': plc_www['host'],
94                      'BOOT_BASE': plc_boot['host'],
95
96                      'MA_SA_NAMESPACE': plc_ma_sa['namespace'],
97                      'SESSION_LENGTH_HOURS': "24",
98                      'ROOT_CA_EMAIL': root_ca_email,
99                      'ROOT_CA_PUB_KEY': plc['root_ca_ssl_key_pub'],
100                      'API_CERT_PATH': plc_ma_sa['api_crt'],
101                      'MA_SA_PRIVATE_KEY': plc_ma_sa['ssl_key'],
102                      'PL_API_TICKET_KEY_FILE': plc_ma_sa['ssl_key']}
103
104     # The format of an "allowed maintenance source" specification is
105     #
106     # ip:max_role_id:organization_id:password
107     #
108     # It is unlikely that we will let federated sites use the
109     # maintenance account to access each others' APIs, so we always
110     # set organization_id to -1.
111     old_variables['PL_API_CAPABILITY_SOURCES'] = " ".join(
112         ["%s:-1:-1:%s" % (ip, plc_api['maintenance_password']) \
113          for ip in plc_api['maintenance_sources'].split()])
114
115     old_config = open("/etc/planetlab/plc_api", "w")
116     for name, value in old_variables.iteritems():
117         old_config.write("%s='%s'\n" % (name, value))
118     old_config.close()
119
120
121 if __name__ == '__main__':
122     main()