Revision 0aee8ee9

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

  
90 90

  
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,
91
def GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, new_confd_hmac_key,
92
                          new_cds, rapi_cert_pem=None, cds=None,
95 93
                          nodecert_file=constants.NODED_CERT_FILE,
96 94
                          rapicert_file=constants.RAPI_CERT_FILE,
97
                          spicecert_file=constants.SPICE_CERT_FILE,
98
                          spicecacert_file=constants.SPICE_CACERT_FILE,
99 95
                          hmackey_file=constants.CONFD_HMAC_KEY,
100 96
                          cds_file=constants.CLUSTER_DOMAIN_SECRET_FILE):
101 97
  """Updates the cluster certificates, keys and secrets.
......
104 100
  @param new_cluster_cert: Whether to generate a new cluster certificate
105 101
  @type new_rapi_cert: bool
106 102
  @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
109 103
  @type new_confd_hmac_key: bool
110 104
  @param new_confd_hmac_key: Whether to generate a new HMAC key
111 105
  @type new_cds: bool
112 106
  @param new_cds: Whether to generate a new cluster domain secret
113 107
  @type rapi_cert_pem: string
114 108
  @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
120 109
  @type cds: string
121 110
  @param cds: New cluster domain secret
122 111
  @type nodecert_file: string
123 112
  @param nodecert_file: optional override of the node cert file path
124 113
  @type rapicert_file: string
125 114
  @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
130 115
  @type hmackey_file: string
131 116
  @param hmackey_file: optional override of the hmac key file path
132 117

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

  
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

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

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

  
211 171
  result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
212 172
  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",
113 112
  "NIC_PARAMS_OPT",
114 113
  "NODE_FORCE_JOIN_OPT",
115 114
  "NODE_LIST_OPT",
......
160 159
  "SHOWCMD_OPT",
161 160
  "SHUTDOWN_TIMEOUT_OPT",
162 161
  "SINGLE_NODE_OPT",
163
  "SPICE_CACERT_OPT",
164
  "SPICE_CERT_OPT",
165 162
  "SRC_DIR_OPT",
166 163
  "SRC_NODE_OPT",
167 164
  "SUBMIT_OPT",
......
1089 1086
                               help=("Generate a new self-signed RAPI"
1090 1087
                                     " certificate"))
1091 1088

  
1092
SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
1093
                           default=None,
1094
                           help="File containing new SPICE certificate")
1095

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

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

  
1107 1089
NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
1108 1090
                                    dest="new_confd_hmac_key",
1109 1091
                                    default=False, action="store_true",
b/lib/client/gnt_cluster.py
672 672
    ToStdout("%s %s", path, tag)
673 673

  
674 674

  
675
def _ReadAndVerifyCert(cert_filename, verify_private_key=False):
676
  """Reads and verifies an X509 certificate.
677

  
678
  @type cert_filename: string
679
  @param cert_filename: the path of the file containing the certificate to
680
                        verify encoded in PEM format
681
  @type verify_private_key: bool
682
  @param verify_private_key: whether to verify the private key in addition to
683
                             the public certificate
684
  @rtype: string
685
  @return: a string containing the PEM-encoded certificate.
686

  
687
  """
688
  try:
689
    pem = utils.ReadFile(cert_filename)
690
  except IOError, err:
691
    raise errors.X509CertError(cert_filename,
692
                               "Unable to read certificate: %s" % str(err))
693

  
694
  try:
695
    OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem)
696
  except Exception, err:
697
    raise errors.X509CertError(cert_filename,
698
                               "Unable to load certificate: %s" % str(err))
699

  
700
  if verify_private_key:
701
    try:
702
      OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, pem)
703
    except Exception, err:
704
      raise errors.X509CertError(cert_filename,
705
                                 "Unable to load private key: %s" % str(err))
706

  
707
  return pem
708

  
709

  
710
def _RenewCrypto(new_cluster_cert, new_rapi_cert, #pylint: disable=R0911
711
                 rapi_cert_filename, new_spice_cert, spice_cert_filename,
712
                 spice_cacert_filename, new_confd_hmac_key, new_cds,
713
                 cds_filename, force):
675
def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename,
676
                 new_confd_hmac_key, new_cds, cds_filename,
677
                 force):
714 678
  """Renews cluster certificates, keys and secrets.
715 679

  
716 680
  @type new_cluster_cert: bool
......
719 683
  @param new_rapi_cert: Whether to generate a new RAPI certificate
720 684
  @type rapi_cert_filename: string
721 685
  @param rapi_cert_filename: Path to file containing new RAPI certificate
722
  @type new_spice_cert: bool
723
  @param new_spice_cert: Whether to generate a new SPICE certificate
724
  @type spice_cert_filename: string
725
  @param spice_cert_filename: Path to file containing new SPICE certificate
726
  @type spice_cacert_filename: string
727
  @param spice_cacert_filename: Path to file containing the certificate of the
728
                                CA that signed the SPICE certificate
729 686
  @type new_confd_hmac_key: bool
730 687
  @param new_confd_hmac_key: Whether to generate a new HMAC key
731 688
  @type new_cds: bool
......
747 704
             " the same time.")
748 705
    return 1
749 706

  
750
  if new_spice_cert and (spice_cert_filename or spice_cacert_filename):
751
    ToStderr("When using --new-spice-certificate, the --spice-certificate"
752
             " and --spice-ca-certificate must not be used.")
753
    return 1
707
  if rapi_cert_filename:
708
    # Read and verify new certificate
709
    try:
710
      rapi_cert_pem = utils.ReadFile(rapi_cert_filename)
754 711

  
755
  if bool(spice_cacert_filename) ^ bool(spice_cert_filename):
756
    ToStderr("Both --spice-certificate and --spice-ca-certificate must be"
757
             " specified.")
758
    return 1
712
      OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
713
                                      rapi_cert_pem)
714
    except Exception, err: # pylint: disable=W0703
715
      ToStderr("Can't load new RAPI certificate from %s: %s" %
716
               (rapi_cert_filename, str(err)))
717
      return 1
759 718

  
760
  rapi_cert_pem, spice_cert_pem, spice_cacert_pem = (None, None, None)
761
  try:
762
    if rapi_cert_filename:
763
      rapi_cert_pem = _ReadAndVerifyCert(rapi_cert_filename, True)
764
    if spice_cert_filename:
765
      spice_cert_pem = _ReadAndVerifyCert(spice_cert_filename, True)
766
      spice_cacert_pem = _ReadAndVerifyCert(spice_cacert_filename)
767
  except errors.X509CertError, err:
768
    ToStderr("Unable to load X509 certificate from %s: %s", err[0], err[1])
769
    return 1
719
    try:
720
      OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, rapi_cert_pem)
721
    except Exception, err: # pylint: disable=W0703
722
      ToStderr("Can't load new RAPI private key from %s: %s" %
723
               (rapi_cert_filename, str(err)))
724
      return 1
725

  
726
  else:
727
    rapi_cert_pem = None
770 728

  
771 729
  if cds_filename:
772 730
    try:
......
786 744

  
787 745
  def _RenewCryptoInner(ctx):
788 746
    ctx.feedback_fn("Updating certificates and keys")
789
    bootstrap.GenerateClusterCrypto(new_cluster_cert,
790
                                    new_rapi_cert,
791
                                    new_spice_cert,
747
    bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert,
792 748
                                    new_confd_hmac_key,
793 749
                                    new_cds,
794 750
                                    rapi_cert_pem=rapi_cert_pem,
795
                                    spice_cert_pem=spice_cert_pem,
796
                                    spice_cacert_pem=spice_cacert_pem,
797 751
                                    cds=cds)
798 752

  
799 753
    files_to_copy = []
......
804 758
    if new_rapi_cert or rapi_cert_pem:
805 759
      files_to_copy.append(constants.RAPI_CERT_FILE)
806 760

  
807
    if new_spice_cert or spice_cert_pem:
808
      files_to_copy.append(constants.SPICE_CERT_FILE)
809
      files_to_copy.append(constants.SPICE_CACERT_FILE)
810

  
811 761
    if new_confd_hmac_key:
812 762
      files_to_copy.append(constants.CONFD_HMAC_KEY)
813 763

  
......
836 786
  return _RenewCrypto(opts.new_cluster_cert,
837 787
                      opts.new_rapi_cert,
838 788
                      opts.rapi_cert,
839
                      opts.new_spice_cert,
840
                      opts.spice_cert,
841
                      opts.spice_cacert,
842 789
                      opts.new_confd_hmac_key,
843 790
                      opts.new_cluster_domain_secret,
844 791
                      opts.cluster_domain_secret,
......
1427 1374
    RenewCrypto, ARGS_NONE,
1428 1375
    [NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT,
1429 1376
     NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT,
1430
     NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT,
1431
     NEW_SPICE_CERT_OPT, SPICE_CERT_OPT, SPICE_CACERT_OPT],
1377
     NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT],
1432 1378
    "[opts...]",
1433 1379
    "Renews cluster certificates, keys and secrets"),
1434 1380
  "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

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

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

  
796 796
    """
797
    # pylint: disable=R0914,R0915
797
    # pylint: disable=R0914
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
264 262

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

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

  
307 303
  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"
127 125
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
128 126
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
129 127
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
......
224 222
                    backup=True)
225 223

  
226 224
    if not options.dry_run:
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)
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)
234 230

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

Also available in: Unified diff