def GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, new_confd_hmac_key,
- rapi_cert_pem=None):
+ new_cds, rapi_cert_pem=None, cds=None):
"""Updates the cluster certificates, keys and secrets.
@type new_cluster_cert: bool
@param new_rapi_cert: Whether to generate a new RAPI certificate
@type new_confd_hmac_key: bool
@param new_confd_hmac_key: Whether to generate a new HMAC key
+ @type new_cds: bool
+ @param new_cds: Whether to generate a new cluster domain secret
@type rapi_cert_pem: string
@param rapi_cert_pem: New RAPI certificate in PEM format
+ @type cds: string
+ @param cds: New cluster domain secret
"""
# noded SSL certificate
constants.RAPI_CERT_FILE)
utils.GenerateSelfSignedSslCert(constants.RAPI_CERT_FILE)
+ # Cluster domain secret
+ if cds:
+ logging.debug("Writing cluster domain secret to %s",
+ constants.CLUSTER_DOMAIN_SECRET_FILE)
+ utils.WriteFile(constants.CLUSTER_DOMAIN_SECRET_FILE,
+ data=cds, backup=True)
+
+ elif new_cds or not os.path.exists(constants.CLUSTER_DOMAIN_SECRET_FILE):
+ logging.debug("Generating new cluster domain secret at %s",
+ constants.CLUSTER_DOMAIN_SECRET_FILE)
+ GenerateHmacKey(constants.CLUSTER_DOMAIN_SECRET_FILE)
+
def _InitGanetiServerSetup(master_name):
"""Setup the necessary configuration for the initial node daemon.
"""
# Generate cluster secrets
- GenerateClusterCrypto(True, False, False)
+ GenerateClusterCrypto(True, False, False, False)
result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
if result.failed:
# and then connect with ssh to set password and start ganeti-noded
# note that all the below variables are sanitized at this point,
# either by being constants or by the checks above
+ # TODO: Could this command exceed a shell's maximum command length?
mycommand = ("umask 077 && "
"cat > '%s' << '!EOF.' && \n"
"%s!EOF.\n"
"AUTO_REPLACE_OPT",
"BACKEND_OPT",
"CLEANUP_OPT",
+ "CLUSTER_DOMAIN_SECRET_OPT",
"CONFIRM_OPT",
"CP_SIZE_OPT",
"DEBUG_OPT",
"MC_OPT",
"NET_OPT",
"NEW_CLUSTER_CERT_OPT",
+ "NEW_CLUSTER_DOMAIN_SECRET_OPT",
"NEW_CONFD_HMAC_KEY_OPT",
"NEW_RAPI_CERT_OPT",
"NEW_SECONDARY_OPT",
metavar="NETDEV",
default=constants.DEFAULT_BRIDGE)
-
GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
help="Specify the default directory (cluster-"
"wide) for storing the file-based disks [%s]" %
help=("Create a new HMAC key for %s" %
constants.CONFD))
+CLUSTER_DOMAIN_SECRET_OPT = cli_option("--cluster-domain-secret",
+ dest="cluster_domain_secret",
+ default=None,
+ help=("Load new new cluster domain"
+ " secret from file"))
+
+NEW_CLUSTER_DOMAIN_SECRET_OPT = cli_option("--new-cluster-domain-secret",
+ dest="new_cluster_domain_secret",
+ default=False, action="store_true",
+ help=("Create a new cluster domain"
+ " secret"))
+
def _ParseArgs(argv, commands, aliases):
"""Parser for the command line arguments.
NODED_CERT_FILE = DATA_DIR + "/server.pem"
RAPI_CERT_FILE = DATA_DIR + "/rapi.pem"
CONFD_HMAC_KEY = DATA_DIR + "/hmac.key"
+CLUSTER_DOMAIN_SECRET_FILE = DATA_DIR + "/cluster-domain-secret"
WATCHER_STATEFILE = DATA_DIR + "/watcher.data"
WATCHER_PAUSEFILE = DATA_DIR + "/watcher.pause"
INSTANCE_UPFILE = RUN_GANETI_DIR + "/instance-status"
<sbr>
<arg choice="opt">--new-rapi-certificate</arg>
<arg choice="opt">--rapi-certificate <replaceable>rapi-cert</replaceable></arg>
+ <sbr>
+ <arg choice="opt">--new-cluster-domain-secret</arg>
+ <arg choice="opt">--cluster-domain-secret <replaceable>filename</replaceable></arg>
</cmdsynopsis>
<para>
cluster-internal SSL certificate respective the HMAC key used by
<citerefentry>
<refentrytitle>ganeti-confd</refentrytitle><manvolnum>8</manvolnum>
- </citerefentry>. To generate a new self-signed RAPI certificate (used
- by <citerefentry>
+ </citerefentry>.
+ </para>
+
+ <para>
+ To generate a new self-signed RAPI certificate (used by <citerefentry>
<refentrytitle>ganeti-rapi</refentrytitle><manvolnum>8</manvolnum>
</citerefentry>) specify <option>--new-rapi-certificate</option>. If
you want to use your own certificate, e.g. one signed by a certificate
authority (CA), pass its filename to
<option>--rapi-certificate</option>.
</para>
+
+ <para>
+ <option>--new-cluster-domain-secret</option> generates a new, random
+ cluster domain secret. <option>--cluster-domain-secret</option> reads
+ the secret from a file. The cluster domain secret is used to sign
+ information exchanged between separate clusters via a third party.
+ </para>
</refsect2>
<refsect2>
# Conflicting options
cmd = ["gnt-cluster", "renew-crypto", "--force",
- "--new-cluster-certificate", "--new-confd-hmac-key",
- "--new-rapi-certificate", "--rapi-certificate=/dev/null"]
- AssertNotEqual(StartSSH(master["primary"],
- utils.ShellQuoteArgs(cmd)).wait(), 0)
+ "--new-cluster-certificate", "--new-confd-hmac-key"]
+ conflicting = [
+ ["--new-rapi-certificate", "--rapi-certificate=/dev/null"],
+ ["--new-cluster-domain-secret", "--cluster-domain-secret=/dev/null"],
+ ]
+ for i in conflicting:
+ AssertNotEqual(StartSSH(master["primary"],
+ utils.ShellQuoteArgs(cmd + i)).wait(), 0)
# Invalid RAPI certificate
cmd = ["gnt-cluster", "renew-crypto", "--force",
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
+ # Custom cluster domain secret
+ cds_fh = tempfile.NamedTemporaryFile()
+ cds_fh.write(utils.GenerateSecret())
+ cds_fh.write("\n")
+ cds_fh.flush()
+
+ tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
+ try:
+ cmd = ["gnt-cluster", "renew-crypto", "--force",
+ "--cluster-domain-secret=%s" % tmpcds]
+ AssertEqual(StartSSH(master["primary"],
+ utils.ShellQuoteArgs(cmd)).wait(), 0)
+ finally:
+ cmd = ["rm", "-f", tmpcds]
+ AssertEqual(StartSSH(master["primary"],
+ utils.ShellQuoteArgs(cmd)).wait(), 0)
+
# Normal case
cmd = ["gnt-cluster", "renew-crypto", "--force",
"--new-cluster-certificate", "--new-confd-hmac-key",
- "--new-rapi-certificate"]
+ "--new-rapi-certificate", "--new-cluster-domain-secret"]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename,
- new_confd_hmac_key, force):
+ new_confd_hmac_key, new_cds, cds_filename,
+ force):
"""Renews cluster certificates, keys and secrets.
@type new_cluster_cert: bool
@param rapi_cert_filename: Path to file containing new RAPI certificate
@type new_confd_hmac_key: bool
@param new_confd_hmac_key: Whether to generate a new HMAC key
+ @type new_cds: bool
+ @param new_cds: Whether to generate a new cluster domain secret
+ @type cds_filename: string
+ @param cds_filename: Path to file containing new cluster domain secret
@type force: bool
@param force: Whether to ask user for confirmation
" options can be specified at the same time.")
return 1
+ if new_cds and cds_filename:
+ ToStderr("Only one of the --new-cluster-domain-secret and"
+ " --cluster-domain-secret options can be specified at"
+ " the same time.")
+ return 1
+
if rapi_cert_filename:
# Read and verify new certificate
try:
else:
rapi_cert_pem = None
+ if cds_filename:
+ try:
+ cds = utils.ReadFile(cds_filename)
+ except Exception, err: # pylint: disable-msg=W0703
+ ToStderr("Can't load new cluster domain secret from %s: %s" %
+ (cds_filename, str(err)))
+ return 1
+ else:
+ cds = None
+
if not force:
usertext = ("This requires all daemons on all nodes to be restarted and"
" may take some time. Continue?")
ctx.feedback_fn("Updating certificates and keys")
bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert,
new_confd_hmac_key,
- rapi_cert_pem=rapi_cert_pem)
+ new_cds,
+ rapi_cert_pem=rapi_cert_pem,
+ cds=cds)
files_to_copy = []
if new_confd_hmac_key:
files_to_copy.append(constants.CONFD_HMAC_KEY)
+ if new_cds or cds:
+ files_to_copy.append(constants.CLUSTER_DOMAIN_SECRET_FILE)
+
if files_to_copy:
for node_name in ctx.nonmaster_nodes:
ctx.feedback_fn("Copying %s to %s" %
opts.new_rapi_cert,
opts.rapi_cert,
opts.new_confd_hmac_key,
+ opts.new_cluster_domain_secret,
+ opts.cluster_domain_secret,
opts.force)
"renew-crypto": (
RenewCrypto, ARGS_NONE,
[NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT,
- NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT],
+ NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT,
+ NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT],
"[opts...]",
"Renews cluster certificates, keys and secrets"),
}
backup=True)
if not options.dry_run:
- bootstrap.GenerateClusterCrypto(False, False, False)
+ bootstrap.GenerateClusterCrypto(False, False, False, False)
except:
logging.critical("Writing configuration failed. It is probably in an"