]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/rgw/test_rgw_kms.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / test / rgw / test_rgw_kms.cc
CommitLineData
9f95a23c
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include <gtest/gtest.h>
5#include <gmock/gmock.h>
6#include "common/ceph_context.h"
7#include "rgw/rgw_common.h"
f67539c2 8#define FORTEST_VIRTUAL virtual
9f95a23c
TL
9#include "rgw/rgw_kms.cc"
10
11using ::testing::_;
12using ::testing::Action;
13using ::testing::ActionInterface;
14using ::testing::MakeAction;
f67539c2 15using ::testing::StrEq;
9f95a23c
TL
16
17
18class MockTransitSecretEngine : public TransitSecretEngine {
19
20public:
f67539c2 21 MockTransitSecretEngine(CephContext *cct, EngineParmMap parms) : TransitSecretEngine(cct, parms){}
9f95a23c 22
f67539c2 23 MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
9f95a23c
TL
24
25};
26
27class MockKvSecretEngine : public KvSecretEngine {
28
29public:
f67539c2 30 MockKvSecretEngine(CephContext *cct, EngineParmMap parms) : KvSecretEngine(cct, parms){}
9f95a23c 31
f67539c2 32 MOCK_METHOD(int, send_request, (const char *method, std::string_view infix, std::string_view key_id, const std::string& postdata, bufferlist &bl), (override));
9f95a23c
TL
33
34};
35
36class TestSSEKMS : public ::testing::Test {
37
38protected:
39 CephContext *cct;
f67539c2 40 MockTransitSecretEngine* old_engine;
9f95a23c 41 MockKvSecretEngine* kv_engine;
f67539c2 42 MockTransitSecretEngine* transit_engine;
9f95a23c
TL
43
44 void SetUp() override {
f67539c2 45 EngineParmMap old_parms, kv_parms, new_parms;
9f95a23c 46 cct = (new CephContext(CEPH_ENTITY_TYPE_ANY))->get();
f67539c2
TL
47 old_parms["compat"] = "2";
48 old_engine = new MockTransitSecretEngine(cct, std::move(old_parms));
49 kv_engine = new MockKvSecretEngine(cct, std::move(kv_parms));
50 new_parms["compat"] = "1";
51 transit_engine = new MockTransitSecretEngine(cct, std::move(new_parms));
9f95a23c
TL
52 }
53
54 void TearDown() {
f67539c2 55 delete old_engine;
9f95a23c 56 delete kv_engine;
f67539c2 57 delete transit_engine;
9f95a23c
TL
58 }
59
60};
61
62
63TEST_F(TestSSEKMS, vault_token_file_unset)
64{
65 cct->_conf.set_val("rgw_crypt_vault_auth", "token");
f67539c2
TL
66 EngineParmMap old_parms, kv_parms;
67 TransitSecretEngine te(cct, std::move(old_parms));
68 KvSecretEngine kv(cct, std::move(kv_parms));
9f95a23c 69
f67539c2 70 std::string_view key_id("my_key");
9f95a23c
TL
71 std::string actual_key;
72
73 ASSERT_EQ(te.get_key(key_id, actual_key), -EINVAL);
74 ASSERT_EQ(kv.get_key(key_id, actual_key), -EINVAL);
75}
76
77
78TEST_F(TestSSEKMS, non_existent_vault_token_file)
79{
80 cct->_conf.set_val("rgw_crypt_vault_auth", "token");
81 cct->_conf.set_val("rgw_crypt_vault_token_file", "/nonexistent/file");
f67539c2
TL
82 EngineParmMap old_parms, kv_parms;
83 TransitSecretEngine te(cct, std::move(old_parms));
84 KvSecretEngine kv(cct, std::move(kv_parms));
9f95a23c 85
f67539c2 86 std::string_view key_id("my_key/1");
9f95a23c
TL
87 std::string actual_key;
88
89 ASSERT_EQ(te.get_key(key_id, actual_key), -ENOENT);
90 ASSERT_EQ(kv.get_key(key_id, actual_key), -ENOENT);
91}
92
93
f67539c2
TL
94typedef int SendRequestMethod(const char *,
95 std::string_view, std::string_view,
96 const std::string &, bufferlist &);
9f95a23c
TL
97
98class SetPointedValueAction : public ActionInterface<SendRequestMethod> {
99 public:
f67539c2 100 std::string json;
9f95a23c
TL
101
102 SetPointedValueAction(std::string json){
103 this->json = json;
104 }
105
f67539c2
TL
106 int Perform(const ::std::tuple<const char *, std::string_view, std::string_view, const std::string &, bufferlist &>& args) override {
107// const char *method = ::std::get<0>(args);
108// std::string_view infix = ::std::get<1>(args);
109// std::string_view key_id = ::std::get<2>(args);
110// const std::string& postdata = ::std::get<3>(args);
111 bufferlist& bl = ::std::get<4>(args);
112
113// std::cout << "method = " << method << " infix = " << infix << " key_id = " << key_id
114// << " postdata = " << postdata
115// << " => json = " << json
116// << std::endl;
117
118 bl.append(json);
119 // note: in the bufferlist, the string is not
120 // necessarily 0 terminated at this point. Logic in
121 // rgw_kms.cc must handle this (by appending a 0.)
9f95a23c
TL
122 return 0;
123 }
124};
125
126Action<SendRequestMethod> SetPointedValue(std::string json) {
127 return MakeAction(new SetPointedValueAction(json));
128}
129
130
131TEST_F(TestSSEKMS, test_transit_key_version_extraction){
132 string json = R"({"data": {"keys": {"6": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
f67539c2 133 EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("1/2/3/4/5/6"), StrEq(""), _)).WillOnce(SetPointedValue(json));
9f95a23c
TL
134
135 std::string actual_key;
136 std::string tests[11] {"/", "my_key/", "my_key", "", "my_key/a", "my_key/1a",
137 "my_key/a1", "my_key/1a1", "my_key/1/a", "1", "my_key/1/"
138 };
139
140 int res;
141 for (const auto &test: tests) {
f67539c2 142 res = old_engine->get_key(std::string_view(test), actual_key);
9f95a23c
TL
143 ASSERT_EQ(res, -EINVAL);
144 }
145
f67539c2 146 res = old_engine->get_key(std::string_view("1/2/3/4/5/6"), actual_key);
9f95a23c
TL
147 ASSERT_EQ(res, 0);
148 ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
149}
150
151
152TEST_F(TestSSEKMS, test_transit_backend){
153
f67539c2 154 std::string_view my_key("my_key/1");
9f95a23c
TL
155 std::string actual_key;
156
157 // Mocks the expected return Value from Vault Server using custom Argument Action
158 string json = R"({"data": {"keys": {"1": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
f67539c2 159 EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
9f95a23c 160
f67539c2 161 int res = old_engine->get_key(my_key, actual_key);
9f95a23c
TL
162
163 ASSERT_EQ(res, 0);
164 ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
165}
166
167
f67539c2
TL
168TEST_F(TestSSEKMS, test_transit_makekey){
169
170 std::string_view my_key("my_key");
171 std::string actual_key;
172 map<string, bufferlist> attrs;
173
174 // Mocks the expected return Value from Vault Server using custom Argument Action
175 string post_json = R"({"data": {"ciphertext": "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ","key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
176 EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/datakey/plaintext/"), StrEq("my_key"), _, _))
177 .WillOnce(SetPointedValue(post_json));
178
179 set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
180 set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
181
182 int res = transit_engine->make_actual_key(attrs, actual_key);
183 std::string cipher_text { get_str_attribute(attrs,RGW_ATTR_CRYPT_DATAKEY) };
184
185 ASSERT_EQ(res, 0);
186 ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
187 ASSERT_EQ(cipher_text, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
188}
189
190TEST_F(TestSSEKMS, test_transit_reconstitutekey){
191
192 std::string_view my_key("my_key");
193 std::string actual_key;
194 map<string, bufferlist> attrs;
195
196 // Mocks the expected return Value from Vault Server using custom Argument Action
197 set_attr(attrs, RGW_ATTR_CRYPT_DATAKEY, "vault:v2:HbdxLnUztGVo+RseCIaYVn/4wEUiJNT6GQfw57KXQmhXVe7i1/kgLWegEPg1I6lexhIuXAM6Q2YvY0aZ");
198 string post_json = R"({"data": {"key_version": 1,"plaintext": "3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="}})";
199 EXPECT_CALL(*transit_engine, send_request(StrEq("POST"), StrEq("/decrypt/"), StrEq("my_key"), _, _))
200 .WillOnce(SetPointedValue(post_json));
201
202 set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, R"({"aws:s3:arn": "fred"})");
203 set_attr(attrs, RGW_ATTR_CRYPT_KEYID, my_key);
204
205 int res = transit_engine->reconstitute_actual_key(attrs, actual_key);
206
207 ASSERT_EQ(res, 0);
208 ASSERT_EQ(actual_key, from_base64("3xfTra/dsIf3TMa3mAT2IxPpM7YWm/NvUb4gDfSDX4g="));
209}
210
9f95a23c
TL
211TEST_F(TestSSEKMS, test_kv_backend){
212
f67539c2 213 std::string_view my_key("my_key");
9f95a23c
TL
214 std::string actual_key;
215
216 // Mocks the expected return value from Vault Server using custom Argument Action
217 string json = R"({"data": {"data": {"key": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
f67539c2
TL
218 EXPECT_CALL(*kv_engine, send_request(StrEq("GET"), StrEq(""), StrEq("my_key"), StrEq(""), _))
219 .WillOnce(SetPointedValue(json));
9f95a23c
TL
220
221 int res = kv_engine->get_key(my_key, actual_key);
222
223 ASSERT_EQ(res, 0);
224 ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
225}
226
227
228TEST_F(TestSSEKMS, concat_url)
229{
230 // Each test has 3 strings:
231 // * the base URL
232 // * the path we want to concatenate
233 // * the exepected final URL
234 std::string tests[9][3] ={
235 {"", "", ""},
236 {"", "bar", "/bar"},
237 {"", "/bar", "/bar"},
238 {"foo", "", "foo"},
239 {"foo", "bar", "foo/bar"},
240 {"foo", "/bar", "foo/bar"},
241 {"foo/", "", "foo/"},
242 {"foo/", "bar", "foo/bar"},
243 {"foo/", "/bar", "foo/bar"},
244 };
245 for (const auto &test: tests) {
246 std::string url(test[0]), path(test[1]), expected(test[2]);
247 concat_url(url, path);
248 ASSERT_EQ(url, expected);
249 }
250}
251
252
f67539c2
TL
253TEST_F(TestSSEKMS, string_ends_maybe_slash)
254{
255 struct { std::string hay, needle; bool expected; } tests[] ={
256 {"jack here", "fred", false},
257 {"here is a fred", "fred", true},
258 {"and a fred/", "fred", true},
259 {"no fred here", "fred", false},
260 {"double fred//", "fred", true},
261 };
262 for (const auto &test: tests) {
263 bool expected { string_ends_maybe_slash(test.hay, test.needle) };
264 ASSERT_EQ(expected, test.expected);
265 }
266}
267
268
9f95a23c
TL
269TEST_F(TestSSEKMS, test_transit_backend_empty_response)
270{
f67539c2 271 std::string_view my_key("/key/nonexistent/1");
9f95a23c
TL
272 std::string actual_key;
273
274 // Mocks the expected return Value from Vault Server using custom Argument Action
275 string json = R"({"errors": ["version does not exist or cannot be found"]})";
f67539c2 276 EXPECT_CALL(*old_engine, send_request(StrEq("GET"), StrEq(""), StrEq("/key/nonexistent/1"), StrEq(""), _)).WillOnce(SetPointedValue(json));
9f95a23c 277
f67539c2 278 int res = old_engine->get_key(my_key, actual_key);
9f95a23c
TL
279
280 ASSERT_EQ(res, -EINVAL);
281 ASSERT_EQ(actual_key, from_base64(""));
282}