-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* create a firewall interface This is creating a firewall interface and reintroduce the iptables integration as a fallback. * update * update * update * update * revert to go v1.20 * use NewFirewallClient name * formatting * formatting --------- Co-authored-by: Roman Dodin <dodin.roman@gmail.com>
- Loading branch information
Showing
8 changed files
with
553 additions
and
362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package docker | ||
|
||
import ( | ||
log "github.com/sirupsen/logrus" | ||
"github.com/srl-labs/containerlab/runtime/docker/firewall" | ||
) | ||
|
||
// deleteFwdRule deletes `allow` rule installed with installFwdRule when the bridge interface doesn't exist anymore. | ||
func (d *DockerRuntime) deleteFwdRule() (err error) { | ||
if !*d.mgmt.ExternalAccess { | ||
return | ||
} | ||
|
||
f, err := firewall.NewFirewallClient(d.mgmt.Bridge) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return f.DeleteForwardingRules() | ||
} | ||
|
||
// installFwdRule installs the `allow` rule for traffic destined to the nodes | ||
// on the clab management network. | ||
// This rule is required for external access to the nodes. | ||
func (d *DockerRuntime) installFwdRule() (err error) { | ||
if !*d.mgmt.ExternalAccess { | ||
return | ||
} | ||
|
||
if d.mgmt.Bridge == "" { | ||
log.Debug("skipping setup of forwarding rules for non-bridged management network") | ||
return | ||
} | ||
|
||
f, err := firewall.NewFirewallClient(d.mgmt.Bridge) | ||
if err != nil { | ||
return err | ||
} | ||
log.Debugf("using %s as the firewall interface", f.Name()) | ||
|
||
return f.InstallForwardingRules() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package definitions | ||
|
||
import "errors" | ||
|
||
var ErrNotAvailabel = errors.New("not available") | ||
|
||
const ( | ||
DockerFWUserChain = "DOCKER-USER" | ||
DockerFWTable = "filter" | ||
|
||
IPTablesRuleComment = "set by containerlab" | ||
|
||
IPTablesCommentMaxSize = 256 | ||
) | ||
|
||
// ClabFirewall is the interface that all firewall clients must implement. | ||
type ClabFirewall interface { | ||
DeleteForwardingRules() error | ||
InstallForwardingRules() error | ||
Name() string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package firewall | ||
|
||
import ( | ||
"github.com/srl-labs/containerlab/runtime/docker/firewall/definitions" | ||
"github.com/srl-labs/containerlab/runtime/docker/firewall/iptables" | ||
"github.com/srl-labs/containerlab/runtime/docker/firewall/nftables" | ||
) | ||
|
||
// NewFirewallClient returns a firewall client based on the availability of nftables or iptables. | ||
func NewFirewallClient(bridgeName string) (definitions.ClabFirewall, error) { | ||
var clf definitions.ClabFirewall | ||
|
||
clf, err := nftables.NewNftablesClient(bridgeName) | ||
if err == nil { | ||
return clf, nil | ||
} | ||
|
||
clf, err = iptables.NewIpTablesClient(bridgeName) | ||
if err == nil { | ||
return clf, nil | ||
} | ||
|
||
return nil, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package iptables | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/google/shlex" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/srl-labs/containerlab/runtime/docker/firewall/definitions" | ||
"github.com/srl-labs/containerlab/utils" | ||
) | ||
|
||
const ( | ||
iptCheckCmd = "-vL DOCKER-USER" | ||
iptAllowCmd = "-I DOCKER-USER -o %s -j ACCEPT -m comment --comment \"" + definitions.IPTablesRuleComment + "\"" | ||
iptDelCmd = "-D DOCKER-USER -o %s -j ACCEPT -m comment --comment \"" + definitions.IPTablesRuleComment + "\"" | ||
ipTables = "ip_tables" | ||
) | ||
|
||
// IpTablesClient is a client for iptables. | ||
type IpTablesClient struct { | ||
bridgeName string | ||
} | ||
|
||
// NewIpTablesClient returns a new IpTablesClient. | ||
func NewIpTablesClient(bridgeName string) (*IpTablesClient, error) { | ||
loaded, err := utils.IsKernelModuleLoaded("ip_tables") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if !loaded { | ||
log.Debug("ip_tables kernel module not available") | ||
// module is not loaded | ||
return nil, definitions.ErrNotAvailabel | ||
} | ||
|
||
return &IpTablesClient{ | ||
bridgeName: bridgeName, | ||
}, nil | ||
} | ||
|
||
// Name returns the name of the firewall client. | ||
func (*IpTablesClient) Name() string { | ||
return ipTables | ||
} | ||
|
||
// InstallForwardingRules installs the forwarding rules. | ||
func (c *IpTablesClient) InstallForwardingRules() error { | ||
// first check if a rule already exists to not create duplicates | ||
res, err := exec.Command("iptables", strings.Split(iptCheckCmd, " ")...).Output() | ||
if bytes.Contains(res, []byte(c.bridgeName)) { | ||
log.Debugf("found iptables forwarding rule targeting the bridge %q. Skipping creation of the forwarding rule.", c.bridgeName) | ||
return err | ||
} | ||
if err != nil { | ||
// non nil error typically means that DOCKER-USER chain doesn't exist | ||
// this happens with old docker installations (centos7 hello) from default repos | ||
return fmt.Errorf("missing DOCKER-USER iptables chain. See http://containerlab.dev/manual/network/#external-access") | ||
} | ||
|
||
cmd, err := shlex.Split(fmt.Sprintf(iptAllowCmd, c.bridgeName)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Debugf("Installing iptables rules for bridge %q", c.bridgeName) | ||
|
||
stdOutErr, err := exec.Command("iptables", cmd...).CombinedOutput() | ||
if err != nil { | ||
log.Warnf("Iptables install stdout/stderr result is: %s", stdOutErr) | ||
return fmt.Errorf("unable to install iptables rule using '%s' command: %w", cmd, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// DeleteForwardingRules deletes the forwarding rules. | ||
func (c *IpTablesClient) DeleteForwardingRules() error { | ||
// first check if a rule exists before trying to delete it | ||
res, err := exec.Command("iptables", strings.Split(iptCheckCmd, " ")...).Output() | ||
if err != nil { | ||
// non nil error typically means that DOCKER-USER chain doesn't exist | ||
// this happens with old docker installations (centos7 hello) from default repos | ||
return fmt.Errorf("missing DOCKER-USER iptables chain. See http://containerlab.dev/manual/network/#external-access") | ||
} | ||
|
||
if !bytes.Contains(res, []byte(c.bridgeName)) { | ||
log.Debug("external access iptables rule doesn't exist. Skipping deletion") | ||
return nil | ||
} | ||
|
||
// we are not deleting the rule if the bridge still exists | ||
// it happens when bridge is either still in use by docker network | ||
// or it is managed externally (created manually) | ||
_, err = utils.BridgeByName(c.bridgeName) | ||
if err == nil { | ||
log.Debugf("bridge %s is still in use, not removing the forwarding rule", c.bridgeName) | ||
return nil | ||
} | ||
|
||
cmd, err := shlex.Split(fmt.Sprintf(iptDelCmd, c.bridgeName)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Debugf("removing clab iptables rules for bridge %q", c.bridgeName) | ||
log.Debugf("trying to delete the forwarding rule with cmd: iptables %s", cmd) | ||
|
||
stdOutErr, err := exec.Command("iptables", cmd...).CombinedOutput() | ||
if err != nil { | ||
log.Warnf("Iptables delete stdout/stderr result is: %s", stdOutErr) | ||
return fmt.Errorf("unable to delete iptables rules: %w", err) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.