diff --git a/extras/packaging/gnu-linux/debian/postinst b/extras/packaging/gnu-linux/debian/postinst index 33a7e8e11b88d549104ff034381766251da2a938..39ad146ec20fa8eca45a8232e9d49edf1dceb9ee 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 968a9364473bc181e2a6ecbdd828e52716aebbdf..4ad8dfe830ca2c7d74825a1938f7170f1567b391 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 764945ccfe6dbf070a68c9334fc42120c2407991..86bb159385c3d3126ce0b362f1dac8831d7322e9 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 665d2a0af5154b51d033536136e1b9d87d5a10a2..6e5537dfa63c602423c0e4c4606845284842332d 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 412ab08e92874778399d8b1b781440988f67e33f..f1b06ea28ea825d090bce3092509864c6e7ae5c5 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 e8a89e9781b48d680235a72ec9c9f3e62bfd4782..cf989457e92796412df98704426549c90e319ec5 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,