VYPR
High severityNVD Advisory· Published Mar 27, 2024· Updated Apr 10, 2025

Insecure IPsec transport encryption in Cilium

CVE-2024-28860

Description

Cilium is a networking, observability, and security solution with an eBPF-based dataplane. Users of IPsec transparent encryption in Cilium may be vulnerable to cryptographic attacks that render the transparent encryption ineffective. In particular, Cilium is vulnerable to chosen plaintext, key recovery, replay attacks by a man-in-the-middle attacker. These attacks are possible due to an ESP sequence number collision when multiple nodes are configured with the same key. Fixed versions of Cilium use unique keys for each IPsec tunnel established between nodes, resolving all of the above attacks. This vulnerability is fixed in 1.13.13, 1.14.9, and 1.15.3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/cilium/ciliumGo
>= 1.4.0, < 1.13.141.13.14
github.com/cilium/ciliumGo
>= 1.14.0, < 1.14.91.14.9
github.com/cilium/ciliumGo
>= 1.15.0, < 1.15.31.15.3

Affected products

1

Patches

3
311fbce52804

ipsec: fix per-node-pair-key computation

https://github.com/cilium/ciliumRobin GöggeMar 20, 2024via ghsa
1 file changed · +18 3
  • pkg/datapath/linux/ipsec/ipsec_linux.go+18 3 modified
    @@ -152,14 +152,26 @@ func getGlobalIPsecKey(ip net.IP) *ipSecKey {
     // pre-shared key. The per-node-pair keys are computed with a SHA256 hash of
     // the global key, source node IP, destination node IP appended together.
     func computeNodeIPsecKey(globalKey, srcNodeIP, dstNodeIP, srcBootID, dstBootID []byte) []byte {
    -	input := append(globalKey, srcNodeIP...)
    +	inputLen := len(globalKey) + len(srcNodeIP) + len(dstNodeIP) + len(srcBootID) + len(dstBootID)
    +	input := make([]byte, 0, inputLen)
    +	input = append(input, globalKey...)
    +	input = append(input, srcNodeIP...)
     	input = append(input, dstNodeIP...)
    -	input = append(input, srcBootID...)
    -	input = append(input, dstBootID...)
    +	input = append(input, srcBootID[:36]...)
    +	input = append(input, dstBootID[:36]...)
     	output := sha256.Sum256(input)
     	return output[:len(globalKey)]
     }
     
    +// canonicalIP returns a canonical IPv4 address (4 bytes)
    +// in case we were dealing with a v4 mapped V6 address.
    +func canonicalIP(ip net.IP) net.IP {
    +	if v4 := ip.To4(); v4 != nil {
    +		return v4
    +	}
    +	return ip
    +}
    +
     // deriveNodeIPsecKey builds a per-node-pair ipSecKey object from the global
     // ipSecKey object.
     func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBootID, dstBootID string) *ipSecKey {
    @@ -169,6 +181,9 @@ func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBoo
     		ESN:   globalKey.ESN,
     	}
     
    +	srcNodeIP = canonicalIP(srcNodeIP)
    +	dstNodeIP = canonicalIP(dstNodeIP)
    +
     	if globalKey.Aead != nil {
     		nodeKey.Aead = &netlink.XfrmStateAlgo{
     			Name:   globalKey.Aead.Name,
    
a1742b478306

ipsec: fix per-node-pair-key computation

https://github.com/cilium/ciliumRobin GöggeMar 20, 2024via ghsa
1 file changed · +18 3
  • pkg/datapath/linux/ipsec/ipsec_linux.go+18 3 modified
    @@ -150,14 +150,26 @@ func getGlobalIPsecKey(ip net.IP) *ipSecKey {
     // pre-shared key. The per-node-pair keys are computed with a SHA256 hash of
     // the global key, source node IP, destination node IP appended together.
     func computeNodeIPsecKey(globalKey, srcNodeIP, dstNodeIP, srcBootID, dstBootID []byte) []byte {
    -	input := append(globalKey, srcNodeIP...)
    +	inputLen := len(globalKey) + len(srcNodeIP) + len(dstNodeIP) + len(srcBootID) + len(dstBootID)
    +	input := make([]byte, 0, inputLen)
    +	input = append(input, globalKey...)
    +	input = append(input, srcNodeIP...)
     	input = append(input, dstNodeIP...)
    -	input = append(input, srcBootID...)
    -	input = append(input, dstBootID...)
    +	input = append(input, srcBootID[:36]...)
    +	input = append(input, dstBootID[:36]...)
     	output := sha256.Sum256(input)
     	return output[:len(globalKey)]
     }
     
    +// canonicalIP returns a canonical IPv4 address (4 bytes)
    +// in case we were dealing with a v4 mapped V6 address.
    +func canonicalIP(ip net.IP) net.IP {
    +	if v4 := ip.To4(); v4 != nil {
    +		return v4
    +	}
    +	return ip
    +}
    +
     // deriveNodeIPsecKey builds a per-node-pair ipSecKey object from the global
     // ipSecKey object.
     func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBootID, dstBootID string) *ipSecKey {
    @@ -167,6 +179,9 @@ func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBoo
     		ESN:   globalKey.ESN,
     	}
     
    +	srcNodeIP = canonicalIP(srcNodeIP)
    +	dstNodeIP = canonicalIP(dstNodeIP)
    +
     	if globalKey.Aead != nil {
     		nodeKey.Aead = &netlink.XfrmStateAlgo{
     			Name:   globalKey.Aead.Name,
    
a652c1233318

ipsec: fix per-node-pair-key computation

https://github.com/cilium/ciliumRobin GöggeMar 20, 2024via ghsa
1 file changed · +18 3
  • pkg/datapath/linux/ipsec/ipsec_linux.go+18 3 modified
    @@ -150,14 +150,26 @@ func getGlobalIPsecKey(ip net.IP) *ipSecKey {
     // pre-shared key. The per-node-pair keys are computed with a SHA256 hash of
     // the global key, source node IP, destination node IP appended together.
     func computeNodeIPsecKey(globalKey, srcNodeIP, dstNodeIP, srcBootID, dstBootID []byte) []byte {
    -	input := append(globalKey, srcNodeIP...)
    +	inputLen := len(globalKey) + len(srcNodeIP) + len(dstNodeIP) + len(srcBootID) + len(dstBootID)
    +	input := make([]byte, 0, inputLen)
    +	input = append(input, globalKey...)
    +	input = append(input, srcNodeIP...)
     	input = append(input, dstNodeIP...)
    -	input = append(input, srcBootID...)
    -	input = append(input, dstBootID...)
    +	input = append(input, srcBootID[:36]...)
    +	input = append(input, dstBootID[:36]...)
     	output := sha256.Sum256(input)
     	return output[:len(globalKey)]
     }
     
    +// canonicalIP returns a canonical IPv4 address (4 bytes)
    +// in case we were dealing with a v4 mapped V6 address.
    +func canonicalIP(ip net.IP) net.IP {
    +	if v4 := ip.To4(); v4 != nil {
    +		return v4
    +	}
    +	return ip
    +}
    +
     // deriveNodeIPsecKey builds a per-node-pair ipSecKey object from the global
     // ipSecKey object.
     func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBootID, dstBootID string) *ipSecKey {
    @@ -167,6 +179,9 @@ func deriveNodeIPsecKey(globalKey *ipSecKey, srcNodeIP, dstNodeIP net.IP, srcBoo
     		ESN:   globalKey.ESN,
     	}
     
    +	srcNodeIP = canonicalIP(srcNodeIP)
    +	dstNodeIP = canonicalIP(dstNodeIP)
    +
     	if globalKey.Aead != nil {
     		nodeKey.Aead = &netlink.XfrmStateAlgo{
     			Name:   globalKey.Aead.Name,
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.