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