]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Implement webserver acl
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Fri, 29 Nov 2024 13:19:34 +0000 (14:19 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 11 Feb 2025 15:28:22 +0000 (16:28 +0100)
pdns/recursordist/settings/cxxsupport.cc
pdns/recursordist/settings/rust/build.rs
pdns/recursordist/settings/rust/src/bridge.hh
pdns/recursordist/settings/rust/src/web.rs
pdns/recursordist/ws-recursor.cc

index b6118e4634afe3a1a2147b3d73710d5271a6848d..bd5084b6cefb11377b9e45c8cc04622c1a15ac82 100644 (file)
@@ -39,6 +39,8 @@
 #include "base64.hh"
 #include "validate-recursor.hh"
 #include "threadname.hh"
+#include "iputils.hh"
+#include "bridge.hh"
 
 ::rust::Vec<::rust::String> pdns::settings::rec::getStrings(const std::string& name)
 {
@@ -1454,3 +1456,39 @@ bool pdns::rust::settings::rec::isValidHostname(::rust::Str str)
     return false;
   }
 }
+
+namespace pdns::rust::web::rec
+{
+NetmaskGroup::NetmaskGroup(const ::NetmaskGroup& arg) :
+  d_ptr(std::make_unique<::NetmaskGroup>(arg))
+{
+}
+NetmaskGroup::~NetmaskGroup() = default;
+
+
+ComboAddress::ComboAddress(const ::ComboAddress& arg) :
+  d_ptr(std::make_unique<::ComboAddress>(arg))
+{
+}
+ComboAddress::~ComboAddress() = default;
+
+std::unique_ptr<ComboAddress> comboaddress(::rust::Str str)
+{
+  return std::make_unique<ComboAddress>(::ComboAddress(std::string(str)));
+}
+
+[[nodiscard]] const ::NetmaskGroup& NetmaskGroup::get() const
+{
+  return *d_ptr;
+}
+
+[[nodiscard]] const ::ComboAddress& ComboAddress::get() const
+{
+  return *d_ptr;
+}
+  
+bool matches(const std::unique_ptr<NetmaskGroup>& nmg, const std::unique_ptr<ComboAddress>& address)
+{
+  return nmg->get().match(address->get());
+}
+}
index e0fdf17c9c530b41430a09fd3f49e4cc2f36b03f..cdf64ab167a52375ba6c9088632ba4367d70e706 100644 (file)
@@ -4,5 +4,6 @@ fn main() {
         // .file("src/source.cc") Code callable from Rust is in ../cxxsupport.cc
         .flag_if_supported("-std=c++17")
         .flag("-Isrc")
+        .flag("-I../../..")
         .compile("settings");
 }
index 633035f2d66450b20e652100dbc4839baada5678..2d9e04611cc73c2e6768c8bea1afb92c484e49f3 100644 (file)
@@ -22,8 +22,7 @@
 #pragma once
 
 #include "rust/cxx.h"
-#include "../../../credentials.hh"
-
+#include "credentials.hh"
 
 namespace pdns::rust::settings::rec
 {
@@ -32,12 +31,35 @@ bool isValidHostname(::rust::Str str);
 void setThreadName(::rust::Str str);
 }
 
+class NetmaskGroup;
+union ComboAddress;
+
 namespace pdns::rust::web::rec
 {
 using CredentialsHolder = ::CredentialsHolder;
+  //using NetmaskGroup = ::NetmaskGroup;
 struct KeyValue;
 struct Request;
 struct Response;
+class NetmaskGroup
+{
+public:
+  NetmaskGroup(const ::NetmaskGroup& arg);
+  ~NetmaskGroup();
+  [[nodiscard]] const ::NetmaskGroup& get() const;
+private:
+  std::unique_ptr<::NetmaskGroup> d_ptr;
+};
+class ComboAddress
+{
+public:
+  ComboAddress(const ::ComboAddress& arg);
+  ~ComboAddress();
+  [[nodiscard]] const ::ComboAddress& get() const;
+private:
+  std::unique_ptr<::ComboAddress> d_ptr;
+};
+
 void apiServer(const Request& rustRequest, Response& rustResponse);
 void apiDiscovery(const Request& rustRequest, Response& rustResponse);
 void apiDiscoveryV1(const Request& rustRequest, Response& rustResponse);
@@ -59,4 +81,6 @@ void apiServerSearchData(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailGET(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailPUT(const Request& rustRequest, Response& rustResponse);
 void apiServerZoneDetailDELETE(const Request& rustRequest, Response& rustResponse);
+std::unique_ptr<pdns::rust::web::rec::ComboAddress> comboaddress(::rust::Str str);
+bool matches(const std::unique_ptr<NetmaskGroup>& nmg, const std::unique_ptr<ComboAddress>& address);
 }
index 6e0e51966f911137845e0edc300ee11311c7ff4c..c694d3e80dd970c65617016b4f63015ff1f00b76 100644 (file)
@@ -166,6 +166,7 @@ struct Context {
     urls: Vec<String>,
     password_ch: cxx::UniquePtr<rustweb::CredentialsHolder>,
     api_ch: cxx::UniquePtr<rustweb::CredentialsHolder>,
+    acl: cxx::UniquePtr<rustweb::NetmaskGroup>,
     counter: Mutex<u32>,
 }
 
@@ -382,9 +383,22 @@ async fn serveweb_async(listener: TcpListener, ctx: Arc<Context>) -> MyResult<()
     // We start a loop to continuously accept incoming connections
     loop {
         let ctx = Arc::clone(&ctx);
-        let ctx2 = Arc::clone(&ctx);
         let (stream, _) = listener.accept().await?;
 
+        match stream.peer_addr() {
+            Ok(address) => {
+                eprintln!("Peer: {:?}", address);
+                let combo = rustweb::comboaddress(&address.to_string());
+                if !rustweb::matches(&ctx.acl, &combo) {
+                    eprintln!("No acl match! {:?}", address);
+                    continue;
+                }
+            }
+            Err(err) => {
+                eprintln!("Can't get: {:?}", err);
+                continue; // If we can't determine the peer address, don't
+            }
+        }
         // Use an adapter to access something implementing `tokio::io` traits as if they implement
         // `hyper::rt` IO traits.
         let io = TokioIo::new(stream);
@@ -405,12 +419,13 @@ async fn serveweb_async(listener: TcpListener, ctx: Arc<Context>) -> MyResult<()
     }
 }
 
-pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, api_ch: cxx::UniquePtr<rustweb::CredentialsHolder>) -> Result<(), std::io::Error> {
+pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, api_ch: cxx::UniquePtr<rustweb::CredentialsHolder>, acl: cxx::UniquePtr<rustweb::NetmaskGroup>) -> Result<(), std::io::Error> {
     // Context (R/O for now)
     let ctx = Arc::new(Context {
         urls: urls.to_vec(),
         password_ch,
         api_ch,
+        acl,
         counter: Mutex::new(0),
     });
 
@@ -460,18 +475,22 @@ pub fn serveweb(addresses: &Vec<String>, urls: &[String], password_ch: cxx::Uniq
 
 unsafe impl Send for rustweb::CredentialsHolder {}
 unsafe impl Sync for rustweb::CredentialsHolder {}
+unsafe impl Send for rustweb::NetmaskGroup {}
+unsafe impl Sync for rustweb::NetmaskGroup {}
 
 #[cxx::bridge(namespace = "pdns::rust::web::rec")]
 mod rustweb {
     extern "C++" {
-      type CredentialsHolder;
+        type CredentialsHolder;
+        type NetmaskGroup;
+        type ComboAddress;
     }
 
     /*
      * Functions callable from C++
      */
     extern "Rust" {
-        fn serveweb(addreses: &Vec<String>, urls: &[String], pwch: UniquePtr<CredentialsHolder>, apikeych: UniquePtr<CredentialsHolder>) -> Result<()>;
+        fn serveweb(addreses: &Vec<String>, urls: &[String], pwch: UniquePtr<CredentialsHolder>, apikeych: UniquePtr<CredentialsHolder>, acl: UniquePtr<NetmaskGroup>) -> Result<()>;
     }
 
     struct KeyValue {
@@ -520,5 +539,7 @@ mod rustweb {
         fn serveStuff(request: &Request, response: &mut Response) -> Result<()>;
 
         fn matches(self: &CredentialsHolder, str: &CxxString) -> bool;
+        fn comboaddress(address: &str) -> UniquePtr<ComboAddress>;
+        fn matches(nmg: &UniquePtr<NetmaskGroup>, address: &UniquePtr<ComboAddress>) -> bool; // match is a keyword
     }
 }
index d4449971fca7f0d75434fd62975543863cb026a3..10698e2b714ec321379ec3d76b8a6fd43be7c1b6 100644 (file)
@@ -46,6 +46,7 @@
 #include "tcpiohandler.hh"
 #include "rec-main.hh"
 #include "settings/cxxsettings.hh" // IWYU pragma: keep, needed by included generated file
+#include "settings/rust/src/bridge.hh"
 #include "settings/rust/web.rs.h"
 
 using json11::Json;
@@ -994,7 +995,11 @@ void serveRustWeb()
   if (!apikeyString.empty()) {
     apikey = std::make_unique<CredentialsHolder>(std::move(apikeyString), arg().mustDo("webserver-hash-plaintext-credentials"));
   }
-  pdns::rust::web::rec::serveweb({::rust::String(address.toStringWithPort())}, ::rust::Slice<const ::rust::String>{urls.data(), urls.size()}, std::move(password), std::move(apikey));
+  NetmaskGroup acl;
+  acl.toMasks(::arg()["webserver-allow-from"]);
+  auto aclPtr = std::make_unique<pdns::rust::web::rec::NetmaskGroup>(acl);
+
+  pdns::rust::web::rec::serveweb({::rust::String(address.toStringWithPort())}, ::rust::Slice<const ::rust::String>{urls.data(), urls.size()}, std::move(password), std::move(apikey), std::move(aclPtr));
 }
 
 static void fromCxxToRust(const HttpResponse& cxxresp, pdns::rust::web::rec::Response& rustResponse)