Revision b6267745

b/lib/bootstrap.py
88 88
                  backup=True)
89 89

  
90 90

  
91
def GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, new_confd_hmac_key,
92
                          new_cds, rapi_cert_pem=None, cds=None,
91
def GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, new_spice_cert,
92
                          new_confd_hmac_key, new_cds,
93
                          rapi_cert_pem=None, spice_cert_pem=None,
94
                          spice_cacert_pem=None, cds=None,
93 95
                          nodecert_file=constants.NODED_CERT_FILE,
94 96
                          rapicert_file=constants.RAPI_CERT_FILE,
97
                          spicecert_file=constants.SPICE_CERT_FILE,
98
                          spicecacert_file=constants.SPICE_CACERT_FILE,
95 99
                          hmackey_file=constants.CONFD_HMAC_KEY,
96 100
                          cds_file=constants.CLUSTER_DOMAIN_SECRET_FILE):
97 101
  """Updates the cluster certificates, keys and secrets.
......
100 104
  @param new_cluster_cert: Whether to generate a new cluster certificate
101 105
  @type new_rapi_cert: bool
102 106
  @param new_rapi_cert: Whether to generate a new RAPI certificate
107
  @type new_spice_cert: bool
108
  @param new_spice_cert: Whether to generate a new SPICE certificate
103 109
  @type new_confd_hmac_key: bool
104 110
  @param new_confd_hmac_key: Whether to generate a new HMAC key
105 111
  @type new_cds: bool
106 112
  @param new_cds: Whether to generate a new cluster domain secret
107 113
  @type rapi_cert_pem: string
108 114
  @param rapi_cert_pem: New RAPI certificate in PEM format
115
  @type spice_cert_pem: string
116
  @param spice_cert_pem: New SPICE certificate in PEM format
117
  @type spice_cacert_pem: string
118
  @param spice_cacert_pem: Certificate of the CA that signed the SPICE
119
                           certificate, in PEM format
109 120
  @type cds: string
110 121
  @param cds: New cluster domain secret
111 122
  @type nodecert_file: string
112 123
  @param nodecert_file: optional override of the node cert file path
113 124
  @type rapicert_file: string
114 125
  @param rapicert_file: optional override of the rapi cert file path
126
  @type spicecert_file: string
127
  @param spicecert_file: optional override of the spice cert file path
128
  @type spicecacert_file: string
129
  @param spicecacert_file: optional override of the spice CA cert file path
115 130
  @type hmackey_file: string
116 131
  @param hmackey_file: optional override of the hmac key file path
117 132

  
......
145 160
    logging.debug("Generating new RAPI certificate at %s", rapicert_file)
146 161
    utils.GenerateSelfSignedSslCert(rapicert_file)
147 162

  
163
  # SPICE
164
  spice_cert_exists = os.path.exists(spicecert_file)
165
  spice_cacert_exists = os.path.exists(spicecacert_file)
166
  if spice_cert_pem:
167
    # spice_cert_pem implies also spice_cacert_pem
168
    logging.debug("Writing SPICE certificate at %s", spicecert_file)
169
    utils.WriteFile(spicecert_file, data=spice_cert_pem, backup=True)
170
    logging.debug("Writing SPICE CA certificate at %s", spicecacert_file)
171
    utils.WriteFile(spicecacert_file, data=spice_cacert_pem, backup=True)
172
  elif new_spice_cert or not spice_cert_exists:
173
    if spice_cert_exists:
174
      utils.CreateBackup(spicecert_file)
175
    if spice_cacert_exists:
176
      utils.CreateBackup(spicecacert_file)
177

  
178
    logging.debug("Generating new self-signed SPICE certificate at %s",
179
                  spicecert_file)
180
    (_, cert_pem) = utils.GenerateSelfSignedSslCert(spicecert_file)
181

  
182
    # Self-signed certificate -> the public certificate is also the CA public
183
    # certificate
184
    logging.debug("Writing the public certificate to %s",
185
                  spicecert_file)
186
    utils.io.WriteFile(spicecacert_file, mode=0400, data=cert_pem)
187

  
148 188
  # Cluster domain secret
149 189
  if cds:
150 190
    logging.debug("Writing cluster domain secret to %s", cds_file)
......
166 206

  
167 207
  """
168 208
  # Generate cluster secrets
169
  GenerateClusterCrypto(True, False, False, False)
209
  GenerateClusterCrypto(True, False, False, False, False)
170 210

  
171 211
  result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
172 212
  if result.failed:
b/lib/cli.py
109 109
  "NEW_CONFD_HMAC_KEY_OPT",
110 110
  "NEW_RAPI_CERT_OPT",
111 111
  "NEW_SECONDARY_OPT",
112
  "NEW_SPICE_CERT_OPT",
112 113
  "NIC_PARAMS_OPT",
113 114
  "NODE_FORCE_JOIN_OPT",
114 115
  "NODE_LIST_OPT",
......
159 160
  "SHOWCMD_OPT",
160 161
  "SHUTDOWN_TIMEOUT_OPT",
161 162
  "SINGLE_NODE_OPT",
163
  "SPICE_CACERT_OPT",
164
  "SPICE_CERT_OPT",
162 165
  "SRC_DIR_OPT",
163 166
  "SRC_NODE_OPT",
164 167
  "SUBMIT_OPT",
......
1083 1086
                               help=("Generate a new self-signed RAPI"
1084 1087
                                     " certificate"))
1085 1088

  
1089
SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
1090
                           default=None,
1091
                           help="File containing new SPICE certificate")
1092

  
1093
SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert",
1094
                           default=None,
1095
                           help="File containing the certificate of the CA"
1096
                                " which signed the SPICE certificate")
1097

  
1098
NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate",
1099
                               dest="new_spice_cert", default=None,
1100
                               action="store_true",
1101
                               help=("Generate a new self-signed SPICE"
1102
                                     " certificate"))
1103

  
1086 1104
NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
1087 1105
                                    dest="new_confd_hmac_key",
1088 1106
                                    default=False, action="store_true",
b/lib/client/gnt_cluster.py
646 646
    ToStdout("%s %s", path, tag)
647 647

  
648 648

  
649
def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename,
650
                 new_confd_hmac_key, new_cds, cds_filename,
651
                 force):
649
def _ReadAndVerifyCert(cert_filename, verify_private_key=False):
650
  """Reads and verifies an X509 certificate.
651

  
652
  @type cert_filename: string
653
  @param cert_filename: the path of the file containing the certificate to
654
                        verify encoded in PEM format
655
  @type verify_private_key: bool
656
  @param verify_private_key: whether to verify the private key in addition to
657
                             the public certificate
658
  @rtype: string
659
  @return: a string containing the PEM-encoded certificate.
660

  
661
  """
662
  try:
663
    pem = utils.ReadFile(cert_filename)
664
  except IOError, err:
665
    raise errors.X509CertError(cert_filename,
666
                               "Unable to read certificate: %s" % str(err))
667

  
668
  try:
669
    OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem)
670
  except Exception, err:
671
    raise errors.X509CertError(cert_filename,
672
                               "Unable to load certificate: %s" % str(err))
673

  
674
  if verify_private_key:
675
    try:
676
      OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, pem)
677
    except Exception, err:
678
      raise errors.X509CertError(cert_filename,
679
                                 "Unable to load private key: %s" % str(err))
680

  
681
  return pem
682

  
683

  
684
def _RenewCrypto(new_cluster_cert, new_rapi_cert, #pylint: disable=R0911
685
                 rapi_cert_filename, new_spice_cert, spice_cert_filename,
686
                 spice_cacert_filename, new_confd_hmac_key, new_cds,
687
                 cds_filename, force):
652 688
  """Renews cluster certificates, keys and secrets.
653 689

  
654 690
  @type new_cluster_cert: bool
......
657 693
  @param new_rapi_cert: Whether to generate a new RAPI certificate
658 694
  @type rapi_cert_filename: string
659 695
  @param rapi_cert_filename: Path to file containing new RAPI certificate
696
  @type new_spice_cert: bool
697
  @param new_spice_cert: Whether to generate a new SPICE certificate
698
  @type spice_cert_filename: string
699
  @param spice_cert_filename: Path to file containing new SPICE certificate
700
  @type spice_cacert_filename: string
701
  @param spice_cacert_filename: Path to file containing the certificate of the
702
                                CA that signed the SPICE certificate
660 703
  @type new_confd_hmac_key: bool
661 704
  @param new_confd_hmac_key: Whether to generate a new HMAC key
662 705
  @type new_cds: bool
......
678 721
             " the same time.")
679 722
    return 1
680 723

  
681
  if rapi_cert_filename:
682
    # Read and verify new certificate
683
    try:
684
      rapi_cert_pem = utils.ReadFile(rapi_cert_filename)
685

  
686
      OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
687
                                      rapi_cert_pem)
688
    except Exception, err: # pylint: disable=W0703
689
      ToStderr("Can't load new RAPI certificate from %s: %s" %
690
               (rapi_cert_filename, str(err)))
691
      return 1
724
  if new_spice_cert and (spice_cert_filename or spice_cacert_filename):
725
    ToStderr("When using --new-spice-certificate, the --spice-certificate"
726
             " and --spice-ca-certificate must not be used.")
727
    return 1
692 728

  
693
    try:
694
      OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, rapi_cert_pem)
695
    except Exception, err: # pylint: disable=W0703
696
      ToStderr("Can't load new RAPI private key from %s: %s" %
697
               (rapi_cert_filename, str(err)))
698
      return 1
729
  if bool(spice_cacert_filename) ^ bool(spice_cert_filename):
730
    ToStderr("Both --spice-certificate and --spice-ca-certificate must be"
731
             " specified.")
732
    return 1
699 733

  
700
  else:
701
    rapi_cert_pem = None
734
  rapi_cert_pem, spice_cert_pem, spice_cacert_pem = (None, None, None)
735
  try:
736
    if rapi_cert_filename:
737
      rapi_cert_pem = _ReadAndVerifyCert(rapi_cert_filename, True)
738
    if spice_cert_filename:
739
      spice_cert_pem = _ReadAndVerifyCert(spice_cert_filename, True)
740
      spice_cacert_pem = _ReadAndVerifyCert(spice_cacert_filename)
741
  except errors.X509CertError, err:
742
    ToStderr("Unable to load X509 certificate from %s: %s", err[0], err[1])
743
    return 1
702 744

  
703 745
  if cds_filename:
704 746
    try:
......
718 760

  
719 761
  def _RenewCryptoInner(ctx):
720 762
    ctx.feedback_fn("Updating certificates and keys")
721
    bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert,
763
    bootstrap.GenerateClusterCrypto(new_cluster_cert,
764
                                    new_rapi_cert,
765
                                    new_spice_cert,
722 766
                                    new_confd_hmac_key,
723 767
                                    new_cds,
724 768
                                    rapi_cert_pem=rapi_cert_pem,
769
                                    spice_cert_pem=spice_cert_pem,
770
                                    spice_cacert_pem=spice_cacert_pem,
725 771
                                    cds=cds)
726 772

  
727 773
    files_to_copy = []
......
732 778
    if new_rapi_cert or rapi_cert_pem:
733 779
      files_to_copy.append(constants.RAPI_CERT_FILE)
734 780

  
781
    if new_spice_cert or spice_cert_pem:
782
      files_to_copy.append(constants.SPICE_CERT_FILE)
783
      files_to_copy.append(constants.SPICE_CACERT_FILE)
784

  
735 785
    if new_confd_hmac_key:
736 786
      files_to_copy.append(constants.CONFD_HMAC_KEY)
737 787

  
......
760 810
  return _RenewCrypto(opts.new_cluster_cert,
761 811
                      opts.new_rapi_cert,
762 812
                      opts.rapi_cert,
813
                      opts.new_spice_cert,
814
                      opts.spice_cert,
815
                      opts.spice_cacert,
763 816
                      opts.new_confd_hmac_key,
764 817
                      opts.new_cluster_domain_secret,
765 818
                      opts.cluster_domain_secret,
......
1348 1401
    RenewCrypto, ARGS_NONE,
1349 1402
    [NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT,
1350 1403
     NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT,
1351
     NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT],
1404
     NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT,
1405
     NEW_SPICE_CERT_OPT, SPICE_CERT_OPT, SPICE_CACERT_OPT],
1352 1406
    "[opts...]",
1353 1407
    "Renews cluster certificates, keys and secrets"),
1354 1408
  "epo": (
b/lib/errors.py
277 277
  """
278 278

  
279 279

  
280
class X509CertError(GenericError):
281
  """Invalid X509 certificate.
282

  
283
  This error has two arguments: the certificate filename and the error cause.
284

  
285
  """
286

  
287

  
280 288
class TagError(GenericError):
281 289
  """Generic tag error.
282 290

  
b/lib/hypervisor/hv_kvm.py
794 794
    """Generate KVM information to start an instance.
795 795

  
796 796
    """
797
    # pylint: disable=R0914
797
    # pylint: disable=R0914,R0915
798 798
    _, v_major, v_min, _ = self._GetKVMVersion()
799 799

  
800 800
    pidfile = self._InstancePidFile(instance.name)
b/lib/utils/x509.py
259 259
  @param common_name: commonName value
260 260
  @type validity: int
261 261
  @param validity: Validity for certificate in seconds
262
  @return: a tuple of strings containing the PEM-encoded private key and
263
           certificate
262 264

  
263 265
  """
264 266
  # Create private and public key
......
292 294
  @param common_name: commonName value
293 295
  @type validity: int
294 296
  @param validity: validity of certificate in number of days
297
  @return: a tuple of strings containing the PEM-encoded private key and
298
           certificate
295 299

  
296 300
  """
297 301
  # TODO: Investigate using the cluster name instead of X505_CERT_CN for
......
301 305
                                                   validity * 24 * 60 * 60)
302 306

  
303 307
  utils_io.WriteFile(filename, mode=0400, data=key_pem + cert_pem)
308
  return (key_pem, cert_pem)
b/tools/cfgupgrade
122 122
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
123 123
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
124 124
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
125
  options.SPICE_CERT_FILE = options.data_dir + "/spice.pem"
126
  options.SPICE_CACERT_FILE = options.data_dir + "/spice-ca.pem"
125 127
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
126 128
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
127 129
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
......
222 224
                    backup=True)
223 225

  
224 226
    if not options.dry_run:
225
      bootstrap.GenerateClusterCrypto(False, False, False, False,
226
                                      nodecert_file=options.SERVER_PEM_PATH,
227
                                      rapicert_file=options.RAPI_CERT_FILE,
228
                                      hmackey_file=options.CONFD_HMAC_KEY,
229
                                      cds_file=options.CDS_FILE)
227
      bootstrap.GenerateClusterCrypto(False, False, False, False, False,
228
                                     nodecert_file=options.SERVER_PEM_PATH,
229
                                     rapicert_file=options.RAPI_CERT_FILE,
230
                                     spicecert_file=options.SPICE_CERT_FILE,
231
                                     spicecacert_file=options.SPICE_CACERT_FILE,
232
                                     hmackey_file=options.CONFD_HMAC_KEY,
233
                                     cds_file=options.CDS_FILE)
230 234

  
231 235
  except Exception:
232 236
    logging.critical("Writing configuration failed. It is probably in an"

Also available in: Unified diff