Commit d5104ca4 authored by Helga Velroyen's avatar Helga Velroyen

Revert "Disabling client certificate usage"

This reverts commit 45f75526, which was introduced to
temporarily disable the implementation of SSL client
certificates. As this patch series fixes the reason for
the disabling, we are rolling back the patch.
Signed-off-by: default avatarHelga Velroyen <helgav@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent f398c9b9
......@@ -29,6 +29,20 @@ 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
-------------
......
......@@ -3189,6 +3189,7 @@ 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,
self._SSLVerifyCallback)
ssl_verify_callback)
# 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,6 +36,7 @@ import base64
import pycurl
import threading
import copy
import os
from ganeti import utils
from ganeti import objects
......@@ -97,15 +98,23 @@ 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_cert)
curl.setopt(pycurl.SSLCERT, noded_client_cert)
curl.setopt(pycurl.SSLKEYTYPE, "PEM")
curl.setopt(pycurl.SSLKEY, noded_cert)
curl.setopt(pycurl.SSLKEY, noded_client_cert)
curl.setopt(pycurl.CONNECTTIMEOUT, constants.RPC_CONNECT_TIMEOUT)
......
......@@ -766,7 +766,7 @@ RENEW-CRYPTO
~~~~~~~~~~~~
| **renew-crypto** [-f]
| [\--new-cluster-certificate]
| [\--new-cluster-certificate] | [\--new-node-certificates]
| [\--new-confd-hmac-key]
| [\--new-rapi-certificate] [\--rapi-certificate *rapi-cert*]
| [\--new-spice-certificate | \--spice-certificate *spice-cert*
......@@ -779,6 +779,11 @@ 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,6 +90,7 @@ 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
......@@ -228,8 +229,15 @@ getOptionsForCall cert_path client_cert_path call =
executeRpcCalls :: (Rpc a b) => [(Node, a)] -> IO [(Node, ERpcError b)]
executeRpcCalls nodeCalls = do
cert_file <- P.nodedCertFile
let (nodes, calls) = unzip nodeCalls
opts = map (getOptionsForCall cert_file cert_file) calls
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
opts_urls = zipWith3 (\n c o ->
case prepareHttpRequest o n c of
Left v -> Left v
......
......@@ -1085,6 +1085,140 @@ 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,9 +43,11 @@ def main():
version = utils.version.ParseVersion(versionstring)
if utils.version.IsBefore(version, 2, 11, 0):
# FIXME: Add client certificate handling here when resolving issue 692.
pass
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))
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