Loading CMakeLists.txt +6 −0 Changes for CMakeLists.txt: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -98,6 +98,12 @@ install(TARGETS proxyplus-bootstrap RUNTIME DESTINATION bin) set(PROXYPLUS_PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/proxyplus/plugins" CACHE PATH "Directory plugin .so modules install into (must match paths.plugins in config.yaml)") # proxyplus-bootstrap writes a missing config from the installed example, with paths.plugins # set to where the plugins really are. target_compile_definitions(proxyplus-bootstrap PRIVATE PROXYPLUS_EXAMPLE_CONFIG="${CMAKE_INSTALL_PREFIX}/share/proxyplus/examples/proxyplus.yaml" PROXYPLUS_PLUGIN_DIR="${PROXYPLUS_PLUGIN_INSTALL_DIR}") add_subdirectory(plugins/example_dnsbl) add_subdirectory(plugins/acme_common) add_subdirectory(plugins/letsencrypt) Loading tools/proxyplus-bootstrap.cpp +111 −3 Changes for tools/proxyplus-bootstrap.cpp: 111 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -13,9 +13,12 @@ // 4. Creates one GPO per permission in docs/ROLES.md's list, with those groups // resolved to their real ids as allowed members, and records permission->GPO-id into // ProxyPlus's own binary ConfigStore at <data-dir> (never into authdb itself). // 5. Writes the bootstrap config (default /etc/proxyplus/proxyplus.yaml) if there is none // yet: the installed example config with paths.authdb, paths.data and paths.plugins // set for this node. An existing config is left alone. // // Usage: // proxyplus-bootstrap [--insecure] [--client-name <name>] // proxyplus-bootstrap [--insecure] [--client-name <name>] [--config <path>] // <authdb-url> <admin-username> <admin-password> <data-dir> // // The service client is named "proxyplus-service" unless --client-name says otherwise. Loading @@ -24,9 +27,11 @@ #include <algorithm> #include <cctype> #include <filesystem> #include <fstream> #include <iostream> #include <map> #include <sstream> #include <string> #include <vector> Loading Loading @@ -142,6 +147,102 @@ std::string askOtherClientName(const std::string &taken) { return name; } #ifndef PROXYPLUS_EXAMPLE_CONFIG #define PROXYPLUS_EXAMPLE_CONFIG "/usr/local/share/proxyplus/examples/proxyplus.yaml" #endif #ifndef PROXYPLUS_PLUGIN_DIR #define PROXYPLUS_PLUGIN_DIR "/usr/local/lib/proxyplus/plugins" #endif const char *const kDefaultConfigPath = "/etc/proxyplus/proxyplus.yaml"; // YAML double-quoted scalar. std::string yamlQuote(const std::string &s) { std::string out = "\""; for (char c : s) { if (c == '"' || c == '\\') out += '\\'; out += c; } return out + "\""; } // The example config with the given proxyplus.paths entries set (values are YAML, already // quoted where needed): an entry the example has is replaced in place -- only lines at // exactly the paths block's child indentation, so the documentation comments and every // other setting stay as they are -- and one it lacks is added at the end of that block. std::string configFromExample(const std::string &example, const std::map<std::string, std::string> &paths) { std::istringstream in(example); std::ostringstream out; std::string line; bool inPaths = false; std::map<std::string, std::string> missing = paths; auto addMissing = [&]() { for (const auto &kv : missing) out << " " << kv.first << ": " << kv.second << '\n'; missing.clear(); }; while (std::getline(in, line)) { size_t indent = line.find_first_not_of(' '); bool content = indent != std::string::npos && line[indent] != '#'; if (content && line.compare(indent, 6, "paths:") == 0) { inPaths = true; } else if (content && inPaths && indent <= 2) { inPaths = false; addMissing(); } else if (content && inPaths && indent == 4) { size_t colon = line.find(':', indent); std::string key = colon == std::string::npos ? "" : line.substr(indent, colon - indent); auto it = paths.find(key); if (it != paths.end()) { line = " " + key + ": " + it->second; missing.erase(key); } } out << line << '\n'; } if (inPaths) addMissing(); return out.str(); } // Writes `path` from the installed example config unless it already exists. Returns false // only on an actual error; an existing config is a success (left untouched). bool writeConfigIfMissing(const std::string &path, const std::string &authdbUrl, const std::string &dataDir, bool authdbTlsVerify) { std::error_code ec; if (std::filesystem::exists(path, ec)) { std::cerr << "kept existing config " << path << std::endl; return true; } std::ifstream ex(PROXYPLUS_EXAMPLE_CONFIG); if (!ex) { std::cerr << "cannot write " << path << ": example config " << PROXYPLUS_EXAMPLE_CONFIG << " not found" << std::endl; return false; } std::stringstream example; example << ex.rdbuf(); std::map<std::string, std::string> paths = { {"data", yamlQuote(dataDir)}, {"authdb", yamlQuote(authdbUrl)}, {"plugins", yamlQuote(PROXYPLUS_PLUGIN_DIR)}}; // bootstrapped with --insecure: ProxyPlus itself reaches authdb the same way if (!authdbTlsVerify) paths["authdb_tls_verify"] = "false"; std::string config = configFromExample(example.str(), paths); std::filesystem::path p(path); if (p.has_parent_path()) std::filesystem::create_directories(p.parent_path(), ec); std::ofstream out(path, std::ios::trunc); out << config; out.close(); if (!out) { std::cerr << "cannot write " << path << std::endl; return false; } std::cerr << "wrote " << path << " (from " << PROXYPLUS_EXAMPLE_CONFIG << "; review node, listen and process settings)" << std::endl; return true; } } // namespace int main(int argc, char **argv) { Loading @@ -150,26 +251,31 @@ int main(int argc, char **argv) { std::vector<std::string> args; bool tlsVerify = true; std::string serviceClientName = "proxyplus-service"; std::string configPath = kDefaultConfigPath; for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--insecure") { tlsVerify = false; } else if (arg == "--client-name" && i + 1 < argc) { serviceClientName = argv[++i]; } else if (arg == "--config" && i + 1 < argc) { configPath = argv[++i]; } else { args.push_back(arg); } } if (args.size() < 4 || serviceClientName.empty()) { if (args.size() < 4 || serviceClientName.empty() || configPath.empty()) { std::cerr << "usage: " << argv[0] << " [--insecure] [--client-name <name>]" << " [--insecure] [--client-name <name>] [--config <path>]" " <authdb-url> <admin-username> <admin-password> <data-dir>" << std::endl; std::cerr << " --insecure skip TLS peer verification (needed when authdb is reached" " by an IP not covered by its certificate's SAN)" << std::endl; std::cerr << " --client-name name of the service client to create in authdb" " (default: proxyplus-service)" << std::endl; std::cerr << " --config bootstrap config to write if missing (default: " << kDefaultConfigPath << ")" << std::endl; return 1; } std::string url = args[0], username = args[1], password = args[2], dataDir = args[3]; Loading Loading @@ -285,5 +391,7 @@ int main(int argc, char **argv) { std::ofstream credFile(dataDir + "/service-credential", std::ios::trunc); credFile << serviceClientName << ":" << serviceClientSecret; std::cerr << "wrote " << dataDir << "/service-credential" << std::endl; if (!writeConfigIfMissing(configPath, url, dataDir, tlsVerify)) return 1; return 0; } Loading
CMakeLists.txt +6 −0 Changes for CMakeLists.txt: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -98,6 +98,12 @@ install(TARGETS proxyplus-bootstrap RUNTIME DESTINATION bin) set(PROXYPLUS_PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib/proxyplus/plugins" CACHE PATH "Directory plugin .so modules install into (must match paths.plugins in config.yaml)") # proxyplus-bootstrap writes a missing config from the installed example, with paths.plugins # set to where the plugins really are. target_compile_definitions(proxyplus-bootstrap PRIVATE PROXYPLUS_EXAMPLE_CONFIG="${CMAKE_INSTALL_PREFIX}/share/proxyplus/examples/proxyplus.yaml" PROXYPLUS_PLUGIN_DIR="${PROXYPLUS_PLUGIN_INSTALL_DIR}") add_subdirectory(plugins/example_dnsbl) add_subdirectory(plugins/acme_common) add_subdirectory(plugins/letsencrypt) Loading
tools/proxyplus-bootstrap.cpp +111 −3 Changes for tools/proxyplus-bootstrap.cpp: 111 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -13,9 +13,12 @@ // 4. Creates one GPO per permission in docs/ROLES.md's list, with those groups // resolved to their real ids as allowed members, and records permission->GPO-id into // ProxyPlus's own binary ConfigStore at <data-dir> (never into authdb itself). // 5. Writes the bootstrap config (default /etc/proxyplus/proxyplus.yaml) if there is none // yet: the installed example config with paths.authdb, paths.data and paths.plugins // set for this node. An existing config is left alone. // // Usage: // proxyplus-bootstrap [--insecure] [--client-name <name>] // proxyplus-bootstrap [--insecure] [--client-name <name>] [--config <path>] // <authdb-url> <admin-username> <admin-password> <data-dir> // // The service client is named "proxyplus-service" unless --client-name says otherwise. Loading @@ -24,9 +27,11 @@ #include <algorithm> #include <cctype> #include <filesystem> #include <fstream> #include <iostream> #include <map> #include <sstream> #include <string> #include <vector> Loading Loading @@ -142,6 +147,102 @@ std::string askOtherClientName(const std::string &taken) { return name; } #ifndef PROXYPLUS_EXAMPLE_CONFIG #define PROXYPLUS_EXAMPLE_CONFIG "/usr/local/share/proxyplus/examples/proxyplus.yaml" #endif #ifndef PROXYPLUS_PLUGIN_DIR #define PROXYPLUS_PLUGIN_DIR "/usr/local/lib/proxyplus/plugins" #endif const char *const kDefaultConfigPath = "/etc/proxyplus/proxyplus.yaml"; // YAML double-quoted scalar. std::string yamlQuote(const std::string &s) { std::string out = "\""; for (char c : s) { if (c == '"' || c == '\\') out += '\\'; out += c; } return out + "\""; } // The example config with the given proxyplus.paths entries set (values are YAML, already // quoted where needed): an entry the example has is replaced in place -- only lines at // exactly the paths block's child indentation, so the documentation comments and every // other setting stay as they are -- and one it lacks is added at the end of that block. std::string configFromExample(const std::string &example, const std::map<std::string, std::string> &paths) { std::istringstream in(example); std::ostringstream out; std::string line; bool inPaths = false; std::map<std::string, std::string> missing = paths; auto addMissing = [&]() { for (const auto &kv : missing) out << " " << kv.first << ": " << kv.second << '\n'; missing.clear(); }; while (std::getline(in, line)) { size_t indent = line.find_first_not_of(' '); bool content = indent != std::string::npos && line[indent] != '#'; if (content && line.compare(indent, 6, "paths:") == 0) { inPaths = true; } else if (content && inPaths && indent <= 2) { inPaths = false; addMissing(); } else if (content && inPaths && indent == 4) { size_t colon = line.find(':', indent); std::string key = colon == std::string::npos ? "" : line.substr(indent, colon - indent); auto it = paths.find(key); if (it != paths.end()) { line = " " + key + ": " + it->second; missing.erase(key); } } out << line << '\n'; } if (inPaths) addMissing(); return out.str(); } // Writes `path` from the installed example config unless it already exists. Returns false // only on an actual error; an existing config is a success (left untouched). bool writeConfigIfMissing(const std::string &path, const std::string &authdbUrl, const std::string &dataDir, bool authdbTlsVerify) { std::error_code ec; if (std::filesystem::exists(path, ec)) { std::cerr << "kept existing config " << path << std::endl; return true; } std::ifstream ex(PROXYPLUS_EXAMPLE_CONFIG); if (!ex) { std::cerr << "cannot write " << path << ": example config " << PROXYPLUS_EXAMPLE_CONFIG << " not found" << std::endl; return false; } std::stringstream example; example << ex.rdbuf(); std::map<std::string, std::string> paths = { {"data", yamlQuote(dataDir)}, {"authdb", yamlQuote(authdbUrl)}, {"plugins", yamlQuote(PROXYPLUS_PLUGIN_DIR)}}; // bootstrapped with --insecure: ProxyPlus itself reaches authdb the same way if (!authdbTlsVerify) paths["authdb_tls_verify"] = "false"; std::string config = configFromExample(example.str(), paths); std::filesystem::path p(path); if (p.has_parent_path()) std::filesystem::create_directories(p.parent_path(), ec); std::ofstream out(path, std::ios::trunc); out << config; out.close(); if (!out) { std::cerr << "cannot write " << path << std::endl; return false; } std::cerr << "wrote " << path << " (from " << PROXYPLUS_EXAMPLE_CONFIG << "; review node, listen and process settings)" << std::endl; return true; } } // namespace int main(int argc, char **argv) { Loading @@ -150,26 +251,31 @@ int main(int argc, char **argv) { std::vector<std::string> args; bool tlsVerify = true; std::string serviceClientName = "proxyplus-service"; std::string configPath = kDefaultConfigPath; for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--insecure") { tlsVerify = false; } else if (arg == "--client-name" && i + 1 < argc) { serviceClientName = argv[++i]; } else if (arg == "--config" && i + 1 < argc) { configPath = argv[++i]; } else { args.push_back(arg); } } if (args.size() < 4 || serviceClientName.empty()) { if (args.size() < 4 || serviceClientName.empty() || configPath.empty()) { std::cerr << "usage: " << argv[0] << " [--insecure] [--client-name <name>]" << " [--insecure] [--client-name <name>] [--config <path>]" " <authdb-url> <admin-username> <admin-password> <data-dir>" << std::endl; std::cerr << " --insecure skip TLS peer verification (needed when authdb is reached" " by an IP not covered by its certificate's SAN)" << std::endl; std::cerr << " --client-name name of the service client to create in authdb" " (default: proxyplus-service)" << std::endl; std::cerr << " --config bootstrap config to write if missing (default: " << kDefaultConfigPath << ")" << std::endl; return 1; } std::string url = args[0], username = args[1], password = args[2], dataDir = args[3]; Loading Loading @@ -285,5 +391,7 @@ int main(int argc, char **argv) { std::ofstream credFile(dataDir + "/service-credential", std::ios::trunc); credFile << serviceClientName << ":" << serviceClientSecret; std::cerr << "wrote " << dataDir << "/service-credential" << std::endl; if (!writeConfigIfMissing(configPath, url, dataDir, tlsVerify)) return 1; return 0; }