diff options
author | Matthew Heon <mheon@redhat.com> | 2021-05-24 16:11:00 -0400 |
---|---|---|
committer | Matthew Heon <mheon@redhat.com> | 2021-05-26 15:03:30 -0400 |
commit | 533d88b6566974c979932bab071e9408580ac7f8 (patch) | |
tree | c39a1ed5e3c90c3ac61ce7cf3a566721fd990834 /vendor | |
parent | ac94be37e996fdebf44e5ace83be5219b9488ec4 (diff) | |
download | podman-533d88b6566974c979932bab071e9408580ac7f8.tar.gz podman-533d88b6566974c979932bab071e9408580ac7f8.tar.bz2 podman-533d88b6566974c979932bab071e9408580ac7f8.zip |
Add the option of Rootless CNI networking by default
When the containers.conf field "NetNS" is set to "Bridge" and the
"RootlessNetworking" field is set to "cni", Podman will now
handle rootless in the same way it does root - all containers
will be joined to a default CNI network, instead of exclusively
using slirp4netns.
If no CNI default network config is present for the user, one
will be auto-generated (this also works for root, but it won't be
nearly as common there since the package should already ship a
config).
I eventually hope to remove the "NetNS=Bridge" bit from
containers.conf, but let's get something in for Brent to work
with.
Signed-off-by: Matthew Heon <mheon@redhat.com>
Diffstat (limited to 'vendor')
-rw-r--r-- | vendor/github.com/containers/common/pkg/defaultnet/default_network.go | 222 | ||||
-rw-r--r-- | vendor/modules.txt | 1 |
2 files changed, 223 insertions, 0 deletions
diff --git a/vendor/github.com/containers/common/pkg/defaultnet/default_network.go b/vendor/github.com/containers/common/pkg/defaultnet/default_network.go new file mode 100644 index 000000000..9b32241d6 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/defaultnet/default_network.go @@ -0,0 +1,222 @@ +package defaultnet + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "regexp" + "text/template" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// TODO: A smarter implementation would make sure cni-podman0 was unused before +// making the default, and adjust if necessary +const networkTemplate = `{ + "cniVersion": "0.4.0", + "name": "{{{{.Name}}}}", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman0", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [{ "dst": "0.0.0.0/0" }], + "ranges": [ + [ + { + "subnet": "{{{{.Subnet}}}}", + "gateway": "{{{{.Gateway}}}}" + } + ] + ] + } + }, +{{{{- if (eq .Machine true) }}}} + { + "type": "podman-machine", + "capabilities": { + "portMappings": true + } + }, +{{{{- end}}}} + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall" + }, + { + "type": "tuning" + } + ] +} +` + +var ( + // Borrowed from Podman, modified to remove dashes and periods. + nameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_]*$") +) + +// Used to pass info into the template engine +type networkInfo struct { + Name string + Subnet string + Gateway string + Machine bool +} + +// The most trivial definition of a CNI network possible for our use here. +// We need the name, and nothing else. +type network struct { + Name string `json:"name"` +} + +// Create makes the CNI default network, if necessary. +// Accepts the name and subnet of the network to create (a standard template +// will be used, with these values plugged in), the configuration directory +// where CNI configs are stored (to verify if a named configuration already +// exists), an exists directory (where a sentinel file will be stored, to ensure +// the network is only made once), and an isMachine bool (to determine whether +// the machine block will be added to the config). +// Create first checks if a default network has already been created via the +// presence of a sentinel file. If it does exist, it returns immediately without +// error. +// It next checks if a CNI network with the given name already exists. In that +// case, it creates the sentinel file and returns without error. +// If neither of these are true, the default network is created. +func Create(name, subnet, configDir, existsDir string, isMachine bool) error { + // TODO: Should probably regex name to make sure it's valid. + if name == "" || subnet == "" || configDir == "" || existsDir == "" { + return errors.Errorf("must provide values for all arguments to MakeDefaultNetwork") + } + if !nameRegex.MatchString(name) { + return errors.Errorf("invalid default network name %s - letters, numbers, and underscores only", name) + } + + sentinelFile := filepath.Join(existsDir, "defaultCNINetExists") + + // Check if sentinel file exists, return immediately if it does. + if _, err := os.Stat(sentinelFile); err == nil { + return nil + } + + // Create the sentinel file if it doesn't exist, so subsequent checks + // don't need to go further. + file, err := os.Create(sentinelFile) + if err != nil { + return err + } + file.Close() + + // We may need to make the config dir. + if err := os.MkdirAll(configDir, 0755); err != nil && !os.IsExist(err) { + return errors.Wrapf(err, "error creating CNI configuration directory") + } + + // Check all networks in the CNI conflist. + files, err := ioutil.ReadDir(configDir) + if err != nil { + return errors.Wrapf(err, "error reading CNI configuration directory") + } + if len(files) > 0 { + configPaths := make([]string, 0, len(files)) + for _, path := range files { + if !path.IsDir() && filepath.Ext(path.Name()) == ".conflist" { + configPaths = append(configPaths, filepath.Join(configDir, path.Name())) + } + } + for _, config := range configPaths { + configName, err := getConfigName(config) + if err != nil { + logrus.Errorf("Error reading CNI configuration file: %v", err) + continue + } + if configName == name { + return nil + } + } + } + + // We need to make the config. + // Get subnet and gateway. + _, ipNet, err := net.ParseCIDR(subnet) + if err != nil { + return errors.Wrapf(err, "default network subnet %s is invalid", subnet) + } + + ones, bits := ipNet.Mask.Size() + if ones == bits { + return errors.Wrapf(err, "default network subnet %s is to small", subnet) + } + gateway := make(net.IP, len(ipNet.IP)) + // copy the subnet ip to the gateway so we can modify it + copy(gateway, ipNet.IP) + // the default gateway should be the first ip in the subnet + gateway[len(gateway)-1]++ + + netInfo := new(networkInfo) + netInfo.Name = name + netInfo.Gateway = gateway.String() + netInfo.Subnet = ipNet.String() + netInfo.Machine = isMachine + + templ, err := template.New("network_template").Delims("{{{{", "}}}}").Parse(networkTemplate) + if err != nil { + return errors.Wrapf(err, "error compiling template for default network") + } + var output bytes.Buffer + if err := templ.Execute(&output, netInfo); err != nil { + return errors.Wrapf(err, "error executing template for default network") + } + + // Next, we need to place the config on disk. + // Loop through possible indexes, with a limit of 100 attempts. + created := false + for i := 87; i < 187; i++ { + configFile, err := os.OpenFile(filepath.Join(configDir, fmt.Sprintf("%d-%s.conflist", i, name)), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) + if err != nil { + logrus.Infof("Attempt to create default CNI network config file failed: %v", err) + continue + } + defer configFile.Close() + + created = true + + // Success - file is open. Write our buffer to it. + if _, err := configFile.Write(output.Bytes()); err != nil { + return errors.Wrapf(err, "error writing default CNI config to file") + } + break + } + if !created { + return errors.Errorf("no available default network configuration file was found") + } + + return nil +} + +// Get the name of the configuration contained in a given conflist file. Accepts +// the full path of a .conflist CNI configuration. +func getConfigName(file string) (string, error) { + contents, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + config := new(network) + if err := json.Unmarshal(contents, config); err != nil { + return "", errors.Wrapf(err, "error decoding CNI configuration %s", filepath.Base(file)) + } + return config.Name, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index b4c2c6330..50f8e7338 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -102,6 +102,7 @@ github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/chown github.com/containers/common/pkg/completion github.com/containers/common/pkg/config +github.com/containers/common/pkg/defaultnet github.com/containers/common/pkg/filters github.com/containers/common/pkg/manifests github.com/containers/common/pkg/parse |