Skip to content

Commit

Permalink
iptables rules for mgmt net and bridges in both directions (#2406)
Browse files Browse the repository at this point in the history
* fix iptables command direction flag

* get chains for a provided family

* use rule definition

* fix nftables handling mgmt rules in both directions

* augmented iptables check for both directions

* added more debug configs

* adapt iptables ruleExists check for in/out directions

* added make target for building linux/amd64

* added af map

* added docs

* added tests for iptables rules for datapath bridges

* fix chain in the test

* remove testing if the fwd rules are gone, since they are not cleaned up

* meh
  • Loading branch information
hellt authored Jan 17, 2025
1 parent d7588f9 commit 3e7a7c3
Show file tree
Hide file tree
Showing 12 changed files with 369 additions and 120 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ build:

build-linux-arm64:
mkdir -p $(BIN_DIR)
GOARCH=arm64 go build -o $(BINARY) -ldflags="$(LDFLAGS)" main.go
GOOS=linux GOARCH=arm64 go build -o $(BINARY) -ldflags="$(LDFLAGS)" main.go

build-linux-amd64:
mkdir -p $(BIN_DIR)
GOOS=linux GOARCH=amd64 go build -o $(BINARY) -ldflags="$(LDFLAGS)" main.go

build-with-cover:
mkdir -p $(BIN_DIR)
Expand Down
51 changes: 48 additions & 3 deletions docs/manual/dev/debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ We will document the workflow for the latter (non-root user) case, as this is th

The debug configuration defined in the `launch.json` file will contain the important fields such as `asRoot` and `console`, both needed for the debugging as a root user.

Here is an example of a configuration file that launches `containerlab tools vxlan create` command in the debug mode:
Here is an example of a configuration file that has debug configurations to run the following debug configurations:

```json
1. run `containerlab tools vxlan create` command
2. run `containerlab deploy` command for a given topology
3. run `containerlab destroy` for a given topology

```{.json .code-scroll-lg}
{
"version": "0.2.0",
"configurations": [
Expand All @@ -41,6 +45,36 @@ Here is an example of a configuration file that launches `containerlab tools vxl
"ens3"
],
"preLaunchTask": "delete vx-ens3 interface",
},
{
"name": "deploy linux lab",
"type": "go",
"request": "launch",
"mode": "exec",
"console": "integratedTerminal",
"asRoot": true,
"program": "${workspaceFolder}/bin/containerlab",
"args": [
"dep",
"-t",
"private/linux.clab.yml"
],
"preLaunchTask": "delete linux lab",
},
{
"name": "destroy linux lab",
"type": "go",
"request": "launch",
"mode": "exec",
"console": "integratedTerminal",
"asRoot": true,
"program": "${workspaceFolder}/bin/containerlab",
"args": [
"des",
"-t",
"private/linux.clab.yml"
],
"preLaunchTask": "make build-dlv-debug",
}
]
}
Expand All @@ -58,7 +92,7 @@ Here is a simple task file that contains two tasks - one is building the binary

The dependencies between the tasks are defined in the `dependsOn` field, and this is how you can first build the binary and then run the preparation step.

```json
```{.json .code-scroll-lg}
{
"version": "2.0.0",
"tasks": [
Expand All @@ -73,6 +107,17 @@ The dependencies between the tasks are defined in the `dependsOn` field, and thi
"dependsOn": "make build-dlv-debug",
"problemMatcher": []
},
{
"label": "delete linux lab",
"type": "shell",
"command": "sudo clab des -c -t private/linux.clab.yml",
"presentation": {
"reveal": "always",
"panel": "new"
},
"dependsOn": "make build-dlv-debug",
"problemMatcher": []
},
{
"label": "make build-dlv-debug",
"type": "shell",
Expand Down
16 changes: 14 additions & 2 deletions docs/manual/kinds/bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,24 @@ br-clab 8000.6281eb7133d2 no eth1
eth3
```

Containerlab automatically adds iptables rules for the referenced bridges (v4 and v6) to allow traffic ingressing to the bridges. Namely, for a given bridge named `br-clab` containerlab will attempt to create the allowing rule in the filter table, FORWARD chain like this:
Containerlab automatically adds iptables rules for the referenced bridges (v4 and v6) to allow traffic ingressing/egressing to/from the bridges. Namely, for a given bridge named `br-clab` containerlab will attempt to create the allowing rule in the filter table, FORWARD chain like this:

```
iptables -I FORWARD -i br-clab -j ACCEPT
iptables -I FORWARD -o br-clab -j ACCEPT
```

This will ensure that traffic is forwarded when passing this particular bridge. Note, that once you destroy the lab, the rule will stay, if you wish to remove it, you will have to do it manually.
This will ensure that traffic is forwarded when passing this particular bridge.

/// warning
Once you destroy the lab, the rules in the FORWARD chain will stay, if you wish to remove it, you will have to do it manually. For example the with the following script (for v4 family):

```
sudo iptables -vL FORWARD --line-numbers -n | \
grep "set by containerlab" | awk '{print $1}' \
| sort -r | xargs -I {} sudo iptables -D FORWARD {}
```

///

Check out ["External bridge"](../../lab-examples/ext-bridge.md) lab for a ready-made example on how to use bridges.
7 changes: 4 additions & 3 deletions docs/manual/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,13 @@ sudo iptables -vnL DOCKER-USER
```{.no-copy .no-select}
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * br-a8b9fc8b33a2 0.0.0.0/0 0.0.0.0/0 /* set by containerlab */
12719 79M RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT 0 -- br-1351328e1855 * 0.0.0.0/0 0.0.0.0/0 /* set by containerlab */
0 0 ACCEPT 0 -- * br-1351328e1855 0.0.0.0/0 0.0.0.0/0 /* set by containerlab */
0 0 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0
```
</div>

1. The `br-a8b9fc8b33a2` bridge interface is the interface that backs up the containerlab's management network (`clab` docker network).
1. The `br-1351328e1855` bridge interface is the interface that backs up the containerlab's management network (`clab` docker network).

The rule will be removed together with the management network.

Expand Down
26 changes: 24 additions & 2 deletions nodes/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (b *bridge) GetLinkEndpointType() links.LinkEndpointType {
return links.LinkEndpointTypeBridge
}

// installIPTablesBridgeFwdRule installs `allow` rule for the traffic routed to ingress the bridge
// installIPTablesBridgeFwdRule installs `allow` rule for the traffic routed in and out of the bridge
// otherwise, communication over the bridge is not permitted on most systems.
func (b *bridge) installIPTablesBridgeFwdRule() (err error) {
f, err := firewall.NewFirewallClient()
Expand All @@ -138,5 +138,27 @@ func (b *bridge) installIPTablesBridgeFwdRule() (err error) {
}
log.Debugf("setting up bridge firewall rules using %s as the firewall interface", f.Name())

return f.InstallForwardingRules(b.Cfg.ShortName, "", definitions.ForwardChain)
r := definitions.FirewallRule{
Interface: b.Cfg.ShortName,
Direction: definitions.InDirection,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
Table: definitions.FilterTable,
Chain: definitions.ForwardChain,
}
err = f.InstallForwardingRules(r)
if err != nil {
return err
}

r = definitions.FirewallRule{
Interface: b.Cfg.ShortName,
Direction: definitions.OutDirection,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
Table: definitions.FilterTable,
Chain: definitions.ForwardChain,
}

return f.InstallForwardingRules(r)
}
66 changes: 64 additions & 2 deletions runtime/docker/firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,39 @@ func (d *DockerRuntime) deleteMgmtNetworkFwdRule() (err error) {
return err
}

return f.DeleteForwardingRules("", d.mgmt.Bridge, definitions.DockerUserChain)
// delete the rules with the management bridge listed as the outgoing interface with the allow action
// in the DOCKER-USER chain
r := definitions.FirewallRule{
Interface: d.mgmt.Bridge,
Direction: definitions.OutDirection,
Chain: definitions.DockerUserChain,
Table: definitions.FilterTable,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
}

err = f.DeleteForwardingRules(r)
if err != nil {
return err
}

// install the rules with the management bridge listed as the incoming interface with the allow action
// in the DOCKER-USER chain
r = definitions.FirewallRule{
Interface: d.mgmt.Bridge,
Direction: definitions.OutDirection,
Chain: definitions.DockerUserChain,
Table: definitions.FilterTable,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
}

err = f.DeleteForwardingRules(r)
if err != nil {
return err
}

return err
}

// installMgmtNetworkFwdRule installs the `allow` rule for traffic destined to the nodes
Expand All @@ -43,5 +75,35 @@ func (d *DockerRuntime) installMgmtNetworkFwdRule() (err error) {

// install the rules with the management bridge listed as the outgoing interface with the allow action
// in the DOCKER-USER chain
return f.InstallForwardingRules("", d.mgmt.Bridge, definitions.DockerUserChain)
r := definitions.FirewallRule{
Interface: d.mgmt.Bridge,
Direction: definitions.OutDirection,
Chain: definitions.DockerUserChain,
Table: definitions.FilterTable,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
}

err = f.InstallForwardingRules(r)
if err != nil {
return err
}

// install the rules with the management bridge listed as the incoming interface with the allow action
// in the DOCKER-USER chain
r = definitions.FirewallRule{
Interface: d.mgmt.Bridge,
Direction: definitions.InDirection,
Chain: definitions.DockerUserChain,
Table: definitions.FilterTable,
Action: definitions.AcceptAction,
Comment: definitions.ContainerlabComment,
}

err = f.InstallForwardingRules(r)
if err != nil {
return err
}

return err
}
22 changes: 18 additions & 4 deletions runtime/docker/firewall/definitions/definitions.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
package definitions

import "errors"
import (
"errors"
)

var ErrNotAvailable = errors.New("not available")

const (
DockerUserChain = "DOCKER-USER"
ForwardChain = "FORWARD"
FilterTable = "filter"
AcceptAction = "ACCEPT"
InDirection = "in"
OutDirection = "out"

IPTablesRuleComment = "set by containerlab"
ContainerlabComment = "set by containerlab"

IPTablesCommentMaxSize = 256
)

// ClabFirewall is the interface that all firewall clients must implement.
type ClabFirewall interface {
DeleteForwardingRules(inInterface, outInterface, chain string) error
InstallForwardingRules(inInterface, outInterface, chain string) error
DeleteForwardingRules(rule FirewallRule) error
InstallForwardingRules(rule FirewallRule) error
Name() string
}

type FirewallRule struct {
Chain string
Table string
Interface string
Direction string
Action string
Comment string
}
Loading

0 comments on commit 3e7a7c3

Please sign in to comment.