-
Notifications
You must be signed in to change notification settings - Fork 797
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
iptables-allow: new plugin which adds a host interface to the filter/…
…FORWARD chain Distros often have additional rules in the filter table that do things like: -A FORWARD -j REJECT --reject-with icmp-host-prohibited docker, for example, gets around this by adding explicit rules to the filter table's FORWARD chain to allow traffic from the docker0 interface. Do that for a given host interface too, as a chained plugin. eg: { "cniVersion": "0.3.1", "name": "bridge-thing", "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": "iptables-allow" } ] }
- Loading branch information
Showing
4 changed files
with
462 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# iptables-allow plugin | ||
|
||
## Overview | ||
|
||
This plugin creates iptables rules to allow traffic from the first host interface provided by a previous plugin. | ||
It does not create any network interfaces and therefore does not bring connectivity by itself. | ||
It is only useful when used in addition to other plugins. | ||
|
||
## Operation | ||
The following network configuration file | ||
``` | ||
{ | ||
"name": "mytuning", | ||
"type": "iptables-allow", | ||
} | ||
``` | ||
will create a new iptables chain in the `filter` table and add rules that allow | ||
the previous plugin's host interface to send/receive traffic. | ||
|
||
A successful result would simply be: | ||
``` | ||
{ } | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
// Copyright 2016 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 "meta-plugin". It reads in its own netconf, it does not create | ||
// any network interface but just changes the network sysctl. | ||
|
||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/coreos/go-iptables/iptables" | ||
|
||
"github.com/containernetworking/cni/pkg/skel" | ||
"github.com/containernetworking/cni/pkg/types" | ||
"github.com/containernetworking/cni/pkg/types/current" | ||
"github.com/containernetworking/cni/pkg/version" | ||
) | ||
|
||
// TuningConf represents the network tuning configuration. | ||
type IptAllowConf struct { | ||
types.NetConf | ||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` | ||
PrevResult *current.Result `json:"-"` | ||
} | ||
|
||
func parseConf(data []byte) (*IptAllowConf, error) { | ||
conf := IptAllowConf{} | ||
if err := json.Unmarshal(data, &conf); err != nil { | ||
return nil, fmt.Errorf("failed to load netconf: %v", err) | ||
} | ||
|
||
// Parse previous result. | ||
if conf.RawPrevResult != nil { | ||
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 | ||
} | ||
|
||
func getPrivChainRules(intf string) [][]string { | ||
var rules [][]string | ||
rules = append(rules, []string{"-i", intf, "!", "-o", intf, "-j", "ACCEPT"}) | ||
rules = append(rules, []string{"-i", intf, "-o", intf, "-j", "ACCEPT"}) | ||
rules = append(rules, []string{"-o", intf, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}) | ||
return rules | ||
} | ||
|
||
func ensureChain(ipt *iptables.IPTables, table, chain string) error { | ||
chains, err := ipt.ListChains(table) | ||
if err != nil { | ||
return fmt.Errorf("failed to list iptables chains: %v", err) | ||
} | ||
for _, ch := range chains { | ||
if ch == chain { | ||
return nil | ||
} | ||
} | ||
|
||
return ipt.NewChain(table, chain) | ||
} | ||
|
||
func addRules(intf string, proto iptables.Protocol) error { | ||
var ( | ||
err error | ||
ipt *iptables.IPTables | ||
exists bool | ||
) | ||
|
||
ipt, err = iptables.NewWithProtocol(proto) | ||
if err != nil { | ||
return fmt.Errorf("failed to initialize iptables helper: %v", err) | ||
} | ||
|
||
adminChainName := fmt.Sprintf("CNI-ADMIN-%s", intf) | ||
privChainName := fmt.Sprintf("CNI-FORWARD-%s", intf) | ||
|
||
var filterRules [][]string | ||
filterRules = append(filterRules, []string{"-m", "comment", "--comment", fmt.Sprintf("CNI interface %s private rules", intf), "-j", privChainName}) | ||
filterRules = append(filterRules, []string{"-i", intf, "!", "-o", intf, "-m", "comment", "--comment", fmt.Sprintf("CNI interface %s administrator overrides", intf), "-j", adminChainName}) | ||
|
||
defer func() { | ||
// Clean up on error | ||
if err != nil { | ||
ipt.DeleteChain("filter", privChainName) | ||
ipt.DeleteChain("filter", adminChainName) | ||
for _, rule := range filterRules { | ||
ipt.Delete("filter", "FORWARD", rule...) | ||
} | ||
} | ||
}() | ||
|
||
if err := ensureChain(ipt, "filter", privChainName); err != nil { | ||
return err | ||
} | ||
if err := ensureChain(ipt, "filter", adminChainName); err != nil { | ||
return err | ||
} | ||
|
||
for _, rule := range filterRules { | ||
exists, err = ipt.Exists("filter", "FORWARD", rule...) | ||
if !exists && err == nil { | ||
err = ipt.Insert("filter", "FORWARD", 1, rule...) | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, rule := range getPrivChainRules(intf) { | ||
if err := ipt.AppendUnique("filter", privChainName, rule...); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func cmdAdd(args *skel.CmdArgs) error { | ||
iptConf, err := parseConf(args.StdinData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var intfName string | ||
if iptConf.PrevResult != nil { | ||
for _, intf := range iptConf.PrevResult.Interfaces { | ||
if intf.Sandbox == "" && intfName == "" { | ||
intfName = intf.Name | ||
break | ||
} | ||
} | ||
} | ||
if intfName == "" { | ||
return fmt.Errorf("failed to find host interface name from PrevResult") | ||
} | ||
|
||
// Figure out whether we need to add rules to iptables or ip6tables or both | ||
protos := []iptables.Protocol{} | ||
for _, addr := range iptConf.PrevResult.IPs { | ||
if addr.Address.IP.To4() != nil { | ||
protos = append(protos, iptables.ProtocolIPv4) | ||
} else { | ||
protos = append(protos, iptables.ProtocolIPv6) | ||
} | ||
} | ||
|
||
for _, proto := range protos { | ||
if err := addRules(intfName, proto); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return types.PrintResult(iptConf.PrevResult, iptConf.CNIVersion) | ||
} | ||
|
||
func cmdDel(args *skel.CmdArgs) error { | ||
// TODO: the settings are not reverted to the previous values. Reverting the | ||
// settings is not useful when the whole container goes away but it could be | ||
// useful in scenarios where plugins are added and removed at runtime. | ||
return nil | ||
} | ||
|
||
func main() { | ||
skel.PluginMain(cmdAdd, cmdDel, version.PluginSupports("0.3.0", version.Current())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Copyright 2017 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. | ||
|
||
package main | ||
|
||
import ( | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
|
||
"testing" | ||
) | ||
|
||
func TestIptablesAllow(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "iptables_allow Suite") | ||
} |
Oops, something went wrong.