diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8741b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/dist/ +/vpnportal +*.log +.DS_Store diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b2e37f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +BINARY := vpnportal +PKG := git.ravensburg.dev/cabele/opnsense-portal/cmd/vpnportal +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none) +DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) +LDFLAGS := -s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE) + +# sha256sum gibt es auf macOS nicht; shasum ist dort das Äquivalent. +SHA256 := $(shell command -v sha256sum >/dev/null 2>&1 && echo sha256sum || echo "shasum -a 256") + +export CGO_ENABLED := 0 + +.PHONY: all build test race vet lint release clean + +all: vet test build + +build: + go build -trimpath -ldflags '$(LDFLAGS)' -o dist/$(BINARY) $(PKG) + +test: + go test ./... + +race: + go test -race ./... + +vet: + go vet ./... + +lint: vet + @test -z "$$(gofmt -l . | tee /dev/stderr)" || (echo "gofmt-Fehler: bitte 'gofmt -w .' ausführen" && exit 1) + +release: clean + @set -e; for arch in amd64 arm64; do \ + echo "==> linux/$$arch"; \ + mkdir -p dist/linux-$$arch; \ + GOOS=linux GOARCH=$$arch go build -trimpath -ldflags '$(LDFLAGS)' \ + -o dist/linux-$$arch/$(BINARY) $(PKG); \ + cp deploy/config.example.yaml deploy/vpnportal.service README.md dist/linux-$$arch/; \ + tar -czf dist/$(BINARY)-$(VERSION)-linux-$$arch.tar.gz -C dist/linux-$$arch .; \ + done + cd dist && $(SHA256) *.tar.gz > SHA256SUMS + +clean: + rm -rf dist diff --git a/cmd/vpnportal/buildinfo.go b/cmd/vpnportal/buildinfo.go new file mode 100644 index 0000000..1db3a45 --- /dev/null +++ b/cmd/vpnportal/buildinfo.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +// Per -ldflags gesetzt; siehe Makefile. +var ( + version = "dev" + commit = "none" + date = "unknown" +) + +func versionString() string { + return fmt.Sprintf("vpnportal %s (commit %s, built %s)", version, commit, date) +} diff --git a/cmd/vpnportal/check.go b/cmd/vpnportal/check.go new file mode 100644 index 0000000..ec6b1a1 --- /dev/null +++ b/cmd/vpnportal/check.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "io" +) + +func runCheck(args []string, stdout, stderr io.Writer) int { + fs, path := configFlags("check", stderr) + testAuth := fs.String("test-auth", "", "Suche und Gruppenprüfung für einen Benutzer durchspielen") + if err := fs.Parse(args); err != nil { + return 2 + } + _ = testAuth + fmt.Fprintf(stderr, "Fehler: config %q kann noch nicht geladen werden (nicht implementiert)\n", *path) + return 1 +} diff --git a/cmd/vpnportal/main.go b/cmd/vpnportal/main.go new file mode 100644 index 0000000..842bed6 --- /dev/null +++ b/cmd/vpnportal/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "io" + "os" +) + +const usage = `vpnportal — VPN-Konfig-Portal für OPNsense + +Aufruf: + vpnportal serve [--config PFAD] Portal starten (Default) + vpnportal check [--config PFAD] [--test-auth BENUTZER] + vpnportal version + +Optionen: + --config PFAD Pfad zur Konfigurationsdatei (Default: /etc/vpnportal/config.yaml) +` + +func main() { + os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) +} + +func run(args []string, stdout, stderr io.Writer) int { + cmd := "serve" + rest := args + if len(args) > 0 && len(args[0]) > 0 && args[0][0] != '-' { + cmd, rest = args[0], args[1:] + } + + switch cmd { + case "version": + fmt.Fprintln(stdout, versionString()) + return 0 + case "serve": + return runServe(rest, stdout, stderr) + case "check": + return runCheck(rest, stdout, stderr) + case "help", "-h", "--help": + fmt.Fprint(stdout, usage) + return 0 + default: + fmt.Fprintf(stderr, "Unbekanntes Kommando: %s\n\n%s", cmd, usage) + return 2 + } +} diff --git a/cmd/vpnportal/main_test.go b/cmd/vpnportal/main_test.go new file mode 100644 index 0000000..fe02445 --- /dev/null +++ b/cmd/vpnportal/main_test.go @@ -0,0 +1,43 @@ +package main + +import ( + "bytes" + "strings" + "testing" +) + +func TestRunVersion(t *testing.T) { + var out, errOut bytes.Buffer + code := run([]string{"version"}, &out, &errOut) + if code != 0 { + t.Fatalf("exit code = %d, want 0 (stderr: %s)", code, errOut.String()) + } + got := out.String() + for _, want := range []string{"vpnportal", "commit", "built"} { + if !strings.Contains(got, want) { + t.Errorf("output %q missing %q", got, want) + } + } +} + +func TestRunUnknownCommand(t *testing.T) { + var out, errOut bytes.Buffer + if code := run([]string{"frobnicate"}, &out, &errOut); code != 2 { + t.Fatalf("exit code = %d, want 2", code) + } + if !strings.Contains(errOut.String(), "frobnicate") { + t.Errorf("stderr should name the unknown command, got %q", errOut.String()) + } +} + +func TestRunNoArgsDefaultsToServe(t *testing.T) { + var out, errOut bytes.Buffer + // serve ohne --config muss mit Fehler abbrechen, nicht mit "unknown command" + code := run(nil, &out, &errOut) + if code == 2 { + t.Fatalf("no args must default to serve, not unknown-command") + } + if !strings.Contains(errOut.String(), "config") { + t.Errorf("stderr should complain about missing config, got %q", errOut.String()) + } +} diff --git a/cmd/vpnportal/serve.go b/cmd/vpnportal/serve.go new file mode 100644 index 0000000..bcc9983 --- /dev/null +++ b/cmd/vpnportal/serve.go @@ -0,0 +1,25 @@ +package main + +import ( + "flag" + "fmt" + "io" +) + +const defaultConfigPath = "/etc/vpnportal/config.yaml" + +func configFlags(name string, stderr io.Writer) (*flag.FlagSet, *string) { + fs := flag.NewFlagSet(name, flag.ContinueOnError) + fs.SetOutput(stderr) + path := fs.String("config", defaultConfigPath, "Pfad zur Konfigurationsdatei") + return fs, path +} + +func runServe(args []string, stdout, stderr io.Writer) int { + fs, path := configFlags("serve", stderr) + if err := fs.Parse(args); err != nil { + return 2 + } + fmt.Fprintf(stderr, "Fehler: config %q kann noch nicht geladen werden (nicht implementiert)\n", *path) + return 1 +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..591fea0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.ravensburg.dev/cabele/opnsense-portal + +go 1.26.5