Revision d722af8b
b/lib/backend.py | ||
---|---|---|
1162 | 1162 |
return result |
1163 | 1163 |
|
1164 | 1164 |
|
1165 |
def GetCryptoTokens(token_types): |
|
1166 |
"""Get the node's public cryptographic tokens. |
|
1167 |
|
|
1168 |
This can be the public ssh key of the node or the certificate digest of |
|
1169 |
the node's public client SSL certificate. |
|
1170 |
|
|
1171 |
Note: so far, only retrieval of the SSL digest is implemented |
|
1172 |
|
|
1173 |
@type token_types: list of strings in constants.CRYPTO_TYPES |
|
1174 |
@param token_types: list of types of requested cryptographic tokens |
|
1165 |
def GetCryptoTokens(token_requests): |
|
1166 |
"""Perform actions on the node's cryptographic tokens. |
|
1167 |
|
|
1168 |
Token types can be 'ssl' or 'ssh'. So far only some actions are implemented |
|
1169 |
for 'ssl'. Action 'get' returns the digest of the public client ssl |
|
1170 |
certificate. Action 'create' creates a new client certificate and private key |
|
1171 |
and also returns the digest of the certificate. The third parameter of a |
|
1172 |
token request are optional parameters for the actions, so far only the |
|
1173 |
filename is supported. |
|
1174 |
|
|
1175 |
@type token_requests: list of tuples of (string, string, dict), where the |
|
1176 |
first string is in constants.CRYPTO_TYPES, the second in |
|
1177 |
constants.CRYPTO_ACTIONS. The third parameter is a dictionary of string |
|
1178 |
to string. |
|
1179 |
@param token_requests: list of requests of cryptographic tokens and actions |
|
1180 |
to perform on them. The actions come with a dictionary of options. |
|
1175 | 1181 |
@rtype: list of tuples (string, string) |
1176 | 1182 |
@return: list of tuples of the token type and the public crypto token |
1177 | 1183 |
|
1178 | 1184 |
""" |
1185 |
_VALID_CERT_FILES = [pathutils.NODED_CERT_FILE, |
|
1186 |
pathutils.NODED_CLIENT_CERT_FILE, |
|
1187 |
pathutils.NODED_CLIENT_CERT_FILE_TMP] |
|
1188 |
_DEFAULT_CERT_FILE = pathutils.NODED_CLIENT_CERT_FILE |
|
1179 | 1189 |
tokens = [] |
1180 |
for token_type in token_types:
|
|
1190 |
for (token_type, action, options) in token_requests:
|
|
1181 | 1191 |
if token_type not in constants.CRYPTO_TYPES: |
1182 |
raise errors.ProgrammerError("Token type %s not supported." %
|
|
1192 |
raise errors.ProgrammerError("Token type '%s' not supported." %
|
|
1183 | 1193 |
token_type) |
1194 |
if action not in constants.CRYPTO_ACTIONS: |
|
1195 |
raise errors.ProgrammerError("Action '%s' is not supported." % |
|
1196 |
action) |
|
1184 | 1197 |
if token_type == constants.CRYPTO_TYPE_SSL_DIGEST: |
1185 |
tokens.append((token_type, utils.GetClientCertificateDigest())) |
|
1198 |
if action == constants.CRYPTO_ACTION_CREATE: |
|
1199 |
cert_filename = None |
|
1200 |
if options: |
|
1201 |
cert_filename = options.get(constants.CRYPTO_OPTION_CERT_FILE) |
|
1202 |
if not cert_filename: |
|
1203 |
cert_filename = _DEFAULT_CERT_FILE |
|
1204 |
# For security reason, we don't allow arbitrary filenames |
|
1205 |
if not cert_filename in _VALID_CERT_FILES: |
|
1206 |
raise errors.ProgrammerError( |
|
1207 |
"The certificate file name path '%s' is not allowed." % |
|
1208 |
cert_filename) |
|
1209 |
utils.GenerateNewSslCert( |
|
1210 |
True, cert_filename, |
|
1211 |
"Create new client SSL certificate in %s." % cert_filename) |
|
1212 |
tokens.append((token_type, |
|
1213 |
utils.GetClientCertificateDigest( |
|
1214 |
cert_filename=cert_filename))) |
|
1215 |
elif action == constants.CRYPTO_ACTION_GET: |
|
1216 |
tokens.append((token_type, |
|
1217 |
utils.GetClientCertificateDigest())) |
|
1186 | 1218 |
return tokens |
1187 | 1219 |
|
1188 | 1220 |
|
b/lib/cmdlib/common.py | ||
---|---|---|
1228 | 1228 |
|
1229 | 1229 |
""" |
1230 | 1230 |
result = lu.rpc.call_node_crypto_tokens( |
1231 |
node_uuid, [constants.CRYPTO_TYPE_SSL_DIGEST]) |
|
1231 |
node_uuid, |
|
1232 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_GET, |
|
1233 |
None)]) |
|
1232 | 1234 |
result.Raise("Could not retrieve the node's (uuid %s) SSL digest." |
1233 | 1235 |
% node_uuid) |
1234 | 1236 |
((crypto_type, digest), ) = result.payload |
b/lib/pathutils.py | ||
---|---|---|
107 | 107 |
#: Node daemon certificate path |
108 | 108 |
NODED_CERT_FILE = DATA_DIR + "/server.pem" |
109 | 109 |
NODED_CLIENT_CERT_FILE = DATA_DIR + "/client.pem" |
110 |
NODED_CLIENT_CERT_FILE_TMP = DATA_DIR + "/client.pem.tmp" |
|
110 | 111 |
|
111 | 112 |
#: Node daemon certificate file permissions |
112 | 113 |
NODED_CERT_MODE = 0440 |
b/lib/rpc_defs.py | ||
---|---|---|
506 | 506 |
("ovs_link", None, "Link of the OpenvSwitch to the outside"), |
507 | 507 |
], None, None, "This will create and setup the OpenvSwitch"), |
508 | 508 |
("node_crypto_tokens", SINGLE, None, constants.RPC_TMO_NORMAL, [ |
509 |
("token_types", None, "List of requested crypto token types"), |
|
510 |
], None, None, "Retrieve public crypto tokens from the node."), |
|
509 |
("token_request", None, |
|
510 |
"List of tuples of requested crypto token types, actions"), |
|
511 |
], None, None, "Handle crypto tokens of the node."), |
|
511 | 512 |
] |
512 | 513 |
|
513 | 514 |
_MISC_CALLS = [ |
b/lib/server/noded.py | ||
---|---|---|
869 | 869 |
"""Gets the node's public crypto tokens. |
870 | 870 |
|
871 | 871 |
""" |
872 |
token_types = params[0]
|
|
873 |
return backend.GetCryptoTokens(token_types)
|
|
872 |
token_requests = params[0]
|
|
873 |
return backend.GetCryptoTokens(token_requests)
|
|
874 | 874 |
|
875 | 875 |
# cluster -------------------------- |
876 | 876 |
|
b/src/Ganeti/Constants.hs | ||
---|---|---|
4157 | 4157 |
cryptoTypes :: FrozenSet String |
4158 | 4158 |
cryptoTypes = ConstantUtils.mkSet [cryptoTypeSslDigest] |
4159 | 4159 |
|
4160 |
-- * Crypto Actions |
|
4161 |
-- Actions that can be performed on crypto tokens |
|
4162 |
|
|
4163 |
cryptoActionGet :: String |
|
4164 |
cryptoActionGet = "get" |
|
4165 |
|
|
4166 |
-- This is 'create and get' |
|
4167 |
cryptoActionCreate :: String |
|
4168 |
cryptoActionCreate = "create" |
|
4169 |
|
|
4170 |
cryptoActions :: FrozenSet String |
|
4171 |
cryptoActions = ConstantUtils.mkSet [cryptoActionGet, cryptoActionCreate] |
|
4172 |
|
|
4173 |
-- * Options for CryptoActions |
|
4174 |
|
|
4175 |
-- Filename of the certificate |
|
4176 |
cryptoOptionCertFile :: String |
|
4177 |
cryptoOptionCertFile = "cert_file" |
|
4160 | 4178 |
|
4161 | 4179 |
-- * SSH key types |
4162 | 4180 |
|
b/test/py/ganeti.backend_unittest.py | ||
---|---|---|
34 | 34 |
from ganeti import hypervisor |
35 | 35 |
from ganeti import netutils |
36 | 36 |
from ganeti import objects |
37 |
from ganeti import pathutils |
|
37 | 38 |
from ganeti import utils |
38 | 39 |
|
39 | 40 |
|
... | ... | |
76 | 77 |
class TestGetCryptoTokens(testutils.GanetiTestCase): |
77 | 78 |
|
78 | 79 |
def setUp(self): |
79 |
self._digest_fn_orig = utils.GetClientCertificateDigest |
|
80 |
self._get_digest_fn_orig = utils.GetClientCertificateDigest |
|
81 |
self._create_digest_fn_orig = utils.GenerateNewSslCert |
|
80 | 82 |
self._ssl_digest = "12345" |
81 | 83 |
utils.GetClientCertificateDigest = mock.Mock( |
82 | 84 |
return_value=self._ssl_digest) |
85 |
utils.GenerateNewSslCert = mock.Mock() |
|
83 | 86 |
|
84 | 87 |
def tearDown(self): |
85 |
utils.GetClientCertificateDigest = self._digest_fn_orig |
|
88 |
utils.GetClientCertificateDigest = self._get_digest_fn_orig |
|
89 |
utils.GenerateNewSslCert = self._create_digest_fn_orig |
|
86 | 90 |
|
87 |
def testSslToken(self): |
|
88 |
result = backend.GetCryptoTokens([constants.CRYPTO_TYPE_SSL_DIGEST]) |
|
91 |
def testGetSslToken(self): |
|
92 |
result = backend.GetCryptoTokens( |
|
93 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_GET, None)]) |
|
89 | 94 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
90 | 95 |
in result) |
91 | 96 |
|
92 |
def testUnknownToken(self): |
|
97 |
def testCreateSslToken(self): |
|
98 |
result = backend.GetCryptoTokens( |
|
99 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, |
|
100 |
None)]) |
|
101 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
|
102 |
in result) |
|
103 |
self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) |
|
104 |
|
|
105 |
def testCreateSslTokenDifferentFilename(self): |
|
106 |
result = backend.GetCryptoTokens( |
|
107 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_CREATE, |
|
108 |
{constants.CRYPTO_OPTION_CERT_FILE: |
|
109 |
pathutils.NODED_CLIENT_CERT_FILE_TMP})]) |
|
110 |
self.assertTrue((constants.CRYPTO_TYPE_SSL_DIGEST, self._ssl_digest) |
|
111 |
in result) |
|
112 |
self.assertTrue(utils.GenerateNewSslCert.assert_calls().once()) |
|
113 |
|
|
114 |
def testUnknownTokenType(self): |
|
115 |
self.assertRaises(errors.ProgrammerError, |
|
116 |
backend.GetCryptoTokens, |
|
117 |
[("pink_bunny", constants.CRYPTO_ACTION_GET, None)]) |
|
118 |
|
|
119 |
def testUnknownAction(self): |
|
93 | 120 |
self.assertRaises(errors.ProgrammerError, |
94 |
backend.GetCryptoTokens, ["pink_bunny"]) |
|
121 |
backend.GetCryptoTokens, |
|
122 |
[(constants.CRYPTO_TYPE_SSL_DIGEST, "illuminate", None)]) |
|
95 | 123 |
|
96 | 124 |
|
97 | 125 |
class TestNodeVerify(testutils.GanetiTestCase): |
Also available in: Unified diff