diff --git a/.github/workflows/build-nethadone-binary.yml b/.github/workflows/build-nethadone-binary.yml new file mode 100644 index 0000000..ed871a7 --- /dev/null +++ b/.github/workflows/build-nethadone-binary.yml @@ -0,0 +1,32 @@ +name: Build nethadone binary + +on: + workflow_dispatch: + +jobs: + + build-nethadone-binary: + runs-on: ubuntu-22.04 + steps: + - id: generate_release_name + run: | + echo "release_name=nethadone-$(date -u -I)" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.22 + + - name: Build binaries + run: make all + + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + with: + repo_name: atomic77/nethadone + file: build/nethadone-* + tag: ${{ steps.generate_release_name.outputs.release_name }} + overwrite: true + file_glob: true \ No newline at end of file diff --git a/Makefile b/Makefile index 985dd23..3064f2c 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,12 @@ up: ## Run the project in a local container run-root: go build -o build/ ./cmd/nethadone.go sudo build/nethadone -lan-interface $(NTH_LAN) -wan-interface $(NTH_WAN) -config-file config/nethadone.yml +all: + GOARCH=arm GOOS=linux go build -o build/nethadone-arm-linux ./cmd/nethadone.go + GOARCH=arm64 GOOS=linux go build -o build/nethadone-arm64-linux ./cmd/nethadone.go + GOARCH=amd64 GOOS=linux go build -o build/nethadone-arm-linux ./cmd/nethadone.go -build: ## Generate docker image +build: go build ./cmd/nethadone.go build-docker: ## Generate docker image diff --git a/cmd/nethadone.go b/cmd/nethadone.go index a7c332e..31102a3 100644 --- a/cmd/nethadone.go +++ b/cmd/nethadone.go @@ -3,12 +3,14 @@ package main import ( "flag" "log" + "net/http" "github.com/alecthomas/repr" "github.com/atomic77/nethadone/config" "github.com/atomic77/nethadone/database" "github.com/atomic77/nethadone/handlers" "github.com/atomic77/nethadone/policy" + "github.com/atomic77/nethadone/views" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/adaptor" "github.com/gofiber/fiber/v2/middleware/logger" @@ -32,8 +34,8 @@ func main() { database.Connect() handlers.Initialize() - // Pass the engine to the Views - engine := html.New("./views", ".tpl") + // Use embedded templates + engine := html.NewFileSystem(http.FS(views.EmbedTemplates), ".tpl") app := fiber.New(fiber.Config{ Views: engine, }) diff --git a/handlers/bpf.go b/handlers/bpf.go index 06a8b10..1bf44ca 100644 --- a/handlers/bpf.go +++ b/handlers/bpf.go @@ -88,28 +88,41 @@ func Initialize() { } func ApplyPolicies(ipPolicies *[]models.IpPolicy) { - rebuildBpf("ebpf/throttle.bpf.c.tpl", "ebpf/throttle.bpf.c", ipPolicies) - reattachThrottler(config.Cfg.LanInterface, tc.HandleMinEgress) + targFile, err := os.CreateTemp("", "throttle-*.bpf.c") + if err != nil { + log.Fatal("could not create throttler file: ", err) + } + objFile, err := os.CreateTemp("", "throttle-*.o") + if err != nil { + log.Fatal("could not create throttler object file: ", err) + } + + rebuildBpf("throttle.bpf.c.tpl", targFile, objFile, ipPolicies) + reattachThrottler(objFile, config.Cfg.LanInterface, tc.HandleMinEgress) + os.Remove(targFile.Name()) + os.Remove(objFile.Name()) } -func rebuildBpf(tplfile string, target string, ipPolicies *[]models.IpPolicy) { +func rebuildBpf(tplfile string, target *os.File, objFile *os.File, ipPolicies *[]models.IpPolicy) { log.Println("Rebuilding with ", len(*ipPolicies), " throttle targets from policy database") - - f, err := os.Create(target) - if err != nil { - log.Fatal("failed to create rendered file ", err) - } - tpl := template.Must(template.ParseFiles(tplfile)) + tpl := template.Must(template.ParseFS(EmbedThrottlerCode, tplfile)) type fdata struct { IpPolicies *[]models.IpPolicy } - err = tpl.Execute(f, fdata{IpPolicies: ipPolicies}) + + log.Println("Temporary file ", target.Name()) + err := tpl.Execute(target, fdata{IpPolicies: ipPolicies}) if err != nil { log.Fatal("failed to render file ", err) } // FIXME There surely must be a better way of doing this dynamically - cmd := exec.Command("make", "build-throttler") - cmd.Dir = "ebpf/" + cmd := exec.Command( + "clang", "-g", "-O2", + // Include both armv7 and aarch64 include folders + "-I/usr/include/aarch64-linux-gnu", "-I/usr/arm-linux-gnueabi/include", + "-Wall", "-target", "bpf", "-c", target.Name(), "-o", objFile.Name(), + ) + cmd.Dir = os.TempDir() out, err := cmd.CombinedOutput() if err != nil { log.Fatal("failed to rebuild throttler eBPF, out: ", string(out), " err: ", err) @@ -144,7 +157,7 @@ func cleanupThrottler(iface *net.Interface) { } -func reattachThrottler(ifname string, direction uint32) { +func reattachThrottler(objFile *os.File, ifname string, direction uint32) { log.Println("(Re)attaching throttler BPF to if ", ifname, " direction ", direction) @@ -157,7 +170,7 @@ func reattachThrottler(ifname string, direction uint32) { cleanupThrottler(iface) BpfCtx.ThrottleObjs = &throttleObjects{} - spec, err := ebpf.LoadCollectionSpec("ebpf/throttle.o") + spec, err := ebpf.LoadCollectionSpec(objFile.Name()) if err != nil { log.Fatal("failed to load spec ", err) } diff --git a/handlers/embed.go b/handlers/embed.go new file mode 100644 index 0000000..7943771 --- /dev/null +++ b/handlers/embed.go @@ -0,0 +1,10 @@ +package handlers + +import "embed" + +// Currently we are handling the management of the throttler +// ebpf outside of bpf2go due to challenges figuring out how to allow +// live recompiling + +//go:embed throttle.bpf.c.tpl +var EmbedThrottlerCode embed.FS diff --git a/handlers/handlers.go b/handlers/handlers.go index f31c51e..22abcc4 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -17,8 +17,6 @@ import ( "net" ) -// var config Config - func Index(c *fiber.Ctx) error { // Render index return c.Render("index", fiber.Map{ diff --git a/ebpf/throttle.bpf.c.tpl b/handlers/throttle.bpf.c.tpl similarity index 100% rename from ebpf/throttle.bpf.c.tpl rename to handlers/throttle.bpf.c.tpl diff --git a/scripts/customize-image.sh b/scripts/customize-image.sh index 0ff80da..ae9550d 100644 --- a/scripts/customize-image.sh +++ b/scripts/customize-image.sh @@ -67,6 +67,25 @@ WantedBy=multi-user.target EOF +## FIXME The interfaces are not being read from config file due to a bug in nethadone.go +cat < /etc/systemd/system/nethadone.service +[Unit] +Description=Nethadone +Wants=network-online.target +After=network-online.target + +[Service] +User=root +Restart=on-failure +RuntimeMaxSec=86400 + +ExecStart=/usr/local/bin/nethadone --config-file=/etc/nethadone.yml + +[Install] +WantedBy=multi-user.target + +EOF + systemctl daemon-reload systemctl enable prometheus diff --git a/views/embed.go b/views/embed.go new file mode 100644 index 0000000..d75a4a2 --- /dev/null +++ b/views/embed.go @@ -0,0 +1,7 @@ +package views + +import "embed" + +//go:embed *.tpl +//go:embed **/*.tpl +var EmbedTemplates embed.FS