Compare commits

...

2 Commits

Author SHA1 Message Date
lordwelch
a5420430ab DNS changes 2020-06-14 10:56:54 -07:00
lordwelch
8ba14148d7 Add the ability to run router7 on a normal Linux distribution 2020-06-12 17:53:13 -07:00
15 changed files with 241 additions and 106 deletions

View File

@ -6,6 +6,15 @@ PKGS := github.com/rtr7/router7/cmd/... \
github.com/stapelberg/zkj-nas-tools/wolgw \
github.com/gokrazy/gdns
build:
mkdir -p result
GOOS=linux go build -o ./result github.com/rtr7/router7/cmd/...
GOOS=linux go build -o ./result/rtr7-init -ldflags "-X main.buildTimestamp=$(shell date '+%Y-%m-%dT%H:%M:%S%z') -X github.com/gokrazy/gokrazy.httpPassword=temp" init/init.go
clean:
rm -rf result
go clean -cache
image:
ifndef DIR
@echo variable DIR unset

View File

@ -16,6 +16,7 @@
package main
import (
"flag"
"net"
"net/http"
"os"
@ -29,9 +30,12 @@ import (
"github.com/rtr7/router7/internal/teelogger"
)
var log = teelogger.NewConsole()
var (
log = teelogger.NewConsole()
httpListeners = multilisten.NewPool()
var httpListeners = multilisten.NewPool()
perm = flag.String("perm", "/perm", "path to replace /perm")
)
func updateListeners() error {
hosts, err := gokrazy.PrivateInterfaceAddrs()
@ -47,7 +51,7 @@ func updateListeners() error {
func logic() error {
http.HandleFunc("/backup.tar.gz", func(w http.ResponseWriter, r *http.Request) {
if err := backup.Archive(w, "/perm"); err != nil {
if err := backup.Archive(w, *perm); err != nil {
log.Printf("backup.tar.gz: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}

View File

@ -24,6 +24,7 @@ import (
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
@ -38,6 +39,7 @@ import (
)
var (
perm = flag.String("perm", "/perm", "path to replace /perm")
hostKeyPath = flag.String("host_key",
"/perm/breakglass.host_key",
"path to a PEM-encoded RSA, DSA or ECDSA private key (create using e.g. ssh-keygen -f /perm/breakglass.host_key -N '' -t rsa)")
@ -149,6 +151,9 @@ func logic() error {
func main() {
flag.Parse()
if *hostKeyPath == "/perm/breakglass.host_key" && *perm != "/perm" {
*hostKeyPath = strings.Replace(*hostKeyPath, "/perm", *perm, 1)
}
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -24,7 +24,9 @@ import (
"net"
"os"
"os/signal"
"path"
"path/filepath"
"strings"
"syscall"
"time"
@ -43,6 +45,7 @@ var log = teelogger.NewConsole()
var (
netInterface = flag.String("interface", "uplink0", "network interface to operate on")
stateDir = flag.String("state_dir", "/perm/dhcp4", "directory in which to store lease data (wire/lease.json) and last ACK (wire/ack)")
perm = flag.String("perm", "/perm", "path to replace /perm")
)
func logic() error {
@ -59,7 +62,7 @@ func logic() error {
// still use the old hardware address. We overwrite it with the address that
// netconfigd is going to use to fix this issue without additional
// synchronization.
details, err := netconfig.Interface("/perm", *netInterface)
details, err := netconfig.Interface(*perm, *netInterface)
if err == nil {
if spoof := details.SpoofHardwareAddr; spoof != "" {
if addr, err := net.ParseMAC(spoof); err == nil {
@ -118,7 +121,7 @@ func logic() error {
if err := renameio.WriteFile(ackFn, buf.Bytes(), 0644); err != nil {
return fmt.Errorf("persisting DHCPACK to %s: %v", ackFn, err)
}
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying netconfig: %v", err)
}
select {
@ -138,6 +141,9 @@ func logic() error {
func main() {
// TODO: drop privileges, run as separate uid?
flag.Parse()
if *stateDir == "/perm/dhcp4" && *perm != "/perm" {
*stateDir = strings.Replace(*stateDir, "/perm", *perm, 1)
}
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -28,6 +28,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"path/filepath"
"sort"
"strings"
@ -50,14 +51,16 @@ import (
"github.com/rtr7/router7/internal/teelogger"
)
var iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
var (
log = teelogger.NewConsole()
nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
Name: "non_expired_leases",
Help: "Number of non-expired DHCP leases",
})
var log = teelogger.NewConsole()
var nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
Name: "non_expired_leases",
Help: "Number of non-expired DHCP leases",
})
iface = flag.String("interface", "lan0", "ethernet interface to listen for DHCPv4 requests on")
perm = flag.String("perm", "/perm", "path to replace /perm")
)
func updateNonExpired(leases []*dhcp4d.Lease) {
now := time.Now()
@ -71,7 +74,7 @@ func updateNonExpired(leases []*dhcp4d.Lease) {
nonExpiredLeases.Set(float64(nonExpired))
}
var ouiDB = oui.NewDB("/perm/dhcp4d/oui")
var ouiDB = oui.NewDB(path.Join(*perm, "/dhcp4d/oui"))
var (
leasesMu sync.Mutex
@ -218,7 +221,7 @@ func updateListeners() error {
if err != nil {
return err
}
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
hosts = append(hosts, net1)
}
@ -393,7 +396,7 @@ func newSrv(permDir string) (*srv, error) {
errs <- err
}
updateNonExpired(leases)
if err := notify.Process("/user/dnsd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/dnsd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying dnsd: %v", err)
}
}
@ -422,7 +425,7 @@ func (s *srv) run(ctx context.Context) error {
func main() {
// TODO: drop privileges, run as separate uid?
flag.Parse()
srv, err := newSrv("/perm")
srv, err := newSrv(*perm)
if err != nil {
log.Fatal(err)
}

View File

@ -22,6 +22,7 @@ import (
"io/ioutil"
"os"
"os/signal"
"path"
"path/filepath"
"syscall"
"time"
@ -34,15 +35,17 @@ import (
var log = teelogger.NewConsole()
var perm = flag.String("perm", "/perm", "path to replace /perm")
func logic() error {
const leasePath = "/perm/dhcp6/wire/lease.json"
leasePath := path.Join(*perm, "/dhcp6/wire/lease.json")
if err := os.MkdirAll(filepath.Dir(leasePath), 0755); err != nil {
return err
}
duid, err := ioutil.ReadFile("/perm/dhcp6/duid")
duid, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/duid"))
if err != nil {
log.Printf("could not read /perm/dhcp6/duid (%v), proceeding with DUID-LLT", err)
log.Printf("could not read %s (%v), proceeding with DUID-LLT", path.Join(*perm, "/dhcp6/duid"), err)
}
c, err := dhcp6.NewClient(dhcp6.ClientConfig{
@ -68,10 +71,10 @@ func logic() error {
if err := renameio.WriteFile(leasePath, b, 0644); err != nil {
return err
}
if err := notify.Process("/user/netconfigd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/netconfigd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying netconfig: %v", err)
}
if err := notify.Process("/user/radvd", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "/radvd"), syscall.SIGUSR1); err != nil {
log.Printf("notifying radvd: %v", err)
}
select {

View File

@ -38,6 +38,8 @@ import (
var httpListeners = multilisten.NewPool()
var perm = flag.String("perm", "/perm", "path to replace /perm")
func updateListeners() error {
hosts, err := gokrazy.PrivateInterfaceAddrs()
if err != nil {
@ -134,6 +136,7 @@ func logic() error {
func main() {
flag.Parse()
diag.Perm = *perm
if err := logic(); err != nil {
log.Fatal(err)

View File

@ -24,6 +24,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"syscall"
"github.com/gokrazy/gokrazy"
@ -40,6 +41,9 @@ import (
var (
httpListeners = multilisten.NewPool()
dnsListeners = multilisten.NewPool()
perm = flag.String("perm", "/perm", "path to replace /perm")
domain = flag.String("domain", "lan", "domain name for your network")
)
func updateListeners(mux *miekgdns.ServeMux) error {
@ -56,7 +60,7 @@ func updateListeners(mux *miekgdns.ServeMux) error {
}}
})
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
hosts = append(hosts, net1)
}
@ -75,13 +79,13 @@ func (a *listenerAdapter) Close() error { return a.Shutdown() }
func logic() error {
// TODO: set correct upstream DNS resolver(s)
ip, err := netconfig.LinkAddress("/perm", "lan0")
ip, err := netconfig.LinkAddress(*perm, "lan0")
if err != nil {
return err
}
srv := dns.NewServer(ip.String()+":53", "lan")
srv := dns.NewServer(ip.String()+":53", *domain)
readLeases := func() error {
b, err := ioutil.ReadFile("/perm/dhcp4d/leases.json")
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp4d/leases.json"))
if err != nil {
return err
}

View File

@ -25,6 +25,7 @@ import (
"log"
"net"
"os"
"path"
"time"
"github.com/gokrazy/gokrazy"
@ -35,6 +36,8 @@ import (
var update = dyndns.Update
var perm = flag.String("perm", "/perm", "path to replace /perm")
type DynDNSRecord struct {
// TODO: multiple providers support
Cloudflare struct {
@ -105,7 +108,7 @@ func main() {
var (
configFile = flag.String(
"config_file",
"/perm/dyndns.json",
path.Join(*perm, "/dyndns.json"),
"Path to the JSON configuration",
)

View File

@ -21,6 +21,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"sync"
"syscall"
@ -39,7 +40,9 @@ import (
var log = teelogger.NewConsole()
var (
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
linger = flag.Bool("linger", true, "linger around after applying the configuration (until killed)")
perm = flag.String("perm", "/perm", "path to replace /perm")
noFirewall = flag.Bool("nofirewall", false, "disable the rtr7 firewall")
)
func init() {
@ -114,7 +117,7 @@ func updateListeners() error {
if err != nil {
return err
}
if net1, err := multilisten.IPv6Net1("/perm"); err == nil {
if net1, err := multilisten.IPv6Net1(*perm); err == nil {
hosts = append(hosts, net1)
}
@ -134,19 +137,18 @@ func logic() error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for {
err := netconfig.Apply("/perm/", "/")
err := netconfig.Apply(*perm, "/", !*noFirewall)
// Notify dhcp4d so that it can update its listeners for prometheus
// metrics on the external interface.
if err := notify.Process("/user/dhcp4d", syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "dhcp4d"), syscall.SIGUSR1); err != nil {
log.Printf("notifying dhcp4d: %v", err)
}
// Notify gokrazy about new addresses (netconfig.Apply might have
// modified state before returning an error) so that listeners can be
// updated.
p, _ := os.FindProcess(1)
if err := p.Signal(syscall.SIGHUP); err != nil {
if err := notify.Process(path.Join(path.Dir(os.Args[0]), "rtr7-init"), syscall.SIGHUP); err != nil {
log.Printf("kill -HUP 1: %v", err)
}
if err != nil {
@ -165,6 +167,7 @@ func logic() error {
func main() {
flag.Parse()
netconfig.CmdRoot = path.Dir(os.Args[0])
if err := logic(); err != nil {
log.Fatal(err)
}

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Binary radvd sends IPv6 router advertisments.
// Binary radvd sends IPv6 router advertisements.
package main
import (
@ -23,19 +23,22 @@ import (
"net"
"os"
"os/signal"
"path"
"syscall"
"github.com/rtr7/router7/internal/dhcp6"
"github.com/rtr7/router7/internal/radvd"
)
var perm = flag.String("perm", "/perm", "path to replace /perm")
func logic() error {
srv, err := radvd.NewServer()
if err != nil {
return err
}
readConfig := func() error {
b, err := ioutil.ReadFile("/perm/dhcp6/wire/lease.json")
b, err := ioutil.ReadFile(path.Join(*perm, "/dhcp6/wire/lease.json"))
if err != nil {
return err
}
@ -45,7 +48,7 @@ func logic() error {
}
var additional []net.IPNet
if b, err := ioutil.ReadFile("/perm/radvd/prefixes.json"); err == nil {
if b, err := ioutil.ReadFile(path.Join(*perm, "/radvd/prefixes.json")); err == nil {
if err := json.Unmarshal(b, &additional); err != nil {
return err
}

51
init/init.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"flag"
"fmt"
"log"
"os/exec"
"path"
"github.com/gokrazy/gokrazy"
)
// buildTimestamp can be overridden by specifying e.g.
// -ldflags "-X main.buildTimestamp=foo" when building.
var (
buildTimestamp = "2020-06-08T19:45:52-07:00"
domain string
cmdRoot string
perm string
noFirewall bool
)
func main() {
flag.StringVar(&cmdRoot, "cmdroot", "/usr/bin", "path to rtr7 binaries")
flag.StringVar(&domain, "domain", "lan", "domain name for your network")
flag.StringVar(&perm, "perm", "/var/lib/rtr7/", "path to replace /perm")
flag.BoolVar(&noFirewall, "nofirewall", false, "disable the rtr7 firewall")
flag.Parse()
log.SetFlags(log.LstdFlags | log.Lshortfile)
fmt.Printf("gokrazy build timestamp %s\n", buildTimestamp)
cmds := []*exec.Cmd{
// exec.Command(path.Join(cmdRoot, "/ntp")),
exec.Command(path.Join(cmdRoot, "backupd"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "captured"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp4"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp4d"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dhcp6"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "diagd"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dnsd"), fmt.Sprintf("-domain=%s", domain), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "dyndns"), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "netconfigd"), fmt.Sprintf("-nofirewall=%t", noFirewall), "-perm="+perm),
exec.Command(path.Join(cmdRoot, "radvd"), "-perm="+perm),
}
if err := gokrazy.Supervise(cmds); err != nil {
log.Fatal(err)
}
select {}
}

View File

@ -18,9 +18,12 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"path"
"time"
)
var Perm = "/perm"
func leaseValid(fn string) (status string, _ error) {
var lease struct {
ValidUntil time.Time `json:"valid_until"`
@ -56,7 +59,7 @@ func (d *dhcpv4) Children() []Node {
}
func (d *dhcpv4) Evaluate() (string, error) {
return leaseValid("/perm/dhcp4/wire/lease.json")
return leaseValid(path.Join(Perm, "/dhcp4/wire/lease.json"))
}
// DHCPv4 returns a Node which succeeds if /perm/dhcp4/wire/lease.json contains
@ -83,7 +86,7 @@ func (d *dhcpv6) Children() []Node {
}
func (d *dhcpv6) Evaluate() (string, error) {
return leaseValid("/perm/dhcp6/wire/lease.json")
return leaseValid(path.Join(Perm, "/dhcp6/wire/lease.json"))
}
// DHCPv6 returns a Node which succeeds if /perm/dhcp6/wire/lease.json contains

View File

@ -42,6 +42,11 @@ var log = teelogger.NewConsole()
// DHCP-based local name resolution can be made case-insensitive.
type lcHostname string
type DNSIP struct {
IPv6 net.IP
IPv4 net.IP
}
type Server struct {
Mux *dns.ServeMux
@ -59,7 +64,7 @@ type Server struct {
hostname, ip string
hostsByName map[lcHostname]string
hostsByIP map[string]string
subnames map[lcHostname]map[string]net.IP // hostname → subname → ip
subnames map[lcHostname]map[string]DNSIP // hostname → subname → ip
upstreamMu sync.RWMutex
upstream []string
@ -74,6 +79,10 @@ func NewServer(addr, domain string) *Server {
domain: domain,
upstream: []string{
// https://developers.google.com/speed/public-dns/docs/using#google_public_dns_ip_addresses
"1.1.1.1:53",
"1.0.0.1:53",
"2606:4700:4700::1111:53",
"2606:4700:4700::1001:53",
"8.8.8.8:53",
"8.8.4.4:53",
"[2001:4860:4860::8888]:53",
@ -82,7 +91,7 @@ func NewServer(addr, domain string) *Server {
sometimes: rate.NewLimiter(rate.Every(1*time.Second), 1), // at most once per second
hostname: hostname,
ip: ip,
subnames: make(map[lcHostname]map[string]net.IP),
subnames: make(map[lcHostname]map[string]DNSIP),
}
server.prom.registry = prometheus.NewRegistry()
@ -111,7 +120,8 @@ func NewServer(addr, domain string) *Server {
server.prom.registry.MustRegister(prometheus.NewGoCollector())
server.initHostsLocked()
server.Mux.HandleFunc(".", server.handleRequest)
server.Mux.HandleFunc("lan.", server.handleInternal)
server.Mux.HandleFunc(domain+".", server.subnameHandler(domain))
server.Mux.HandleFunc("lan.", server.subnameHandler(domain))
server.Mux.HandleFunc("localhost.", server.handleInternal)
go func() {
for range time.Tick(10 * time.Second) {
@ -124,14 +134,20 @@ func NewServer(addr, domain string) *Server {
func (s *Server) initHostsLocked() {
s.hostsByName = make(map[lcHostname]string)
s.hostsByIP = make(map[string]string)
s.subnames[lcHostname(s.domain)] = make(map[string]DNSIP)
if s.hostname != "" && s.ip != "" {
lower := strings.ToLower(s.hostname)
s.hostsByName[lcHostname(lower)] = s.ip
if rev, err := dns.ReverseAddr(s.ip); err == nil {
s.hostsByIP[rev] = s.hostname
}
s.Mux.HandleFunc(lower+".", s.subnameHandler(s.hostname))
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(s.hostname))
subnames := s.subnames[lcHostname(s.domain)]
ip := net.ParseIP(s.ip)
if ip.To4() != nil {
subnames[lower] = DNSIP{IPv4: ip}
} else {
subnames[lower] = DNSIP{IPv6: ip}
}
}
}
@ -195,10 +211,12 @@ func (s *Server) hostByIP(n string) (string, bool) {
return r, ok
}
func (s *Server) subname(hostname, host string) (net.IP, bool) {
func (s *Server) subname(hostname, host string) (DNSIP, bool) {
s.mu.Lock()
defer s.mu.Unlock()
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][host]
// // log.Println(s.subnames)
r, ok := s.subnames[lcHostname(strings.ToLower(hostname))][strings.ToLower(host)]
// log.Println("returning", r, ok)
return r, ok
}
@ -235,10 +253,20 @@ func (s *Server) DyndnsHandler(w http.ResponseWriter, r *http.Request) {
lower := strings.ToLower(hostname)
subnames, ok := s.subnames[lcHostname(lower)]
if !ok {
subnames = make(map[string]net.IP)
subnames = make(map[string]DNSIP)
s.subnames[lcHostname(lower)] = subnames
}
subnames[host] = ip
if ip.To4() != nil {
subnames[host] = DNSIP{
IPv4: ip,
IPv6: subnames[host].IPv6,
}
} else {
subnames[host] = DNSIP{
IPv4: subnames[host].IPv4,
IPv6: ip,
}
}
w.Write([]byte("ok\n"))
}
@ -270,11 +298,27 @@ func (s *Server) SetLeases(leases []dhcp4d.Lease) {
continue // dont overwrite e.g. the hostname entry
}
s.hostsByName[lcHostname(lower)] = l.Addr.String()
subnames, ok := s.subnames[lcHostname(s.domain)]
if !ok {
subnames = make(map[string]DNSIP)
s.subnames[lcHostname(s.domain)] = subnames
}
if l.Addr.To4() != nil {
subnames[lower] = DNSIP{
IPv4: l.Addr,
IPv6: subnames[lower].IPv6,
}
} else {
subnames[lower] = DNSIP{
IPv4: subnames[lower].IPv4,
IPv6: l.Addr,
}
}
if rev, err := dns.ReverseAddr(l.Addr.String()); err == nil {
s.hostsByIP[rev] = l.Hostname
}
s.Mux.HandleFunc(lower+".", s.subnameHandler(lower))
s.Mux.HandleFunc(lower+"."+s.domain+".", s.subnameHandler(lower))
}
}
@ -329,10 +373,7 @@ func isLocalInAddrArpa(q string) bool {
var errEmpty = errors.New("no answers")
func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
if q.Qclass != dns.ClassINET {
return nil, nil
}
func (s *Server) resolveLocal(q dns.Question) (rr dns.RR, err error) {
if strings.ToLower(q.Name) == "localhost." {
if q.Qtype == dns.TypeAAAA {
return dns.NewRR(q.Name + " 3600 IN AAAA ::1")
@ -341,18 +382,6 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
return dns.NewRR(q.Name + " 3600 IN A 127.0.0.1")
}
}
if q.Qtype == dns.TypeA ||
q.Qtype == dns.TypeAAAA ||
q.Qtype == dns.TypeMX {
name := strings.TrimSuffix(q.Name, ".")
name = strings.TrimSuffix(name, "."+s.domain)
if host, ok := s.hostByName(name); ok {
if q.Qtype == dns.TypeA {
return dns.NewRR(q.Name + " 3600 IN A " + host)
}
return nil, errEmpty
}
}
if q.Qtype == dns.TypePTR {
if host, ok := s.hostByIP(q.Name); ok {
return dns.NewRR(q.Name + " 3600 IN PTR " + host + "." + s.domain)
@ -365,13 +394,11 @@ func (s *Server) resolve(q dns.Question) (rr dns.RR, err error) {
}
func (s *Server) handleInternal(w dns.ResponseWriter, r *dns.Msg) {
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues("local").Inc()
s.promInc("local", r)
if len(r.Question) != 1 { // TODO: answer all questions we can answer
return
}
rr, err := s.resolve(r.Question[0])
rr, err := s.resolveLocal(r.Question[0])
if err != nil {
if err == errEmpty {
m := new(dns.Msg)
@ -379,7 +406,7 @@ func (s *Server) handleInternal(w dns.ResponseWriter, r *dns.Msg) {
w.WriteMsg(m)
return
}
log.Fatal(err)
log.Fatalf("question %#v: %v", r.Question[0], err)
}
if rr != nil {
m := new(dns.Msg)
@ -388,7 +415,7 @@ func (s *Server) handleInternal(w dns.ResponseWriter, r *dns.Msg) {
w.WriteMsg(m)
return
}
// Send an authoritative NXDOMAIN for local names:
// Send an authoritative NXDOMAIN for local:
m := new(dns.Msg)
m.SetReply(r)
m.SetRcode(r, dns.RcodeNameError)
@ -412,9 +439,7 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
}
}
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues("DNS").Inc()
s.promInc("DNS", r)
for idx, u := range s.upstreams() {
in, _, err := s.client.Exchange(r, u)
@ -436,36 +461,23 @@ func (s *Server) handleRequest(w dns.ResponseWriter, r *dns.Msg) {
// DNS has no reply for resolving errors
}
func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error) {
func (s *Server) resolveSubname(domain string, q dns.Question) (dns.RR, error) {
// log.Println("relolving subname of", domain, q.Name)
if q.Qclass != dns.ClassINET {
return nil, nil
}
if q.Qtype == dns.TypeA ||
q.Qtype == dns.TypeAAAA ||
q.Qtype == dns.TypeMX {
name := strings.TrimSuffix(q.Name, "."+hostname+".")
name = strings.TrimSuffix(name, "."+hostname+"."+s.domain+".")
if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA /*|| q.Qtype == dns.TypeMX*/ {
name := strings.TrimSuffix(q.Name, ".")
name = strings.TrimSuffix(name, "."+domain)
// log.Println("name to search", name)
if lower := strings.ToLower(q.Name); lower == hostname+"." ||
lower == hostname+"."+s.domain+"." {
host, ok := s.hostByName(hostname)
if !ok {
// The corresponding DHCP lease might have expired, but this
// handler is still installed on the mux.
return nil, nil // NXDOMAIN
}
if q.Qtype == dns.TypeA {
return dns.NewRR(q.Name + " 3600 IN A " + host)
}
return nil, errEmpty
}
if ip, ok := s.subname(domain, name); ok {
if ip, ok := s.subname(hostname, name); ok {
if q.Qtype == dns.TypeA && ip.To4() != nil {
return dns.NewRR(q.Name + " 3600 IN A " + ip.String())
if q.Qtype == dns.TypeA && ip.IPv4.To4() != nil {
return dns.NewRR(q.Name + " 3600 IN A " + ip.IPv4.String())
}
if q.Qtype == dns.TypeAAAA && ip.To4() == nil {
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.String())
if q.Qtype == dns.TypeAAAA && ip.IPv6.To4() == nil && ip.IPv6 != nil {
return dns.NewRR(q.Name + " 3600 IN AAAA " + ip.IPv6.String())
}
return nil, errEmpty
}
@ -473,14 +485,24 @@ func (s *Server) resolveSubname(hostname string, q dns.Question) (dns.RR, error)
return nil, nil
}
func (s *Server) promInc(label string, r *dns.Msg) {
s.prom.queries.Inc()
s.prom.questions.Observe(float64(len(r.Question)))
s.prom.upstream.WithLabelValues(label).Inc()
}
func (s *Server) subnameHandler(hostname string) func(w dns.ResponseWriter, r *dns.Msg) {
return func(w dns.ResponseWriter, r *dns.Msg) {
if len(r.Question) != 1 { // TODO: answer all questions we can answer
s.promInc("local", r)
return
}
rr, err := s.resolveSubname(hostname, r.Question[0])
// log.Println("handle subname", hostname, r.Question[0].Name, rr, err)
if err != nil {
s.promInc("local", r)
if err == errEmpty {
m := new(dns.Msg)
m.SetReply(r)
@ -490,16 +512,24 @@ func (s *Server) subnameHandler(hostname string) func(w dns.ResponseWriter, r *d
log.Fatalf("question %#v: %v", r.Question[0], err)
}
if rr != nil {
s.promInc("local", r)
m := new(dns.Msg)
m.SetReply(r)
m.Answer = append(m.Answer, rr)
w.WriteMsg(m)
return
}
// Send an authoritative NXDOMAIN for local names:
m := new(dns.Msg)
m.SetReply(r)
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
if r.Question[0].Qtype == dns.TypePTR || !strings.Contains(strings.TrimSuffix(r.Question[0].Name, "."), ".") || strings.HasSuffix(r.Question[0].Name, ".lan.") {
s.promInc("local", r)
m := new(dns.Msg)
m.SetReply(r)
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
return
}
s.handleRequest(w, r)
}
}

View File

@ -21,6 +21,7 @@ import (
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
@ -42,6 +43,8 @@ import (
var log = teelogger.NewConsole()
var CmdRoot = "/user"
func subnetMaskSize(mask string) (int, error) {
parts := strings.Split(mask, ".")
if got, want := len(parts), 4; got != want {
@ -729,7 +732,7 @@ func applySysctl(ifname string) error {
return nil
}
func Apply(dir, root string) error {
func Apply(dir, root string, firewall bool) error {
// TODO: split into two parts: delay the up until later
if err := applyInterfaces(dir, root); err != nil {
@ -757,7 +760,7 @@ func Apply(dir, root string) error {
"backupd", // listens on private IPv4/IPv6
"captured", // listens on private IPv4/IPv6
} {
if err := notify.Process("/user/"+process, syscall.SIGUSR1); err != nil {
if err := notify.Process(path.Join(CmdRoot, process), syscall.SIGUSR1); err != nil {
log.Printf("notifying %s: %v", process, err)
}
}
@ -771,8 +774,10 @@ func Apply(dir, root string) error {
appendError(fmt.Errorf("sysctl: %v", err))
}
if err := applyFirewall(dir, ifname); err != nil {
appendError(fmt.Errorf("firewall: %v", err))
if firewall {
if err := applyFirewall(dir, ifname); err != nil {
appendError(fmt.Errorf("firewall: %v", err))
}
}
if err := applyWireGuard(dir); err != nil {