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

Selectively disable iptables proxy's bridge-nf-call-iptables=1 behavior #20647

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/admin/network-plugins.md
Expand Up @@ -42,6 +42,12 @@ The kubelet has a single default network plugin, and a default network common to
* `network-plugin-dir`: Kubelet probes this directory for plugins on startup
* `network-plugin`: The network plugin to use from `network-plugin-dir`. It must match the name reported by a plugin probed from the plugin directory. For CNI plugins, this is simply "cni".

## Network Plugin Requirements

Besides providing the [`NetworkPlugin` interface](../../pkg/kubelet/network/plugins.go) to configure and clean up pod networking, the plugin may also need specific support for kube-proxy. The iptables proxy obviously depends on iptables, and the plugin may need to ensure that container traffic is made available to iptables. For example, if the plugin connects containers to a Linux bridge, the plugin must set the `net/bridge/bridge-nf-call-iptables` sysctl to `1` to ensure that the iptables proxy functions correctly. If the plugin does not use a Linux bridge (but instead something like Open vSwitch or some other mechanism) it should ensure container traffic is appropriately routed for the proxy.
Copy link
Member

Choose a reason for hiding this comment

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

does this imply that the plugin will need to know parts of the kube-proxy config (at least the mode)? is there a way for the plugins to get that information currently?

Copy link
Member Author

Choose a reason for hiding this comment

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

does this imply that the plugin will need to know parts of the kube-proxy config (at least the mode)? is there a way for the plugins to get that information currently?

Yes, plugins will obviously need to know about proxy configuration if their networking setup will have an effect on the proxy. We were just naive before as assumed everyone did everything the same way :(

Copy link
Member

Choose a reason for hiding this comment

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

ok, so how would a plugin find out (for example) if you were running the kube-proxy in userspace or iptables mode?


By default if no kubelet network plugin is specified, the `noop` plugin is used, which sets `net/bridge/bridge-nf-call-iptables=1` to ensure simple configurations (like docker with a bridge) work correctly with the iptables proxy.

### Exec

Place plugins in `network-plugin-dir/plugin-name/plugin-name`, i.e if you have a bridge plugin and `network-plugin-dir` is `/usr/lib/kubernetes`, you'd place the bridge plugin executable at `/usr/lib/kubernetes/bridge/bridge`. See [this comment](../../pkg/kubelet/network/exec/exec.go) for more details.
Expand Down
20 changes: 20 additions & 0 deletions pkg/kubelet/network/plugins.go
Expand Up @@ -28,6 +28,8 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
utilexec "k8s.io/kubernetes/pkg/util/exec"
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
"k8s.io/kubernetes/pkg/util/validation"
)

Expand Down Expand Up @@ -93,6 +95,9 @@ func InitNetworkPlugin(plugins []NetworkPlugin, networkPluginName string, host H
if networkPluginName == "" {
// default to the no_op plugin
plug := &noopNetworkPlugin{}
if err := plug.Init(host); err != nil {
return nil, err
}
return plug, nil
}

Expand Down Expand Up @@ -135,7 +140,22 @@ func UnescapePluginName(in string) string {
type noopNetworkPlugin struct {
}

const sysctlBridgeCallIptables = "net/bridge/bridge-nf-call-iptables"

func (plugin *noopNetworkPlugin) Init(host Host) error {
// Set bridge-nf-call-iptables=1 to maintain compatibility with older
// kubernetes versions to ensure the iptables-based kube proxy functions
// correctly. Other plugins are responsible for setting this correctly
// depending on whether or not they connect containers to Linux bridges
// or use some other mechanism (ie, SDN vswitch).

// Ensure the netfilter module is loaded on kernel >= 3.18; previously
// it was built-in.
utilexec.New().Command("modprobe", "br-netfilter").CombinedOutput()
if err := utilsysctl.SetSysctl(sysctlBridgeCallIptables, 1); err != nil {
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err)
}

return nil
}

Expand Down
19 changes: 13 additions & 6 deletions pkg/proxy/iptables/proxier.go
Expand Up @@ -26,6 +26,7 @@ import (
"encoding/base32"
"fmt"
"net"
"os"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -190,12 +191,18 @@ func NewProxier(ipt utiliptables.Interface, exec utilexec.Interface, syncPeriod
return nil, fmt.Errorf("can't set sysctl %s: %v", sysctlRouteLocalnet, err)
Copy link
Member

Choose a reason for hiding this comment

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

is this sysctl call still appropriate for the proxier to set, rather than the network plugin?

Copy link
Contributor

Choose a reason for hiding this comment

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

We can move it to a plugin when we start using plugins by default. For now I'd like to keep things as they are unless absolutely necessary. the no-op plugin should really no-op.

Copy link
Member

Choose a reason for hiding this comment

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

Worse, there was a separate PR, somewhere, that made the no-op plugin go away and be a nil pointer.

}

// Load the module. It's OK if this fails (e.g. the module is not present)
// because we'll catch the error on the sysctl, which is what we actually
// care about.
exec.Command("modprobe", "br-netfilter").CombinedOutput()
if err := utilsysctl.SetSysctl(sysctlBridgeCallIptables, 1); err != nil {
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIptables, err)
// Proxy needs br_netfilter and bridge-nf-call-iptables=1 when containers
// are connected to a Linux bridge (but not SDN bridges). Until most
// plugins handle this, log when config is missing
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you log something here in case someone already has a plugin that needs br-netfilter and they're getting it for free?

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you log something here in case someone already has a plugin that needs br-netfilter and they're getting it for free?

Ok, but not quite sure what you're asking for... logging that the load failed? it's expected to fail in a bunch of systems because br-netfilter used to be built-in to the bridge module, but got split sometime in the late 3.1x timeframe. So new systems need this, older ones don't.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you just run modinfo and log something like: br-netfilter module [not] loaded?

Copy link
Member

Choose a reason for hiding this comment

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

+1

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I'll doing that.

warnBrNetfilter := false
if _, err := os.Stat("/sys/module/br_netfilter"); os.IsNotExist(err) {
warnBrNetfilter = true
}
if val, err := utilsysctl.GetSysctl(sysctlBridgeCallIptables); err == nil && val != 1 {
warnBrNetfilter = true
}
if warnBrNetfilter {
glog.Infof("missing br-netfilter module or unset br-nf-call-iptables; proxy may not work as intended")
}

// Generate the masquerade mark to use for SNAT rules.
Expand Down