Add WireGuard, DNS and more to the web UI and router-setup

Web UI: a WireGuard page (tunnels, peers, keys made on the router, client
configs with a QR code, import of wg-quick configs, live status), a DNS page
(records, wildcards, forward zones), interface dropdowns with VLAN devices,
IPv6 options and a local domain. router-setup asks about VLANs, IPv6 and
the domain. The router publishes its own name in DNS and hands the local
domain to DHCP clients; wg and jq are on the router. Includes a script
that takes over antigua's tunnels.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
2026-10-29 23:53:34 +01:00
co-authored by Claude Sonnet 5.5
parent 54040a1402
commit 84f6002d05
23 changed files with 2484 additions and 64 deletions
+56
View File
@@ -0,0 +1,56 @@
{{define "content"}}
{{with .Data}}
{{$e := .Errors}}
<form method="post" action="/dns">
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<fieldset>
<legend>Records</legend>
<input type="hidden" name="recs" value="{{len .Records}}">
<div class="scroll">
<table>
<tr><th>Name</th><th>Addresses</th><th>Remove</th></tr>
{{range $i, $r := .Records}}
<tr>
<td><input name="rec.{{$i}}.name" value="{{$r.Name}}" size="30" spellcheck="false" placeholder="nas or *.k8s.example.org"></td>
<td><input name="rec.{{$i}}.addresses" value="{{$r.Addresses}}" size="40" spellcheck="false" placeholder="192.168.10.20, fd00::20"></td>
<td>{{if $r.Name}}<input type="checkbox" name="rec.{{$i}}.remove">{{end}}</td>
</tr>
{{range $f := list "name" "addresses"}}{{with index $e (printf "rec[%d].%s" $i $f)}}<tr><td colspan="3" class="field-error">{{$f}}: {{.}}</td></tr>{{end}}{{end}}
{{end}}
</table>
</div>
<p class="hint">An IPv4 address becomes an A record, an IPv6 address an AAAA record; several addresses give several records.
A name without a dot lives in the local domain{{if .Domain}} (<code>{{.Domain}}</code>){{else}} (none is set: do that on the Network page){{end}}.
A full name such as <code>photos.example.org</code> overrides public DNS for just that name.</p>
<p class="hint"><code>*.k8s.example.org</code> is a wildcard: <code>k8s.example.org</code> itself and every name below it
(<code>a.k8s.example.org</code>, <code>x.y.k8s.example.org</code>) answer with these addresses.</p>
</fieldset>
<fieldset>
<legend>Forward zones</legend>
<input type="hidden" name="zones" value="{{len .Zones}}">
<table>
<tr><th>Domain</th><th>Resolvers</th><th>Remove</th></tr>
{{range $i, $z := .Zones}}
<tr>
<td><input name="zone.{{$i}}.name" value="{{$z.Name}}" size="30" spellcheck="false" placeholder="branch.example.org"></td>
<td><input name="zone.{{$i}}.addresses" value="{{$z.Addresses}}" size="30" spellcheck="false" placeholder="10.1.0.254"></td>
<td>{{if $z.Name}}<input type="checkbox" name="zone.{{$i}}.remove">{{end}}</td>
</tr>
{{range $f := list "name" "addresses"}}{{with index $e (printf "zone[%d].%s" $i $f)}}<tr><td colspan="3" class="field-error">{{$f}}: {{.}}</td></tr>{{end}}{{end}}
{{end}}
</table>
<p class="hint">Names below these domains are asked from these resolvers (for example across a tunnel), not from the internet.</p>
</fieldset>
<button>Save</button>
</form>
{{if .Auto}}
<section>
<h2>Published automatically</h2>
<ul>{{range .Auto}}<li>{{.}}</li>{{end}}</ul>
<p class="hint">The router's own name and the DHCP reservations (edit those on the DHCP page).</p>
</section>
{{end}}
{{end}}
{{end}}