- fix version number, bump release number, added changelog
[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.15 2006/07/11 20:57:25 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 and public key from the root CA certificate
32     root_ca_email = commands.getoutput("openssl x509 -in %s -noout -email" % \
33                                        plc_ma_sa['ca_ssl_crt'])
34     root_ca_key_pub = commands.getoutput("openssl x509 -in %s -noout -pubkey" % \
35                                          plc_ma_sa['ca_ssl_crt'])
36
37     # Verify API certificate
38     if os.path.exists(plc_ma_sa['api_crt']):
39         print "Verifying API certificate '%s'" % plc_ma_sa['api_crt']
40         try:
41             cert_xml = file(plc_ma_sa['api_crt']).read().strip()
42             # Verify root CA signature
43             CertOps.authenticate_cert(cert_xml, {root_ca_email: root_ca_key_pub})
44             # Check if MA/SA e-mail address has changed
45             dom = xml.dom.minidom.parseString(cert_xml)
46             for subject in dom.getElementsByTagName('subject'):
47                 if subject.getAttribute('email') != plc_mail['support_address']:
48                     raise Exception, "E-mail address '%s' in certificate '%s' does not match support address '%s'" % \
49                           (subject.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 self-signed 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 = commands.getoutput("openssl x509 -in %s -noout -pubkey" % \
61                                                    plc_ma_sa['ssl_crt'])
62             cert.add_subject_pubkey(pubkey = ma_sa_ssl_key_pub, email = plc_mail['support_address'])
63             root_ca_subject = commands.getoutput("openssl x509 -in %s -noout -subject" % \
64                                                  plc_ma_sa['ssl_crt'])
65             m = re.search('/CN=([^/]*).*', root_ca_subject)
66             if m is None:
67                 root_ca_cn = plc['name'] + " Management and Slice Authority"
68             else:
69                 root_ca_cn = m.group(1)
70             cert.set_issuer(email = root_ca_email, cn = root_ca_cn)
71             cert_xml = cert.sign(plc_ma_sa['ssl_key'])
72             ma_sa_api_crt = file(plc_ma_sa['api_crt'], "w")
73             ma_sa_api_crt.write(cert_xml)
74             ma_sa_api_crt.close()
75         except Exception, e:
76             print "Warning: Could not generate API certificate: ", e
77
78     # For backward compatibility, until we can convert all code to use
79     # the now standardized variable names.
80
81     # API expects root SSH public key to be at /etc/planetlab/node_root_key
82     if not os.path.exists("/etc/planetlab/node_root_key"):
83         os.symlink(plc['root_ssh_key_pub'], "/etc/planetlab/node_root_key")
84
85     # Old variable names littered throughout the API
86     if plc_mail['enabled'] == "true":
87         plc_mail_enabled = "1"
88     else:
89         plc_mail_enabled = "0"        
90
91     old_variables = {'PL_API_SERVER': plc_api['host'],
92                      'PL_API_PATH': plc_api['path'],
93                      'PL_API_PORT': plc_api['port'],
94                      'PL_API_CAPABILITY_AUTH_METHOD': "capability",
95                      'PL_API_CAPABILITY_PASS': plc_api['maintenance_password'],
96                      'PL_API_CAPABILITY_USERNAME': plc_api['maintenance_user'],
97                      'PLANETLAB_SUPPORT_EMAIL': plc_mail['support_address'],
98                      'BOOT_MESSAGES_EMAIL': plc_mail['boot_address'],
99                      'WWW_BASE': plc_www['host'],
100                      'BOOT_BASE': plc_boot['host'],
101
102                      'PLC_MAIL_ENABLED': plc_mail_enabled,
103                      'MA_SA_NAMESPACE': plc_ma_sa['namespace'],
104                      'SESSION_LENGTH_HOURS': "24",
105                      'ROOT_CA_EMAIL': root_ca_email,
106                      'ROOT_CA_PUB_KEY': plc_ma_sa['ca_ssl_key_pub'],
107                      'API_CERT_PATH': plc_ma_sa['api_crt'],
108                      'MA_SA_PRIVATE_KEY': plc_ma_sa['ssl_key'],
109                      'PL_API_TICKET_KEY_FILE': plc_ma_sa['ssl_key']}
110
111     # The format of an "allowed maintenance source" specification is
112     #
113     # ip:max_role_id:organization_id:password
114     #
115     # It is unlikely that we will let federated sites use the
116     # maintenance account to access each others' APIs, so we always
117     # set organization_id to -1.
118     old_variables['PL_API_CAPABILITY_SOURCES'] = " ".join(
119         ["%s:-1:-1:%s" % (ip, plc_api['maintenance_password']) \
120          for ip in plc_api['maintenance_sources'].split()])
121
122     old_config = open("/etc/planetlab/plc_api", "w")
123     for name, value in old_variables.iteritems():
124         old_config.write("%s='%s'\n" % (name, value))
125     old_config.close()
126
127
128 if __name__ == '__main__':
129     main()