From 4822fff22c2d0e287f0bb607f5645cb4dfb82061 Mon Sep 17 00:00:00 2001 From: nileshpatil6 Date: Fri, 21 Aug 2026 11:24:06 +0530 Subject: [PATCH] config: include IsFedramp in Forwarder.Hash Forwarder.Hash hashes five of the struct's six fields. IsFedramp is never mixed into the digest, so two forwarders that differ only in that setting produce an identical hash. The hash is the identity function for live config reload. When the config file changes, AppService.handleConfigUpdate builds a ForwarderService per forwarders entry and calls overwatch AppManager.Add, which does: if currentService.Hash() == service.Hash() { return // the exact same service, no changes, so move along } currentService.Shutdown() ForwarderService.Hash delegates to Forwarder.Hash, so flipping isFedramp on an existing forwarder makes Add early-return: the running listener is never shut down and the new one never starts. The change is silently discarded with no log line until the process restarts. The setting is not cosmetic. Forwarder.IsFedramp reaches carrier.StartOptions in access.StartForwarder and is passed to token.FetchTokenWithRedirect, which selects the FedRAMP or the commercial Access endpoint for the token exchange. IsFedramp was added to the struct in 8825cee, which also rewrote the body of Forwarder.Hash line by line without adding it, while the sibling Hash in that same commit did mix in its bool. --- config/model.go | 1 + config/model_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 config/model_test.go diff --git a/config/model.go b/config/model.go index 85b839fab0c..e56a6e3e83f 100644 --- a/config/model.go +++ b/config/model.go @@ -40,5 +40,6 @@ func (f *Forwarder) Hash() string { _, _ = io.WriteString(h, f.TokenClientID) _, _ = io.WriteString(h, f.TokenSecret) _, _ = io.WriteString(h, f.Destination) + _, _ = io.WriteString(h, fmt.Sprintf("%v", f.IsFedramp)) return fmt.Sprintf("%x", h.Sum(nil)) } diff --git a/config/model_test.go b/config/model_test.go new file mode 100644 index 00000000000..a5f164f1e41 --- /dev/null +++ b/config/model_test.go @@ -0,0 +1,26 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestForwarderHashIncludesIsFedramp ensures that toggling IsFedramp produces a +// different hash. The hash is what overwatch.AppManager uses to decide whether a +// forwarder from an updated config file is the same service as the running one, +// so a collision means the FedRAMP change is silently never applied. +func TestForwarderHashIncludesIsFedramp(t *testing.T) { + commercial := Forwarder{ + URL: "ssh.example.com", + Listener: "127.0.0.1:2222", + TokenClientID: "id", + TokenSecret: "secret", + Destination: "destination", + IsFedramp: false, + } + fedramp := commercial + fedramp.IsFedramp = true + + assert.NotEqual(t, commercial.Hash(), fedramp.Hash(), "changing IsFedramp must change the forwarder hash") +}