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