|
@@ -13,14 +13,17 @@ import (
|
|
|
)
|
|
|
|
|
|
type Action string
|
|
|
+type Table string
|
|
|
|
|
|
const (
|
|
|
- Add Action = "-A"
|
|
|
+ Append Action = "-A"
|
|
|
Delete Action = "-D"
|
|
|
+ Insert Action = "-I"
|
|
|
+ Nat Table = "nat"
|
|
|
+ Filter Table = "filter"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- nat = []string{"-t", "nat"}
|
|
|
supportsXlock = false
|
|
|
ErrIptablesNotFound = errors.New("Iptables not found")
|
|
|
)
|
|
@@ -28,6 +31,7 @@ var (
|
|
|
type Chain struct {
|
|
|
Name string
|
|
|
Bridge string
|
|
|
+ Table Table
|
|
|
}
|
|
|
|
|
|
type ChainError struct {
|
|
@@ -43,34 +47,74 @@ func init() {
|
|
|
supportsXlock = exec.Command("iptables", "--wait", "-L", "-n").Run() == nil
|
|
|
}
|
|
|
|
|
|
-func NewChain(name, bridge string) (*Chain, error) {
|
|
|
- if output, err := Raw("-t", "nat", "-N", name); err != nil {
|
|
|
- return nil, err
|
|
|
- } else if len(output) != 0 {
|
|
|
- return nil, fmt.Errorf("Error creating new iptables chain: %s", output)
|
|
|
- }
|
|
|
- chain := &Chain{
|
|
|
+func NewChain(name, bridge string, table Table) (*Chain, error) {
|
|
|
+ c := &Chain{
|
|
|
Name: name,
|
|
|
Bridge: bridge,
|
|
|
+ Table: table,
|
|
|
+ }
|
|
|
+
|
|
|
+ if string(c.Table) == "" {
|
|
|
+ c.Table = Filter
|
|
|
}
|
|
|
|
|
|
- if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
|
|
|
- return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
|
|
|
+ // 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)
|
|
|
+ }
|
|
|
}
|
|
|
- if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
|
|
|
- return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
|
|
|
+
|
|
|
+ switch table {
|
|
|
+ case Nat:
|
|
|
+ preroute := []string{
|
|
|
+ "-m", "addrtype",
|
|
|
+ "--dst-type", "LOCAL"}
|
|
|
+ if !Exists(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(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{"FORWARD",
|
|
|
+ "-o", c.Bridge,
|
|
|
+ "-j", c.Name}
|
|
|
+ if !Exists(link...) {
|
|
|
+ insert := append([]string{string(Insert)}, 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 chain, nil
|
|
|
+ return c, nil
|
|
|
}
|
|
|
|
|
|
-func RemoveExistingChain(name string) error {
|
|
|
- chain := &Chain{
|
|
|
- Name: name,
|
|
|
+func RemoveExistingChain(name string, table Table) error {
|
|
|
+ c := &Chain{
|
|
|
+ Name: name,
|
|
|
+ Table: table,
|
|
|
+ }
|
|
|
+ if string(c.Table) == "" {
|
|
|
+ c.Table = Filter
|
|
|
}
|
|
|
- return chain.Remove()
|
|
|
+ return c.Remove()
|
|
|
}
|
|
|
|
|
|
-func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
|
|
|
+// Add 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
|
|
@@ -78,39 +122,75 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr str
|
|
|
// value" by both iptables and ip6tables.
|
|
|
daddr = "0/0"
|
|
|
}
|
|
|
- if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
|
|
|
+ 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(dest_addr, strconv.Itoa(dest_port))); err != nil {
|
|
|
+ "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
|
|
|
return err
|
|
|
} else if len(output) != 0 {
|
|
|
return &ChainError{Chain: "FORWARD", Output: output}
|
|
|
}
|
|
|
|
|
|
- fAction := action
|
|
|
- if fAction == Add {
|
|
|
- fAction = "-I"
|
|
|
- }
|
|
|
- if output, err := Raw(string(fAction), "FORWARD",
|
|
|
+ if output, err := Raw("-t", string(Filter), string(action), c.Name,
|
|
|
"!", "-i", c.Bridge,
|
|
|
"-o", c.Bridge,
|
|
|
"-p", proto,
|
|
|
- "-d", dest_addr,
|
|
|
- "--dport", strconv.Itoa(dest_port),
|
|
|
+ "-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
|
|
|
+}
|
|
|
+
|
|
|
+// Add 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
|
|
|
}
|
|
|
|
|
|
+// Add linking rule to nat/PREROUTING chain.
|
|
|
func (c *Chain) Prerouting(action Action, args ...string) error {
|
|
|
- a := append(nat, fmt.Sprint(action), "PREROUTING")
|
|
|
+ a := []string{"-t", string(Nat), string(action), "PREROUTING"}
|
|
|
if len(args) > 0 {
|
|
|
a = append(a, args...)
|
|
|
}
|
|
@@ -122,8 +202,9 @@ func (c *Chain) Prerouting(action Action, args ...string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+// Add linking rule to an OUTPUT chain
|
|
|
func (c *Chain) Output(action Action, args ...string) error {
|
|
|
- a := append(nat, fmt.Sprint(action), "OUTPUT")
|
|
|
+ a := []string{"-t", string(c.Table), string(action), "OUTPUT"}
|
|
|
if len(args) > 0 {
|
|
|
a = append(a, args...)
|
|
|
}
|
|
@@ -137,20 +218,20 @@ func (c *Chain) Output(action Action, args ...string) error {
|
|
|
|
|
|
func (c *Chain) Remove() error {
|
|
|
// Ignore errors - This could mean the chains were never set up
|
|
|
- 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", "nat", "-F", c.Name)
|
|
|
- Raw("-t", "nat", "-X", c.Name)
|
|
|
+ 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
|
|
|
}
|
|
|
|
|
|
-// Check if an existing rule exists
|
|
|
+// Check if a rule exists
|
|
|
func Exists(args ...string) bool {
|
|
|
// iptables -C, --check option was added in v.1.4.11
|
|
|
// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
|
|
@@ -175,6 +256,7 @@ func Exists(args ...string) bool {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+// Call 'iptables' system command, passing supplied arguments
|
|
|
func Raw(args ...string) ([]byte, error) {
|
|
|
path, err := exec.LookPath("iptables")
|
|
|
if err != nil {
|