CVE-2026-41185
Description
When Calico is configured with the Azure IPAM plugin, the Calico CNI binary mutates the incoming CNI configuration to attach subnet information before delegating to the IPAM plugin. After mutating, the Azure IPAM helper logs the entire unmarshaled configuration map (stdinData) at INFO level to /var/log/calico/cni/cni.log on every CNI ADD and DEL invocation — once per pod scheduled or terminated on the node. When the cluster is deployed using token-based Kubernetes authentication, this log entry contains the ServiceAccount token, client key, and certificate authority in plaintext. Any principal with read access to /var/log/calico/cni/cni.log on a node can read these logs and extract the credentials, which grant cluster-wide Calico networking admin privileges.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Calico CNI with Azure IPAM logs Kubernetes ServiceAccount tokens and credentials in plaintext, allowing local privilege escalation to Calico admin.
Vulnerability
When Calico is configured with the Azure IPAM plugin, the Calico CNI binary mutates the incoming CNI configuration to attach subnet information before delegating to the IPAM plugin. After mutating, the Azure IPAM helper logs the entire unmarshaled configuration map (stdinData) at INFO level to /var/log/calico/cni/cni.log on every CNI ADD and DEL invocation — once per pod scheduled or terminated on the node. When the cluster is deployed using token-based Kubernetes authentication, this log entry contains the ServiceAccount token, client key, and certificate authority in plaintext. Affected versions include all Calico releases prior to the fixes in pull requests #12502, #12527 (release 3.31), and #12526 (release 3.32). [1][2][3]
Exploitation
An attacker needs only read access to /var/log/calico/cni/cni.log on any node in the cluster. No additional authentication or network position is required. The log is written on every pod creation (CNI ADD) and deletion (CNI DEL), so the attacker can repeatedly observe the log file. By parsing the log entries, the attacker can extract the ServiceAccount token, client key, and CA certificate embedded in the configuration map. [1][2]
Impact
Successful extraction of the credentials grants the attacker cluster-wide Calico networking admin privileges. With these credentials, the attacker can manipulate Calico network policies, routing, and potentially disrupt or monitor all cluster network traffic. The compromise is at the Calico admin level, which can affect the entire Kubernetes cluster's network security. [1][2]
Mitigation
The vulnerability is fixed in Calico releases 3.31 and 3.32 via pull requests #12527 and #12526 respectively, which sanitize the log output to remove sensitive credentials. [1][2][3] Users should upgrade to these patched versions. If an immediate upgrade is not possible, restrict read access to /var/log/calico/cni/cni.log to only root or the Calico components. No known listing in CISA's Known Exploited Vulnerabilities (KEV) catalog as of publication date.
- [release 3.31] Sanitize log output in CNI plugin (pick #12502) by Behnam-Shobiri · Pull Request #12527 · projectcalico/calico
- Sanitize log output in CNI plugin by Behnam-Shobiri · Pull Request #12502 · projectcalico/calico
- [release 3.32] Sanitize log output in CNI plugin (pick #12502) by Behnam-Shobiri · Pull Request #12526 · projectcalico/calico
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)
Patches
3da9ea3a13927Merge pull request #12527 from Behnam-Shobiri/fix-logs31
2 files changed · +10 −9
cni-plugin/internal/pkg/azure/azure.go+7 −2 modified@@ -30,7 +30,8 @@ func MutateConfigAdd(args *skel.CmdArgs, network AzureNetwork) error { if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Add: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithField("subnet", network.Subnets[0]).Info("Updated CNI network configuration for Azure Add") return nil } @@ -67,6 +68,10 @@ func MutateConfigDel(args *skel.CmdArgs, network AzureNetwork, endpoint AzureEnd if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Del: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithFields(logrus.Fields{ + "subnet": network.Subnets[0], + "ipAddress": splits[0], + }).Info("Updated CNI network configuration for Azure Del") return nil }
cni-plugin/pkg/install/install.go+3 −7 modified@@ -376,7 +376,8 @@ func writeCNIConfig(c config) { err = isValidJSON(netconf) if err != nil { - logrus.Fatalf("%s is not a valid json object\nerror: %s", netconf, err) + // Don't log the entire config/netconf here because it may contain sensitive information + logrus.Fatalf("CNI config is not valid JSON: %v", err) } // Write out the file. @@ -401,13 +402,8 @@ func writeCNIConfig(c config) { logrus.Fatal(err) } - content, err := os.ReadFile(path) - if err != nil { - logrus.Fatal(err) - } + // Do not log the config file (path) contents here — it may contain sensitive credentials. logrus.Infof("Created %s", winutils.GetHostPath(fmt.Sprintf("/host/etc/cni/net.d/%s", name))) - text := string(content) - fmt.Println(text) // Remove any old config file, if one exists. oldName := getEnv("CNI_OLD_CONF_NAME", "10-calico.conflist")
0ca653db0ae7Merge pull request #12526 from Behnam-Shobiri/fix-logs32
2 files changed · +10 −9
cni-plugin/internal/pkg/azure/azure.go+7 −2 modified@@ -30,7 +30,8 @@ func MutateConfigAdd(args *skel.CmdArgs, network AzureNetwork) error { if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Add: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithField("subnet", network.Subnets[0]).Info("Updated CNI network configuration for Azure Add") return nil } @@ -67,6 +68,10 @@ func MutateConfigDel(args *skel.CmdArgs, network AzureNetwork, endpoint AzureEnd if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Del: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithFields(logrus.Fields{ + "subnet": network.Subnets[0], + "ipAddress": splits[0], + }).Info("Updated CNI network configuration for Azure Del") return nil }
cni-plugin/pkg/install/install.go+3 −7 modified@@ -381,7 +381,8 @@ func writeCNIConfig(c config) { err = isValidJSON(netconf) if err != nil { - logrus.Fatalf("%s is not a valid json object\nerror: %s", netconf, err) + // Don't log the entire config/netconf here because it may contain sensitive information + logrus.Fatalf("CNI config is not valid JSON: %v", err) } // Write out the file. @@ -406,13 +407,8 @@ func writeCNIConfig(c config) { logrus.Fatal(err) } - content, err := os.ReadFile(path) - if err != nil { - logrus.Fatal(err) - } + // Do not log the config file (path) contents here — it may contain sensitive credentials. logrus.Infof("Created %s", winutils.GetHostPath(fmt.Sprintf("/host/etc/cni/net.d/%s", name))) - text := string(content) - fmt.Println(text) // Remove any old config file, if one exists. oldName := getEnv("CNI_OLD_CONF_NAME", "10-calico.conflist")
cd73bc2cea0fMerge pull request #12502 from Behnam-Shobiri/fix-logs
2 files changed · +10 −9
cni-plugin/internal/pkg/azure/azure.go+7 −2 modified@@ -30,7 +30,8 @@ func MutateConfigAdd(args *skel.CmdArgs, network AzureNetwork) error { if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Add: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithField("subnet", network.Subnets[0]).Info("Updated CNI network configuration for Azure Add") return nil } @@ -67,6 +68,10 @@ func MutateConfigDel(args *skel.CmdArgs, network AzureNetwork, endpoint AzureEnd if err != nil { return err } - logrus.Infof("Updated CNI network configuration for Azure Del: %#v", stdinData) + // Don't log the entire stdinData here because it may contain sensitive information + logrus.WithFields(logrus.Fields{ + "subnet": network.Subnets[0], + "ipAddress": splits[0], + }).Info("Updated CNI network configuration for Azure Del") return nil }
cni-plugin/pkg/install/install.go+3 −7 modified@@ -381,7 +381,8 @@ func writeCNIConfig(c config) { err = isValidJSON(netconf) if err != nil { - logrus.Fatalf("%s is not a valid json object\nerror: %s", netconf, err) + // Don't log the entire config/netconf here because it may contain sensitive information + logrus.Fatalf("CNI config is not valid JSON: %v", err) } // Write out the file. @@ -406,13 +407,8 @@ func writeCNIConfig(c config) { logrus.Fatal(err) } - content, err := os.ReadFile(path) - if err != nil { - logrus.Fatal(err) - } + // Do not log the config file (path) contents here — it may contain sensitive credentials. logrus.Infof("Created %s", winutils.GetHostPath(fmt.Sprintf("/host/etc/cni/net.d/%s", name))) - text := string(content) - fmt.Println(text) // Remove any old config file, if one exists. oldName := getEnv("CNI_OLD_CONF_NAME", "10-calico.conflist")
Vulnerability mechanics
Root cause
"The Azure IPAM helper logs the entire unmarshaled CNI configuration map (stdinData) at INFO level, which includes ServiceAccount tokens, client keys, and certificate authority material when token-based Kubernetes authentication is used."
Attack vector
When Calico is configured with the Azure IPAM plugin, the CNI binary mutates the incoming configuration to attach subnet information before delegating to the IPAM plugin. After mutation, the Azure IPAM helper logs the entire unmarshaled configuration map (stdinData) at INFO level to /var/log/calico/cni/cni.log on every CNI ADD and DEL invocation [ref_id=1]. In clusters using token-based Kubernetes authentication, this log entry contains the ServiceAccount token, client key, and certificate authority in plaintext. Any principal with read access to that log file on a node can extract these credentials, which grant cluster-wide Calico networking admin privileges.
Affected code
cni-plugin/internal/pkg/azure/azure.go — the Azure IPAM helper logs the entire stdinData map using %#v formatting [ref_id=1]. cni-plugin/pkg/install/install.go — the install helper outputs full config/netconf content in fatal logs and prints the generated CNI config file contents after writing it [ref_id=1].
What the fix does
The patch in [patch_id=2961927] (backported via [patch_id=2961929]) replaces the %#v dump of stdinData in cni-plugin/internal/pkg/azure/azure.go with structured logs that emit only selected fields, avoiding the full config payload [ref_id=1]. The companion patch [patch_id=2961928] removes full config/netconf output from fatal logs and stops printing config file contents after writing the CNI conflist in cni-plugin/pkg/install/install.go [ref_id=1]. Together these changes prevent sensitive credentials embedded in the CNI configuration from being written to logs.
Preconditions
- configCalico must be configured with the Azure IPAM plugin.
- configThe cluster must use token-based Kubernetes authentication (so the ServiceAccount token, client key, and CA are present in the CNI config).
- authThe attacker must have read access to /var/log/calico/cni/cni.log on a node.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.