podman

Форк
0
/
utils_linux.go 
258 строк · 7.4 Кб
1
package util
2

3
import (
4
	"errors"
5
	"fmt"
6
	"io/fs"
7
	"os"
8
	"path/filepath"
9
	"strconv"
10
	"strings"
11
	"syscall"
12

13
	"github.com/containers/podman/v5/libpod/define"
14
	"github.com/containers/podman/v5/pkg/rootless"
15
	"github.com/containers/psgo"
16
	spec "github.com/opencontainers/runtime-spec/specs-go"
17
	"github.com/opencontainers/runtime-tools/generate"
18
	"github.com/sirupsen/logrus"
19
	"golang.org/x/sys/unix"
20
)
21

22
var (
23
	errNotADevice = errors.New("not a device node")
24
)
25

26
// GetContainerPidInformationDescriptors returns a string slice of all supported
27
// format descriptors of GetContainerPidInformation.
28
func GetContainerPidInformationDescriptors() ([]string, error) {
29
	return psgo.ListDescriptors(), nil
30
}
31

32
// FindDeviceNodes parses /dev/ into a set of major:minor -> path, where
33
// [major:minor] is the device's major and minor numbers formatted as, for
34
// example, 2:0 and path is the path to the device node.
35
// Symlinks to nodes are ignored.
36
func FindDeviceNodes() (map[string]string, error) {
37
	nodes := make(map[string]string)
38
	err := filepath.WalkDir("/dev", func(path string, d fs.DirEntry, err error) error {
39
		if err != nil {
40
			if !errors.Is(err, fs.ErrNotExist) {
41
				logrus.Warnf("Error descending into path %s: %v", path, err)
42
			}
43
			return filepath.SkipDir
44
		}
45

46
		// If we aren't a device node, do nothing.
47
		if d.Type()&(os.ModeDevice|os.ModeCharDevice) == 0 {
48
			return nil
49
		}
50

51
		info, err := d.Info()
52
		if err != nil {
53
			// Info() can return ErrNotExist if the file was deleted between the readdir and stat call.
54
			// This race can happen and is no reason to log an ugly error. If this is a container device
55
			// that is used the code later will print a proper error in such case.
56
			// There also seem to be cases were ErrNotExist is always returned likely due a weird device
57
			// state, e.g. removing a device forcefully. This can happen with iSCSI devices.
58
			if !errors.Is(err, fs.ErrNotExist) {
59
				logrus.Errorf("Failed to get device information for %s: %v", path, err)
60
			}
61
			// return nil here as we want to continue looking for more device and not stop the WalkDir()
62
			return nil
63
		}
64
		// We are a device node. Get major/minor.
65
		sysstat, ok := info.Sys().(*syscall.Stat_t)
66
		if !ok {
67
			return errors.New("could not convert stat output for use")
68
		}
69
		// We must typeconvert sysstat.Rdev from uint64->int to avoid constant overflow
70
		rdev := int(sysstat.Rdev)
71
		major := ((rdev >> 8) & 0xfff) | ((rdev >> 32) & ^0xfff)
72
		minor := (rdev & 0xff) | ((rdev >> 12) & ^0xff)
73

74
		nodes[fmt.Sprintf("%d:%d", major, minor)] = path
75

76
		return nil
77
	})
78
	if err != nil {
79
		return nil, err
80
	}
81

82
	return nodes, nil
83
}
84

85
// isVirtualConsoleDevice returns true if path is a virtual console device
86
// (/dev/tty\d+).
87
// The passed path must be clean (filepath.Clean).
88
func isVirtualConsoleDevice(path string) bool {
89
	/*
90
		Virtual consoles are of the form `/dev/tty\d+`, any other device such as
91
		/dev/tty, ttyUSB0, or ttyACM0 should not be matched.
92
		See `man 4 console` for more information.
93
	*/
94
	suffix := strings.TrimPrefix(path, "/dev/tty")
95
	if suffix == path || suffix == "" {
96
		return false
97
	}
98

99
	// 16bit because, max. supported TTY devices is 512 in Linux 6.1.5.
100
	_, err := strconv.ParseUint(suffix, 10, 16)
101
	return err == nil
102
}
103

104
func AddPrivilegedDevices(g *generate.Generator, systemdMode bool) error {
105
	hostDevices, err := getDevices("/dev")
106
	if err != nil {
107
		return err
108
	}
109
	g.ClearLinuxDevices()
110

111
	if rootless.IsRootless() {
112
		mounts := make(map[string]interface{})
113
		for _, m := range g.Mounts() {
114
			mounts[m.Destination] = true
115
		}
116
		newMounts := []spec.Mount{}
117
		for _, d := range hostDevices {
118
			devMnt := spec.Mount{
119
				Destination: d.Path,
120
				Type:        define.TypeBind,
121
				Source:      d.Path,
122
				Options:     []string{"slave", "nosuid", "noexec", "rw", "rbind"},
123
			}
124

125
			/* The following devices should not be mounted in rootless containers:
126
			 *
127
			 *   /dev/ptmx: The host-provided /dev/ptmx should not be shared to
128
			 *              the rootless containers for security reasons, and
129
			 *              the container runtime will create it for us
130
			 *              anyway (ln -s /dev/pts/ptmx /dev/ptmx);
131
			 *   /dev/tty and
132
			 *   /dev/tty[0-9]+: Prevent the container from taking over the host's
133
			 *                   virtual consoles, even when not in systemd mode
134
			 *                   for backwards compatibility.
135
			 */
136
			if d.Path == "/dev/ptmx" || d.Path == "/dev/tty" || isVirtualConsoleDevice(d.Path) {
137
				continue
138
			}
139
			if _, found := mounts[d.Path]; found {
140
				continue
141
			}
142
			newMounts = append(newMounts, devMnt)
143
		}
144
		g.Config.Mounts = append(newMounts, g.Config.Mounts...)
145
		if g.Config.Linux.Resources != nil {
146
			g.Config.Linux.Resources.Devices = nil
147
		}
148
	} else {
149
		for _, d := range hostDevices {
150
			/* Restrict access to the virtual consoles *only* when running
151
			 * in systemd mode to improve backwards compatibility. See
152
			 * https://github.com/containers/podman/issues/15878.
153
			 *
154
			 * NOTE: May need revisiting in the future to drop the systemd
155
			 * condition if more use cases end up breaking the virtual terminals
156
			 * of people who specifically disable the systemd mode. It would
157
			 * also provide a more consistent behaviour between rootless and
158
			 * rootfull containers.
159
			 */
160
			if systemdMode && isVirtualConsoleDevice(d.Path) {
161
				continue
162
			}
163
			g.AddDevice(d)
164
		}
165
		// Add resources device - need to clear the existing one first.
166
		if g.Config.Linux.Resources != nil {
167
			g.Config.Linux.Resources.Devices = nil
168
		}
169
		g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
170
	}
171

172
	return nil
173
}
174

175
// based on getDevices from runc (libcontainer/devices/devices.go)
176
func getDevices(path string) ([]spec.LinuxDevice, error) {
177
	files, err := os.ReadDir(path)
178
	if err != nil {
179
		if rootless.IsRootless() && os.IsPermission(err) {
180
			return nil, nil
181
		}
182
		return nil, err
183
	}
184
	out := []spec.LinuxDevice{}
185
	for _, f := range files {
186
		switch {
187
		case f.IsDir():
188
			switch f.Name() {
189
			// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
190
			case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
191
				continue
192
			default:
193
				sub, err := getDevices(filepath.Join(path, f.Name()))
194
				if err != nil {
195
					return nil, err
196
				}
197
				if sub != nil {
198
					out = append(out, sub...)
199
				}
200
				continue
201
			}
202
		case f.Name() == "console":
203
			continue
204
		case f.Type()&os.ModeSymlink != 0:
205
			continue
206
		}
207

208
		device, err := DeviceFromPath(filepath.Join(path, f.Name()))
209
		if err != nil {
210
			if err == errNotADevice {
211
				continue
212
			}
213
			if os.IsNotExist(err) {
214
				continue
215
			}
216
			return nil, err
217
		}
218
		out = append(out, *device)
219
	}
220
	return out, nil
221
}
222

223
// Copied from github.com/opencontainers/runc/libcontainer/devices
224
// Given the path to a device look up the information about a linux device
225
func DeviceFromPath(path string) (*spec.LinuxDevice, error) {
226
	var stat unix.Stat_t
227
	err := unix.Lstat(path, &stat)
228
	if err != nil {
229
		return nil, err
230
	}
231
	var (
232
		devType   string
233
		mode      = stat.Mode
234
		devNumber = uint64(stat.Rdev) //nolint: unconvert
235
		m         = os.FileMode(mode)
236
	)
237

238
	switch {
239
	case mode&unix.S_IFBLK == unix.S_IFBLK:
240
		devType = "b"
241
	case mode&unix.S_IFCHR == unix.S_IFCHR:
242
		devType = "c"
243
	case mode&unix.S_IFIFO == unix.S_IFIFO:
244
		devType = "p"
245
	default:
246
		return nil, errNotADevice
247
	}
248

249
	return &spec.LinuxDevice{
250
		Type:     devType,
251
		Path:     path,
252
		FileMode: &m,
253
		UID:      &stat.Uid,
254
		GID:      &stat.Gid,
255
		Major:    int64(unix.Major(devNumber)),
256
		Minor:    int64(unix.Minor(devNumber)),
257
	}, nil
258
}
259

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

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

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

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