Compare commits

...

5 Commits

4 changed files with 284 additions and 142 deletions

View File

@ -2,19 +2,11 @@
{ config, lib, pkgs, ... }:
let
dyn-dns = config.sane.services.dyn-dns;
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
bindOvpn = "10.0.1.5";
in lib.mkMerge [
in
{
services.trust-dns.enable = true;
# don't bind to IPv6 until i explicitly test that stack
services.trust-dns.settings.listen_addrs_ipv6 = [];
services.trust-dns.quiet = true;
# FIXME(2023/11/26): services.trust-dns.debug doesn't log requests: use RUST_LOG=debug env for that.
# - see: <https://github.com/hickory-dns/hickory-dns/issues/2082>
# services.trust-dns.debug = true;
sane.ports.ports."53" = {
protocol = [ "udp" "tcp" ];
visibleTo.lan = true;
@ -66,23 +58,6 @@ in lib.mkMerge [
services.trust-dns.settings.zones = [ "uninsane.org" ];
# TODO: can i transform this into some sort of service group?
# have `systemctl restart trust-dns.service` restart all the individual services?
systemd.services.trust-dns.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "trust-dns";
Group = "trust-dns";
wantedBy = lib.mkForce [];
};
systemd.services.trust-dns.enable = false;
users.groups.trust-dns = {};
users.users.trust-dns = {
group = "trust-dns";
isSystemUser = true;
};
# sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
networking.nat.enable = true;
networking.nat.extraCommands = ''
@ -107,98 +82,73 @@ in lib.mkMerge [
visibleTo.lan = true;
description = "colin-redirected-dns-for-lan-namespace";
};
}
{
systemd.services =
let
sed = "${pkgs.gnused}/bin/sed";
stateDir = "/var/lib/trust-dns";
zoneTemplate = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
zoneDirFor = flavor: "${stateDir}/${flavor}";
zoneFor = flavor: "${zoneDirFor flavor}/uninsane.org.zone";
mkTrustDnsService = opts: flavor: let
flags = let baseCfg = config.services.trust-dns; in
(lib.optional baseCfg.debug "--debug") ++ (lib.optional baseCfg.quiet "--quiet");
flagsStr = builtins.concatStringsSep " " flags;
anative = nativeAddrs."servo.${flavor}";
toml = pkgs.formats.toml { };
configTemplate = opts.config or (toml.generate "trust-dns-${flavor}.toml" (
(
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
) // {
listen_addrs_ipv4 = opts.listen or [ anative ];
}
));
configFile = "${stateDir}/${flavor}-config.toml";
port = opts.port or 53;
in {
description = "trust-dns Domain Name Server (serving ${flavor})";
unitConfig.Documentation = "https://trust-dns.org/";
preStart = ''
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
${sed} s/%AWAN%/$wan/ ${configTemplate} > ${configFile}
'' + lib.optionalString (!opts ? config) ''
mkdir -p ${zoneDirFor flavor}
${sed} \
-e s/%CNAMENATIVE%/servo.${flavor}/ \
-e s/%ANATIVE%/${anative}/ \
-e s/%AWAN%/$wan/ \
-e s/%AOVPNS%/185.157.162.178/ \
${zoneTemplate} > ${zoneFor flavor}
'';
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
ExecStart = ''
${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram} \
--port ${builtins.toString port} \
--zonedir ${zoneDirFor flavor}/ \
--config ${configFile} ${flagsStr}
'';
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
in {
trust-dns-wan = mkTrustDnsService { listen = [ nativeAddrs."servo.lan" bindOvpn ]; } "wan";
trust-dns-lan = mkTrustDnsService { port = 1053; } "lan";
trust-dns-hn = mkTrustDnsService { port = 1053; } "hn";
trust-dns-hn-resolver = mkTrustDnsService {
config = pkgs.writeText "hn-resolver-config.toml" ''
# i host a resolver in the wireguard VPN so that clients can resolve DNS through the VPN.
# (that's what this file achieves).
#
# one would expect this resolver could host the authoritative zone for `uninsane.org`, and then forward everything else to the system resolver...
# and while that works for `dig`, it breaks for `nslookup` (and so `ssh`, etc).
#
# DNS responses include a flag for if the responding server is the authority of the zone queried.
# it seems that default Linux stub resolvers either:
# - expect DNSSEC when the response includes that bit, or
# - expect A records to be in the `answer` section instead of `additional` section.
# or perhaps something more nuanced. but for `nslookup` to be reliable, it has to talk to an
# instance of trust-dns which is strictly a resolver, with no authority.
# hence, this config: a resolver which forwards to the actual authority.
listen_addrs_ipv4 = ["${nativeAddrs."servo.hn"}"]
listen_addrs_ipv6 = []
[[zones]]
zone = "uninsane.org"
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "${nativeAddrs."servo.hn"}:1053", protocol = "udp", trust_nx_responses = true }] }
[[zones]]
# forward the root zone to the local DNS resolver
zone = "."
zone_type = "Forward"
stores = { type = "forward", name_servers = [{ socket_addr = "127.0.0.53:53", protocol = "udp", trust_nx_responses = true }] }
'';
} "hn-resolver";
sane.services.trust-dns.enable = true;
sane.services.trust-dns.instances = let
mkSubstitutions = flavor: {
"%AWAN%" = "$(cat '${dyn-dns.ipPath}')";
"%CNAMENATIVE%" = "servo.${flavor}";
"%ANATIVE%" = nativeAddrs."servo.${flavor}";
"%AOVPNS%" = "185.157.162.178";
};
in
{
wan = {
substitutions = mkSubstitutions "wan";
listenAddrs = [
nativeAddrs."servo.lan"
bindOvpn
];
};
lan = {
substitutions = mkSubstitutions "lan";
listenAddrs = [ nativeAddrs."servo.lan" ];
port = 1053;
};
hn = {
substitutions = mkSubstitutions "hn";
listenAddrs = [ nativeAddrs."servo.hn" ];
port = 1053;
};
hn-resolver = {
# don't need %AWAN% here because we forward to the hn instance.
listenAddrs = [ nativeAddrs."servo.hn" ];
extraConfig = {
zones = [
{
zone = "uninsane.org";
zone_type = "Forward";
stores = {
type = "forward";
name_servers = [
{
socket_addr = "${nativeAddrs."servo.hn"}:1053";
protocol = "udp";
trust_nx_responses = true;
}
];
};
}
{
# forward the root zone to the local DNS resolver
zone = ".";
zone_type = "Forward";
stores = {
type = "forward";
name_servers = [
{
socket_addr = "127.0.0.53:53";
protocol = "udp";
trust_nx_responses = true;
}
];
};
}
];
};
};
};
sane.services.dyn-dns.restartOnChange = [
"trust-dns-wan.service"
@ -207,4 +157,3 @@ in lib.mkMerge [
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
];
}
]

View File

@ -12,8 +12,8 @@
#
# example of a design which considers these things:
# - when unlocked:
# - volup tap -> app menu
# - volup hold -> file browser
# - volup tap -> file browser
# - volup hold -> app menu
# - voldown press -> keyboard
# - voldown hold -> terminal
# - power x2 -> screenoff
@ -60,6 +60,12 @@ KEYBOARD="${KEYBOARD:-wvkbd-mobintl}"
action="$1"
log() {
printf "sane-input-handler: %s\n" "$1"
}
## HELPERS
isTouchOn() {
# success if all touch inputs have their events enabled
swaymsg -t get_inputs --raw \
@ -73,6 +79,13 @@ isScreenOn() {
> /dev/null
}
isLandscape() {
# success if all outputs are landscape
swaymsg -t get_outputs --raw \
| jq --exit-status '. | all(.transform == "90" or .transform == "270")' \
> /dev/null
}
isAllOn() {
isTouchOn && isScreenOn
}
@ -81,7 +94,23 @@ isInhibited() {
pidof rofi
}
handleWith() {
local state=
if [ -n "$_isInhibited" ]; then
state="inhibited+"
fi
if [ -n "$_isAllOn" ]; then
state="${state}on"
else
state="${state}off"
fi
log "state=$state action=$action: handleWith: $@"
"$@"
exit $?
}
## HANDLERS
ignore() {
true
}
@ -101,27 +130,55 @@ allOff() {
swaymsg -- input type:touch events disabled
}
toggleKeyboard() {
local kbpid=$(pidof "$KEYBOARD")
if [ -z "$kbpid" ] || ! ( env kill -s RTMIN+0 "$kbpid" ); then
echo "sane-input-handler: failed to toggle keyboard: $KEYBOARD"
_keyboardPid=
setKeyboard() {
if [ -z "$_keyboardPid" ]; then
# lazy init
_keyboardPid=$(pidof "$KEYBOARD")
fi
if [ -z "$_keyboardPid" ]; then
log "cannot find $KEYBOARD"
return
fi
case "$1" in
"toggle")
# `env` so that we get the right `kill` binary instead of bash's builtin
env kill -s RTMIN+0 "$_keyboardPid"
;;
"show")
env kill -s USR2 "$_keyboardPid"
;;
"hide")
env kill -s USR1 "$_keyboardPid"
;;
"set")
case "$2" in
"0")
setKeyboard hide
;;
"1")
setKeyboard show
;;
*)
log "setKeyboard: unknown option 'set $2'"
;;
esac
;;
"ifRoom")
if isLandscape; then
setKeyboard hide
else
setKeyboard show
fi
;;
*)
log "setKeyboard: unknown option '$1'"
;;
esac
}
handleWith() {
state=
if [ -n "$_isInhibited" ]; then
state="inhibited+"
fi
if [ -n "$_isAllOn" ]; then
state="${state}on"
else
state="${state}off"
fi
echo "sane-input-handler: state=$state action=$action: handleWith: $@"
"$@"
exit 0
}
## DISPATCHERS
dispatchDefault() {
case "$action" in
@ -185,17 +242,21 @@ dispatchOn() {
;;
"volup_tap_1")
# volume up once: system menu
handleWith sane-open-desktop rofi.desktop
# volume up once: filesystem browser
setKeyboard ifRoom &
handleWith sane-open-desktop rofi-filebrowser.desktop
;;
"volup_hold_1")
# volume up hold: just browse files
handleWith sane-open-desktop rofi-filebrowser.desktop
# volume up hold: browse files and apps
setKeyboard ifRoom &
# reset fs directory: useful in case you get stuck in broken directory (e.g. one which lacks a `..` entry)
rm -f ~/.cache/rofi/rofi3.filebrowsercache
handleWith sane-open-desktop rofi.desktop
;;
"voldown_start")
# volume down once: toggle keyboard
handleWith toggleKeyboard
handleWith setKeyboard "toggle"
;;
"voldown_hold_1")
# hold voldown to launch terminal

View File

@ -7,5 +7,6 @@
./kiwix-serve.nix
./mautrix-signal.nix
./nixserve.nix
./trust-dns.nix
];
}

View File

@ -0,0 +1,131 @@
{ config, lib, pkgs, ... }:
let
cfg = config.sane.services.trust-dns;
dns = config.sane.dns;
toml = pkgs.formats.toml { };
instanceModule = with lib; types.submodule {
options = {
port = mkOption {
type = types.port;
default = 53;
};
listenAddrs = mkOption {
type = types.listOf types.str;
default = [];
description = ''
IP addresses to serve requests from.
'';
};
substitutions = mkOption {
type = types.attrsOf types.str;
default = {};
description = ''
text substitutions to make on the config and zone file before starting trust-dns.
'';
example = {
"%CNAMESELF%" = "lappy";
"%AWAN%" = ''"$(cat /var/www/wan.txt)"'';
};
};
extraConfig = mkOption {
type = types.attrs;
default = {};
};
};
};
mkSystemdService = flavor: { port, listenAddrs, substitutions, extraConfig }: let
sed = "${pkgs.gnused}/bin/sed";
zoneTemplate = pkgs.writeText
"uninsane.org.zone.in"
config.sane.dns.zones."uninsane.org".rendered;
configTemplate = toml.generate "trust-dns-${flavor}.toml" (
(
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
) // {
listen_addrs_ipv4 = listenAddrs;
} // extraConfig
);
configPath = "/var/lib/trust-dns/${flavor}-config.toml";
sedArgs = lib.mapAttrsToList (key: value: ''-e "s/${key}/${value}/g"'') substitutions;
subs = lib.concatStringsSep " " sedArgs;
in {
description = "trust-dns Domain Name Server (serving ${flavor})";
unitConfig.Documentation = "https://trust-dns.org/";
preStart = lib.concatStringsSep "\n" (
[''
mkdir -p "/var/lib/trust-dns/${flavor}"
${sed} ${subs} -e "" "${configTemplate}" > "${configPath}"
''] ++ lib.mapAttrsToList (zone: { rendered, ... }: ''
${sed} ${subs} -e "" ${pkgs.writeText "${zone}.zone.in" rendered} \
> "/var/lib/trust-dns/${flavor}/${zone}.zone"
'') dns.zones
);
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
ExecStart = lib.escapeShellArgs ([
"${pkgs.trust-dns}/bin/${pkgs.trust-dns.meta.mainProgram}"
"--port" (builtins.toString port)
"--zonedir" "/var/lib/trust-dns/${flavor}"
"--config" "${configPath}"
] ++ lib.optionals config.services.trust-dns.debug [
"--debug"
] ++ lib.optionals config.services.trust-dns.quiet [
"--quiet"
]);
ReadOnlyPaths = [ "/var/lib/uninsane" ]; # for dyn-dns (wan.txt)
};
};
in
{
options = with lib; {
sane.services.trust-dns = {
enable = mkOption {
default = false;
type = types.bool;
};
instances = mkOption {
default = {};
type = types.attrsOf instanceModule;
};
};
};
config = lib.mkIf cfg.enable {
# enable nixpkgs' trust-dns so that i get its config generation
# but don't actually enable the systemd service... i'll instantiate *multiple* instances per interface further below
services.trust-dns.enable = true;
# don't bind to IPv6 until i explicitly test that stack
services.trust-dns.settings.listen_addrs_ipv6 = [];
services.trust-dns.quiet = true;
# FIXME(2023/11/26): services.trust-dns.debug doesn't log requests: use RUST_LOG=debug env for that.
# - see: <https://github.com/hickory-dns/hickory-dns/issues/2082>
# services.trust-dns.debug = true;
users.groups.trust-dns = {};
users.users.trust-dns = {
group = "trust-dns";
isSystemUser = true;
};
systemd.services = lib.mkMerge [
{
trust-dns.enable = false;
trust-dns.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "trust-dns";
Group = "trust-dns";
wantedBy = lib.mkForce [];
};
}
(lib.mapAttrs'
(flavor: instanceConfig: {
name = "trust-dns-${flavor}";
value = mkSystemdService flavor instanceConfig;
})
cfg.instances
)
];
};
}