mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Brought in iptables package into libnetwork.
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
parent
25102c6e4d
commit
9714bcac87
10 changed files with 772 additions and 6 deletions
|
@ -7,8 +7,8 @@ import (
|
|||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
"github.com/docker/libnetwork/pkg/netlabel"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
)
|
||||
|
||||
type link struct {
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
)
|
||||
|
||||
// DockerChain: DOCKER iptable chain name
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
169
libnetwork/pkg/iptables/firewalld.go
Normal file
169
libnetwork/pkg/iptables/firewalld.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
// IPV defines the table string
|
||||
type IPV string
|
||||
|
||||
const (
|
||||
// Iptables point ipv4 table
|
||||
Iptables IPV = "ipv4"
|
||||
// IP6tables point to ipv6 table
|
||||
IP6tables IPV = "ipv6"
|
||||
// Ebtables point to bridge table
|
||||
Ebtables IPV = "eb"
|
||||
)
|
||||
const (
|
||||
dbusInterface = "org.fedoraproject.FirewallD1"
|
||||
dbusPath = "/org/fedoraproject/FirewallD1"
|
||||
)
|
||||
|
||||
// Conn is a connection to firewalld dbus endpoint.
|
||||
type Conn struct {
|
||||
sysconn *dbus.Conn
|
||||
sysobj *dbus.Object
|
||||
signal chan *dbus.Signal
|
||||
}
|
||||
|
||||
var (
|
||||
connection *Conn
|
||||
firewalldRunning bool // is Firewalld service running
|
||||
onReloaded []*func() // callbacks when Firewalld has been reloaded
|
||||
)
|
||||
|
||||
// FirewalldInit initializes firewalld management code.
|
||||
func FirewalldInit() {
|
||||
var err error
|
||||
|
||||
connection, err = newConnection()
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to connect to D-Bus system bus: %s", err)
|
||||
}
|
||||
if connection != nil {
|
||||
go signalHandler()
|
||||
}
|
||||
|
||||
firewalldRunning = checkRunning()
|
||||
}
|
||||
|
||||
// New() establishes a connection to the system bus.
|
||||
func newConnection() (*Conn, error) {
|
||||
c := new(Conn)
|
||||
if err := c.initConnection(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Innitialize D-Bus connection.
|
||||
func (c *Conn) initConnection() error {
|
||||
var err error
|
||||
|
||||
c.sysconn, err = dbus.SystemBus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This never fails, even if the service is not running atm.
|
||||
c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))
|
||||
|
||||
rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
|
||||
dbusPath, dbusInterface, dbusInterface)
|
||||
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
|
||||
|
||||
rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
|
||||
dbusInterface)
|
||||
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
|
||||
|
||||
c.signal = make(chan *dbus.Signal, 10)
|
||||
c.sysconn.Signal(c.signal)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func signalHandler() {
|
||||
for signal := range connection.signal {
|
||||
if strings.Contains(signal.Name, "NameOwnerChanged") {
|
||||
firewalldRunning = checkRunning()
|
||||
dbusConnectionChanged(signal.Body)
|
||||
} else if strings.Contains(signal.Name, "Reloaded") {
|
||||
reloaded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dbusConnectionChanged(args []interface{}) {
|
||||
name := args[0].(string)
|
||||
oldOwner := args[1].(string)
|
||||
newOwner := args[2].(string)
|
||||
|
||||
if name != dbusInterface {
|
||||
return
|
||||
}
|
||||
|
||||
if len(newOwner) > 0 {
|
||||
connectionEstablished()
|
||||
} else if len(oldOwner) > 0 {
|
||||
connectionLost()
|
||||
}
|
||||
}
|
||||
|
||||
func connectionEstablished() {
|
||||
reloaded()
|
||||
}
|
||||
|
||||
func connectionLost() {
|
||||
// Doesn't do anything for now. Libvirt also doesn't react to this.
|
||||
}
|
||||
|
||||
// call all callbacks
|
||||
func reloaded() {
|
||||
for _, pf := range onReloaded {
|
||||
(*pf)()
|
||||
}
|
||||
}
|
||||
|
||||
// OnReloaded add callback
|
||||
func OnReloaded(callback func()) {
|
||||
for _, pf := range onReloaded {
|
||||
if pf == &callback {
|
||||
return
|
||||
}
|
||||
}
|
||||
onReloaded = append(onReloaded, &callback)
|
||||
}
|
||||
|
||||
// Call some remote method to see whether the service is actually running.
|
||||
func checkRunning() bool {
|
||||
var zone string
|
||||
var err error
|
||||
|
||||
if connection != nil {
|
||||
err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
|
||||
logrus.Infof("Firewalld running: %t", err == nil)
|
||||
return err == nil
|
||||
}
|
||||
logrus.Info("Firewalld not running")
|
||||
return false
|
||||
}
|
||||
|
||||
// Passthrough method simply passes args through to iptables/ip6tables
|
||||
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
|
||||
var output string
|
||||
|
||||
logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
|
||||
err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output)
|
||||
if output != "" {
|
||||
logrus.Debugf("passthrough output: %s", output)
|
||||
}
|
||||
|
||||
return []byte(output), err
|
||||
}
|
78
libnetwork/pkg/iptables/firewalld_test.go
Normal file
78
libnetwork/pkg/iptables/firewalld_test.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFirewalldInit(t *testing.T) {
|
||||
FirewalldInit()
|
||||
}
|
||||
|
||||
func TestReloaded(t *testing.T) {
|
||||
var err error
|
||||
var fwdChain *Chain
|
||||
|
||||
fwdChain, err = NewChain("FWD", "lo", Filter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fwdChain.Remove()
|
||||
|
||||
// copy-pasted from iptables_test:TestLink
|
||||
ip1 := net.ParseIP("192.168.1.1")
|
||||
ip2 := net.ParseIP("192.168.1.2")
|
||||
port := 1234
|
||||
proto := "tcp"
|
||||
|
||||
err = fwdChain.Link(Append, ip1, ip2, port, proto)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
// to be re-called again later
|
||||
OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) })
|
||||
}
|
||||
|
||||
rule1 := []string{
|
||||
"-i", fwdChain.Bridge,
|
||||
"-o", fwdChain.Bridge,
|
||||
"-p", proto,
|
||||
"-s", ip1.String(),
|
||||
"-d", ip2.String(),
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "ACCEPT"}
|
||||
|
||||
if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
|
||||
t.Fatalf("rule1 does not exist")
|
||||
}
|
||||
|
||||
// flush all rules
|
||||
fwdChain.Remove()
|
||||
|
||||
reloaded()
|
||||
|
||||
// make sure the rules have been recreated
|
||||
if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
|
||||
t.Fatalf("rule1 hasn't been recreated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassthrough(t *testing.T) {
|
||||
rule1 := []string{
|
||||
"-i", "lo",
|
||||
"-p", "udp",
|
||||
"--dport", "123",
|
||||
"-j", "ACCEPT"}
|
||||
|
||||
if firewalldRunning {
|
||||
_, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !Exists(Filter, "INPUT", rule1...) {
|
||||
t.Fatalf("rule1 does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
321
libnetwork/pkg/iptables/iptables.go
Normal file
321
libnetwork/pkg/iptables/iptables.go
Normal file
|
@ -0,0 +1,321 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
//Action signifies the iptable action.
|
||||
type Action string
|
||||
|
||||
//Table refers to Nat, Filter or Mangle.
|
||||
type Table string
|
||||
|
||||
const (
|
||||
//Append appends the rule at the end of the chain.
|
||||
Append Action = "-A"
|
||||
//Delete deletes the rule from the chain.
|
||||
Delete Action = "-D"
|
||||
//Insert inserts the rule at the top of the chain.
|
||||
Insert Action = "-I"
|
||||
//Nat table is used for nat translation rules.
|
||||
Nat Table = "nat"
|
||||
//Filter table is used for filter rules.
|
||||
Filter Table = "filter"
|
||||
//Mangle table is used for mangling the packet.
|
||||
Mangle Table = "mangle"
|
||||
)
|
||||
|
||||
var (
|
||||
iptablesPath string
|
||||
supportsXlock = false
|
||||
//ErrIptablesNotFound is returned when the rule is not found.
|
||||
ErrIptablesNotFound = errors.New("Iptables not found")
|
||||
)
|
||||
|
||||
//Chain defines the iptables chain.
|
||||
type Chain struct {
|
||||
Name string
|
||||
Bridge string
|
||||
Table Table
|
||||
}
|
||||
|
||||
//ChainError is returned to represent errors during ip table operation.
|
||||
type ChainError struct {
|
||||
Chain string
|
||||
Output []byte
|
||||
}
|
||||
|
||||
func (e ChainError) Error() string {
|
||||
return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output))
|
||||
}
|
||||
|
||||
func initCheck() error {
|
||||
|
||||
if iptablesPath == "" {
|
||||
path, err := exec.LookPath("iptables")
|
||||
if err != nil {
|
||||
return ErrIptablesNotFound
|
||||
}
|
||||
iptablesPath = path
|
||||
supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//NewChain adds a new chain to ip table.
|
||||
func NewChain(name, bridge string, table Table) (*Chain, error) {
|
||||
c := &Chain{
|
||||
Name: name,
|
||||
Bridge: bridge,
|
||||
Table: table,
|
||||
}
|
||||
|
||||
if string(c.Table) == "" {
|
||||
c.Table = Filter
|
||||
}
|
||||
|
||||
// Add chain if it doesn't exist
|
||||
if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil {
|
||||
if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil {
|
||||
return nil, err
|
||||
} else if len(output) != 0 {
|
||||
return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output)
|
||||
}
|
||||
}
|
||||
|
||||
switch table {
|
||||
case Nat:
|
||||
preroute := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL"}
|
||||
if !Exists(Nat, "PREROUTING", preroute...) {
|
||||
if err := c.Prerouting(Append, preroute...); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
||||
}
|
||||
}
|
||||
output := []string{
|
||||
"-m", "addrtype",
|
||||
"--dst-type", "LOCAL",
|
||||
"!", "--dst", "127.0.0.0/8"}
|
||||
if !Exists(Nat, "OUTPUT", output...) {
|
||||
if err := c.Output(Append, output...); err != nil {
|
||||
return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
|
||||
}
|
||||
}
|
||||
case Filter:
|
||||
link := []string{
|
||||
"-o", c.Bridge,
|
||||
"-j", c.Name}
|
||||
if !Exists(Filter, "FORWARD", link...) {
|
||||
insert := append([]string{string(Insert), "FORWARD"}, link...)
|
||||
if output, err := Raw(insert...); err != nil {
|
||||
return nil, err
|
||||
} else if len(output) != 0 {
|
||||
return nil, fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
//RemoveExistingChain removes existing chain from the table.
|
||||
func RemoveExistingChain(name string, table Table) error {
|
||||
c := &Chain{
|
||||
Name: name,
|
||||
Table: table,
|
||||
}
|
||||
if string(c.Table) == "" {
|
||||
c.Table = Filter
|
||||
}
|
||||
return c.Remove()
|
||||
}
|
||||
|
||||
//Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table
|
||||
func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int) error {
|
||||
daddr := ip.String()
|
||||
if ip.IsUnspecified() {
|
||||
// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
|
||||
// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
|
||||
// value" by both iptables and ip6tables.
|
||||
daddr = "0/0"
|
||||
}
|
||||
if output, err := Raw("-t", string(Nat), string(action), c.Name,
|
||||
"-p", proto,
|
||||
"-d", daddr,
|
||||
"--dport", strconv.Itoa(port),
|
||||
"!", "-i", c.Bridge,
|
||||
"-j", "DNAT",
|
||||
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "FORWARD", Output: output}
|
||||
}
|
||||
|
||||
if output, err := Raw("-t", string(Filter), string(action), c.Name,
|
||||
"!", "-i", c.Bridge,
|
||||
"-o", c.Bridge,
|
||||
"-p", proto,
|
||||
"-d", destAddr,
|
||||
"--dport", strconv.Itoa(destPort),
|
||||
"-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "FORWARD", Output: output}
|
||||
}
|
||||
|
||||
if output, err := Raw("-t", string(Nat), string(action), "POSTROUTING",
|
||||
"-p", proto,
|
||||
"-s", destAddr,
|
||||
"-d", destAddr,
|
||||
"--dport", strconv.Itoa(destPort),
|
||||
"-j", "MASQUERADE"); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "FORWARD", Output: output}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Link adds reciprocal ACCEPT rule for two supplied IP addresses.
|
||||
// Traffic is allowed from ip1 to ip2 and vice-versa
|
||||
func (c *Chain) Link(action Action, ip1, ip2 net.IP, port int, proto string) error {
|
||||
if output, err := Raw("-t", string(Filter), string(action), c.Name,
|
||||
"-i", c.Bridge, "-o", c.Bridge,
|
||||
"-p", proto,
|
||||
"-s", ip1.String(),
|
||||
"-d", ip2.String(),
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return fmt.Errorf("Error iptables forward: %s", output)
|
||||
}
|
||||
if output, err := Raw("-t", string(Filter), string(action), c.Name,
|
||||
"-i", c.Bridge, "-o", c.Bridge,
|
||||
"-p", proto,
|
||||
"-s", ip2.String(),
|
||||
"-d", ip1.String(),
|
||||
"--sport", strconv.Itoa(port),
|
||||
"-j", "ACCEPT"); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return fmt.Errorf("Error iptables forward: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Prerouting adds linking rule to nat/PREROUTING chain.
|
||||
func (c *Chain) Prerouting(action Action, args ...string) error {
|
||||
a := []string{"-t", string(Nat), string(action), "PREROUTING"}
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "PREROUTING", Output: output}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Output adds linking rule to an OUTPUT chain
|
||||
func (c *Chain) Output(action Action, args ...string) error {
|
||||
a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
|
||||
if len(args) > 0 {
|
||||
a = append(a, args...)
|
||||
}
|
||||
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
|
||||
return err
|
||||
} else if len(output) != 0 {
|
||||
return ChainError{Chain: "OUTPUT", Output: output}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the chain
|
||||
func (c *Chain) Remove() error {
|
||||
// Ignore errors - This could mean the chains were never set up
|
||||
if c.Table == Nat {
|
||||
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
|
||||
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
|
||||
|
||||
c.Prerouting(Delete)
|
||||
c.Output(Delete)
|
||||
}
|
||||
Raw("-t", string(c.Table), "-F", c.Name)
|
||||
Raw("-t", string(c.Table), "-X", c.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Exists checks if a rule exists
|
||||
func Exists(table Table, chain string, rule ...string) bool {
|
||||
if string(table) == "" {
|
||||
table = Filter
|
||||
}
|
||||
|
||||
// iptables -C, --check option was added in v.1.4.11
|
||||
// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
|
||||
|
||||
// try -C
|
||||
// if exit status is 0 then return true, the rule exists
|
||||
if _, err := Raw(append([]string{
|
||||
"-t", string(table), "-C", chain}, rule...)...); err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// parse "iptables -S" for the rule (this checks rules in a specific chain
|
||||
// in a specific table)
|
||||
ruleString := strings.Join(rule, " ")
|
||||
existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output()
|
||||
|
||||
// regex to replace ips in rule
|
||||
// because MASQUERADE rule will not be exactly what was passed
|
||||
re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`)
|
||||
|
||||
return strings.Contains(
|
||||
re.ReplaceAllString(string(existingRules), "?"),
|
||||
re.ReplaceAllString(ruleString, "?"),
|
||||
)
|
||||
}
|
||||
|
||||
//Raw calls 'iptables' system command, passing supplied arguments
|
||||
func Raw(args ...string) ([]byte, error) {
|
||||
if firewalldRunning {
|
||||
output, err := Passthrough(Iptables, args...)
|
||||
if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") {
|
||||
return output, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := initCheck(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if supportsXlock {
|
||||
args = append([]string{"--wait"}, args...)
|
||||
}
|
||||
|
||||
logrus.Debugf("%s, %v", iptablesPath, args)
|
||||
|
||||
output, err := exec.Command(iptablesPath, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
|
||||
}
|
||||
|
||||
// ignore iptables' message about xtables lock
|
||||
if strings.Contains(string(output), "waiting for it to exit") {
|
||||
output = []byte("")
|
||||
}
|
||||
|
||||
return output, err
|
||||
}
|
198
libnetwork/pkg/iptables/iptables_test.go
Normal file
198
libnetwork/pkg/iptables/iptables_test.go
Normal file
|
@ -0,0 +1,198 @@
|
|||
package iptables
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const chainName = "DOCKERTEST"
|
||||
|
||||
var natChain *Chain
|
||||
var filterChain *Chain
|
||||
|
||||
func TestNewChain(t *testing.T) {
|
||||
var err error
|
||||
|
||||
natChain, err = NewChain(chainName, "lo", Nat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
filterChain, err = NewChain(chainName, "lo", Filter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForward(t *testing.T) {
|
||||
ip := net.ParseIP("192.168.1.1")
|
||||
port := 1234
|
||||
dstAddr := "172.17.0.1"
|
||||
dstPort := 4321
|
||||
proto := "tcp"
|
||||
|
||||
err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dnatRule := []string{
|
||||
"!", "-i", filterChain.Bridge,
|
||||
"-d", ip.String(),
|
||||
"-p", proto,
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "DNAT",
|
||||
"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
|
||||
}
|
||||
|
||||
if !Exists(natChain.Table, natChain.Name, dnatRule...) {
|
||||
t.Fatalf("DNAT rule does not exist")
|
||||
}
|
||||
|
||||
filterRule := []string{
|
||||
"!", "-i", filterChain.Bridge,
|
||||
"-o", filterChain.Bridge,
|
||||
"-d", dstAddr,
|
||||
"-p", proto,
|
||||
"--dport", strconv.Itoa(dstPort),
|
||||
"-j", "ACCEPT",
|
||||
}
|
||||
|
||||
if !Exists(filterChain.Table, filterChain.Name, filterRule...) {
|
||||
t.Fatalf("filter rule does not exist")
|
||||
}
|
||||
|
||||
masqRule := []string{
|
||||
"-d", dstAddr,
|
||||
"-s", dstAddr,
|
||||
"-p", proto,
|
||||
"--dport", strconv.Itoa(dstPort),
|
||||
"-j", "MASQUERADE",
|
||||
}
|
||||
|
||||
if !Exists(natChain.Table, "POSTROUTING", masqRule...) {
|
||||
t.Fatalf("MASQUERADE rule does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLink(t *testing.T) {
|
||||
var err error
|
||||
|
||||
ip1 := net.ParseIP("192.168.1.1")
|
||||
ip2 := net.ParseIP("192.168.1.2")
|
||||
port := 1234
|
||||
proto := "tcp"
|
||||
|
||||
err = filterChain.Link(Append, ip1, ip2, port, proto)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule1 := []string{
|
||||
"-i", filterChain.Bridge,
|
||||
"-o", filterChain.Bridge,
|
||||
"-p", proto,
|
||||
"-s", ip1.String(),
|
||||
"-d", ip2.String(),
|
||||
"--dport", strconv.Itoa(port),
|
||||
"-j", "ACCEPT"}
|
||||
|
||||
if !Exists(filterChain.Table, filterChain.Name, rule1...) {
|
||||
t.Fatalf("rule1 does not exist")
|
||||
}
|
||||
|
||||
rule2 := []string{
|
||||
"-i", filterChain.Bridge,
|
||||
"-o", filterChain.Bridge,
|
||||
"-p", proto,
|
||||
"-s", ip2.String(),
|
||||
"-d", ip1.String(),
|
||||
"--sport", strconv.Itoa(port),
|
||||
"-j", "ACCEPT"}
|
||||
|
||||
if !Exists(filterChain.Table, filterChain.Name, rule2...) {
|
||||
t.Fatalf("rule2 does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrerouting(t *testing.T) {
|
||||
args := []string{
|
||||
"-i", "lo",
|
||||
"-d", "192.168.1.1"}
|
||||
|
||||
err := natChain.Prerouting(Insert, args...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "PREROUTING", rule...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutput(t *testing.T) {
|
||||
args := []string{
|
||||
"-o", "lo",
|
||||
"-d", "192.168.1.1"}
|
||||
|
||||
err := natChain.Output(Insert, args...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rule := []string{
|
||||
"-j", natChain.Name}
|
||||
|
||||
rule = append(rule, args...)
|
||||
|
||||
if !Exists(natChain.Table, "OUTPUT", rule...) {
|
||||
t.Fatalf("rule does not exist")
|
||||
}
|
||||
|
||||
delRule := append([]string{"-D", "OUTPUT", "-t",
|
||||
string(natChain.Table)}, rule...)
|
||||
if _, err = Raw(delRule...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanup(t *testing.T) {
|
||||
var err error
|
||||
var rules []byte
|
||||
|
||||
// Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
|
||||
link := []string{"-t", string(filterChain.Table),
|
||||
string(Delete), "FORWARD",
|
||||
"-o", filterChain.Bridge,
|
||||
"-j", filterChain.Name}
|
||||
if _, err = Raw(link...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
filterChain.Remove()
|
||||
|
||||
err = RemoveExistingChain(chainName, Nat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rules, err = exec.Command("iptables-save").Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(rules), chainName) {
|
||||
t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
"github.com/docker/libnetwork/pkg/portallocator"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/iptables"
|
||||
"github.com/docker/libnetwork/pkg/iptables"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
Loading…
Add table
Reference in a new issue