Skip to content
Snippets Groups Projects
Commit 2ee14f02 authored by Amna Snene's avatar Amna Snene Committed by Louis Maillard
Browse files

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
parent 77e27dcb
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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,
......
......@@ -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
......@@ -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,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment