bridge: Add macspoofchk support

The new macspoofchk field is added to the bridge plugin to support
anti-mac-spoofing.
When the parameter is enabled, traffic is limited to the mac addresses
of the container interface (the veth peer that is placed in the
container ns).
Any traffic that exits the pod is checked against the source mac address
that is expected. If the mac address is different, the frames are
dropped.

The implementation is using nftables and should only be used on nodes
that support it.

Signed-off-by: Edward Haas <edwardh@redhat.com>
This commit is contained in:
Edward Haas
2021-06-15 21:12:57 +03:00
parent 8632ace977
commit 081ed44a1d
24 changed files with 2132 additions and 14 deletions

View File

@ -23,6 +23,7 @@ import (
"strings"
"github.com/coreos/go-iptables/iptables"
"github.com/networkplumbing/go-nft/nft"
"github.com/vishvananda/netlink/nl"
"github.com/containernetworking/cni/pkg/skel"
@ -65,19 +66,20 @@ type Net struct {
// testCase defines the CNI network configuration and the expected
// bridge addresses for a test case.
type testCase struct {
cniVersion string // CNI Version
subnet string // Single subnet config: Subnet CIDR
gateway string // Single subnet config: Gateway
ranges []rangeInfo // Ranges list (multiple subnets config)
isGW bool
isLayer2 bool
expGWCIDRs []string // Expected gateway addresses in CIDR form
vlan int
ipMasq bool
AddErr020 string
DelErr020 string
AddErr010 string
DelErr010 string
cniVersion string // CNI Version
subnet string // Single subnet config: Subnet CIDR
gateway string // Single subnet config: Gateway
ranges []rangeInfo // Ranges list (multiple subnets config)
isGW bool
isLayer2 bool
expGWCIDRs []string // Expected gateway addresses in CIDR form
vlan int
ipMasq bool
macspoofchk bool
AddErr020 string
DelErr020 string
AddErr010 string
DelErr010 string
envArgs string // CNI_ARGS
runtimeConfig struct {
@ -164,6 +166,9 @@ const (
ipamEndStr = `
}`
macspoofchkFormat = `,
"macspoofchk": %t`
argsFormat = `,
"args": {
"cni": {
@ -193,6 +198,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
if tc.runtimeConfig.mac != "" {
conf += fmt.Sprintf(runtimeConfig, tc.runtimeConfig.mac)
}
if tc.macspoofchk {
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
}
if !tc.isLayer2 {
conf += netDefault
@ -2175,6 +2183,35 @@ var _ = Describe("bridge Operations", func() {
})
}
}
It(fmt.Sprintf("[%s] configures mac spoof-check (no mac spoofing)", ver), func() {
Expect(originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
tc := testCase{
cniVersion: ver,
subnet: "10.1.2.0/24",
macspoofchk: true,
}
args := tc.createCmdArgs(originalNS, dataDir)
_, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
assertMacSpoofCheckRulesExist()
Expect(testutils.CmdDelWithArgs(args, func() error {
if err := cmdDel(args); err != nil {
return err
}
assertMacSpoofCheckRulesMissing()
return nil
})).To(Succeed())
return nil
})).To(Succeed())
})
}
It("check vlan id when loading net conf", func() {
@ -2211,3 +2248,50 @@ var _ = Describe("bridge Operations", func() {
}
})
})
func assertMacSpoofCheckRulesExist() {
assertMacSpoofCheckRules(
func(actual interface{}, expectedLen int) {
ExpectWithOffset(3, actual).To(HaveLen(expectedLen))
})
}
func assertMacSpoofCheckRulesMissing() {
assertMacSpoofCheckRules(
func(actual interface{}, _ int) {
ExpectWithOffset(3, actual).To(BeEmpty())
})
}
func assertMacSpoofCheckRules(assert func(actual interface{}, expectedLen int)) {
c, err := nft.ReadConfig()
ExpectWithOffset(2, err).NotTo(HaveOccurred())
expectedTable := nft.NewTable("nat", "bridge")
filter := nft.TypeFilter
hook := nft.HookPreRouting
prio := -300
policy := nft.PolicyAccept
expectedBaseChain := nft.NewChain(expectedTable, "PREROUTING", &filter, &hook, &prio, &policy)
assert(c.LookupRule(nft.NewRule(
expectedTable,
expectedBaseChain,
nil, nil, nil,
"macspoofchk-dummy-0-eth0",
)), 1)
assert(c.LookupRule(nft.NewRule(
expectedTable,
nft.NewRegularChain(expectedTable, "cni-br-iface-dummy-0-eth0"),
nil, nil, nil,
"macspoofchk-dummy-0-eth0",
)), 1)
assert(c.LookupRule(nft.NewRule(
expectedTable,
nft.NewRegularChain(expectedTable, "cni-br-iface-dummy-0-eth0-mac"),
nil, nil, nil,
"macspoofchk-dummy-0-eth0",
)), 2)
}