Revision ab4b1cf2
b/lib/backend.py | ||
---|---|---|
1206 | 1206 |
action) |
1207 | 1207 |
if token_type == constants.CRYPTO_TYPE_SSL_DIGEST: |
1208 | 1208 |
if action == constants.CRYPTO_ACTION_CREATE: |
1209 |
|
|
1210 |
# extract file name from options |
|
1209 | 1211 |
cert_filename = None |
1210 | 1212 |
if options: |
1211 | 1213 |
cert_filename = options.get(constants.CRYPTO_OPTION_CERT_FILE) |
... | ... | |
1216 | 1218 |
raise errors.ProgrammerError( |
1217 | 1219 |
"The certificate file name path '%s' is not allowed." % |
1218 | 1220 |
cert_filename) |
1221 |
|
|
1222 |
# extract serial number from options |
|
1223 |
serial_no = None |
|
1224 |
if options: |
|
1225 |
try: |
|
1226 |
serial_no = int(options[constants.CRYPTO_OPTION_SERIAL_NO]) |
|
1227 |
except ValueError: |
|
1228 |
raise errors.ProgrammerError( |
|
1229 |
"The given serial number is not an intenger: %s." % |
|
1230 |
options.get(constants.CRYPTO_OPTION_SERIAL_NO)) |
|
1231 |
except KeyError: |
|
1232 |
raise errors.ProgrammerError("No serial number was provided.") |
|
1233 |
|
|
1234 |
if not serial_no: |
|
1235 |
raise errors.ProgrammerError( |
|
1236 |
"Cannot create an SSL certificate without a serial no.") |
|
1237 |
|
|
1219 | 1238 |
utils.GenerateNewSslCert( |
1220 |
True, cert_filename, |
|
1239 |
True, cert_filename, serial_no,
|
|
1221 | 1240 |
"Create new client SSL certificate in %s." % cert_filename) |
1222 | 1241 |
tokens.append((token_type, |
1223 | 1242 |
utils.GetCertificateDigest( |
... | ... | |
3684 | 3703 |
""" |
3685 | 3704 |
(key_pem, cert_pem) = \ |
3686 | 3705 |
utils.GenerateSelfSignedX509Cert(netutils.Hostname.GetSysName(), |
3687 |
min(validity, _MAX_SSL_CERT_VALIDITY)) |
|
3706 |
min(validity, _MAX_SSL_CERT_VALIDITY), 1)
|
|
3688 | 3707 |
|
3689 | 3708 |
cert_dir = tempfile.mkdtemp(dir=cryptodir, |
3690 | 3709 |
prefix="x509-%s-" % utils.TimestampForFilename()) |
b/lib/bootstrap.py | ||
---|---|---|
138 | 138 |
# pylint: disable=R0913 |
139 | 139 |
# noded SSL certificate |
140 | 140 |
utils.GenerateNewSslCert( |
141 |
new_cluster_cert, nodecert_file, |
|
141 |
new_cluster_cert, nodecert_file, 1,
|
|
142 | 142 |
"Generating new cluster certificate at %s" % nodecert_file) |
143 | 143 |
|
144 | 144 |
# confd HMAC key |
... | ... | |
153 | 153 |
|
154 | 154 |
else: |
155 | 155 |
utils.GenerateNewSslCert( |
156 |
new_rapi_cert, rapicert_file, |
|
156 |
new_rapi_cert, rapicert_file, 1,
|
|
157 | 157 |
"Generating new RAPI certificate at %s" % rapicert_file) |
158 | 158 |
|
159 | 159 |
# SPICE |
... | ... | |
173 | 173 |
|
174 | 174 |
logging.debug("Generating new self-signed SPICE certificate at %s", |
175 | 175 |
spicecert_file) |
176 |
(_, cert_pem) = utils.GenerateSelfSignedSslCert(spicecert_file) |
|
176 |
(_, cert_pem) = utils.GenerateSelfSignedSslCert(spicecert_file, 1)
|
|
177 | 177 |
|
178 | 178 |
# Self-signed certificate -> the public certificate is also the CA public |
179 | 179 |
# certificate |
b/lib/cmdlib/common.py | ||
---|---|---|
1268 | 1268 |
options = {} |
1269 | 1269 |
if filename: |
1270 | 1270 |
options[constants.CRYPTO_OPTION_CERT_FILE] = filename |
1271 |
options[constants.CRYPTO_OPTION_SERIAL_NO] = utils.UuidToInt(node_uuid) |
|
1271 | 1272 |
result = lu.rpc.call_node_crypto_tokens( |
1272 | 1273 |
node_uuid, |
1273 | 1274 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, |
b/lib/utils/security.py | ||
---|---|---|
25 | 25 |
import logging |
26 | 26 |
import OpenSSL |
27 | 27 |
import os |
28 |
import uuid as uuid_module |
|
28 | 29 |
|
29 | 30 |
from ganeti.utils import io |
30 | 31 |
from ganeti.utils import x509 |
... | ... | |
33 | 34 |
from ganeti import pathutils |
34 | 35 |
|
35 | 36 |
|
37 |
def UuidToInt(uuid): |
|
38 |
uuid_obj = uuid_module.UUID(uuid) |
|
39 |
return uuid_obj.int # pylint: disable=E1101 |
|
40 |
|
|
41 |
|
|
36 | 42 |
def AddNodeToCandidateCerts(node_uuid, cert_digest, candidate_certs, |
37 | 43 |
info_fn=logging.info, warn_fn=logging.warn): |
38 | 44 |
"""Adds an entry to the candidate certificate map. |
... | ... | |
94 | 100 |
return cert.digest("sha1") |
95 | 101 |
|
96 | 102 |
|
97 |
def GenerateNewSslCert(new_cert, cert_filename, log_msg): |
|
103 |
def GenerateNewSslCert(new_cert, cert_filename, serial_no, log_msg):
|
|
98 | 104 |
"""Creates a new SSL certificate and backups the old one. |
99 | 105 |
|
100 | 106 |
@type new_cert: boolean |
101 | 107 |
@param new_cert: whether a new certificate should be created |
102 | 108 |
@type cert_filename: string |
103 | 109 |
@param cert_filename: filename of the certificate file |
110 |
@type serial_no: int |
|
111 |
@param serial_no: serial number of the certificate |
|
104 | 112 |
@type log_msg: string |
105 | 113 |
@param log_msg: log message to be written on certificate creation |
106 | 114 |
|
... | ... | |
111 | 119 |
io.CreateBackup(cert_filename) |
112 | 120 |
|
113 | 121 |
logging.debug(log_msg) |
114 |
x509.GenerateSelfSignedSslCert(cert_filename) |
|
122 |
x509.GenerateSelfSignedSslCert(cert_filename, serial_no)
|
|
115 | 123 |
|
116 | 124 |
|
117 | 125 |
def VerifyCertificate(filename): |
b/lib/utils/x509.py | ||
---|---|---|
254 | 254 |
return (cert, salt) |
255 | 255 |
|
256 | 256 |
|
257 |
def GenerateSelfSignedX509Cert(common_name, validity): |
|
257 |
def GenerateSelfSignedX509Cert(common_name, validity, serial_no):
|
|
258 | 258 |
"""Generates a self-signed X509 certificate. |
259 | 259 |
|
260 | 260 |
@type common_name: string |
... | ... | |
273 | 273 |
cert = OpenSSL.crypto.X509() |
274 | 274 |
if common_name: |
275 | 275 |
cert.get_subject().CN = common_name |
276 |
cert.set_serial_number(1)
|
|
276 |
cert.set_serial_number(serial_no)
|
|
277 | 277 |
cert.gmtime_adj_notBefore(0) |
278 | 278 |
cert.gmtime_adj_notAfter(validity) |
279 | 279 |
cert.set_issuer(cert.get_subject()) |
... | ... | |
286 | 286 |
return (key_pem, cert_pem) |
287 | 287 |
|
288 | 288 |
|
289 |
def GenerateSelfSignedSslCert(filename, common_name=constants.X509_CERT_CN, |
|
289 |
def GenerateSelfSignedSslCert(filename, serial_no, |
|
290 |
common_name=constants.X509_CERT_CN, |
|
290 | 291 |
validity=constants.X509_CERT_DEFAULT_VALIDITY): |
291 | 292 |
"""Legacy function to generate self-signed X509 certificate. |
292 | 293 |
|
... | ... | |
303 | 304 |
# TODO: Investigate using the cluster name instead of X505_CERT_CN for |
304 | 305 |
# common_name, as cluster-renames are very seldom, and it'd be nice if RAPI |
305 | 306 |
# and node daemon certificates have the proper Subject/Issuer. |
306 |
(key_pem, cert_pem) = GenerateSelfSignedX509Cert(common_name,
|
|
307 |
validity * 24 * 60 * 60)
|
|
307 |
(key_pem, cert_pem) = GenerateSelfSignedX509Cert( |
|
308 |
common_name, validity * 24 * 60 * 60, serial_no)
|
|
308 | 309 |
|
309 | 310 |
utils_io.WriteFile(filename, mode=0400, data=key_pem + cert_pem) |
310 | 311 |
return (key_pem, cert_pem) |
b/qa/qa_cluster.py | ||
---|---|---|
1049 | 1049 |
# Ensure certificate doesn't cause "gnt-cluster verify" to complain |
1050 | 1050 |
validity = constants.SSL_CERT_EXPIRATION_WARN * 3 |
1051 | 1051 |
|
1052 |
utils.GenerateSelfSignedSslCert(fh.name, validity=validity) |
|
1052 |
utils.GenerateSelfSignedSslCert(fh.name, 1, validity=validity)
|
|
1053 | 1053 |
|
1054 | 1054 |
tmpcert = qa_utils.UploadFile(master.primary, fh.name) |
1055 | 1055 |
try: |
b/src/Ganeti/Constants.hs | ||
---|---|---|
4207 | 4207 |
cryptoOptionCertFile :: String |
4208 | 4208 |
cryptoOptionCertFile = "cert_file" |
4209 | 4209 |
|
4210 |
-- Serial number of the certificate |
|
4211 |
cryptoOptionSerialNo :: String |
|
4212 |
cryptoOptionSerialNo = "serial_no" |
|
4213 |
|
|
4210 | 4214 |
-- * SSH key types |
4211 | 4215 |
|
4212 | 4216 |
sshkDsa :: String |
b/test/py/ganeti.backend_unittest.py | ||
---|---|---|
97 | 97 |
def testCreateSslToken(self): |
98 | 98 |
result = backend.GetCryptoTokens( |
99 | 99 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, |
100 |
None)])
|
|
100 |
{constants.CRYPTO_OPTION_SERIAL_NO: 42})])
|
|
101 | 101 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
102 | 102 |
in result) |
103 | 103 |
self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) |
... | ... | |
106 | 106 |
result = backend.GetCryptoTokens( |
107 | 107 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, |
108 | 108 |
{constants.CRYPTO_OPTION_CERT_FILE: |
109 |
pathutils.NODED_CLIENT_CERT_FILE_TMP})]) |
|
109 |
pathutils.NODED_CLIENT_CERT_FILE_TMP, |
|
110 |
constants.CRYPTO_OPTION_SERIAL_NO: 42})]) |
|
111 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
|
112 |
in result) |
|
113 |
self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) |
|
114 |
|
|
115 |
def testCreateSslTokenSerialNo(self): |
|
116 |
result = backend.GetCryptoTokens( |
|
117 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, |
|
118 |
{constants.CRYPTO_OPTION_SERIAL_NO: 42})]) |
|
110 | 119 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
111 | 120 |
in result) |
112 | 121 |
self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) |
b/test/py/ganeti.utils.security_unittest.py | ||
---|---|---|
33 | 33 |
import testutils |
34 | 34 |
|
35 | 35 |
|
36 |
class TestUuidConversion(unittest.TestCase): |
|
37 |
|
|
38 |
def testUuidConversion(self): |
|
39 |
uuid_as_int = security.UuidToInt("5cd037f4-9587-49c4-a23e-142f8b7e909d") |
|
40 |
self.assertEqual(uuid_as_int, int(uuid_as_int)) |
|
41 |
|
|
42 |
|
|
36 | 43 |
class TestCandidateCerts(unittest.TestCase): |
37 | 44 |
|
38 | 45 |
def setUp(self): |
b/test/py/ganeti.utils.x509_unittest.py | ||
---|---|---|
96 | 96 |
|
97 | 97 |
def test(self): |
98 | 98 |
# Generate certificate valid for 5 minutes |
99 |
(_, cert_pem) = utils.GenerateSelfSignedX509Cert(None, 300) |
|
99 |
(_, cert_pem) = utils.GenerateSelfSignedX509Cert(None, 300, 1)
|
|
100 | 100 |
|
101 | 101 |
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, |
102 | 102 |
cert_pem) |
... | ... | |
257 | 257 |
|
258 | 258 |
def test(self): |
259 | 259 |
for common_name in [None, ".", "Ganeti", "node1.example.com"]: |
260 |
(key_pem, cert_pem) = utils.GenerateSelfSignedX509Cert(common_name, 300) |
|
260 |
(key_pem, cert_pem) = utils.GenerateSelfSignedX509Cert(common_name, 300, |
|
261 |
1) |
|
261 | 262 |
self._checkRsaPrivateKey(key_pem) |
262 | 263 |
self._checkCertificate(cert_pem) |
263 | 264 |
|
... | ... | |
277 | 278 |
def testLegacy(self): |
278 | 279 |
cert1_filename = os.path.join(self.tmpdir, "cert1.pem") |
279 | 280 |
|
280 |
utils.GenerateSelfSignedSslCert(cert1_filename, validity=1) |
|
281 |
utils.GenerateSelfSignedSslCert(cert1_filename, 1, validity=1)
|
|
281 | 282 |
|
282 | 283 |
cert1 = utils.ReadFile(cert1_filename) |
283 | 284 |
|
b/test/py/import-export_unittest-helper | ||
---|---|---|
97 | 97 |
elif what == "connected": |
98 | 98 |
WaitForConnected(filename) |
99 | 99 |
elif what == "gencert": |
100 |
utils.GenerateSelfSignedSslCert(filename, validity=VALIDITY) |
|
100 |
utils.GenerateSelfSignedSslCert(filename, 1, validity=VALIDITY)
|
|
101 | 101 |
else: |
102 | 102 |
raise Exception("Unknown command '%s'" % what) |
103 | 103 |
|
b/tools/cfgupgrade12 | ||
---|---|---|
405 | 405 |
if not options.dry_run: |
406 | 406 |
if not os.path.exists(options.RAPI_CERT_FILE): |
407 | 407 |
logging.debug("Writing RAPI certificate to %s", options.RAPI_CERT_FILE) |
408 |
utils.GenerateSelfSignedSslCert(options.RAPI_CERT_FILE) |
|
408 |
utils.GenerateSelfSignedSslCert(options.RAPI_CERT_FILE, 1)
|
|
409 | 409 |
|
410 | 410 |
except: |
411 | 411 |
logging.critical("Writing configuration failed. It is probably in an" |
Also available in: Unified diff