Revision 3db3eb2a

b/lib/bootstrap.py
77 77

  
78 78

  
79 79
def GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, new_confd_hmac_key,
80
                          rapi_cert_pem=None):
80
                          new_cds, rapi_cert_pem=None, cds=None):
81 81
  """Updates the cluster certificates, keys and secrets.
82 82

  
83 83
  @type new_cluster_cert: bool
......
86 86
  @param new_rapi_cert: Whether to generate a new RAPI certificate
87 87
  @type new_confd_hmac_key: bool
88 88
  @param new_confd_hmac_key: Whether to generate a new HMAC key
89
  @type new_cds: bool
90
  @param new_cds: Whether to generate a new cluster domain secret
89 91
  @type rapi_cert_pem: string
90 92
  @param rapi_cert_pem: New RAPI certificate in PEM format
93
  @type cds: string
94
  @param cds: New cluster domain secret
91 95

  
92 96
  """
93 97
  # noded SSL certificate
......
122 126
                  constants.RAPI_CERT_FILE)
123 127
    utils.GenerateSelfSignedSslCert(constants.RAPI_CERT_FILE)
124 128

  
129
  # Cluster domain secret
130
  if cds:
131
    logging.debug("Writing cluster domain secret to %s",
132
                  constants.CLUSTER_DOMAIN_SECRET_FILE)
133
    utils.WriteFile(constants.CLUSTER_DOMAIN_SECRET_FILE,
134
                    data=cds, backup=True)
135

  
136
  elif new_cds or not os.path.exists(constants.CLUSTER_DOMAIN_SECRET_FILE):
137
    logging.debug("Generating new cluster domain secret at %s",
138
                  constants.CLUSTER_DOMAIN_SECRET_FILE)
139
    GenerateHmacKey(constants.CLUSTER_DOMAIN_SECRET_FILE)
140

  
125 141

  
126 142
def _InitGanetiServerSetup(master_name):
127 143
  """Setup the necessary configuration for the initial node daemon.
......
131 147

  
132 148
  """
133 149
  # Generate cluster secrets
134
  GenerateClusterCrypto(True, False, False)
150
  GenerateClusterCrypto(True, False, False, False)
135 151

  
136 152
  result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
137 153
  if result.failed:
......
415 431
  # and then connect with ssh to set password and start ganeti-noded
416 432
  # note that all the below variables are sanitized at this point,
417 433
  # either by being constants or by the checks above
434
  # TODO: Could this command exceed a shell's maximum command length?
418 435
  mycommand = ("umask 077 && "
419 436
               "cat > '%s' << '!EOF.' && \n"
420 437
               "%s!EOF.\n"
b/lib/cli.py
50 50
  "AUTO_REPLACE_OPT",
51 51
  "BACKEND_OPT",
52 52
  "CLEANUP_OPT",
53
  "CLUSTER_DOMAIN_SECRET_OPT",
53 54
  "CONFIRM_OPT",
54 55
  "CP_SIZE_OPT",
55 56
  "DEBUG_OPT",
......
81 82
  "MC_OPT",
82 83
  "NET_OPT",
83 84
  "NEW_CLUSTER_CERT_OPT",
85
  "NEW_CLUSTER_DOMAIN_SECRET_OPT",
84 86
  "NEW_CONFD_HMAC_KEY_OPT",
85 87
  "NEW_RAPI_CERT_OPT",
86 88
  "NEW_SECONDARY_OPT",
......
824 826
                               metavar="NETDEV",
825 827
                               default=constants.DEFAULT_BRIDGE)
826 828

  
827

  
828 829
GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
829 830
                                help="Specify the default directory (cluster-"
830 831
                                "wide) for storing the file-based disks [%s]" %
......
898 899
                                    help=("Create a new HMAC key for %s" %
899 900
                                          constants.CONFD))
900 901

  
902
CLUSTER_DOMAIN_SECRET_OPT = cli_option("--cluster-domain-secret",
903
                                       dest="cluster_domain_secret",
904
                                       default=None,
905
                                       help=("Load new new cluster domain"
906
                                             " secret from file"))
907

  
908
NEW_CLUSTER_DOMAIN_SECRET_OPT = cli_option("--new-cluster-domain-secret",
909
                                           dest="new_cluster_domain_secret",
910
                                           default=False, action="store_true",
911
                                           help=("Create a new cluster domain"
912
                                                 " secret"))
913

  
901 914

  
902 915
def _ParseArgs(argv, commands, aliases):
903 916
  """Parser for the command line arguments.
b/lib/constants.py
100 100
NODED_CERT_FILE = DATA_DIR + "/server.pem"
101 101
RAPI_CERT_FILE = DATA_DIR + "/rapi.pem"
102 102
CONFD_HMAC_KEY = DATA_DIR + "/hmac.key"
103
CLUSTER_DOMAIN_SECRET_FILE = DATA_DIR + "/cluster-domain-secret"
103 104
WATCHER_STATEFILE = DATA_DIR + "/watcher.data"
104 105
WATCHER_PAUSEFILE = DATA_DIR + "/watcher.pause"
105 106
INSTANCE_UPFILE = RUN_GANETI_DIR + "/instance-status"
b/man/gnt-cluster.sgml
715 715
        <sbr>
716 716
        <arg choice="opt">--new-rapi-certificate</arg>
717 717
        <arg choice="opt">--rapi-certificate <replaceable>rapi-cert</replaceable></arg>
718
        <sbr>
719
        <arg choice="opt">--new-cluster-domain-secret</arg>
720
        <arg choice="opt">--cluster-domain-secret <replaceable>filename</replaceable></arg>
718 721
      </cmdsynopsis>
719 722

  
720 723
      <para>
......
726 729
        cluster-internal SSL certificate respective the HMAC key used by
727 730
        <citerefentry>
728 731
        <refentrytitle>ganeti-confd</refentrytitle><manvolnum>8</manvolnum>
729
        </citerefentry>. To generate a new self-signed RAPI certificate (used
730
        by <citerefentry>
732
        </citerefentry>.
733
      </para>
734

  
735
      <para>
736
        To generate a new self-signed RAPI certificate (used by <citerefentry>
731 737
        <refentrytitle>ganeti-rapi</refentrytitle><manvolnum>8</manvolnum>
732 738
        </citerefentry>) specify <option>--new-rapi-certificate</option>. If
733 739
        you want to use your own certificate, e.g. one signed by a certificate
734 740
        authority (CA), pass its filename to
735 741
        <option>--rapi-certificate</option>.
736 742
      </para>
743

  
744
      <para>
745
        <option>--new-cluster-domain-secret</option> generates a new, random
746
        cluster domain secret. <option>--cluster-domain-secret</option> reads
747
        the secret from a file. The cluster domain secret is used to sign
748
        information exchanged between separate clusters via a third party.
749
      </para>
737 750
    </refsect2>
738 751

  
739 752
    <refsect2>
b/qa/qa_cluster.py
151 151

  
152 152
  # Conflicting options
153 153
  cmd = ["gnt-cluster", "renew-crypto", "--force",
154
         "--new-cluster-certificate", "--new-confd-hmac-key",
155
         "--new-rapi-certificate", "--rapi-certificate=/dev/null"]
156
  AssertNotEqual(StartSSH(master["primary"],
157
                          utils.ShellQuoteArgs(cmd)).wait(), 0)
154
         "--new-cluster-certificate", "--new-confd-hmac-key"]
155
  conflicting = [
156
    ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
157
    ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
158
    ]
159
  for i in conflicting:
160
    AssertNotEqual(StartSSH(master["primary"],
161
                            utils.ShellQuoteArgs(cmd + i)).wait(), 0)
158 162

  
159 163
  # Invalid RAPI certificate
160 164
  cmd = ["gnt-cluster", "renew-crypto", "--force",
......
181 185
    AssertEqual(StartSSH(master["primary"],
182 186
                         utils.ShellQuoteArgs(cmd)).wait(), 0)
183 187

  
188
  # Custom cluster domain secret
189
  cds_fh = tempfile.NamedTemporaryFile()
190
  cds_fh.write(utils.GenerateSecret())
191
  cds_fh.write("\n")
192
  cds_fh.flush()
193

  
194
  tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
195
  try:
196
    cmd = ["gnt-cluster", "renew-crypto", "--force",
197
           "--cluster-domain-secret=%s" % tmpcds]
198
    AssertEqual(StartSSH(master["primary"],
199
                         utils.ShellQuoteArgs(cmd)).wait(), 0)
200
  finally:
201
    cmd = ["rm", "-f", tmpcds]
202
    AssertEqual(StartSSH(master["primary"],
203
                         utils.ShellQuoteArgs(cmd)).wait(), 0)
204

  
184 205
  # Normal case
185 206
  cmd = ["gnt-cluster", "renew-crypto", "--force",
186 207
         "--new-cluster-certificate", "--new-confd-hmac-key",
187
         "--new-rapi-certificate"]
208
         "--new-rapi-certificate", "--new-cluster-domain-secret"]
188 209
  AssertEqual(StartSSH(master["primary"],
189 210
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
190 211

  
b/scripts/gnt-cluster
495 495

  
496 496

  
497 497
def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename,
498
                 new_confd_hmac_key, force):
498
                 new_confd_hmac_key, new_cds, cds_filename,
499
                 force):
499 500
  """Renews cluster certificates, keys and secrets.
500 501

  
501 502
  @type new_cluster_cert: bool
......
506 507
  @param rapi_cert_filename: Path to file containing new RAPI certificate
507 508
  @type new_confd_hmac_key: bool
508 509
  @param new_confd_hmac_key: Whether to generate a new HMAC key
510
  @type new_cds: bool
511
  @param new_cds: Whether to generate a new cluster domain secret
512
  @type cds_filename: string
513
  @param cds_filename: Path to file containing new cluster domain secret
509 514
  @type force: bool
510 515
  @param force: Whether to ask user for confirmation
511 516

  
......
515 520
             " options can be specified at the same time.")
516 521
    return 1
517 522

  
523
  if new_cds and cds_filename:
524
    ToStderr("Only one of the --new-cluster-domain-secret and"
525
             " --cluster-domain-secret options can be specified at"
526
             " the same time.")
527
    return 1
528

  
518 529
  if rapi_cert_filename:
519 530
    # Read and verify new certificate
520 531
    try:
......
537 548
  else:
538 549
    rapi_cert_pem = None
539 550

  
551
  if cds_filename:
552
    try:
553
      cds = utils.ReadFile(cds_filename)
554
    except Exception, err: # pylint: disable-msg=W0703
555
      ToStderr("Can't load new cluster domain secret from %s: %s" %
556
               (cds_filename, str(err)))
557
      return 1
558
  else:
559
    cds = None
560

  
540 561
  if not force:
541 562
    usertext = ("This requires all daemons on all nodes to be restarted and"
542 563
                " may take some time. Continue?")
......
547 568
    ctx.feedback_fn("Updating certificates and keys")
548 569
    bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert,
549 570
                                    new_confd_hmac_key,
550
                                    rapi_cert_pem=rapi_cert_pem)
571
                                    new_cds,
572
                                    rapi_cert_pem=rapi_cert_pem,
573
                                    cds=cds)
551 574

  
552 575
    files_to_copy = []
553 576

  
......
560 583
    if new_confd_hmac_key:
561 584
      files_to_copy.append(constants.CONFD_HMAC_KEY)
562 585

  
586
    if new_cds or cds:
587
      files_to_copy.append(constants.CLUSTER_DOMAIN_SECRET_FILE)
588

  
563 589
    if files_to_copy:
564 590
      for node_name in ctx.nonmaster_nodes:
565 591
        ctx.feedback_fn("Copying %s to %s" %
......
583 609
                      opts.new_rapi_cert,
584 610
                      opts.rapi_cert,
585 611
                      opts.new_confd_hmac_key,
612
                      opts.new_cluster_domain_secret,
613
                      opts.cluster_domain_secret,
586 614
                      opts.force)
587 615

  
588 616

  
......
789 817
  "renew-crypto": (
790 818
    RenewCrypto, ARGS_NONE,
791 819
    [NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT,
792
     NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT],
820
     NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT,
821
     NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT],
793 822
    "[opts...]",
794 823
    "Renews cluster certificates, keys and secrets"),
795 824
  }
b/tools/cfgupgrade
174 174
                    backup=True)
175 175

  
176 176
    if not options.dry_run:
177
      bootstrap.GenerateClusterCrypto(False, False, False)
177
      bootstrap.GenerateClusterCrypto(False, False, False, False)
178 178

  
179 179
  except:
180 180
    logging.critical("Writing configuration failed. It is probably in an"

Also available in: Unified diff