Merge pull request #1615 from jagerman/lokinet-systemd-resolved

Lokinet systemd-resolved support
pull/1617/head
Jeff 3 years ago committed by GitHub
commit 8a3e3a0c39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 systemctl enable --now systemd-resolved

@ -1,3 +0,0 @@
[Resolve]
DNS=127.3.2.1
Domains=~loki ~snode

@ -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

@ -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/router.cpp
router/route_poker.cpp
router/systemd_resolved.cpp
routing/dht_message.cpp
routing/message_parser.cpp
routing/path_confirm_message.cpp

@ -13,6 +13,7 @@
#include <llarp/ev/ev.hpp>
#include <llarp/net/net.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/systemd_resolved.hpp>
#include <llarp/service/context.hpp>
#include <llarp/service/outbound_context.hpp>
#include <llarp/service/endpoint_state.hpp>
@ -784,6 +785,12 @@ namespace llarp
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)
{
m_OnUp->NotifyAsync(NotifyParams());

@ -342,6 +342,17 @@ namespace llarp
return {m_addr4.sin_addr.s_addr};
}
nuint128_t
SockAddr::getIPv6() const
{
nuint128_t a;
// Explicit cast to void* here to avoid non-trivial type copying warnings (technically this
// isn't trivial because of the zeroing default constructor, but it's trivial enough that this
// copy is safe).
std::memcpy(static_cast<void*>(&a), &m_addr.sin6_addr, 16);
return a;
}
void
SockAddr::setIPv4(nuint32_t ip)
{

@ -1,5 +1,6 @@
#include "route_poker.hpp"
#include "abstractrouter.hpp"
#include "net/sock_addr.hpp"
#include <llarp/net/route.hpp>
#include <llarp/service/context.hpp>
#include <unordered_set>
@ -157,6 +158,11 @@ namespace llarp
Update();
m_Enabling = false;
m_Enabled = true;
systemd_resolved_set_dns(
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
m_Router->GetConfig()->dns.m_bind.createSockAddr(),
true /* route all DNS */);
}
void
@ -167,6 +173,11 @@ namespace llarp
DisableAllRoutes();
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

@ -5,6 +5,7 @@
#include <memory>
#include <optional>
#include <llarp/net/net_int.hpp>
#include "systemd_resolved.hpp"
namespace llarp
{

@ -0,0 +1,171 @@
#include "systemd_resolved.hpp"
#include <llarp/util/logging/logger.hpp>
#ifndef WITH_SYSTEMD
namespace llarp
{
bool
systemd_resolved_set_dns(std::string, llarp::SockAddr, bool)
{
LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS");
return false;
}
} // namespace llarp
#else
#include <stdexcept>
extern "C"
{
#include <systemd/sd-bus.h>
#include <net/if.h>
}
using namespace std::literals;
namespace llarp
{
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);
}
};
} // namespace
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
// clang-format off
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
// clang-format on
(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
// clang-format off
a[0], a[1], a[2], a[3], // yuck
// clang-format on
(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;
}
} // namespace llarp
#endif // WITH_SYSTEMD

@ -0,0 +1,19 @@
#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);
} // namespace llarp
Loading…
Cancel
Save