1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// build +linux
package rootless
import (
"fmt"
"io/ioutil"
"os"
gosignal "os/signal"
"runtime"
"strconv"
"syscall"
"github.com/containers/storage/pkg/idtools"
"github.com/docker/docker/pkg/signal"
"github.com/pkg/errors"
)
/*
extern int reexec_in_user_namespace(int ready);
extern int reexec_in_user_namespace_wait(int pid);
*/
import "C"
// IsRootless tells us if we are running in rootless mode
func IsRootless() bool {
return os.Getuid() != 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != ""
}
// GetRootlessUID returns the UID of the user in the parent userNS
func GetRootlessUID() int {
uidEnv := os.Getenv("_LIBPOD_ROOTLESS_UID")
if uidEnv != "" {
u, _ := strconv.Atoi(uidEnv)
return u
}
return os.Getuid()
}
// BecomeRootInUserNS re-exec podman in a new userNS
func BecomeRootInUserNS() (bool, error) {
if os.Getuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" {
if os.Getenv("_LIBPOD_USERNS_CONFIGURED") == "init" {
return false, runInUser()
}
return false, nil
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
r, w, err := os.Pipe()
if err != nil {
return false, err
}
defer r.Close()
defer w.Close()
pidC := C.reexec_in_user_namespace(C.int(r.Fd()))
pid := int(pidC)
if pid < 0 {
return false, errors.Errorf("cannot re-exec process")
}
setgroups := fmt.Sprintf("/proc/%d/setgroups", pid)
err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666)
if err != nil {
return false, errors.Wrapf(err, "cannot write setgroups file")
}
var uids, gids []idtools.IDMap
username := os.Getenv("USER")
mappings, err := idtools.NewIDMappings(username, username)
if err == nil {
uids = mappings.UIDs()
gids = mappings.GIDs()
}
uidsMapped := false
if mappings != nil && uids != nil {
uidsMapped = tryMappingTool("newuidmap", pid, os.Getuid(), uids) == nil
}
if !uidsMapped {
uidMap := fmt.Sprintf("/proc/%d/uid_map", pid)
err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getuid())), 0666)
if err != nil {
return false, errors.Wrapf(err, "cannot write uid_map")
}
}
gidsMapped := false
if mappings != nil && gids != nil {
gidsMapped = tryMappingTool("newgidmap", pid, os.Getgid(), gids) == nil
}
if !gidsMapped {
gidMap := fmt.Sprintf("/proc/%d/gid_map", pid)
err = ioutil.WriteFile(gidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Getgid())), 0666)
if err != nil {
return false, errors.Wrapf(err, "cannot write gid_map")
}
}
_, err = w.Write([]byte("1"))
if err != nil {
return false, errors.Wrapf(err, "write to sync pipe")
}
c := make(chan os.Signal, 1)
gosignal.Notify(c)
defer gosignal.Reset()
go func() {
for s := range c {
if s == signal.SIGCHLD || s == signal.SIGPIPE {
continue
}
syscall.Kill(int(pidC), s.(syscall.Signal))
}
}()
if C.reexec_in_user_namespace_wait(pidC) < 0 {
return false, errors.Wrapf(err, "error waiting for the re-exec process")
}
return true, nil
}
|