Add noupdate flag in root topologies, add ability for mktopology to read from a template.

This commit is contained in:
Adam Ierymenko 2014-09-03 11:56:36 -07:00
parent 644db7a04a
commit dcea212e40
3 changed files with 43 additions and 4 deletions

View File

@ -228,6 +228,7 @@ struct _NodeImpl
volatile bool started; volatile bool started;
volatile bool running; volatile bool running;
volatile bool resynchronize; volatile bool resynchronize;
volatile bool disableRootTopologyUpdates;
// This function performs final node tear-down // This function performs final node tear-down
inline Node::ReasonForTermination terminate() inline Node::ReasonForTermination terminate()
@ -395,6 +396,7 @@ Node::Node(
impl->started = false; impl->started = false;
impl->running = false; impl->running = false;
impl->resynchronize = false; impl->resynchronize = false;
impl->disableRootTopologyUpdates = false;
} }
Node::~Node() Node::~Node()
@ -471,6 +473,7 @@ Node::ReasonForTermination Node::run()
// Create non-crypto PRNG right away in case other code in init wants to use it // Create non-crypto PRNG right away in case other code in init wants to use it
_r->prng = new CMWC4096(); _r->prng = new CMWC4096();
// Read identity public and secret, generating if not present
bool gotId = false; bool gotId = false;
std::string identitySecretPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.secret"); std::string identitySecretPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.secret");
std::string identityPublicPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.public"); std::string identityPublicPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.public");
@ -511,6 +514,7 @@ Node::ReasonForTermination Node::run()
#endif #endif
} }
// Read configuration authentication token, generating if not present
std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret"); std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
std::string configAuthToken; std::string configAuthToken;
if (!Utils::readFile(configAuthTokenPath.c_str(),configAuthToken)) { if (!Utils::readFile(configAuthTokenPath.c_str(),configAuthToken)) {
@ -546,18 +550,26 @@ Node::ReasonForTermination Node::run()
} }
#endif #endif
// Initialize root topology from defaults or root-toplogy file in home path on disk
std::string rootTopologyPath(_r->homePath + ZT_PATH_SEPARATOR_S + "root-topology"); std::string rootTopologyPath(_r->homePath + ZT_PATH_SEPARATOR_S + "root-topology");
std::string rootTopology; std::string rootTopology;
if (!Utils::readFile(rootTopologyPath.c_str(),rootTopology)) if (!Utils::readFile(rootTopologyPath.c_str(),rootTopology))
rootTopology = ZT_DEFAULTS.defaultRootTopology; rootTopology = ZT_DEFAULTS.defaultRootTopology;
try { try {
Dictionary rt(rootTopology); Dictionary rt(rootTopology);
if (Topology::authenticateRootTopology(rt)) { if (Topology::authenticateRootTopology(rt)) {
_r->topology->setSupernodes(Dictionary(rt.get("supernodes"))); // Set supernodes if root topology signature is valid
_r->topology->setSupernodes(Dictionary(rt.get("supernodes",""))); // set supernodes from root-topology
// If root-topology contains noupdate=1, disable further updates and only use what was on disk
impl->disableRootTopologyUpdates = (Utils::strToInt(rt.get("noupdate","0").c_str()) > 0);
} else { } else {
// Revert to built-in defaults if root topology fails signature check
LOG("%s failed signature check, using built-in defaults instead",rootTopologyPath.c_str()); LOG("%s failed signature check, using built-in defaults instead",rootTopologyPath.c_str());
Utils::rm(rootTopologyPath.c_str()); Utils::rm(rootTopologyPath.c_str());
_r->topology->setSupernodes(Dictionary(Dictionary(ZT_DEFAULTS.defaultRootTopology).get("supernodes"))); _r->topology->setSupernodes(Dictionary(Dictionary(ZT_DEFAULTS.defaultRootTopology).get("supernodes","")));
impl->disableRootTopologyUpdates = false;
} }
} catch ( ... ) { } catch ( ... ) {
return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"invalid root-topology format"); return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"invalid root-topology format");
@ -761,10 +773,13 @@ Node::ReasonForTermination Node::run()
_r->sm->send(ZT_DEFAULTS.v4Broadcast,false,false,bcn,ZT_PROTO_BEACON_LENGTH); _r->sm->send(ZT_DEFAULTS.v4Broadcast,false,false,bcn,ZT_PROTO_BEACON_LENGTH);
} }
// Check for updates to root topology (supernodes) periodically
if ((now - lastRootTopologyFetch) >= ZT_UPDATE_ROOT_TOPOLOGY_CHECK_INTERVAL) { if ((now - lastRootTopologyFetch) >= ZT_UPDATE_ROOT_TOPOLOGY_CHECK_INTERVAL) {
lastRootTopologyFetch = now; lastRootTopologyFetch = now;
TRACE("fetching root topology from %s",ZT_DEFAULTS.rootTopologyUpdateURL.c_str()); if (!impl->disableRootTopologyUpdates) {
_r->http->GET(ZT_DEFAULTS.rootTopologyUpdateURL,HttpClient::NO_HEADERS,60,&_cbHandleGetRootTopology,_r); TRACE("fetching root topology from %s",ZT_DEFAULTS.rootTopologyUpdateURL.c_str());
_r->http->GET(ZT_DEFAULTS.rootTopologyUpdateURL,HttpClient::NO_HEADERS,60,&_cbHandleGetRootTopology,_r);
}
} }
// Sleep for loop interval or until something interesting happens. // Sleep for loop interval or until something interesting happens.

View File

@ -1,5 +1,16 @@
This folder contains the source files to compile the signed network root topology dictionary. Users outside ZeroTier won't find this useful except for testing, since the root topology must be signed by the root topology authority (public identity in root-topology-authority.public) to be considered valid. This folder contains the source files to compile the signed network root topology dictionary. Users outside ZeroTier won't find this useful except for testing, since the root topology must be signed by the root topology authority (public identity in root-topology-authority.public) to be considered valid.
Keys in the root topology dictionary are:
* **supernodes**: contains another Dictionary mapping supernode address to supernode definition
* **##########**: supernode address, contains supernode definition
* **id**: supernode identity (public) in string-serialized format
* **udp**: comma-delimited list of ip/port UDP addresses of node
* **tcp**: comma-delimited list of ip/port TCP addresses of node
* **desc**: human-readable description (optional)
* **dns**: DNS name (optional, not currently used for anything)
* **noupdate**: if the value of this is '1', do not auto-update from ZeroTier's servers
ZT_DEFAULT_ROOT_TOPOLOGY.c contains the current default value, and this URL is periodically checked for updates: ZT_DEFAULT_ROOT_TOPOLOGY.c contains the current default value, and this URL is periodically checked for updates:
http://download.zerotier.com/net/topology/ROOT http://download.zerotier.com/net/topology/ROOT

View File

@ -16,6 +16,7 @@ int main(int argc,char **argv)
{ {
std::string buf; std::string buf;
// Read root-topology-authority.secret signing authority, must be symlinked and online
if (!Utils::readFile("root-topology-authority.secret",buf)) { if (!Utils::readFile("root-topology-authority.secret",buf)) {
std::cerr << "Cannot read root-topology-authority.secret" << std::endl; std::cerr << "Cannot read root-topology-authority.secret" << std::endl;
return 1; return 1;
@ -24,6 +25,14 @@ int main(int argc,char **argv)
Dictionary topology; Dictionary topology;
// Read template.dict to populate default fields in root topology
// if this file exists. Otherwise we just start empty.
buf.clear();
if (Utils::readFile("template.dict",buf))
topology.fromString(buf);
// Read all entries in supernodes/ that correspond to supernode entry dictionaries
// and add them to topology under supernodes/ subkey.
Dictionary supernodes; Dictionary supernodes;
std::map<std::string,bool> supernodeDictionaries(Utils::listDirectory("supernodes")); std::map<std::string,bool> supernodeDictionaries(Utils::listDirectory("supernodes"));
for(std::map<std::string,bool>::iterator sn(supernodeDictionaries.begin());sn!=supernodeDictionaries.end();++sn) { for(std::map<std::string,bool>::iterator sn(supernodeDictionaries.begin());sn!=supernodeDictionaries.end();++sn) {
@ -38,17 +47,21 @@ int main(int argc,char **argv)
} }
topology["supernodes"] = supernodes.toString(); topology["supernodes"] = supernodes.toString();
// Sign topology with root-topology-authority.secret
if (!topology.sign(topologyAuthority)) { if (!topology.sign(topologyAuthority)) {
std::cerr << "Unable to sign!" << std::endl; std::cerr << "Unable to sign!" << std::endl;
return 1; return 1;
} }
// Test signature to make sure signing worked
Dictionary test(topology.toString()); Dictionary test(topology.toString());
if (!test.verify(topologyAuthority)) { if (!test.verify(topologyAuthority)) {
std::cerr << "Test verification of signed dictionary failed!" << std::endl; std::cerr << "Test verification of signed dictionary failed!" << std::endl;
return 1; return 1;
} }
// Output to stdout
std::cout << topology.toString(); std::cout << topology.toString();
return 0; return 0;
} }