diff --git a/.ci/shellcheck.sh b/.ci/shellcheck.sh index bd870c4af..d8c46bbb6 100755 --- a/.ci/shellcheck.sh +++ b/.ci/shellcheck.sh @@ -17,6 +17,7 @@ sh_files=" ./main/mdss-fb-init-hack/mdss-fb-init-hack.sh ./main/osk-sdl/unlock.sh ./main/postmarketos-base/rootfs-usr-lib-firmwareload.sh + ./main/postmarketos-base-ui/rootfs-etc-NetworkManager-dispatcher.d-99-dns-filter.sh ./main/postmarketos-installkernel/installkernel-pmos ./main/postmarketos-initramfs/init.sh ./main/postmarketos-initramfs/init_functions.sh diff --git a/main/postmarketos-base-ui/APKBUILD b/main/postmarketos-base-ui/APKBUILD index 2715398c3..d5df724f4 100644 --- a/main/postmarketos-base-ui/APKBUILD +++ b/main/postmarketos-base-ui/APKBUILD @@ -1,6 +1,6 @@ # Maintainer: Clayton Craft pkgname=postmarketos-base-ui -pkgver=7 +pkgver=8 pkgrel=0 pkgdesc="Meta package for minimal postmarketOS UI base" url="https://postmarketos.org" @@ -39,6 +39,7 @@ replaces_priority=100 # leave plenty for alpine _source644=" etc/NetworkManager/conf.d/hostname-mode.conf + etc/NetworkManager/conf.d/use-dnsmasq.conf etc/chrony/chrony.conf etc/conf.d/openrc-settingsd etc/conf.d/tinydm @@ -50,6 +51,7 @@ _source644=" etc/X11/Xwrapper.config " _source755=" + etc/NetworkManager/dispatcher.d/99-dns-filter.sh etc/tinydm.d/env-wayland.d/50-firefox-wayland.sh etc/tinydm.d/env-wayland.d/50-sdl-wayland.sh " @@ -129,13 +131,19 @@ _obexd() { networkmanager() { depends=" + busctl + dnsmasq-dnssec-dbus>=2.89-r2 + gojq networkmanager networkmanager-cli networkmanager-openrc networkmanager-tui networkmanager-wifi networkmanager-wwan" + amove etc/NetworkManager/conf.d/hostname-mode.conf + amove etc/NetworkManager/conf.d/use-dnsmasq.conf + amove etc/NetworkManager/dispatcher.d/99-dns-filter.sh } _default_camera() { @@ -147,6 +155,7 @@ _default_camera() { sha512sums=" 3c9ae7415f4891bee8595166ed6a42cb577a837f741c6b5409d193558626348b41516888a01d0c4895282c5f4e9a1ff838c19712888750b2ef68429bb4b42ee3 rootfs-etc-NetworkManager-conf.d-hostname-mode.conf +900554534191fa0797064d35350934cdd8af59f30f0ae7d8ec63c2e11c44a16c643d3024b6543940488cd590fec1d392548bcaacc3be88cddff90f69b17ece07 rootfs-etc-NetworkManager-conf.d-use-dnsmasq.conf e5d049db1d82c510bab9246208b51b8ec2711d008d67792fc10d4c0b65ed4dece7b5ae3c3dd28a8539d177b6849c1f921cb9fef3d2c7bee0355451f7b4757ec6 rootfs-etc-chrony-chrony.conf 49fb494b659fe0149a93eafe109609acce6a470bb8acea160638d07e0e4b11af2544f34549d5ef2deb2914a7ef13d0d470b04ad62981f14f96999af02a5f24cf rootfs-etc-conf.d-openrc-settingsd 44e4283c6f77de83915977dd3bc2d8e2d96b3ed6cc68d3cc156304359ae649b5a8b0bac843e517ec6faa2066dd43ba85e313899b1eda04862f864fb9eb508aa0 rootfs-etc-conf.d-tinydm @@ -156,6 +165,7 @@ fe0651904c1f40ffa67d83daca190af199f63247e53642a59a1e1147cd06776fcf20b7b2fcc53737 90b30cbea660ef6cd4c0461b6935de0cd63a84a1a40edb24348a83044c97935b974bd8bafda9cd558e92d3eb69e22c5ccf55483b80f839e24f0eb57ae2df6fe3 rootfs-etc-skel-.profile 6b9c7bb73213187eb9ca8a94109b2b816f50c1158c90fec2e92b373864280d67741589e5bfbab8810945f031d2f4b535aad78a72e46e52ea50be5b85324da381 rootfs-etc-sleep-inhibitor.conf cac604e25c46e695dd30bd5a10cfd2d69595fcc3bc290096ac94b76b10834d591ea6576afb79c46b5da492a1dbf8660cf87b6110cd39937e15237bc74fa7a5c6 rootfs-etc-X11-Xwrapper.config +52d58729cbf3cd0318de633e8a8da74c7af246025a8c5746d5e7c854bdabbf27fa07d8558ffec92a30491cdb687fe4414de5adcddd7da5be3510f918fba463a2 rootfs-etc-NetworkManager-dispatcher.d-99-dns-filter.sh d1ddd43489e6016e3ffd716027ed2bae4a2ab5f213118bdbcb96750e267ab7c0367cd0e0e386300aa5550352653144f5caeddd790621fe0879f83ca1995bb65c rootfs-etc-tinydm.d-env-wayland.d-50-firefox-wayland.sh ecaa57d033a119a53a6574c27636b7c89d659d75ea48a973a6a4ff6f90e5d07202529fd489bfc9dfc7430f5b60f40612f6d5c06f7fab47e681b0a3112a874058 rootfs-etc-tinydm.d-env-wayland.d-50-sdl-wayland.sh " diff --git a/main/postmarketos-base-ui/rootfs-etc-NetworkManager-conf.d-use-dnsmasq.conf b/main/postmarketos-base-ui/rootfs-etc-NetworkManager-conf.d-use-dnsmasq.conf new file mode 100644 index 000000000..53a8b1725 --- /dev/null +++ b/main/postmarketos-base-ui/rootfs-etc-NetworkManager-conf.d-use-dnsmasq.conf @@ -0,0 +1,2 @@ +[main] +dns=dnsmasq diff --git a/main/postmarketos-base-ui/rootfs-etc-NetworkManager-dispatcher.d-99-dns-filter.sh b/main/postmarketos-base-ui/rootfs-etc-NetworkManager-dispatcher.d-99-dns-filter.sh new file mode 100644 index 000000000..cbc57fc9b --- /dev/null +++ b/main/postmarketos-base-ui/rootfs-etc-NetworkManager-dispatcher.d-99-dns-filter.sh @@ -0,0 +1,145 @@ +#!/bin/sh +# This dispatcher script exists to configure a dnsmasq instance managed by +# NetworkManager to restrict DNS responses based on the IP configuration +# supported by NetworkManager's "Primary Connection". The script requires that +# NetworkManager is configured to use/manage dnsmasq. +# For more information about *why* this is necessary, see: +# - https://gitlab.com/postmarketOS/pmaports/-/issues/1430 +# - https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3823 +# Related: +# - https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/1279 + +set -eu + +log_tag="nm-dns-filter" +dbus="busctl --json=short" +# gojq is like... 10x faster than jq on a modest phone... (0.07s vs 0.89s) +jq=gojq + +action="${2:-}" + +# Get the gateway for the given connection and IP protocol version +# $1: D-Bus path to connection +# $2: IP version, e.g. '4' or '6' +get_gateway() { + _conn="$1" + _ver="$2" + + _ipcfg="$($dbus get-property \ + org.freedesktop.NetworkManager \ + "$_conn" \ + org.freedesktop.NetworkManager.Connection.Active \ + "Ip${_ver}Config" | $jq -r '.data')" + + if [ -z "$_ipcfg" ]; then + logger -i -t "$log_tag" "error: unable to determine IP config for primary connection!" + exit 1 + fi + logger -i -t "$log_tag" "pri conn ip$_ver config path: $_ipcfg" + + _attempt=0 + # NetworkManager dispatches to the script as soon as the interface comes + # up, which for an IPv6 network may be before the device has received an + # Router Advertisement (RA). This means that there may be no IPv6 gateway + # defined right now, but if we wait a few seconds an RA might arrive and a + # gateway might become defined. NM does not dispatch to scripts when an RA + # arrives and triggers a change in IP address / gateway / routes, so the + # current invocation is our only chance. So we wait up to 5 seconds for the + # gateway to be defined before deciding that the network doesn't have IPv6 + # connectivity. NetworkManager-dispatcher man page says that the dispatcher + # will kill scripts that run for "too long", that's currently (in NM 1.42) + # set to 10 minutes, but let's keep our timeout much shorter. + while [ "$_attempt" -lt 5 ]; do + _gateway="$($dbus get-property \ + org.freedesktop.NetworkManager \ + "$_ipcfg" \ + "org.freedesktop.NetworkManager.IP${_ver}Config" \ + Gateway | $jq -r '.data')" + if [ -n "$_gateway" ]; then + break + fi + sleep 1 + _attempt="$(( _attempt + 1 ))" + done + + logger -i -t "$log_tag" "ip$_ver gateway: $_gateway" + + echo "$_gateway" +} + +# $1: filter record type, e.g. 'A' or 'AAAA' +# $2: bool value, e.g. 'true' or 'false' +set_filter() { + _type="$1" + _val="$2" + logger -i -t "$log_tag" "setting $_type filter to '$_val'" + $dbus call org.freedesktop.NetworkManager.dnsmasq \ + /uk/org/thekelleys/dnsmasq \ + org.freedesktop.NetworkManager.dnsmasq \ + "SetFilter$_type" b "$_val" +} + +# The up/down actions are probably(?) sufficient to act on. The goal is to only +# update filtering in dnsmasq when there's some possibility that the _primary +# connection_ has changed. +case "$action" in + "up" | "down" | "reapply") + ;; + *) + # unsupported action + exit 0 +esac + +primaryConn="$($dbus get-property \ + org.freedesktop.NetworkManager \ + /org/freedesktop/NetworkManager \ + org.freedesktop.NetworkManager \ + PrimaryConnection | $jq -r '.data')" + +if [ -z "$primaryConn" ]; then + logger -i -t "$log_tag" "unable to determine primary connection!" + exit 1 +fi + +# This looks at the primary connection's 'gateway' property for the IP6Config +# and IP4Config interfaces, and assumes that if the gateway is unset then the +# connection does not "support" the protocol. + +ip4gateway="$(get_gateway "$primaryConn" 4)" +ip6gateway="$(get_gateway "$primaryConn" 6)" + +# if no gateways, disable all filtering (default dnsmasq behavior) +if [ -z "$ip4gateway" ] && [ -z "$ip6gateway" ]; then + logger -i -t "$log_tag" "no gateways for primary connection" + logger -i -t "$log_tag" "disabling IPv4 filter" + set_filter A false + logger -i -t "$log_tag" "disabling IPv6 filter" + set_filter AAAA false +else # toggle filters individually depending on gateway status + if [ -z "$ip4gateway" ]; then + logger -i -t "$log_tag" "enabling IPv4 filter" + set_filter A true + else + logger -i -t "$log_tag" "disabling IPv4 filter" + set_filter A false + fi + + if [ -z "$ip6gateway" ]; then + logger -i -t "$log_tag" "enabling IPv6 filter" + set_filter AAAA true + else + logger -i -t "$log_tag" "disabling IPv6 filter" + set_filter AAAA false + fi +fi + +# dnsmasq will continue to use possibly unfiltered results from its cache, so +# clear the cache +# also see: https://www.mail-archive.com/dnsmasq-discuss@lists.thekelleys.org.uk/msg16695.html + +$dbus call \ + org.freedesktop.NetworkManager.dnsmasq \ + /uk/org/thekelleys/dnsmasq org.freedesktop.NetworkManager.dnsmasq \ + ClearCache + +logger -i -t "$log_tag" "dns filter config applied successfully"