So far, the client certificate of a node that is added to the cluster was created in LUNodeAdd using an RPC call. This is now simplified by creating the certificate already in tools/node_daemon_setup.py and only retrieving its fingerprint by RPC to add it to the configuration.
This simplifies the backend function from only reading the fingerprint instead of creating the certificate. Signed-off-by: Helga Velroyen <[email protected]> --- lib/backend.py | 49 +++----------------------------------- lib/bootstrap.py | 1 + lib/cmdlib/cluster.py | 9 ++++--- lib/cmdlib/common.py | 9 ++++--- lib/cmdlib/node.py | 4 ++-- lib/tools/common.py | 2 ++ lib/tools/node_daemon_setup.py | 3 +++ src/Ganeti/Constants.hs | 6 +---- test/py/ganeti.backend_unittest.py | 26 -------------------- 9 files changed, 20 insertions(+), 89 deletions(-) diff --git a/lib/backend.py b/lib/backend.py index 402aae1..5830f67 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -1211,13 +1211,8 @@ def GetCryptoTokens(token_requests): @return: list of tuples of the token type and the public crypto token """ - getents = runtime.GetEnts() - _VALID_CERT_FILES = [pathutils.NODED_CERT_FILE, - pathutils.NODED_CLIENT_CERT_FILE, - pathutils.NODED_CLIENT_CERT_FILE_TMP] - _DEFAULT_CERT_FILE = pathutils.NODED_CLIENT_CERT_FILE tokens = [] - for (token_type, action, options) in token_requests: + for (token_type, action, _) in token_requests: if token_type not in constants.CRYPTO_TYPES: raise errors.ProgrammerError("Token type '%s' not supported." % token_type) @@ -1225,46 +1220,8 @@ def GetCryptoTokens(token_requests): raise errors.ProgrammerError("Action '%s' is not supported." % action) if token_type == constants.CRYPTO_TYPE_SSL_DIGEST: - if action == constants.CRYPTO_ACTION_CREATE: - - # extract file name from options - cert_filename = None - if options: - cert_filename = options.get(constants.CRYPTO_OPTION_CERT_FILE) - if not cert_filename: - cert_filename = _DEFAULT_CERT_FILE - # For security reason, we don't allow arbitrary filenames - if not cert_filename in _VALID_CERT_FILES: - raise errors.ProgrammerError( - "The certificate file name path '%s' is not allowed." % - cert_filename) - - # extract serial number from options - serial_no = None - if options: - try: - serial_no = int(options[constants.CRYPTO_OPTION_SERIAL_NO]) - except ValueError: - raise errors.ProgrammerError( - "The given serial number is not an intenger: %s." % - options.get(constants.CRYPTO_OPTION_SERIAL_NO)) - except KeyError: - raise errors.ProgrammerError("No serial number was provided.") - - if not serial_no: - raise errors.ProgrammerError( - "Cannot create an SSL certificate without a serial no.") - - utils.GenerateNewSslCert( - True, cert_filename, serial_no, - "Create new client SSL certificate in %s." % cert_filename, - uid=getents.masterd_uid, gid=getents.masterd_gid) - tokens.append((token_type, - utils.GetCertificateDigest( - cert_filename=cert_filename))) - elif action == constants.CRYPTO_ACTION_GET: - tokens.append((token_type, - utils.GetCertificateDigest())) + tokens.append((token_type, + utils.GetCertificateDigest())) return tokens diff --git a/lib/bootstrap.py b/lib/bootstrap.py index a9b1f68..fa6ae19 100644 --- a/lib/bootstrap.py +++ b/lib/bootstrap.py @@ -1010,6 +1010,7 @@ def SetupNodeDaemon(opts, cluster_name, node, ssh_port): utils.ReadFile(pathutils.NODED_CERT_FILE), constants.NDS_SSCONF: ssconf.SimpleStore().ReadAll(), constants.NDS_START_NODE_DAEMON: True, + constants.NDS_NODE_NAME: node, } RunNodeSetupCmd(cluster_name, node, pathutils.NODE_DAEMON_SETUP, diff --git a/lib/cmdlib/cluster.py b/lib/cmdlib/cluster.py index 0251525..5d09202 100644 --- a/lib/cmdlib/cluster.py +++ b/lib/cmdlib/cluster.py @@ -65,10 +65,9 @@ from ganeti.cmdlib.common import ShareAll, RunPostHook, \ CheckOSParams, CheckHVParams, AdjustCandidatePool, CheckNodePVs, \ ComputeIPolicyInstanceViolation, AnnotateDiskParams, SupportsOob, \ CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \ - CheckDiskAccessModeConsistency, CreateNewClientCert, \ + CheckDiskAccessModeConsistency, GetClientCertDigest, \ AddInstanceCommunicationNetworkOp, ConnectInstanceCommunicationNetworkOp, \ - CheckImageValidity, \ - CheckDiskAccessModeConsistency, CreateNewClientCert, EnsureKvmdOnNodes + CheckImageValidity, CheckDiskAccessModeConsistency, EnsureKvmdOnNodes import ganeti.masterd.instance @@ -93,7 +92,7 @@ def _UpdateMasterClientCert( @return: the digest of the newly created client certificate """ - client_digest = CreateNewClientCert(lu, master_uuid, filename=client_cert_tmp) + client_digest = GetClientCertDigest(lu, master_uuid, filename=client_cert_tmp) cfg.AddNodeToCandidateCerts(master_uuid, client_digest) # This triggers an update of the config and distribution of it with the old # SSL certificate @@ -188,7 +187,7 @@ class LUClusterRenewCrypto(NoHooksLU): last_exception = None for i in range(self._MAX_NUM_RETRIES): try: - new_digest = CreateNewClientCert(self, node_uuid) + new_digest = GetClientCertDigest(self, node_uuid) if node_info.master_candidate: self.cfg.AddNodeToCandidateCerts(node_uuid, new_digest) diff --git a/lib/cmdlib/common.py b/lib/cmdlib/common.py index e2eb909..4298e6c 100644 --- a/lib/cmdlib/common.py +++ b/lib/cmdlib/common.py @@ -1366,8 +1366,8 @@ def RemoveNodeCertFromCandidateCerts(cfg, node_uuid): cfg.RemoveNodeFromCandidateCerts(node_uuid) -def CreateNewClientCert(lu, node_uuid, filename=None): - """Creates a new client SSL certificate for the node. +def GetClientCertDigest(lu, node_uuid, filename=None): + """Get the client SSL certificate digest for the node. @type node_uuid: string @param node_uuid: the node's UUID @@ -1380,13 +1380,12 @@ def CreateNewClientCert(lu, node_uuid, filename=None): options = {} if filename: options[constants.CRYPTO_OPTION_CERT_FILE] = filename - options[constants.CRYPTO_OPTION_SERIAL_NO] = utils.UuidToInt(node_uuid) result = lu.rpc.call_node_crypto_tokens( node_uuid, [(constants.CRYPTO_TYPE_SSL_DIGEST, - constants.CRYPTO_ACTION_CREATE, + constants.CRYPTO_ACTION_GET, options)]) - result.Raise("Could not create the node's (uuid %s) SSL client" + result.Raise("Could not fetch the node's (uuid %s) SSL client" " certificate." % node_uuid) ((crypto_type, new_digest), ) = result.payload assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST diff --git a/lib/cmdlib/node.py b/lib/cmdlib/node.py index 1b13aa9..2f83c73 100644 --- a/lib/cmdlib/node.py +++ b/lib/cmdlib/node.py @@ -51,7 +51,7 @@ from ganeti.cmdlib.common import CheckParamsNotGlobal, \ CheckInstanceState, INSTANCE_DOWN, GetUpdatedParams, \ AdjustCandidatePool, CheckIAllocatorOrNode, LoadNodeEvacResult, \ GetWantedNodes, MapInstanceLvsToNodes, RunPostHook, \ - FindFaultyInstanceDisks, CheckStorageTypeEnabled, CreateNewClientCert, \ + FindFaultyInstanceDisks, CheckStorageTypeEnabled, GetClientCertDigest, \ AddNodeCertToCandidateCerts, RemoveNodeCertFromCandidateCerts, \ EnsureKvmdOnNodes @@ -431,7 +431,7 @@ class LUNodeAdd(LogicalUnit): RedistributeAncillaryFiles(self) # We create a new certificate even if the node is readded - digest = CreateNewClientCert(self, self.new_node.uuid) + digest = GetClientCertDigest(self, self.new_node.uuid) if self.new_node.master_candidate: self.cfg.AddNodeToCandidateCerts(self.new_node.uuid, digest) else: diff --git a/lib/tools/common.py b/lib/tools/common.py index 547c072..b48b1ee 100644 --- a/lib/tools/common.py +++ b/lib/tools/common.py @@ -165,6 +165,8 @@ def GenerateClientCertificate( # The hostname of the node is provided with the input data. hostname = data.get(constants.NDS_NODE_NAME) + if not hostname: + raise error_fn("No hostname found.") utils.GenerateSignedSslCert(client_cert, serial_no, signing_cert, common_name=hostname) diff --git a/lib/tools/node_daemon_setup.py b/lib/tools/node_daemon_setup.py index 3eec177..89e8a18 100644 --- a/lib/tools/node_daemon_setup.py +++ b/lib/tools/node_daemon_setup.py @@ -48,6 +48,7 @@ from ganeti import serializer from ganeti import runtime from ganeti import ht from ganeti import ssconf +from ganeti.tools import common _DATA_CHECK = ht.TStrictDict(False, True, { @@ -55,6 +56,7 @@ _DATA_CHECK = ht.TStrictDict(False, True, { constants.NDS_NODE_DAEMON_CERTIFICATE: ht.TNonEmptyString, constants.NDS_SSCONF: ht.TDictOf(ht.TNonEmptyString, ht.TString), constants.NDS_START_NODE_DAEMON: ht.TBool, + constants.NDS_NODE_NAME: ht.TString, }) @@ -227,6 +229,7 @@ def Main(): mode=pathutils.NODED_CERT_MODE, uid=getent.masterd_uid, gid=getent.masterd_gid, dry_run=opts.dry_run) + common.GenerateClientCertificate(data, SetupError) if (data.get(constants.NDS_START_NODE_DAEMON) and # pylint: disable=E1103 not opts.dry_run): diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs index 9428780..77b0949 100644 --- a/src/Ganeti/Constants.hs +++ b/src/Ganeti/Constants.hs @@ -4386,12 +4386,8 @@ cryptoTypes = ConstantUtils.mkSet [cryptoTypeSslDigest] cryptoActionGet :: String cryptoActionGet = "get" --- This is 'create and get' -cryptoActionCreate :: String -cryptoActionCreate = "create" - cryptoActions :: FrozenSet String -cryptoActions = ConstantUtils.mkSet [cryptoActionGet, cryptoActionCreate] +cryptoActions = ConstantUtils.mkSet [cryptoActionGet] -- * Options for CryptoActions diff --git a/test/py/ganeti.backend_unittest.py b/test/py/ganeti.backend_unittest.py index 2e33993..6a3346d 100755 --- a/test/py/ganeti.backend_unittest.py +++ b/test/py/ganeti.backend_unittest.py @@ -105,32 +105,6 @@ class TestGetCryptoTokens(testutils.GanetiTestCase): self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) in result) - def testCreateSslToken(self): - result = backend.GetCryptoTokens( - [(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, - {constants.CRYPTO_OPTION_SERIAL_NO: 42})]) - self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) - in result) - self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) - - def testCreateSslTokenDifferentFilename(self): - result = backend.GetCryptoTokens( - [(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, - {constants.CRYPTO_OPTION_CERT_FILE: - pathutils.NODED_CLIENT_CERT_FILE_TMP, - constants.CRYPTO_OPTION_SERIAL_NO: 42})]) - self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) - in result) - self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) - - def testCreateSslTokenSerialNo(self): - result = backend.GetCryptoTokens( - [(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, - {constants.CRYPTO_OPTION_SERIAL_NO: 42})]) - self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) - in result) - self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) - def testUnknownTokenType(self): self.assertRaises(errors.ProgrammerError, backend.GetCryptoTokens, -- 2.4.3.573.g4eafbef
