jsecrets.cpp 26 KB

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