Hi,
I’ve recently migrated a moderately complex existing network from an ancient modded ASUS router to an Omnia with Turris OS 4.x. As part of that work, I needed to delegate a sub-domain of .lan
to an external resolver, and I wanted to do that in way that is as robust as possible, i.e. least likely to break in the presence of future TurrisOS updates.
The existing documentation (mainly here and here) is a bit sparse on this, so I’m sharing my setup in the hope that it will be useful to others, and perhaps we can use it to improve the “official” documentation.
TL;DR: This is essentially a work-around for the lack of a policy.add_front()
or similar API in knot-resolver. Full explanation follows.
After some experimentation using the “live” kresd console, I ended up with the following in /etc/kresd/custom.conf
:
-- Custom configuration starts here
local function copyPolicyRules(r)
r1 = {}
for k, v in pairs(r) do
table.insert(r1, v.cb)
end
return r1
end
local function addPolicyRules(r)
for k, v in pairs(r) do
policy.add(v)
end
end
-- Our configuration file gets included AFTER the Turris-generated one, but we
-- need to add our policy rules BEFORE any added by the Turris-generated config.
-- Take a copy of the complied rule data, i.e. policy.rules[].cb.
local rulesCopy = copyPolicyRules(policy.rules)
-- Drop all existing policy rules.
policy.rules = {}
-- Add our cystom rules:
-- Add an uncached STUB zone, forwarding queries for 'ci.lan' to
-- the dnsmasq instance running on 10.70.22.2.
local customStubZones = policy.todnames({'ci.lan'})
policy.add(policy.suffix(policy.FLAGS({'NO_CACHE'}), customStubZones))
policy.add(policy.suffix(policy.STUB({'10.70.22.2'}), customStubZones))
-- Add the previously saved copy of the system-generated rules.
addPolicyRules(rulesCopy)
To actually enable loading the custom.conf
, run:
uci set resolver.kresd.include_config=/etc/kresd/custom.conf
uci commit
service kresd restart
Note that the policy.FLAGS({'NO_CACHE'}
rule is not required, but makes debugging of the setup easier. In my case I actually want the uncached behaviour, as that DNS zone has ephemeral machines coming and going in it and I would like clients to react to those changes immediately.
Technical explanation
What I’m trying to do here is essentially replace part of the DNS tree, as documented in the knot-resolver documentation here. However, the mechanism for including a custom kresd configuration in Turris OS always includes the configuration after any system-generated configuration.
Such system-generated configuration may contain catch-all FORWARD
rules, or in fact anything else. Given that policy is evaluated in order, we need to ensure that our custom rules are evaluated before any catch-all rules installed by Turris OS. We do that by taking a copy of the existing policy.rules[...].cb
(the compiled callback for each rule), dropping all of policy.rules[]
and then adding what we need in the right order, with the system-generated rules last.
Questions
These are mostly for Turris OS developers / knot-resolver experts (ping @Pepe, @vcunat):
- Do you anticipate any problems with this approach in the future? As stated, my goal is to ideally not have it break during updates.
- I’m not a Lua expert, if someone could confirm that the above copy-based approach does not produce any unexpected side-effects, that’d be great.
- I noticed that the Forris “Configuration backup”
.tar.bz2
only includes some subset of files in/etc
. It’d be useful to have a well-defined place for custom configuration files such as/etc/custom
, to ensure that these get backed up and restored(!) with all of the system configuration. - (minor) Can we get a
rlwrap
package or similar tool? It’d make live coding against the kresd control socket so much easier… I’d also suggest installing akresd-console
script encapsulating thesocat
invocations. - (minor)
service kresd restart
produces the following errors on the console on my system. These appear to be unrelated to the presence of anycustom.conf
:syntax error. Last token seen: + Garbled time