1 =======================================
2 Design for a X509 Certificate Authority
3 =======================================
5 .. contents:: :depth: 4
7 Current state and shortcomings
8 ------------------------------
10 Import/export in Ganeti have a need for many unique X509 certificates.
11 So far these were all self-signed, but with the :doc:`new design for
12 import/export <design-impexp2>` they need to be signed by a Certificate
19 The plan is to implement a simple CA in Ganeti.
21 Interacting with an external CA is too difficult or impossible for
22 automated processes like exporting instances, so each Ganeti cluster
23 will have its own CA. The public key will be stored in
24 ``…/lib/ganeti/ca/cert.pem``, the private key (only readable by the
25 master daemon) in ``…/lib/ganeti/ca/key.pem``.
27 Similar to the RAPI certificate, a new CA certificate can be installed
28 using the ``gnt-cluster renew-crypto`` command. Such a CA could be an
29 intermediate of a third-party CA. By default a self-signed CA is
34 Each certificate signed by the CA is required to have a unique serial
35 number. The serial number is stored in the file
36 ``…/lib/ganeti/ca/serial``, replicated to all master candidates and
37 never reset, even when a new CA is installed.
39 The threat model is expected to be the same as with self-signed
40 certificates. To reinforce this, all certificates signed by the CA must
41 be valid for less than one week (168 hours).
43 Implementing support for Certificate Revocation Lists (CRL) using
44 OpenSSL is non-trivial. Lighttpd doesn't support them at all and
45 `apparently never will in version 1.4.x
46 <http://redmine.lighttpd.net/issues/2278>`_. Some CRL-related parts have
47 only been added in the most recent version of pyOpenSSL (0.11). Instead
48 of a CRL, Ganeti will gain a new cluster configuration property defining
49 the minimum accepted serial number. In case of a lost or compromised
50 private key this property can be set to the most recently generated
53 While possible to implement in the future, other X509 certificates used
54 by the cluster (e.g. RAPI or inter-node communication) will not be
55 automatically signed by the per-cluster CA.
57 The ``commonName`` attribute of signed certificates must be set to the
58 the cluster name or the name of a node in the cluster.
64 - pyOpenSSL 0.10 or above (lower versions can't set the X509v3 extension
65 ``subjectKeyIdentifier`` recommended for certificate authority
66 certificates by :rfc:`3280`, section 4.2.1.2)
72 Generating X509 CA using pyOpenSSL
73 ++++++++++++++++++++++++++++++++++
77 The following code sample shows how to generate a CA certificate using
80 key = OpenSSL.crypto.PKey()
81 key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
83 ca = OpenSSL.crypto.X509()
85 ca.set_serial_number(1)
86 ca.get_subject().CN = "ca.example.com"
87 ca.gmtime_adj_notBefore(0)
88 ca.gmtime_adj_notAfter(24 * 60 * 60)
89 ca.set_issuer(ca.get_subject())
92 OpenSSL.crypto.X509Extension("basicConstraints", True,
93 "CA:TRUE, pathlen:0"),
94 OpenSSL.crypto.X509Extension("keyUsage", True,
95 "keyCertSign, cRLSign"),
96 OpenSSL.crypto.X509Extension("subjectKeyIdentifier", False, "hash",
102 Signing X509 certificate using CA
103 +++++++++++++++++++++++++++++++++
105 .. highlight:: python
107 The following code sample shows how to sign an X509 certificate using a
110 ca_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
112 ca_key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
115 key = OpenSSL.crypto.PKey()
116 key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
118 cert = OpenSSL.crypto.X509()
119 cert.get_subject().CN = "node1.example.com"
120 cert.set_serial_number(1)
121 cert.gmtime_adj_notBefore(0)
122 cert.gmtime_adj_notAfter(24 * 60 * 60)
123 cert.set_issuer(ca_cert.get_subject())
125 cert.sign(ca_key, "sha1")
128 How to generate Certificate Signing Request
129 +++++++++++++++++++++++++++++++++++++++++++
131 .. highlight:: python
133 The following code sample shows how to generate an X509 Certificate
136 key = OpenSSL.crypto.PKey()
137 key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
139 req = OpenSSL.crypto.X509Req()
140 req.get_subject().CN = "node1.example.com"
142 req.sign(key, "sha1")
145 print OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
148 print OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_PEM, req)
151 X509 certificate from Certificate Signing Request
152 +++++++++++++++++++++++++++++++++++++++++++++++++
154 .. highlight:: python
156 The following code sample shows how to create an X509 certificate from a
157 Certificate Signing Request and sign it with a CA::
159 ca_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
161 ca_key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
163 req = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_PEM,
164 open("req.csr").read())
166 cert = OpenSSL.crypto.X509()
167 cert.set_subject(req.get_subject())
168 cert.set_serial_number(1)
169 cert.gmtime_adj_notBefore(0)
170 cert.gmtime_adj_notAfter(24 * 60 * 60)
171 cert.set_issuer(ca_cert.get_subject())
172 cert.set_pubkey(req.get_pubkey())
173 cert.sign(ca_key, "sha1")
175 print OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
178 Verify whether X509 certificate matches private key
179 +++++++++++++++++++++++++++++++++++++++++++++++++++
181 .. highlight:: python
183 The code sample below shows how to check whether a certificate matches
184 with a certain private key. OpenSSL has a function for this,
185 ``X509_check_private_key``, but pyOpenSSL provides no access to it.
189 ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
190 ctx.use_privatekey(key)
191 ctx.use_certificate(cert)
193 ctx.check_privatekey()
194 except OpenSSL.SSL.Error:
195 print "Incorrect key"
197 print "Key matches certificate"
200 .. vim: set textwidth=72 :