From 2ee14f029fa845c1483ce2e009e0d3f777893c0f Mon Sep 17 00:00:00 2001 From: Amna <amna.snene@savoirfairelinux.com> Date: Wed, 24 Jul 2024 15:15:55 -0400 Subject: [PATCH] tools/dnc: add authorized services on server side The client requests to open a socket on ip:port on the server side via the channel name. On connexion request, the server checks if the ip:port requested is authorized. Also add a new option in config files to configure allow-list. Change-Id: I85253fce83874e3c1b83e545d9e4139e04a09972 --- extras/packaging/gnu-linux/debian/postinst | 9 +++++ tools/dhtnet_crtmgr/main.cpp | 11 ++++++ tools/dnc/dnc.cpp | 41 +++++++++++++++++++--- tools/dnc/dnc.h | 3 +- tools/dnc/dnc.yaml | 10 ++++++ tools/dnc/main.cpp | 23 ++++++++++-- 6 files changed, 90 insertions(+), 7 deletions(-) diff --git a/extras/packaging/gnu-linux/debian/postinst b/extras/packaging/gnu-linux/debian/postinst index 33a7e8e..39ad146 100644 --- a/extras/packaging/gnu-linux/debian/postinst +++ b/extras/packaging/gnu-linux/debian/postinst @@ -68,6 +68,15 @@ configure_yaml() { echo "# When anonymous is set to false, the server allows only connection which are issued by the same CA as the server" echo "anonymous: false" echo "" + echo "# List of authorized services" + echo "# Each service is defined by an IP and a port" + echo "authorized_services:" + echo " - ip: \"127.0.0.1\"" + echo " port: 22" + echo " # - ip: \"127.0.0.1\"" + echo " # port: 80" + echo " # - ip: \"127.0.0.1\"" + echo " # port: 443" echo "" } > /etc/dhtnet/dnc.yaml fi diff --git a/tools/dhtnet_crtmgr/main.cpp b/tools/dhtnet_crtmgr/main.cpp index 968a936..4ad8dfe 100644 --- a/tools/dhtnet_crtmgr/main.cpp +++ b/tools/dhtnet_crtmgr/main.cpp @@ -135,6 +135,17 @@ int create_yaml_config(std::filesystem::path file, std::filesystem::path certifi yaml_file << "\n# When anonymous is set to true, the server accepts any connection without checking CA\n"; yaml_file << "# When anonymous is set to false, the server allows only connection which are issued by the same CA as the server\n"; yaml_file << "anonymous: false\n"; + + yaml_file << "\n# List of authorized services\n"; + yaml_file << "# Each service is defined by an IP and a port\n"; + yaml_file << "# If no authorized services are defined, the server will accept any connection.\n"; + yaml_file << "authorized_services:\n"; + yaml_file << " - ip: \"127.0.0.1\"\n"; + yaml_file << " port: 22\n"; + yaml_file << " # - ip: \"127.0.0.1\"\n"; + yaml_file << " # port: 80\n"; + yaml_file << " # - ip: \"127.0.0.1\"\n"; + yaml_file << " # port: 443\n"; } yaml_file.close(); fmt::print("Configuration file created in {}\n", file); diff --git a/tools/dnc/dnc.cpp b/tools/dnc/dnc.cpp index 764945c..86bb159 100644 --- a/tools/dnc/dnc.cpp +++ b/tools/dnc/dnc.cpp @@ -60,7 +60,8 @@ Dnc::Dnc(dht::crypto::Identity identity, const std::string& turn_pass, const std::string& turn_realm, const bool anonymous, - const bool verbose) + const bool verbose, + const std::map<std::string, std::vector<int>> authorized_services) :logger(verbose ? dht::log::getStdLogger() : nullptr), ioContext(std::make_shared<asio::io_context>()), iceFactory(std::make_shared<IceTransportFactory>(logger)) @@ -104,9 +105,40 @@ Dnc::Dnc(dht::crypto::Identity identity, std::unique_lock lk {mtx}; connectionManager->onChannelRequest( - [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) { + [authorized_services, this](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) { // handle channel request - fmt::print("Channel request received: {}\n", name); + if (authorized_services.empty()) { + // Accept all connections if no authorized services are provided + return true; + } + // parse channel name to get the ip address and port: nc://<ip>:<port> + auto parsedName = parseName(name); + const std::string &ip = parsedName.first; + int port = 0; + try { + port = std::stoi(parsedName.second); + } + catch (std::exception const &err) { + fmt::print(stderr, "Rejecting connection: port '{}' is not a valid number", parsedName.second); + return false; + } + + // Check if the IP is authorized + auto it = authorized_services.find(ip); + if (it == authorized_services.end()) { + // Reject the connection if the ip is not authorized + fmt::print("Rejecting connection to {}:{}", ip, port); + return false; + } + + // Check if the port is authorized + const auto &ports = it->second; + if (std::find(ports.begin(), ports.end(), port) == ports.end()) { + // Reject the connection if the port is not authorized + fmt::print("Rejecting connection to {}:{}", ip, port); + return false; + } + fmt::print("Accepting connection to {}:{}", ip, port); return true; }); @@ -175,10 +207,11 @@ Dnc::Dnc(dht::crypto::Identity identity, const std::string& turn_pass, const std::string& turn_realm, const bool verbose) - : Dnc(identity, bootstrap,turn_host,turn_user,turn_pass, turn_realm, true, verbose) + : Dnc(identity, bootstrap,turn_host,turn_user,turn_pass, turn_realm, true, verbose, {}) { std::condition_variable cv; auto name = fmt::format("nc://{:s}:{:d}", remote_host, remote_port); + fmt::print("Requesting socket: %s\n", name.c_str()); connectionManager->connectDevice( peer_id, name, [&](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) { if (socket) { diff --git a/tools/dnc/dnc.h b/tools/dnc/dnc.h index 665d2a0..6e5537d 100644 --- a/tools/dnc/dnc.h +++ b/tools/dnc/dnc.h @@ -40,7 +40,8 @@ public: const std::string& turn_pass, const std::string& turn_realm, const bool anonymous, - const bool verbose); + const bool verbose, + const std::map<std::string, std::vector<int>> authorized_services); // Build a client Dnc( dht::crypto::Identity identity, diff --git a/tools/dnc/dnc.yaml b/tools/dnc/dnc.yaml index 412ab08..f1b06ea 100644 --- a/tools/dnc/dnc.yaml +++ b/tools/dnc/dnc.yaml @@ -39,3 +39,13 @@ port: 22 # When anonymous is set to false, the server allows only connection which are issued by the same CA as the server anonymous: false +# List of authorized services +# Each service is defined by an IP and a port +# If no authorized services are defined, the server will accept any connection. +authorized_services: + - ip: "127.0.0.1" + port: 22 + # - ip: "127.0.0.1" + # port: 80 + # - ip: "127.0.0.1" + # port: 443 diff --git a/tools/dnc/main.cpp b/tools/dnc/main.cpp index e8a89e9..cf98945 100644 --- a/tools/dnc/main.cpp +++ b/tools/dnc/main.cpp @@ -50,6 +50,7 @@ struct dhtnc_params std::string configuration {}; bool anonymous_cnx {false}; bool verbose {false}; + std::map<std::string, std::vector<int>> authorizedServices {}; }; static const constexpr struct option long_options[] @@ -172,7 +173,7 @@ parse_args(int argc, char** argv) params.cert = config["certificate"].as<std::string>(); } if (config["ip"] && params.remote_host.empty()) { - params.configuration = config["ip"].as<std::string>(); + params.remote_host = config["ip"].as<std::string>(); } if (config["port"] && params.remote_port == 0) { params.remote_port = config["port"].as<int>(); @@ -183,6 +184,23 @@ parse_args(int argc, char** argv) if (config["verbose"] && !params.verbose) { params.verbose = config["verbose"].as<bool>(); } + if (config["authorized_services"]) { + for (auto service : config["authorized_services"]) { + std::string ip = service["ip"].as<std::string>(); + int port = 0; + try { + port = service["port"].as<int>(); + } catch (YAML::TypedBadConversion<int> e) { + std::cerr << "Error: Invalid port number in configuration file.\n"; + exit(EXIT_FAILURE); + } + if (port < 1 || port > 65535 || ip.empty()) { + std::cerr << "Error: Invalid ip or port number in configuration file.\n"; + exit(EXIT_FAILURE); + } + params.authorizedServices[ip].push_back(port); + } + } } } return params; @@ -253,7 +271,8 @@ main(int argc, char** argv) params.turn_pass, params.turn_realm, params.anonymous_cnx, - params.verbose); + params.verbose, + params.authorizedServices); } else { dhtnc = std::make_unique<dhtnet::Dnc>(identity, params.bootstrap, -- GitLab