|
@@ -81,6 +81,12 @@
|
|
|
#include "../ext/http-parser/http_parser.h"
|
|
#include "../ext/http-parser/http_parser.h"
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+extern "C" {
|
|
|
|
|
+#include <curl/curl.h>
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
#include "../ext/json/json.hpp"
|
|
#include "../ext/json/json.hpp"
|
|
|
|
|
|
|
|
using json = nlohmann::json;
|
|
using json = nlohmann::json;
|
|
@@ -158,6 +164,14 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
|
|
|
// TCP activity timeout
|
|
// TCP activity timeout
|
|
|
#define ZT_TCP_ACTIVITY_TIMEOUT 60000
|
|
#define ZT_TCP_ACTIVITY_TIMEOUT 60000
|
|
|
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data)
|
|
|
|
|
+{
|
|
|
|
|
+ data->append((char*)ptr, size * nmemb);
|
|
|
|
|
+ return size * nmemb;
|
|
|
|
|
+}
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
namespace ZeroTier {
|
|
namespace ZeroTier {
|
|
|
|
|
|
|
|
namespace {
|
|
namespace {
|
|
@@ -478,10 +492,12 @@ public:
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
// HashiCorp Vault Settings
|
|
// HashiCorp Vault Settings
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
bool _vaultEnabled;
|
|
bool _vaultEnabled;
|
|
|
std::string _vaultURL;
|
|
std::string _vaultURL;
|
|
|
std::string _vaultToken;
|
|
std::string _vaultToken;
|
|
|
std::string _vaultPath; // defaults to cubbyhole/zerotier/identity.secret for per-access key storage
|
|
std::string _vaultPath; // defaults to cubbyhole/zerotier/identity.secret for per-access key storage
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
// Set to false to force service to stop
|
|
// Set to false to force service to stop
|
|
|
volatile bool _run;
|
|
volatile bool _run;
|
|
@@ -518,12 +534,16 @@ public:
|
|
|
,_vaultEnabled(false)
|
|
,_vaultEnabled(false)
|
|
|
,_vaultURL()
|
|
,_vaultURL()
|
|
|
,_vaultToken()
|
|
,_vaultToken()
|
|
|
- ,_vaultPath("cubbyhole/zerotier/identity.secret")
|
|
|
|
|
|
|
+ ,_vaultPath("cubbyhole/zerotier")
|
|
|
,_run(true)
|
|
,_run(true)
|
|
|
{
|
|
{
|
|
|
_ports[0] = 0;
|
|
_ports[0] = 0;
|
|
|
_ports[1] = 0;
|
|
_ports[1] = 0;
|
|
|
_ports[2] = 0;
|
|
_ports[2] = 0;
|
|
|
|
|
+
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
|
|
+#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
virtual ~OneServiceImpl()
|
|
virtual ~OneServiceImpl()
|
|
@@ -560,20 +580,6 @@ public:
|
|
|
_authToken = _trimString(_authToken);
|
|
_authToken = _trimString(_authToken);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- {
|
|
|
|
|
- struct ZT_Node_Callbacks cb;
|
|
|
|
|
- cb.version = 0;
|
|
|
|
|
- cb.stateGetFunction = SnodeStateGetFunction;
|
|
|
|
|
- cb.statePutFunction = SnodeStatePutFunction;
|
|
|
|
|
- cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
|
|
|
|
|
- cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
|
|
|
|
|
- cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
|
|
|
|
|
- cb.eventCallback = SnodeEventCallback;
|
|
|
|
|
- cb.pathCheckFunction = SnodePathCheckFunction;
|
|
|
|
|
- cb.pathLookupFunction = SnodePathLookupFunction;
|
|
|
|
|
- _node = new Node(this,(void *)0,&cb,OSUtils::now());
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
// Read local configuration
|
|
// Read local configuration
|
|
|
std::vector<InetAddress> explicitBind;
|
|
std::vector<InetAddress> explicitBind;
|
|
|
{
|
|
{
|
|
@@ -663,14 +669,25 @@ public:
|
|
|
for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
|
|
for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
|
|
|
_node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
|
|
_node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- json &vaultConfig = _localConfig["vault"];
|
|
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Apply other runtime configuration from local.conf
|
|
// Apply other runtime configuration from local.conf
|
|
|
applyLocalConfig();
|
|
applyLocalConfig();
|
|
|
|
|
|
|
|
|
|
+ {
|
|
|
|
|
+ struct ZT_Node_Callbacks cb;
|
|
|
|
|
+ cb.version = 0;
|
|
|
|
|
+ cb.stateGetFunction = SnodeStateGetFunction;
|
|
|
|
|
+ cb.statePutFunction = SnodeStatePutFunction;
|
|
|
|
|
+ cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
|
|
|
|
|
+ cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
|
|
|
|
|
+ cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
|
|
|
|
|
+ cb.eventCallback = SnodeEventCallback;
|
|
|
|
|
+ cb.pathCheckFunction = SnodePathCheckFunction;
|
|
|
|
|
+ cb.pathLookupFunction = SnodePathLookupFunction;
|
|
|
|
|
+ _node = new Node(this, (void *)0, &cb, OSUtils::now());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Make sure we can use the primary port, and hunt for one if configured to do so
|
|
// Make sure we can use the primary port, and hunt for one if configured to do so
|
|
|
const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
|
|
const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
|
|
|
for(int k=0;k<portTrials;++k) {
|
|
for(int k=0;k<portTrials;++k) {
|
|
@@ -1524,22 +1541,43 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- json &vault = settings["valut"];
|
|
|
|
|
|
|
+ json &vault = settings["vault"];
|
|
|
if (vault.is_object()) {
|
|
if (vault.is_object()) {
|
|
|
const std::string url(OSUtils::jsonString(vault["vaultURL"], "").c_str());
|
|
const std::string url(OSUtils::jsonString(vault["vaultURL"], "").c_str());
|
|
|
- if (!url.empty())
|
|
|
|
|
|
|
+ if (!url.empty()) {
|
|
|
_vaultURL = url;
|
|
_vaultURL = url;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
const std::string token(OSUtils::jsonString(vault["vaultToken"], "").c_str());
|
|
const std::string token(OSUtils::jsonString(vault["vaultToken"], "").c_str());
|
|
|
- if (!token.empty())
|
|
|
|
|
|
|
+ if (!token.empty()) {
|
|
|
_vaultToken = token;
|
|
_vaultToken = token;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
const std::string path(OSUtils::jsonString(vault["vaultPath"], "").c_str());
|
|
const std::string path(OSUtils::jsonString(vault["vaultPath"], "").c_str());
|
|
|
- if (!path.empty())
|
|
|
|
|
|
|
+ if (!path.empty()) {
|
|
|
_vaultPath = path;
|
|
_vaultPath = path;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (!_vaultURL.empty() && !_vaultToken.empty())
|
|
|
|
|
- _vaultEnabled = true;
|
|
|
|
|
|
|
+ // also check environment variables for values. Environment variables
|
|
|
|
|
+ // will override local.conf variables
|
|
|
|
|
+ const std::string envURL(getenv("VAULT_ADDR"));
|
|
|
|
|
+ if (!envURL.empty()) {
|
|
|
|
|
+ _vaultURL = envURL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const std::string envToken(getenv("VAULT_TOKEN"));
|
|
|
|
|
+ if (!envToken.empty()) {
|
|
|
|
|
+ _vaultToken = envToken;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const std::string envPath(getenv("VAULT_PATH"));
|
|
|
|
|
+ if (!envPath.empty()) {
|
|
|
|
|
+ _vaultPath = envPath;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!_vaultURL.empty() && !_vaultToken.empty()) {
|
|
|
|
|
+ _vaultEnabled = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2109,17 +2147,88 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- inline void nodeVaultPutIdentitySecret(const void *data, int len)
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ inline bool nodeVaultPutIdentity(enum ZT_StateObjectType type, const void *data, int len)
|
|
|
{
|
|
{
|
|
|
- return;
|
|
|
|
|
|
|
+ bool retval = false;
|
|
|
|
|
+ if (type != ZT_STATE_OBJECT_IDENTITY_PUBLIC && type != ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
+ return retval;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ CURL *curl = curl_easy_init();
|
|
|
|
|
+ if (curl) {
|
|
|
|
|
+ char token[512] = { 0 };
|
|
|
|
|
+ snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
|
|
|
|
|
+
|
|
|
|
|
+ struct curl_slist *chunk = NULL;
|
|
|
|
|
+ chunk = curl_slist_append(chunk, token);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ char content_type[512] = { 0 };
|
|
|
|
|
+ snprintf(content_type, sizeof(content_type), "Content-Type: application/json");
|
|
|
|
|
+
|
|
|
|
|
+ chunk = curl_slist_append(chunk, content_type);
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
|
|
|
|
+
|
|
|
|
|
+ char url[2048] = { 0 };
|
|
|
|
|
+ snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
|
+
|
|
|
|
|
+ json d = json::object();
|
|
|
|
|
+ if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
|
|
|
|
|
+ std::string key((const char*)data, len);
|
|
|
|
|
+ d["public"] = key;
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
+ std::string key((const char*)data, len);
|
|
|
|
|
+ d["secret"] = key;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!d.empty()) {
|
|
|
|
|
+ std::string post = d.dump();
|
|
|
|
|
+
|
|
|
|
|
+ if (!post.empty()) {
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str());
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post.length());
|
|
|
|
|
+
|
|
|
|
|
+#ifndef NDEBUG
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ CURLcode res = curl_easy_perform(curl);
|
|
|
|
|
+ if (res == CURLE_OK) {
|
|
|
|
|
+ long response_code = 0;
|
|
|
|
|
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
|
|
|
+ if (response_code == 200 || response_code == 204) {
|
|
|
|
|
+ retval = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_cleanup(curl);
|
|
|
|
|
+ curl = NULL;
|
|
|
|
|
+ curl_slist_free_all(chunk);
|
|
|
|
|
+ chunk = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return retval;
|
|
|
}
|
|
}
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
|
|
inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
|
|
|
{
|
|
{
|
|
|
- if (_vaultEnabled && type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
- nodeVaultPutIdentitySecret(data, len);
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
|
|
|
|
|
+ if (nodeVaultPutIdentity(type, data, len)) {
|
|
|
|
|
+ // value successfully written to Vault
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // else fallback to disk
|
|
|
}
|
|
}
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
char p[1024];
|
|
char p[1024];
|
|
|
FILE *f;
|
|
FILE *f;
|
|
@@ -2186,21 +2295,96 @@ public:
|
|
|
OSUtils::rm(p);
|
|
OSUtils::rm(p);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- inline int nodeVaultGetIdentitySecret(void *data, unsigned int maxlen)
|
|
|
|
|
|
|
+
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ inline int nodeVaultGetIdentity(enum ZT_StateObjectType type, void *data, unsigned int maxlen)
|
|
|
{
|
|
{
|
|
|
- return 0;
|
|
|
|
|
|
|
+ if (type != ZT_STATE_OBJECT_IDENTITY_SECRET && type != ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int ret = -1;
|
|
|
|
|
+ CURL *curl = curl_easy_init();
|
|
|
|
|
+ if (curl) {
|
|
|
|
|
+ char token[512] = { 0 };
|
|
|
|
|
+ snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
|
|
|
|
|
+
|
|
|
|
|
+ struct curl_slist *chunk = NULL;
|
|
|
|
|
+ chunk = curl_slist_append(chunk, token);
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
|
|
|
|
+
|
|
|
|
|
+ char url[2048] = { 0 };
|
|
|
|
|
+ snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
|
|
|
+
|
|
|
|
|
+ std::string response;
|
|
|
|
|
+ std::string res_headers;
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlResponseWrite);
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, &res_headers);
|
|
|
|
|
+
|
|
|
|
|
+#ifndef NDEBUG
|
|
|
|
|
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
|
|
+ CURLcode res = curl_easy_perform(curl);
|
|
|
|
|
+
|
|
|
|
|
+ if (res == CURLE_OK) {
|
|
|
|
|
+ long response_code = 0;
|
|
|
|
|
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
|
|
|
+ if (response_code == 200) {
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ json payload = json::parse(response);
|
|
|
|
|
+ if (!payload["data"].is_null()) {
|
|
|
|
|
+ json &d = payload["data"];
|
|
|
|
|
+ if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
+ std::string secret = OSUtils::jsonString(d["secret"],"");
|
|
|
|
|
+
|
|
|
|
|
+ if (!secret.empty()) {
|
|
|
|
|
+ ret = (int)secret.length();
|
|
|
|
|
+ memcpy(data, secret.c_str(), ret);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
|
|
|
|
|
+ std::string pub = OSUtils::jsonString(d["public"],"");
|
|
|
|
|
+
|
|
|
|
|
+ if (!pub.empty()) {
|
|
|
|
|
+ ret = (int)pub.length();
|
|
|
|
|
+ memcpy(data, pub.c_str(), ret);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (...) {
|
|
|
|
|
+ ret = -1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ curl_easy_cleanup(curl);
|
|
|
|
|
+ curl = NULL;
|
|
|
|
|
+ curl_slist_free_all(chunk);
|
|
|
|
|
+ chunk = NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ return ret;
|
|
|
}
|
|
}
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
|
|
inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
|
|
|
{
|
|
{
|
|
|
- if (_vaultEnabled && type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
- int retval = nodeVaultGetIdentitySecret(data, maxlen);
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) ) {
|
|
|
|
|
+ int retval = nodeVaultGetIdentity(type, data, maxlen);
|
|
|
if (retval >= 0)
|
|
if (retval >= 0)
|
|
|
return retval;
|
|
return retval;
|
|
|
|
|
|
|
|
// else continue file based lookup
|
|
// else continue file based lookup
|
|
|
}
|
|
}
|
|
|
|
|
+#endif
|
|
|
|
|
|
|
|
char p[4096];
|
|
char p[4096];
|
|
|
switch(type) {
|
|
switch(type) {
|
|
@@ -2229,15 +2413,17 @@ public:
|
|
|
if (f) {
|
|
if (f) {
|
|
|
int n = (int)fread(data,1,maxlen,f);
|
|
int n = (int)fread(data,1,maxlen,f);
|
|
|
fclose(f);
|
|
fclose(f);
|
|
|
-
|
|
|
|
|
- if (_vaultEnabled && type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
|
|
|
|
|
|
|
+#if ZT_VAULT_SUPPORT
|
|
|
|
|
+ if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
|
|
|
// If we've gotten here while Vault is enabled, Vault does not know the key and it's been
|
|
// If we've gotten here while Vault is enabled, Vault does not know the key and it's been
|
|
|
// read from disk instead.
|
|
// read from disk instead.
|
|
|
//
|
|
//
|
|
|
// We should put the value in Vault and remove the local file.
|
|
// We should put the value in Vault and remove the local file.
|
|
|
- nodeVaultPutIdentitySecret(data, n);
|
|
|
|
|
- unlink(p);
|
|
|
|
|
|
|
+ if (nodeVaultPutIdentity(type, data, n)) {
|
|
|
|
|
+ unlink(p);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+#endif
|
|
|
if (n >= 0)
|
|
if (n >= 0)
|
|
|
return n;
|
|
return n;
|
|
|
}
|
|
}
|