github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/app_sandbox.go (about)

     1  // Copyright 2016 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/appc/spec/schema/types"
    24  	"github.com/opencontainers/selinux/go-selinux/label"
    25  	"github.com/rkt/rkt/common"
    26  	"github.com/rkt/rkt/pkg/lock"
    27  	"github.com/rkt/rkt/pkg/pod"
    28  	"github.com/rkt/rkt/pkg/user"
    29  	"github.com/rkt/rkt/stage0"
    30  	"github.com/rkt/rkt/store/imagestore"
    31  	"github.com/rkt/rkt/store/treestore"
    32  	"github.com/spf13/cobra"
    33  )
    34  
    35  var (
    36  	cmdAppSandbox = &cobra.Command{
    37  		Use:   "sandbox",
    38  		Short: "Create an empty pod application sandbox",
    39  		Long:  "Initializes an empty pod having no applications.",
    40  		Run:   runWrapper(runAppSandbox),
    41  	}
    42  	flagAppPorts        appPortList
    43  	flagAnnotations     kvMap
    44  	flagUserAnnotations kvMap
    45  	flagLabels          kvMap
    46  )
    47  
    48  func init() {
    49  	cmdApp.AddCommand(cmdAppSandbox)
    50  
    51  	addStage1ImageFlags(cmdAppSandbox.Flags())
    52  	cmdAppSandbox.Flags().StringVar(&flagUUIDFileSave, "uuid-file-save", "", "write out pod UUID to specified file")
    53  	cmdAppSandbox.Flags().Var(&flagNet, "net", "configure the pod's networking. Optionally, pass a list of user-configured networks to load and set arguments to pass to each network, respectively. Syntax: --net[=n[:args], ...]")
    54  	cmdAppSandbox.Flags().BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem")
    55  	cmdAppSandbox.Flags().Var(&flagDNS, "dns", "name servers to write in /etc/resolv.conf")
    56  	cmdAppSandbox.Flags().Var(&flagDNSSearch, "dns-search", "DNS search domains to write in /etc/resolv.conf")
    57  	cmdAppSandbox.Flags().Var(&flagDNSOpt, "dns-opt", "DNS options to write in /etc/resolv.conf")
    58  	cmdAppSandbox.Flags().StringVar(&flagDNSDomain, "dns-domain", "", "DNS domain to write in /etc/resolv.conf")
    59  	cmdAppSandbox.Flags().Var(&flagHostsEntries, "hosts-entry", "Entries to add to the pod-wide /etc/hosts. Pass 'host' to use the host's /etc/hosts")
    60  	cmdAppSandbox.Flags().StringVar(&flagHostname, "hostname", "", `pod's hostname. If empty, it will be "rkt-$PODUUID"`)
    61  	cmdAppSandbox.Flags().Var(&flagAppPorts, "port", "ports to forward. format: \"name:proto:podPort:hostIP:hostPort\"")
    62  
    63  	flagAppPorts = appPortList{}
    64  	cmdAppSandbox.Flags().Var(&flagAnnotations, "annotation", "optional, set the pod's annotations in the form of key=value")
    65  	cmdAppSandbox.Flags().Var(&flagUserAnnotations, "user-annotation", "optional, set the pod's user annotations in the form of key=value")
    66  	cmdAppSandbox.Flags().Var(&flagLabels, "user-label", "optional, set the pod's label in the form of key=value")
    67  }
    68  
    69  func runAppSandbox(cmd *cobra.Command, args []string) int {
    70  	s, err := imagestore.NewStore(storeDir())
    71  	if err != nil {
    72  		stderr.PrintE("cannot open store", err)
    73  		return 1
    74  	}
    75  
    76  	ts, err := treestore.NewStore(treeStoreDir(), s)
    77  	if err != nil {
    78  		stderr.PrintE("cannot open treestore", err)
    79  		return 1
    80  	}
    81  
    82  	config, err := getConfig()
    83  	if err != nil {
    84  		stderr.PrintE("cannot get configuration", err)
    85  		return 1
    86  	}
    87  
    88  	s1img, err := getStage1Hash(s, ts, config)
    89  	if err != nil {
    90  		stderr.Error(err)
    91  		return 1
    92  	}
    93  
    94  	p, err := pod.NewPod(getDataDir())
    95  	if err != nil {
    96  		stderr.PrintE("error creating new pod", err)
    97  		return 1
    98  	}
    99  
   100  	if flagUUIDFileSave != "" {
   101  		if err := pod.WriteUUIDToFile(p.UUID, flagUUIDFileSave); err != nil {
   102  			stderr.PrintE("error saving pod UUID to file", err)
   103  			return 1
   104  		}
   105  	}
   106  
   107  	processLabel, mountLabel, err := label.InitLabels([]string{})
   108  	if err != nil {
   109  		stderr.PrintE("error initialising SELinux", err)
   110  		return 1
   111  	}
   112  
   113  	p.MountLabel = mountLabel
   114  	cfg := stage0.CommonConfig{
   115  		DataDir:      getDataDir(),
   116  		MountLabel:   mountLabel,
   117  		ProcessLabel: processLabel,
   118  		Store:        s,
   119  		TreeStore:    ts,
   120  		Stage1Image:  *s1img,
   121  		UUID:         p.UUID,
   122  		Debug:        globalFlags.Debug,
   123  		Mutable:      true,
   124  		Annotations:  parseAnnotations(&flagAnnotations),
   125  	}
   126  
   127  	ovlOk := true
   128  	if err := common.PathSupportsOverlay(getDataDir()); err != nil {
   129  		if oerr, ok := err.(common.ErrOverlayUnsupported); ok {
   130  			stderr.Printf("disabling overlay support: %q", oerr.Error())
   131  			ovlOk = false
   132  		} else {
   133  			stderr.PrintE("error determining overlay support", err)
   134  			return 1
   135  		}
   136  	}
   137  
   138  	useOverlay := !flagNoOverlay && ovlOk
   139  
   140  	pcfg := stage0.PrepareConfig{
   141  		CommonConfig:    &cfg,
   142  		UseOverlay:      useOverlay,
   143  		PrivateUsers:    user.NewBlankUidRange(),
   144  		Apps:            &rktApps,
   145  		Ports:           []types.ExposedPort(flagAppPorts),
   146  		UserAnnotations: parseUserAnnotations(&flagUserAnnotations),
   147  		UserLabels:      parseLabels(&flagLabels),
   148  	}
   149  
   150  	if globalFlags.Debug {
   151  		stage0.InitDebug()
   152  	}
   153  
   154  	keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock)
   155  	if err != nil {
   156  		stderr.PrintE("cannot get shared prepare lock", err)
   157  		return 1
   158  	}
   159  
   160  	err = stage0.Prepare(pcfg, p.Path(), p.UUID)
   161  	if err != nil {
   162  		stderr.PrintE("error setting up stage0", err)
   163  		keyLock.Close()
   164  		return 1
   165  	}
   166  	keyLock.Close()
   167  
   168  	// get the lock fd for run
   169  	lfd, err := p.Fd()
   170  	if err != nil {
   171  		stderr.PrintE("error getting pod lock fd", err)
   172  		return 1
   173  	}
   174  
   175  	// skip prepared by jumping directly to run, we own this pod
   176  	if err := p.ToRun(); err != nil {
   177  		stderr.PrintE("unable to transition to run", err)
   178  		return 1
   179  	}
   180  
   181  	rktgid, err := common.LookupGid(common.RktGroup)
   182  	if err != nil {
   183  		stderr.Printf("group %q not found, will use default gid when rendering images", common.RktGroup)
   184  		rktgid = -1
   185  	}
   186  
   187  	DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain)
   188  	if err != nil {
   189  		stderr.PrintE("error with dns flags", err)
   190  		return 1
   191  	}
   192  
   193  	rcfg := stage0.RunConfig{
   194  		CommonConfig:         &cfg,
   195  		Net:                  flagNet,
   196  		LockFd:               lfd,
   197  		Interactive:          false,
   198  		DNSConfMode:          DNSConfMode,
   199  		DNSConfig:            DNSConfig,
   200  		MDSRegister:          false,
   201  		LocalConfig:          globalFlags.LocalConfigDir,
   202  		RktGid:               rktgid,
   203  		Hostname:             flagHostname,
   204  		InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(),
   205  		InsecurePaths:        globalFlags.InsecureFlags.SkipPaths(),
   206  		InsecureSeccomp:      globalFlags.InsecureFlags.SkipSeccomp(),
   207  		UseOverlay:           useOverlay,
   208  		HostsEntries:         *HostsEntries,
   209  	}
   210  
   211  	_, manifest, err := p.PodManifest()
   212  	if err != nil {
   213  		stderr.PrintE("cannot get the pod manifest", err)
   214  		return 1
   215  	}
   216  	rcfg.Apps = manifest.Apps
   217  	stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns
   218  
   219  	return 1
   220  }
   221  
   222  /*
   223   * The sandbox uses a different style of port forwarding - instead of mapping
   224   * from port to app (via name), we just map ports directly.
   225   *
   226   * The format is name:proto:podPort:hostIP:hostPort
   227   * e.g. http:tcp:8080:0.0.0.0:80
   228   */
   229  type appPortList []types.ExposedPort
   230  
   231  func (apl *appPortList) Set(s string) error {
   232  	parts := strings.SplitN(s, ":", 5)
   233  	if len(parts) != 5 {
   234  		return fmt.Errorf("--port invalid format")
   235  	}
   236  
   237  	// parsey parsey
   238  	name, err := types.NewACName(parts[0])
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	proto := parts[1]
   244  	switch proto {
   245  	case "tcp", "udp":
   246  	default:
   247  		return fmt.Errorf("invalid protocol %q", proto)
   248  	}
   249  
   250  	p, err := strconv.ParseUint(parts[2], 10, 16)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	podPortNo := uint(p)
   255  
   256  	ip := net.ParseIP(parts[3])
   257  	if ip == nil {
   258  		return fmt.Errorf("could not parse IP %q", ip)
   259  	}
   260  
   261  	p, err = strconv.ParseUint(parts[4], 10, 16)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	hostPortNo := uint(p)
   266  
   267  	podSide := types.Port{
   268  		Name:            *name,
   269  		Protocol:        proto,
   270  		Port:            podPortNo,
   271  		Count:           1,
   272  		SocketActivated: false,
   273  	}
   274  
   275  	hostSide := types.ExposedPort{
   276  		Name:     *name,
   277  		HostPort: hostPortNo,
   278  		HostIP:   ip,
   279  		PodPort:  &podSide,
   280  	}
   281  
   282  	*apl = append(*apl, hostSide)
   283  	return nil
   284  }
   285  
   286  func (apl *appPortList) String() string {
   287  	ss := make([]string, 0, len(*apl))
   288  	for _, p := range *apl {
   289  		ss = append(ss, fmt.Sprintf("%s:%s:%d:%s:%d",
   290  			p.Name, p.PodPort.Protocol, p.PodPort.Port,
   291  			p.HostIP, p.HostPort))
   292  
   293  	}
   294  	return strings.Join(ss, ",")
   295  }
   296  
   297  func (apl *appPortList) Type() string {
   298  	return "appPortList"
   299  }
   300  
   301  // parseUserAnnotations converts the user annotations set by '--user-annotation' flag,
   302  // and returns types.UserAnnotations.
   303  func parseUserAnnotations(flagUserAnnotations *kvMap) types.UserAnnotations {
   304  	if flagUserAnnotations.IsEmpty() {
   305  		return nil
   306  	}
   307  	userAnnotations := make(types.UserAnnotations)
   308  	for k, v := range flagUserAnnotations.mapping {
   309  		userAnnotations[k] = v
   310  	}
   311  	return userAnnotations
   312  }
   313  
   314  // parseAnnotations converts the annotations set by '--annotation' flag,
   315  // and returns map[types.ACIdentifier]string
   316  func parseAnnotations(flagAnnotations *kvMap) map[types.ACIdentifier]string {
   317  	if flagAnnotations.IsEmpty() {
   318  		return nil
   319  	}
   320  	annotations := make(map[types.ACIdentifier]string)
   321  	for k, v := range flagAnnotations.mapping {
   322  		annotations[types.ACIdentifier(k)] = v
   323  	}
   324  	return annotations
   325  }
   326  
   327  // parseLabels converts the labels set by '--user-label' flag,
   328  // and returns types.UserLabels.
   329  func parseLabels(flagLabels *kvMap) types.UserLabels {
   330  	if flagLabels.IsEmpty() {
   331  		return nil
   332  	}
   333  	labels := make(types.UserLabels)
   334  	for k, v := range flagLabels.mapping {
   335  		labels[k] = v
   336  	}
   337  	return labels
   338  }