From 29a0f02ceb5b9938f8a1326e5e5a27c57e4ad6e8 Mon Sep 17 00:00:00 2001 From: Milo Schwartz Date: Thu, 9 Jan 2025 16:57:17 -0500 Subject: [PATCH 1/7] fix typos in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f410c3e..f7f247c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Gerbil is a simple [WireGuard](https://www.wireguard.com/) interface management ### Installation and Documentation -Gerbil can be used stand alone with your own API, a static JSON file, or with Pangolin and Newt as part of the larger system. See documentation below: +Gerbil can be used standalone with your own API, a static JSON file, or with Pangolin and Newt as part of the larger system. See documentation below: - [Installation Instructions](https://docs.fossorial.io) - [Full Documentation](https://docs.fossorial.io) @@ -19,7 +19,7 @@ _Sample output of a Gerbil container connected to Pangolin and terminating vario ### Setup WireGuard -A WireGuard interface will be created and configured on the local Linux machine or in the Docker container according to the values given in either a JSON config file or via the remote server. If the interface already exists it will be reconfigured. +A WireGuard interface will be created and configured on the local Linux machine or in the Docker container according to the values given in either a JSON config file or via the remote server. If the interface already exists, it will be reconfigured. ### Manage Peers @@ -27,7 +27,7 @@ Gerbil will create the peers defined in the config on the WireGuard interface. T ### Report Bandwidth -Bytes transmitted in and out of each peer is collected every 10 seconds and incremental usage is reported via the "reportBandwidthTo" endpoint. This can be used to track data usage of each peer on the remote server. +Bytes transmitted in and out of each peer are collected every 10 seconds, and incremental usage is reported via the "reportBandwidthTo" endpoint. This can be used to track data usage of each peer on the remote server. ## CLI Args @@ -97,4 +97,4 @@ Gerbil is dual licensed under the AGPLv3 and the Fossorial Commercial license. F ## Contributions -Please see [CONTRIBUTIONS](./CONTRIBUTING.md) in the repository for guidelines and best practices. \ No newline at end of file +Please see [CONTRIBUTIONS](./CONTRIBUTING.md) in the repository for guidelines and best practices. From b44d3209681458275ddd398c98b4eaba623722e5 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Mon, 13 Jan 2025 22:52:11 -0500 Subject: [PATCH 2/7] Allow changing mtu; set default low --- entrypoint.sh | 11 ----------- main.go | 13 +++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 88edb6c..5058133 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,7 +1,5 @@ #!/bin/sh -# Sample from https://github.com/traefik/traefik-library-image/blob/5070edb25b03cca6802d75d5037576c840f73fdd/v3.1/alpine/entrypoint.sh - set -e # first arg is `-f` or `--some-option` @@ -9,13 +7,4 @@ if [ "${1#-}" != "$1" ]; then set -- gerbil "$@" fi -# if our command is a valid Gerbil subcommand, let's invoke it through Gerbil instead -# (this allows for "docker run gerbil version", etc) -if gerbil "$1" --help >/dev/null 2>&1 -then - set -- gerbil "$@" -else - echo "= '$1' is not a Gerbil command: assuming shell execution." 1>&2 -fi - exec "$@" \ No newline at end of file diff --git a/main.go b/main.go index ac574b7..473bd3d 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "os" + "strconv" "strings" "sync" "time" @@ -22,6 +23,7 @@ import ( var ( interfaceName = "wg0" listenAddr = ":3003" + mtu = 1420 lastReadings = make(map[string]PeerReading) mu sync.Mutex ) @@ -77,6 +79,7 @@ func main() { // Define command line flags interfaceNameArg := flag.String("interface", "wg0", "Name of the WireGuard interface") + mtuRead := flag.String("mtu", "1280", "MTU of the interface") configFile := flag.String("config", "", "Path to local configuration file") remoteConfigURL := flag.String("remoteConfig", "", "URL to fetch remote configuration") listenAddrArg := flag.String("listen", ":3003", "Address to listen on") @@ -97,6 +100,11 @@ func main() { listenAddr = *listenAddrArg } + mtu, err = strconv.Atoi(*mtuRead) + if err != nil { + logger.Fatal("Failed to parse MTU: %v", err) + } + // Validate that only one config option is provided if (*configFile != "" && *remoteConfigURL != "") || (*configFile == "" && *remoteConfigURL == "") { logger.Fatal("Please provide either --config or --remoteConfig, but not both") @@ -288,6 +296,11 @@ func ensureWireguardInterface(wgconfig WgConfig) error { if err != nil { return fmt.Errorf("failed to get interface: %v", err) } + + if err := netlink.LinkSetMTU(link, mtu); err != nil { + return fmt.Errorf("failed to set MTU: %v", err) + } + if err := netlink.LinkSetUp(link); err != nil { return fmt.Errorf("failed to bring up interface: %v", err) } From 217379d28694c448362d367a5d250b21c3831e76 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Tue, 14 Jan 2025 21:57:00 -0500 Subject: [PATCH 3/7] Working on mtu --- main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 473bd3d..e049f21 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,7 @@ import ( var ( interfaceName = "wg0" listenAddr = ":3003" - mtu = 1420 + mtuInt = 1420 lastReadings = make(map[string]PeerReading) mu sync.Mutex ) @@ -79,7 +79,7 @@ func main() { // Define command line flags interfaceNameArg := flag.String("interface", "wg0", "Name of the WireGuard interface") - mtuRead := flag.String("mtu", "1280", "MTU of the interface") + mtu := flag.String("mtu", "1280", "MTU of the interface") configFile := flag.String("config", "", "Path to local configuration file") remoteConfigURL := flag.String("remoteConfig", "", "URL to fetch remote configuration") listenAddrArg := flag.String("listen", ":3003", "Address to listen on") @@ -100,7 +100,7 @@ func main() { listenAddr = *listenAddrArg } - mtu, err = strconv.Atoi(*mtuRead) + mtuInt, err = strconv.Atoi(*mtu) if err != nil { logger.Fatal("Failed to parse MTU: %v", err) } @@ -297,7 +297,7 @@ func ensureWireguardInterface(wgconfig WgConfig) error { return fmt.Errorf("failed to get interface: %v", err) } - if err := netlink.LinkSetMTU(link, mtu); err != nil { + if err := netlink.LinkSetMTU(link, mtuInt); err != nil { return fmt.Errorf("failed to set MTU: %v", err) } From 5101e6f12533b9a8bd2a83840bb509c31787d12c Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Tue, 14 Jan 2025 22:04:06 -0500 Subject: [PATCH 4/7] Add env vars --- entrypoint.sh | 13 +------- main.go | 84 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 88edb6c..79ae7a0 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,21 +1,10 @@ #!/bin/sh -# Sample from https://github.com/traefik/traefik-library-image/blob/5070edb25b03cca6802d75d5037576c840f73fdd/v3.1/alpine/entrypoint.sh - set -e # first arg is `-f` or `--some-option` if [ "${1#-}" != "$1" ]; then - set -- gerbil "$@" -fi - -# if our command is a valid Gerbil subcommand, let's invoke it through Gerbil instead -# (this allows for "docker run gerbil version", etc) -if gerbil "$1" --help >/dev/null 2>&1 -then - set -- gerbil "$@" -else - echo "= '$1' is not a Gerbil command: assuming shell execution." 1>&2 + set -- newt "$@" fi exec "$@" \ No newline at end of file diff --git a/main.go b/main.go index ac574b7..1893905 100644 --- a/main.go +++ b/main.go @@ -72,52 +72,78 @@ func parseLogLevel(level string) logger.LogLevel { } func main() { - var err error - var wgconfig WgConfig + var ( + err error + wgconfig WgConfig + interfaceName string + configFile string + remoteConfigURL string + listenAddr string + reportBandwidthTo string + generateAndSaveKeyTo string + reachableAt string + logLevel string + ) - // Define command line flags - interfaceNameArg := flag.String("interface", "wg0", "Name of the WireGuard interface") - configFile := flag.String("config", "", "Path to local configuration file") - remoteConfigURL := flag.String("remoteConfig", "", "URL to fetch remote configuration") - listenAddrArg := flag.String("listen", ":3003", "Address to listen on") - reportBandwidthTo := flag.String("reportBandwidthTo", "", "Address to listen on") - generateAndSaveKeyTo := flag.String("generateAndSaveKeyTo", "", "Path to save generated private key") - reachableAt := flag.String("reachableAt", "", "Endpoint of the http server to tell remote config about") - logLevel := flag.String("log-level", "INFO", "Log level (DEBUG, INFO, WARN, ERROR, FATAL)") + interfaceName = os.Getenv("INTERFACE") + configFile = os.Getenv("CONFIG") + remoteConfigURL = os.Getenv("REMOTE_CONFIG") + listenAddr = os.Getenv("LISTEN") + reportBandwidthTo = os.Getenv("REPORT_BANDWIDTH_TO") + generateAndSaveKeyTo = os.Getenv("GENERATE_AND_SAVE_KEY_TO") + reachableAt = os.Getenv("REACHABLE_AT") + logLevel = os.Getenv("LOG_LEVEL") + if interfaceName == "" { + flag.StringVar(&interfaceName, "interface", "wg0", "Name of the WireGuard interface") + } + if configFile == "" { + flag.StringVar(&configFile, "config", "", "Path to local configuration file") + } + if remoteConfigURL == "" { + flag.StringVar(&remoteConfigURL, "remoteConfig", "", "URL to fetch remote configuration") + } + if listenAddr == "" { + flag.StringVar(&listenAddr, "listen", ":3003", "Address to listen on") + } + if reportBandwidthTo == "" { + flag.StringVar(&reportBandwidthTo, "reportBandwidthTo", "", "Address to listen on") + } + if generateAndSaveKeyTo == "" { + flag.StringVar(&generateAndSaveKeyTo, "generateAndSaveKeyTo", "", "Path to save generated private key") + } + if reachableAt == "" { + flag.StringVar(&reachableAt, "reachableAt", "", "Endpoint of the http server to tell remote config about") + } + if logLevel == "" { + flag.StringVar(&logLevel, "log-level", "INFO", "Log level (DEBUG, INFO, WARN, ERROR, FATAL)") + } flag.Parse() logger.Init() - logger.GetLogger().SetLevel(parseLogLevel(*logLevel)) - - if *interfaceNameArg != "" { - interfaceName = *interfaceNameArg - } - if *listenAddrArg != "" { - listenAddr = *listenAddrArg - } + logger.GetLogger().SetLevel(parseLogLevel(logLevel)) // Validate that only one config option is provided - if (*configFile != "" && *remoteConfigURL != "") || (*configFile == "" && *remoteConfigURL == "") { + if (configFile != "" && remoteConfigURL != "") || (configFile == "" && remoteConfigURL == "") { logger.Fatal("Please provide either --config or --remoteConfig, but not both") } var key wgtypes.Key // if generateAndSaveKeyTo is provided, generate a private key and save it to the file. if the file already exists, load the key from the file - if *generateAndSaveKeyTo != "" { - if _, err := os.Stat(*generateAndSaveKeyTo); os.IsNotExist(err) { + if generateAndSaveKeyTo != "" { + if _, err := os.Stat(generateAndSaveKeyTo); os.IsNotExist(err) { // generate a new private key key, err = wgtypes.GeneratePrivateKey() if err != nil { logger.Fatal("Failed to generate private key: %v", err) } // save the key to the file - err = os.WriteFile(*generateAndSaveKeyTo, []byte(key.String()), 0644) + err = os.WriteFile(generateAndSaveKeyTo, []byte(key.String()), 0644) if err != nil { logger.Fatal("Failed to save private key: %v", err) } } else { - keyData, err := os.ReadFile(*generateAndSaveKeyTo) + keyData, err := os.ReadFile(generateAndSaveKeyTo) if err != nil { logger.Fatal("Failed to read private key: %v", err) } @@ -138,8 +164,8 @@ func main() { } // Load configuration based on provided argument - if *configFile != "" { - wgconfig, err = loadConfig(*configFile) + if configFile != "" { + wgconfig, err = loadConfig(configFile) if err != nil { logger.Fatal("Failed to load configuration: %v", err) } @@ -147,7 +173,7 @@ func main() { wgconfig.PrivateKey = key.String() } } else { - wgconfig, err = loadRemoteConfig(*remoteConfigURL, key, *reachableAt) + wgconfig, err = loadRemoteConfig(remoteConfigURL, key, reachableAt) if err != nil { logger.Fatal("Failed to load configuration: %v", err) } @@ -168,8 +194,8 @@ func main() { // Ensure the WireGuard peers exist ensureWireguardPeers(wgconfig.Peers) - if *reportBandwidthTo != "" { - go periodicBandwidthCheck(*reportBandwidthTo) + if reportBandwidthTo != "" { + go periodicBandwidthCheck(reportBandwidthTo) } http.HandleFunc("/peer", handlePeer) From fc451baa0613bb5b0e7b76a79c1b7331023d0e30 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 15 Jan 2025 00:01:34 -0500 Subject: [PATCH 5/7] Mtu flag working --- entrypoint.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 23310b2..5058133 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -4,11 +4,7 @@ set -e # first arg is `-f` or `--some-option` if [ "${1#-}" != "$1" ]; then -<<<<<<< HEAD set -- gerbil "$@" -======= - set -- newt "$@" ->>>>>>> env-vars fi exec "$@" \ No newline at end of file From 1712b88e18759aa30f5fbde9f55df71ac9195268 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 15 Jan 2025 20:54:24 -0500 Subject: [PATCH 6/7] MTU 1280 by default --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 6fbfd24..1164bf5 100644 --- a/main.go +++ b/main.go @@ -121,7 +121,7 @@ func main() { flag.StringVar(&logLevel, "log-level", "INFO", "Log level (DEBUG, INFO, WARN, ERROR, FATAL)") } if mtu == "" { - flag.StringVar(&mtu, "mtu", "1420", "MTU of the WireGuard interface") + flag.StringVar(&mtu, "mtu", "1280", "MTU of the WireGuard interface") } flag.Parse() From bc69b625fa7ee695479ccedda46bb1d19b3da397 Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Wed, 15 Jan 2025 21:55:18 -0500 Subject: [PATCH 7/7] Better feedback about config --- main.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 1164bf5..c4b1764 100644 --- a/main.go +++ b/main.go @@ -133,9 +133,14 @@ func main() { logger.Fatal("Failed to parse MTU: %v", err) } - // Validate that only one config option is provided - if (configFile != "" && remoteConfigURL != "") || (configFile == "" && remoteConfigURL == "") { - logger.Fatal("Please provide either --config or --remoteConfig, but not both") + // are they missing either the config file or the remote config URL? + if configFile == "" && remoteConfigURL == "" { + logger.Fatal("You must provide either a config file or a remote config URL") + } + + // do they have both the config file and the remote config URL? + if configFile != "" && remoteConfigURL != "" { + logger.Fatal("You must provide either a config file or a remote config URL, not both") } var key wgtypes.Key