Commit 45f75526 authored by Helga Velroyen's avatar Helga Velroyen
Browse files

Disabling client certificate usage



This patch temporarily disables the usage of the client
SSL certificates. The handling of RPC connections had a
conceptional flaw, because the certificates lack a proper
signature. For this, Ganeti needs to implement a CA,
which is already designed (see design-x509-ca.rst) but
not implemented yet. This patch keeps most of the
client certificate infrastructure intact which was already
created and and can be reused, but just disables the
actual usage of the certificates in RPC calls till the CA
is in place.
Signed-off-by: default avatarHelga Velroyen <helgav@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent 658eb2dc
......@@ -29,20 +29,6 @@ This will carry out the steps described below in the section on upgrades from
way, specifiying the smaller version on the ``--to`` argument.
2.11
----
When upgrading to 2.11, first apply the instructions of ``2.11 and
above``. 2.11 comes with the new feature of enhanced RPC security
through client certificates. This features needs to be enabled after the
upgrade by::
$ gnt-cluster renew-crypto --new-node-certificates
Note that new node certificates are generated automatically without
warning when upgrading with ``gnt-cluster upgrade``.
2.1 and above
-------------
......
......@@ -3182,7 +3182,6 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
feedback_fn("* Verifying configuration file consistency")
self._VerifyClientCertificates(self.my_node_info.values(), all_nvinfo)
# If not all nodes are being checked, we need to make sure the master node
# and a non-checked vm_capable node are in the list.
absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
......
......@@ -610,7 +610,7 @@ class HttpBase(object):
if ssl_verify_peer:
ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
ssl_verify_callback)
self._SSLVerifyCallback)
# Also add our certificate as a trusted CA to be sent to the client.
# This is required at least for GnuTLS clients to work.
......
......@@ -36,7 +36,6 @@ import base64
import pycurl
import threading
import copy
import os
from ganeti import utils
from ganeti import objects
......@@ -98,23 +97,15 @@ def Shutdown():
def _ConfigRpcCurl(curl):
noded_cert = str(pathutils.NODED_CERT_FILE)
noded_client_cert = str(pathutils.NODED_CLIENT_CERT_FILE)
# FIXME: The next two lines are necessary to ensure upgradability from
# 2.10 to 2.11. Remove in 2.12, because this slows down RPC calls.
if not os.path.exists(noded_client_cert):
logging.info("Using server certificate as client certificate for RPC"
"call.")
noded_client_cert = noded_cert
curl.setopt(pycurl.FOLLOWLOCATION, False)
curl.setopt(pycurl.CAINFO, noded_cert)
curl.setopt(pycurl.SSL_VERIFYHOST, 0)
curl.setopt(pycurl.SSL_VERIFYPEER, True)
curl.setopt(pycurl.SSLCERTTYPE, "PEM")
curl.setopt(pycurl.SSLCERT, noded_client_cert)
curl.setopt(pycurl.SSLCERT, noded_cert)
curl.setopt(pycurl.SSLKEYTYPE, "PEM")
curl.setopt(pycurl.SSLKEY, noded_client_cert)
curl.setopt(pycurl.SSLKEY, noded_cert)
curl.setopt(pycurl.CONNECTTIMEOUT, constants.RPC_CONNECT_TIMEOUT)
......
......@@ -766,7 +766,7 @@ RENEW-CRYPTO
~~~~~~~~~~~~
| **renew-crypto** [-f]
| [\--new-cluster-certificate] | [\--new-node-certificates]
| [\--new-cluster-certificate]
| [\--new-confd-hmac-key]
| [\--new-rapi-certificate] [\--rapi-certificate *rapi-cert*]
| [\--new-spice-certificate | \--spice-certificate *spice-cert*
......@@ -779,11 +779,6 @@ options ``--new-cluster-certificate`` and ``--new-confd-hmac-key``
can be used to regenerate respectively the cluster-internal SSL
certificate and the HMAC key used by **ganeti-confd**\(8).
The option ``--new-node-certificates`` will generate new node SSL
certificates for all nodes. Note that the regeneration of the node
certificates takes place after the other certificates are created
and distributed and the ganeti daemons are restarted again.
To generate a new self-signed RAPI certificate (used by
**ganeti-rapi**\(8)) specify ``--new-rapi-certificate``. If you want to
use your own certificate, e.g. one signed by a certificate
......
......@@ -90,7 +90,6 @@ import Data.Maybe (fromMaybe)
import qualified Text.JSON as J
import Text.JSON.Pretty (pp_value)
import qualified Data.ByteString.Base64.Lazy as Base64
import System.Directory
import Network.Curl hiding (content)
import qualified Ganeti.Path as P
......@@ -229,15 +228,8 @@ getOptionsForCall cert_path client_cert_path call =
executeRpcCalls :: (Rpc a b) => [(Node, a)] -> IO [(Node, ERpcError b)]
executeRpcCalls nodeCalls = do
cert_file <- P.nodedCertFile
client_cert_file_name <- P.nodedClientCertFile
client_file_exists <- doesFileExist client_cert_file_name
-- FIXME: This is needed to ensure upgradability to 2.11
-- Remove in 2.12.
let client_cert_file = if client_file_exists
then client_cert_file_name
else cert_file
(nodes, calls) = unzip nodeCalls
opts = map (getOptionsForCall cert_file client_cert_file) calls
let (nodes, calls) = unzip nodeCalls
opts = map (getOptionsForCall cert_file cert_file) calls
opts_urls = zipWith3 (\n c o ->
case prepareHttpRequest o n c of
Left v -> Left v
......
......@@ -1085,140 +1085,6 @@ class TestLUClusterVerifyGroup(CmdlibTestCase):
self.ExecOpCode(op)
class TestLUClusterVerifyClientCerts(CmdlibTestCase):
def _AddNormalNode(self):
self.normalnode = copy.deepcopy(self.master)
self.normalnode.master_candidate = False
self.normalnode.uuid = "normal-node-uuid"
self.cfg.AddNode(self.normalnode, None)
def testVerifyMasterCandidate(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {self.master.uuid: client_cert}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
def testVerifyMasterCandidateInvalid(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {self.master.uuid: client_cert}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (666, "Invalid Certificate")}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("Client certificate")
self.mcpu.assertLogContainsRegex("failed validation")
def testVerifyNoMasterCandidateMap(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex(
"list of master candidate certificates is empty")
def testVerifyNoSharingMasterCandidates(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {
self.master.uuid: client_cert,
"some-other-master-candidate-uuid": client_cert}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex(
"two master candidates configured to use the same")
def testVerifyMasterCandidateCertMismatch(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {self.master.uuid: "different-cert-digest"}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("does not match its entry")
def testVerifyMasterCandidateUnregistered(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {"other-node-uuid": "different-cert-digest"}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("does not have an entry")
def testVerifyMasterCandidateOtherNodesCert(self):
client_cert = "client-cert-digest"
self.cluster.candidate_certs = {"other-node-uuid": client_cert}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("using a certificate of another node")
def testNormalNodeStillInList(self):
self._AddNormalNode()
client_cert_master = "client-cert-digest-master"
client_cert_normal = "client-cert-digest-normal"
self.cluster.candidate_certs = {
self.normalnode.uuid: client_cert_normal,
self.master.uuid: client_cert_master}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.normalnode,
{constants.NV_CLIENT_CERT: (None, client_cert_normal)}) \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("not a master candidate")
self.mcpu.assertLogContainsRegex("still listed")
def testNormalNodeStealingMasterCandidateCert(self):
self._AddNormalNode()
client_cert_master = "client-cert-digest-master"
self.cluster.candidate_certs = {
self.master.uuid: client_cert_master}
self.rpc.call_node_verify.return_value = \
RpcResultsBuilder() \
.AddSuccessfulNode(self.normalnode,
{constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
.AddSuccessfulNode(self.master,
{constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
.Build()
op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
self.ExecOpCode(op)
self.mcpu.assertLogContainsRegex("not a master candidate")
self.mcpu.assertLogContainsRegex(
"certificate of another node which is master candidate")
class TestLUClusterVerifyGroupMethods(CmdlibTestCase):
"""Base class for testing individual methods in LUClusterVerifyGroup.
......
......@@ -43,11 +43,9 @@ def main():
version = utils.version.ParseVersion(versionstring)
if utils.version.IsBefore(version, 2, 11, 0):
result = utils.RunCmd(["gnt-cluster", "renew-crypto",
"--new-node-certificates", "-f"])
if result.failed:
cli.ToStderr("Failed to create node certificates: %s; Output %s" %
(result.fail_reason, result.output))
# FIXME: Add client certificate handling here when resolving issue 692.
pass
return 0
if __name__ == "__main__":
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment