Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation required to enable Forwarding if it is already disabled #4172

Merged
merged 1 commit into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
39 changes: 39 additions & 0 deletions docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Disable Forwarding Config
arghosh93 marked this conversation as resolved.
Show resolved Hide resolved

OVN-Kubernetes allows to enable or disable IP forwarding for all traffic on OVN-Kubernetes managed interfaces (such as br-ex). By default forwarding is enabled and this allows host to forward traffic across OVN-Kubernetes managed interfaces. If forwarding is disabled then Kubernetes related traffic is still forwarded appropriately, but other IP traffic will not be routed by cluster nodes.

IP forwarding is implemented at cluster node level by modifying both iptables `FORWARD` chain and IP forwarding `sysctl` parameters.

- If forwarding is enabled(default) then system administrators need to set following sysctl parameters. An operator can be built to manage forwarding sysctl parameters based on forwarding mode. No extra iptables rules are added by OVN-Kubernetes to FORWARD chain while using this IP forwarding mode.

```
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
```

- IP forwarding can be disabled either by setting `disable-forwarding` command line option to `true` while starting ovnkube or by setting `disable-forwarding` to `true` in config file. If forwarding is disabled then system administrators need to set following sysctl parameters to stop routing other IP traffic. An operator can be built to manage forwarding sysctl parameters based on forwarding mode.

```
net.ipv4.ip_forward=0
net.ipv6.conf.all.forwarding=0
```

When IP forwarding is disabled, following sysctl parameters are modified by OVN-Kubernetes to allow forwarding Kubernetes related traffic on OVN-Kubernetes managed bridge interfaces and management port interface.

```
net.ipv4.conf.br-ex.forwarding=1
net.ipv4.conf.ovn-k8s-mp0.forwarding = 1
```

Additionally following iptables rules are added at FORWARD chain to forward clusterNetwork and serviceNetwork traffic to their intended destinations.

```
-A FORWARD -s 10.128.0.0/14 -j ACCEPT
-A FORWARD -d 10.128.0.0/14 -j ACCEPT
-A FORWARD -s 169.254.169.1 -j ACCEPT
-A FORWARD -d 169.254.169.1 -j ACCEPT
-A FORWARD -d 172.16.1.0/24 -j ACCEPT
-A FORWARD -s 172.16.1.0/24 -j ACCEPT
-A FORWARD -i breth1 -j DROP
-A FORWARD -o breth1 -j DROP
```
17 changes: 17 additions & 0 deletions go-controller/pkg/node/gateway_iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func appendIptRules(rules []nodeipt.Rule) error {
return nodeipt.AddRules(rules, true)
}

// deleteIptRules removes provided rules from the chain
func deleteIptRules(rules []nodeipt.Rule) error {
return nodeipt.DelRules(rules)
}

func getGatewayInitRules(chain string, proto iptables.Protocol) []nodeipt.Rule {
iptRules := []nodeipt.Rule{}
if chain == egressservice.Chain {
Expand Down Expand Up @@ -396,6 +401,12 @@ func initExternalBridgeServiceForwardingRules(cidrs []*net.IPNet) error {
return insertIptRules(getGatewayForwardRules(cidrs))
}

// delExternalBridgeServiceForwardingRules removes iptables rules which might
// have been added to disable forwarding
func delExternalBridgeServiceForwardingRules(cidrs []*net.IPNet) error {
return deleteIptRules(getGatewayForwardRules(cidrs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the extra abstraction here? can't we just call nodeipt.DelRules(getGatewayForwardRules(cidrs)) straight from here? its straightforward enough for readability that I don't see the need for abstracting

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done this to keep consistency with the initExternalBridgeServiceForwardingRules() and initExternalBridgeDropForwardingRules() function. init functions uses this abstraction mostly due to use of insertIptRules() in one and appendIptRules() in the other init function.

Copy link
Member

@tssurya tssurya Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but those functions have abstraction since we pass true/false for insert/append. We don't need that here which makes the layer useless right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

// initExternalBridgeDropRules sets up iptables rules to block forwarding
// in br-* interfaces (also for 2ndary bridge) - we block for v4 and v6 based on clusterStack
// -A FORWARD -i breth1 -j DROP
Expand All @@ -404,6 +415,12 @@ func initExternalBridgeDropForwardingRules(ifName string) error {
return appendIptRules(getGatewayDropRules(ifName))
}

// delExternalBridgeDropForwardingRules removes iptables rules which might
// have been added to disable forwarding
func delExternalBridgeDropForwardingRules(ifName string) error {
return deleteIptRules(getGatewayDropRules(ifName))
}

func getLocalGatewayFilterRules(ifname string, cidr *net.IPNet) []nodeipt.Rule {
// Allow packets to/from the gateway interface in case defaults deny
protocol := getIPTablesProtocol(cidr.IP.String())
Expand Down
6 changes: 5 additions & 1 deletion go-controller/pkg/node/gateway_localnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ func newLocalGateway(nodeName string, hostSubnets []*net.IPNet, gwNextHops []net
}
if config.Gateway.DisableForwarding {
if err := initExternalBridgeDropForwardingRules(exGwBridge.bridgeName); err != nil {
return fmt.Errorf("failed to add forwarding block rules for bridge %s: err %v", exGwBridge.bridgeName, err)
return fmt.Errorf("failed to add drop rules in forwarding table for bridge %s: err %v", exGwBridge.bridgeName, err)
}
} else {
tssurya marked this conversation as resolved.
Show resolved Hide resolved
if err := delExternalBridgeDropForwardingRules(exGwBridge.bridgeName); err != nil {
return fmt.Errorf("failed to delete drop rules in forwarding table for bridge %s: err %v", exGwBridge.bridgeName, err)
}
}
}
Expand Down
145 changes: 145 additions & 0 deletions go-controller/pkg/node/gateway_localnet_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
v4localnetGatewayIP = "10.244.0.1"
v6localnetGatewayIP = "fd00:96:1::1"
gwMAC = "0a:0b:0c:0d:0e:0f"
linkName = "breth0"
)

func initFakeNodePortWatcher(iptV4, iptV6 util.IPTablesHelper) *nodePortWatcher {
Expand Down Expand Up @@ -77,6 +78,29 @@ func startNodePortWatcher(n *nodePortWatcher, fakeClient *util.OVNNodeClientset,
ip, ipnet, _ := net.ParseCIDR(localHostNetEp)
n.nodeIPManager.addAddr(net.IPNet{IP: ip, Mask: ipnet.Mask})

// Add or delete iptables rules from FORWARD chain based on DisableForwarding. This is
// to imitate addition or deletion of iptales rules done in newNodePortWatcher().
var subnets []*net.IPNet
for _, subnet := range config.Default.ClusterSubnets {
subnets = append(subnets, subnet.CIDR)
}
subnets = append(subnets, config.Kubernetes.ServiceCIDRs...)
if config.Gateway.DisableForwarding {
if err := initExternalBridgeServiceForwardingRules(subnets); err != nil {
return fmt.Errorf("failed to add accept rules in forwarding table for bridge %s: err %v", linkName, err)
}
if err := initExternalBridgeDropForwardingRules(linkName); err != nil {
return fmt.Errorf("failed to add drop rules in forwarding table for bridge %s: err %v", linkName, err)
}
} else {
if err := delExternalBridgeServiceForwardingRules(subnets); err != nil {
return fmt.Errorf("failed to delete accept rules in forwarding table for bridge %s: err %v", linkName, err)
}
if err := delExternalBridgeDropForwardingRules(linkName); err != nil {
return fmt.Errorf("failed to delete drop rules in forwarding table for bridge %s: err %v", linkName, err)
}
}

// set up a controller to handle events on services to mock the nodeportwatcher bits
// in gateway.go and trigger code in gateway_shared_intf.go
_, err := n.watchFactory.AddServiceHandler(cache.ResourceEventHandlerFuncs{
Expand Down Expand Up @@ -2758,4 +2782,125 @@ var _ = Describe("Node Operations", func() {
Expect(err).NotTo(HaveOccurred())
})
})

Context("disable-forwarding", func() {
It("adds or removes iptables rules upon change in forwarding mode", func() {
app.Action = func(ctx *cli.Context) error {
config.Gateway.DisableForwarding = true
fakeOvnNode.start(ctx)
fNPW.watchFactory = fakeOvnNode.watcher
Expect(startNodePortWatcher(fNPW, fakeOvnNode.fakeClient, &fakeMgmtPortConfig)).To(Succeed())
expectedTables := map[string]util.FakeTable{
"nat": {
"PREROUTING": []string{
"-j OVN-KUBE-ETP",
"-j OVN-KUBE-EXTERNALIP",
"-j OVN-KUBE-NODEPORT",
},
"OUTPUT": []string{
"-j OVN-KUBE-EXTERNALIP",
"-j OVN-KUBE-NODEPORT",
"-j OVN-KUBE-ITP",
},
"OVN-KUBE-NODEPORT": []string{},
"OVN-KUBE-EXTERNALIP": []string{},
"POSTROUTING": []string{
"-j OVN-KUBE-EGRESS-SVC",
},
"OVN-KUBE-SNAT-MGMTPORT": []string{},
"OVN-KUBE-ETP": []string{},
"OVN-KUBE-ITP": []string{},
"OVN-KUBE-EGRESS-SVC": []string{},
},
"filter": {
"FORWARD": []string{
"-d 169.254.169.1 -j ACCEPT",
"-s 169.254.169.1 -j ACCEPT",
"-d 172.16.1.0/24 -j ACCEPT",
"-s 172.16.1.0/24 -j ACCEPT",
"-d 10.1.0.0/16 -j ACCEPT",
"-s 10.1.0.0/16 -j ACCEPT",
"-i breth0 -j DROP",
"-o breth0 -j DROP",
},
},
"mangle": {
"OUTPUT": []string{
"-j OVN-KUBE-ITP",
},
"OVN-KUBE-ITP": []string{},
},
}

f4 := iptV4.(*util.FakeIPTables)
err := f4.MatchState(expectedTables)
Expect(err).NotTo(HaveOccurred())
expectedTables = map[string]util.FakeTable{
"nat": {},
"filter": {},
"mangle": {},
}
f6 := iptV6.(*util.FakeIPTables)
err = f6.MatchState(expectedTables)
Expect(err).NotTo(HaveOccurred())

// Enable forwarding and test deletion of iptables rules from FORWARD chain
config.Gateway.DisableForwarding = false
fNPW.watchFactory = fakeOvnNode.watcher
Expect(startNodePortWatcher(fNPW, fakeOvnNode.fakeClient, &fakeMgmtPortConfig)).To(Succeed())
expectedTables = map[string]util.FakeTable{
"nat": {
"PREROUTING": []string{
"-j OVN-KUBE-ETP",
"-j OVN-KUBE-EXTERNALIP",
"-j OVN-KUBE-NODEPORT",
},
"OUTPUT": []string{
"-j OVN-KUBE-EXTERNALIP",
"-j OVN-KUBE-NODEPORT",
"-j OVN-KUBE-ITP",
},
"OVN-KUBE-NODEPORT": []string{},
"OVN-KUBE-EXTERNALIP": []string{},
"POSTROUTING": []string{
"-j OVN-KUBE-EGRESS-SVC",
},
"OVN-KUBE-SNAT-MGMTPORT": []string{},
"OVN-KUBE-ETP": []string{},
"OVN-KUBE-ITP": []string{},
"OVN-KUBE-EGRESS-SVC": []string{},
},
"filter": {
"FORWARD": []string{},
},
"mangle": {
"OUTPUT": []string{
"-j OVN-KUBE-ITP",
},
"OVN-KUBE-ITP": []string{},
},
}

f4 = iptV4.(*util.FakeIPTables)
err = f4.MatchState(expectedTables)
Expect(err).NotTo(HaveOccurred())
expectedTables = map[string]util.FakeTable{
"nat": {},
"filter": {},
"mangle": {},
}
f6 = iptV6.(*util.FakeIPTables)
err = f6.MatchState(expectedTables)
Expect(err).NotTo(HaveOccurred())
return nil
}
err := app.Run([]string{
app.Name,
"--cluster-subnets=" + "10.1.0.0/16",
"--k8s-service-cidrs=" + "172.16.1.0/24",
})
Expect(err).NotTo(HaveOccurred())
})
})

})
27 changes: 19 additions & 8 deletions go-controller/pkg/node/gateway_shared_intf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1760,7 +1760,11 @@ func newSharedGateway(nodeName string, subnets []*net.IPNet, gwNextHops []net.IP
}
if config.Gateway.DisableForwarding {
if err := initExternalBridgeDropForwardingRules(exGwBridge.bridgeName); err != nil {
return fmt.Errorf("failed to add forwarding block rules for bridge %s: err %v", exGwBridge.bridgeName, err)
return fmt.Errorf("failed to add drop rules in forwarding table for bridge %s: err %v", exGwBridge.bridgeName, err)
}
} else {
if err := delExternalBridgeDropForwardingRules(exGwBridge.bridgeName); err != nil {
return fmt.Errorf("failed to delete drop rules in forwarding table for bridge %s: err %v", exGwBridge.bridgeName, err)
}
}
}
Expand Down Expand Up @@ -1865,17 +1869,24 @@ func newNodePortWatcher(gwBridge *bridgeConfiguration, ofm *openflowManager,
}
}

var subnets []*net.IPNet
for _, subnet := range config.Default.ClusterSubnets {
subnets = append(subnets, subnet.CIDR)
}
subnets = append(subnets, config.Kubernetes.ServiceCIDRs...)
if config.Gateway.DisableForwarding {
var subnets []*net.IPNet
for _, subnet := range config.Default.ClusterSubnets {
subnets = append(subnets, subnet.CIDR)
}
subnets = append(subnets, config.Kubernetes.ServiceCIDRs...)
if err := initExternalBridgeServiceForwardingRules(subnets); err != nil {
return nil, fmt.Errorf("failed to add forwarding rules for bridge %s: err %v", gwBridge.bridgeName, err)
return nil, fmt.Errorf("failed to add accept rules in forwarding table for bridge %s: err %v", gwBridge.bridgeName, err)
}
if err := initExternalBridgeDropForwardingRules(gwBridge.bridgeName); err != nil {
return nil, fmt.Errorf("failed to add forwarding rules for bridge %s: err %v", gwBridge.bridgeName, err)
return nil, fmt.Errorf("failed to add drop rules in forwarding table for bridge %s: err %v", gwBridge.bridgeName, err)
}
} else {
if err := delExternalBridgeServiceForwardingRules(subnets); err != nil {
return nil, fmt.Errorf("failed to delete accept rules in forwarding table for bridge %s: err %v", gwBridge.bridgeName, err)
}
if err := delExternalBridgeDropForwardingRules(gwBridge.bridgeName); err != nil {
return nil, fmt.Errorf("failed to delete drop rules in forwarding table for bridge %s: err %v", gwBridge.bridgeName, err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ nav:
- Run OVN-Kubernetes From Release Image: getting-started/running-release.md
- Run OVN-Kubernetes From RPM: getting-started/running-rpm.md
- CLI Guide: getting-started/cli-guide.md
- Configuration Guide: developer-guide/configuration.md
- Configuration Guide: getting-started/configuration.md
- Developer Guide:
- Contributing Guide: governance/CONTRIBUTING.md
- Reviewing Guide: governance/REVIEWING.md
Expand Down