Add identity: whois resolver, checker, header mode, middleware; config for wake, peers, identity
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Wake is the magic-wake pattern sent to a host to rouse it: its MAC, the
|
||||
// broadcast address to aim at, and how long to wait for the answer.
|
||||
type Wake struct {
|
||||
MAC string `toml:"mac"`
|
||||
Broadcast string `toml:"broadcast"`
|
||||
Wait Duration `toml:"wait"`
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultWakeWait = 45 * time.Second
|
||||
MinWakeWait = 5 * time.Second
|
||||
)
|
||||
|
||||
// identityMode reports whether s is a recognized identity backend.
|
||||
func identityMode(s string) bool {
|
||||
return s == "off" || s == "tailscale" || s == "header"
|
||||
}
|
||||
|
||||
// checkWake validates and defaults the magic-wake pattern of each host that has
|
||||
// one.
|
||||
func (c *Config) checkWake() *Error {
|
||||
for name := range c.Hosts {
|
||||
h := c.Hosts[name]
|
||||
w := h.Wake
|
||||
if w == nil {
|
||||
continue
|
||||
}
|
||||
wakeField := fmt.Sprintf("hosts.%s.wake", name)
|
||||
|
||||
mac, err := net.ParseMAC(w.MAC)
|
||||
if err != nil || len(mac) != 6 {
|
||||
return &Error{Field: wakeField + ".mac", Msg: "must be a MAC address"}
|
||||
}
|
||||
|
||||
if _, _, err := net.SplitHostPort(w.Broadcast); err != nil || w.Broadcast == "" {
|
||||
return &Error{Field: wakeField + ".broadcast", Msg: "must be a non-empty host:port"}
|
||||
}
|
||||
|
||||
if w.Wait.Duration == 0 {
|
||||
w.Wait.Duration = DefaultWakeWait
|
||||
} else if w.Wait.Duration < MinWakeWait {
|
||||
return &Error{Field: wakeField + ".wait", Msg: "must be at least 5s"}
|
||||
}
|
||||
h.Wake = w
|
||||
c.Hosts[name] = h
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkIdentity rejects an unrecognized identity backend.
|
||||
func (c *Config) checkIdentity() *Error {
|
||||
if !identityMode(c.Identity) {
|
||||
return &Error{Field: "identity", Msg: `must be "off", "tailscale", or "header"`}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkPeers enforces the peers/identity contract for one route: peers may only
|
||||
// be set with an identity backend on, and may not be an empty list.
|
||||
func checkPeers(name string, peers []string, peersDefined, identityDefined bool, identity string) *Error {
|
||||
peersField := fmt.Sprintf("routes.%s.peers", name)
|
||||
switch {
|
||||
case len(peers) > 0 && identityDefined && identity == "off":
|
||||
return &Error{Field: peersField, Msg: "peers need identity = tailscale or header"}
|
||||
case len(peers) == 0 && peersDefined && identityDefined && identity != "off":
|
||||
return &Error{Field: peersField, Msg: "empty peers list"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user