Skip to content

Commit

Permalink
ipsec: Catch-default default drop policy for encryption
Browse files Browse the repository at this point in the history
This commit adds a catch-all XFRM policy for outgoing traffic that has the
encryption bit. The goal here is to catch any traffic that may passthrough
our encryption while we are replacing XFRM policies & states. Those
operations cannot always be performed atomically so we may have brief
moments where there is no XFRM policy to encrypt a subset of traffic. This
policy ensures we drop such traffic and don't let it flow in plain text.

We do need to match on the mark because there is also traffic flowing
through XFRM that we don't want to encrypt (e.g., hostns traffic).

Signed-off-by: Paul Chaignon <paul@cilium.io>
  • Loading branch information
pchaigno authored and joestringer committed Apr 11, 2023
1 parent ddd491b commit 7d44f37
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
56 changes: 56 additions & 0 deletions pkg/datapath/linux/ipsec/ipsec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,27 @@ var (
IP: wildcardIPv6,
Mask: net.CIDRMask(128, 128),
}

defaultDropMark = &netlink.XfrmMark{
Value: linux_defaults.RouteMarkEncrypt,
Mask: linux_defaults.IPsecMarkMaskIn,
}
defaultDropPolicyIPv4 = &netlink.XfrmPolicy{
Dir: netlink.XFRM_DIR_OUT,
Src: wildcardCIDRv4,
Dst: wildcardCIDRv4,
Mark: defaultDropMark,
Action: netlink.XFRM_POLICY_BLOCK,
Priority: 1,
}
defaultDropPolicyIPv6 = &netlink.XfrmPolicy{
Dir: netlink.XFRM_DIR_OUT,
Src: wildcardCIDRv6,
Dst: wildcardCIDRv6,
Mark: defaultDropMark,
Action: netlink.XFRM_POLICY_BLOCK,
Priority: 1,
}
)

func getIPSecKeys(ip net.IP) *ipSecKey {
Expand Down Expand Up @@ -342,6 +363,23 @@ func IpSecReplacePolicyFwd(dst *net.IPNet, tmplDst net.IP) error {
return _ipSecReplacePolicyInFwd(nil, dst, net.IP{}, tmplDst, false, netlink.XFRM_DIR_FWD)
}

// Installs a catch-all policy for outgoing traffic that has the encryption
// bit. The goal here is to catch any traffic that may passthrough our
// encryption while we are replacing XFRM policies & states. Those operations
// cannot always be performed atomically so we may have brief moments where
// there is no XFRM policy to encrypt a subset of traffic. This policy ensures
// we drop such traffic and don't let it flow in plain text.
//
// We do need to match on the mark because there is also traffic flowing
// through XFRM that we don't want to encrypt (e.g., hostns traffic).
func IPsecDefaultDropPolicy(ipv6 bool) error {
defaultDropPolicy := defaultDropPolicyIPv4
if ipv6 {
defaultDropPolicy = defaultDropPolicyIPv6
}
return netlink.XfrmPolicyUpdate(defaultDropPolicy)
}

// ipSecXfrmMarkSetSPI takes a XfrmMark base value, an SPI, returns the mark
// value with the SPI value encoded in it
func ipSecXfrmMarkSetSPI(markValue uint32, spi uint8) uint32 {
Expand Down Expand Up @@ -896,6 +934,10 @@ func deleteStaleXfrmPolicies(reclaimTimestamp time.Time) {
continue
}

if isDefaultDropPolicy(&p) {
continue
}

scopedLog = log.WithField(logfields.OldSPI, policySPI)

scopedLog.Info("Deleting stale XFRM policy")
Expand All @@ -905,6 +947,20 @@ func deleteStaleXfrmPolicies(reclaimTimestamp time.Time) {
}
}

func isDefaultDropPolicy(p *netlink.XfrmPolicy) bool {
return equalDefaultDropPolicy(defaultDropPolicyIPv4, p) ||
equalDefaultDropPolicy(defaultDropPolicyIPv6, p)
}

func equalDefaultDropPolicy(defaultDropPolicy, p *netlink.XfrmPolicy) bool {
return p.Priority == defaultDropPolicy.Priority &&
p.Action == defaultDropPolicy.Action &&
p.Dir == defaultDropPolicy.Dir &&
xfrmMarkEqual(p.Mark, defaultDropPolicy.Mark) &&
p.Src.String() == defaultDropPolicy.Src.String() &&
p.Dst.String() == defaultDropPolicy.Dst.String()
}

func doReclaimStaleKeys() {
ipSecLock.Lock()
defer ipSecLock.Unlock()
Expand Down
7 changes: 7 additions & 0 deletions pkg/datapath/linux/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,9 @@ func (n *linuxNodeHandler) enableIPsec(newNode *nodeTypes.Node) {
if newNode.IsLocal() {
n.replaceNodeIPSecInRoute(new4Net)

err = ipsec.IPsecDefaultDropPolicy(false)
upsertIPsecLog(err, "default-drop IPv4", wildcardCIDR, wildcardCIDR, spi)

if localIP := newNode.GetCiliumInternalIP(false); localIP != nil {
if n.subnetEncryption() {
for _, cidr := range n.nodeConfig.IPv4PodSubnets {
Expand Down Expand Up @@ -998,6 +1001,10 @@ func (n *linuxNodeHandler) enableIPsec(newNode *nodeTypes.Node) {

if newNode.IsLocal() {
n.replaceNodeIPSecInRoute(new6Net)

err = ipsec.IPsecDefaultDropPolicy(true)
upsertIPsecLog(err, "default-drop IPv6", wildcardCIDR, wildcardCIDR, spi)

if localIP := newNode.GetCiliumInternalIP(true); localIP != nil {
if n.subnetEncryption() {
for _, cidr := range n.nodeConfig.IPv6PodSubnets {
Expand Down

0 comments on commit 7d44f37

Please sign in to comment.