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