From d20eb01f68a6d63ff4b1ff52c2409cd6d45cc10f Mon Sep 17 00:00:00 2001 From: Dave Cottlehuber Date: Fri, 15 Mar 2024 05:09:13 +0000 Subject: [PATCH] net/zerotier: fix local auth, add internal webserver --- net/zerotier/Makefile | 1 + net/zerotier/files/patch-README.md | 26 ++++ ...h-controller_EmbeddedNetworkController.cpp | 26 ++++ net/zerotier/files/patch-node_InetAddress.cpp | 23 +++ .../files/patch-service_OneService.cpp | 138 ++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 net/zerotier/files/patch-README.md create mode 100644 net/zerotier/files/patch-controller_EmbeddedNetworkController.cpp create mode 100644 net/zerotier/files/patch-node_InetAddress.cpp create mode 100644 net/zerotier/files/patch-service_OneService.cpp diff --git a/net/zerotier/Makefile b/net/zerotier/Makefile index 31625cd8b1c4..b66ebf553035 100644 --- a/net/zerotier/Makefile +++ b/net/zerotier/Makefile @@ -1,5 +1,6 @@ PORTNAME= zerotier DISTVERSION= 1.12.2 +PORTREVISION= 1 CATEGORIES= net MAINTAINER= dch@FreeBSD.org diff --git a/net/zerotier/files/patch-README.md b/net/zerotier/files/patch-README.md new file mode 100644 index 000000000000..2ef84861cac6 --- /dev/null +++ b/net/zerotier/files/patch-README.md @@ -0,0 +1,26 @@ +--- README.md.orig 2023-09-14 19:09:26 UTC ++++ README.md +@@ -169,3 +169,23 @@ If there are other metrics you'd like to see tracked, + | zt_peer_packet_errors | node_id | Counter | number of incoming packet errors from a peer | + + If there are other metrics you'd like to see tracked, ask us in an Issue or send us a Pull Request! ++ ++### HTTP / App server ++ ++There is a static http file server suitable for hosting Single Page Apps at http://localhost:9993/app/ ++ ++Use `zerotier-cli info -j` to find your zerotier-one service's homeDir ++ ++``` sh ++cd $ZT_HOME ++sudo mkdir -p app/app1 ++sudo mkdir -p app/appB ++echo 'appA

hello world A' | sudo tee app/appA/index.html ++echo 'app2

hello world 2' | sudo tee app/app2/index.html ++curl -sL http://localhost:9993/app/appA http://localhost:9993/app/app2 ++``` ++ ++Then visit [http://localhost:9993/app/app1/](http://localhost:9993/app/app1/) and [http://localhost:9993/app/appB/](http://localhost:9993/app/appB/) ++ ++Requests to paths don't exist return the app root index.html, as is customary for SPAs. ++If you want, you can write some javascript that talks to the service or controller [api](https://docs.zerotier.com/service/v1). diff --git a/net/zerotier/files/patch-controller_EmbeddedNetworkController.cpp b/net/zerotier/files/patch-controller_EmbeddedNetworkController.cpp new file mode 100644 index 000000000000..1f037774fcfb --- /dev/null +++ b/net/zerotier/files/patch-controller_EmbeddedNetworkController.cpp @@ -0,0 +1,26 @@ +--- controller/EmbeddedNetworkController.cpp.orig 2023-09-14 19:09:26 UTC ++++ controller/EmbeddedNetworkController.cpp +@@ -874,6 +874,7 @@ void EmbeddedNetworkController::configureHTTPControlPl + std::string memberListPath = "/controller/network/([0-9a-fA-F]{16})/member"; + std::string memberPath = "/controller/network/([0-9a-fA-F]{16})/member/([0-9a-fA-F]{10})"; + ++ + auto controllerGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { + char tmp[4096]; + const bool dbOk = _db.isReady(); +@@ -885,11 +886,11 @@ void EmbeddedNetworkController::configureHTTPControlPl + (unsigned long long)OSUtils::now(), + dbOk ? "true" : "false"); + +- if (!dbOk) { +- res.status = 503; +- } ++ if (!dbOk) { ++ res.status = 503; ++ } + +- setContent(req, res, tmp); ++ setContent(req, res, tmp); + }; + s.Get(controllerPath, controllerGet); + sv6.Get(controllerPath, controllerGet); diff --git a/net/zerotier/files/patch-node_InetAddress.cpp b/net/zerotier/files/patch-node_InetAddress.cpp new file mode 100644 index 000000000000..2b1b7a6e4b4a --- /dev/null +++ b/net/zerotier/files/patch-node_InetAddress.cpp @@ -0,0 +1,23 @@ +--- node/InetAddress.cpp.orig 2023-09-14 19:09:26 UTC ++++ node/InetAddress.cpp +@@ -132,7 +132,20 @@ InetAddress::IpScope InetAddress::ipScope() const + return IP_SCOPE_PRIVATE; // fc00::/7 + } + } ++ ++ // :::ffff:127.0.0.1 ++ // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1 + unsigned int k = 0; ++ while ((!ip[k])&&(k < 9)) { ++ ++k; ++ } ++ if (k == 9) { ++ if (ip[10] == 0xff && ip[11] == 0xff && ip[12] == 0x7f) { ++ return IP_SCOPE_LOOPBACK; ++ } ++ } ++ ++ k = 0; + while ((!ip[k])&&(k < 15)) { + ++k; + } diff --git a/net/zerotier/files/patch-service_OneService.cpp b/net/zerotier/files/patch-service_OneService.cpp new file mode 100644 index 000000000000..884b42e88a65 --- /dev/null +++ b/net/zerotier/files/patch-service_OneService.cpp @@ -0,0 +1,138 @@ +--- service/OneService.cpp.orig 2023-09-14 19:09:26 UTC ++++ service/OneService.cpp +@@ -795,6 +795,7 @@ class OneServiceImpl : public OneService (public) + bool _allowTcpFallbackRelay; + bool _forceTcpRelay; + bool _allowSecondaryPort; ++ bool _enableWebServer; + + unsigned int _primaryPort; + unsigned int _secondaryPort; +@@ -1557,6 +1558,7 @@ class OneServiceImpl : public OneService (public) + + std::vector noAuthEndpoints { "/sso", "/health" }; + ++ + auto setContent = [=] (const httplib::Request &req, httplib::Response &res, std::string content) { + if (req.has_param("jsonp")) { + if (content.length() > 0) { +@@ -1573,8 +1575,98 @@ class OneServiceImpl : public OneService (public) + } + }; + ++ // ++ // static file server for app ui' ++ // ++ if (_enableWebServer) { ++ static std::string appUiPath = "/app"; ++ static char appUiDir[16384]; ++ sprintf(appUiDir,"%s%s",_homePath.c_str(),appUiPath.c_str()); + +- auto authCheck = [=] (const httplib::Request &req, httplib::Response &res) { ++ auto ret = _controlPlane.set_mount_point(appUiPath, appUiDir); ++ _controlPlaneV6.set_mount_point(appUiPath, appUiDir); ++ if (!ret) { ++ fprintf(stderr, "Mounting app directory failed. Creating it. Path: %s - Dir: %s\n", appUiPath.c_str(), appUiDir); ++ if (!OSUtils::mkdir(appUiDir)) { ++ fprintf(stderr, "Could not create app directory either. Path: %s - Dir: %s\n", appUiPath.c_str(), appUiDir); ++ } else { ++ ret = _controlPlane.set_mount_point(appUiPath, appUiDir); ++ _controlPlaneV6.set_mount_point(appUiPath, appUiDir); ++ if (!ret) { ++ fprintf(stderr, "Really could not create and mount directory. Path: %s - Dir: %s\nWeb apps won't work.\n", appUiPath.c_str(), appUiDir); ++ } ++ } ++ } ++ ++ if (ret) { ++ // fallback to /index.html for paths that don't exist for SPAs ++ auto indexFallbackGet = [](const httplib::Request &req, httplib::Response &res) { ++ // fprintf(stderr, "fallback \n"); ++ ++ auto match = req.matches[1]; ++ if (match.matched) { ++ ++ // fallback ++ char indexHtmlPath[16384]; ++ sprintf(indexHtmlPath,"%s/%s/%s", appUiDir, match.str().c_str(), "index.html"); ++ // fprintf(stderr, "fallback path %s\n", indexHtmlPath); ++ ++ std::string indexHtml; ++ ++ if (!OSUtils::readFile(indexHtmlPath, indexHtml)) { ++ res.status = 500; ++ return; ++ } ++ ++ res.set_content(indexHtml.c_str(), "text/html"); ++ } else { ++ res.status = 500; ++ return; ++ } ++ }; ++ ++ auto slashRedirect = [](const httplib::Request &req, httplib::Response &res) { ++ // fprintf(stderr, "redirect \n"); ++ ++ // add .html ++ std::string htmlFile; ++ char htmlPath[16384]; ++ sprintf(htmlPath,"%s%s%s", appUiDir, (req.path).substr(appUiPath.length()).c_str(), ".html"); ++ // fprintf(stderr, "path: %s\n", htmlPath); ++ if (OSUtils::readFile(htmlPath, htmlFile)) { ++ res.set_content(htmlFile.c_str(), "text/html"); ++ return; ++ } else { ++ res.status = 301; ++ res.set_header("location", req.path + "/"); ++ } ++ }; ++ ++ // auto missingAssetGet = [&, setContent](const httplib::Request &req, httplib::Response &res) { ++ // fprintf(stderr, "missing \n"); ++ // res.status = 404; ++ // std::string html = "oops"; ++ // res.set_content(html, "text/plain"); ++ // res.set_header("Content-Type", "text/plain"); ++ // return; ++ // }; ++ ++ // auto fix no trailing slash by adding .html or redirecting to path/ ++ _controlPlane.Get(appUiPath + R"((/[\w|-]+)+$)", slashRedirect); ++ _controlPlaneV6.Get(appUiPath + R"((/[\w|-]+)+$)", slashRedirect); ++ ++ // // 404 missing assets for *.ext paths ++ // s.Get(appUiPath + R"(/\.\w+$)", missingAssetGet); ++ // sv6.Get(appUiPath + R"(/\.\w+$)", missingAssetGet); ++ ++ // fallback to index.html for unknown paths/files ++ _controlPlane.Get(appUiPath + R"((/[\w|-]+)(/[\w|-]+)*/$)", indexFallbackGet); ++ _controlPlaneV6.Get(appUiPath + R"((/[\w|-]+)(/[\w|-]+)*/$)", indexFallbackGet); ++ ++ } ++ } ++ ++ auto authCheck = [=] (const httplib::Request &req, httplib::Response &res) { + if (req.path == "/metrics") { + + if (req.has_header("x-zt1-auth")) { +@@ -1624,6 +1716,11 @@ class OneServiceImpl : public OneService (public) + isAuth = true; + } + ++ // Web Apps base path ++ if (req.path.rfind("/app", 0) == 0) { //starts with /app ++ isAuth = true; ++ } ++ + if (!isAuth) { + // check auth token + if (req.has_header("x-zt1-auth")) { +@@ -2429,6 +2526,7 @@ class OneServiceImpl : public OneService (public) + // bondingPolicy cannot be used with allowTcpFallbackRelay + _allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true); + _forceTcpRelay = OSUtils::jsonBool(settings["forceTcpRelay"],false); ++ _enableWebServer = (OSUtils::jsonBool(settings["enableWebServer"],false)); + + #ifdef ZT_TCP_FALLBACK_RELAY + _fallbackRelayAddress = InetAddress(OSUtils::jsonString(settings["tcpFallbackRelay"], ZT_TCP_FALLBACK_RELAY).c_str()); -- 2.38.5