From c13d4f998d24c1fd6f39d31d8fb06ea3f22bddd6 Mon Sep 17 00:00:00 2001 From: Jacob Wen Date: Mon, 13 Mar 2017 15:46:46 +0800 Subject: [PATCH] Add a filter chain to allow persistent rules Allow users to configure firewall policies in a way that persists docker operations/restarts. Docker will not delete or modify any pre-existing rules from the DOCKER-USER filter chain. This allows the user to create in advance any rules required to further restrict access from/to the containers. Fixes docker/docker#29184 Fixes docker/docker#23987 Related to docker/docker#24848 Signed-off-by: Jacob Wen --- controller.go | 29 ++++++++++++++++++++ drivers/bridge/setup_ip_tables.go | 44 ++----------------------------- iptables/iptables.go | 41 ++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 42 deletions(-) diff --git a/controller.go b/controller.go index a62d4fb980..49c207f64b 100644 --- a/controller.go +++ b/controller.go @@ -65,6 +65,7 @@ import ( "github.com/docker/libnetwork/drvregistry" "github.com/docker/libnetwork/hostdiscovery" "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/iptables" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" @@ -719,6 +720,10 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... } }() + c.Lock() + arrangeUserFilterRule() + c.Unlock() + // First store the endpoint count, then the network. To avoid to // end up with a datastore containing a network and not an epCnt, // in case of an ungraceful shutdown during this function call. @@ -743,6 +748,7 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... if !c.isDistributedControl() { c.Lock() arrangeIngressFilterRule() + arrangeUserFilterRule() c.Unlock() } @@ -1178,3 +1184,26 @@ func (c *controller) clearIngress(clusterLeave bool) { } } } + +const userChain = "DOCKER-USER" + +// This chain allow users to configure firewall policies in a way that persists +// docker operations/restarts. Docker will not delete or modify any pre-existing +// rules from the DOCKER-USER filter chain. +func arrangeUserFilterRule() { + _, err := iptables.NewChain(userChain, iptables.Filter, false) + if err != nil { + logrus.Warnf("Failed to create %s chain: %v", userChain, err) + return + } + + if err = iptables.AddReturnRule(userChain); err != nil { + logrus.Warnf("Failed to add the RETURN rule for %s: %v", userChain, err) + return + } + + err = iptables.EnsureJumpRule("FORWARD", userChain) + if err != nil { + logrus.Warnf("Failed to ensure the jump rule for %s: %v", userChain, err) + } +} diff --git a/drivers/bridge/setup_ip_tables.go b/drivers/bridge/setup_ip_tables.go index 1ac7fbc808..d087f0c547 100644 --- a/drivers/bridge/setup_ip_tables.go +++ b/drivers/bridge/setup_ip_tables.go @@ -52,7 +52,7 @@ func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainI return nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err) } - if err := addReturnRule(IsolationChain); err != nil { + if err := iptables.AddReturnRule(IsolationChain); err != nil { return nil, nil, nil, err } @@ -116,7 +116,7 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt } d.Lock() - err = ensureJumpRule("FORWARD", IsolationChain) + err = iptables.EnsureJumpRule("FORWARD", IsolationChain) d.Unlock() if err != nil { return err @@ -285,46 +285,6 @@ func setINC(iface1, iface2 string, enable bool) error { return nil } -func addReturnRule(chain string) error { - var ( - table = iptables.Filter - args = []string{"-j", "RETURN"} - ) - - if iptables.Exists(table, chain, args...) { - return nil - } - - err := iptables.RawCombinedOutput(append([]string{"-I", chain}, args...)...) - if err != nil { - return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error()) - } - - return nil -} - -// Ensure the jump rule is on top -func ensureJumpRule(fromChain, toChain string) error { - var ( - table = iptables.Filter - args = []string{"-j", toChain} - ) - - if iptables.Exists(table, fromChain, args...) { - err := iptables.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...) - if err != nil { - return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) - } - } - - err := iptables.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...) - if err != nil { - return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) - } - - return nil -} - func removeIPChains() { for _, chainInfo := range []iptables.ChainInfo{ {Name: DockerChain, Table: iptables.Nat}, diff --git a/iptables/iptables.go b/iptables/iptables.go index d4f4aa23dd..7913b3d4d9 100644 --- a/iptables/iptables.go +++ b/iptables/iptables.go @@ -465,3 +465,44 @@ func parseVersionNumbers(input string) (major, minor, micro int) { func supportsCOption(mj, mn, mc int) bool { return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11))) } + +// AddReturnRule adds a return rule for the chain in the filter table +func AddReturnRule(chain string) error { + var ( + table = Filter + args = []string{"-j", "RETURN"} + ) + + if Exists(table, chain, args...) { + return nil + } + + err := RawCombinedOutput(append([]string{"-A", chain}, args...)...) + if err != nil { + return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error()) + } + + return nil +} + +// EnsureJumpRule ensures the jump rule is on top +func EnsureJumpRule(fromChain, toChain string) error { + var ( + table = Filter + args = []string{"-j", toChain} + ) + + if Exists(table, fromChain, args...) { + err := RawCombinedOutput(append([]string{"-D", fromChain}, args...)...) + if err != nil { + return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) + } + } + + err := RawCombinedOutput(append([]string{"-I", fromChain}, args...)...) + if err != nil { + return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) + } + + return nil +}