mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
Add systemd-resolved dynamic DNS updating
Wires up systemd support to configure DNS on startup and when enabling/disabling exit mode. On startup (and when turning off an exit) we tell systemd-resolved to direct .loki and .snode lookups to lokinet (leaving other DNS traffic alone). On exit enabling, we reconfigure it to resolve "." (i.e. the root DNS domain) so that all lookups come into it.
This commit is contained in:
parent
35e4e8817b
commit
4ef25ef679
|
@ -1,6 +1,20 @@
|
||||||
To be put at `/usr/lib/systemd/resolved.conf.d/lokinet.conf` for distro use and `/etc/systemd/resolved.conf.d/lokinet.conf` for local admin use.
|
Lokinet now talks to systemd directly via sdbus to set up DNS, but in order for this to work the
|
||||||
|
user running lokinet (assumed `_lokinet` in these example files) needs permission to set dns servers
|
||||||
|
and domains.
|
||||||
|
|
||||||
To make use of it:
|
To set up the permissions:
|
||||||
|
|
||||||
|
- If lokinet is running as some user other than `_lokinet` the change the `_lokinet` username inside
|
||||||
|
`lokinet.rules` and `lokinet.pkla`.
|
||||||
|
|
||||||
|
- If on a Debian or Debian-derived distribution (such as Ubuntu) using polkit 105,
|
||||||
|
copy `lokinet.pkla` to `/var/lib/polkit-1/localauthority/10-vendor.d/lokinet.pkla` (for a distro
|
||||||
|
install) or `/etc/polkit-1/localauthority.conf.d/` (for a local install).
|
||||||
|
|
||||||
|
- Copy `lokinet.rules` to `/usr/share/polkit-1/rules.d/` (distro install) or `/etc/polkit-1/rules.d`
|
||||||
|
(local install).
|
||||||
|
|
||||||
|
Make use of it by switching to systemd-resolved:
|
||||||
```
|
```
|
||||||
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||||
sudo systemctl enable --now systemd-resolved
|
sudo systemctl enable --now systemd-resolved
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[Resolve]
|
|
||||||
DNS=127.3.2.1
|
|
||||||
Domains=~loki ~snode
|
|
4
contrib/systemd-resolved/lokinet.pkla
Normal file
4
contrib/systemd-resolved/lokinet.pkla
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[Allow lokinet to set DNS settings]
|
||||||
|
Identity=unix-user:_lokinet
|
||||||
|
Action=org.freedesktop.resolve1.set-dns-servers;org.freedesktop.resolve1.set-domains
|
||||||
|
ResultAny=yes
|
9
contrib/systemd-resolved/lokinet.rules
Normal file
9
contrib/systemd-resolved/lokinet.rules
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/* Allow lokinet to set DNS settings */
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if ((action.id == "org.freedesktop.resolve1.set-dns-servers" ||
|
||||||
|
action.id == "org.freedesktop.resolve1.set-domains") &&
|
||||||
|
subject.user == "_lokinet") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -186,6 +186,7 @@ add_library(liblokinet
|
||||||
router/rc_gossiper.cpp
|
router/rc_gossiper.cpp
|
||||||
router/router.cpp
|
router/router.cpp
|
||||||
router/route_poker.cpp
|
router/route_poker.cpp
|
||||||
|
router/systemd_resolved.cpp
|
||||||
routing/dht_message.cpp
|
routing/dht_message.cpp
|
||||||
routing/message_parser.cpp
|
routing/message_parser.cpp
|
||||||
routing/path_confirm_message.cpp
|
routing/path_confirm_message.cpp
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <llarp/ev/ev.hpp>
|
#include <llarp/ev/ev.hpp>
|
||||||
#include <llarp/net/net.hpp>
|
#include <llarp/net/net.hpp>
|
||||||
#include <llarp/router/abstractrouter.hpp>
|
#include <llarp/router/abstractrouter.hpp>
|
||||||
|
#include <llarp/router/systemd_resolved.hpp>
|
||||||
#include <llarp/service/context.hpp>
|
#include <llarp/service/context.hpp>
|
||||||
#include <llarp/service/outbound_context.hpp>
|
#include <llarp/service/outbound_context.hpp>
|
||||||
#include <llarp/service/endpoint_state.hpp>
|
#include <llarp/service/endpoint_state.hpp>
|
||||||
|
@ -784,6 +785,10 @@ namespace llarp
|
||||||
|
|
||||||
Router()->loop()->add_ticker([this] { Flush(); });
|
Router()->loop()->add_ticker([this] { Flush(); });
|
||||||
|
|
||||||
|
// Attempt to register DNS on the interface
|
||||||
|
systemd_resolved_set_dns(m_IfName, m_LocalResolverAddr.createSockAddr(),
|
||||||
|
false /* just .loki/.snode DNS initially */);
|
||||||
|
|
||||||
if (m_OnUp)
|
if (m_OnUp)
|
||||||
{
|
{
|
||||||
m_OnUp->NotifyAsync(NotifyParams());
|
m_OnUp->NotifyAsync(NotifyParams());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "route_poker.hpp"
|
#include "route_poker.hpp"
|
||||||
#include "abstractrouter.hpp"
|
#include "abstractrouter.hpp"
|
||||||
|
#include "net/sock_addr.hpp"
|
||||||
#include <llarp/net/route.hpp>
|
#include <llarp/net/route.hpp>
|
||||||
#include <llarp/service/context.hpp>
|
#include <llarp/service/context.hpp>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
@ -157,6 +158,11 @@ namespace llarp
|
||||||
Update();
|
Update();
|
||||||
m_Enabling = false;
|
m_Enabling = false;
|
||||||
m_Enabled = true;
|
m_Enabled = true;
|
||||||
|
|
||||||
|
systemd_resolved_set_dns(
|
||||||
|
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
|
||||||
|
m_Router->GetConfig()->dns.m_bind.createSockAddr(),
|
||||||
|
true /* route all DNS */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -167,6 +173,11 @@ namespace llarp
|
||||||
|
|
||||||
DisableAllRoutes();
|
DisableAllRoutes();
|
||||||
m_Enabled = false;
|
m_Enabled = false;
|
||||||
|
|
||||||
|
systemd_resolved_set_dns(
|
||||||
|
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
|
||||||
|
m_Router->GetConfig()->dns.m_bind.createSockAddr(),
|
||||||
|
false /* route DNS only for .loki/.snode */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <llarp/net/net_int.hpp>
|
#include <llarp/net/net_int.hpp>
|
||||||
|
#include "systemd_resolved.hpp"
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
{
|
{
|
||||||
|
|
129
llarp/router/systemd_resolved.cpp
Normal file
129
llarp/router/systemd_resolved.cpp
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
#include "systemd_resolved.hpp"
|
||||||
|
#include <llarp/util/logging/logger.hpp>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace llarp {
|
||||||
|
|
||||||
|
#ifndef WITH_SYSTEMD
|
||||||
|
|
||||||
|
bool systemd_resolved_set_dns(std::string, llarp::SockAddr, bool) {
|
||||||
|
LogDebug("lokinet is not build with systemd support, cannot set systemd resolved DNS");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <typename... T>
|
||||||
|
void resolved_call(sd_bus* bus, const char* method, const char* arg_format, T... args) {
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
sd_bus_message *msg = nullptr;
|
||||||
|
int r = sd_bus_call_method(bus,
|
||||||
|
"org.freedesktop.resolve1",
|
||||||
|
"/org/freedesktop/resolve1",
|
||||||
|
"org.freedesktop.resolve1.Manager",
|
||||||
|
method,
|
||||||
|
&error,
|
||||||
|
&msg,
|
||||||
|
arg_format,
|
||||||
|
args...);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
throw std::runtime_error{"sdbus resolved "s + method + " failed: " + strerror(-r)};
|
||||||
|
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sd_bus_deleter { void operator()(sd_bus* ptr) const { sd_bus_unref(ptr); } };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool systemd_resolved_set_dns(std::string ifname, llarp::SockAddr dns, bool global) {
|
||||||
|
unsigned int if_ndx = if_nametoindex(ifname.c_str());
|
||||||
|
if (if_ndx == 0) {
|
||||||
|
LogWarn("No such interface '", ifname, "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the system bus
|
||||||
|
sd_bus *bus = nullptr;
|
||||||
|
int r = sd_bus_open_system(&bus);
|
||||||
|
if (r < 0) {
|
||||||
|
LogWarn("Failed to connect to system bus to set DNS: ", strerror(-r));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::unique_ptr<sd_bus, sd_bus_deleter> bus_ptr{bus};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but the
|
||||||
|
// alternative is to build up a bunch of crap with va_args, which is slightly more gross.
|
||||||
|
if (dns.isIPv6()) {
|
||||||
|
auto ipv6 = dns.getIPv6();
|
||||||
|
static_assert(sizeof(ipv6) == 16);
|
||||||
|
auto* a = reinterpret_cast<const uint8_t*>(&ipv6);
|
||||||
|
resolved_call(bus, "SetLinkDNSEx", "ia(iayqs)",
|
||||||
|
(int32_t) if_ndx,
|
||||||
|
(int) 1, // number of "iayqs"s we are passing
|
||||||
|
(int32_t) AF_INET6, // network address type
|
||||||
|
(int) 16, // network addr byte size
|
||||||
|
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck
|
||||||
|
(uint16_t) dns.getPort(),
|
||||||
|
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto ipv4 = dns.getIPv4();
|
||||||
|
static_assert(sizeof(ipv4) == 4);
|
||||||
|
auto* a = reinterpret_cast<const uint8_t*>(&ipv4);
|
||||||
|
resolved_call(bus, "SetLinkDNSEx", "ia(iayqs)",
|
||||||
|
(int32_t) if_ndx,
|
||||||
|
(int) 1, // number of "iayqs"s we are passing
|
||||||
|
(int32_t) AF_INET, // network address type
|
||||||
|
(int) 4, // network addr byte size
|
||||||
|
a[0], a[1], a[2], a[3], // yuck
|
||||||
|
(uint16_t) dns.getPort(),
|
||||||
|
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global)
|
||||||
|
// Setting "." as a routing domain gives this DNS server higher priority in resolution
|
||||||
|
// compared to dns servers that are set without a domain (e.g. the default for a
|
||||||
|
// DHCP-configured DNS server)
|
||||||
|
resolved_call(bus, "SetLinkDomains", "ia(sb)",
|
||||||
|
(int32_t) if_ndx,
|
||||||
|
(int) 1, // array size
|
||||||
|
"." // global DNS root
|
||||||
|
);
|
||||||
|
else
|
||||||
|
// Only resolve .loki and .snode through lokinet (so you keep using your local DNS server for
|
||||||
|
// everything else, which is nicer than forcing everything though lokinet's upstream DNS).
|
||||||
|
resolved_call(bus, "SetLinkDomains", "ia(sb)",
|
||||||
|
(int32_t) if_ndx,
|
||||||
|
(int) 2, // array size
|
||||||
|
"loki", // domain
|
||||||
|
(int) 1, // routing domain = true
|
||||||
|
"snode", // domain
|
||||||
|
(int) 1 // routing domain = true
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LogWarn("Failed to set DNS via systemd-resolved: ", e.what());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WITH_SYSTEMD
|
||||||
|
|
||||||
|
} // namespace llarp
|
18
llarp/router/systemd_resolved.hpp
Normal file
18
llarp/router/systemd_resolved.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <llarp/net/sock_addr.hpp>
|
||||||
|
|
||||||
|
namespace llarp
|
||||||
|
{
|
||||||
|
/// Attempts to set lokinet as the DNS server for systemd-resolved. Returns true if successful,
|
||||||
|
/// false if unsupported or fails. (When compiled without systemd support this always returns
|
||||||
|
/// false without doing anything).
|
||||||
|
///
|
||||||
|
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0.
|
||||||
|
/// Typically tun_endpoint.GetIfName().
|
||||||
|
/// \param dns -- the listening address of the lokinet DNS server
|
||||||
|
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
|
||||||
|
/// addresses (false).
|
||||||
|
bool systemd_resolved_set_dns(std::string if_name, llarp::SockAddr dns, bool global);
|
||||||
|
}
|
Loading…
Reference in a new issue