--- categories: - General - Linux comments: true date: 2021-01-30 01:05:17+01:00 disable_share: true draft: false featured_image: /images/banner.jpg omit_header_text: true tags: - general - openwrt - turris-omnia title: 'Quick tips: Server Name Indication for internal domains with Turris Omnia' --- ## Omnia? The [Turris Omnia](https://www.turris.com/en/omnia/overview/) is quite a nice (although a little pricey) OpenWRT-based router from [CZ.NIC](https://www.nic.cz/). It provides a fairly powerful CPU, relatively unconstrained [eMMC](https://en.wikipedia.org/wiki/MultiMediaCard#eMMC) space, and quite a lot of hackability (some revisions even have GPIO ports to play with). It runs a modified version of OpenWRT, named [TurrisOS](https://docs.turris.cz/basics/tos-versions/). ## The problem A few years ago, I built a custom NAS for my storage needs, using a cheap Intel SoC (J1900 chipset) and a (much pricier) [mini-ITX small form factor server tower](https://www.ipc.in-win.com/soho-smb-iw-ms04). While I want it to be primarily a NAS, I wanted to run a few services on it, for example [Nextcloud](https://nextcloud.org) or [Pymedusa](https://github.com/pymedusa/Medusa). Of course, in these days it's better to use HTTPS, and the process is both cheap and fast using [Let's Encrypt](https://letsencrypt.org). I also wanted to use [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) with multiple subdomains (`files.example.com`, `medusa.example.com`, etc.) pointing to the same internal LAN IP (because some of the software I used is much harder to set up with subpaths of a single domain). Actually setting up the certificates (with the dns-01 challenge) was rather easy to do (I might do a separate blog post on the topic), but then I got hit by a problem: the default domain used by DHCP in Omnia is `lan`, which of course is not the same as the one used with the Let's Encrypt certificates. Also, subdomains meant that I had to insert static host mapping for each one of them, which made testing of new software (on a new subdomain) and maintenance tedious. Hence, the question: **How could I have an arbitrary number of subdomains (`foo.bar`, `baz.foo.bar`) all resolve to the same IP without manual mapping? ## The solution Unlike upstream OpenWRT, TurrisOS does not use [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) for name resolution (it is only used as DHCP server), opting instead for [Knot Resolver](https://www.knot-resolver.cz/), also developed by CZ.NIC. kresd (the Knot Resolver daemon) is sufficiently nimble and can be scripted and configured with a bit of Lua. Unlike others that absolutely require features that are dnsmasq specific, I had absolutely no problems with it. Thanks to the [extensive documentation](https://knot-resolver.readthedocs.io/en/stable/) and [some helpful developers](https://forum.turris.cz/u/vcunat) I was able to configure it to do exactly like I wanted. One first needs to check, on the Omnia, the configuration file which is located at `/etc/config/resolver`, and navigate to the options related to kresd: ``` config resolver 'kresd' option rundir '/tmp/kresd' option log_stderr '0' option log_stdout '0' option forks '1' option include_config '/etc/kresd/custom.conf' option keep_cache '0' list hostname_config '/etc/hosts' ``` This is my configuration, which might be different from yours. What's important is `option include_config`, which you can point to a custom file where you can set additional kresd configuration. What I did was to create this file and add: ```lua local genRR = policy.ANSWER({ [kres.type.A] = { rdata=kres.str2ip('192.168.30.55'), ttl=900 }, }, true) policy.add(policy.suffix(genRR, { todname('internal.example.com.') })) ``` What does it do? It sets a [query policy](https://knot-resolver.readthedocs.io/en/stable/modules-policy.html#mod-policy), which tells kresd what to do exactly with some requests. Notice that these statements will *only* work with kresd >= 5.1, but even the legacy TurrisOS 3.x has the latest version, if you are up-to-date with updates. In particular, when the request is `kres.type.A`, so an A record, it gives back `192.168.30.55` with a [time-to-live](https://en.wikipedia.org/wiki/Time_to_live) of 900 seconds. This means that every request that follows this policy will answer the same IP address. The secont line adds [a new policy rule](https://knot-resolver.readthedocs.io/en/stable/modules-policy.html#policy.add) to the resolver, which means that every subdomain of `internal.example.com` will resolve to 192.168.30.55. Since kresd assumes data in the DNS wire format, as defined in [RFC 1035](https://tools.ietf.org/html/rfc1035), we use a couple of convenience functions (`kres.str2ip` and `todname`) so we can just type our IP addresses or domain names without any trouble. We can also, potentially, specify multiple subdomains to check with the `policy.todnames` function: ```lua policy.add(policy.suffix(genRR, policy.todnames({'internal.example.com', 'external.example.com'}))) ``` Once that is a done deal, you can just restart kresd on the Omnia (`/etc/init.d/resolver restart`) to have kresd pick up your configuration. If it doesn't start... well, you have a problem. In this case you can set `log_stderr` and `log_stdout` to 1 in the configuration and then check `/var/log/messages` for potential configuration errors. ## Conclusion Like this, I was able to set Let's Encrypt for all the subdomains I needed and I could also use HTTPS internally, without resorting to ideas like adding my own CA (which would have complicated things quite a bit. It took a while to figure out, including [troubles due to API changes](https://forum.turris.cz/t/kresd-crashes-with-policy-configuration-and-fqdn-as-router-name/14722), but now it performs exactly as I want to.