Add the ability to run router7 on a normal Linux distribution

This commit is contained in:
lordwelch 2020-06-12 17:53:13 -07:00
parent ee17db29b6
commit 3c451f06ca
15 changed files with 147 additions and 41 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,15 +51,17 @@ 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()
var nonExpiredLeases = promauto.NewGauge(prometheus.GaugeOpts{
var (
log = teelogger.NewConsole()
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()
nonExpired := 0
@ -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"
@ -35,15 +36,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{
@ -78,10 +81,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"
@ -40,6 +41,8 @@ var log = teelogger.NewConsole()
var (
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

@ -112,6 +112,7 @@ func NewServer(addr, domain string) *Server {
server.initHostsLocked()
server.Mux.HandleFunc(".", server.handleRequest)
server.Mux.HandleFunc("lan.", server.handleInternal)
server.Mux.HandleFunc(domain+".", server.handleInternal)
server.Mux.HandleFunc("localhost.", server.handleInternal)
go func() {
for range time.Tick(10 * time.Second) {

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,9 +774,11 @@ func Apply(dir, root string) error {
appendError(fmt.Errorf("sysctl: %v", err))
}
if firewall {
if err := applyFirewall(dir, ifname); err != nil {
appendError(fmt.Errorf("firewall: %v", err))
}
}
if err := applyWireGuard(dir); err != nil {
appendError(fmt.Errorf("wireguard: %v", err))