From ba9ca9f09724df1bbf207521b7d8af4e30731442 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:14:37 +0000 Subject: [PATCH 01/11] Bump actions/setup-go from 5 to 6 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/cicd.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2364f89..f5b16c2 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -31,7 +31,7 @@ jobs: run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 1.25 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79143df..781d9c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v5 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 1.25 From 0fc13be413b356768c7840baef0b64599b16518e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4fer?= Date: Fri, 19 Sep 2025 16:25:04 +0200 Subject: [PATCH 02/11] feat(Docs): Addding GoReport Badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6eb32ee..8b8f99c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Newt +[![Go Report Card](https://goreportcard.com/badge/github.com/fosrl/newt)](https://goreportcard.com/report/github.com/fosrl/newt) + Newt is a fully user space [WireGuard](https://www.wireguard.com/) tunnel client and TCP/UDP proxy, designed to securely expose private resources controlled by Pangolin. By using Newt, you don't need to manage complex WireGuard tunnels and NATing. ### Installation and Documentation From a08a3b96659816cfeb0dea84f5e00be8cb630f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4fer?= Date: Fri, 19 Sep 2025 16:34:44 +0200 Subject: [PATCH 03/11] feat(Docs): Add License Badge and PkgGo Badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b8f99c..42b306e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Newt - +[![PkgGoDev](https://pkg.go.dev/badge/github.com/fosrl/newt)](https://pkg.go.dev/github.com/fosrl/newt) +[![GitHub License](https://img.shields.io/github/license/fosrl/newt)](https://github.com/fosrl/newt/blob/main/LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/fosrl/newt)](https://goreportcard.com/report/github.com/fosrl/newt) Newt is a fully user space [WireGuard](https://www.wireguard.com/) tunnel client and TCP/UDP proxy, designed to securely expose private resources controlled by Pangolin. By using Newt, you don't need to manage complex WireGuard tunnels and NATing. From 9bd96ac5401de68f04eb7270f24295a0d63236db Mon Sep 17 00:00:00 2001 From: rgutmen Date: Fri, 19 Sep 2025 21:45:23 +0100 Subject: [PATCH 04/11] Support TLS_CLIENT_CERT, TLS_CLIENT_KEY and TLS_CA_CERT in Docker Compose --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 12849b1..28c50ae 100644 --- a/main.go +++ b/main.go @@ -169,7 +169,7 @@ func main() { // Legacy PKCS12 support (deprecated) tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT_PKCS12") // Keep backward compatibility with old environment variable name - if tlsPrivateKey == "" { + if tlsPrivateKey == "" && tlsClientKey == "" && len(tlsClientCAs) == 0 { tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT") } From aff928e60f973adbd576e0eaacb23183dfafcf38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4fer?= Date: Mon, 22 Sep 2025 00:22:42 +0200 Subject: [PATCH 05/11] fix(gh-actions): Workflow does not contain permissions --- .github/workflows/cicd.yml | 3 +++ .github/workflows/test.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2364f89..7c463f5 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -1,5 +1,8 @@ name: CI/CD Pipeline +permissions: + contents: read + on: push: tags: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79143df..8fba9ae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Run Tests +permissions: + contents: read + on: pull_request: branches: From 18813091484d89cc39cb6248ca5354d42825d5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4fer?= Date: Mon, 22 Sep 2025 00:30:33 +0200 Subject: [PATCH 06/11] chore(deps): update golang.org/x/crypto to v0.42.0, golang.org/x/net to v0.44.0, and golang.org/x/sys to v0.36.0 --- go.mod | 9 ++++----- go.sum | 19 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 0ee2cbb..cb813e6 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ require ( github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.3 github.com/vishvananda/netlink v1.3.1 - golang.org/x/crypto v0.41.0 + golang.org/x/crypto v0.42.0 golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 - golang.org/x/net v0.43.0 + golang.org/x/net v0.44.0 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c @@ -48,8 +48,7 @@ require ( go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect + golang.org/x/sys v0.36.0 // indirect golang.org/x/time v0.12.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index c10012f..76b7685 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAj golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -117,8 +117,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -129,12 +129,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -161,7 +161,6 @@ google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= @@ -169,4 +168,4 @@ gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c h1:m/r7OM+Y2Ty1sgBQ7Qb27VgIMBW8ZZhT4gLnUyDIhzI= gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= software.sslmate.com/src/go-pkcs12 v0.6.0 h1:f3sQittAeF+pao32Vb+mkli+ZyT+VwKaD014qFGq6oU= -software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= \ No newline at end of file From 9dc5a3d91c2fc326cf0c97fa9fdc296133abca3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4fer?= Date: Mon, 22 Sep 2025 00:40:18 +0200 Subject: [PATCH 07/11] fix(deps): add missing gopkg.in/yaml.v3 v3.0.1 back --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index cb813e6..a18c2d4 100644 --- a/go.mod +++ b/go.mod @@ -51,4 +51,5 @@ require ( golang.org/x/sys v0.36.0 // indirect golang.org/x/time v0.12.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) \ No newline at end of file From 30907188fb565ac0ef015b38f909681f4c44e746 Mon Sep 17 00:00:00 2001 From: Andrew Barrientos Date: Fri, 26 Sep 2025 06:46:32 +0800 Subject: [PATCH 08/11] docs: Add new cli arg and env var Include blueprint-file as an option in the cli arguments and environment variable --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 42b306e..82ff42a 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ When Newt receives WireGuard control messages, it will use the information encod - `native` (optional): Use native WireGuard interface when accepting clients (requires WireGuard kernel module and Linux, must run as root). Default: false (uses userspace netstack) - `interface` (optional): Name of the WireGuard interface. Default: newt - `keep-interface` (optional): Keep the WireGuard interface. Default: false +- `blueprint-file` (optional): Path to blueprint file to define Pangolin resources and configurations. ## Environment Variables @@ -84,6 +85,7 @@ All CLI arguments can be set using environment variables as an alternative to co - `INTERFACE`: Name of the WireGuard interface. Default: newt (equivalent to `--interface`) - `KEEP_INTERFACE`: Keep the WireGuard interface after shutdown. Default: false (equivalent to `--keep-interface`) - `CONFIG_FILE`: Load the config json from this file instead of in the home folder. +- `BLUEPRINT_FILE`: Path to blueprint file to define Pangolin resources and configurations. (equivalent to `--blueprint-file`) ## Loading secrets from files From 75f6362a90bbbb826ced7498abe7fddcd19ffb2b Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 25 Sep 2025 17:18:28 -0700 Subject: [PATCH 09/11] Add logging to config --- websocket/config.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/websocket/config.go b/websocket/config.go index 6803e81..1d821b6 100644 --- a/websocket/config.go +++ b/websocket/config.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" "runtime" + + "github.com/fosrl/newt/logger" ) func getConfigPath(clientType string) string { @@ -34,6 +36,7 @@ func getConfigPath(clientType string) string { func (c *Client) loadConfig() error { if c.config.ID != "" && c.config.Secret != "" && c.config.Endpoint != "" { + logger.Debug("Config already provided, skipping loading from file") return nil } @@ -65,6 +68,9 @@ func (c *Client) loadConfig() error { c.baseURL = config.Endpoint } + logger.Debug("Loaded config from %s", configPath) + logger.Debug("Config: %+v", c.config) + return nil } From 5d891225dee484441fdfd9909ede51b9c5634062 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 11:28:31 -0700 Subject: [PATCH 10/11] Fix generateAndSaveKeyTo --- key | 1 + wgnetstack/wgnetstack.go | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 key diff --git a/key b/key new file mode 100644 index 0000000..62c22b9 --- /dev/null +++ b/key @@ -0,0 +1 @@ +oBvcoMJZXGzTZ4X+aNSCCQIjroREFBeRCs+a328xWGA= \ No newline at end of file diff --git a/wgnetstack/wgnetstack.go b/wgnetstack/wgnetstack.go index 6684c40..08d740e 100644 --- a/wgnetstack/wgnetstack.go +++ b/wgnetstack/wgnetstack.go @@ -187,6 +187,13 @@ func NewWireGuardService(interfaceName string, mtu int, generateAndSaveKeyTo str // Load or generate private key if generateAndSaveKeyTo != "" { if _, err := os.Stat(generateAndSaveKeyTo); os.IsNotExist(err) { + // File doesn't exist, save the generated key + err = os.WriteFile(generateAndSaveKeyTo, []byte(key.String()), 0600) + if err != nil { + return nil, fmt.Errorf("failed to save private key: %v", err) + } + } else { + // File exists, read the existing key keyData, err := os.ReadFile(generateAndSaveKeyTo) if err != nil { return nil, fmt.Errorf("failed to read private key: %v", err) @@ -195,11 +202,6 @@ func NewWireGuardService(interfaceName string, mtu int, generateAndSaveKeyTo str if err != nil { return nil, fmt.Errorf("failed to parse private key: %v", err) } - } else { - err = os.WriteFile(generateAndSaveKeyTo, []byte(key.String()), 0600) - if err != nil { - return nil, fmt.Errorf("failed to save private key: %v", err) - } } } From 4e648af8e9ed080d820ca482ed47d60964738e41 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 28 Sep 2025 16:26:36 -0700 Subject: [PATCH 11/11] Pick up the existing interface private key --- wg/wg.go | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/wg/wg.go b/wg/wg.go index 3cee1a9..5a512d6 100644 --- a/wg/wg.go +++ b/wg/wg.go @@ -152,6 +152,7 @@ func NewWireGuardService(interfaceName string, mtu int, generateAndSaveKeyTo str } var key wgtypes.Key + var port uint16 // 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 key, err = wgtypes.GeneratePrivateKey() if err != nil { @@ -177,40 +178,43 @@ func NewWireGuardService(interfaceName string, mtu int, generateAndSaveKeyTo str } } - service := &WireGuardService{ - interfaceName: interfaceName, - mtu: mtu, - client: wsClient, - wgClient: wgClient, - key: key, - keyFilePath: generateAndSaveKeyTo, - newtId: newtId, - host: host, - lastReadings: make(map[string]PeerReading), - stopHolepunch: make(chan struct{}), - } - - // Get the existing wireguard port (keep this part) - device, err := service.wgClient.Device(service.interfaceName) + // Get the existing wireguard port + device, err := wgClient.Device(interfaceName) if err == nil { - service.Port = uint16(device.ListenPort) - if service.Port != 0 { - logger.Info("WireGuard interface %s already exists with port %d\n", service.interfaceName, service.Port) + port = uint16(device.ListenPort) + // also set the private key to the existing key + key = device.PrivateKey + if port != 0 { + logger.Info("WireGuard interface %s already exists with port %d\n", interfaceName, port) } else { - service.Port, err = FindAvailableUDPPort(49152, 65535) + port, err = FindAvailableUDPPort(49152, 65535) if err != nil { fmt.Printf("Error finding available port: %v\n", err) return nil, err } } } else { - service.Port, err = FindAvailableUDPPort(49152, 65535) + port, err = FindAvailableUDPPort(49152, 65535) if err != nil { fmt.Printf("Error finding available port: %v\n", err) return nil, err } } + service := &WireGuardService{ + interfaceName: interfaceName, + mtu: mtu, + client: wsClient, + wgClient: wgClient, + key: key, + Port: port, + keyFilePath: generateAndSaveKeyTo, + newtId: newtId, + host: host, + lastReadings: make(map[string]PeerReading), + stopHolepunch: make(chan struct{}), + } + // Register websocket handlers wsClient.RegisterHandler("newt/wg/receive-config", service.handleConfig) wsClient.RegisterHandler("newt/wg/peer/add", service.handleAddPeer)