From 07b7025a24a99e41cc5653f9e3af85d50e718b70 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 2 Sep 2025 12:20:07 -0700 Subject: [PATCH 01/20] Ensure backward compatability with --docker-socket --- docker/client.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docker/client.go b/docker/client.go index f8b9d0c..2a42023 100644 --- a/docker/client.go +++ b/docker/client.go @@ -73,8 +73,11 @@ func parseDockerHost(raw string) (dockerHost, error) { s = strings.TrimPrefix(s, "http://") s = strings.TrimPrefix(s, "https://") return dockerHost{"tcp", s}, nil + case strings.HasPrefix(raw, "/"): + // Absolute path without scheme - treat as unix socket + return dockerHost{"unix", raw}, nil default: - // default fallback to unix + // For relative paths or other formats, also default to unix return dockerHost{"unix", raw}, nil } } @@ -85,6 +88,13 @@ func CheckSocket(socketPath string) bool { if socketPath == "" { socketPath = "unix:///var/run/docker.sock" } + + // Ensure the socket path is properly formatted + if !strings.Contains(socketPath, "://") { + // If no scheme provided, assume unix socket + socketPath = "unix://" + socketPath + } + host, err := parseDockerHost(socketPath) if err != nil { logger.Debug("Invalid Docker socket path '%s': %v", socketPath, err) @@ -149,7 +159,13 @@ func IsWithinHostNetwork(socketPath string, targetAddress string, targetPort int func ListContainers(socketPath string, enforceNetworkValidation bool) ([]Container, error) { // Use the provided socket path or default to standard location if socketPath == "" { - socketPath = "/var/run/docker.sock" + socketPath = "unix:///var/run/docker.sock" + } + + // Ensure the socket path is properly formatted for the Docker client + if !strings.Contains(socketPath, "://") { + // If no scheme provided, assume unix socket + socketPath = "unix://" + socketPath } // Used to filter down containers returned to Pangolin 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 02/20] 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 8b0cc36554252e5a822319ba8af5287814d1cb01 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 8 Sep 2025 15:25:05 -0700 Subject: [PATCH 03/20] Add blueprint yaml sending --- go.mod | 1 + go.sum | 1 + main.go | 35 +++++++++++++++++++++++++++++++++++ util.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/go.mod b/go.mod index 061e828..0ee2cbb 100644 --- a/go.mod +++ b/go.mod @@ -51,4 +51,5 @@ require ( golang.org/x/sys v0.35.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 ) diff --git a/go.sum b/go.sum index 51efaf1..c10012f 100644 --- a/go.sum +++ b/go.sum @@ -161,6 +161,7 @@ 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= diff --git a/main.go b/main.go index 12849b1..30a30e9 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,11 @@ type ExitNodePingResult struct { WasPreviouslyConnected bool `json:"wasPreviouslyConnected"` } +type BlueprintResult struct { + Success bool `json:"success"` + Message string `json:"message,omitempty"` +} + // Custom flag type for multiple CA files type stringSlice []string @@ -115,6 +120,7 @@ var ( preferEndpoint string healthMonitor *healthcheck.Monitor enforceHealthcheckCert bool + blueprintFile string // New mTLS configuration variables tlsClientCert string @@ -172,6 +178,7 @@ func main() { if tlsPrivateKey == "" { tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT") } + blueprintFile = os.Getenv("BLUEPRINT_FILE") if endpoint == "" { flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server") @@ -271,6 +278,9 @@ func main() { if healthFile == "" { flag.StringVar(&healthFile, "health-file", "", "Path to health file (if unset, health file won't be written)") } + if blueprintFile == "" { + flag.StringVar(&blueprintFile, "blueprint-file", "", "Path to blueprint file (if unset, no blueprint will be applied)") + } // do a --version check version := flag.Bool("version", false, "Print the version") @@ -1193,6 +1203,29 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub } }) + // Register handler for getting health check status + client.RegisterHandler("newt/blueprint/results", func(msg websocket.WSMessage) { + logger.Debug("Received blueprint results message") + + var blueprintResult BlueprintResult + + jsonData, err := json.Marshal(msg.Data) + if err != nil { + logger.Info("Error marshaling data: %v", err) + return + } + if err := json.Unmarshal(jsonData, &blueprintResult); err != nil { + logger.Info("Error unmarshaling config results data: %v", err) + return + } + + if blueprintResult.Success { + logger.Info("Blueprint applied successfully!") + } else { + logger.Warn("Blueprint application failed: %s", blueprintResult.Message) + } + }) + client.OnConnect(func() error { publicKey = privateKey.PublicKey() logger.Debug("Public key: %s", publicKey) @@ -1216,6 +1249,8 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub "backwardsCompatible": true, }) + sendBlueprint(client) + if err != nil { logger.Error("Failed to send registration message: %v", err) return err diff --git a/util.go b/util.go index 7d6da4f..72d2bda 100644 --- a/util.go +++ b/util.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/ipv4" "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun/netstack" + "gopkg.in/yaml.v3" ) func fixKey(key string) string { @@ -558,3 +559,47 @@ func executeUpdownScript(action, proto, target string) (string, error) { return target, nil } + +func sendBlueprint(client *websocket.Client) error { + if blueprintFile == "" { + return nil + } + // try to read the blueprint file + blueprintData, err := os.ReadFile(blueprintFile) + if err != nil { + logger.Error("Failed to read blueprint file: %v", err) + } else { + // first we should convert the yaml to json and error if the yaml is bad + var yamlObj interface{} + var blueprintJsonData string + + err = yaml.Unmarshal(blueprintData, &yamlObj) + if err != nil { + logger.Error("Failed to parse blueprint YAML: %v", err) + } else { + // convert to json + jsonBytes, err := json.Marshal(yamlObj) + if err != nil { + logger.Error("Failed to convert blueprint to JSON: %v", err) + } else { + blueprintJsonData = string(jsonBytes) + logger.Debug("Converted blueprint to JSON: %s", blueprintJsonData) + } + } + + // if we have valid json data, we can send it to the server + if blueprintJsonData == "" { + logger.Error("No valid blueprint JSON data to send to server") + return nil + } + + logger.Info("Sending blueprint to server for application") + + // send the blueprint data to the server + err = client.SendMessage("newt/blueprint/apply", map[string]interface{}{ + "blueprint": blueprintJsonData, + }) + } + + return nil +} From 92cedd00b3aadbfdf88d00fdf88f0fd64d2f6e67 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 15 Sep 2025 10:58:40 -0700 Subject: [PATCH 04/20] Quiet up the logs --- blueprint.yaml | 37 +++++++++++++++++++++++++++++++++++++ healthcheck/healthcheck.go | 2 +- main.go | 14 +++++++------- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 blueprint.yaml diff --git a/blueprint.yaml b/blueprint.yaml new file mode 100644 index 0000000..5c979d1 --- /dev/null +++ b/blueprint.yaml @@ -0,0 +1,37 @@ +resources: + resource-nice-id: + name: this is my resource + protocol: http + full-domain: level1.test3.example.com + host-header: example.com + tls-server-name: example.com + auth: + pincode: 123456 + password: sadfasdfadsf + sso-enabled: true + sso-roles: + - Member + sso-users: + - owen@fossorial.io + whitelist-users: + - owen@fossorial.io + targets: + # - site: glossy-plains-viscacha-rat + - hostname: localhost + method: http + port: 8000 + healthcheck: + port: 8000 + hostname: localhost + # - site: glossy-plains-viscacha-rat + - hostname: localhost + method: http + port: 8001 + resource-nice-id2: + name: this is other resource + protocol: tcp + proxy-port: 3000 + targets: + # - site: glossy-plains-viscacha-rat + - hostname: localhost + port: 3000 \ No newline at end of file diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index 9cce0f9..49ac718 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -76,7 +76,7 @@ type Monitor struct { // NewMonitor creates a new health check monitor func NewMonitor(callback StatusChangeCallback, enforceCert bool) *Monitor { - logger.Info("Creating new health check monitor with certificate enforcement: %t", enforceCert) + logger.Debug("Creating new health check monitor with certificate enforcement: %t", enforceCert) // Configure TLS settings based on certificate enforcement transport := &http.Transport{ diff --git a/main.go b/main.go index 30a30e9..33b55a5 100644 --- a/main.go +++ b/main.go @@ -478,7 +478,7 @@ func main() { // Register handlers for different message types client.RegisterHandler("newt/wg/connect", func(msg websocket.WSMessage) { - logger.Info("Received registration message") + logger.Debug("Received registration message") if stopFunc != nil { stopFunc() // stop the ws from sending more requests stopFunc = nil // reset stopFunc to nil to avoid double stopping @@ -571,7 +571,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err != nil { logger.Warn("Initial reliable ping failed, but continuing: %v", err) } else { - logger.Info("Initial connection test successful") + logger.Debug("Initial connection test successful") } pingWithRetryStopChan, _ = pingWithRetry(tnet, wgData.ServerIP, pingTimeout) @@ -610,7 +610,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err := healthMonitor.AddTargets(wgData.HealthCheckTargets); err != nil { logger.Error("Failed to bulk add health check targets: %v", err) } else { - logger.Info("Successfully added %d health check targets", len(wgData.HealthCheckTargets)) + logger.Debug("Successfully added %d health check targets", len(wgData.HealthCheckTargets)) } err = pm.Start() @@ -657,7 +657,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub }) client.RegisterHandler("newt/ping/exitNodes", func(msg websocket.WSMessage) { - logger.Info("Received ping message") + logger.Debug("Received ping message") if stopFunc != nil { stopFunc() // stop the ws from sending more requests stopFunc = nil // reset stopFunc to nil to avoid double stopping @@ -979,7 +979,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err != nil { logger.Error("Failed to send Docker container list: %v", err) } else { - logger.Info("Docker container list sent, count: %d", len(containers)) + logger.Debug("Docker container list sent, count: %d", len(containers)) } }) @@ -1095,7 +1095,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err := healthMonitor.AddTargets(config.Targets); err != nil { logger.Error("Failed to add health check targets: %v", err) } else { - logger.Info("Added %d health check targets", len(config.Targets)) + logger.Debug("Added %d health check targets", len(config.Targets)) } logger.Debug("Health check targets added: %+v", config.Targets) @@ -1238,7 +1238,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub } // request from the server the list of nodes to ping at newt/ping/request stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{}, 3*time.Second) - logger.Info("Requesting exit nodes from server") + logger.Debug("Requesting exit nodes from server") clientsOnConnect() } From 9bd96ac5401de68f04eb7270f24295a0d63236db Mon Sep 17 00:00:00 2001 From: rgutmen Date: Fri, 19 Sep 2025 21:45:23 +0100 Subject: [PATCH 05/20] 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 60873f0a4f2cab00958ff82b623b058df06966a1 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 21 Sep 2025 11:19:52 -0400 Subject: [PATCH 06/20] React to docker events --- docker/client.go | 126 +++++++++++++++++++++++++++++++++++++++++++++++ main.go | 33 +++++++++++++ 2 files changed, 159 insertions(+) diff --git a/docker/client.go b/docker/client.go index 2a42023..adcc15c 100644 --- a/docker/client.go +++ b/docker/client.go @@ -10,6 +10,7 @@ import ( "time" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" "github.com/fosrl/newt/logger" @@ -321,3 +322,128 @@ func getHostContainer(dockerContext context.Context, dockerClient *client.Client return &hostContainer, nil } + +// EventCallback defines the function signature for handling Docker events +type EventCallback func(containers []Container) + +// EventMonitor handles Docker event monitoring +type EventMonitor struct { + client *client.Client + ctx context.Context + cancel context.CancelFunc + callback EventCallback + socketPath string + enforceNetworkValidation bool +} + +// NewEventMonitor creates a new Docker event monitor +func NewEventMonitor(socketPath string, enforceNetworkValidation bool, callback EventCallback) (*EventMonitor, error) { + if socketPath == "" { + socketPath = "unix:///var/run/docker.sock" + } + + if !strings.Contains(socketPath, "://") { + socketPath = "unix://" + socketPath + } + + cli, err := client.NewClientWithOpts( + client.WithHost(socketPath), + client.WithAPIVersionNegotiation(), + ) + if err != nil { + return nil, fmt.Errorf("failed to create Docker client: %v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + + return &EventMonitor{ + client: cli, + ctx: ctx, + cancel: cancel, + callback: callback, + socketPath: socketPath, + enforceNetworkValidation: enforceNetworkValidation, + }, nil +} + +// Start begins monitoring Docker events +func (em *EventMonitor) Start() error { + logger.Info("Starting Docker event monitoring") + + // Filter for container events we care about + eventFilters := filters.NewArgs() + eventFilters.Add("type", "container") + eventFilters.Add("event", "create") + eventFilters.Add("event", "start") + eventFilters.Add("event", "stop") + eventFilters.Add("event", "destroy") + eventFilters.Add("event", "die") + eventFilters.Add("event", "pause") + eventFilters.Add("event", "unpause") + + // Start listening for events + eventCh, errCh := em.client.Events(em.ctx, events.ListOptions{ + Filters: eventFilters, + }) + + go func() { + defer func() { + if err := em.client.Close(); err != nil { + logger.Error("Error closing Docker client: %v", err) + } + }() + + for { + select { + case event := <-eventCh: + logger.Debug("Docker event received: %s %s for container %s", event.Action, event.Type, event.Actor.ID[:12]) + + // Fetch updated container list and trigger callback + go em.handleEvent(event) + + case err := <-errCh: + if err != nil && err != context.Canceled { + logger.Error("Docker event stream error: %v", err) + // Try to reconnect after a brief delay + time.Sleep(5 * time.Second) + if em.ctx.Err() == nil { + logger.Info("Attempting to reconnect to Docker event stream") + eventCh, errCh = em.client.Events(em.ctx, events.ListOptions{ + Filters: eventFilters, + }) + } + } + return + + case <-em.ctx.Done(): + logger.Info("Docker event monitoring stopped") + return + } + } + }() + + return nil +} + +// handleEvent processes a Docker event and triggers the callback with updated container list +func (em *EventMonitor) handleEvent(event events.Message) { + // Add a small delay to ensure Docker has fully processed the event + time.Sleep(100 * time.Millisecond) + + containers, err := ListContainers(em.socketPath, em.enforceNetworkValidation) + if err != nil { + logger.Error("Failed to list containers after Docker event %s: %v", event.Action, err) + return + } + + logger.Debug("Triggering callback with %d containers after Docker event %s", len(containers), event.Action) + em.callback(containers) +} + +// Stop stops the event monitoring +func (em *EventMonitor) Stop() { + logger.Info("Stopping Docker event monitoring") + if em.cancel != nil { + em.cancel() + } +} diff --git a/main.go b/main.go index 33b55a5..54c4985 100644 --- a/main.go +++ b/main.go @@ -413,6 +413,7 @@ func main() { var pm *proxy.ProxyManager var connected bool var wgData WgData + var dockerEventMonitor *docker.EventMonitor if acceptClients { setupClients(client) @@ -1265,6 +1266,34 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub } defer client.Close() + // Initialize Docker event monitoring if Docker socket is available and monitoring is enabled + if dockerSocket != "" { + logger.Info("Initializing Docker event monitoring") + dockerEventMonitor, err = docker.NewEventMonitor(dockerSocket, dockerEnforceNetworkValidationBool, func(containers []docker.Container) { + // Send updated container list via websocket when Docker events occur + logger.Debug("Docker event detected, sending updated container list (%d containers)", len(containers)) + err := client.SendMessage("newt/socket/containers", map[string]interface{}{ + "containers": containers, + }) + if err != nil { + logger.Error("Failed to send updated container list after Docker event: %v", err) + } else { + logger.Debug("Updated container list sent successfully") + } + }) + + if err != nil { + logger.Error("Failed to create Docker event monitor: %v", err) + } else { + err = dockerEventMonitor.Start() + if err != nil { + logger.Error("Failed to start Docker event monitoring: %v", err) + } else { + logger.Info("Docker event monitoring started successfully") + } + } + } + // Wait for interrupt signal sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) @@ -1273,6 +1302,10 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub // Close clients first (including WGTester) closeClients() + if dockerEventMonitor != nil { + dockerEventMonitor.Stop() + } + if healthMonitor != nil { healthMonitor.Stop() } From f6e7bfe8ea7dec2d1fcfcea9f873442855254328 Mon Sep 17 00:00:00 2001 From: Owen Date: Sun, 21 Sep 2025 11:32:47 -0400 Subject: [PATCH 07/20] Watching socket and quiteting some events --- docker/client.go | 12 ++++++------ main.go | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docker/client.go b/docker/client.go index adcc15c..281c594 100644 --- a/docker/client.go +++ b/docker/client.go @@ -368,18 +368,18 @@ func NewEventMonitor(socketPath string, enforceNetworkValidation bool, callback // Start begins monitoring Docker events func (em *EventMonitor) Start() error { - logger.Info("Starting Docker event monitoring") + logger.Debug("Starting Docker event monitoring") // Filter for container events we care about eventFilters := filters.NewArgs() eventFilters.Add("type", "container") - eventFilters.Add("event", "create") + // eventFilters.Add("event", "create") eventFilters.Add("event", "start") eventFilters.Add("event", "stop") - eventFilters.Add("event", "destroy") - eventFilters.Add("event", "die") - eventFilters.Add("event", "pause") - eventFilters.Add("event", "unpause") + // eventFilters.Add("event", "destroy") + // eventFilters.Add("event", "die") + // eventFilters.Add("event", "pause") + // eventFilters.Add("event", "unpause") // Start listening for events eventCh, errCh := em.client.Events(em.ctx, events.ListOptions{ diff --git a/main.go b/main.go index 54c4985..c080d22 100644 --- a/main.go +++ b/main.go @@ -949,7 +949,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err != nil { logger.Error("Failed to send Docker socket check response: %v", err) } else { - logger.Info("Docker socket check response sent: available=%t", isAvailable) + logger.Debug("Docker socket check response sent: available=%t", isAvailable) } }) @@ -1221,7 +1221,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub } if blueprintResult.Success { - logger.Info("Blueprint applied successfully!") + logger.Debug("Blueprint applied successfully!") } else { logger.Warn("Blueprint application failed: %s", blueprintResult.Message) } @@ -1268,7 +1268,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub // Initialize Docker event monitoring if Docker socket is available and monitoring is enabled if dockerSocket != "" { - logger.Info("Initializing Docker event monitoring") + logger.Debug("Initializing Docker event monitoring") dockerEventMonitor, err = docker.NewEventMonitor(dockerSocket, dockerEnforceNetworkValidationBool, func(containers []docker.Container) { // Send updated container list via websocket when Docker events occur logger.Debug("Docker event detected, sending updated container list (%d containers)", len(containers)) @@ -1289,7 +1289,7 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if err != nil { logger.Error("Failed to start Docker event monitoring: %v", err) } else { - logger.Info("Docker event monitoring started successfully") + logger.Debug("Docker event monitoring started successfully") } } } 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 08/20] 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 09/20] 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 10/20] 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 11/20] 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 12/20] 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 13/20] 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 14/20] 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) From f1c4e1db71acdf9a1782b8af95d6c8e8ec6b07c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:31:05 +0000 Subject: [PATCH 15/20] Bump github.com/docker/docker Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.3.3+incompatible to 28.4.0+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.3.3...v28.4.0) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-version: 28.4.0+incompatible dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 7 +++---- go.sum | 37 ++++++++++++------------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index a18c2d4..2f4b285 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/fosrl/newt go 1.25 require ( - github.com/docker/docker v28.3.3+incompatible + github.com/docker/docker v28.4.0+incompatible github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.3 github.com/vishvananda/netlink v1.3.1 @@ -12,6 +12,7 @@ require ( 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 + gopkg.in/yaml.v3 v3.0.1 gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c software.sslmate.com/src/go-pkcs12 v0.6.0 ) @@ -26,7 +27,6 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/josharian/native v1.1.0 // indirect @@ -51,5 +51,4 @@ 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 +) diff --git a/go.sum b/go.sum index 76b7685..1849112 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= +github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -28,8 +28,6 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -44,8 +42,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5uk github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= @@ -72,6 +72,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -80,8 +82,6 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= @@ -104,48 +104,32 @@ go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9f go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= 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.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= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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.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= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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.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.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= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= @@ -161,6 +145,9 @@ 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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= @@ -168,4 +155,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= \ No newline at end of file +software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= From 8f224e2a45ee7b6653bac89ced554cc949c45df7 Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 29 Sep 2025 12:25:07 -0700 Subject: [PATCH 16/20] Add no cloud option --- README.md | 2 ++ main.go | 24 ++++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 82ff42a..413d353 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ When Newt receives WireGuard control messages, it will use the information encod - `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. +- `no-cloud` (optional): Don't fail over to the cloud when using managed nodes in Pangolin Cloud. Default: false ## Environment Variables @@ -86,6 +87,7 @@ All CLI arguments can be set using environment variables as an alternative to co - `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`) +- `NO_CLOUD`: Don't fail over to the cloud when using managed nodes in Pangolin Cloud. Default: false (equivalent to `--no-cloud`) ## Loading secrets from files diff --git a/main.go b/main.go index fb31cfe..b6ccc94 100644 --- a/main.go +++ b/main.go @@ -121,6 +121,7 @@ var ( healthMonitor *healthcheck.Monitor enforceHealthcheckCert bool blueprintFile string + noCloud bool // New mTLS configuration variables tlsClientCert string @@ -143,15 +144,13 @@ func main() { interfaceName = os.Getenv("INTERFACE") generateAndSaveKeyTo = os.Getenv("GENERATE_AND_SAVE_KEY_TO") keepInterfaceEnv := os.Getenv("KEEP_INTERFACE") - acceptClientsEnv := os.Getenv("ACCEPT_CLIENTS") - useNativeInterfaceEnv := os.Getenv("USE_NATIVE_INTERFACE") - enforceHealthcheckCertEnv := os.Getenv("ENFORCE_HC_CERT") - keepInterface = keepInterfaceEnv == "true" + acceptClientsEnv := os.Getenv("ACCEPT_CLIENTS") acceptClients = acceptClientsEnv == "true" + useNativeInterfaceEnv := os.Getenv("USE_NATIVE_INTERFACE") useNativeInterface = useNativeInterfaceEnv == "true" + enforceHealthcheckCertEnv := os.Getenv("ENFORCE_HC_CERT") enforceHealthcheckCert = enforceHealthcheckCertEnv == "true" - dockerSocket = os.Getenv("DOCKER_SOCKET") pingIntervalStr := os.Getenv("PING_INTERVAL") pingTimeoutStr := os.Getenv("PING_TIMEOUT") @@ -179,6 +178,8 @@ func main() { tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT") } blueprintFile = os.Getenv("BLUEPRINT_FILE") + noCloudEnv := os.Getenv("NO_CLOUD") + noCloud = noCloudEnv == "true" if endpoint == "" { flag.StringVar(&endpoint, "endpoint", "", "Endpoint of your pangolin server") @@ -281,6 +282,9 @@ func main() { if blueprintFile == "" { flag.StringVar(&blueprintFile, "blueprint-file", "", "Path to blueprint file (if unset, no blueprint will be applied)") } + if noCloudEnv == "" { + flag.BoolVar(&noCloud, "no-cloud", false, "Disable cloud failover") + } // do a --version check version := flag.Bool("version", false, "Print the version") @@ -635,7 +639,9 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub } // Request exit nodes from the server - stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{}, 3*time.Second) + stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{ + "noCloud": noCloud, + }, 3*time.Second) logger.Info("Tunnel destroyed, ready for reconnection") }) @@ -1237,8 +1243,10 @@ persistent_keepalive_interval=5`, fixKey(privateKey.String()), fixKey(wgData.Pub if stopFunc != nil { stopFunc() } - // request from the server the list of nodes to ping at newt/ping/request - stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{}, 3*time.Second) + // request from the server the list of nodes to ping + stopFunc = client.SendMessageInterval("newt/ping/request", map[string]interface{}{ + "noCloud": noCloud, + }, 3*time.Second) logger.Debug("Requesting exit nodes from server") clientsOnConnect() } From dda0b414cca49d66a61ebd935d2bf6e360d34fac Mon Sep 17 00:00:00 2001 From: Owen Date: Mon, 29 Sep 2025 14:55:26 -0700 Subject: [PATCH 17/20] Add timeouts to hp --- wg/wg.go | 12 ++++++++++-- wgnetstack/wgnetstack.go | 6 ++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/wg/wg.go b/wg/wg.go index 5a512d6..a14e2c3 100644 --- a/wg/wg.go +++ b/wg/wg.go @@ -952,22 +952,30 @@ func (s *WireGuardService) encryptPayload(payload []byte) (interface{}, error) { } func (s *WireGuardService) keepSendingUDPHolePunch(host string) { + logger.Info("Starting UDP hole punch routine to %s:21820", host) + // send initial hole punch if err := s.sendUDPHolePunch(host + ":21820"); err != nil { - logger.Error("Failed to send initial UDP hole punch: %v", err) + logger.Debug("Failed to send initial UDP hole punch: %v", err) } ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() + timeout := time.NewTimer(15 * time.Second) + defer timeout.Stop() + for { select { case <-s.stopHolepunch: logger.Info("Stopping UDP holepunch") return + case <-timeout.C: + logger.Info("UDP holepunch routine timed out after 15 seconds") + return case <-ticker.C: if err := s.sendUDPHolePunch(host + ":21820"); err != nil { - logger.Error("Failed to send UDP hole punch: %v", err) + logger.Debug("Failed to send UDP hole punch: %v", err) } } } diff --git a/wgnetstack/wgnetstack.go b/wgnetstack/wgnetstack.go index 08d740e..f6708e9 100644 --- a/wgnetstack/wgnetstack.go +++ b/wgnetstack/wgnetstack.go @@ -1076,11 +1076,17 @@ func (s *WireGuardService) keepSendingUDPHolePunch(host string) { ticker := time.NewTicker(3 * time.Second) defer ticker.Stop() + timeout := time.NewTimer(15 * time.Second) + defer timeout.Stop() + for { select { case <-s.stopHolepunch: logger.Info("Stopping UDP holepunch") return + case <-timeout.C: + logger.Info("UDP holepunch routine timed out after 15 seconds") + return case <-ticker.C: if err := s.sendUDPHolePunch(host + ":21820"); err != nil { logger.Debug("Failed to send UDP hole punch: %v", err) From 71c5bf7e65f7fa7ab090a91c25195f4815cd7226 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Mon, 29 Sep 2025 16:38:39 -0700 Subject: [PATCH 18/20] update template --- .../DISCUSSION_TEMPLATE/feature-requests.yml | 47 +++++++++++++++++ .github/ISSUE_TEMPLATE/1.bug_report.yml | 51 +++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 +++ 3 files changed, 106 insertions(+) create mode 100644 .github/DISCUSSION_TEMPLATE/feature-requests.yml create mode 100644 .github/ISSUE_TEMPLATE/1.bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/DISCUSSION_TEMPLATE/feature-requests.yml b/.github/DISCUSSION_TEMPLATE/feature-requests.yml new file mode 100644 index 0000000..03b580c --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/feature-requests.yml @@ -0,0 +1,47 @@ +body: + - type: textarea + attributes: + label: Summary + description: A clear and concise summary of the requested feature. + validations: + required: true + + - type: textarea + attributes: + label: Motivation + description: | + Why is this feature important? + Explain the problem this feature would solve or what use case it would enable. + validations: + required: true + + - type: textarea + attributes: + label: Proposed Solution + description: | + How would you like to see this feature implemented? + Provide as much detail as possible about the desired behavior, configuration, or changes. + validations: + required: true + + - type: textarea + attributes: + label: Alternatives Considered + description: Describe any alternative solutions or workarounds you've thought about. + validations: + required: false + + - type: textarea + attributes: + label: Additional Context + description: Add any other context, mockups, or screenshots about the feature request here. + validations: + required: false + + - type: markdown + attributes: + value: | + Before submitting, please: + - Check if there is an existing issue for this feature. + - Clearly explain the benefit and use case. + - Be as specific as possible to help contributors evaluate and implement. diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 0000000..41dbe7b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,51 @@ +name: Bug Report +description: Create a bug report +labels: [] +body: + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + attributes: + label: Environment + description: Please fill out the relevant details below for your environment. + value: | + - OS Type & Version: (e.g., Ubuntu 22.04) + - Pangolin Version: + - Gerbil Version: + - Traefik Version: + - Newt Version: + - Olm Version: (if applicable) + validations: + required: true + + - type: textarea + attributes: + label: To Reproduce + description: | + Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. + + If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken. + validations: + required: true + + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + + - type: markdown + attributes: + value: | + Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + + - type: markdown + attributes: + value: | + Contributors should be able to follow the steps provided in order to reproduce the bug. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a3739c4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Need help or have questions? + url: https://github.com/orgs/fosrl/discussions + about: Ask questions, get help, and discuss with other community members + - name: Request a Feature + url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests + about: Feature requests should be opened as discussions so others can upvote and comment From 348b8f6b940adaf0b60826567c239b0a44204fd6 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 1 Oct 2025 10:31:14 -0700 Subject: [PATCH 19/20] Try to fix overwriting config file --- websocket/client.go | 1 + websocket/config.go | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/websocket/client.go b/websocket/client.go index 0c0664a..c580f0e 100644 --- a/websocket/client.go +++ b/websocket/client.go @@ -37,6 +37,7 @@ type Client struct { writeMux sync.Mutex clientType string // Type of client (e.g., "newt", "olm") tlsConfig TLSConfig + configNeedsSave bool // Flag to track if config needs to be saved } type ClientOption func(*Client) diff --git a/websocket/config.go b/websocket/config.go index 1d821b6..72c9164 100644 --- a/websocket/config.go +++ b/websocket/config.go @@ -35,15 +35,25 @@ func getConfigPath(clientType string) string { } func (c *Client) loadConfig() error { + originalConfig := *c.config // Store original config to detect changes + configPath := getConfigPath(c.clientType) + if c.config.ID != "" && c.config.Secret != "" && c.config.Endpoint != "" { logger.Debug("Config already provided, skipping loading from file") + // Check if config file exists, if not, we should save it + if _, err := os.Stat(configPath); os.IsNotExist(err) { + logger.Info("Config file does not exist at %s, will create it", configPath) + c.configNeedsSave = true + } return nil } - configPath := getConfigPath(c.clientType) + logger.Info("Loading config from: %s", configPath) data, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { + logger.Info("Config file does not exist at %s, will create it with provided values", configPath) + c.configNeedsSave = true return nil } return err @@ -54,6 +64,12 @@ func (c *Client) loadConfig() error { return err } + // Track what was loaded from file vs provided by CLI + fileHadID := c.config.ID == "" + fileHadSecret := c.config.Secret == "" + fileHadCert := c.config.TlsClientCert == "" + fileHadEndpoint := c.config.Endpoint == "" + if c.config.ID == "" { c.config.ID = config.ID } @@ -68,6 +84,15 @@ func (c *Client) loadConfig() error { c.baseURL = config.Endpoint } + // Check if CLI args provided values that override file values + if (!fileHadID && originalConfig.ID != "") || + (!fileHadSecret && originalConfig.Secret != "") || + (!fileHadCert && originalConfig.TlsClientCert != "") || + (!fileHadEndpoint && originalConfig.Endpoint != "") { + logger.Info("CLI arguments provided, config will be updated") + c.configNeedsSave = true + } + logger.Debug("Loaded config from %s", configPath) logger.Debug("Config: %+v", c.config) @@ -75,10 +100,21 @@ func (c *Client) loadConfig() error { } func (c *Client) saveConfig() error { + if !c.configNeedsSave { + logger.Debug("Config has not changed, skipping save") + return nil + } + configPath := getConfigPath(c.clientType) data, err := json.MarshalIndent(c.config, "", " ") if err != nil { return err } - return os.WriteFile(configPath, data, 0644) + + logger.Info("Saving config to: %s", configPath) + err = os.WriteFile(configPath, data, 0644) + if err == nil { + c.configNeedsSave = false // Reset flag after successful save + } + return err } From f2878884807a868fae3133eef1a15b8d9dcb3870 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 09:23:09 +0000 Subject: [PATCH 20/20] Bump the prod-minor-updates group across 1 directory with 2 updates Bumps the prod-minor-updates group with 2 updates in the / directory: [github.com/docker/docker](https://github.com/docker/docker) and [golang.org/x/net](https://github.com/golang/net). Updates `github.com/docker/docker` from 28.4.0+incompatible to 28.5.0+incompatible - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.4.0...v28.5.0) Updates `golang.org/x/net` from 0.44.0 to 0.45.0 - [Commits](https://github.com/golang/net/compare/v0.44.0...v0.45.0) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-version: 28.5.0+incompatible dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates - dependency-name: golang.org/x/net dependency-version: 0.45.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2f4b285..42fb32d 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/fosrl/newt go 1.25 require ( - github.com/docker/docker v28.4.0+incompatible + github.com/docker/docker v28.5.0+incompatible 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.42.0 golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 - golang.org/x/net v0.44.0 + golang.org/x/net v0.45.0 golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 1849112..f5d9c97 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= -github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.5.0+incompatible h1:ZdSQoRUE9XxhFI/B8YLvhnEFMmYN9Pp8Egd2qcaFk1E= +github.com/docker/docker v28.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -112,8 +112,8 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -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/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=