Add implementation and test for SetupIPTables()

- Port and refactor docker/damon/driver ip tables setup function
  into libnetwork.
- Taken care of golint guideline for CI to pass
- Ran one more time goimports for CI to pass...

Signed-off-by: Alessandro Boch <aboch@socketplane.io>
This commit is contained in:
Alessandro Boch 2015-03-05 22:43:14 -08:00
parent 735dbcf3ab
commit 6311a96710
4 changed files with 254 additions and 6 deletions

View file

@ -19,6 +19,8 @@ type Configuration struct {
FixedCIDRv6 *net.IPNet
EnableIPv6 bool
EnableIPTables bool
EnableIPMasquerade bool
EnableICC bool
EnableIPForwarding bool
}

View file

@ -23,9 +23,3 @@ func (b *bridgeSetup) apply() error {
func (b *bridgeSetup) queueStep(step setupStep) {
b.steps = append(b.steps, step)
}
//---------------------------------------------------------------------------//
func setupIPTables(i *bridgeInterface) error {
return nil
}

View file

@ -0,0 +1,150 @@
package bridge
import (
"fmt"
"net"
"github.com/docker/docker/daemon/networkdriver"
"github.com/docker/docker/daemon/networkdriver/portmapper"
"github.com/docker/docker/pkg/iptables"
)
// DockerChain: DOCKER iptable chain name
const (
DockerChain = "DOCKER"
)
func setupIPTables(i *bridgeInterface) error {
// Sanity check.
if i.Config.EnableIPTables == false {
return fmt.Errorf("Unexpected request to set IP tables for interface: %s", i.Config.BridgeName)
}
addrv4, _, err := networkdriver.GetIfaceAddr(i.Config.BridgeName)
if err != nil {
return fmt.Errorf("Failed to setup IP tables, cannot acquire Interface address: %s", err.Error())
}
if err = setupIPTablesInternal(i.Config.BridgeName, addrv4, i.Config.EnableICC, i.Config.EnableIPMasquerade, true); err != nil {
return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
}
_, err = iptables.NewChain(DockerChain, i.Config.BridgeName, iptables.Nat)
if err != nil {
return fmt.Errorf("Failed to create NAT chain: %s", err.Error())
}
chain, err := iptables.NewChain(DockerChain, i.Config.BridgeName, iptables.Filter)
if err != nil {
return fmt.Errorf("Failed to create FILTER chain: %s", err.Error())
}
portmapper.SetIptablesChain(chain)
return nil
}
func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, enable bool) error {
var (
address = addr.String()
natRule = []string{"POSTROUTING", "-t", "nat", "-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
outRule = []string{"FORWARD", "-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}
inRule = []string{"FORWARD", "-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
)
// Set NAT.
if ipmasq {
if err := programChainRule(natRule, "NAT", enable); err != nil {
return err
}
}
// Set Inter Container Communication.
if err := setIcc(bridgeIface, icc, enable); err != nil {
return err
}
// Set Accept on all non-intercontainer outgoing packets.
if err := programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable); err != nil {
return err
}
// Set Accept on incoming packets for existing connections.
if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil {
return err
}
return nil
}
func programChainRule(ruleArgs []string, ruleDescr string, insert bool) error {
var (
prefix []string
operation string
condition bool
)
if insert {
condition = !iptables.Exists(ruleArgs...)
prefix = []string{"-I"}
operation = "enable"
} else {
condition = iptables.Exists(ruleArgs...)
prefix = []string{"-D"}
operation = "disable"
}
if condition {
if output, err := iptables.Raw(append(prefix, ruleArgs...)...); err != nil {
return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
} else if len(output) != 0 {
return &iptables.ChainError{Chain: ruleDescr, Output: output}
}
}
return nil
}
func setIcc(bridgeIface string, iccEnable, insert bool) error {
var (
args = []string{"FORWARD", "-i", bridgeIface, "-o", bridgeIface, "-j"}
acceptArgs = append(args, "ACCEPT")
dropArgs = append(args, "DROP")
)
if insert {
if !iccEnable {
iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
if !iptables.Exists(dropArgs...) {
if output, err := iptables.Raw(append([]string{"-I"}, dropArgs...)...); err != nil {
return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
} else if len(output) != 0 {
return fmt.Errorf("Error disabling intercontainer communication: %s", output)
}
}
} else {
iptables.Raw(append([]string{"-D"}, dropArgs...)...)
if !iptables.Exists(acceptArgs...) {
if output, err := iptables.Raw(append([]string{"-I"}, acceptArgs...)...); err != nil {
return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
} else if len(output) != 0 {
return fmt.Errorf("Error enabling intercontainer communication: %s", output)
}
}
}
} else {
// Remove any ICC rule.
if !iccEnable {
if iptables.Exists(dropArgs...) {
iptables.Raw(append([]string{"-D"}, dropArgs...)...)
}
} else {
if iptables.Exists(acceptArgs...) {
iptables.Raw(append([]string{"-D"}, acceptArgs...)...)
}
}
}
return nil
}

View file

@ -0,0 +1,102 @@
package bridge
import (
"net"
"testing"
"github.com/docker/docker/pkg/iptables"
"github.com/docker/libnetwork"
)
const (
iptablesTestBridgeIP = "192.168.42.1"
)
func TestProgramIPTable(t *testing.T) {
// Create a test bridge with a basic bridge configuration (name + IPv4).
defer libnetwork.SetupTestNetNS(t)()
createTestBridge(getBasicTestConfig(), t)
// Store various iptables chain rules we care for.
rules := []struct {
ruleArgs []string
descr string
}{{[]string{"FORWARD", "-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}, "Test Loopback"},
{[]string{"POSTROUTING", "-t", "nat", "-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}, "NAT Test"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}, "Test ACCEPT NON_ICC OUTGOING"},
{[]string{"FORWARD", "-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}, "Test ACCEPT INCOMING"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}, "Test enable ICC"},
{[]string{"FORWARD", "-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}, "Test disable ICC"},
}
// Assert the chain rules' insertion and removal.
for _, c := range rules {
assertIPTableChainProgramming(c.ruleArgs, c.descr, t)
}
}
func TestSetupIPTables(t *testing.T) {
// Create a test bridge with a basic bridge configuration (name + IPv4).
defer libnetwork.SetupTestNetNS(t)()
br := getBasicTestConfig()
createTestBridge(br, t)
// Modify iptables params in base configuration and apply them.
br.Config.EnableIPTables = true
assertBridgeConfig(br, t)
br.Config.EnableIPMasquerade = true
assertBridgeConfig(br, t)
br.Config.EnableICC = true
assertBridgeConfig(br, t)
br.Config.EnableIPMasquerade = false
assertBridgeConfig(br, t)
}
func getBasicTestConfig() *bridgeInterface {
return &bridgeInterface{
Config: &Configuration{
BridgeName: DefaultBridgeName,
AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
},
}
}
func createTestBridge(br *bridgeInterface, t *testing.T) {
if err := setupDevice(br); err != nil {
t.Fatalf("Failed to create the testing Bridge: %s", err.Error())
}
if err := setupBridgeIPv4(br); err != nil {
t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
}
}
// Assert base function which pushes iptables chain rules on insertion and removal.
func assertIPTableChainProgramming(args []string, descr string, t *testing.T) {
// Add
if err := programChainRule(args, descr, true); err != nil {
t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
}
if iptables.Exists(args...) == false {
t.Fatalf("Failed to effectively program iptable rule: %s", descr)
}
// Remove
if err := programChainRule(args, descr, false); err != nil {
t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
}
if iptables.Exists(args...) == true {
t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
}
}
// Assert function which pushes chains based on bridge config parameters.
func assertBridgeConfig(br *bridgeInterface, t *testing.T) {
// Attempt programming of ip tables.
err := setupIPTables(br)
if err != nil {
t.Fatalf("%v", err)
}
}