Reorg and add host flag, and catch error when not root:

- A previous workaround done to recover from a panic on nil
    ref is now not needed and removed. The issue was that I assumed
    cli.Exit(err, int) was a way to exit; i.e. it uses os.Exit()
    under the hood. However, it only constructs a struct that implements
    error. Hence, we should return it, not just execute it.

    - Also warn on root and Windows

    - Move IPPool init to setup rather than run
This commit is contained in:
HeshamTB 2024-03-12 23:35:57 +03:00
parent 1a611616bd
commit a9ad981137

View File

@ -1,10 +1,10 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"net/http" "net/http"
"net/netip"
"os" "os"
"os/signal" "os/signal"
"time" "time"
@ -15,7 +15,13 @@ import (
hvpnnode3 "gitea.hbanafa.com/HeshamTB/hvpn-node3" hvpnnode3 "gitea.hbanafa.com/HeshamTB/hvpn-node3"
) )
var IPPool *hvpnnode3.IPPool /*
Can change all therse vars to local func vars
IPPool can be internal to wglink or other abstraction. As well as
POrt, ifname and CIDR and so on. Use Clie.ctx to get the values
*/
var IPPool hvpnnode3.IPPool
var VPNIPCIDR string var VPNIPCIDR string
var PrivateKeyPath cli.Path var PrivateKeyPath cli.Path
var InterfaceName string var InterfaceName string
@ -30,6 +36,13 @@ func main() {
// TODO: Define error exit codes // TODO: Define error exit codes
slog.SetLogLoggerLevel(slog.LevelDebug) slog.SetLogLoggerLevel(slog.LevelDebug)
uid := os.Getuid()
if uid == -1 {
slog.Warn("Running on windows! whatrr u doing?")
} else if uid == 0 {
slog.Warn("Running as root! avoid running as root by setting CAP_NET_ADMIN")
}
app := createCliApp() app := createCliApp()
err := app.Run(os.Args) err := app.Run(os.Args)
if err != nil { if err != nil {
@ -40,27 +53,8 @@ func main() {
} }
func run() { func run(ctx *cli.Context) {
IPPool, err := hvpnnode3.NewPool(VPNIPCIDR) slog.Debug("Starting run()")
if err != nil {
slog.Error(fmt.Sprintf("main.IPPool: %s", err))
os.Exit(1)
}
slog.Info(fmt.Sprintf("Init ip pool %s", VPNIPCIDR))
testVip, err := IPPool.Allocate()
if err != nil {
slog.Error("main.testVip: ", err)
os.Exit(1)
}
slog.Info(fmt.Sprintf("main.testVip: IP Pool Test IP: %s", testVip.String()))
err = IPPool.Free(testVip)
if err != nil {
slog.Error("main.testVip: Could not free test Vip from IPPool!", err)
os.Exit(1)
}
slog.Info("main.testVip: Test IP Freed")
apiMux := http.NewServeMux() apiMux := http.NewServeMux()
apiMux.HandleFunc("GET /peer", hvpnnode3.HandleGetPeer(wgLink)) apiMux.HandleFunc("GET /peer", hvpnnode3.HandleGetPeer(wgLink))
@ -71,7 +65,11 @@ func run() {
handler = hvpnnode3.HttpLogHandler2(handler) handler = hvpnnode3.HttpLogHandler2(handler)
port := fmt.Sprintf("%d", httpPort) port := fmt.Sprintf("%d", httpPort)
host := "0.0.0.0" host := ctx.String("host")
if host == "" {
slog.Info("Host is not set. Using 0.0.0.0")
host = "0.0.0.0"
}
hostPort := fmt.Sprintf("%s:%s", host, port) hostPort := fmt.Sprintf("%s:%s", host, port)
slog.Info(fmt.Sprintf("Starting HVPN node on %s", hostPort)) slog.Info(fmt.Sprintf("Starting HVPN node on %s", hostPort))
@ -156,6 +154,20 @@ func createCliApp() *cli.App {
} }
app.Flags = append(app.Flags, &wgPort) app.Flags = append(app.Flags, &wgPort)
httpListenAddr := cli.StringFlag{
Name: "host",
Usage: "IP address to listen on for HTTP API requests",
Value: "0.0.0.0",
Action: func(ctx *cli.Context, s string) error {
_, err := netip.ParseAddr(s)
if err != nil {
return cli.Exit(fmt.Sprintf("Can not parse %s as a network IP", s), 1)
}
return nil
},
}
app.Flags = append(app.Flags, &httpListenAddr)
httpPort := cli.IntFlag{ httpPort := cli.IntFlag{
Name: "http-port", Name: "http-port",
Usage: "TCP Port for HTTP API", Usage: "TCP Port for HTTP API",
@ -170,7 +182,7 @@ func createCliApp() *cli.App {
if err != nil { if err != nil {
return err return err
} }
run() run(ctx)
return nil return nil
} }
@ -178,6 +190,7 @@ func createCliApp() *cli.App {
} }
func setup() error { func setup() error {
slog.Debug("Starting setup()")
privKeyFile, err := os.Open(PrivateKeyPath) privKeyFile, err := os.Open(PrivateKeyPath)
if err != nil { if err != nil {
return cli.Exit(err, 1) return cli.Exit(err, 1)
@ -208,24 +221,14 @@ func setup() error {
WgPort, WgPort,
) )
if err != nil { if err != nil {
slog.Warn("Error while initlizing Wireguard netlink and device!")
slog.Warn("Ensure to run as root or with CAP_NET_ADMIN")
return cli.Exit(err, 1) return cli.Exit(err, 1)
} }
wgLink = wg wgLink = wg
// this is done to recover from the next call to wgLink.Device call slog.Info("Wireguard netlink is setup and running")
// idk a better way to recover or prevent the panic when running user
// does not have root or CAP_NET_ADMIN.
defer func() {
if r := recover(); r != nil {
slog.Error(fmt.Sprint(r))
slog.Error("Recovered from panic. Ensure to run as root or with CAP_NET_ADMIN")
cli.Exit(errors.New("Recovered from panic. Ensure to run as root or with CAP_NET_ADMIN"),1)
os.Exit(-1)
}
}()
slog.Info("Wireguard device is setup and running")
dev, err := wgLink.Device(InterfaceName) dev, err := wgLink.Device(InterfaceName)
@ -241,6 +244,29 @@ func setup() error {
), ),
) )
ipPool, err := hvpnnode3.NewPool(VPNIPCIDR)
if err != nil {
slog.Error(fmt.Sprintf("main.IPPool: %s", err))
os.Exit(1)
}
slog.Info(fmt.Sprintf("Init ip pool %s", VPNIPCIDR))
testVip, err := ipPool.Allocate()
if err != nil {
slog.Error("main.testVip: ", err)
os.Exit(1)
}
slog.Info(fmt.Sprintf("main.testVip: IP Pool Test IP: %s", testVip.String()))
err = ipPool.Free(testVip)
if err != nil {
slog.Error("main.testVip: Could not free test Vip from IPPool!", err)
os.Exit(1)
}
slog.Info("main.testVip: Test IP Freed")
IPPool = ipPool
//defer wgLink.Close() //defer wgLink.Close()
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill) signal.Notify(c, os.Interrupt, os.Kill)