
Golangci-lint is now running version 1.52.1. This introduced some errors. Signed-off-by: Marcelo Guerrero Viveros <marguerr@redhat.com>
173 lines
5.7 KiB
Go
173 lines
5.7 KiB
Go
// Copyright 2022 CNI authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// This is a sample chained plugin that supports multiple CNI versions. It
|
|
// parses prevResult according to the cniVersion
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/coreos/go-iptables/iptables"
|
|
|
|
types100 "github.com/containernetworking/cni/pkg/types/100"
|
|
"github.com/containernetworking/plugins/pkg/utils"
|
|
)
|
|
|
|
func setupIngressPolicy(conf *FirewallNetConf, prevResult *types100.Result) error {
|
|
switch conf.IngressPolicy {
|
|
case "", IngressPolicyOpen:
|
|
// NOP
|
|
return nil
|
|
case IngressPolicySameBridge:
|
|
return setupIngressPolicySameBridge(conf, prevResult)
|
|
default:
|
|
return fmt.Errorf("unknown ingress policy: %q", conf.IngressPolicy)
|
|
}
|
|
}
|
|
|
|
func setupIngressPolicySameBridge(conf *FirewallNetConf, prevResult *types100.Result) error {
|
|
if len(prevResult.Interfaces) == 0 {
|
|
return fmt.Errorf("interface needs to be set for ingress policy %q, make sure to chain \"firewall\" plugin with \"bridge\"",
|
|
conf.IngressPolicy)
|
|
}
|
|
intf := prevResult.Interfaces[0]
|
|
if intf == nil {
|
|
return fmt.Errorf("got nil interface")
|
|
}
|
|
bridgeName := intf.Name
|
|
if bridgeName == "" {
|
|
return fmt.Errorf("got empty bridge name")
|
|
}
|
|
for _, iptProto := range findProtos(conf) {
|
|
ipt, err := iptables.NewWithProtocol(iptProto)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := setupIsolationChains(ipt, bridgeName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func teardownIngressPolicy(conf *FirewallNetConf) error {
|
|
switch conf.IngressPolicy {
|
|
case "", IngressPolicyOpen:
|
|
// NOP
|
|
return nil
|
|
case IngressPolicySameBridge:
|
|
// NOP
|
|
//
|
|
// We can't be sure whether conf.bridgeName is still in use by other containers.
|
|
// So we do not remove the iptable rules that are created per bridge.
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unknown ingress policy: %q", conf.IngressPolicy)
|
|
}
|
|
}
|
|
|
|
const (
|
|
filterTableName = "filter" // built-in
|
|
forwardChainName = "FORWARD" // built-in
|
|
)
|
|
|
|
// setupIsolationChains executes the following iptables commands for isolating networks:
|
|
// ```
|
|
// iptables -N CNI-ISOLATION-STAGE-1
|
|
// iptables -N CNI-ISOLATION-STAGE-2
|
|
// # NOTE: "-j CNI-ISOLATION-STAGE-1" needs to be before "CNI-FORWARD" chain. So we use -I here.
|
|
// iptables -I FORWARD -j CNI-ISOLATION-STAGE-1
|
|
// iptables -A CNI-ISOLATION-STAGE-1 -i ${bridgeName} ! -o ${bridgeName} -j CNI-ISOLATION-STAGE-2
|
|
// iptables -A CNI-ISOLATION-STAGE-1 -j RETURN
|
|
// iptables -A CNI-ISOLATION-STAGE-2 -o ${bridgeName} -j DROP
|
|
// iptables -A CNI-ISOLATION-STAGE-2 -j RETURN
|
|
// ```
|
|
func setupIsolationChains(ipt *iptables.IPTables, bridgeName string) error {
|
|
const (
|
|
// Future version may support custom chain names
|
|
stage1Chain = "CNI-ISOLATION-STAGE-1"
|
|
stage2Chain = "CNI-ISOLATION-STAGE-2"
|
|
)
|
|
// Commands:
|
|
// ```
|
|
// iptables -N CNI-ISOLATION-STAGE-1
|
|
// iptables -N CNI-ISOLATION-STAGE-2
|
|
// ```
|
|
for _, chain := range []string{stage1Chain, stage2Chain} {
|
|
if err := utils.EnsureChain(ipt, filterTableName, chain); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Commands:
|
|
// ```
|
|
// iptables -I FORWARD -j CNI-ISOLATION-STAGE-1
|
|
// ```
|
|
jumpToStage1 := withDefaultComment([]string{"-j", stage1Chain})
|
|
// NOTE: "-j CNI-ISOLATION-STAGE-1" needs to be before "CNI-FORWARD" created by CNI firewall plugin.
|
|
// So we specify prepend = true .
|
|
const jumpToStage1Prepend = true
|
|
if err := utils.InsertUnique(ipt, filterTableName, forwardChainName, jumpToStage1Prepend, jumpToStage1); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Commands:
|
|
// ```
|
|
// iptables -A CNI-ISOLATION-STAGE-1 -i ${bridgeName} ! -o ${bridgeName} -j CNI-ISOLATION-STAGE-2
|
|
// iptables -A CNI-ISOLATION-STAGE-1 -j RETURN
|
|
// ```
|
|
stage1Bridge := withDefaultComment(isolationStage1BridgeRule(bridgeName, stage2Chain))
|
|
// prepend = true because this needs to be before "-j RETURN"
|
|
const stage1BridgePrepend = true
|
|
if err := utils.InsertUnique(ipt, filterTableName, stage1Chain, stage1BridgePrepend, stage1Bridge); err != nil {
|
|
return err
|
|
}
|
|
stage1Return := withDefaultComment([]string{"-j", "RETURN"})
|
|
if err := utils.InsertUnique(ipt, filterTableName, stage1Chain, false, stage1Return); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Commands:
|
|
// ```
|
|
// iptables -A CNI-ISOLATION-STAGE-2 -o ${bridgeName} -j DROP
|
|
// iptables -A CNI-ISOLATION-STAGE-2 -j RETURN
|
|
// ```
|
|
stage2Bridge := withDefaultComment(isolationStage2BridgeRule(bridgeName))
|
|
// prepend = true because this needs to be before "-j RETURN"
|
|
const stage2BridgePrepend = true
|
|
if err := utils.InsertUnique(ipt, filterTableName, stage2Chain, stage2BridgePrepend, stage2Bridge); err != nil {
|
|
return err
|
|
}
|
|
stage2Return := withDefaultComment([]string{"-j", "RETURN"})
|
|
return utils.InsertUnique(ipt, filterTableName, stage2Chain, false, stage2Return)
|
|
}
|
|
|
|
func isolationStage1BridgeRule(bridgeName, stage2Chain string) []string {
|
|
return []string{"-i", bridgeName, "!", "-o", bridgeName, "-j", stage2Chain}
|
|
}
|
|
|
|
func isolationStage2BridgeRule(bridgeName string) []string {
|
|
return []string{"-o", bridgeName, "-j", "DROP"}
|
|
}
|
|
|
|
func withDefaultComment(rule []string) []string {
|
|
defaultComment := "CNI firewall plugin rules (ingressPolicy: same-bridge)"
|
|
return withComment(rule, defaultComment)
|
|
}
|
|
|
|
func withComment(rule []string, comment string) []string {
|
|
return append(rule, []string{"-m", "comment", "--comment", comment}...)
|
|
}
|