Skip to content

Commit 981ddb5

Browse files
authored
Add optional OpenNIC root support (OPENNIC=1) (#125)
* Add optional OpenNIC root support via OPENNIC=1 environment variable * Only set up the trust anchor if var/ was created successfully
1 parent d125d70 commit 981ddb5

6 files changed

Lines changed: 94 additions & 2 deletions

File tree

‎Dockerfile‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ RUN mkdir -p \
6464
COPY --chmod=644 encrypted-dns.toml.in /opt/encrypted-dns/etc/
6565
COPY --chmod=644 undelegated.txt /opt/encrypted-dns/etc/
6666

67+
COPY --chmod=644 opennic.hints /opt/unbound/etc/unbound/
68+
COPY --chmod=644 opennic.key /opt/unbound/etc/unbound/
69+
6770
COPY --chmod=755 entrypoint.sh /
6871

6972
COPY --chmod=755 unbound.sh /var/svc/unbound/run

‎README.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Table of contents:
1919
- [Prometheus metrics](#prometheus-metrics)
2020
- [TLS (including HTTPS and DoH) forwarding](#tls-including-https-and-doh-forwarding)
2121
- [Filtering](#filtering)
22+
- [OpenNIC](#opennic)
2223
- [Join the network](#join-the-network)
2324
- [Usage with Docker Compose](#usage-with-docker-compose)
2425
- [Usage with Kubernetes](#usage-with-kubernetes)
@@ -186,6 +187,35 @@ And put the list of domains to block in a file named `/etc/dnscrypt-server/lists
186187

187188
Then, follow the upgrade procedure, adding the following option to the `docker run` command: `-v /etc/dnscrypt-server/lists:/opt/encrypted-dns/etc/lists`.
188189

190+
## OpenNIC
191+
192+
The server can resolve names using the [OpenNIC](https://www.opennic.org/)
193+
root instead of the ICANN root, giving access to the OpenNIC TLDs (`.bbs`,
194+
`.chan`, `.cyb`, `.dyn`, `.epic`, `.geek`, `.gopher`, `.indy`, `.libre`,
195+
`.neo`, `.null`, `.o`, `.oss`, `.oz`, `.parody`, `.pirate`) in addition to
196+
the regular namespace.
197+
198+
Enable it by setting the `OPENNIC=1` environment variable when creating the
199+
container (add `-e OPENNIC=1` to the `docker run` command). Without it, the
200+
image keeps the default behavior and resolves from the ICANN root.
201+
202+
In OpenNIC mode:
203+
204+
- Unbound bootstraps from the OpenNIC root servers (`opennic.hints`) and never
205+
contacts the ICANN root servers, internic.net, or IANA.
206+
- DNSSEC validation is anchored to the OpenNIC root key (`opennic.key`) and
207+
kept up to date automatically (RFC 5011). The OpenNIC root also serves the
208+
DS records of the ICANN TLDs, so DNSSEC keeps working for the regular
209+
namespace.
210+
211+
If the OpenNIC root server set or root key ever changes, refresh both files
212+
and rebuild the image:
213+
214+
```sh
215+
dig +https @dns1.slowb.ro . NS # compare against opennic.hints
216+
dig +https @dns1.slowb.ro . DNSKEY # compare the KSK (flags 257) against opennic.key
217+
```
218+
189219
# Join the network
190220

191221
If you want to help against DNS centralization and surveillance,
@@ -295,6 +325,8 @@ docker rmi --force jedisct1/dnscrypt-server ||:
295325
- Caching resolver: [Unbound](https://www.unbound.net/), with DNSSEC, prefetching,
296326
and no logs. The number of threads and memory usage are automatically adjusted.
297327
Latest stable version, compiled from source. qname minimisation is enabled.
328+
Optionally resolves from the [OpenNIC](https://www.opennic.org/) root (see
329+
[OpenNIC](#opennic)).
298330
- [encrypted-dns-server](https://github.com/jedisct1/encrypted-dns-server).
299331
Compiled from source.
300332

‎entrypoint.sh‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ start() {
186186
ln -s -f "$service" "${SERVICES_DIR}/"
187187
done
188188

189+
# runit wipes the environment before running services, so persist
190+
# settings that service scripts need to a file.
191+
printf '%s\n' "${OPENNIC:-}" >/etc/opennic-env
192+
189193
exec /etc/runit/2 </dev/null >/dev/null 2>/dev/null
190194
}
191195

‎opennic.hints‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; OpenNIC root hints
2+
; https://servers.opennicproject.org/
3+
; Verified 2026-07-14 against two independent OpenNIC DoH servers.
4+
; Refresh with: dig +https @dns1.slowb.ro . NS (then A/AAAA for each NS)
5+
. 3600000 NS ns2.opennic.glue.
6+
. 3600000 NS ns4.opennic.glue.
7+
. 3600000 NS ns6.opennic.glue.
8+
. 3600000 NS ns7.opennic.glue.
9+
. 3600000 NS ns8.opennic.glue.
10+
ns2.opennic.glue. 3600000 A 161.97.219.84
11+
ns2.opennic.glue. 3600000 AAAA 2001:470:4212:10:0:100:53:10
12+
ns4.opennic.glue. 3600000 A 116.203.104.203
13+
ns4.opennic.glue. 3600000 AAAA 2a01:4f8:c2c:da9c::1
14+
ns6.opennic.glue. 3600000 A 45.79.189.158
15+
ns7.opennic.glue. 3600000 A 153.56.130.67
16+
ns7.opennic.glue. 3600000 AAAA 2001:470:7493::e0
17+
ns8.opennic.glue. 3600000 A 178.63.116.152
18+
ns8.opennic.glue. 3600000 AAAA 2a01:4f8:141:4281::999

‎opennic.key‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
; OpenNIC root DNSSEC trust anchor (KSK, key tag 60820, alg 8/RSASHA256)
2+
; Corresponding DS: 60820 8 2 A01E33C8E95712E555FA9E6C09921830F3A518E36C5998F4ADBF5570AA86B538
3+
; Verified 2026-07-14 against two OpenNIC DoH servers and https://wiki.opennic.org/opennic/dnssec
4+
; Seeds var/opennic.key on first start; unbound then maintains it via RFC 5011.
5+
. 86400 IN DNSKEY 257 3 8 AwEAAbtbsu+wl3fbEDbgvMgJ1BDXeAk5t6BU7B1KGVvc13zMJtjvarxpWWrAb7fmWERX8kJawa3KpYty0EDFQ24nfQyhwEOld442ca89u4/ZU3jPuwKohbGn55vIQ7KjCIrDNvRYjGVn2MNwZnL4WVVclJYsa1cGwVQ9t575I5yvU+5g+jVcjUsGwFn6xmuJC0Z33ABKsC8b1cjfcnvE4wP3CrXOlDQ+Er4uPUtMKrmG+Sj1Bm5U+do78mwEXOlTz/sNj8tkpL0pYB2j+XNaDVrO0uS1beekejnttMsC4SHMCsiwMvigW2O54ByhzijU2v87d7U9WEMVfPvO6gearg1fo/1Tk4buzPZcS+W9WZgFAt7kT1ois3x0GGT7J55zENB9IZU4tMmWdYbZJOZsdAzmshuWJIUlTZdNN5671Rhc6P9TWnMlvb9iNT7G3DZ9PhBw1OF/OmmXobv3Wygbt5+u7q2CPPzwU4WTGpVNtr3Iry2SPW3XVpJSM3+nW7LfxxtWZJlN4MDQYC5IptU+A5EO80/yE38E9tKGDWC1+Nw59QLaBE7ff+Jkq7OMjTHjFhYivkJSv+8LEbkGjWoaMAS2CT3/ZVMYLiQn8THiZUBF+aOzJMw0EGPag1Qq4vfGgFkQMM3hOaH6bWN1yCvmspuiwLYkNCZZ/l8ThKc57bGYy9TX

‎unbound.sh‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ else
1919
threads=1
2020
fi
2121

22+
if [ -z "${OPENNIC:-}" ] && [ -f /etc/opennic-env ]; then
23+
OPENNIC=$(cat /etc/opennic-env)
24+
fi
25+
opennic_enabled=0
26+
case "${OPENNIC:-}" in
27+
1 | true | yes | on) opennic_enabled=1 ;;
28+
esac
29+
2230
provider_name=$(cat "$KEYS_DIR/provider_name")
2331

2432
sed \
@@ -57,7 +65,6 @@ server:
5765
udp-connect: no
5866
chroot: "/opt/unbound/etc/unbound"
5967
directory: "/opt/unbound/etc/unbound"
60-
auto-trust-anchor-file: "var/root.key"
6168
num-queries-per-thread: 4096
6269
outgoing-range: 8192
6370
msg-cache-size: @MSG_CACHE_SIZE@
@@ -130,6 +137,21 @@ server:
130137
remote-control:
131138
control-enable: yes
132139
control-interface: 127.0.0.1
140+
EOT
141+
142+
if [ "$opennic_enabled" = 1 ]; then
143+
cat >>/opt/unbound/etc/unbound/unbound.conf <<EOT
144+
145+
server:
146+
# OpenNIC root: no ICANN root servers, internic.net, or IANA involved
147+
root-hints: "opennic.hints"
148+
auto-trust-anchor-file: "var/opennic.key"
149+
EOT
150+
else
151+
cat >>/opt/unbound/etc/unbound/unbound.conf <<EOT
152+
153+
server:
154+
auto-trust-anchor-file: "var/root.key"
133155
134156
auth-zone:
135157
name: "."
@@ -139,13 +161,21 @@ auth-zone:
139161
for-upstream: yes
140162
zonefile: "var/root.zone"
141163
EOT
164+
fi
142165

143166
mkdir -p /opt/unbound/etc/unbound/dev &&
144167
cp -a /dev/random /dev/urandom /opt/unbound/etc/unbound/dev/
145168

146169
mkdir -p -m 700 /opt/unbound/etc/unbound/var &&
147170
chown _unbound:_unbound /opt/unbound/etc/unbound/var &&
148-
/opt/unbound/sbin/unbound-anchor -a /opt/unbound/etc/unbound/var/root.key
171+
if [ "$opennic_enabled" = 1 ]; then
172+
if [ ! -f /opt/unbound/etc/unbound/var/opennic.key ]; then
173+
cp /opt/unbound/etc/unbound/opennic.key /opt/unbound/etc/unbound/var/opennic.key
174+
fi &&
175+
chown _unbound:_unbound /opt/unbound/etc/unbound/var/opennic.key
176+
else
177+
/opt/unbound/sbin/unbound-anchor -a /opt/unbound/etc/unbound/var/root.key
178+
fi
149179

150180
if [ ! -f /opt/unbound/etc/unbound/unbound_control.pem ]; then
151181
/opt/unbound/sbin/unbound-control-setup 2>/dev/null || :

0 commit comments

Comments
 (0)