Add Check support to firewall meta plugin, test cases
This commit is contained in:
@ -28,6 +28,7 @@ Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
|
||||
* `portmap`: An iptables-based portmapping plugin. Maps ports from the host's address space to the container.
|
||||
* `bandwidth`: Allows bandwidth-limiting through use of traffic control tbf (ingress/egress).
|
||||
* `sbr`: A plugin that configures source based routing for an interface (from which it is chained).
|
||||
* `firewall`: A firewall plugin which uses iptables or firewalld to add rules to allow traffic to/from the container.
|
||||
|
||||
### Sample
|
||||
The sample plugin provides an example for building your own plugin.
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
This plugin creates firewall rules to allow traffic to/from container IP address via the host network .
|
||||
It does not create any network interfaces and therefore does not set up connectivity by itself.
|
||||
It is only useful when used in addition to other plugins.
|
||||
It is intended to be used as a chained plugins.
|
||||
|
||||
## Operation
|
||||
The following network configuration file
|
||||
@ -45,7 +45,91 @@ Available backends include `iptables` and `firewalld` and may be selected with t
|
||||
If no `backend` key is given, the plugin will use firewalld if the service exists on the D-Bus system bus.
|
||||
If no firewalld service is found, it will fall back to iptables.
|
||||
|
||||
## firewalld backend rule structure
|
||||
When the `firewalld` backend is used, this example will place the IPAM allocated address for the container (e.g. 10.88.0.2) into firewalld's `trusted` zone, allowing it to send/receive traffic.
|
||||
|
||||
|
||||
A sample standalone config list (with the file extension .conflist) using firewalld backend might
|
||||
look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "bridge-firewalld",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isGateway": true,
|
||||
"ipMasq": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.88.0.0/16",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "firewall",
|
||||
"backend": "firewalld"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
`FORWARD_IN_ZONES_SOURCE` chain:
|
||||
- `-d 10.88.0.2 -j FWDI_trusted`
|
||||
|
||||
`CNI_FORWARD_OUT_ZONES_SOURCE` chain:
|
||||
- `-s 10.88.0.2 -j FWDO_trusted`
|
||||
|
||||
|
||||
## iptables backend rule structure
|
||||
|
||||
A sample standalone config list (with the file extension .conflist) using iptables backend might
|
||||
look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "bridge-firewalld",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isGateway": true,
|
||||
"ipMasq": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.88.0.0/16",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "firewall",
|
||||
"backend": "iptables"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When the `iptables` backend is used, the above example will create two new iptables chains in the `filter` table and add rules that allow the given interface to send/receive traffic.
|
||||
When the `firewalld` backend is used, the above example will place the `cni0` interface into firewalld's `trusted` zone, allowing it to send/receive traffic.
|
||||
|
||||
### FORWARD
|
||||
A new chain, CNI-FORWARD is added to the FORWARD chain. CNI-FORWARD is the chain where rules will be added
|
||||
when containers are created and from where rules will be removed when containers terminate.
|
||||
|
||||
`FORWARD` chain:
|
||||
- `-j CNI-FORWARD`
|
||||
|
||||
CNI-FORWARD will have a pair of rules added, one for each direction, using the IPAM assigned IP address
|
||||
of the container as shown:
|
||||
|
||||
`CNI_FORWARD` chain:
|
||||
- `-s 10.88.0.2 -m conntrack --ctstate RELATED,ESTABLISHED -j CNI-FORWARD`
|
||||
- `-d 10.88.0.2 -j CNI-FORWARD`
|
||||
|
||||
|
@ -45,14 +45,12 @@ type FirewallNetConf struct {
|
||||
// the firewalld backend is used but the zone is not given, it defaults
|
||||
// to 'trusted'
|
||||
FirewalldZone string `json:"firewalldZone,omitempty"`
|
||||
|
||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||
PrevResult *current.Result `json:"-"`
|
||||
}
|
||||
|
||||
type FirewallBackend interface {
|
||||
Add(*FirewallNetConf) error
|
||||
Del(*FirewallNetConf) error
|
||||
Add(*FirewallNetConf, *current.Result) error
|
||||
Del(*FirewallNetConf, *current.Result) error
|
||||
Check(*FirewallNetConf, *current.Result) error
|
||||
}
|
||||
|
||||
func ipString(ip net.IPNet) string {
|
||||
@ -62,10 +60,27 @@ func ipString(ip net.IPNet) string {
|
||||
return ip.IP.String() + "/32"
|
||||
}
|
||||
|
||||
func parseConf(data []byte) (*FirewallNetConf, error) {
|
||||
func parseConf(data []byte) (*FirewallNetConf, *current.Result, error) {
|
||||
conf := FirewallNetConf{}
|
||||
if err := json.Unmarshal(data, &conf); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
return nil, nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
if conf.RawPrevResult == nil {
|
||||
return nil, nil, fmt.Errorf("missing prevResult from earlier plugin")
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
var result *current.Result
|
||||
var err error
|
||||
if err = version.ParsePrevResult(&conf.NetConf); err != nil {
|
||||
return nil, nil, fmt.Errorf("could not parse prevResult: %v", err)
|
||||
}
|
||||
|
||||
result, err = current.NewResultFromResult(conf.PrevResult)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not convert result to current version: %v", err)
|
||||
}
|
||||
|
||||
// Default the firewalld zone to trusted
|
||||
@ -73,26 +88,7 @@ func parseConf(data []byte) (*FirewallNetConf, error) {
|
||||
conf.FirewalldZone = "trusted"
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
if conf.RawPrevResult == nil {
|
||||
return nil, fmt.Errorf("missing prevResult from earlier plugin")
|
||||
}
|
||||
|
||||
resultBytes, err := json.Marshal(conf.RawPrevResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not serialize prevResult: %v", err)
|
||||
}
|
||||
res, err := version.NewResult(conf.CNIVersion, resultBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse prevResult: %v", err)
|
||||
}
|
||||
conf.RawPrevResult = nil
|
||||
conf.PrevResult, err = current.NewResultFromResult(res)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert result to current version: %v", err)
|
||||
}
|
||||
|
||||
return &conf, nil
|
||||
return &conf, result, nil
|
||||
}
|
||||
|
||||
func getBackend(conf *FirewallNetConf) (FirewallBackend, error) {
|
||||
@ -113,7 +109,7 @@ func getBackend(conf *FirewallNetConf) (FirewallBackend, error) {
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
conf, err := parseConf(args.StdinData)
|
||||
conf, result, err := parseConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -123,11 +119,10 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := backend.Add(conf); err != nil {
|
||||
if err := backend.Add(conf, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := conf.PrevResult
|
||||
if result == nil {
|
||||
result = ¤t.Result{}
|
||||
}
|
||||
@ -135,7 +130,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
conf, err := parseConf(args.StdinData)
|
||||
conf, result, err := parseConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -152,7 +147,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
// Runtime errors are ignored
|
||||
if err := backend.Del(conf); err != nil {
|
||||
if err := backend.Del(conf, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -160,5 +155,28 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.All)
|
||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.PluginSupports("0.4.0"), "TODO")
|
||||
}
|
||||
|
||||
func cmdCheck(args *skel.CmdArgs) error {
|
||||
conf, result, err := parseConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure we have previous result.
|
||||
if result == nil {
|
||||
return fmt.Errorf("Required prevResult missing")
|
||||
}
|
||||
|
||||
backend, err := getBackend(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := backend.Check(conf, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -81,6 +81,16 @@ func (f *fakeFirewalld) RemoveSource(zone, source string) (string, *dbus.Error)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (f *fakeFirewalld) QuerySource(zone, source string) (bool, *dbus.Error) {
|
||||
if f.zone != zone {
|
||||
return false, nil
|
||||
}
|
||||
if f.source != source {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func spawnSessionDbus(wg *sync.WaitGroup) (string, *exec.Cmd) {
|
||||
// Start a private D-Bus session bus
|
||||
path, err := invoke.FindInPath("dbus-daemon", []string{
|
||||
@ -150,6 +160,7 @@ var _ = Describe("firewalld test", func() {
|
||||
// Go public methods to the D-Bus name
|
||||
methods := map[string]string{
|
||||
"AddSource": firewalldAddSourceMethod,
|
||||
"QuerySource": firewalldQuerySourceMethod,
|
||||
"RemoveSource": firewalldRemoveSourceMethod,
|
||||
}
|
||||
conn.ExportWithMap(fwd, methods, firewalldPath, firewalldZoneInterface)
|
||||
@ -178,7 +189,7 @@ var _ = Describe("firewalld test", func() {
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
_, _, err := testutils.CmdAddWithResult(targetNs.Path(), ifname, []byte(conf), func() error {
|
||||
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -186,7 +197,7 @@ var _ = Describe("firewalld test", func() {
|
||||
Expect(fwd.source).To(Equal("10.0.0.2/32"))
|
||||
fwd.clear()
|
||||
|
||||
err = testutils.CmdDelWithResult(targetNs.Path(), ifname, func() error {
|
||||
err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -224,7 +235,7 @@ var _ = Describe("firewalld test", func() {
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
_, _, err := testutils.CmdAddWithResult(targetNs.Path(), ifname, []byte(conf), func() error {
|
||||
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -262,7 +273,7 @@ var _ = Describe("firewalld test", func() {
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
r, _, err := testutils.CmdAddWithResult(targetNs.Path(), ifname, []byte(conf), func() error {
|
||||
r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -275,4 +286,58 @@ var _ = Describe("firewalld test", func() {
|
||||
Expect(len(result.IPs)).To(Equal(1))
|
||||
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
|
||||
})
|
||||
|
||||
It("works with a 0.4.0 config, including Check", func() {
|
||||
Expect(isFirewalldRunning()).To(BeTrue())
|
||||
|
||||
conf := `{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "firewalld-test",
|
||||
"type": "firewall",
|
||||
"backend": "firewalld",
|
||||
"zone": "trusted",
|
||||
"prevResult": {
|
||||
"cniVersion": "0.4.0",
|
||||
"interfaces": [
|
||||
{"name": "eth0", "sandbox": "/foobar"}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "10.0.0.2/24",
|
||||
"gateway": "10.0.0.1",
|
||||
"interface": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(fwd.zone).To(Equal("trusted"))
|
||||
Expect(fwd.source).To(Equal("10.0.0.2/32"))
|
||||
|
||||
_, err = current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = testutils.CmdCheckWithArgs(args, func() error {
|
||||
return cmdCheck(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(fwd.zone).To(Equal("trusted"))
|
||||
Expect(fwd.source).To(Equal("10.0.0.2/32"))
|
||||
})
|
||||
})
|
||||
|
@ -235,7 +235,7 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
r, _, err := testutils.CmdAddWithResult(targetNS.Path(), IFNAME, fullConf, func() error {
|
||||
r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -264,7 +264,7 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAddWithResult(targetNS.Path(), IFNAME, fullConf, func() error {
|
||||
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -312,7 +312,7 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAddWithResult(targetNS.Path(), IFNAME, conf, func() error {
|
||||
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, conf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@ -350,13 +350,157 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAddWithResult(targetNS.Path(), IFNAME, fullConf, func() error {
|
||||
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
validateFullRuleset(fullConf)
|
||||
|
||||
err = testutils.CmdDelWithResult(targetNS.Path(), IFNAME, func() error {
|
||||
err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
validateCleanedUp(fullConf)
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("installs the right iptables rules on the host v4.0.x and check is successful", func() {
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNS.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: fullConf,
|
||||
}
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
validateFullRuleset(fullConf)
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("cleans up on delete v4.0.x", func() {
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNS.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: fullConf,
|
||||
}
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
validateFullRuleset(fullConf)
|
||||
|
||||
err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
validateCleanedUp(fullConf)
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("firewall plugin iptables backend v0.4.x", func() {
|
||||
var originalNS, targetNS ns.NetNS
|
||||
const IFNAME string = "dummy0"
|
||||
|
||||
fullConf := []byte(`{
|
||||
"name": "test",
|
||||
"type": "firewall",
|
||||
"backend": "iptables",
|
||||
"ifName": "dummy0",
|
||||
"cniVersion": "0.4.0",
|
||||
"prevResult": {
|
||||
"interfaces": [
|
||||
{"name": "dummy0"}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "10.0.0.2/24",
|
||||
"interface": 0
|
||||
},
|
||||
{
|
||||
"version": "6",
|
||||
"address": "2001:db8:1:2::1/64",
|
||||
"interface": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}`)
|
||||
|
||||
BeforeEach(func() {
|
||||
// Create a new NetNS so we don't modify the host
|
||||
var err error
|
||||
originalNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = netlink.LinkAdd(&netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: IFNAME,
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
targetNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(targetNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("installs iptables rules, Check rules then cleans up on delete using v4.0.x", func() {
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNS.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: fullConf,
|
||||
}
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = testutils.CmdCheckWithArgs(args, func() error {
|
||||
return cmdCheck(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
validateFullRuleset(fullConf)
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
@ -31,6 +32,7 @@ const (
|
||||
firewalldZoneInterface = "org.fedoraproject.FirewallD1.zone"
|
||||
firewalldAddSourceMethod = "addSource"
|
||||
firewalldRemoveSourceMethod = "removeSource"
|
||||
firewalldQuerySourceMethod = "querySource"
|
||||
|
||||
errZoneAlreadySet = "ZONE_ALREADY_SET"
|
||||
)
|
||||
@ -80,8 +82,8 @@ func newFirewalldBackend(conf *FirewallNetConf) (FirewallBackend, error) {
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
func (fb *fwdBackend) Add(conf *FirewallNetConf) error {
|
||||
for _, ip := range conf.PrevResult.IPs {
|
||||
func (fb *fwdBackend) Add(conf *FirewallNetConf, result *current.Result) error {
|
||||
for _, ip := range result.IPs {
|
||||
ipStr := ipString(ip.Address)
|
||||
// Add a firewalld rule which assigns the given source IP to the given zone
|
||||
firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
|
||||
@ -95,8 +97,8 @@ func (fb *fwdBackend) Add(conf *FirewallNetConf) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fb *fwdBackend) Del(conf *FirewallNetConf) error {
|
||||
for _, ip := range conf.PrevResult.IPs {
|
||||
func (fb *fwdBackend) Del(conf *FirewallNetConf, result *current.Result) error {
|
||||
for _, ip := range result.IPs {
|
||||
ipStr := ipString(ip.Address)
|
||||
// Remove firewalld rules which assigned the given source IP to the given zone
|
||||
firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
|
||||
@ -105,3 +107,16 @@ func (fb *fwdBackend) Del(conf *FirewallNetConf) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fb *fwdBackend) Check(conf *FirewallNetConf, result *current.Result) error {
|
||||
for _, ip := range result.IPs {
|
||||
ipStr := ipString(ip.Address)
|
||||
// Check for a firewalld rule for the given source IP to the given zone
|
||||
firewalldObj := fb.conn.Object(firewalldName, firewalldPath)
|
||||
var res bool
|
||||
if err := firewalldObj.Call(firewalldZoneInterface+"."+firewalldQuerySourceMethod, 0, conf.FirewalldZone, ipStr).Store(&res); err != nil {
|
||||
return fmt.Errorf("failed to find the address %v in %v zone", ipStr, conf.FirewalldZone)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
)
|
||||
|
||||
@ -99,9 +100,9 @@ func protoForIP(ip net.IPNet) iptables.Protocol {
|
||||
return iptables.ProtocolIPv6
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) addRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error {
|
||||
func (ib *iptablesBackend) addRules(conf *FirewallNetConf, result *current.Result, ipt *iptables.IPTables, proto iptables.Protocol) error {
|
||||
rules := make([][]string, 0)
|
||||
for _, ip := range conf.PrevResult.IPs {
|
||||
for _, ip := range result.IPs {
|
||||
if protoForIP(ip.Address) == proto {
|
||||
rules = append(rules, getPrivChainRules(ipString(ip.Address))...)
|
||||
}
|
||||
@ -131,9 +132,9 @@ func (ib *iptablesBackend) addRules(conf *FirewallNetConf, ipt *iptables.IPTable
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) delRules(conf *FirewallNetConf, ipt *iptables.IPTables, proto iptables.Protocol) error {
|
||||
func (ib *iptablesBackend) delRules(conf *FirewallNetConf, result *current.Result, ipt *iptables.IPTables, proto iptables.Protocol) error {
|
||||
rules := make([][]string, 0)
|
||||
for _, ip := range conf.PrevResult.IPs {
|
||||
for _, ip := range result.IPs {
|
||||
if protoForIP(ip.Address) == proto {
|
||||
rules = append(rules, getPrivChainRules(ipString(ip.Address))...)
|
||||
}
|
||||
@ -146,13 +147,69 @@ func (ib *iptablesBackend) delRules(conf *FirewallNetConf, ipt *iptables.IPTable
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) checkRules(conf *FirewallNetConf, result *current.Result, ipt *iptables.IPTables, proto iptables.Protocol) error {
|
||||
rules := make([][]string, 0)
|
||||
for _, ip := range result.IPs {
|
||||
if protoForIP(ip.Address) == proto {
|
||||
rules = append(rules, getPrivChainRules(ipString(ip.Address))...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rules) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure our private chains exist
|
||||
if err := ensureChain(ipt, "filter", ib.privChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure our filter rule exists in the forward chain
|
||||
privRule := generateFilterRule(ib.privChainName)
|
||||
privExists, err := ipt.Exists("filter", "FORWARD", privRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !privExists {
|
||||
return fmt.Errorf("expected %v rule %v not found", "FORWARD", privRule)
|
||||
}
|
||||
|
||||
// Ensure our admin override chain rule exists in our private chain
|
||||
adminRule := generateFilterRule(ib.adminChainName)
|
||||
adminExists, err := ipt.Exists("filter", ib.privChainName, adminRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !adminExists {
|
||||
return fmt.Errorf("expected %v rule %v not found", ib.privChainName, adminRule)
|
||||
}
|
||||
|
||||
// ensure rules for this IP address exist
|
||||
for _, rule := range rules {
|
||||
// Ensure our rule exists in our private chain
|
||||
exists, err := ipt.Exists("filter", ib.privChainName, rule...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("expected rule %v not found", rule)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findProtos(conf *FirewallNetConf) []iptables.Protocol {
|
||||
protos := []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6}
|
||||
if conf.PrevResult != nil {
|
||||
// If PrevResult is given, scan all IP addresses to figure out
|
||||
// which IP versions to use
|
||||
protos = []iptables.Protocol{}
|
||||
for _, addr := range conf.PrevResult.IPs {
|
||||
result, _ := current.NewResultFromResult(conf.PrevResult)
|
||||
for _, addr := range result.IPs {
|
||||
if addr.Address.IP.To4() != nil {
|
||||
protos = append(protos, iptables.ProtocolIPv4)
|
||||
} else {
|
||||
@ -196,18 +253,27 @@ func newIptablesBackend(conf *FirewallNetConf) (FirewallBackend, error) {
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) Add(conf *FirewallNetConf) error {
|
||||
func (ib *iptablesBackend) Add(conf *FirewallNetConf, result *current.Result) error {
|
||||
for proto, ipt := range ib.protos {
|
||||
if err := ib.addRules(conf, ipt, proto); err != nil {
|
||||
if err := ib.addRules(conf, result, ipt, proto); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) Del(conf *FirewallNetConf) error {
|
||||
func (ib *iptablesBackend) Del(conf *FirewallNetConf, result *current.Result) error {
|
||||
for proto, ipt := range ib.protos {
|
||||
ib.delRules(conf, ipt, proto)
|
||||
ib.delRules(conf, result, ipt, proto)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ib *iptablesBackend) Check(conf *FirewallNetConf, result *current.Result) error {
|
||||
for proto, ipt := range ib.protos {
|
||||
if err := ib.checkRules(conf, result, ipt, proto); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user