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 }