github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/pkg/rootless/rootless.go (about) 1 // Copyright (c) 2019 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 // Copyright 2015-2019 CNI authors 7 // 8 // Licensed under the Apache License, Version 2.0 (the "License"); 9 // you may not use this file except in compliance with the License. 10 // You may obtain a copy of the License at 11 // 12 // http://www.apache.org/licenses/LICENSE-2.0 13 // 14 // Unless required by applicable law or agreed to in writing, software 15 // distributed under the License is distributed on an "AS IS" BASIS, 16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 // See the License for the specific language governing permissions and 18 // limitations under the License. 19 20 package rootless 21 22 import ( 23 "context" 24 "crypto/rand" 25 "fmt" 26 "os" 27 "path/filepath" 28 "runtime" 29 "strings" 30 "sync" 31 32 "github.com/containernetworking/plugins/pkg/ns" 33 "github.com/opencontainers/runc/libcontainer/system" 34 "github.com/sirupsen/logrus" 35 "golang.org/x/sys/unix" 36 ) 37 38 var ( 39 // isRootless states whether execution is rootless or not 40 // If nil, rootless is auto-detected 41 isRootless *bool 42 43 // lock for the initRootless and isRootless variables 44 rLock sync.Mutex 45 46 // XDG_RUNTIME_DIR defines the base directory relative to 47 // which user-specific non-essential runtime files are stored. 48 rootlessDir = os.Getenv("XDG_RUNTIME_DIR") 49 50 rootlessLog = logrus.WithFields(logrus.Fields{ 51 "source": "rootless", 52 }) 53 54 // IsRootless is declared this way for mocking in unit tests 55 IsRootless = isRootlessFunc 56 ) 57 58 func SetRootless(rootless bool) { 59 isRootless = &rootless 60 } 61 62 // SetLogger sets up a logger for the rootless pkg 63 func SetLogger(ctx context.Context, logger *logrus.Entry) { 64 fields := rootlessLog.Data 65 rootlessLog = logger.WithFields(fields) 66 } 67 68 // isRootlessFunc states whether kata is being ran with root or not 69 func isRootlessFunc() bool { 70 rLock.Lock() 71 defer rLock.Unlock() 72 // auto-detect if nil 73 if isRootless == nil { 74 SetRootless(true) 75 // --rootless and --systemd-cgroup options must honoured 76 // but with the current implementation this is not possible 77 // https://github.com/kata-containers/runtime/issues/2412 78 if os.Geteuid() != 0 { 79 return true 80 } 81 if system.RunningInUserNS() { 82 return true 83 } 84 SetRootless(false) 85 } 86 return *isRootless 87 } 88 89 // GetRootlessDir returns the path to the location for rootless 90 // container and sandbox storage 91 func GetRootlessDir() string { 92 return rootlessDir 93 } 94 95 // Creates a new persistent network namespace and returns an object 96 // representing that namespace, without switching to it 97 func NewNS() (ns.NetNS, error) { 98 nsRunDir := filepath.Join(GetRootlessDir(), "netns") 99 100 b := make([]byte, 16) 101 _, err := rand.Reader.Read(b) 102 if err != nil { 103 return nil, fmt.Errorf("failed to generate random netns name: %v", err) 104 } 105 106 // Create the directory for mounting network namespaces 107 // This needs to be a shared mountpoint in case it is mounted in to 108 // other namespaces (containers) 109 err = os.MkdirAll(nsRunDir, 0755) 110 if err != nil { 111 return nil, err 112 } 113 114 nsName := fmt.Sprintf("net-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 115 116 // create an empty file at the mount point 117 nsPath := filepath.Join(nsRunDir, nsName) 118 mountPointFd, err := os.Create(nsPath) 119 if err != nil { 120 return nil, err 121 } 122 if err := mountPointFd.Close(); err != nil { 123 return nil, err 124 } 125 126 // Ensure the mount point is cleaned up on errors; if the namespace 127 // was successfully mounted this will have no effect because the file 128 // is in-use 129 defer func() { 130 _ = os.RemoveAll(nsPath) 131 }() 132 133 var wg sync.WaitGroup 134 wg.Add(1) 135 136 // do namespace work in a dedicated goroutine, so that we can safely 137 // Lock/Unlock OSThread without upsetting the lock/unlock state of 138 // the caller of this function 139 go (func() { 140 defer wg.Done() 141 runtime.LockOSThread() 142 // Don't unlock. By not unlocking, golang will kill the OS thread when the 143 // goroutine is done (for go1.10+) 144 145 threadNsPath := getCurrentThreadNetNSPath() 146 147 var origNS ns.NetNS 148 origNS, err = ns.GetNS(threadNsPath) 149 if err != nil { 150 rootlessLog.Warnf("cannot open current network namespace %s: %q", threadNsPath, err) 151 return 152 } 153 defer func() { 154 if err := origNS.Close(); err != nil { 155 rootlessLog.Errorf("unable to close namespace: %q", err) 156 } 157 }() 158 159 // create a new netns on the current thread 160 err = unix.Unshare(unix.CLONE_NEWNET) 161 if err != nil { 162 rootlessLog.Warnf("cannot create a new network namespace: %q", err) 163 return 164 } 165 166 // Put this thread back to the orig ns, since it might get reused (pre go1.10) 167 defer func() { 168 if err := origNS.Set(); err != nil { 169 if IsRootless() && strings.Contains(err.Error(), "operation not permitted") { 170 // When running in rootless mode it will fail to re-join 171 // the network namespace owned by root on the host. 172 return 173 } 174 rootlessLog.Warnf("unable to reset namespace: %q", err) 175 } 176 }() 177 178 // bind mount the netns from the current thread (from /proc) onto the 179 // mount point. This causes the namespace to persist, even when there 180 // are no threads in the ns. 181 err = unix.Mount(threadNsPath, nsPath, "none", unix.MS_BIND, "") 182 if err != nil { 183 err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err) 184 } 185 })() 186 wg.Wait() 187 188 if err != nil { 189 unix.Unmount(nsPath, unix.MNT_DETACH) 190 return nil, fmt.Errorf("failed to create namespace: %v", err) 191 } 192 193 return ns.GetNS(nsPath) 194 } 195 196 // getCurrentThreadNetNSPath copied from pkg/ns 197 func getCurrentThreadNetNSPath() string { 198 // /proc/self/ns/net returns the namespace of the main thread, not 199 // of whatever thread this goroutine is running on. Make sure we 200 // use the thread's net namespace since the thread is switching around 201 return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) 202 }