github.com/containerd/nerdctl@v1.7.7/pkg/containerutil/container_network_manager_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package containerutil
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"io/fs"
    23  	"path/filepath"
    24  
    25  	"github.com/containerd/containerd"
    26  	"github.com/containerd/containerd/oci"
    27  	"github.com/containerd/log"
    28  	"github.com/containerd/nerdctl/pkg/api/types"
    29  	"github.com/containerd/nerdctl/pkg/clientutil"
    30  	"github.com/containerd/nerdctl/pkg/dnsutil"
    31  	"github.com/containerd/nerdctl/pkg/dnsutil/hostsstore"
    32  	"github.com/containerd/nerdctl/pkg/netutil"
    33  	"github.com/containerd/nerdctl/pkg/resolvconf"
    34  	"github.com/containerd/nerdctl/pkg/rootlessutil"
    35  )
    36  
    37  type cniNetworkManagerPlatform struct {
    38  }
    39  
    40  // Verifies that the internal network settings are correct.
    41  func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
    42  	e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	if m.netOpts.MACAddress != "" {
    48  		macValidNetworks := []string{"bridge", "macvlan"}
    49  		if _, err := verifyNetworkTypes(e, m.netOpts.NetworkSlice, macValidNetworks); err != nil {
    50  			return err
    51  		}
    52  	}
    53  
    54  	return validateUtsSettings(m.netOpts)
    55  }
    56  
    57  // Performs setup actions required for the container with the given ID.
    58  func (m *cniNetworkManager) SetupNetworking(_ context.Context, _ string) error {
    59  	// NOTE: on non-Windows systems which support OCI hooks, CNI networking setup
    60  	// is performed via createRuntime and postCreate hooks whose logic can
    61  	// be found in the pkg/ocihook package.
    62  	return nil
    63  }
    64  
    65  // Performs any required cleanup actions for the given container.
    66  // Should only be called to revert any setup steps performed in setupNetworking.
    67  func (m *cniNetworkManager) CleanupNetworking(_ context.Context, _ containerd.Container) error {
    68  	// NOTE: on non-Windows systems which support OCI hooks, CNI networking setup
    69  	// is performed via createRuntime and postCreate hooks whose logic can
    70  	// be found in the pkg/ocihook package.
    71  	return nil
    72  }
    73  
    74  // Returns the set of NetworkingOptions which should be set as labels on the container.
    75  func (m *cniNetworkManager) InternalNetworkingOptionLabels(_ context.Context) (types.NetworkOptions, error) {
    76  	return m.netOpts, nil
    77  }
    78  
    79  // Returns a slice of `oci.SpecOpts` and `containerd.NewContainerOpts` which represent
    80  // the network specs which need to be applied to the container with the given ID.
    81  func (m *cniNetworkManager) ContainerNetworkingOpts(_ context.Context, containerID string) ([]oci.SpecOpts, []containerd.NewContainerOpts, error) {
    82  	opts := []oci.SpecOpts{}
    83  	cOpts := []containerd.NewContainerOpts{}
    84  
    85  	dataStore, err := clientutil.DataStore(m.globalOptions.DataRoot, m.globalOptions.Address)
    86  	if err != nil {
    87  		return nil, nil, err
    88  	}
    89  
    90  	stateDir, err := ContainerStateDirPath(m.globalOptions.Namespace, dataStore, containerID)
    91  	if err != nil {
    92  		return nil, nil, err
    93  	}
    94  
    95  	resolvConfPath := filepath.Join(stateDir, "resolv.conf")
    96  	if err := m.buildResolvConf(resolvConfPath); err != nil {
    97  		return nil, nil, err
    98  	}
    99  
   100  	// the content of /etc/hosts is created in OCI Hook
   101  	etcHostsPath, err := hostsstore.AllocHostsFile(dataStore, m.globalOptions.Namespace, containerID)
   102  	if err != nil {
   103  		return nil, nil, err
   104  	}
   105  	opts = append(opts, withCustomResolvConf(resolvConfPath), withCustomHosts(etcHostsPath))
   106  
   107  	if m.netOpts.UTSNamespace != UtsNamespaceHost {
   108  		// If no hostname is set, default to first 12 characters of the container ID.
   109  		hostname := m.netOpts.Hostname
   110  		if hostname == "" {
   111  			hostname = containerID
   112  			if len(hostname) > 12 {
   113  				hostname = hostname[0:12]
   114  			}
   115  		}
   116  		m.netOpts.Hostname = hostname
   117  
   118  		hostnameOpts, err := writeEtcHostnameForContainer(m.globalOptions, m.netOpts.Hostname, containerID)
   119  		if err != nil {
   120  			return nil, nil, err
   121  		}
   122  		if hostnameOpts != nil {
   123  			opts = append(opts, hostnameOpts...)
   124  		}
   125  	}
   126  
   127  	return opts, cOpts, nil
   128  }
   129  
   130  func (m *cniNetworkManager) buildResolvConf(resolvConfPath string) error {
   131  	var err error
   132  	slirp4Dns := []string{}
   133  	if rootlessutil.IsRootlessChild() {
   134  		slirp4Dns, err = dnsutil.GetSlirp4netnsDNS()
   135  		if err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	var (
   141  		nameServers   = m.netOpts.DNSServers
   142  		searchDomains = m.netOpts.DNSSearchDomains
   143  		dnsOptions    = m.netOpts.DNSResolvConfOptions
   144  	)
   145  
   146  	// Use host defaults if any DNS settings are missing:
   147  	if len(nameServers) == 0 || len(searchDomains) == 0 || len(dnsOptions) == 0 {
   148  		conf, err := resolvconf.Get()
   149  		if err != nil {
   150  			if !errors.Is(err, fs.ErrNotExist) {
   151  				return err
   152  			}
   153  			// if resolvConf file does't exist, using default resolvers
   154  			conf = &resolvconf.File{}
   155  			log.L.WithError(err).Debugf("resolvConf file doesn't exist on host")
   156  		}
   157  		conf, err = resolvconf.FilterResolvDNS(conf.Content, true)
   158  		if err != nil {
   159  			return err
   160  		}
   161  		if len(nameServers) == 0 {
   162  			nameServers = resolvconf.GetNameservers(conf.Content, resolvconf.IPv4)
   163  		}
   164  		if len(searchDomains) == 0 {
   165  			searchDomains = resolvconf.GetSearchDomains(conf.Content)
   166  		}
   167  		if len(dnsOptions) == 0 {
   168  			dnsOptions = resolvconf.GetOptions(conf.Content)
   169  		}
   170  	}
   171  
   172  	_, err = resolvconf.Build(resolvConfPath, append(slirp4Dns, nameServers...), searchDomains, dnsOptions)
   173  	return err
   174  }