Revision 45f75526

b/UPGRADE
29 29
way, specifiying the smaller version on the ``--to`` argument.
30 30

  
31 31

  
32
2.11
33
----
34

  
35
When upgrading to 2.11, first apply the instructions of ``2.11 and
36
above``. 2.11 comes with the new feature of enhanced RPC security
37
through client certificates. This features needs to be enabled after the
38
upgrade by::
39

  
40
   $ gnt-cluster renew-crypto --new-node-certificates
41

  
42
Note that new node certificates are generated automatically without
43
warning when upgrading with ``gnt-cluster upgrade``.
44

  
45

  
46 32
2.1 and above
47 33
-------------
48 34

  
b/lib/cmdlib/cluster.py
3182 3182

  
3183 3183
    feedback_fn("* Verifying configuration file consistency")
3184 3184

  
3185
    self._VerifyClientCertificates(self.my_node_info.values(), all_nvinfo)
3186 3185
    # If not all nodes are being checked, we need to make sure the master node
3187 3186
    # and a non-checked vm_capable node are in the list.
3188 3187
    absent_node_uuids = set(self.all_node_info).difference(self.my_node_info)
b/lib/http/__init__.py
610 610
    if ssl_verify_peer:
611 611
      ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
612 612
                     OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
613
                     ssl_verify_callback)
613
                     self._SSLVerifyCallback)
614 614

  
615 615
      # Also add our certificate as a trusted CA to be sent to the client.
616 616
      # This is required at least for GnuTLS clients to work.
b/lib/rpc/node.py
36 36
import pycurl
37 37
import threading
38 38
import copy
39
import os
40 39

  
41 40
from ganeti import utils
42 41
from ganeti import objects
......
98 97

  
99 98
def _ConfigRpcCurl(curl):
100 99
  noded_cert = str(pathutils.NODED_CERT_FILE)
101
  noded_client_cert = str(pathutils.NODED_CLIENT_CERT_FILE)
102

  
103
  # FIXME: The next two lines are necessary to ensure upgradability from
104
  # 2.10 to 2.11. Remove in 2.12, because this slows down RPC calls.
105
  if not os.path.exists(noded_client_cert):
106
    logging.info("Using server certificate as client certificate for RPC"
107
                 "call.")
108
    noded_client_cert = noded_cert
109 100

  
110 101
  curl.setopt(pycurl.FOLLOWLOCATION, False)
111 102
  curl.setopt(pycurl.CAINFO, noded_cert)
112 103
  curl.setopt(pycurl.SSL_VERIFYHOST, 0)
113 104
  curl.setopt(pycurl.SSL_VERIFYPEER, True)
114 105
  curl.setopt(pycurl.SSLCERTTYPE, "PEM")
115
  curl.setopt(pycurl.SSLCERT, noded_client_cert)
106
  curl.setopt(pycurl.SSLCERT, noded_cert)
116 107
  curl.setopt(pycurl.SSLKEYTYPE, "PEM")
117
  curl.setopt(pycurl.SSLKEY, noded_client_cert)
108
  curl.setopt(pycurl.SSLKEY, noded_cert)
118 109
  curl.setopt(pycurl.CONNECTTIMEOUT, constants.RPC_CONNECT_TIMEOUT)
119 110

  
120 111

  
b/man/gnt-cluster.rst
766 766
~~~~~~~~~~~~
767 767

  
768 768
| **renew-crypto** [-f]
769
| [\--new-cluster-certificate] | [\--new-node-certificates]
769
| [\--new-cluster-certificate]
770 770
| [\--new-confd-hmac-key]
771 771
| [\--new-rapi-certificate] [\--rapi-certificate *rapi-cert*]
772 772
| [\--new-spice-certificate | \--spice-certificate *spice-cert*
......
779 779
can be used to regenerate respectively the cluster-internal SSL
780 780
certificate and the HMAC key used by **ganeti-confd**\(8).
781 781

  
782
The option ``--new-node-certificates`` will generate new node SSL
783
certificates for all nodes. Note that the regeneration of the node
784
certificates takes place after the other certificates are created
785
and distributed and the ganeti daemons are restarted again.
786

  
787 782
To generate a new self-signed RAPI certificate (used by
788 783
**ganeti-rapi**\(8)) specify ``--new-rapi-certificate``. If you want to
789 784
use your own certificate, e.g. one signed by a certificate
b/src/Ganeti/Rpc.hs
90 90
import qualified Text.JSON as J
91 91
import Text.JSON.Pretty (pp_value)
92 92
import qualified Data.ByteString.Base64.Lazy as Base64
93
import System.Directory
94 93

  
95 94
import Network.Curl hiding (content)
96 95
import qualified Ganeti.Path as P
......
229 228
executeRpcCalls :: (Rpc a b) => [(Node, a)] -> IO [(Node, ERpcError b)]
230 229
executeRpcCalls nodeCalls = do
231 230
  cert_file <- P.nodedCertFile
232
  client_cert_file_name <- P.nodedClientCertFile
233
  client_file_exists <- doesFileExist client_cert_file_name
234
  -- FIXME: This is needed to ensure upgradability to 2.11
235
  -- Remove in 2.12.
236
  let client_cert_file = if client_file_exists
237
                         then client_cert_file_name
238
                         else cert_file
239
      (nodes, calls) = unzip nodeCalls
240
      opts = map (getOptionsForCall cert_file client_cert_file) calls
231
  let (nodes, calls) = unzip nodeCalls
232
      opts = map (getOptionsForCall cert_file cert_file) calls
241 233
      opts_urls = zipWith3 (\n c o ->
242 234
                         case prepareHttpRequest o n c of
243 235
                           Left v -> Left v
b/test/py/cmdlib/cluster_unittest.py
1085 1085
    self.ExecOpCode(op)
1086 1086

  
1087 1087

  
1088
class TestLUClusterVerifyClientCerts(CmdlibTestCase):
1089

  
1090
  def _AddNormalNode(self):
1091
    self.normalnode = copy.deepcopy(self.master)
1092
    self.normalnode.master_candidate = False
1093
    self.normalnode.uuid = "normal-node-uuid"
1094
    self.cfg.AddNode(self.normalnode, None)
1095

  
1096
  def testVerifyMasterCandidate(self):
1097
    client_cert = "client-cert-digest"
1098
    self.cluster.candidate_certs = {self.master.uuid: client_cert}
1099
    self.rpc.call_node_verify.return_value = \
1100
      RpcResultsBuilder() \
1101
        .AddSuccessfulNode(self.master,
1102
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1103
        .Build()
1104
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1105
    self.ExecOpCode(op)
1106

  
1107
  def testVerifyMasterCandidateInvalid(self):
1108
    client_cert = "client-cert-digest"
1109
    self.cluster.candidate_certs = {self.master.uuid: client_cert}
1110
    self.rpc.call_node_verify.return_value = \
1111
      RpcResultsBuilder() \
1112
        .AddSuccessfulNode(self.master,
1113
          {constants.NV_CLIENT_CERT: (666, "Invalid Certificate")}) \
1114
        .Build()
1115
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1116
    self.ExecOpCode(op)
1117
    self.mcpu.assertLogContainsRegex("Client certificate")
1118
    self.mcpu.assertLogContainsRegex("failed validation")
1119

  
1120
  def testVerifyNoMasterCandidateMap(self):
1121
    client_cert = "client-cert-digest"
1122
    self.cluster.candidate_certs = {}
1123
    self.rpc.call_node_verify.return_value = \
1124
      RpcResultsBuilder() \
1125
        .AddSuccessfulNode(self.master,
1126
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1127
        .Build()
1128
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1129
    self.ExecOpCode(op)
1130
    self.mcpu.assertLogContainsRegex(
1131
      "list of master candidate certificates is empty")
1132

  
1133
  def testVerifyNoSharingMasterCandidates(self):
1134
    client_cert = "client-cert-digest"
1135
    self.cluster.candidate_certs = {
1136
      self.master.uuid: client_cert,
1137
      "some-other-master-candidate-uuid": client_cert}
1138
    self.rpc.call_node_verify.return_value = \
1139
      RpcResultsBuilder() \
1140
        .AddSuccessfulNode(self.master,
1141
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1142
        .Build()
1143
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1144
    self.ExecOpCode(op)
1145
    self.mcpu.assertLogContainsRegex(
1146
      "two master candidates configured to use the same")
1147

  
1148
  def testVerifyMasterCandidateCertMismatch(self):
1149
    client_cert = "client-cert-digest"
1150
    self.cluster.candidate_certs = {self.master.uuid: "different-cert-digest"}
1151
    self.rpc.call_node_verify.return_value = \
1152
      RpcResultsBuilder() \
1153
        .AddSuccessfulNode(self.master,
1154
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1155
        .Build()
1156
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1157
    self.ExecOpCode(op)
1158
    self.mcpu.assertLogContainsRegex("does not match its entry")
1159

  
1160
  def testVerifyMasterCandidateUnregistered(self):
1161
    client_cert = "client-cert-digest"
1162
    self.cluster.candidate_certs = {"other-node-uuid": "different-cert-digest"}
1163
    self.rpc.call_node_verify.return_value = \
1164
      RpcResultsBuilder() \
1165
        .AddSuccessfulNode(self.master,
1166
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1167
        .Build()
1168
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1169
    self.ExecOpCode(op)
1170
    self.mcpu.assertLogContainsRegex("does not have an entry")
1171

  
1172
  def testVerifyMasterCandidateOtherNodesCert(self):
1173
    client_cert = "client-cert-digest"
1174
    self.cluster.candidate_certs = {"other-node-uuid": client_cert}
1175
    self.rpc.call_node_verify.return_value = \
1176
      RpcResultsBuilder() \
1177
        .AddSuccessfulNode(self.master,
1178
          {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1179
        .Build()
1180
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1181
    self.ExecOpCode(op)
1182
    self.mcpu.assertLogContainsRegex("using a certificate of another node")
1183

  
1184
  def testNormalNodeStillInList(self):
1185
    self._AddNormalNode()
1186
    client_cert_master = "client-cert-digest-master"
1187
    client_cert_normal = "client-cert-digest-normal"
1188
    self.cluster.candidate_certs = {
1189
      self.normalnode.uuid: client_cert_normal,
1190
      self.master.uuid: client_cert_master}
1191
    self.rpc.call_node_verify.return_value = \
1192
      RpcResultsBuilder() \
1193
        .AddSuccessfulNode(self.normalnode,
1194
          {constants.NV_CLIENT_CERT: (None, client_cert_normal)}) \
1195
        .AddSuccessfulNode(self.master,
1196
          {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1197
        .Build()
1198
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1199
    self.ExecOpCode(op)
1200
    self.mcpu.assertLogContainsRegex("not a master candidate")
1201
    self.mcpu.assertLogContainsRegex("still listed")
1202

  
1203
  def testNormalNodeStealingMasterCandidateCert(self):
1204
    self._AddNormalNode()
1205
    client_cert_master = "client-cert-digest-master"
1206
    self.cluster.candidate_certs = {
1207
      self.master.uuid: client_cert_master}
1208
    self.rpc.call_node_verify.return_value = \
1209
      RpcResultsBuilder() \
1210
        .AddSuccessfulNode(self.normalnode,
1211
          {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1212
        .AddSuccessfulNode(self.master,
1213
          {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1214
        .Build()
1215
    op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1216
    self.ExecOpCode(op)
1217
    self.mcpu.assertLogContainsRegex("not a master candidate")
1218
    self.mcpu.assertLogContainsRegex(
1219
      "certificate of another node which is master candidate")
1220

  
1221

  
1222 1088
class TestLUClusterVerifyGroupMethods(CmdlibTestCase):
1223 1089
  """Base class for testing individual methods in LUClusterVerifyGroup.
1224 1090

  
b/tools/post-upgrade
43 43
  version = utils.version.ParseVersion(versionstring)
44 44

  
45 45
  if utils.version.IsBefore(version, 2, 11, 0):
46
    result = utils.RunCmd(["gnt-cluster", "renew-crypto",
47
                           "--new-node-certificates", "-f"])
48
    if result.failed:
49
      cli.ToStderr("Failed to create node certificates: %s; Output %s" %
50
                   (result.fail_reason, result.output))
46
    # FIXME: Add client certificate handling here when resolving issue 692.
47
    pass
48

  
51 49
  return 0
52 50

  
53 51
if __name__ == "__main__":

Also available in: Unified diff