jsecrets.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "jlog.hpp"
  15. #include "jutil.hpp"
  16. #include "jexcept.hpp"
  17. #include "jmutex.hpp"
  18. #include "jfile.hpp"
  19. #include "jptree.hpp"
  20. #include "jerror.hpp"
  21. #include "jsecrets.hpp"
  22. //including cpp-httplib single header file REST client
  23. // doesn't work with format-nonliteral as an error
  24. //
  25. #if defined(__clang__) || defined(__GNUC__)
  26. #pragma GCC diagnostic push
  27. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  28. #endif
  29. #ifdef _USE_OPENSSL
  30. #define CPPHTTPLIB_OPENSSL_SUPPORT
  31. #endif
  32. #undef INVALID_SOCKET
  33. #include "httplib.h"
  34. #if defined(__clang__) || defined(__GNUC__)
  35. #pragma GCC diagnostic pop
  36. #endif
  37. #ifdef _USE_OPENSSL
  38. #include <openssl/x509v3.h>
  39. #endif
  40. #include <vector>
  41. enum class CVaultKind { kv_v1, kv_v2 };
  42. CVaultKind getSecretType(const char *s)
  43. {
  44. if (isEmptyString(s))
  45. return CVaultKind::kv_v2;
  46. if (streq(s, "kv_v1"))
  47. return CVaultKind::kv_v1;
  48. return CVaultKind::kv_v2;
  49. }
  50. interface IVaultManager : extends IInterface
  51. {
  52. virtual bool getCachedSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  53. virtual bool requestSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  54. virtual bool getCachedSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  55. virtual bool requestSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) = 0;
  56. };
  57. static CriticalSection secretCacheCS;
  58. static Owned<IPropertyTree> secretCache;
  59. static CriticalSection mtlsInfoCacheCS;
  60. static Owned<IPropertyTree> mtlsInfoCache;
  61. static Owned<IVaultManager> vaultManager;
  62. MODULE_INIT(INIT_PRIORITY_SYSTEM)
  63. {
  64. secretCache.setown(createPTree());
  65. mtlsInfoCache.setown(createPTree());
  66. return true;
  67. }
  68. MODULE_EXIT()
  69. {
  70. vaultManager.clear();
  71. secretCache.clear();
  72. mtlsInfoCache.clear();
  73. }
  74. static void splitUrlAddress(const char *address, size_t len, StringBuffer &host, StringBuffer *port)
  75. {
  76. if (!address || len==0)
  77. return;
  78. const char *sep = (const char *)memchr(address, ':', len);
  79. if (!sep)
  80. host.append(len, address);
  81. else
  82. {
  83. host.append(sep - address, address);
  84. len = len - (sep - address) - 1;
  85. if (port)
  86. port->append(len, sep+1);
  87. else
  88. host.append(':').append(len, sep+1);
  89. }
  90. }
  91. static void splitUrlAuthority(const char *authority, size_t authorityLen, StringBuffer &user, StringBuffer &password, StringBuffer &host, StringBuffer *port)
  92. {
  93. if (!authority || authorityLen==0)
  94. return;
  95. const char *at = (const char *) memchr(authority, '@', authorityLen);
  96. if (!at)
  97. splitUrlAddress(authority, authorityLen, host, port);
  98. else
  99. {
  100. size_t userinfoLen = (at - authority);
  101. splitUrlAddress(at+1, authorityLen - userinfoLen - 1, host, port);
  102. const char *sep = (const char *) memchr(authority, ':', at - authority);
  103. if (!sep)
  104. user.append(at-authority, authority);
  105. else
  106. {
  107. user.append(sep-authority, authority);
  108. size_t passwordLen = (at - sep - 1);
  109. password.append(passwordLen, sep+1);
  110. }
  111. }
  112. }
  113. static inline void extractUrlProtocol(const char *&url, StringBuffer *scheme)
  114. {
  115. if (!url)
  116. throw makeStringException(-1, "Invalid empty URL");
  117. if (0 == strnicmp(url, "HTTPS://", 8))
  118. {
  119. url+=8;
  120. if (scheme)
  121. scheme->append("https://");
  122. }
  123. else if (0 == strnicmp(url, "HTTP://", 7))
  124. {
  125. url+=7;
  126. if (scheme)
  127. scheme->append("http://");
  128. }
  129. else
  130. throw MakeStringException(-1, "Invalid URL, protocol not recognized %s", url);
  131. }
  132. static void splitUrlSections(const char *url, const char * &authority, size_t &authorityLen, StringBuffer &fullpath, StringBuffer *scheme)
  133. {
  134. extractUrlProtocol(url, scheme);
  135. const char* path = strchr(url, '/');
  136. authority = url;
  137. if (!path)
  138. authorityLen = strlen(authority);
  139. else
  140. {
  141. authorityLen = path-url;
  142. fullpath.append(path);
  143. }
  144. }
  145. extern jlib_decl void splitFullUrl(const char *url, StringBuffer &user, StringBuffer &password, StringBuffer &host, StringBuffer &port, StringBuffer &path)
  146. {
  147. const char *authority = nullptr;
  148. size_t authorityLen = 0;
  149. splitUrlSections(url, authority, authorityLen, path, nullptr);
  150. splitUrlAuthority(authority, authorityLen, user, password, host, &port);
  151. }
  152. extern jlib_decl void splitUrlSchemeHostPort(const char *url, StringBuffer &user, StringBuffer &password, StringBuffer &schemeHostPort, StringBuffer &path)
  153. {
  154. const char *authority = nullptr;
  155. size_t authorityLen = 0;
  156. splitUrlSections(url, authority, authorityLen, path, &schemeHostPort);
  157. splitUrlAuthority(authority, authorityLen, user, password, schemeHostPort, nullptr);
  158. }
  159. //---------------------------------------------------------------------------------------------------------------------
  160. static StringBuffer secretDirectory;
  161. static CriticalSection secretCS;
  162. //there are various schemes for renewing kubernetes secrets and they are likely to vary greatly in how often
  163. // a secret gets updated this timeout determines the maximum amount of time before we'll pick up a change
  164. // 10 minutes for now we can change this as we gather more experience and user feedback
  165. static unsigned secretTimeoutMs = 10 * 60 * 1000;
  166. extern jlib_decl unsigned getSecretTimeout()
  167. {
  168. return secretTimeoutMs;
  169. }
  170. extern jlib_decl void setSecretTimeout(unsigned timeoutMs)
  171. {
  172. secretTimeoutMs = timeoutMs;
  173. }
  174. extern jlib_decl void setSecretMount(const char * path)
  175. {
  176. if (!path)
  177. {
  178. getPackageFolder(secretDirectory);
  179. addPathSepChar(secretDirectory).append("secrets");
  180. }
  181. else
  182. secretDirectory.set(path);
  183. }
  184. static inline bool checkSecretExpired(unsigned created)
  185. {
  186. if (!created)
  187. return false;
  188. unsigned age = msTick() - created;
  189. return age > getSecretTimeout();
  190. }
  191. class CVault
  192. {
  193. private:
  194. bool useKubernetesAuth = true;
  195. CVaultKind kind;
  196. CriticalSection vaultCS;
  197. Owned<IPropertyTree> cache;
  198. StringBuffer schemeHostPort;
  199. StringBuffer path;
  200. StringBuffer username;
  201. StringBuffer password;
  202. StringAttr name;
  203. StringAttr role;
  204. StringAttr token;
  205. public:
  206. CVault(IPropertyTree *vault)
  207. {
  208. cache.setown(createPTree());
  209. StringBuffer url;
  210. replaceEnvVariables(url, vault->queryProp("@url"), false);
  211. if (url.length())
  212. splitUrlSchemeHostPort(url.str(), username, password, schemeHostPort, path);
  213. name.set(vault->queryProp("@name"));
  214. kind = getSecretType(vault->queryProp("@kind"));
  215. if (vault->hasProp("@role"))
  216. role.set(vault->queryProp("@role"));
  217. else
  218. role.set("hpcc-vault-access");
  219. if (vault->hasProp("@client-secret"))
  220. {
  221. useKubernetesAuth = false;
  222. //for now only support direct access token. we can support other combinations for example login token, ldap login, etc later.
  223. Owned<IPropertyTree> clientSecret = getLocalSecret("system", vault->queryProp("@client-secret"));
  224. if (clientSecret)
  225. token.set(clientSecret->queryProp("token"));
  226. }
  227. }
  228. CVaultKind getVaultKind() const { return kind; }
  229. void kubernetesLogin()
  230. {
  231. CriticalBlock block(vaultCS);
  232. if (token.length())
  233. return;
  234. StringBuffer login_token;
  235. login_token.loadFile("/var/run/secrets/kubernetes.io/serviceaccount/token");
  236. if (login_token.length())
  237. {
  238. std::string json;
  239. json.append("{\"jwt\": \"").append(login_token.str()).append("\", \"role\": \"").append(role.str()).append("\"}");
  240. httplib::Client cli(schemeHostPort.str());
  241. if (username.length() && password.length())
  242. cli.set_basic_auth(username, password);
  243. httplib::Result res = cli.Post("/v1/auth/kubernetes/login", json, "application/json");
  244. if (res)
  245. {
  246. if (res->status == 200)
  247. {
  248. const char *response = res->body.c_str();
  249. if (!isEmptyString(response))
  250. {
  251. Owned<IPropertyTree> respTree = createPTreeFromJSONString(response);
  252. if (respTree)
  253. token.set(respTree->queryProp("auth/client_token"));
  254. }
  255. }
  256. else
  257. {
  258. Owned<IException> e = MakeStringException(0, "Vault kube auth error [%d](%d) - vault: %s - response: %s", res->status, res.error(), name.str(), res->body.c_str());
  259. OWARNLOG(e);
  260. throw e.getClear();
  261. }
  262. }
  263. }
  264. }
  265. bool getCachedSecret(CVaultKind &rkind, StringBuffer &content, const char *secret, const char *version)
  266. {
  267. CriticalBlock block(vaultCS);
  268. IPropertyTree *tree = cache->queryPropTree(secret);
  269. if (tree)
  270. {
  271. VStringBuffer vername("v.%s", isEmptyString(version) ? "latest" : version);
  272. IPropertyTree *envelope = tree->queryPropTree(vername);
  273. if (!envelope)
  274. return false;
  275. if (checkSecretExpired((unsigned) envelope->getPropInt("@created")))
  276. {
  277. tree->removeTree(envelope);
  278. return false;
  279. }
  280. const char *s = envelope->queryProp("");
  281. if (!isEmptyString(s))
  282. {
  283. rkind = kind;
  284. content.append(s);
  285. return true;
  286. }
  287. }
  288. return false;
  289. }
  290. void addCachedSecret(const char *content, const char *secret, const char *version)
  291. {
  292. VStringBuffer vername("v.%s", isEmptyString(version) ? "latest" : version);
  293. Owned<IPropertyTree> envelope = createPTree(vername);
  294. envelope->setPropInt("@created", (int) msTick());
  295. envelope->setProp("", content);
  296. {
  297. CriticalBlock block(vaultCS);
  298. IPropertyTree *parent = ensurePTree(cache, secret);
  299. parent->setPropTree(vername, envelope.getClear());
  300. }
  301. }
  302. bool requestSecret(CVaultKind &rkind, StringBuffer &content, const char *secret, const char *version)
  303. {
  304. if (isEmptyString(secret))
  305. return false;
  306. if (useKubernetesAuth && token.isEmpty())
  307. kubernetesLogin();
  308. if (token.isEmpty())
  309. {
  310. Owned<IException> e = MakeStringException(0, "Vault auth error - vault: %s - vault access token not provided", name.str());
  311. OERRLOG(e);
  312. throw e.getClear();
  313. }
  314. StringBuffer location(path);
  315. location.replaceString("${secret}", secret);
  316. location.replaceString("${version}", version ? version : "1");
  317. httplib::Client cli(schemeHostPort.str());
  318. if (username.length() && password.length())
  319. cli.set_basic_auth(username.str(), password.str());
  320. httplib::Headers headers = {
  321. { "X-Vault-Token", token.str() }
  322. };
  323. if (httplib::Result res = cli.Get(location, headers))
  324. {
  325. if (res->status == 200)
  326. {
  327. rkind = kind;
  328. content.append(res->body.c_str());
  329. addCachedSecret(content.str(), secret, version);
  330. return true;
  331. }
  332. else
  333. {
  334. DBGLOG("Vault %s error accessing secret %s.%s [%d](%d) - response: %s", name.str(), secret, version ? version : "", res->status, res.error(), res->body.c_str());
  335. }
  336. }
  337. return false;
  338. }
  339. };
  340. class CVaultSet
  341. {
  342. private:
  343. std::map<std::string, std::unique_ptr<CVault>> vaults;
  344. public:
  345. CVaultSet()
  346. {
  347. }
  348. void addVault(IPropertyTree *vault)
  349. {
  350. const char *name = vault->queryProp("@name");
  351. if (!isEmptyString(name))
  352. vaults.emplace(name, std::unique_ptr<CVault>(new CVault(vault)));
  353. }
  354. bool getCachedSecret(CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  355. {
  356. auto it = vaults.begin();
  357. for (; it != vaults.end(); it++)
  358. {
  359. if (it->second->getCachedSecret(kind, content, secret, version))
  360. return true;
  361. }
  362. return false;
  363. }
  364. bool requestSecret(CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  365. {
  366. auto it = vaults.begin();
  367. for (; it != vaults.end(); it++)
  368. {
  369. if (it->second->requestSecret(kind, content, secret, version))
  370. return true;
  371. }
  372. return false;
  373. }
  374. bool getCachedSecretFromVault(const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  375. {
  376. if (isEmptyString(vaultId))
  377. return false;
  378. auto it = vaults.find(vaultId);
  379. if (it == vaults.end())
  380. return false;
  381. return it->second->getCachedSecret(kind, content, secret, version);
  382. }
  383. bool requestSecretFromVault(const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version)
  384. {
  385. if (isEmptyString(vaultId))
  386. return false;
  387. auto it = vaults.find(vaultId);
  388. if (it == vaults.end())
  389. return false;
  390. return it->second->requestSecret(kind, content, secret, version);
  391. }
  392. };
  393. class CVaultManager : public CInterfaceOf<IVaultManager>
  394. {
  395. private:
  396. std::map<std::string, std::unique_ptr<CVaultSet>> categories;
  397. public:
  398. CVaultManager()
  399. {
  400. IPropertyTree *config = nullptr;
  401. try
  402. {
  403. config = queryComponentConfig().queryPropTree("vaults");
  404. }
  405. catch (IException * e)
  406. {
  407. EXCLOG(e);
  408. e->Release();
  409. }
  410. if (!config)
  411. return;
  412. Owned<IPropertyTreeIterator> iter = config->getElements("*");
  413. ForEach (*iter)
  414. {
  415. IPropertyTree &vault = iter->query();
  416. const char *category = vault.queryName();
  417. auto it = categories.find(category);
  418. if (it == categories.end())
  419. {
  420. auto placed = categories.emplace(category, std::unique_ptr<CVaultSet>(new CVaultSet()));
  421. if (placed.second)
  422. it = placed.first;
  423. }
  424. if (it != categories.end())
  425. it->second->addVault(&vault);
  426. }
  427. }
  428. bool getCachedSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  429. {
  430. if (isEmptyString(category))
  431. return false;
  432. auto it = categories.find(category);
  433. if (it == categories.end())
  434. return false;
  435. return it->second->getCachedSecretFromVault(vaultId, kind, content, secret, version);
  436. }
  437. bool requestSecretFromVault(const char *category, const char *vaultId, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  438. {
  439. if (isEmptyString(category))
  440. return false;
  441. auto it = categories.find(category);
  442. if (it == categories.end())
  443. return false;
  444. return it->second->requestSecretFromVault(vaultId, kind, content, secret, version);
  445. }
  446. bool getCachedSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  447. {
  448. if (isEmptyString(category))
  449. return false;
  450. auto it = categories.find(category);
  451. if (it == categories.end())
  452. return false;
  453. return it->second->getCachedSecret(kind, content, secret, version);
  454. }
  455. bool requestSecretByCategory(const char *category, CVaultKind &kind, StringBuffer &content, const char *secret, const char *version) override
  456. {
  457. if (isEmptyString(category))
  458. return false;
  459. auto it = categories.find(category);
  460. if (it == categories.end())
  461. return false;
  462. return it->second->requestSecret(kind, content, secret, version);
  463. }
  464. };
  465. IVaultManager *ensureVaultManager()
  466. {
  467. CriticalBlock block(secretCS);
  468. if (!vaultManager)
  469. vaultManager.setown(new CVaultManager());
  470. return vaultManager;
  471. }
  472. static IPropertyTree *getCachedLocalSecret(const char *category, const char *name)
  473. {
  474. if (isEmptyString(name))
  475. return nullptr;
  476. Owned<IPropertyTree> secret;
  477. {
  478. CriticalBlock block(secretCacheCS);
  479. IPropertyTree *tree = secretCache->queryPropTree(category);
  480. if (!tree)
  481. return nullptr;
  482. secret.setown(tree->getPropTree(name));
  483. if (secret)
  484. {
  485. if (checkSecretExpired((unsigned) secret->getPropInt("@created")))
  486. {
  487. secretCache->removeProp(name);
  488. return nullptr;
  489. }
  490. return secret.getClear();
  491. }
  492. }
  493. return nullptr;
  494. }
  495. static void addCachedLocalSecret(const char *category, const char *name, IPropertyTree *secret)
  496. {
  497. if (!secret || isEmptyString(name) || isEmptyString(category))
  498. return;
  499. secret->setPropInt("@created", (int)msTick());
  500. {
  501. CriticalBlock block(secretCacheCS);
  502. IPropertyTree *tree = ensurePTree(secretCache, category);
  503. tree->setPropTree(name, LINK(secret));
  504. }
  505. }
  506. static const char *ensureSecretDirectory()
  507. {
  508. CriticalBlock block(secretCS);
  509. if (secretDirectory.isEmpty())
  510. setSecretMount(nullptr);
  511. return secretDirectory;
  512. }
  513. static IPropertyTree *loadLocalSecret(const char *category, const char * name)
  514. {
  515. StringBuffer path;
  516. addPathSepChar(path.append(ensureSecretDirectory())).append(category).append(PATHSEPCHAR).append(name).append(PATHSEPCHAR);
  517. Owned<IDirectoryIterator> entries = createDirectoryIterator(path);
  518. if (!entries || !entries->first())
  519. return nullptr;
  520. Owned<IPropertyTree> tree = createPTree(name);
  521. tree->setPropInt("@created", (int) msTick());
  522. ForEach(*entries)
  523. {
  524. if (entries->isDir())
  525. continue;
  526. StringBuffer name;
  527. entries->getName(name);
  528. if (!validateXMLTag(name))
  529. continue;
  530. MemoryBuffer content;
  531. Owned<IFileIO> io = entries->query().open(IFOread);
  532. read(io, 0, (size32_t)-1, content);
  533. if (!content.length())
  534. continue;
  535. tree->setPropBin(name, content.length(), content.bufferBase());
  536. }
  537. addCachedLocalSecret(category, name, tree);
  538. return tree.getClear();
  539. }
  540. extern jlib_decl IPropertyTree *getLocalSecret(const char *category, const char * name)
  541. {
  542. Owned<IPropertyTree> tree = getCachedLocalSecret(category, name);
  543. if (tree)
  544. return tree.getClear();
  545. return loadLocalSecret(category, name);
  546. }
  547. static IPropertyTree *createPTreeFromVaultSecret(const char *content, CVaultKind kind)
  548. {
  549. if (isEmptyString(content))
  550. return nullptr;
  551. Owned<IPropertyTree> tree = createPTreeFromJSONString(content);
  552. if (!tree)
  553. return nullptr;
  554. switch (kind)
  555. {
  556. case CVaultKind::kv_v1:
  557. tree.setown(tree->getPropTree("data"));
  558. break;
  559. default:
  560. case CVaultKind::kv_v2:
  561. tree.setown(tree->getPropTree("data/data"));
  562. break;
  563. }
  564. return tree.getClear();
  565. }
  566. static IPropertyTree *getCachedVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  567. {
  568. CVaultKind kind;
  569. StringBuffer json;
  570. IVaultManager *vaultmgr = ensureVaultManager();
  571. if (isEmptyString(vaultId))
  572. {
  573. if (!vaultmgr->getCachedSecretByCategory(category, kind, json, name, version))
  574. return nullptr;
  575. }
  576. else
  577. {
  578. if (!vaultmgr->getCachedSecretFromVault(category, vaultId, kind, json, name, version))
  579. return nullptr;
  580. }
  581. return createPTreeFromVaultSecret(json.str(), kind);
  582. }
  583. static IPropertyTree *requestVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  584. {
  585. CVaultKind kind;
  586. StringBuffer json;
  587. IVaultManager *vaultmgr = ensureVaultManager();
  588. if (isEmptyString(vaultId))
  589. {
  590. if (!vaultmgr->requestSecretByCategory(category, kind, json, name, version))
  591. return nullptr;
  592. }
  593. else
  594. {
  595. if (!vaultmgr->requestSecretFromVault(category, vaultId, kind, json, name, version))
  596. return nullptr;
  597. }
  598. return createPTreeFromVaultSecret(json.str(), kind);
  599. }
  600. extern jlib_decl IPropertyTree *getVaultSecret(const char *category, const char *vaultId, const char * name, const char *version)
  601. {
  602. CVaultKind kind;
  603. StringBuffer json;
  604. IVaultManager *vaultmgr = ensureVaultManager();
  605. if (isEmptyString(vaultId))
  606. {
  607. if (!vaultmgr->getCachedSecretByCategory(category, kind, json, name, version))
  608. vaultmgr->requestSecretByCategory(category, kind, json, name, version);
  609. }
  610. else
  611. {
  612. if (!vaultmgr->getCachedSecretFromVault(category, vaultId, kind, json, name, version))
  613. vaultmgr->requestSecretFromVault(category, vaultId, kind, json, name, version);
  614. }
  615. return createPTreeFromVaultSecret(json.str(), kind);
  616. }
  617. extern jlib_decl IPropertyTree *getSecret(const char *category, const char * name)
  618. {
  619. //check for any chached first
  620. Owned<IPropertyTree> secret = getCachedLocalSecret(category, name);
  621. if (!secret)
  622. secret.setown(getCachedVaultSecret(category, nullptr, name, nullptr));
  623. //now check local, then vaults
  624. if (!secret)
  625. secret.setown(loadLocalSecret(category, name));
  626. if (!secret)
  627. secret.setown(requestVaultSecret(category, nullptr, name, nullptr));
  628. return secret.getClear();
  629. }
  630. extern jlib_decl bool getSecretKeyValue(MemoryBuffer & result, IPropertyTree *secret, const char * key)
  631. {
  632. IPropertyTree *tree = secret->queryPropTree(key);
  633. if (tree)
  634. return tree->getPropBin(nullptr, result);
  635. return false;
  636. }
  637. extern jlib_decl bool getSecretKeyValue(StringBuffer & result, IPropertyTree *secret, const char * key)
  638. {
  639. IPropertyTree *tree = secret->queryPropTree(key);
  640. if (!tree)
  641. return false;
  642. if (tree->isBinary(nullptr))
  643. {
  644. MemoryBuffer mb;
  645. tree->getPropBin(nullptr, mb);
  646. //caller implies it's a string
  647. result.append(mb.length(), mb.toByteArray());
  648. return true;
  649. }
  650. const char *value = tree->queryProp(nullptr);
  651. if (value)
  652. {
  653. result.append(value);
  654. return true;
  655. }
  656. return false;
  657. }
  658. extern jlib_decl bool getSecretValue(StringBuffer & result, const char *category, const char * name, const char * key, bool required)
  659. {
  660. Owned<IPropertyTree> secret = getSecret(category, name);
  661. if (required && !secret)
  662. throw MakeStringException(-1, "secret %s.%s not found", category, name);
  663. bool found = getSecretKeyValue(result, secret, key);
  664. if (required && !found)
  665. throw MakeStringException(-1, "secret %s.%s missing key %s", category, name, key);
  666. return true;
  667. }
  668. bool getSecretUdpKey(MemoryAttr &updkey)
  669. {
  670. bool ret = false;
  671. updkey.clear();
  672. #if defined(_CONTAINERIZED) && defined(_USE_OPENSSL)
  673. BIO *in = BIO_new_file("/opt/HPCCSystems/secrets/certificates/udp/tls.key", "r");
  674. if (in == nullptr)
  675. return false;
  676. EC_KEY *eckey = PEM_read_bio_ECPrivateKey(in, nullptr, nullptr, nullptr);
  677. if (eckey)
  678. {
  679. unsigned char *priv = NULL;
  680. size_t privlen = EC_KEY_priv2buf(eckey, &priv);
  681. if (privlen != 0)
  682. {
  683. updkey.set(privlen, priv);
  684. OPENSSL_clear_free(priv, privlen);
  685. ret = true;
  686. }
  687. EC_KEY_free(eckey);
  688. }
  689. BIO_free(in);
  690. #endif
  691. return ret;
  692. }
  693. IPropertyTree *queryMtlsSecretInfo(const char *name)
  694. {
  695. if (isEmptyString(name))
  696. return nullptr;
  697. CriticalBlock block(mtlsInfoCacheCS);
  698. IPropertyTree *info = mtlsInfoCache->queryPropTree(name);
  699. if (info)
  700. return info;
  701. StringBuffer filepath;
  702. StringBuffer secretpath;
  703. addPathSepChar(secretpath.append(ensureSecretDirectory())).append("certificates").append(PATHSEPCHAR).append(name).append(PATHSEPCHAR);
  704. filepath.set(secretpath).append("tls.crt");
  705. if (!checkFileExists(filepath))
  706. return nullptr;
  707. info = mtlsInfoCache->setPropTree(name);
  708. info->setProp("certificate", filepath.str());
  709. filepath.set(secretpath).append("tls.key");
  710. if (checkFileExists(filepath))
  711. info->setProp("privatekey", filepath.str());
  712. IPropertyTree *verify = ensurePTree(info, "verify");
  713. if (verify)
  714. {
  715. filepath.set(secretpath).append("ca.crt");
  716. if (checkFileExists(filepath))
  717. {
  718. IPropertyTree *ca = ensurePTree(verify, "ca_certificates");
  719. if (ca)
  720. ca->setProp("@path", filepath.str());
  721. }
  722. verify->setPropBool("@enable", true);
  723. verify->setPropBool("@address_match", false);
  724. verify->setPropBool("@accept_selfsigned", false);
  725. verify->setProp("trusted_peers", "anyone");
  726. }
  727. return info;
  728. }