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:
parent
735dbcf3ab
commit
6311a96710
4 changed files with 254 additions and 6 deletions
|
@ -19,6 +19,8 @@ type Configuration struct {
|
|||
FixedCIDRv6 *net.IPNet
|
||||
EnableIPv6 bool
|
||||
EnableIPTables bool
|
||||
EnableIPMasquerade bool
|
||||
EnableICC bool
|
||||
EnableIPForwarding bool
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
150
libnetwork/drivers/bridge/setup_ip_tables.go
Normal file
150
libnetwork/drivers/bridge/setup_ip_tables.go
Normal 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
|
||||
}
|
102
libnetwork/drivers/bridge/setup_ip_tables_test.go
Normal file
102
libnetwork/drivers/bridge/setup_ip_tables_test.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue