2022-12-08 13:05:19 -03:00

103 lines
2.9 KiB
Go

// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package main
import (
"encoding/json"
"os"
"os/exec"
"syscall"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
const RETRY = 3
var (
defaultOOMScoreAdj = 1000
)
func main() {
log := logrus.New()
log.SetLevel(logrus.DebugLevel)
var err error
runcPath, err := exec.LookPath("runc")
if err != nil {
log.WithError(err).Fatal("runc not found")
}
var useFacade bool
for _, arg := range os.Args {
if arg == "create" {
useFacade = true
break
}
}
if useFacade {
err = createAndRunc(runcPath, log)
} else {
err = syscall.Exec(runcPath, os.Args, os.Environ())
}
if err != nil {
log.WithError(err).Fatal("failed")
}
}
func createAndRunc(runcPath string, log *logrus.Logger) error {
fc, err := os.ReadFile("config.json")
if err != nil {
return xerrors.Errorf("cannot read config.json: %w", err)
}
var cfg specs.Spec
err = json.Unmarshal(fc, &cfg)
if err != nil {
return xerrors.Errorf("cannot decode config.json: %w", err)
}
cfg.Process.OOMScoreAdj = &defaultOOMScoreAdj
delete(cfg.Linux.Sysctl, "net.ipv4.ip_unprivileged_port_start")
// TODO(toru): Drop the `kernel.domainame`` setting as it currently fails in the rootless container.
// - https://github.com/opencontainers/runc/issues/2091
// - https://github.com/opencontainers/runtime-spec/issues/592
// Perhaps using OCI hooks can solve this problem, but it's a bit tricky and hard.
if _, ok := cfg.Linux.Sysctl["kernel.domainname"]; ok {
log.Warnln("Since the rootless container cannot use domainname yet, we ignored it.")
delete(cfg.Linux.Sysctl, "kernel.domainname")
}
cfg.Process.Capabilities.Ambient = append(cfg.Process.Capabilities.Ambient, "CAP_NET_BIND_SERVICE")
cfg.Process.Capabilities.Bounding = append(cfg.Process.Capabilities.Bounding, "CAP_NET_BIND_SERVICE")
cfg.Process.Capabilities.Effective = append(cfg.Process.Capabilities.Effective, "CAP_NET_BIND_SERVICE")
cfg.Process.Capabilities.Inheritable = append(cfg.Process.Capabilities.Inheritable, "CAP_NET_BIND_SERVICE")
cfg.Process.Capabilities.Permitted = append(cfg.Process.Capabilities.Permitted, "CAP_NET_BIND_SERVICE")
fc, err = json.Marshal(cfg)
if err != nil {
return xerrors.Errorf("cannot encode config.json: %w", err)
}
for _, fn := range []string{"config.json", "/tmp/debug.json"} {
err = os.WriteFile(fn, fc, 0644)
if err != nil {
return xerrors.Errorf("cannot encode config.json: %w", err)
}
}
// See here for more details on why retries are necessary.
// https://github.com/gitpod-io/gitpod/issues/12365
for i := 0; i <= RETRY; i++ {
err = syscall.Exec(runcPath, os.Args, os.Environ())
if err == nil {
return err
} else {
log.WithError(err).Warn("runc failed")
}
}
return xerrors.Errorf("exec %s: %w", runcPath, err)
}