podman

Форк
0
1985 строк · 62.0 Кб
1
//go:build linux || freebsd
2
// +build linux freebsd
3

4
package buildah
5

6
import (
7
	"bytes"
8
	"encoding/json"
9
	"errors"
10
	"fmt"
11
	"io"
12
	"io/fs"
13
	"net"
14
	"os"
15
	"os/exec"
16
	"os/signal"
17
	"path/filepath"
18
	"runtime"
19
	"strconv"
20
	"strings"
21
	"sync"
22
	"sync/atomic"
23
	"syscall"
24
	"time"
25

26
	"github.com/containers/buildah/bind"
27
	"github.com/containers/buildah/copier"
28
	"github.com/containers/buildah/define"
29
	"github.com/containers/buildah/internal"
30
	internalUtil "github.com/containers/buildah/internal/util"
31
	"github.com/containers/buildah/internal/volumes"
32
	"github.com/containers/buildah/pkg/overlay"
33
	"github.com/containers/buildah/pkg/sshagent"
34
	"github.com/containers/buildah/util"
35
	"github.com/containers/common/libnetwork/etchosts"
36
	"github.com/containers/common/libnetwork/network"
37
	"github.com/containers/common/libnetwork/resolvconf"
38
	netTypes "github.com/containers/common/libnetwork/types"
39
	netUtil "github.com/containers/common/libnetwork/util"
40
	"github.com/containers/common/pkg/config"
41
	"github.com/containers/common/pkg/subscriptions"
42
	imageTypes "github.com/containers/image/v5/types"
43
	"github.com/containers/storage"
44
	"github.com/containers/storage/pkg/fileutils"
45
	"github.com/containers/storage/pkg/idtools"
46
	"github.com/containers/storage/pkg/ioutils"
47
	"github.com/containers/storage/pkg/lockfile"
48
	"github.com/containers/storage/pkg/reexec"
49
	"github.com/containers/storage/pkg/unshare"
50
	storageTypes "github.com/containers/storage/types"
51
	"github.com/opencontainers/go-digest"
52
	"github.com/opencontainers/runtime-spec/specs-go"
53
	"github.com/opencontainers/runtime-tools/generate"
54
	"github.com/opencontainers/selinux/go-selinux/label"
55
	"github.com/sirupsen/logrus"
56
	"golang.org/x/sys/unix"
57
	"golang.org/x/term"
58
)
59

60
func (b *Builder) createResolvConf(rdir string, chownOpts *idtools.IDPair) (string, error) {
61
	cfile := filepath.Join(rdir, "resolv.conf")
62
	f, err := os.Create(cfile)
63
	if err != nil {
64
		return "", err
65
	}
66
	defer f.Close()
67

68
	uid := 0
69
	gid := 0
70
	if chownOpts != nil {
71
		uid = chownOpts.UID
72
		gid = chownOpts.GID
73
	}
74
	if err = f.Chown(uid, gid); err != nil {
75
		return "", err
76
	}
77

78
	if err := relabel(cfile, b.MountLabel, false); err != nil {
79
		return "", err
80
	}
81
	return cfile, nil
82
}
83

84
// addResolvConf copies files from host and sets them up to bind mount into container
85
func (b *Builder) addResolvConfEntries(file string, networkNameServer []string,
86
	namespaces []specs.LinuxNamespace, keepHostServers, ipv6 bool) error {
87
	defaultConfig, err := config.Default()
88
	if err != nil {
89
		return fmt.Errorf("failed to get config: %w", err)
90
	}
91

92
	dnsServers, dnsSearch, dnsOptions := b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions
93
	nameservers := make([]string, 0, len(defaultConfig.Containers.DNSServers.Get())+len(dnsServers))
94
	nameservers = append(nameservers, defaultConfig.Containers.DNSServers.Get()...)
95
	nameservers = append(nameservers, dnsServers...)
96

97
	searches := make([]string, 0, len(defaultConfig.Containers.DNSSearches.Get())+len(dnsSearch))
98
	searches = append(searches, defaultConfig.Containers.DNSSearches.Get()...)
99
	searches = append(searches, dnsSearch...)
100

101
	options := make([]string, 0, len(defaultConfig.Containers.DNSOptions.Get())+len(dnsOptions))
102
	options = append(options, defaultConfig.Containers.DNSOptions.Get()...)
103
	options = append(options, dnsOptions...)
104

105
	if len(nameservers) == 0 {
106
		nameservers = networkNameServer
107
	}
108

109
	if err := resolvconf.New(&resolvconf.Params{
110
		Path:            file,
111
		Namespaces:      namespaces,
112
		IPv6Enabled:     ipv6,
113
		KeepHostServers: keepHostServers,
114
		Nameservers:     nameservers,
115
		Searches:        searches,
116
		Options:         options,
117
	}); err != nil {
118
		return fmt.Errorf("building resolv.conf for container %s: %w", b.ContainerID, err)
119
	}
120

121
	return nil
122
}
123

124
// createHostsFile creates a containers hosts file
125
func (b *Builder) createHostsFile(rdir string, chownOpts *idtools.IDPair) (string, error) {
126
	targetfile := filepath.Join(rdir, "hosts")
127
	f, err := os.Create(targetfile)
128
	if err != nil {
129
		return "", err
130
	}
131
	defer f.Close()
132
	uid := 0
133
	gid := 0
134
	if chownOpts != nil {
135
		uid = chownOpts.UID
136
		gid = chownOpts.GID
137
	}
138
	if err := f.Chown(uid, gid); err != nil {
139
		return "", err
140
	}
141
	if err := relabel(targetfile, b.MountLabel, false); err != nil {
142
		return "", err
143
	}
144

145
	return targetfile, nil
146
}
147

148
func (b *Builder) addHostsEntries(file, imageRoot string, entries etchosts.HostEntries, exculde []net.IP) error {
149
	conf, err := config.Default()
150
	if err != nil {
151
		return err
152
	}
153

154
	base, err := etchosts.GetBaseHostFile(conf.Containers.BaseHostsFile, imageRoot)
155
	if err != nil {
156
		return err
157
	}
158
	return etchosts.New(&etchosts.Params{
159
		BaseFile:                 base,
160
		ExtraHosts:               b.CommonBuildOpts.AddHost,
161
		HostContainersInternalIP: etchosts.GetHostContainersInternalIPExcluding(conf, nil, nil, exculde),
162
		TargetFile:               file,
163
		ContainerIPs:             entries,
164
	})
165
}
166

167
// generateHostname creates a containers /etc/hostname file
168
func (b *Builder) generateHostname(rdir, hostname string, chownOpts *idtools.IDPair) (string, error) {
169
	var err error
170
	hostnamePath := "/etc/hostname"
171

172
	var hostnameBuffer bytes.Buffer
173
	hostnameBuffer.Write([]byte(fmt.Sprintf("%s\n", hostname)))
174

175
	cfile := filepath.Join(rdir, filepath.Base(hostnamePath))
176
	if err = ioutils.AtomicWriteFile(cfile, hostnameBuffer.Bytes(), 0644); err != nil {
177
		return "", fmt.Errorf("writing /etc/hostname into the container: %w", err)
178
	}
179

180
	uid := 0
181
	gid := 0
182
	if chownOpts != nil {
183
		uid = chownOpts.UID
184
		gid = chownOpts.GID
185
	}
186
	if err = os.Chown(cfile, uid, gid); err != nil {
187
		return "", err
188
	}
189
	if err := relabel(cfile, b.MountLabel, false); err != nil {
190
		return "", err
191
	}
192

193
	return cfile, nil
194
}
195

196
func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy, terminalSize *specs.Box) {
197
	switch terminalPolicy {
198
	case DefaultTerminal:
199
		onTerminal := term.IsTerminal(unix.Stdin) && term.IsTerminal(unix.Stdout) && term.IsTerminal(unix.Stderr)
200
		if onTerminal {
201
			logrus.Debugf("stdio is a terminal, defaulting to using a terminal")
202
		} else {
203
			logrus.Debugf("stdio is not a terminal, defaulting to not using a terminal")
204
		}
205
		g.SetProcessTerminal(onTerminal)
206
	case WithTerminal:
207
		g.SetProcessTerminal(true)
208
	case WithoutTerminal:
209
		g.SetProcessTerminal(false)
210
	}
211
	if terminalSize != nil {
212
		g.SetProcessConsoleSize(terminalSize.Width, terminalSize.Height)
213
	}
214
}
215

216
// Search for a command that isn't given as an absolute path using the $PATH
217
// under the rootfs.  We can't resolve absolute symbolic links without
218
// chroot()ing, which we may not be able to do, so just accept a link as a
219
// valid resolution.
220
func runLookupPath(g *generate.Generator, command []string) []string {
221
	// Look for the configured $PATH.
222
	spec := g.Config
223
	envPath := ""
224
	for i := range spec.Process.Env {
225
		if strings.HasPrefix(spec.Process.Env[i], "PATH=") {
226
			envPath = spec.Process.Env[i]
227
		}
228
	}
229
	// If there is no configured $PATH, supply one.
230
	if envPath == "" {
231
		defaultPath := "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
232
		envPath = "PATH=" + defaultPath
233
		g.AddProcessEnv("PATH", defaultPath)
234
	}
235
	// No command, nothing to do.
236
	if len(command) == 0 {
237
		return command
238
	}
239
	// Command is already an absolute path, use it as-is.
240
	if filepath.IsAbs(command[0]) {
241
		return command
242
	}
243
	// For each element in the PATH,
244
	for _, pathEntry := range filepath.SplitList(envPath[5:]) {
245
		// if it's the empty string, it's ".", which is the Cwd,
246
		if pathEntry == "" {
247
			pathEntry = spec.Process.Cwd
248
		}
249
		// build the absolute path which it might be,
250
		candidate := filepath.Join(pathEntry, command[0])
251
		// check if it's there,
252
		if fi, err := os.Lstat(filepath.Join(spec.Root.Path, candidate)); fi != nil && err == nil {
253
			// and if it's not a directory, and either a symlink or executable,
254
			if !fi.IsDir() && ((fi.Mode()&os.ModeSymlink != 0) || (fi.Mode()&0111 != 0)) {
255
				// use that.
256
				return append([]string{candidate}, command[1:]...)
257
			}
258
		}
259
	}
260
	return command
261
}
262

263
func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, options RunOptions) (string, error) {
264
	// Set the user UID/GID/supplemental group list/capabilities lists.
265
	user, homeDir, err := b.userForRun(mountPoint, options.User)
266
	if err != nil {
267
		return "", err
268
	}
269
	if err := setupCapabilities(g, b.Capabilities, options.AddCapabilities, options.DropCapabilities); err != nil {
270
		return "", err
271
	}
272
	g.SetProcessUID(user.UID)
273
	g.SetProcessGID(user.GID)
274
	g.AddProcessAdditionalGid(user.GID)
275
	for _, gid := range user.AdditionalGids {
276
		g.AddProcessAdditionalGid(gid)
277
	}
278
	for _, group := range b.GroupAdd {
279
		if group == "keep-groups" {
280
			if len(b.GroupAdd) > 1 {
281
				return "", errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options")
282
			}
283
			g.AddAnnotation("run.oci.keep_original_groups", "1")
284
			continue
285
		}
286
		gid, err := strconv.ParseUint(group, 10, 32)
287
		if err != nil {
288
			return "", err
289
		}
290
		g.AddProcessAdditionalGid(uint32(gid))
291
	}
292

293
	// Remove capabilities if not running as root except Bounding set
294
	if user.UID != 0 && g.Config.Process.Capabilities != nil {
295
		bounding := g.Config.Process.Capabilities.Bounding
296
		g.ClearProcessCapabilities()
297
		g.Config.Process.Capabilities.Bounding = bounding
298
	}
299

300
	return homeDir, nil
301
}
302

303
func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions, defaultEnv []string) {
304
	g.ClearProcessEnv()
305

306
	if b.CommonBuildOpts.HTTPProxy {
307
		for _, envSpec := range config.ProxyEnv {
308
			if envVal, ok := os.LookupEnv(envSpec); ok {
309
				g.AddProcessEnv(envSpec, envVal)
310
			}
311
		}
312
	}
313

314
	for _, envSpec := range util.MergeEnv(util.MergeEnv(defaultEnv, b.Env()), options.Env) {
315
		env := strings.SplitN(envSpec, "=", 2)
316
		if len(env) > 1 {
317
			g.AddProcessEnv(env[0], env[1])
318
		}
319
	}
320
}
321

322
// getNetworkInterface creates the network interface
323
func getNetworkInterface(store storage.Store, cniConfDir, cniPluginPath string) (netTypes.ContainerNetwork, error) {
324
	conf, err := config.Default()
325
	if err != nil {
326
		return nil, err
327
	}
328
	// copy the config to not modify the default by accident
329
	newconf := *conf
330
	if len(cniConfDir) > 0 {
331
		newconf.Network.NetworkConfigDir = cniConfDir
332
	}
333
	if len(cniPluginPath) > 0 {
334
		plugins := strings.Split(cniPluginPath, string(os.PathListSeparator))
335
		newconf.Network.CNIPluginDirs.Set(plugins)
336
	}
337

338
	_, netInt, err := network.NetworkBackend(store, &newconf, false)
339
	if err != nil {
340
		return nil, err
341
	}
342
	return netInt, nil
343
}
344

345
func netStatusToNetResult(netStatus map[string]netTypes.StatusBlock, hostnames []string) *netResult {
346
	result := &netResult{
347
		keepHostResolvers: false,
348
	}
349
	for _, status := range netStatus {
350
		for _, dns := range status.DNSServerIPs {
351
			result.dnsServers = append(result.dnsServers, dns.String())
352
		}
353
		for _, netInt := range status.Interfaces {
354
			for _, netAddress := range netInt.Subnets {
355
				e := etchosts.HostEntry{IP: netAddress.IPNet.IP.String(), Names: hostnames}
356
				result.entries = append(result.entries, e)
357
				if !result.ipv6 && netUtil.IsIPv6(netAddress.IPNet.IP) {
358
					result.ipv6 = true
359
				}
360
			}
361
		}
362
	}
363
	return result
364
}
365

366
// DefaultNamespaceOptions returns the default namespace settings from the
367
// runtime-tools generator library.
368
func DefaultNamespaceOptions() (define.NamespaceOptions, error) {
369
	cfg, err := config.Default()
370
	if err != nil {
371
		return nil, fmt.Errorf("failed to get container config: %w", err)
372
	}
373
	options := define.NamespaceOptions{
374
		{Name: string(specs.CgroupNamespace), Host: cfg.CgroupNS() == "host"},
375
		{Name: string(specs.IPCNamespace), Host: cfg.IPCNS() == "host"},
376
		{Name: string(specs.MountNamespace), Host: false},
377
		{Name: string(specs.NetworkNamespace), Host: cfg.NetNS() == "host"},
378
		{Name: string(specs.PIDNamespace), Host: cfg.PidNS() == "host"},
379
		{Name: string(specs.UserNamespace), Host: cfg.Containers.UserNS == "" || cfg.Containers.UserNS == "host"},
380
		{Name: string(specs.UTSNamespace), Host: cfg.UTSNS() == "host"},
381
	}
382
	return options, nil
383
}
384

385
func checkAndOverrideIsolationOptions(isolation define.Isolation, options *RunOptions) error {
386
	switch isolation {
387
	case IsolationOCIRootless:
388
		// only change the netns if the caller did not set it
389
		if ns := options.NamespaceOptions.Find(string(specs.NetworkNamespace)); ns == nil {
390
			if _, err := exec.LookPath("slirp4netns"); err != nil {
391
				// if slirp4netns is not installed we have to use the hosts net namespace
392
				options.NamespaceOptions.AddOrReplace(define.NamespaceOption{Name: string(specs.NetworkNamespace), Host: true})
393
			}
394
		}
395
		fallthrough
396
	case IsolationOCI:
397
		pidns := options.NamespaceOptions.Find(string(specs.PIDNamespace))
398
		userns := options.NamespaceOptions.Find(string(specs.UserNamespace))
399
		if (pidns != nil && pidns.Host) && (userns != nil && !userns.Host) {
400
			return fmt.Errorf("not allowed to mix host PID namespace with container user namespace")
401
		}
402
	case IsolationChroot:
403
		logrus.Info("network namespace isolation not supported with chroot isolation, forcing host network")
404
		options.NamespaceOptions.AddOrReplace(define.NamespaceOption{Name: string(specs.NetworkNamespace), Host: true})
405
	}
406
	return nil
407
}
408

409
// fileCloser is a helper struct to prevent closing the file twice in the code
410
// users must call (fileCloser).Close() and not fileCloser.File.Close()
411
type fileCloser struct {
412
	file   *os.File
413
	closed bool
414
}
415

416
func (f *fileCloser) Close() {
417
	if !f.closed {
418
		if err := f.file.Close(); err != nil {
419
			logrus.Errorf("failed to close file: %v", err)
420
		}
421
		f.closed = true
422
	}
423
}
424

425
// waitForSync waits for a maximum of 4 minutes to read something from the file
426
func waitForSync(pipeR *os.File) error {
427
	if err := pipeR.SetDeadline(time.Now().Add(4 * time.Minute)); err != nil {
428
		return err
429
	}
430
	b := make([]byte, 16)
431
	_, err := pipeR.Read(b)
432
	return err
433
}
434

435
func runUsingRuntime(options RunOptions, configureNetwork bool, moreCreateArgs []string, spec *specs.Spec, bundlePath, containerName string,
436
	containerCreateW io.WriteCloser, containerStartR io.ReadCloser) (wstatus unix.WaitStatus, err error) {
437
	if options.Logger == nil {
438
		options.Logger = logrus.StandardLogger()
439
	}
440

441
	// Lock the caller to a single OS-level thread.
442
	runtime.LockOSThread()
443

444
	// Set up bind mounts for things that a namespaced user might not be able to get to directly.
445
	unmountAll, err := bind.SetupIntermediateMountNamespace(spec, bundlePath)
446
	if unmountAll != nil {
447
		defer func() {
448
			if err := unmountAll(); err != nil {
449
				options.Logger.Error(err)
450
			}
451
		}()
452
	}
453
	if err != nil {
454
		return 1, err
455
	}
456

457
	// Write the runtime configuration.
458
	specbytes, err := json.Marshal(spec)
459
	if err != nil {
460
		return 1, fmt.Errorf("encoding configuration %#v as json: %w", spec, err)
461
	}
462
	if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0600); err != nil {
463
		return 1, fmt.Errorf("storing runtime configuration: %w", err)
464
	}
465

466
	logrus.Debugf("config = %v", string(specbytes))
467

468
	// Decide which runtime to use.
469
	runtime := options.Runtime
470
	if runtime == "" {
471
		runtime = util.Runtime()
472
	}
473
	localRuntime := util.FindLocalRuntime(runtime)
474
	if localRuntime != "" {
475
		runtime = localRuntime
476
	}
477

478
	// Default to just passing down our stdio.
479
	getCreateStdio := func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) {
480
		return os.Stdin, os.Stdout, os.Stderr
481
	}
482

483
	// Figure out how we're doing stdio handling, and create pipes and sockets.
484
	var stdio sync.WaitGroup
485
	var consoleListener *net.UnixListener
486
	var errorFds, closeBeforeReadingErrorFds []int
487
	stdioPipe := make([][]int, 3)
488
	copyConsole := false
489
	copyPipes := false
490
	finishCopy := make([]int, 2)
491
	if err = unix.Pipe(finishCopy); err != nil {
492
		return 1, fmt.Errorf("creating pipe for notifying to stop stdio: %w", err)
493
	}
494
	finishedCopy := make(chan struct{}, 1)
495
	var pargs []string
496
	if spec.Process != nil {
497
		pargs = spec.Process.Args
498
		if spec.Process.Terminal {
499
			copyConsole = true
500
			// Create a listening socket for accepting the container's terminal's PTY master.
501
			socketPath := filepath.Join(bundlePath, "console.sock")
502
			consoleListener, err = net.ListenUnix("unix", &net.UnixAddr{Name: socketPath, Net: "unix"})
503
			if err != nil {
504
				return 1, fmt.Errorf("creating socket %q to receive terminal descriptor: %w", consoleListener.Addr(), err)
505
			}
506
			// Add console socket arguments.
507
			moreCreateArgs = append(moreCreateArgs, "--console-socket", socketPath)
508
		} else {
509
			copyPipes = true
510
			// Figure out who should own the pipes.
511
			uid, gid, err := util.GetHostRootIDs(spec)
512
			if err != nil {
513
				return 1, err
514
			}
515
			// Create stdio pipes.
516
			if stdioPipe, err = runMakeStdioPipe(int(uid), int(gid)); err != nil {
517
				return 1, err
518
			}
519
			if spec.Linux != nil {
520
				if err = runLabelStdioPipes(stdioPipe, spec.Process.SelinuxLabel, spec.Linux.MountLabel); err != nil {
521
					return 1, err
522
				}
523
			}
524
			errorFds = []int{stdioPipe[unix.Stdout][0], stdioPipe[unix.Stderr][0]}
525
			closeBeforeReadingErrorFds = []int{stdioPipe[unix.Stdout][1], stdioPipe[unix.Stderr][1]}
526
			// Set stdio to our pipes.
527
			getCreateStdio = func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) {
528
				stdin := os.NewFile(uintptr(stdioPipe[unix.Stdin][0]), "/dev/stdin")
529
				stdout := os.NewFile(uintptr(stdioPipe[unix.Stdout][1]), "/dev/stdout")
530
				stderr := os.NewFile(uintptr(stdioPipe[unix.Stderr][1]), "/dev/stderr")
531
				return stdin, stdout, stderr
532
			}
533
		}
534
	} else {
535
		if options.Quiet {
536
			// Discard stdout.
537
			getCreateStdio = func() (io.ReadCloser, io.WriteCloser, io.WriteCloser) {
538
				return os.Stdin, nil, os.Stderr
539
			}
540
		}
541
	}
542

543
	runtimeArgs := options.Args[:]
544
	if options.CgroupManager == config.SystemdCgroupsManager {
545
		runtimeArgs = append(runtimeArgs, "--systemd-cgroup")
546
	}
547

548
	// Build the commands that we'll execute.
549
	pidFile := filepath.Join(bundlePath, "pid")
550
	args := append(append(append(runtimeArgs, "create", "--bundle", bundlePath, "--pid-file", pidFile), moreCreateArgs...), containerName)
551
	create := exec.Command(runtime, args...)
552
	setPdeathsig(create)
553
	create.Dir = bundlePath
554
	stdin, stdout, stderr := getCreateStdio()
555
	create.Stdin, create.Stdout, create.Stderr = stdin, stdout, stderr
556

557
	args = append(options.Args, "start", containerName)
558
	start := exec.Command(runtime, args...)
559
	setPdeathsig(start)
560
	start.Dir = bundlePath
561
	start.Stderr = os.Stderr
562

563
	kill := func(signal string) *exec.Cmd {
564
		args := append(options.Args, "kill", containerName)
565
		if signal != "" {
566
			args = append(args, signal)
567
		}
568
		kill := exec.Command(runtime, args...)
569
		kill.Dir = bundlePath
570
		kill.Stderr = os.Stderr
571
		return kill
572
	}
573

574
	args = append(options.Args, "delete", containerName)
575
	del := exec.Command(runtime, args...)
576
	del.Dir = bundlePath
577
	del.Stderr = os.Stderr
578

579
	// Actually create the container.
580
	logrus.Debugf("Running %q", create.Args)
581
	err = create.Run()
582
	if err != nil {
583
		return 1, fmt.Errorf("from %s creating container for %v: %s: %w", runtime, pargs, runCollectOutput(options.Logger, errorFds, closeBeforeReadingErrorFds), err)
584
	}
585
	defer func() {
586
		err2 := del.Run()
587
		if err2 != nil {
588
			if err == nil {
589
				err = fmt.Errorf("deleting container: %w", err2)
590
			} else {
591
				options.Logger.Infof("error from %s deleting container: %v", runtime, err2)
592
			}
593
		}
594
	}()
595

596
	// Make sure we read the container's exit status when it exits.
597
	pidValue, err := os.ReadFile(pidFile)
598
	if err != nil {
599
		return 1, err
600
	}
601
	pid, err := strconv.Atoi(strings.TrimSpace(string(pidValue)))
602
	if err != nil {
603
		return 1, fmt.Errorf("parsing pid %s as a number: %w", string(pidValue), err)
604
	}
605
	var stopped uint32
606
	var reaping sync.WaitGroup
607
	reaping.Add(1)
608
	go func() {
609
		defer reaping.Done()
610
		var err error
611
		_, err = unix.Wait4(pid, &wstatus, 0, nil)
612
		if err != nil {
613
			wstatus = 0
614
			options.Logger.Errorf("error waiting for container child process %d: %v\n", pid, err)
615
		}
616
		atomic.StoreUint32(&stopped, 1)
617
	}()
618

619
	if configureNetwork {
620
		if _, err := containerCreateW.Write([]byte{1}); err != nil {
621
			return 1, err
622
		}
623
		containerCreateW.Close()
624
		logrus.Debug("waiting for parent start message")
625
		b := make([]byte, 1)
626
		if _, err := containerStartR.Read(b); err != nil {
627
			return 1, fmt.Errorf("did not get container start message from parent: %w", err)
628
		}
629
		containerStartR.Close()
630
	}
631

632
	if copyPipes {
633
		// We don't need the ends of the pipes that belong to the container.
634
		stdin.Close()
635
		if stdout != nil {
636
			stdout.Close()
637
		}
638
		stderr.Close()
639
	}
640

641
	// Handle stdio for the container in the background.
642
	stdio.Add(1)
643
	go runCopyStdio(options.Logger, &stdio, copyPipes, stdioPipe, copyConsole, consoleListener, finishCopy, finishedCopy, spec)
644

645
	// Start the container.
646
	logrus.Debugf("Running %q", start.Args)
647
	err = start.Run()
648
	if err != nil {
649
		return 1, fmt.Errorf("from %s starting container: %w", runtime, err)
650
	}
651
	defer func() {
652
		if atomic.LoadUint32(&stopped) == 0 {
653
			if err := kill("").Run(); err != nil {
654
				options.Logger.Infof("error from %s stopping container: %v", runtime, err)
655
			}
656
			atomic.StoreUint32(&stopped, 1)
657
		}
658
	}()
659

660
	// Wait for the container to exit.
661
	interrupted := make(chan os.Signal, 100)
662
	go func() {
663
		for range interrupted {
664
			if err := kill("SIGKILL").Run(); err != nil {
665
				logrus.Errorf("%v sending SIGKILL", err)
666
			}
667
		}
668
	}()
669
	signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
670
	for {
671
		now := time.Now()
672
		var state specs.State
673
		args = append(options.Args, "state", containerName)
674
		stat := exec.Command(runtime, args...)
675
		stat.Dir = bundlePath
676
		stat.Stderr = os.Stderr
677
		stateOutput, err := stat.Output()
678
		if err != nil {
679
			if atomic.LoadUint32(&stopped) != 0 {
680
				// container exited
681
				break
682
			}
683
			return 1, fmt.Errorf("reading container state from %s (got output: %q): %w", runtime, string(stateOutput), err)
684
		}
685
		if err = json.Unmarshal(stateOutput, &state); err != nil {
686
			return 1, fmt.Errorf("parsing container state %q from %s: %w", string(stateOutput), runtime, err)
687
		}
688
		switch state.Status {
689
		case "running":
690
		case "stopped":
691
			atomic.StoreUint32(&stopped, 1)
692
		default:
693
			return 1, fmt.Errorf("container status unexpectedly changed to %q", state.Status)
694
		}
695
		if atomic.LoadUint32(&stopped) != 0 {
696
			break
697
		}
698
		select {
699
		case <-finishedCopy:
700
			atomic.StoreUint32(&stopped, 1)
701
		case <-time.After(time.Until(now.Add(100 * time.Millisecond))):
702
			continue
703
		}
704
		if atomic.LoadUint32(&stopped) != 0 {
705
			break
706
		}
707
	}
708
	signal.Stop(interrupted)
709
	close(interrupted)
710

711
	// Close the writing end of the stop-handling-stdio notification pipe.
712
	unix.Close(finishCopy[1])
713
	// Wait for the stdio copy goroutine to flush.
714
	stdio.Wait()
715
	// Wait until we finish reading the exit status.
716
	reaping.Wait()
717

718
	return wstatus, nil
719
}
720

721
func runCollectOutput(logger *logrus.Logger, fds, closeBeforeReadingFds []int) string { //nolint:interfacer
722
	for _, fd := range closeBeforeReadingFds {
723
		unix.Close(fd)
724
	}
725
	var b bytes.Buffer
726
	buf := make([]byte, 8192)
727
	for _, fd := range fds {
728
		nread, err := unix.Read(fd, buf)
729
		if err != nil {
730
			if errno, isErrno := err.(syscall.Errno); isErrno {
731
				switch errno {
732
				default:
733
					logger.Errorf("error reading from pipe %d: %v", fd, err)
734
				case syscall.EINTR, syscall.EAGAIN:
735
				}
736
			} else {
737
				logger.Errorf("unable to wait for data from pipe %d: %v", fd, err)
738
			}
739
			continue
740
		}
741
		for nread > 0 {
742
			r := buf[:nread]
743
			if nwritten, err := b.Write(r); err != nil || nwritten != len(r) {
744
				if nwritten != len(r) {
745
					logger.Errorf("error buffering data from pipe %d: %v", fd, err)
746
					break
747
				}
748
			}
749
			nread, err = unix.Read(fd, buf)
750
			if err != nil {
751
				if errno, isErrno := err.(syscall.Errno); isErrno {
752
					switch errno {
753
					default:
754
						logger.Errorf("error reading from pipe %d: %v", fd, err)
755
					case syscall.EINTR, syscall.EAGAIN:
756
					}
757
				} else {
758
					logger.Errorf("unable to wait for data from pipe %d: %v", fd, err)
759
				}
760
				break
761
			}
762
		}
763
	}
764
	return b.String()
765
}
766

767
func setNonblock(logger *logrus.Logger, fd int, description string, nonblocking bool) (bool, error) { //nolint:interfacer
768
	mask, err := unix.FcntlInt(uintptr(fd), unix.F_GETFL, 0)
769
	if err != nil {
770
		return false, err
771
	}
772
	blocked := mask&unix.O_NONBLOCK == 0
773

774
	if err := unix.SetNonblock(fd, nonblocking); err != nil {
775
		if nonblocking {
776
			logger.Errorf("error setting %s to nonblocking: %v", description, err)
777
		} else {
778
			logger.Errorf("error setting descriptor %s blocking: %v", description, err)
779
		}
780
	}
781
	return blocked, err
782
}
783

784
func runCopyStdio(logger *logrus.Logger, stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}, spec *specs.Spec) {
785
	defer func() {
786
		unix.Close(finishCopy[0])
787
		if copyPipes {
788
			unix.Close(stdioPipe[unix.Stdin][1])
789
			unix.Close(stdioPipe[unix.Stdout][0])
790
			unix.Close(stdioPipe[unix.Stderr][0])
791
		}
792
		stdio.Done()
793
		finishedCopy <- struct{}{}
794
		close(finishedCopy)
795
	}()
796
	// Map describing where data on an incoming descriptor should go.
797
	relayMap := make(map[int]int)
798
	// Map describing incoming and outgoing descriptors.
799
	readDesc := make(map[int]string)
800
	writeDesc := make(map[int]string)
801
	// Buffers.
802
	relayBuffer := make(map[int]*bytes.Buffer)
803
	// Set up the terminal descriptor or pipes for polling.
804
	if copyConsole {
805
		// Accept a connection over our listening socket.
806
		fd, err := runAcceptTerminal(logger, consoleListener, spec.Process.ConsoleSize)
807
		if err != nil {
808
			logger.Errorf("%v", err)
809
			return
810
		}
811
		terminalFD := fd
812
		// Input from our stdin, output from the terminal descriptor.
813
		relayMap[unix.Stdin] = terminalFD
814
		readDesc[unix.Stdin] = "stdin"
815
		relayBuffer[terminalFD] = new(bytes.Buffer)
816
		writeDesc[terminalFD] = "container terminal input"
817
		relayMap[terminalFD] = unix.Stdout
818
		readDesc[terminalFD] = "container terminal output"
819
		relayBuffer[unix.Stdout] = new(bytes.Buffer)
820
		writeDesc[unix.Stdout] = "output"
821
		// Set our terminal's mode to raw, to pass handling of special
822
		// terminal input to the terminal in the container.
823
		if term.IsTerminal(unix.Stdin) {
824
			if state, err := term.MakeRaw(unix.Stdin); err != nil {
825
				logger.Warnf("error setting terminal state: %v", err)
826
			} else {
827
				defer func() {
828
					if err = term.Restore(unix.Stdin, state); err != nil {
829
						logger.Errorf("unable to restore terminal state: %v", err)
830
					}
831
				}()
832
			}
833
		}
834
	}
835
	if copyPipes {
836
		// Input from our stdin, output from the stdout and stderr pipes.
837
		relayMap[unix.Stdin] = stdioPipe[unix.Stdin][1]
838
		readDesc[unix.Stdin] = "stdin"
839
		relayBuffer[stdioPipe[unix.Stdin][1]] = new(bytes.Buffer)
840
		writeDesc[stdioPipe[unix.Stdin][1]] = "container stdin"
841
		relayMap[stdioPipe[unix.Stdout][0]] = unix.Stdout
842
		readDesc[stdioPipe[unix.Stdout][0]] = "container stdout"
843
		relayBuffer[unix.Stdout] = new(bytes.Buffer)
844
		writeDesc[unix.Stdout] = "stdout"
845
		relayMap[stdioPipe[unix.Stderr][0]] = unix.Stderr
846
		readDesc[stdioPipe[unix.Stderr][0]] = "container stderr"
847
		relayBuffer[unix.Stderr] = new(bytes.Buffer)
848
		writeDesc[unix.Stderr] = "stderr"
849
	}
850
	// Set our reading descriptors to non-blocking.
851
	for rfd, wfd := range relayMap {
852
		blocked, err := setNonblock(logger, rfd, readDesc[rfd], true)
853
		if err != nil {
854
			return
855
		}
856
		if blocked {
857
			defer setNonblock(logger, rfd, readDesc[rfd], false) // nolint:errcheck
858
		}
859
		setNonblock(logger, wfd, writeDesc[wfd], false) // nolint:errcheck
860
	}
861

862
	if copyPipes {
863
		setNonblock(logger, stdioPipe[unix.Stdin][1], writeDesc[stdioPipe[unix.Stdin][1]], true) // nolint:errcheck
864
	}
865

866
	runCopyStdioPassData(copyPipes, stdioPipe, finishCopy, relayMap, relayBuffer, readDesc, writeDesc)
867
}
868

869
func canRetry(err error) bool {
870
	if errno, isErrno := err.(syscall.Errno); isErrno {
871
		return errno == syscall.EINTR || errno == syscall.EAGAIN
872
	}
873
	return false
874
}
875

876
func runCopyStdioPassData(copyPipes bool, stdioPipe [][]int, finishCopy []int, relayMap map[int]int, relayBuffer map[int]*bytes.Buffer, readDesc map[int]string, writeDesc map[int]string) {
877
	closeStdin := false
878

879
	// Pass data back and forth.
880
	pollTimeout := -1
881
	for len(relayMap) > 0 {
882
		// Start building the list of descriptors to poll.
883
		pollFds := make([]unix.PollFd, 0, len(relayMap)+1)
884
		// Poll for a notification that we should stop handling stdio.
885
		pollFds = append(pollFds, unix.PollFd{Fd: int32(finishCopy[0]), Events: unix.POLLIN | unix.POLLHUP})
886
		// Poll on our reading descriptors.
887
		for rfd := range relayMap {
888
			pollFds = append(pollFds, unix.PollFd{Fd: int32(rfd), Events: unix.POLLIN | unix.POLLHUP})
889
		}
890
		buf := make([]byte, 8192)
891
		// Wait for new data from any input descriptor, or a notification that we're done.
892
		_, err := unix.Poll(pollFds, pollTimeout)
893
		if !util.LogIfNotRetryable(err, fmt.Sprintf("error waiting for stdio/terminal data to relay: %v", err)) {
894
			return
895
		}
896
		removes := make(map[int]struct{})
897
		for _, pollFd := range pollFds {
898
			// If this descriptor's just been closed from the other end, mark it for
899
			// removal from the set that we're checking for.
900
			if pollFd.Revents&unix.POLLHUP == unix.POLLHUP {
901
				removes[int(pollFd.Fd)] = struct{}{}
902
			}
903
			// If the descriptor was closed elsewhere, remove it from our list.
904
			if pollFd.Revents&unix.POLLNVAL != 0 {
905
				logrus.Debugf("error polling descriptor %s: closed?", readDesc[int(pollFd.Fd)])
906
				removes[int(pollFd.Fd)] = struct{}{}
907
			}
908
			// If the POLLIN flag isn't set, then there's no data to be read from this descriptor.
909
			if pollFd.Revents&unix.POLLIN == 0 {
910
				continue
911
			}
912
			// Read whatever there is to be read.
913
			readFD := int(pollFd.Fd)
914
			writeFD, needToRelay := relayMap[readFD]
915
			if needToRelay {
916
				n, err := unix.Read(readFD, buf)
917
				if !util.LogIfNotRetryable(err, fmt.Sprintf("unable to read %s data: %v", readDesc[readFD], err)) {
918
					return
919
				}
920
				// If it's zero-length on our stdin and we're
921
				// using pipes, it's an EOF, so close the stdin
922
				// pipe's writing end.
923
				if n == 0 && !canRetry(err) && int(pollFd.Fd) == unix.Stdin {
924
					removes[int(pollFd.Fd)] = struct{}{}
925
				} else if n > 0 {
926
					// Buffer the data in case we get blocked on where they need to go.
927
					nwritten, err := relayBuffer[writeFD].Write(buf[:n])
928
					if err != nil {
929
						logrus.Debugf("buffer: %v", err)
930
						continue
931
					}
932
					if nwritten != n {
933
						logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", n, nwritten)
934
						continue
935
					}
936
					// If this is the last of the data we'll be able to read from this
937
					// descriptor, read all that there is to read.
938
					for pollFd.Revents&unix.POLLHUP == unix.POLLHUP {
939
						nr, err := unix.Read(readFD, buf)
940
						util.LogIfUnexpectedWhileDraining(err, fmt.Sprintf("read %s: %v", readDesc[readFD], err))
941
						if nr <= 0 {
942
							break
943
						}
944
						nwritten, err := relayBuffer[writeFD].Write(buf[:nr])
945
						if err != nil {
946
							logrus.Debugf("buffer: %v", err)
947
							break
948
						}
949
						if nwritten != nr {
950
							logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nr, nwritten)
951
							break
952
						}
953
					}
954
				}
955
			}
956
		}
957
		// Try to drain the output buffers.  Set the default timeout
958
		// for the next poll() to 100ms if we still have data to write.
959
		pollTimeout = -1
960
		for writeFD := range relayBuffer {
961
			if relayBuffer[writeFD].Len() > 0 {
962
				n, err := unix.Write(writeFD, relayBuffer[writeFD].Bytes())
963
				if !util.LogIfNotRetryable(err, fmt.Sprintf("unable to write %s data: %v", writeDesc[writeFD], err)) {
964
					return
965
				}
966
				if n > 0 {
967
					relayBuffer[writeFD].Next(n)
968
				}
969
				if closeStdin && writeFD == stdioPipe[unix.Stdin][1] && stdioPipe[unix.Stdin][1] >= 0 && relayBuffer[stdioPipe[unix.Stdin][1]].Len() == 0 {
970
					logrus.Debugf("closing stdin")
971
					unix.Close(stdioPipe[unix.Stdin][1])
972
					stdioPipe[unix.Stdin][1] = -1
973
				}
974
			}
975
			if relayBuffer[writeFD].Len() > 0 {
976
				pollTimeout = 100
977
			}
978
		}
979
		// Remove any descriptors which we don't need to poll any more from the poll descriptor list.
980
		for remove := range removes {
981
			if copyPipes && remove == unix.Stdin {
982
				closeStdin = true
983
				if relayBuffer[stdioPipe[unix.Stdin][1]].Len() == 0 {
984
					logrus.Debugf("closing stdin")
985
					unix.Close(stdioPipe[unix.Stdin][1])
986
					stdioPipe[unix.Stdin][1] = -1
987
				}
988
			}
989
			delete(relayMap, remove)
990
		}
991
		// If the we-can-return pipe had anything for us, we're done.
992
		for _, pollFd := range pollFds {
993
			if int(pollFd.Fd) == finishCopy[0] && pollFd.Revents != 0 {
994
				// The pipe is closed, indicating that we can stop now.
995
				return
996
			}
997
		}
998
	}
999
}
1000

1001
func runAcceptTerminal(logger *logrus.Logger, consoleListener *net.UnixListener, terminalSize *specs.Box) (int, error) {
1002
	defer consoleListener.Close()
1003
	c, err := consoleListener.AcceptUnix()
1004
	if err != nil {
1005
		return -1, fmt.Errorf("accepting socket descriptor connection: %w", err)
1006
	}
1007
	defer c.Close()
1008
	// Expect a control message over our new connection.
1009
	b := make([]byte, 8192)
1010
	oob := make([]byte, 8192)
1011
	n, oobn, _, _, err := c.ReadMsgUnix(b, oob)
1012
	if err != nil {
1013
		return -1, fmt.Errorf("reading socket descriptor: %w", err)
1014
	}
1015
	if n > 0 {
1016
		logrus.Debugf("socket descriptor is for %q", string(b[:n]))
1017
	}
1018
	if oobn > len(oob) {
1019
		return -1, fmt.Errorf("too much out-of-bounds data (%d bytes)", oobn)
1020
	}
1021
	// Parse the control message.
1022
	scm, err := unix.ParseSocketControlMessage(oob[:oobn])
1023
	if err != nil {
1024
		return -1, fmt.Errorf("parsing out-of-bound data as a socket control message: %w", err)
1025
	}
1026
	logrus.Debugf("control messages: %v", scm)
1027
	// Expect to get a descriptor.
1028
	terminalFD := -1
1029
	for i := range scm {
1030
		fds, err := unix.ParseUnixRights(&scm[i])
1031
		if err != nil {
1032
			return -1, fmt.Errorf("parsing unix rights control message: %v: %w", &scm[i], err)
1033
		}
1034
		logrus.Debugf("fds: %v", fds)
1035
		if len(fds) == 0 {
1036
			continue
1037
		}
1038
		terminalFD = fds[0]
1039
		break
1040
	}
1041
	if terminalFD == -1 {
1042
		return -1, fmt.Errorf("unable to read terminal descriptor")
1043
	}
1044
	// Set the pseudoterminal's size to the configured size, or our own.
1045
	winsize := &unix.Winsize{}
1046
	if terminalSize != nil {
1047
		// Use configured sizes.
1048
		winsize.Row = uint16(terminalSize.Height)
1049
		winsize.Col = uint16(terminalSize.Width)
1050
	} else {
1051
		if term.IsTerminal(unix.Stdin) {
1052
			// Use the size of our terminal.
1053
			if winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ); err != nil {
1054
				logger.Warnf("error reading size of controlling terminal: %v", err)
1055
				winsize.Row = 0
1056
				winsize.Col = 0
1057
			}
1058
		}
1059
	}
1060
	if winsize.Row != 0 && winsize.Col != 0 {
1061
		if err = unix.IoctlSetWinsize(terminalFD, unix.TIOCSWINSZ, winsize); err != nil {
1062
			logger.Warnf("error setting size of container pseudoterminal: %v", err)
1063
		}
1064
		// FIXME - if we're connected to a terminal, we should
1065
		// be passing the updated terminal size down when we
1066
		// receive a SIGWINCH.
1067
	}
1068
	return terminalFD, nil
1069
}
1070

1071
func runUsingRuntimeMain() {
1072
	var options runUsingRuntimeSubprocOptions
1073
	// Set logging.
1074
	if level := os.Getenv("LOGLEVEL"); level != "" {
1075
		if ll, err := strconv.Atoi(level); err == nil {
1076
			logrus.SetLevel(logrus.Level(ll))
1077
		}
1078
	}
1079
	// Unpack our configuration.
1080
	confPipe := os.NewFile(3, "confpipe")
1081
	if confPipe == nil {
1082
		fmt.Fprintf(os.Stderr, "error reading options pipe\n")
1083
		os.Exit(1)
1084
	}
1085
	defer confPipe.Close()
1086
	if err := json.NewDecoder(confPipe).Decode(&options); err != nil {
1087
		fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err)
1088
		os.Exit(1)
1089
	}
1090
	// Set ourselves up to read the container's exit status.  We're doing this in a child process
1091
	// so that we won't mess with the setting in a caller of the library.
1092
	if err := setChildProcess(); err != nil {
1093
		os.Exit(1)
1094
	}
1095
	ospec := options.Spec
1096
	if ospec == nil {
1097
		fmt.Fprintf(os.Stderr, "options spec not specified\n")
1098
		os.Exit(1)
1099
	}
1100

1101
	// open the pipes used to communicate with the parent process
1102
	var containerCreateW *os.File
1103
	var containerStartR *os.File
1104
	if options.ConfigureNetwork {
1105
		containerCreateW = os.NewFile(4, "containercreatepipe")
1106
		if containerCreateW == nil {
1107
			fmt.Fprintf(os.Stderr, "could not open fd 4\n")
1108
			os.Exit(1)
1109
		}
1110
		containerStartR = os.NewFile(5, "containerstartpipe")
1111
		if containerStartR == nil {
1112
			fmt.Fprintf(os.Stderr, "could not open fd 5\n")
1113
			os.Exit(1)
1114
		}
1115
	}
1116

1117
	// Run the container, start to finish.
1118
	status, err := runUsingRuntime(options.Options, options.ConfigureNetwork, options.MoreCreateArgs, ospec, options.BundlePath, options.ContainerName, containerCreateW, containerStartR)
1119
	if err != nil {
1120
		fmt.Fprintf(os.Stderr, "error running container: %v\n", err)
1121
		os.Exit(1)
1122
	}
1123
	// Pass the container's exit status back to the caller by exiting with the same status.
1124
	if status.Exited() {
1125
		os.Exit(status.ExitStatus())
1126
	} else if status.Signaled() {
1127
		fmt.Fprintf(os.Stderr, "container exited on %s\n", status.Signal())
1128
		os.Exit(1)
1129
	}
1130
	os.Exit(1)
1131
}
1132

1133
func (b *Builder) runUsingRuntimeSubproc(isolation define.Isolation, options RunOptions, configureNetwork bool, networkString string,
1134
	moreCreateArgs []string, spec *specs.Spec, rootPath, bundlePath, containerName, buildContainerName, hostsFile, resolvFile string) (err error) {
1135
	// Lock the caller to a single OS-level thread.
1136
	runtime.LockOSThread()
1137
	defer runtime.UnlockOSThread()
1138

1139
	var confwg sync.WaitGroup
1140
	config, conferr := json.Marshal(runUsingRuntimeSubprocOptions{
1141
		Options:          options,
1142
		Spec:             spec,
1143
		RootPath:         rootPath,
1144
		BundlePath:       bundlePath,
1145
		ConfigureNetwork: configureNetwork,
1146
		MoreCreateArgs:   moreCreateArgs,
1147
		ContainerName:    containerName,
1148
		Isolation:        isolation,
1149
	})
1150
	if conferr != nil {
1151
		return fmt.Errorf("encoding configuration for %q: %w", runUsingRuntimeCommand, conferr)
1152
	}
1153
	cmd := reexec.Command(runUsingRuntimeCommand)
1154
	setPdeathsig(cmd)
1155
	cmd.Dir = bundlePath
1156
	cmd.Stdin = options.Stdin
1157
	if cmd.Stdin == nil {
1158
		cmd.Stdin = os.Stdin
1159
	}
1160
	cmd.Stdout = options.Stdout
1161
	if cmd.Stdout == nil {
1162
		cmd.Stdout = os.Stdout
1163
	}
1164
	cmd.Stderr = options.Stderr
1165
	if cmd.Stderr == nil {
1166
		cmd.Stderr = os.Stderr
1167
	}
1168
	cmd.Env = util.MergeEnv(os.Environ(), []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())})
1169
	preader, pwriter, err := os.Pipe()
1170
	if err != nil {
1171
		return fmt.Errorf("creating configuration pipe: %w", err)
1172
	}
1173
	confwg.Add(1)
1174
	go func() {
1175
		_, conferr = io.Copy(pwriter, bytes.NewReader(config))
1176
		if conferr != nil {
1177
			conferr = fmt.Errorf("while copying configuration down pipe to child process: %w", conferr)
1178
		}
1179
		confwg.Done()
1180
	}()
1181

1182
	// create network configuration pipes
1183
	var containerCreateR, containerCreateW fileCloser
1184
	var containerStartR, containerStartW fileCloser
1185
	if configureNetwork {
1186
		containerCreateR.file, containerCreateW.file, err = os.Pipe()
1187
		if err != nil {
1188
			return fmt.Errorf("creating container create pipe: %w", err)
1189
		}
1190
		defer containerCreateR.Close()
1191
		defer containerCreateW.Close()
1192

1193
		containerStartR.file, containerStartW.file, err = os.Pipe()
1194
		if err != nil {
1195
			return fmt.Errorf("creating container start pipe: %w", err)
1196
		}
1197
		defer containerStartR.Close()
1198
		defer containerStartW.Close()
1199
		cmd.ExtraFiles = []*os.File{containerCreateW.file, containerStartR.file}
1200
	}
1201

1202
	cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...)
1203
	defer preader.Close()
1204
	defer pwriter.Close()
1205
	if err := cmd.Start(); err != nil {
1206
		return fmt.Errorf("while starting runtime: %w", err)
1207
	}
1208

1209
	interrupted := make(chan os.Signal, 100)
1210
	go func() {
1211
		for receivedSignal := range interrupted {
1212
			if err := cmd.Process.Signal(receivedSignal); err != nil {
1213
				logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal)
1214
			}
1215
		}
1216
	}()
1217
	signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
1218

1219
	if configureNetwork {
1220
		// we already passed the fd to the child, now close the writer so we do not hang if the child closes it
1221
		containerCreateW.Close()
1222
		if err := waitForSync(containerCreateR.file); err != nil {
1223
			// we do not want to return here since we want to capture the exit code from the child via cmd.Wait()
1224
			// close the pipes here so that the child will not hang forever
1225
			containerCreateR.Close()
1226
			containerStartW.Close()
1227
			logrus.Errorf("did not get container create message from subprocess: %v", err)
1228
		} else {
1229
			pidFile := filepath.Join(bundlePath, "pid")
1230
			pidValue, err := os.ReadFile(pidFile)
1231
			if err != nil {
1232
				return err
1233
			}
1234
			pid, err := strconv.Atoi(strings.TrimSpace(string(pidValue)))
1235
			if err != nil {
1236
				return fmt.Errorf("parsing pid %s as a number: %w", string(pidValue), err)
1237
			}
1238

1239
			teardown, netResult, err := b.runConfigureNetwork(pid, isolation, options, networkString, containerName, []string{spec.Hostname, buildContainerName})
1240
			if teardown != nil {
1241
				defer teardown()
1242
			}
1243
			if err != nil {
1244
				return fmt.Errorf("setup network: %w", err)
1245
			}
1246

1247
			// only add hosts if we manage the hosts file
1248
			if hostsFile != "" {
1249
				err = b.addHostsEntries(hostsFile, rootPath, netResult.entries, netResult.excludeIPs)
1250
				if err != nil {
1251
					return err
1252
				}
1253
			}
1254

1255
			if resolvFile != "" {
1256
				err = b.addResolvConfEntries(resolvFile, netResult.dnsServers, spec.Linux.Namespaces, netResult.keepHostResolvers, netResult.ipv6)
1257
				if err != nil {
1258
					return err
1259
				}
1260
			}
1261

1262
			logrus.Debug("network namespace successfully setup, send start message to child")
1263
			_, err = containerStartW.file.Write([]byte{1})
1264
			if err != nil {
1265
				return err
1266
			}
1267
		}
1268
	}
1269

1270
	if err := cmd.Wait(); err != nil {
1271
		return fmt.Errorf("while running runtime: %w", err)
1272
	}
1273
	confwg.Wait()
1274
	signal.Stop(interrupted)
1275
	close(interrupted)
1276
	if err == nil {
1277
		return conferr
1278
	}
1279
	if conferr != nil {
1280
		logrus.Debugf("%v", conferr)
1281
	}
1282
	return err
1283
}
1284

1285
type runUsingRuntimeSubprocOptions struct {
1286
	Options          RunOptions
1287
	Spec             *specs.Spec
1288
	RootPath         string
1289
	BundlePath       string
1290
	ConfigureNetwork bool
1291
	MoreCreateArgs   []string
1292
	ContainerName    string
1293
	Isolation        define.Isolation
1294
}
1295

1296
func init() {
1297
	reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain)
1298
}
1299

1300
// If this succeeds, the caller must call cleanupMounts().
1301
func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, runFileMounts []string, runMountInfo runMountInfo) (*runMountArtifacts, error) {
1302
	// Start building a new list of mounts.
1303
	var mounts []specs.Mount
1304
	haveMount := func(destination string) bool {
1305
		for _, mount := range mounts {
1306
			if mount.Destination == destination {
1307
				// Already have something to mount there.
1308
				return true
1309
			}
1310
		}
1311
		return false
1312
	}
1313

1314
	specMounts, err := setupSpecialMountSpecChanges(spec, b.CommonBuildOpts.ShmSize)
1315
	if err != nil {
1316
		return nil, err
1317
	}
1318

1319
	// Get the list of files we need to bind into the container.
1320
	bindFileMounts := runSetupBoundFiles(bundlePath, bindFiles)
1321

1322
	// After this point we need to know the per-container persistent storage directory.
1323
	cdir, err := b.store.ContainerDirectory(b.ContainerID)
1324
	if err != nil {
1325
		return nil, fmt.Errorf("determining work directory for container %q: %w", b.ContainerID, err)
1326
	}
1327

1328
	// Figure out which UID and GID to tell the subscriptions package to use
1329
	// for files that it creates.
1330
	rootUID, rootGID, err := util.GetHostRootIDs(spec)
1331
	if err != nil {
1332
		return nil, err
1333
	}
1334

1335
	// Get host UID and GID of the container process.
1336
	var uidMap = []specs.LinuxIDMapping{}
1337
	var gidMap = []specs.LinuxIDMapping{}
1338
	if spec.Linux != nil {
1339
		uidMap = spec.Linux.UIDMappings
1340
		gidMap = spec.Linux.GIDMappings
1341
	}
1342
	processUID, processGID, err := util.GetHostIDs(uidMap, gidMap, spec.Process.User.UID, spec.Process.User.GID)
1343
	if err != nil {
1344
		return nil, err
1345
	}
1346

1347
	// Get the list of subscriptions mounts.
1348
	subscriptionMounts := subscriptions.MountsWithUIDGID(b.MountLabel, cdir, b.DefaultMountsFilePath, mountPoint, int(rootUID), int(rootGID), unshare.IsRootless(), false)
1349

1350
	idMaps := IDMaps{
1351
		uidmap:     uidMap,
1352
		gidmap:     gidMap,
1353
		rootUID:    int(rootUID),
1354
		rootGID:    int(rootGID),
1355
		processUID: int(processUID),
1356
		processGID: int(processGID),
1357
	}
1358
	// Get the list of mounts that are just for this Run() call.
1359
	runMounts, mountArtifacts, err := b.runSetupRunMounts(mountPoint, runFileMounts, runMountInfo, idMaps)
1360
	if err != nil {
1361
		return nil, err
1362
	}
1363
	succeeded := false
1364
	defer func() {
1365
		if !succeeded {
1366
			volumes.UnlockLockArray(mountArtifacts.TargetLocks)
1367
		}
1368
	}()
1369
	// Add temporary copies of the contents of volume locations at the
1370
	// volume locations, unless we already have something there.
1371
	builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, builtinVolumes, int(rootUID), int(rootGID))
1372
	if err != nil {
1373
		return nil, err
1374
	}
1375

1376
	// Get the list of explicitly-specified volume mounts.
1377
	var mountLabel = ""
1378
	if spec.Linux != nil {
1379
		mountLabel = spec.Linux.MountLabel
1380
	}
1381
	volumes, err := b.runSetupVolumeMounts(mountLabel, volumeMounts, optionMounts, idMaps)
1382
	if err != nil {
1383
		return nil, err
1384
	}
1385

1386
	// prepare list of mount destinations which can be cleaned up safely.
1387
	// we can clean bindFiles, subscriptionMounts and specMounts
1388
	// everything other than these might have users content
1389
	mountArtifacts.RunMountTargets = append(append(append(mountArtifacts.RunMountTargets, cleanableDestinationListFromMounts(bindFileMounts)...), cleanableDestinationListFromMounts(subscriptionMounts)...), cleanableDestinationListFromMounts(specMounts)...)
1390

1391
	allMounts := util.SortMounts(append(append(append(append(append(volumes, builtins...), runMounts...), subscriptionMounts...), bindFileMounts...), specMounts...))
1392
	// Add them all, in the preferred order, except where they conflict with something that was previously added.
1393
	for _, mount := range allMounts {
1394
		if haveMount(mount.Destination) {
1395
			// Already mounting something there, no need to bother with this one.
1396
			continue
1397
		}
1398
		// Add the mount.
1399
		mounts = append(mounts, mount)
1400
	}
1401

1402
	// Set the list in the spec.
1403
	spec.Mounts = mounts
1404
	succeeded = true
1405
	return mountArtifacts, nil
1406
}
1407

1408
func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtinVolumes []string, rootUID, rootGID int) ([]specs.Mount, error) {
1409
	var mounts []specs.Mount
1410
	hostOwner := idtools.IDPair{UID: rootUID, GID: rootGID}
1411
	// Add temporary copies of the contents of volume locations at the
1412
	// volume locations, unless we already have something there.
1413
	for _, volume := range builtinVolumes {
1414
		volumePath := filepath.Join(containerDir, "buildah-volumes", digest.Canonical.FromString(volume).Hex())
1415
		initializeVolume := false
1416
		// If we need to, create the directory that we'll use to hold
1417
		// the volume contents.  If we do need to create it, then we'll
1418
		// need to populate it, too, so make a note of that.
1419
		if err := fileutils.Exists(volumePath); err != nil {
1420
			if !errors.Is(err, fs.ErrNotExist) {
1421
				return nil, err
1422
			}
1423
			logrus.Debugf("setting up built-in volume path at %q for %q", volumePath, volume)
1424
			if err = os.MkdirAll(volumePath, 0755); err != nil {
1425
				return nil, err
1426
			}
1427
			if err = relabel(volumePath, mountLabel, false); err != nil {
1428
				return nil, err
1429
			}
1430
			initializeVolume = true
1431
		}
1432
		// Make sure the volume exists in the rootfs and read its attributes.
1433
		createDirPerms := os.FileMode(0755)
1434
		err := copier.Mkdir(mountPoint, filepath.Join(mountPoint, volume), copier.MkdirOptions{
1435
			ChownNew: &hostOwner,
1436
			ChmodNew: &createDirPerms,
1437
		})
1438
		if err != nil {
1439
			return nil, fmt.Errorf("ensuring volume path %q: %w", filepath.Join(mountPoint, volume), err)
1440
		}
1441
		srcPath, err := copier.Eval(mountPoint, filepath.Join(mountPoint, volume), copier.EvalOptions{})
1442
		if err != nil {
1443
			return nil, fmt.Errorf("evaluating path %q: %w", srcPath, err)
1444
		}
1445
		stat, err := os.Stat(srcPath)
1446
		if err != nil && !errors.Is(err, os.ErrNotExist) {
1447
			return nil, err
1448
		}
1449
		// If we need to populate the mounted volume's contents with
1450
		// content from the rootfs, set it up now.
1451
		if initializeVolume {
1452
			if err = os.Chmod(volumePath, stat.Mode().Perm()); err != nil {
1453
				return nil, err
1454
			}
1455
			if err = os.Chown(volumePath, int(stat.Sys().(*syscall.Stat_t).Uid), int(stat.Sys().(*syscall.Stat_t).Gid)); err != nil {
1456
				return nil, err
1457
			}
1458
			logrus.Debugf("populating directory %q for volume %q using contents of %q", volumePath, volume, srcPath)
1459
			if err = extractWithTar(mountPoint, srcPath, volumePath); err != nil && !errors.Is(err, os.ErrNotExist) {
1460
				return nil, fmt.Errorf("populating directory %q for volume %q using contents of %q: %w", volumePath, volume, srcPath, err)
1461
			}
1462
		}
1463
		// Add the bind mount.
1464
		mounts = append(mounts, specs.Mount{
1465
			Source:      volumePath,
1466
			Destination: volume,
1467
			Type:        define.TypeBind,
1468
			Options:     define.BindOptions,
1469
		})
1470
	}
1471
	return mounts, nil
1472
}
1473

1474
// Destinations which can be cleaned up after every RUN
1475
func cleanableDestinationListFromMounts(mounts []specs.Mount) []string {
1476
	mountDest := []string{}
1477
	for _, mount := range mounts {
1478
		// Add all destination to mountArtifacts so that they can be cleaned up later
1479
		if mount.Destination != "" {
1480
			cleanPath := true
1481
			for _, prefix := range nonCleanablePrefixes {
1482
				if strings.HasPrefix(mount.Destination, prefix) {
1483
					cleanPath = false
1484
					break
1485
				}
1486
			}
1487
			if cleanPath {
1488
				mountDest = append(mountDest, mount.Destination)
1489
			}
1490
		}
1491
	}
1492
	return mountDest
1493
}
1494

1495
func checkIfMountDestinationPreExists(root string, dest string) (bool, error) {
1496
	statResults, err := copier.Stat(root, "", copier.StatOptions{}, []string{dest})
1497
	if err != nil {
1498
		return false, err
1499
	}
1500
	if len(statResults) > 0 {
1501
		// We created exact path for globbing so it will
1502
		// return only one result.
1503
		if statResults[0].Error != "" && len(statResults[0].Globbed) == 0 {
1504
			// Path do not exist.
1505
			return false, nil
1506
		}
1507
		// Path exists.
1508
		return true, nil
1509
	}
1510
	return false, nil
1511
}
1512

1513
// runSetupRunMounts sets up mounts that exist only in this RUN, not in subsequent runs
1514
//
1515
// If this function succeeds, the caller must unlock runMountArtifacts.TargetLocks (when??)
1516
func (b *Builder) runSetupRunMounts(mountPoint string, mounts []string, sources runMountInfo, idMaps IDMaps) ([]specs.Mount, *runMountArtifacts, error) {
1517
	mountTargets := make([]string, 0, 10)
1518
	tmpFiles := make([]string, 0, len(mounts))
1519
	mountImages := make([]string, 0, 10)
1520
	finalMounts := make([]specs.Mount, 0, len(mounts))
1521
	agents := make([]*sshagent.AgentServer, 0, len(mounts))
1522
	sshCount := 0
1523
	defaultSSHSock := ""
1524
	targetLocks := []*lockfile.LockFile{}
1525
	succeeded := false
1526
	defer func() {
1527
		if !succeeded {
1528
			volumes.UnlockLockArray(targetLocks)
1529
		}
1530
	}()
1531
	for _, mount := range mounts {
1532
		var mountSpec *specs.Mount
1533
		var err error
1534
		var envFile, image string
1535
		var agent *sshagent.AgentServer
1536
		var tl *lockfile.LockFile
1537
		tokens := strings.Split(mount, ",")
1538

1539
		// If `type` is not set default to TypeBind
1540
		mountType := define.TypeBind
1541

1542
		for _, field := range tokens {
1543
			if strings.HasPrefix(field, "type=") {
1544
				kv := strings.Split(field, "=")
1545
				if len(kv) != 2 {
1546
					return nil, nil, errors.New("invalid mount type")
1547
				}
1548
				mountType = kv[1]
1549
			}
1550
		}
1551
		switch mountType {
1552
		case "secret":
1553
			mountSpec, envFile, err = b.getSecretMount(tokens, sources.Secrets, idMaps, sources.WorkDir)
1554
			if err != nil {
1555
				return nil, nil, err
1556
			}
1557
			if mountSpec != nil {
1558
				finalMounts = append(finalMounts, *mountSpec)
1559
				if envFile != "" {
1560
					tmpFiles = append(tmpFiles, envFile)
1561
				}
1562
			}
1563
		case "ssh":
1564
			mountSpec, agent, err = b.getSSHMount(tokens, sshCount, sources.SSHSources, idMaps)
1565
			if err != nil {
1566
				return nil, nil, err
1567
			}
1568
			if mountSpec != nil {
1569
				finalMounts = append(finalMounts, *mountSpec)
1570
				agents = append(agents, agent)
1571
				if sshCount == 0 {
1572
					defaultSSHSock = mountSpec.Destination
1573
				}
1574
				// Count is needed as the default destination of the ssh sock inside the container is  /run/buildkit/ssh_agent.{i}
1575
				sshCount++
1576
			}
1577
		case define.TypeBind:
1578
			mountSpec, image, err = b.getBindMount(tokens, sources.SystemContext, sources.ContextDir, sources.StageMountPoints, idMaps, sources.WorkDir)
1579
			if err != nil {
1580
				return nil, nil, err
1581
			}
1582
			finalMounts = append(finalMounts, *mountSpec)
1583
			// only perform cleanup if image was mounted ignore everything else
1584
			if image != "" {
1585
				mountImages = append(mountImages, image)
1586
			}
1587
		case "tmpfs":
1588
			mountSpec, err = b.getTmpfsMount(tokens, idMaps)
1589
			if err != nil {
1590
				return nil, nil, err
1591
			}
1592
			finalMounts = append(finalMounts, *mountSpec)
1593
		case "cache":
1594
			mountSpec, tl, err = b.getCacheMount(tokens, sources.StageMountPoints, idMaps, sources.WorkDir)
1595
			if err != nil {
1596
				return nil, nil, err
1597
			}
1598
			finalMounts = append(finalMounts, *mountSpec)
1599
			if tl != nil {
1600
				targetLocks = append(targetLocks, tl)
1601
			}
1602
		default:
1603
			return nil, nil, fmt.Errorf("invalid mount type %q", mountType)
1604
		}
1605

1606
		if mountSpec != nil {
1607
			pathPreExists, err := checkIfMountDestinationPreExists(mountPoint, mountSpec.Destination)
1608
			if err != nil {
1609
				return nil, nil, err
1610
			}
1611
			if !pathPreExists {
1612
				// In such case it means that the path did not exists before
1613
				// creating any new mounts therefore we must clean the newly
1614
				// created directory after this step.
1615
				mountTargets = append(mountTargets, mountSpec.Destination)
1616
			}
1617
		}
1618
	}
1619
	succeeded = true
1620
	artifacts := &runMountArtifacts{
1621
		RunMountTargets: mountTargets,
1622
		TmpFiles:        tmpFiles,
1623
		Agents:          agents,
1624
		MountedImages:   mountImages,
1625
		SSHAuthSock:     defaultSSHSock,
1626
		TargetLocks:     targetLocks,
1627
	}
1628
	return finalMounts, artifacts, nil
1629
}
1630

1631
func (b *Builder) getBindMount(tokens []string, context *imageTypes.SystemContext, contextDir string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*specs.Mount, string, error) {
1632
	if contextDir == "" {
1633
		return nil, "", errors.New("Context Directory for current run invocation is not configured")
1634
	}
1635
	var optionMounts []specs.Mount
1636
	mount, image, err := volumes.GetBindMount(context, tokens, contextDir, b.store, b.MountLabel, stageMountPoints, workDir)
1637
	if err != nil {
1638
		return nil, image, err
1639
	}
1640
	optionMounts = append(optionMounts, mount)
1641
	volumes, err := b.runSetupVolumeMounts(b.MountLabel, nil, optionMounts, idMaps)
1642
	if err != nil {
1643
		return nil, image, err
1644
	}
1645
	return &volumes[0], image, nil
1646
}
1647

1648
func (b *Builder) getTmpfsMount(tokens []string, idMaps IDMaps) (*specs.Mount, error) {
1649
	var optionMounts []specs.Mount
1650
	mount, err := volumes.GetTmpfsMount(tokens)
1651
	if err != nil {
1652
		return nil, err
1653
	}
1654
	optionMounts = append(optionMounts, mount)
1655
	volumes, err := b.runSetupVolumeMounts(b.MountLabel, nil, optionMounts, idMaps)
1656
	if err != nil {
1657
		return nil, err
1658
	}
1659
	return &volumes[0], nil
1660
}
1661

1662
func (b *Builder) getSecretMount(tokens []string, secrets map[string]define.Secret, idMaps IDMaps, workdir string) (*specs.Mount, string, error) {
1663
	errInvalidSyntax := errors.New("secret should have syntax id=id[,target=path,required=bool,mode=uint,uid=uint,gid=uint")
1664
	if len(tokens) == 0 {
1665
		return nil, "", errInvalidSyntax
1666
	}
1667
	var err error
1668
	var id, target string
1669
	var required bool
1670
	var uid, gid uint32
1671
	var mode uint32 = 0400
1672
	for _, val := range tokens {
1673
		kv := strings.SplitN(val, "=", 2)
1674
		switch kv[0] {
1675
		case "type":
1676
			// This is already processed
1677
			continue
1678
		case "id":
1679
			id = kv[1]
1680
		case "target", "dst", "destination":
1681
			target = kv[1]
1682
			if !filepath.IsAbs(target) {
1683
				target = filepath.Join(workdir, target)
1684
			}
1685
		case "required":
1686
			required = true
1687
			if len(kv) > 1 {
1688
				required, err = strconv.ParseBool(kv[1])
1689
				if err != nil {
1690
					return nil, "", errInvalidSyntax
1691
				}
1692
			}
1693
		case "mode":
1694
			mode64, err := strconv.ParseUint(kv[1], 8, 32)
1695
			if err != nil {
1696
				return nil, "", errInvalidSyntax
1697
			}
1698
			mode = uint32(mode64)
1699
		case "uid":
1700
			uid64, err := strconv.ParseUint(kv[1], 10, 32)
1701
			if err != nil {
1702
				return nil, "", errInvalidSyntax
1703
			}
1704
			uid = uint32(uid64)
1705
		case "gid":
1706
			gid64, err := strconv.ParseUint(kv[1], 10, 32)
1707
			if err != nil {
1708
				return nil, "", errInvalidSyntax
1709
			}
1710
			gid = uint32(gid64)
1711
		default:
1712
			return nil, "", errInvalidSyntax
1713
		}
1714
	}
1715

1716
	if id == "" {
1717
		return nil, "", errInvalidSyntax
1718
	}
1719
	// Default location for secretis is /run/secrets/id
1720
	if target == "" {
1721
		target = "/run/secrets/" + id
1722
	}
1723

1724
	secr, ok := secrets[id]
1725
	if !ok {
1726
		if required {
1727
			return nil, "", fmt.Errorf("secret required but no secret with id %s found", id)
1728
		}
1729
		return nil, "", nil
1730
	}
1731
	var data []byte
1732
	var envFile string
1733
	var ctrFileOnHost string
1734

1735
	switch secr.SourceType {
1736
	case "env":
1737
		data = []byte(os.Getenv(secr.Source))
1738
		tmpFile, err := os.CreateTemp(define.TempDir, "buildah*")
1739
		if err != nil {
1740
			return nil, "", err
1741
		}
1742
		envFile = tmpFile.Name()
1743
		ctrFileOnHost = tmpFile.Name()
1744
	case "file":
1745
		containerWorkingDir, err := b.store.ContainerDirectory(b.ContainerID)
1746
		if err != nil {
1747
			return nil, "", err
1748
		}
1749
		data, err = os.ReadFile(secr.Source)
1750
		if err != nil {
1751
			return nil, "", err
1752
		}
1753
		ctrFileOnHost = filepath.Join(containerWorkingDir, "secrets", id)
1754
	default:
1755
		return nil, "", errors.New("invalid source secret type")
1756
	}
1757

1758
	// Copy secrets to container working dir (or tmp dir if it's an env), since we need to chmod,
1759
	// chown and relabel it for the container user and we don't want to mess with the original file
1760
	if err := os.MkdirAll(filepath.Dir(ctrFileOnHost), 0755); err != nil {
1761
		return nil, "", err
1762
	}
1763
	if err := os.WriteFile(ctrFileOnHost, data, 0644); err != nil {
1764
		return nil, "", err
1765
	}
1766

1767
	if err := relabel(ctrFileOnHost, b.MountLabel, false); err != nil {
1768
		return nil, "", err
1769
	}
1770
	hostUID, hostGID, err := util.GetHostIDs(idMaps.uidmap, idMaps.gidmap, uid, gid)
1771
	if err != nil {
1772
		return nil, "", err
1773
	}
1774
	if err := os.Lchown(ctrFileOnHost, int(hostUID), int(hostGID)); err != nil {
1775
		return nil, "", err
1776
	}
1777
	if err := os.Chmod(ctrFileOnHost, os.FileMode(mode)); err != nil {
1778
		return nil, "", err
1779
	}
1780
	newMount := specs.Mount{
1781
		Destination: target,
1782
		Type:        define.TypeBind,
1783
		Source:      ctrFileOnHost,
1784
		Options:     append(define.BindOptions, "rprivate", "ro"),
1785
	}
1786
	return &newMount, envFile, nil
1787
}
1788

1789
// getSSHMount parses the --mount type=ssh flag in the Containerfile, checks if there's an ssh source provided, and creates and starts an ssh-agent to be forwarded into the container
1790
func (b *Builder) getSSHMount(tokens []string, count int, sshsources map[string]*sshagent.Source, idMaps IDMaps) (*specs.Mount, *sshagent.AgentServer, error) {
1791
	errInvalidSyntax := errors.New("ssh should have syntax id=id[,target=path,required=bool,mode=uint,uid=uint,gid=uint")
1792

1793
	var err error
1794
	var id, target string
1795
	var required bool
1796
	var uid, gid uint32
1797
	var mode uint32 = 400
1798
	for _, val := range tokens {
1799
		kv := strings.SplitN(val, "=", 2)
1800
		if len(kv) < 2 {
1801
			return nil, nil, errInvalidSyntax
1802
		}
1803
		switch kv[0] {
1804
		case "type":
1805
			// This is already processed
1806
			continue
1807
		case "id":
1808
			id = kv[1]
1809
		case "target", "dst", "destination":
1810
			target = kv[1]
1811
		case "required":
1812
			required, err = strconv.ParseBool(kv[1])
1813
			if err != nil {
1814
				return nil, nil, errInvalidSyntax
1815
			}
1816
		case "mode":
1817
			mode64, err := strconv.ParseUint(kv[1], 8, 32)
1818
			if err != nil {
1819
				return nil, nil, errInvalidSyntax
1820
			}
1821
			mode = uint32(mode64)
1822
		case "uid":
1823
			uid64, err := strconv.ParseUint(kv[1], 10, 32)
1824
			if err != nil {
1825
				return nil, nil, errInvalidSyntax
1826
			}
1827
			uid = uint32(uid64)
1828
		case "gid":
1829
			gid64, err := strconv.ParseUint(kv[1], 10, 32)
1830
			if err != nil {
1831
				return nil, nil, errInvalidSyntax
1832
			}
1833
			gid = uint32(gid64)
1834
		default:
1835
			return nil, nil, errInvalidSyntax
1836
		}
1837
	}
1838

1839
	if id == "" {
1840
		id = "default"
1841
	}
1842
	// Default location for secretis is /run/buildkit/ssh_agent.{i}
1843
	if target == "" {
1844
		target = fmt.Sprintf("/run/buildkit/ssh_agent.%d", count)
1845
	}
1846

1847
	sshsource, ok := sshsources[id]
1848
	if !ok {
1849
		if required {
1850
			return nil, nil, fmt.Errorf("ssh required but no ssh with id %s found", id)
1851
		}
1852
		return nil, nil, nil
1853
	}
1854
	// Create new agent from keys or socket
1855
	fwdAgent, err := sshagent.NewAgentServer(sshsource)
1856
	if err != nil {
1857
		return nil, nil, err
1858
	}
1859
	// Start ssh server, and get the host sock we're mounting in the container
1860
	hostSock, err := fwdAgent.Serve(b.ProcessLabel)
1861
	if err != nil {
1862
		return nil, nil, err
1863
	}
1864

1865
	if err := relabel(filepath.Dir(hostSock), b.MountLabel, false); err != nil {
1866
		if shutdownErr := fwdAgent.Shutdown(); shutdownErr != nil {
1867
			b.Logger.Errorf("error shutting down agent: %v", shutdownErr)
1868
		}
1869
		return nil, nil, err
1870
	}
1871
	if err := relabel(hostSock, b.MountLabel, false); err != nil {
1872
		if shutdownErr := fwdAgent.Shutdown(); shutdownErr != nil {
1873
			b.Logger.Errorf("error shutting down agent: %v", shutdownErr)
1874
		}
1875
		return nil, nil, err
1876
	}
1877
	hostUID, hostGID, err := util.GetHostIDs(idMaps.uidmap, idMaps.gidmap, uid, gid)
1878
	if err != nil {
1879
		if shutdownErr := fwdAgent.Shutdown(); shutdownErr != nil {
1880
			b.Logger.Errorf("error shutting down agent: %v", shutdownErr)
1881
		}
1882
		return nil, nil, err
1883
	}
1884
	if err := os.Lchown(hostSock, int(hostUID), int(hostGID)); err != nil {
1885
		if shutdownErr := fwdAgent.Shutdown(); shutdownErr != nil {
1886
			b.Logger.Errorf("error shutting down agent: %v", shutdownErr)
1887
		}
1888
		return nil, nil, err
1889
	}
1890
	if err := os.Chmod(hostSock, os.FileMode(mode)); err != nil {
1891
		if shutdownErr := fwdAgent.Shutdown(); shutdownErr != nil {
1892
			b.Logger.Errorf("error shutting down agent: %v", shutdownErr)
1893
		}
1894
		return nil, nil, err
1895
	}
1896
	newMount := specs.Mount{
1897
		Destination: target,
1898
		Type:        define.TypeBind,
1899
		Source:      hostSock,
1900
		Options:     append(define.BindOptions, "rprivate", "ro"),
1901
	}
1902
	return &newMount, fwdAgent, nil
1903
}
1904

1905
func (b *Builder) cleanupTempVolumes() {
1906
	for tempVolume, val := range b.TempVolumes {
1907
		if val {
1908
			if err := overlay.RemoveTemp(tempVolume); err != nil {
1909
				b.Logger.Errorf(err.Error())
1910
			}
1911
			b.TempVolumes[tempVolume] = false
1912
		}
1913
	}
1914
}
1915

1916
// cleanupRunMounts cleans up run mounts so they only appear in this run.
1917
func (b *Builder) cleanupRunMounts(context *imageTypes.SystemContext, mountpoint string, artifacts *runMountArtifacts) error {
1918
	for _, agent := range artifacts.Agents {
1919
		err := agent.Shutdown()
1920
		if err != nil {
1921
			return err
1922
		}
1923
	}
1924

1925
	//cleanup any mounted images for this run
1926
	for _, image := range artifacts.MountedImages {
1927
		if image != "" {
1928
			// if flow hits here some image was mounted for this run
1929
			i, err := internalUtil.LookupImage(context, b.store, image)
1930
			if err == nil {
1931
				// silently try to unmount and do nothing
1932
				// if image is being used by something else
1933
				_ = i.Unmount(false)
1934
			}
1935
			if errors.Is(err, storageTypes.ErrImageUnknown) {
1936
				// Ignore only if ErrImageUnknown
1937
				// Reason: Image is already unmounted do nothing
1938
				continue
1939
			}
1940
			return err
1941
		}
1942
	}
1943
	opts := copier.RemoveOptions{
1944
		All: true,
1945
	}
1946
	for _, path := range artifacts.RunMountTargets {
1947
		err := copier.Remove(mountpoint, path, opts)
1948
		if err != nil {
1949
			return err
1950
		}
1951
	}
1952
	var prevErr error
1953
	for _, path := range artifacts.TmpFiles {
1954
		err := os.Remove(path)
1955
		if !errors.Is(err, os.ErrNotExist) {
1956
			if prevErr != nil {
1957
				logrus.Error(prevErr)
1958
			}
1959
			prevErr = err
1960
		}
1961
	}
1962
	// unlock if any locked files from this RUN statement
1963
	volumes.UnlockLockArray(artifacts.TargetLocks)
1964
	return prevErr
1965
}
1966

1967
// setPdeathsig sets a parent-death signal for the process
1968
// the goroutine that starts the child process should lock itself to
1969
// a native thread using runtime.LockOSThread() until the child exits
1970
func setPdeathsig(cmd *exec.Cmd) {
1971
	if cmd.SysProcAttr == nil {
1972
		cmd.SysProcAttr = &syscall.SysProcAttr{}
1973
	}
1974
	cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
1975
}
1976

1977
func relabel(path, mountLabel string, recurse bool) error {
1978
	if err := label.Relabel(path, mountLabel, recurse); err != nil {
1979
		if !errors.Is(err, syscall.ENOTSUP) {
1980
			return err
1981
		}
1982
		logrus.Debugf("Labeling not supported on %q", path)
1983
	}
1984
	return nil
1985
}
1986

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.