GitHub Security Advisory: GHSA-fcmh-qfxc-w685
Package: github.com/cloudnativelabs/kube-router/v2 (GO)
Severity: MODERATE (CVSS 4.1)
CWE: CWE-532: Insertion of Sensitive Information into Log File
Vulnerable versions: >= 2.7.0, <= 2.8.0
Patched in: unpatched

Summary: kube-router: BGP Peer Passwords Exposed in Logs at Verbose Logging Level

Details:
## Summary

When kube-router is configured with per-node BGP peer passwords using the `kube-router.io/peer.passwords` node annotation, and verbose logging is enabled (`--v=2` or higher), the raw Kubernetes node annotation map is logged verbatim — including the base64-encoded BGP MD5 passwords. Anyone with access to kube-router's logs (via `kubectl logs`, log aggregation systems, or shared log dumps during debugging) can extract and decode the BGP peer passwords. The official troubleshooting documentation instructs users to collect logs at `-v=2` before filing issues, making accidental disclosure during support interactions a realistic scenario.

## Details

The vulnerability is at `pkg/controllers/routing/network_routes_controller.go:1129`:

```go
// pkg/controllers/routing/network_routes_controller.go:1127-1133
// If the global routing peer is configured then peer with it
// else attempt to get peers from node specific BGP annotations.
if len(nrc.globalPeerRouters) == 0 {
    klog.V(2).Infof("Attempting to construct peer configs from annotation: %+v", node.Annotations)
    peerCfgs, err := bgpPeerConfigsFromAnnotations(
```

`node.Annotations` is of type `map[string]string`. This type does not implement `fmt.Stringer`, so `%+v` formatting dumps every key-value pair verbatim. When `kube-router.io/peer.passwords` is set on the node (the documented mechanism for providing per-node BGP MD5 passwords), its base64-encoded value appears in the log output.

The BGP peer password annotation is documented in `docs/user-guide.md` and has the constant:

```go
// pkg/controllers/routing/network_routes_controller.go:59
peerPasswordAnnotation = "kube-router.io/peer.passwords"
```

Note that a password-safe `String()` method exists on `PeerConfig` and `PeerConfigs` in `pkg/bgp/peer_config.go` and is tested:

```go
// pkg/bgp/peer_config.go:63-79
// Custom Stringer to prevent leaking passwords when printed
func (p PeerConfig) String() string {
    // ...password field is intentionally omitted...
}
```

However, this protective method is never invoked by the vulnerable log statement, which dumps the raw annotation map before any parsing occurs. The password masking only applies after the annotation is parsed into `PeerConfig` structs.

The second log statement at line 1510 (`klog.Infof("Peer config from %s annotation: %+v", peersAnnotation, peerConfigs)`) is **not vulnerable** — `peerConfigs` is of type `bgp.PeerConfigs` which implements `fmt.Stringer` and correctly masks passwords.

The vulnerable path (`bgpPeerConfigsFromIndividualAnnotations`) is triggered when the `kube-router.io/peers` consolidated YAML annotation is not set — i.e., when operators use the older individual annotation format (`kube-router.io/peer.ips`, `kube-router.io/peer.asns`, `kube-router.io/peer.passwords`). This older format remains fully supported and documented.

## PoC

**Setup**: Node has per-node BGP peer annotations including a password:
```bash
kubectl annotate node worker-1 \
  k