use rootless netns from c/common

Use the new rootlessnetns logic from c/common, drop the podman code
here and make use of the new much simpler API.

ref: https://github.com/containers/common/pull/1761

[NO NEW TESTS NEEDED]

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2023-11-24 18:00:24 +01:00
parent 605a29a714
commit a687c38860
38 changed files with 1171 additions and 1072 deletions

View File

@@ -26,8 +26,10 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/version"
@@ -80,6 +82,16 @@ func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData [
c.Env = append(c.Env, "XDG_RUNTIME_DIR=")
}
// The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put
// /usr/sbin in $PATH for rootless users. This will break rootless networking completely.
// We might break existing users and we cannot expect everyone to change their $PATH so
// let's add /usr/sbin to $PATH ourselves.
path := os.Getenv("PATH")
if !strings.Contains(path, "/usr/sbin") {
path += ":/usr/sbin"
c.Env = append(c.Env, "PATH="+path)
}
err := c.Run()
if err != nil {
return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes())

View File

@@ -16,6 +16,7 @@ import (
"time"
"github.com/containernetworking/cni/libcni"
"github.com/containers/common/libnetwork/internal/rootlessnetns"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/version"
@@ -53,6 +54,9 @@ type cniNetwork struct {
// networks is a map with loaded networks, the key is the network name
networks map[string]*network
// rootlessNetns is used for the rootless network setup/teardown
rootlessNetns *rootlessnetns.Netns
}
type network struct {
@@ -65,21 +69,14 @@ type network struct {
type InitConfig struct {
// CNIConfigDir is directory where the cni config files are stored.
CNIConfigDir string
// CNIPluginDirs is a list of directories where cni should look for the plugins.
CNIPluginDirs []string
// RunDir is a directory where temporary files can be stored.
RunDir string
// DefaultNetwork is the name for the default network.
DefaultNetwork string
// DefaultSubnet is the default subnet for the default network.
DefaultSubnet string
// DefaultsubnetPools contains the subnets which must be used to allocate a free subnet by network create
DefaultsubnetPools []config.SubnetPool
// IsMachine describes whenever podman runs in a podman machine environment.
IsMachine bool
// Config containers.conf options
Config *config.Config
}
// NewCNINetworkInterface creates the ContainerNetwork interface for the CNI backend.
@@ -96,12 +93,12 @@ func NewCNINetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) {
return nil, err
}
defaultNetworkName := conf.DefaultNetwork
defaultNetworkName := conf.Config.Network.DefaultNetwork
if defaultNetworkName == "" {
defaultNetworkName = types.DefaultNetworkName
}
defaultSubnet := conf.DefaultSubnet
defaultSubnet := conf.Config.Network.DefaultSubnet
if defaultSubnet == "" {
defaultSubnet = types.DefaultSubnet
}
@@ -110,21 +107,30 @@ func NewCNINetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) {
return nil, fmt.Errorf("failed to parse default subnet: %w", err)
}
defaultSubnetPools := conf.DefaultsubnetPools
defaultSubnetPools := conf.Config.Network.DefaultSubnetPools
if defaultSubnetPools == nil {
defaultSubnetPools = config.DefaultSubnetPools
}
cni := libcni.NewCNIConfig(conf.CNIPluginDirs, &cniExec{})
var netns *rootlessnetns.Netns
if unshare.IsRootless() {
netns, err = rootlessnetns.New(conf.RunDir, rootlessnetns.CNI, conf.Config)
if err != nil {
return nil, err
}
}
cni := libcni.NewCNIConfig(conf.Config.Network.CNIPluginDirs.Values, &cniExec{})
n := &cniNetwork{
cniConfigDir: conf.CNIConfigDir,
cniPluginDirs: conf.CNIPluginDirs,
cniPluginDirs: conf.Config.Network.CNIPluginDirs.Get(),
cniConf: cni,
defaultNetwork: defaultNetworkName,
defaultSubnet: defaultNet,
defaultsubnetPools: defaultSubnetPools,
isMachine: conf.IsMachine,
lock: lock,
rootlessNetns: netns,
}
return n, nil

View File

@@ -39,61 +39,71 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
return nil, fmt.Errorf("failed to set the loopback adapter up: %w", err)
}
var retErr error
teardownOpts := options
teardownOpts.Networks = map[string]types.PerNetworkOptions{}
// make sure to teardown the already connected networks on error
defer func() {
if retErr != nil {
if len(teardownOpts.Networks) > 0 {
err := n.teardown(namespacePath, types.TeardownOptions(teardownOpts))
if err != nil {
logrus.Warn(err)
results := make(map[string]types.StatusBlock, len(options.Networks))
setup := func() error {
var retErr error
teardownOpts := options
teardownOpts.Networks = map[string]types.PerNetworkOptions{}
// make sure to teardown the already connected networks on error
defer func() {
if retErr != nil {
if len(teardownOpts.Networks) > 0 {
err := n.teardown(namespacePath, types.TeardownOptions(teardownOpts))
if err != nil {
logrus.Warn(err)
}
}
}
}()
ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings)
if err != nil {
return err
}
}()
ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings)
if err != nil {
return nil, err
}
for name, netOpts := range options.Networks {
netOpts := netOpts
network := n.networks[name]
rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts)
results := make(map[string]types.StatusBlock, len(options.Networks))
for name, netOpts := range options.Networks {
netOpts := netOpts
network := n.networks[name]
rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts)
// If we have more than one static ip we need parse the ips via runtime config,
// make sure to add the ips capability to the first plugin otherwise it doesn't get the ips
if len(netOpts.StaticIPs) > 0 && !network.cniNet.Plugins[0].Network.Capabilities["ips"] {
caps := make(map[string]interface{})
caps["capabilities"] = map[string]bool{"ips": true}
network.cniNet.Plugins[0], retErr = libcni.InjectConf(network.cniNet.Plugins[0], caps)
if retErr != nil {
return nil, retErr
// If we have more than one static ip we need parse the ips via runtime config,
// make sure to add the ips capability to the first plugin otherwise it doesn't get the ips
if len(netOpts.StaticIPs) > 0 && !network.cniNet.Plugins[0].Network.Capabilities["ips"] {
caps := make(map[string]interface{})
caps["capabilities"] = map[string]bool{"ips": true}
network.cniNet.Plugins[0], retErr = libcni.InjectConf(network.cniNet.Plugins[0], caps)
if retErr != nil {
return retErr
}
}
}
var res cnitypes.Result
res, retErr = n.cniConf.AddNetworkList(context.Background(), network.cniNet, rt)
// Add this network to teardown opts since it is now connected.
// Also add this if an errors was returned since we want to call teardown on this regardless.
teardownOpts.Networks[name] = netOpts
if retErr != nil {
return nil, retErr
}
var res cnitypes.Result
res, retErr = n.cniConf.AddNetworkList(context.Background(), network.cniNet, rt)
// Add this network to teardown opts since it is now connected.
// Also add this if an errors was returned since we want to call teardown on this regardless.
teardownOpts.Networks[name] = netOpts
if retErr != nil {
return retErr
}
logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, res)
var status types.StatusBlock
status, retErr = CNIResultToStatus(res)
if retErr != nil {
return nil, retErr
logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, res)
var status types.StatusBlock
status, retErr = CNIResultToStatus(res)
if retErr != nil {
return retErr
}
results[name] = status
}
results[name] = status
return nil
}
return results, nil
if n.rootlessNetns != nil {
err = n.rootlessNetns.Setup(len(options.Networks), setup)
} else {
err = setup()
}
return results, err
}
// CNIResultToStatus convert the cni result to status block
@@ -225,28 +235,39 @@ func (n *cniNetwork) teardown(namespacePath string, options types.TeardownOption
}
var multiErr *multierror.Error
for name, netOpts := range options.Networks {
netOpts := netOpts
rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts)
teardown := func() error {
for name, netOpts := range options.Networks {
netOpts := netOpts
rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, &netOpts)
cniConfList, newRt, err := getCachedNetworkConfig(n.cniConf, name, rt)
if err == nil {
rt = newRt
} else {
logrus.Warnf("Failed to load cached network config: %v, falling back to loading network %s from disk", err, name)
network := n.networks[name]
if network == nil {
multiErr = multierror.Append(multiErr, fmt.Errorf("network %s: %w", name, types.ErrNoSuchNetwork))
continue
cniConfList, newRt, err := getCachedNetworkConfig(n.cniConf, name, rt)
if err == nil {
rt = newRt
} else {
logrus.Warnf("Failed to load cached network config: %v, falling back to loading network %s from disk", err, name)
network := n.networks[name]
if network == nil {
multiErr = multierror.Append(multiErr, fmt.Errorf("network %s: %w", name, types.ErrNoSuchNetwork))
continue
}
cniConfList = network.cniNet
}
cniConfList = network.cniNet
}
err = n.cniConf.DelNetworkList(context.Background(), cniConfList, rt)
if err != nil {
multiErr = multierror.Append(multiErr, err)
err = n.cniConf.DelNetworkList(context.Background(), cniConfList, rt)
if err != nil {
multiErr = multierror.Append(multiErr, err)
}
}
return nil
}
if n.rootlessNetns != nil {
err = n.rootlessNetns.Teardown(len(options.Networks), teardown)
} else {
err = teardown()
}
multiErr = multierror.Append(multiErr, err)
return multiErr.ErrorOrNil()
}
@@ -267,3 +288,10 @@ func getCachedNetworkConfig(cniConf *libcni.CNIConfig, name string, rt *libcni.R
}
return cniConfList, rt, nil
}
func (n *cniNetwork) RunInRootlessNetns(toRun func() error) error {
if n.rootlessNetns == nil {
return types.ErrNotRootlessNetns
}
return n.rootlessNetns.Run(n.lock, toRun)
}