github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/rootlessport/rootlessport_linux.go (about) 1 // +build linux 2 3 // Package rootlessport provides reexec for RootlessKit-based port forwarder. 4 // 5 // init() contains reexec.Register() for ReexecKey . 6 // 7 // The reexec requires Config to be provided via stdin. 8 // 9 // The reexec writes human-readable error message on stdout on error. 10 // 11 // Debug log is printed on stderr. 12 package rootlessport 13 14 import ( 15 "context" 16 "encoding/json" 17 "fmt" 18 "io" 19 "io/ioutil" 20 "os" 21 "os/exec" 22 "os/signal" 23 24 "github.com/containernetworking/plugins/pkg/ns" 25 "github.com/containers/storage/pkg/reexec" 26 "github.com/cri-o/ocicni/pkg/ocicni" 27 "github.com/pkg/errors" 28 rkport "github.com/rootless-containers/rootlesskit/pkg/port" 29 rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin" 30 rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil" 31 "github.com/sirupsen/logrus" 32 "golang.org/x/sys/unix" 33 ) 34 35 const ( 36 // ReexecKey is the reexec key for the parent process. 37 ReexecKey = "containers-rootlessport" 38 // reexecChildKey is used internally for the second reexec 39 reexecChildKey = "containers-rootlessport-child" 40 reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE" 41 ) 42 43 // Config needs to be provided to the process via stdin as a JSON string. 44 // stdin needs to be closed after the message has been written. 45 type Config struct { 46 Mappings []ocicni.PortMapping 47 NetNSPath string 48 ExitFD int 49 ReadyFD int 50 TmpDir string 51 ChildIP string 52 } 53 54 func init() { 55 reexec.Register(ReexecKey, func() { 56 if err := parent(); err != nil { 57 fmt.Println(err) 58 os.Exit(1) 59 } 60 }) 61 reexec.Register(reexecChildKey, func() { 62 if err := child(); err != nil { 63 fmt.Println(err) 64 os.Exit(1) 65 } 66 }) 67 68 } 69 70 func loadConfig(r io.Reader) (*Config, io.ReadCloser, io.WriteCloser, error) { 71 stdin, err := ioutil.ReadAll(r) 72 if err != nil { 73 return nil, nil, nil, err 74 } 75 var cfg Config 76 if err := json.Unmarshal(stdin, &cfg); err != nil { 77 return nil, nil, nil, err 78 } 79 if cfg.NetNSPath == "" { 80 return nil, nil, nil, errors.New("missing NetNSPath") 81 } 82 if cfg.ExitFD <= 0 { 83 return nil, nil, nil, errors.New("missing ExitFD") 84 } 85 exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile") 86 if exitFile == nil { 87 return nil, nil, nil, errors.New("invalid ExitFD") 88 } 89 if cfg.ReadyFD <= 0 { 90 return nil, nil, nil, errors.New("missing ReadyFD") 91 } 92 readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile") 93 if readyFile == nil { 94 return nil, nil, nil, errors.New("invalid ReadyFD") 95 } 96 return &cfg, exitFile, readyFile, nil 97 } 98 99 func parent() error { 100 // load config from stdin 101 cfg, exitR, readyW, err := loadConfig(os.Stdin) 102 if err != nil { 103 return err 104 } 105 106 exitC := make(chan os.Signal, 1) 107 defer close(exitC) 108 109 go func() { 110 sigC := make(chan os.Signal, 1) 111 signal.Notify(sigC, unix.SIGPIPE) 112 defer func() { 113 signal.Stop(sigC) 114 close(sigC) 115 }() 116 117 select { 118 case s := <-sigC: 119 if s == unix.SIGPIPE { 120 if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil { 121 unix.Dup2(int(f.Fd()), 1) // nolint:errcheck 122 unix.Dup2(int(f.Fd()), 2) // nolint:errcheck 123 f.Close() 124 } 125 } 126 case <-exitC: 127 } 128 }() 129 130 // create the parent driver 131 stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport") 132 if err != nil { 133 return err 134 } 135 defer os.RemoveAll(stateDir) 136 driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir) 137 if err != nil { 138 return err 139 } 140 initComplete := make(chan struct{}) 141 quit := make(chan struct{}) 142 errCh := make(chan error) 143 // start the parent driver. initComplete will be closed when the child connected to the parent. 144 logrus.Infof("starting parent driver") 145 go func() { 146 driverErr := driver.RunParentDriver(initComplete, quit, nil) 147 if driverErr != nil { 148 logrus.WithError(driverErr).Warn("parent driver exited") 149 } 150 errCh <- driverErr 151 close(errCh) 152 }() 153 opaque := driver.OpaqueForChild() 154 logrus.Infof("opaque=%+v", opaque) 155 opaqueJSON, err := json.Marshal(opaque) 156 if err != nil { 157 return err 158 } 159 childQuitR, childQuitW, err := os.Pipe() 160 if err != nil { 161 return err 162 } 163 defer func() { 164 // stop the child 165 logrus.Info("stopping child driver") 166 if err := childQuitW.Close(); err != nil { 167 logrus.WithError(err).Warn("unable to close childQuitW") 168 } 169 }() 170 171 // reexec the child process in the child netns 172 cmd := exec.Command("/proc/self/exe") 173 cmd.Args = []string{reexecChildKey} 174 cmd.Stdin = childQuitR 175 cmd.Stdout = &logrusWriter{prefix: "child"} 176 cmd.Stderr = cmd.Stdout 177 cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON)) 178 childNS, err := ns.GetNS(cfg.NetNSPath) 179 if err != nil { 180 return err 181 } 182 if err := childNS.Do(func(_ ns.NetNS) error { 183 logrus.Infof("starting child driver in child netns (%q %v)", cmd.Path, cmd.Args) 184 return cmd.Start() 185 }); err != nil { 186 return err 187 } 188 189 childErrCh := make(chan error) 190 go func() { 191 err := cmd.Wait() 192 childErrCh <- err 193 close(childErrCh) 194 }() 195 196 defer func() { 197 if err := unix.Kill(cmd.Process.Pid, unix.SIGTERM); err != nil { 198 logrus.WithError(err).Warn("kill child process") 199 } 200 }() 201 202 logrus.Info("waiting for initComplete") 203 // wait for the child to connect to the parent 204 outer: 205 for { 206 select { 207 case <-initComplete: 208 logrus.Infof("initComplete is closed; parent and child established the communication channel") 209 break outer 210 case err := <-childErrCh: 211 if err != nil { 212 return err 213 } 214 case err := <-errCh: 215 if err != nil { 216 return err 217 } 218 } 219 } 220 221 defer func() { 222 logrus.Info("stopping parent driver") 223 quit <- struct{}{} 224 if err := <-errCh; err != nil { 225 logrus.WithError(err).Warn("parent driver returned error on exit") 226 } 227 }() 228 229 // let parent expose ports 230 logrus.Infof("exposing ports %v", cfg.Mappings) 231 if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil { 232 return err 233 } 234 235 // write and close ReadyFD (convention is same as slirp4netns --ready-fd) 236 logrus.Info("ready") 237 if _, err := readyW.Write([]byte("1")); err != nil { 238 return err 239 } 240 if err := readyW.Close(); err != nil { 241 return err 242 } 243 244 // wait for ExitFD to be closed 245 logrus.Info("waiting for exitfd to be closed") 246 if _, err := ioutil.ReadAll(exitR); err != nil { 247 return err 248 } 249 return nil 250 } 251 252 func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping, childIP string) error { 253 ctx := context.TODO() 254 for _, i := range portMappings { 255 hostIP := i.HostIP 256 if hostIP == "" { 257 hostIP = "0.0.0.0" 258 } 259 spec := rkport.Spec{ 260 Proto: i.Protocol, 261 ParentIP: hostIP, 262 ParentPort: int(i.HostPort), 263 ChildPort: int(i.ContainerPort), 264 ChildIP: childIP, 265 } 266 if err := rkportutil.ValidatePortSpec(spec, nil); err != nil { 267 return err 268 } 269 if _, err := pm.AddPort(ctx, spec); err != nil { 270 return err 271 } 272 } 273 return nil 274 } 275 276 func child() error { 277 // load the config from the parent 278 var opaque map[string]string 279 if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil { 280 return err 281 } 282 283 // start the child driver 284 quit := make(chan struct{}) 285 errCh := make(chan error) 286 go func() { 287 d := rkbuiltin.NewChildDriver(os.Stderr) 288 dErr := d.RunChildDriver(opaque, quit) 289 errCh <- dErr 290 }() 291 defer func() { 292 logrus.Info("stopping child driver") 293 quit <- struct{}{} 294 if err := <-errCh; err != nil { 295 logrus.WithError(err).Warn("child driver returned error on exit") 296 } 297 }() 298 299 // wait for stdin to be closed 300 if _, err := ioutil.ReadAll(os.Stdin); err != nil { 301 return err 302 } 303 return nil 304 } 305 306 type logrusWriter struct { 307 prefix string 308 } 309 310 func (w *logrusWriter) Write(p []byte) (int, error) { 311 logrus.Infof("%s%s", w.prefix, string(p)) 312 return len(p), nil 313 }