Skip to content

Commit

Permalink
Adding additional IP table rules so container can be reached from hos…
Browse files Browse the repository at this point in the history
…t and other containers
  • Loading branch information
JustinJudd committed Aug 22, 2016
1 parent 17f6b4b commit 48f5f7a
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 54 deletions.
126 changes: 72 additions & 54 deletions forward/forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"io/ioutil"
"strconv"
"strings"
"time"

"github.com/coreos/go-iptables/iptables"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
"gopkg.in/yaml.v2"
)

Expand Down Expand Up @@ -168,6 +170,10 @@ func (f Forwarder) ForwardContainer(container string) error {
return fmt.Errorf("unable to get container state for container %s: %s", container, err)
}

if state.StatusCode != shared.Running {
return fmt.Errorf("Container %s is not currently running", container)
}

// Get list of IP addresses on the container to forward to
ip4Addresses := []string{}
ip6Addresses := []string{}
Expand Down Expand Up @@ -201,30 +207,49 @@ func (f Forwarder) ForwardContainer(container string) error {
}

// Create a new custom chain for the IPTable rules for just this container
customChain := getChain(container)
err = iptable.NewChain(IPTable, customChain)
customDstChain := getChain(container, Dst)
customSrcChain := getChain(container, Src)

err = iptable.NewChain(IPTable, customDstChain)
if err != nil {
return err
}
err = iptable.NewChain(IPTable, customSrcChain)
if err != nil {
return err
}
err = ip6table.NewChain(IPTable, customChain)
err = ip6table.NewChain(IPTable, customDstChain)
if err != nil {
return err
}
err = ip6table.NewChain(IPTable, customSrcChain)
if err != nil {
return err
}

// Tell IPTables when to use our custom chain
err = iptable.Insert(IPTable, "PREROUTING", 1, []string{
"-m", "addrtype",
"--dst-type", "LOCAL",
"-j", customChain,
}...)
err = iptable.Insert(IPTable, "PREROUTING", 1, getChainForwardRule(container, IPv4, Dst)...)
if err != nil {
return err
}
err = ip6table.Insert(IPTable, "PREROUTING", 1, getChainForwardRule(container, IPv6, Dst)...)
if err != nil {
return err
}
err = iptable.Insert(IPTable, "OUTPUT", 1, getChainForwardRule(container, IPv4, Dst)...)
if err != nil {
return err
}
err = ip6table.Insert(IPTable, "PREROUTING", 1, []string{
"-m", "addrtype",
"--dst-type", "LOCAL",
"-j", customChain,
}...)
err = ip6table.Insert(IPTable, "OUTPUT", 1, getChainForwardRule(container, IPv6, Dst)...)
if err != nil {
return err
}

err = iptable.Insert(IPTable, "POSTROUTING", 1, getChainForwardRule(container, IPv4, Src)...)
if err != nil {
return err
}
err = ip6table.Insert(IPTable, "POSTROUTING", 1, getChainForwardRule(container, IPv6, Src)...)
if err != nil {
return err
}
Expand All @@ -235,23 +260,17 @@ func (f Forwarder) ForwardContainer(container string) error {
for hostPort, containerPort := range portForwards.Ports {

for _, address := range ip4Addresses {
iptable.Append(IPTable, customChain, []string{
//"-i", iface,
"-p", protocol,
"--dport", hostPort,
"-j", "DNAT",
"--to", fmt.Sprintf("%s:%d", address, containerPort),
}...)
iptable.Append(IPTable, customDstChain, getPortForwardRule(protocol, address, strconv.Itoa(containerPort), hostPort, IPv4, Dst)...)

iptable.Append(IPTable, customSrcChain, getPortForwardRule(protocol, address, strconv.Itoa(containerPort), hostPort, IPv4, Src)...)

}

for _, address := range ip6Addresses {
ip6table.Append(IPTable, customChain, []string{
//"-i", iface,
"-p", protocol,
"--dport", hostPort,
"-j", "DNAT",
"--to", fmt.Sprintf("[%s]:%d", address, containerPort),
}...)
ip6table.Append(IPTable, customDstChain, getPortForwardRule(protocol, address, strconv.Itoa(containerPort), hostPort, IPv6, Dst)...)

ip6table.Append(IPTable, customSrcChain, getPortForwardRule(protocol, address, strconv.Itoa(containerPort), hostPort, IPv6, Src)...)

}

}
Expand All @@ -262,7 +281,9 @@ func (f Forwarder) ForwardContainer(container string) error {

// ReverseContainer removes port forwarding for the provided container
func (f Forwarder) ReverseContainer(container string) error {
customChain := getChain(container)
customDstChain := getChain(container, Dst)
customSrcChain := getChain(container, Src)

iptable, err := iptables.New()
if err != nil {
return err
Expand All @@ -272,27 +293,24 @@ func (f Forwarder) ReverseContainer(container string) error {
return err
}

err = iptable.Delete(IPTable, "PREROUTING", []string{
"-m", "addrtype",
"--dst-type", "LOCAL",
"-j", customChain,
}...)
if err != nil {
return err
}
err = ip6table.Delete(IPTable, "PREROUTING", []string{
"-m", "addrtype",
"--dst-type", "LOCAL",
"-j", customChain,
}...)
if err != nil {
return err
}
iptable.Delete(IPTable, "PREROUTING", getChainForwardRule(container, IPv4, Dst)...)
ip6table.Delete(IPTable, "PREROUTING", getChainForwardRule(container, IPv6, Dst)...)

iptable.Delete(IPTable, "OUTPUT", getChainForwardRule(container, IPv4, Dst)...)
ip6table.Delete(IPTable, "OUTPUT", getChainForwardRule(container, IPv6, Dst)...)

iptable.Delete(IPTable, "POSTROUTING", getChainForwardRule(container, IPv4, Src)...)
ip6table.Delete(IPTable, "POSTROUTING", getChainForwardRule(container, IPv6, Src)...)

iptable.ClearChain(IPTable, customChain)
iptable.DeleteChain(IPTable, customChain)
ip6table.ClearChain(IPTable, customChain)
ip6table.DeleteChain(IPTable, customChain)
iptable.ClearChain(IPTable, customDstChain)
iptable.DeleteChain(IPTable, customDstChain)
iptable.ClearChain(IPTable, customSrcChain)
iptable.DeleteChain(IPTable, customSrcChain)

ip6table.ClearChain(IPTable, customDstChain)
ip6table.DeleteChain(IPTable, customDstChain)
ip6table.ClearChain(IPTable, customSrcChain)
ip6table.DeleteChain(IPTable, customSrcChain)

return nil
}
Expand Down Expand Up @@ -325,7 +343,12 @@ func (f Forwarder) Watch() {
}
switch message {
case ContainerStarted:
f.ForwardContainer(container)
go func() {
// Wait a few seconds for the newly running container to get an IP address
time.Sleep(2 * time.Second)
f.ForwardContainer(container)
}()

case ContainerStopped:
f.ReverseContainer(container)
}
Expand All @@ -335,8 +358,3 @@ func (f Forwarder) Watch() {

f.Monitor([]string{}, handler)
}

// getChain returns the custom IPTables chain that should be used for the rules for a container
func getChain(container string) string {
return fmt.Sprintf("LXD-%s", container)
}
153 changes: 153 additions & 0 deletions forward/iptables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package forward

import "fmt"

// IPVersion is used to modify IPTables rules as needed for iptables vs ip6tables
type IPVersion int

const (
//IPv4 is for rules to iptables
IPv4 IPVersion = iota
//IPv6 is for rules to ip6tables
IPv6
)

// Direction is used to define the direction of traffic aka if the host is the source or destination of traffic
type Direction int

const (
// Dst is for traffic where the host is the destination
Dst Direction = iota
// Src is fot when the host is the source
Src
)

func (d Direction) String() string {
switch d {
case Dst:
return "dst"
case Src:
return "src"
default:
return "unknown"
}
}

// getChain returns the custom IPTables chain that should be used for the rules for a container
func getChain(container string, dir Direction) string {
return fmt.Sprintf("LXD-%s-%s", container, dir)
}

func getChainForwardRule(container string, ipVersion IPVersion, dir Direction) []string {
chain := getChain(container, dir)
switch dir {
case Dst:
return getDestinationChainForwardRule(chain)

case Src:
return getSourceChainForwardRule(chain, ipVersion)

default:
return []string{}
}
}

func getSourceChainForwardRule(chain string, ipVersion IPVersion) []string {
switch ipVersion {
case IPv4:
return []string{
"-s", "127.0.0.0/8",
"!", "-d", "127.0.0.0/8",
"-j", chain,
}

case IPv6:
return []string{
"-s", "::1",
"!", "-d", "::1",
"-j", chain,
}

default:
return []string{}

}

}

func getDestinationChainForwardRule(chain string) []string {
return []string{
"-m", "addrtype",
"--dst-type", "LOCAL",
"-j", chain,
}
}

func getPortForwardRule(protocol, containerIP, containerPort, hostPort string, ipVersion IPVersion, dir Direction) []string {
switch dir {
case Dst:
return getDestinationPortForwardRule(protocol, containerIP, containerPort, hostPort, ipVersion)

case Src:
return getSourcePortForwardRule(protocol, containerIP, containerPort, ipVersion)

default:
return []string{}
}
}

func getSourcePortForwardRule(protocol, containerIP, containerPort string, ipVersion IPVersion) []string {

switch ipVersion {
case IPv4:
return []string{
"-p", protocol,
"-s", "127.0.0.0/8",
"-d", containerIP,
"--dport", containerPort,
"-j", "MASQUERADE",
}

case IPv6:
return []string{
"-p", protocol,
"-s", "::1",
"-d", containerIP,
"--dport", containerPort,
"-j", "MASQUERADE",
}

default:
return []string{}

}

}

func getDestinationPortForwardRule(protocol, containerIP, containerPort, hostPort string, ipVersion IPVersion) []string {

switch ipVersion {
case IPv4:
return []string{
//"-i", iface,
"-p", protocol,
"--dport", hostPort,
"-j", "DNAT",
"--to", fmt.Sprintf("%s:%s", containerIP, containerPort),
}

case IPv6:
return []string{
//"-i", iface,
"-p", protocol,
"--dport", hostPort,
"-j", "DNAT",
"--to", fmt.Sprintf("[%s]:%s", containerIP, containerPort),
}

default:
return []string{}

}

}

0 comments on commit 48f5f7a

Please sign in to comment.