Skip to content

Commit

Permalink
ms8.1 - further improvements for single-binary operation
Browse files Browse the repository at this point in the history
Embed web-based and BPF C file templates so that nethadone
can run entirely from a pre-built binary and not require
a checkout of the source code

External JS libraries will still be pulled from CDNs when accessing
the web interface, tbd whether it's worth the trouble to embed those

Github action to release AMD64, ARM64 and ARM single binary builds
  • Loading branch information
atomic77 committed Aug 11, 2024
1 parent 0709c95 commit 06d82ad
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 19 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/build-nethadone-binary.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions cmd/nethadone.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
})
Expand Down
41 changes: 27 additions & 14 deletions handlers/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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)
}
Expand Down
10 changes: 10 additions & 0 deletions handlers/embed.go
Original file line number Diff line number Diff line change
@@ -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
2 changes: 0 additions & 2 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import (
"net"
)

// var config Config

func Index(c *fiber.Ctx) error {
// Render index
return c.Render("index", fiber.Map{
Expand Down
File renamed without changes.
19 changes: 19 additions & 0 deletions scripts/customize-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<EOF > /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

Expand Down
7 changes: 7 additions & 0 deletions views/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package views

import "embed"

//go:embed *.tpl
//go:embed **/*.tpl
var EmbedTemplates embed.FS

0 comments on commit 06d82ad

Please sign in to comment.