From 80e63b3c06cabd85645bdf664ed76b2402e6d8cf Mon Sep 17 00:00:00 2001 From: Haiyan Meng Date: Fri, 8 Jul 2016 17:12:45 -0400 Subject: [PATCH] Move generate code into a separate library Signed-off-by: Haiyan Meng --- .gitignore | 2 +- Makefile | 2 +- cmd/ocitools/generate.go | 384 +++++++++ main.go => cmd/ocitools/main.go | 0 validate.go => cmd/ocitools/validate.go | 3 +- generate.go | 852 ------------------- generate/default.go | 21 + generate/generate.go | 1015 +++++++++++++++++++++++ generate/seccomp/doc.go | 2 + 9 files changed, 1426 insertions(+), 855 deletions(-) create mode 100644 cmd/ocitools/generate.go rename main.go => cmd/ocitools/main.go (100%) rename validate.go => cmd/ocitools/validate.go (99%) delete mode 100644 generate.go create mode 100644 generate/default.go create mode 100644 generate/generate.go create mode 100644 generate/seccomp/doc.go diff --git a/.gitignore b/.gitignore index f4736847e..4f37b3967 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ _testmain.go # Compiled binaries oci -ocitools +/ocitools /runtimetest *.1 diff --git a/Makefile b/Makefile index fd00b4247..3991254b5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BUILDTAGS= export GOPATH:=$(CURDIR)/Godeps/_workspace:$(GOPATH) all: - go build -tags "$(BUILDTAGS)" -o ocitools . + go build -tags "$(BUILDTAGS)" -o ocitools ./cmd/ocitools go build -tags "$(BUILDTAGS)" -o runtimetest ./cmd/runtimetest .PHONY: man diff --git a/cmd/ocitools/generate.go b/cmd/ocitools/generate.go new file mode 100644 index 000000000..52ba079da --- /dev/null +++ b/cmd/ocitools/generate.go @@ -0,0 +1,384 @@ +package main + +import ( + "os" + "runtime" + + "github.com/opencontainers/ocitools/generate" + rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +var generateFlags = []cli.Flag{ + cli.StringFlag{Name: "output", Value: "output", Usage: "output file (defaults to stdout)"}, + cli.StringFlag{Name: "rootfs", Value: "rootfs", Usage: "path to the rootfs"}, + cli.BoolFlag{Name: "read-only", Usage: "make the container's rootfs read-only"}, + cli.BoolFlag{Name: "privileged", Usage: "enabled privileged container settings"}, + cli.BoolFlag{Name: "no-new-privileges", Usage: "set no new privileges bit for the container process"}, + cli.BoolFlag{Name: "tty", Usage: "allocate a new tty for the container process"}, + cli.StringFlag{Name: "hostname", Usage: "hostname value for the container"}, + cli.IntFlag{Name: "uid", Usage: "uid for the process"}, + cli.IntFlag{Name: "gid", Usage: "gid for the process"}, + cli.StringSliceFlag{Name: "groups", Usage: "supplementary groups for the process"}, + cli.StringSliceFlag{Name: "cap-add", Usage: "add capabilities"}, + cli.StringSliceFlag{Name: "cap-drop", Usage: "drop capabilities"}, + cli.StringFlag{Name: "cgroup", Usage: "cgroup namespace"}, + cli.StringFlag{Name: "network", Usage: "network namespace"}, + cli.StringFlag{Name: "mount", Usage: "mount namespace"}, + cli.StringFlag{Name: "pid", Usage: "pid namespace"}, + cli.StringFlag{Name: "ipc", Usage: "ipc namespace"}, + cli.StringFlag{Name: "user", Usage: "user namespace"}, + cli.StringFlag{Name: "uts", Usage: "uts namespace"}, + cli.StringFlag{Name: "selinux-label", Usage: "process selinux label"}, + cli.StringFlag{Name: "mount-label", Usage: "selinux mount context label"}, + cli.StringSliceFlag{Name: "tmpfs", Usage: "mount tmpfs"}, + cli.StringSliceFlag{Name: "args", Usage: "command to run in the container"}, + cli.StringSliceFlag{Name: "env", Usage: "add environment variable"}, + cli.StringFlag{Name: "cgroups-path", Usage: "specify the path to the cgroups"}, + cli.StringFlag{Name: "mount-cgroups", Value: "no", Usage: "mount cgroups (rw,ro,no)"}, + cli.StringSliceFlag{Name: "bind", Usage: "bind mount directories src:dest:(rw,ro)"}, + cli.StringSliceFlag{Name: "prestart", Usage: "path to prestart hooks"}, + cli.StringSliceFlag{Name: "poststart", Usage: "path to poststart hooks"}, + cli.StringSliceFlag{Name: "poststop", Usage: "path to poststop hooks"}, + cli.StringFlag{Name: "root-propagation", Usage: "mount propagation for root"}, + cli.StringFlag{Name: "os", Value: runtime.GOOS, Usage: "operating system the container is created for"}, + cli.StringFlag{Name: "arch", Value: runtime.GOARCH, Usage: "architecture the container is created for"}, + cli.StringFlag{Name: "cwd", Value: "/", Usage: "current working directory for the process"}, + cli.StringSliceFlag{Name: "uidmappings", Usage: "add UIDMappings e.g HostID:ContainerID:Size"}, + cli.StringSliceFlag{Name: "gidmappings", Usage: "add GIDMappings e.g HostID:ContainerID:Size"}, + cli.StringSliceFlag{Name: "sysctl", Usage: "add sysctl settings e.g net.ipv4.forward=1"}, + cli.StringFlag{Name: "apparmor", Usage: "specifies the the apparmor profile for the container"}, + cli.StringFlag{Name: "seccomp-default", Usage: "specifies the the defaultaction of Seccomp syscall restrictions"}, + cli.StringSliceFlag{Name: "seccomp-arch", Usage: "specifies Additional architectures permitted to be used for system calls"}, + cli.StringSliceFlag{Name: "seccomp-syscalls", Usage: "specifies Additional architectures permitted to be used for system calls, e.g Name:Action:Arg1_index/Arg1_value/Arg1_valuetwo/Arg1_op, Arg2_index/Arg2_value/Arg2_valuetwo/Arg2_op "}, + cli.StringSliceFlag{Name: "seccomp-allow", Usage: "specifies syscalls to be added to allowed"}, + cli.StringSliceFlag{Name: "seccomp-errno", Usage: "specifies syscalls to be added to list that returns an error"}, + cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"}, + cli.StringSliceFlag{Name: "label", Usage: "add annotations to the configuration e.g. key=value"}, +} + +var generateCommand = cli.Command{ + Name: "generate", + Usage: "generate an OCI spec file", + Flags: generateFlags, + Before: before, + Action: func(context *cli.Context) error { + // Start from the default template. + specgen := generate.New() + + var template string + if context.IsSet("template") { + template = context.String("template") + } + if template != "" { + var err error + specgen, err = generate.NewFromFile(template) + if err != nil { + return err + } + } + + err := setupSpec(specgen, context) + if err != nil { + return err + } + + if context.IsSet("output") { + output := context.String("output") + err = specgen.SaveToFile(output) + } else { + err = specgen.Save(os.Stdout) + } + if err != nil { + return err + } + return nil + }, +} + +func setupSpec(g generate.Generator, context *cli.Context) error { + spec := g.GetSpec() + + if len(spec.Version) == 0 { + g.SetVersion(rspec.Version) + } + + if context.IsSet("hostname") { + g.SetHostname(context.String("hostname")) + } + + g.SetPlatformOS(context.String("os")) + g.SetPlatformArch(context.String("arch")) + + if context.IsSet("label") { + annotations := context.StringSlice("label") + for _, s := range annotations { + if err := g.AddAnnotation(s); err != nil { + return err + } + } + } + + g.SetRootPath(context.String("rootfs")) + + if context.IsSet("read-only") { + g.SetRootReadonly(context.Bool("read-only")) + } + + if context.IsSet("uid") { + g.SetProcessUID(uint32(context.Int("uid"))) + } + + if context.IsSet("gid") { + g.SetProcessGID(uint32(context.Int("gid"))) + } + + if context.IsSet("selinux-label") { + g.SetProcessSelinuxLabel(context.String("selinux-label")) + } + + g.SetProcessCwd(context.String("cwd")) + + if context.IsSet("apparmor") { + g.SetProcessApparmorProfile(context.String("apparmor")) + } + + if context.IsSet("no-new-privileges") { + g.SetProcessNoNewPrivileges(context.Bool("no-new-privileges")) + } + + if context.IsSet("tty") { + g.SetProcessTerminal(context.Bool("tty")) + } + + if context.IsSet("args") { + g.SetProcessArgs(context.StringSlice("args")) + } + + if context.IsSet("env") { + envs := context.StringSlice("env") + for _, env := range envs { + g.AddProcessEnv(env) + } + } + + if context.IsSet("groups") { + groups := context.StringSlice("groups") + for _, group := range groups { + g.AddProcessAdditionalGid(group) + } + } + + if context.IsSet("cgroups-path") { + g.SetLinuxCgroupsPath(context.String("cgroups-path")) + } + + if context.IsSet("mount-label") { + g.SetLinuxMountLabel(context.String("mount-label")) + } + + if context.IsSet("sysctl") { + sysctls := context.StringSlice("sysctl") + for _, s := range sysctls { + g.AddLinuxSysctl(s) + } + } + + privileged := false + if context.IsSet("privileged") { + privileged = context.Bool("privileged") + } + g.SetupPrivileged(privileged) + + if context.IsSet("cap-add") { + addCaps := context.StringSlice("cap-add") + for _, cap := range addCaps { + if err := g.AddProcessCapability(cap); err != nil { + return err + } + } + } + + if context.IsSet("cap-drop") { + dropCaps := context.StringSlice("cap-drop") + for _, cap := range dropCaps { + if err := g.DropProcessCapability(cap); err != nil { + return err + } + } + } + + needsNewUser := false + + var uidMaps, gidMaps []string + + if context.IsSet("uidmappings") { + uidMaps = context.StringSlice("uidmappings") + } + + if context.IsSet("gidmappings") { + gidMaps = context.StringSlice("gidmappings") + } + + if len(uidMaps) > 0 || len(gidMaps) > 0 { + needsNewUser = true + } + + nsMaps := map[string]string{} + for _, nsName := range generate.Namespaces { + if context.IsSet(nsName) { + nsMaps[nsName] = context.String(nsName) + } + } + setupLinuxNamespaces(g, needsNewUser, nsMaps) + + if context.IsSet("tmpfs") { + tmpfsSlice := context.StringSlice("tmpfs") + for _, s := range tmpfsSlice { + if err := g.AddTmpfsMount(s); err != nil { + return err + } + } + } + + mountCgroupOption := context.String("mount-cgroups") + if err := g.AddCgroupsMount(mountCgroupOption); err != nil { + return err + } + + if context.IsSet("bind") { + binds := context.StringSlice("bind") + for _, bind := range binds { + if err := g.AddBindMount(bind); err != nil { + return err + } + } + } + + if context.IsSet("prestart") { + preStartHooks := context.StringSlice("prestart") + for _, hook := range preStartHooks { + if err := g.AddPreStartHook(hook); err != nil { + return err + } + } + } + + if context.IsSet("poststop") { + postStopHooks := context.StringSlice("poststop") + for _, hook := range postStopHooks { + if err := g.AddPostStopHook(hook); err != nil { + return err + } + } + } + + if context.IsSet("poststart") { + postStartHooks := context.StringSlice("poststart") + for _, hook := range postStartHooks { + if err := g.AddPostStartHook(hook); err != nil { + return err + } + } + } + + if context.IsSet("root-propagation") { + rp := context.String("root-propagation") + if err := g.SetLinuxRootPropagation(rp); err != nil { + return err + } + } + + for _, uidMap := range uidMaps { + if err := g.AddLinuxUIDMapping(uidMap); err != nil { + return err + } + } + + for _, gidMap := range gidMaps { + if err := g.AddLinuxGIDMapping(gidMap); err != nil { + return err + } + } + + var sd string + var sa, ss []string + + if context.IsSet("seccomp-default") { + sd = context.String("seccomp-default") + } + + if context.IsSet("seccomp-arch") { + sa = context.StringSlice("seccomp-arch") + } + + if context.IsSet("seccomp-syscalls") { + ss = context.StringSlice("seccomp-syscalls") + } + + if sd == "" && len(sa) == 0 && len(ss) == 0 { + return nil + } + + // Set the DefaultAction of seccomp + if context.IsSet("seccomp-default") { + if err := g.SetLinuxSeccompDefault(sd); err != nil { + return err + } + } + + // Add the additional architectures permitted to be used for system calls + if context.IsSet("seccomp-arch") { + for _, arch := range sa { + if err := g.AddLinuxSeccompArch(arch); err != nil { + return err + } + } + } + + // Set syscall restrict in Seccomp + if context.IsSet("seccomp-syscalls") { + for _, syscall := range ss { + if err := g.AddLinuxSeccompSyscall(syscall); err != nil { + return err + } + } + } + + if context.IsSet("seccomp-allow") { + seccompAllows := context.StringSlice("seccomp-allow") + for _, s := range seccompAllows { + g.AddLinuxSeccompSyscallAllow(s) + } + } + + if context.IsSet("seccomp-errno") { + seccompErrnos := context.StringSlice("seccomp-errno") + for _, s := range seccompErrnos { + g.AddLinuxSeccompSyscallErrno(s) + } + } + + return nil +} + +func checkNs(nsMaps map[string]string, nsName string) bool { + if _, ok := nsMaps[nsName]; !ok { + return false + } + return true +} + +func setupLinuxNamespaces(g generate.Generator, needsNewUser bool, nsMaps map[string]string) { + for _, nsName := range generate.Namespaces { + if !checkNs(nsMaps, nsName) && !(needsNewUser && nsName == "user") { + continue + } + nsPath := nsMaps[nsName] + if nsPath == "host" { + g.RemoveLinuxNamespace(nsName) + continue + } + g.AddOrReplaceLinuxNamespace(nsName, nsPath) + } +} diff --git a/main.go b/cmd/ocitools/main.go similarity index 100% rename from main.go rename to cmd/ocitools/main.go diff --git a/validate.go b/cmd/ocitools/validate.go similarity index 99% rename from validate.go rename to cmd/ocitools/validate.go index e916cc8e7..e0b03d239 100644 --- a/validate.go +++ b/cmd/ocitools/validate.go @@ -16,6 +16,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/blang/semver" + "github.com/opencontainers/ocitools/generate" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" ) @@ -422,7 +423,7 @@ func envValid(env string) bool { } func capValid(capability string) bool { - for _, val := range defaultCaps { + for _, val := range generate.DefaultCaps { if val == capability { return true } diff --git a/generate.go b/generate.go deleted file mode 100644 index b79945e79..000000000 --- a/generate.go +++ /dev/null @@ -1,852 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "runtime" - "strconv" - "strings" - - "github.com/Sirupsen/logrus" - rspec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/syndtr/gocapability/capability" - "github.com/urfave/cli" -) - -var generateFlags = []cli.Flag{ - cli.StringFlag{Name: "output", Value: "output", Usage: "output file (defaults to stdout)"}, - cli.StringFlag{Name: "rootfs", Value: "rootfs", Usage: "path to the rootfs"}, - cli.BoolFlag{Name: "read-only", Usage: "make the container's rootfs read-only"}, - cli.BoolFlag{Name: "privileged", Usage: "enabled privileged container settings"}, - cli.BoolFlag{Name: "no-new-privileges", Usage: "set no new privileges bit for the container process"}, - cli.BoolFlag{Name: "tty", Usage: "allocate a new tty for the container process"}, - cli.StringFlag{Name: "hostname", Usage: "hostname value for the container"}, - cli.IntFlag{Name: "uid", Usage: "uid for the process"}, - cli.IntFlag{Name: "gid", Usage: "gid for the process"}, - cli.StringSliceFlag{Name: "groups", Usage: "supplementary groups for the process"}, - cli.StringSliceFlag{Name: "cap-add", Usage: "add capabilities"}, - cli.StringSliceFlag{Name: "cap-drop", Usage: "drop capabilities"}, - cli.StringFlag{Name: "cgroup", Usage: "cgroup namespace"}, - cli.StringFlag{Name: "network", Usage: "network namespace"}, - cli.StringFlag{Name: "mount", Usage: "mount namespace"}, - cli.StringFlag{Name: "pid", Usage: "pid namespace"}, - cli.StringFlag{Name: "ipc", Usage: "ipc namespace"}, - cli.StringFlag{Name: "user", Usage: "user namespace"}, - cli.StringFlag{Name: "uts", Usage: "uts namespace"}, - cli.StringFlag{Name: "selinux-label", Usage: "process selinux label"}, - cli.StringFlag{Name: "mount-label", Usage: "selinux mount context label"}, - cli.StringSliceFlag{Name: "tmpfs", Usage: "mount tmpfs"}, - cli.StringSliceFlag{Name: "args", Usage: "command to run in the container"}, - cli.StringSliceFlag{Name: "env", Usage: "add environment variable"}, - cli.StringFlag{Name: "cgroups-path", Usage: "specify the path to the cgroups"}, - cli.StringFlag{Name: "mount-cgroups", Value: "no", Usage: "mount cgroups (rw,ro,no)"}, - cli.StringSliceFlag{Name: "bind", Usage: "bind mount directories src:dest:(rw,ro)"}, - cli.StringSliceFlag{Name: "prestart", Usage: "path to prestart hooks"}, - cli.StringSliceFlag{Name: "poststart", Usage: "path to poststart hooks"}, - cli.StringSliceFlag{Name: "poststop", Usage: "path to poststop hooks"}, - cli.StringFlag{Name: "root-propagation", Usage: "mount propagation for root"}, - cli.StringFlag{Name: "os", Value: runtime.GOOS, Usage: "operating system the container is created for"}, - cli.StringFlag{Name: "arch", Value: runtime.GOARCH, Usage: "architecture the container is created for"}, - cli.StringFlag{Name: "cwd", Value: "/", Usage: "current working directory for the process"}, - cli.StringSliceFlag{Name: "uidmappings", Usage: "add UIDMappings e.g HostID:ContainerID:Size"}, - cli.StringSliceFlag{Name: "gidmappings", Usage: "add GIDMappings e.g HostID:ContainerID:Size"}, - cli.StringSliceFlag{Name: "sysctl", Usage: "add sysctl settings e.g net.ipv4.forward=1"}, - cli.StringFlag{Name: "apparmor", Usage: "specifies the the apparmor profile for the container"}, - cli.StringFlag{Name: "seccomp-default", Usage: "specifies the the defaultaction of Seccomp syscall restrictions"}, - cli.StringSliceFlag{Name: "seccomp-arch", Usage: "specifies Additional architectures permitted to be used for system calls"}, - cli.StringSliceFlag{Name: "seccomp-syscalls", Usage: "specifies Additional architectures permitted to be used for system calls, e.g Name:Action:Arg1_index/Arg1_value/Arg1_valuetwo/Arg1_op, Arg2_index/Arg2_value/Arg2_valuetwo/Arg2_op "}, - cli.StringSliceFlag{Name: "seccomp-allow", Usage: "specifies syscalls to be added to allowed"}, - cli.StringSliceFlag{Name: "seccomp-errno", Usage: "specifies syscalls to be added to list that returns an error"}, - cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"}, - cli.StringSliceFlag{Name: "label", Usage: "add annotations to the configuration e.g. key=value"}, -} - -var ( - defaultCaps = []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - } -) - -var generateCommand = cli.Command{ - Name: "generate", - Usage: "generate an OCI spec file", - Flags: generateFlags, - Before: before, - Action: func(context *cli.Context) error { - spec := getDefaultTemplate() - template := context.String("template") - if template != "" { - var err error - spec, err = loadTemplate(template) - if err != nil { - return err - } - } - - err := modify(spec, context) - if err != nil { - return err - } - data, err := json.MarshalIndent(&spec, "", "\t") - if err != nil { - return err - } - if context.IsSet("output") { - output := context.String("output") - if err := ioutil.WriteFile(output, data, 0666); err != nil { - return err - } - } else { - _, err = os.Stdout.Write(data) - if err != nil { - return err - } - } - return nil - }, -} - -func loadTemplate(path string) (spec *rspec.Spec, err error) { - cf, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf("template configuration at %s not found", path) - } - } - defer cf.Close() - - if err = json.NewDecoder(cf).Decode(&spec); err != nil { - return - } - return spec, nil -} - -func modify(spec *rspec.Spec, context *cli.Context) error { - if len(spec.Version) == 0 { - spec.Version = rspec.Version - } - spec.Root.Path = context.String("rootfs") - if context.IsSet("read-only") { - spec.Root.Readonly = context.Bool("read-only") - } - spec.Hostname = context.String("hostname") - spec.Process.User.UID = uint32(context.Int("uid")) - spec.Process.User.GID = uint32(context.Int("gid")) - if spec.Process.Args == nil { - spec.Process.Args = make([]string, 0) - } - spec.Process.SelinuxLabel = context.String("selinux-label") - spec.Linux.CgroupsPath = sPtr(context.String("cgroups-path")) - spec.Linux.MountLabel = context.String("mount-label") - spec.Platform.OS = context.String("os") - spec.Platform.Arch = context.String("arch") - spec.Process.Cwd = context.String("cwd") - spec.Process.ApparmorProfile = context.String("apparmor") - if context.IsSet("no-new-privileges") { - spec.Process.NoNewPrivileges = context.Bool("no-new-privileges") - } - if context.IsSet("tty") { - spec.Process.Terminal = context.Bool("tty") - } - - for i, a := range context.StringSlice("args") { - if a != "" { - if i == 0 { - // Replace "sh" from getDefaultTemplate() - spec.Process.Args[0] = a - } else { - spec.Process.Args = append(spec.Process.Args, a) - } - } - } - - for _, e := range context.StringSlice("env") { - spec.Process.Env = append(spec.Process.Env, e) - } - - groups := context.StringSlice("groups") - if groups != nil { - for _, g := range groups { - groupID, err := strconv.Atoi(g) - if err != nil { - return err - } - spec.Process.User.AdditionalGids = append(spec.Process.User.AdditionalGids, uint32(groupID)) - } - } - - spec.Linux.Sysctl = make(map[string]string) - sysctls := context.StringSlice("sysctl") - for _, s := range sysctls { - pair := strings.Split(s, "=") - if len(pair) != 2 { - return fmt.Errorf("incorrectly specified sysctl: %s", s) - } - spec.Linux.Sysctl[pair[0]] = pair[1] - } - - spec.Annotations = make(map[string]string) - labels := context.StringSlice("label") - for _, l := range labels { - pair := strings.Split(l, "=") - if len(pair) != 2 { - return fmt.Errorf("incorrectly specified label: %s", l) - } - spec.Annotations[pair[0]] = pair[1] - } - - if err := setupCapabilities(spec, context); err != nil { - return err - } - - setupNamespaces(spec, context) - if err := addTmpfsMounts(spec, context); err != nil { - return err - } - if err := mountCgroups(spec, context); err != nil { - return err - } - if err := addBindMounts(spec, context); err != nil { - return err - } - if err := addHooks(spec, context); err != nil { - return err - } - if err := addRootPropagation(spec, context); err != nil { - return err - } - if err := addIDMappings(spec, context); err != nil { - return err - } - if err := addSeccomp(spec, context); err != nil { - return err - } - - return nil -} - -func addSeccompDefault(spec *rspec.Spec, sdefault string, secc *rspec.Seccomp) error { - switch sdefault { - case "": - case "SCMP_ACT_KILL": - case "SCMP_ACT_TRAP": - case "SCMP_ACT_ERRNO": - case "SCMP_ACT_TRACE": - case "SCMP_ACT_ALLOW": - default: - return fmt.Errorf("seccomp-default must be empty or one of " + - "SCMP_ACT_KILL|SCMP_ACT_TRAP|SCMP_ACT_ERRNO|SCMP_ACT_TRACE|" + - "SCMP_ACT_ALLOW") - } - secc.DefaultAction = rspec.Action(sdefault) - return nil -} - -func addSeccompArch(spec *rspec.Spec, sArch []string, secc *rspec.Seccomp) error { - for _, archs := range sArch { - switch archs { - case "": - case "SCMP_ARCH_X86": - case "SCMP_ARCH_X86_64": - case "SCMP_ARCH_X32": - case "SCMP_ARCH_ARM": - case "SCMP_ARCH_AARCH64": - case "SCMP_ARCH_MIPS": - case "SCMP_ARCH_MIPS64": - case "SCMP_ARCH_MIPS64N32": - case "SCMP_ARCH_MIPSEL": - case "SCMP_ARCH_MIPSEL64": - case "SCMP_ARCH_MIPSEL64N32": - default: - return fmt.Errorf("seccomp-arch must be empty or one of " + - "SCMP_ARCH_X86|SCMP_ARCH_X86_64|SCMP_ARCH_X32|SCMP_ARCH_ARM|" + - "SCMP_ARCH_AARCH64SCMP_ARCH_MIPS|SCMP_ARCH_MIPS64|" + - "SCMP_ARCH_MIPS64N32|SCMP_ARCH_MIPSEL|SCMP_ARCH_MIPSEL64|" + - "SCMP_ARCH_MIPSEL64N32") - } - secc.Architectures = append(secc.Architectures, rspec.Arch(archs)) - } - - return nil -} - -func addSeccompSyscall(spec *rspec.Spec, sSyscall []string, secc *rspec.Seccomp) error { - for _, syscalls := range sSyscall { - syscall := strings.Split(syscalls, ":") - if len(syscall) == 3 { - name := syscall[0] - switch syscall[1] { - case "": - case "SCMP_ACT_KILL": - case "SCMP_ACT_TRAP": - case "SCMP_ACT_ERRNO": - case "SCMP_ACT_TRACE": - case "SCMP_ACT_ALLOW": - default: - return fmt.Errorf("seccomp-syscall action must be empty or " + - "one of SCMP_ACT_KILL|SCMP_ACT_TRAP|SCMP_ACT_ERRNO|" + - "SCMP_ACT_TRACE|SCMP_ACT_ALLOW") - } - action := rspec.Action(syscall[1]) - - var Args []rspec.Arg - if strings.EqualFold(syscall[2], "") { - Args = nil - } else { - - argsslice := strings.Split(syscall[2], ",") - for _, argsstru := range argsslice { - args := strings.Split(argsstru, "/") - if len(args) == 4 { - index, err := strconv.Atoi(args[0]) - value, err := strconv.Atoi(args[1]) - value2, err := strconv.Atoi(args[2]) - if err != nil { - return err - } - switch args[3] { - case "": - case "SCMP_CMP_NE": - case "SCMP_CMP_LT": - case "SCMP_CMP_LE": - case "SCMP_CMP_EQ": - case "SCMP_CMP_GE": - case "SCMP_CMP_GT": - case "SCMP_CMP_MASKED_EQ": - default: - return fmt.Errorf("seccomp-syscall args must be " + - "empty or one of SCMP_CMP_NE|SCMP_CMP_LT|" + - "SCMP_CMP_LE|SCMP_CMP_EQ|SCMP_CMP_GE|" + - "SCMP_CMP_GT|SCMP_CMP_MASKED_EQ") - } - op := rspec.Operator(args[3]) - Arg := rspec.Arg{ - Index: uint(index), - Value: uint64(value), - ValueTwo: uint64(value2), - Op: op, - } - Args = append(Args, Arg) - } else { - return fmt.Errorf("seccomp-sysctl args error: %s", argsstru) - } - } - } - - syscallstruct := rspec.Syscall{ - Name: name, - Action: action, - Args: Args, - } - secc.Syscalls = append(secc.Syscalls, syscallstruct) - } else { - return fmt.Errorf("seccomp sysctl must consist of 3 parameters") - } - } - - return nil -} - -func addSeccomp(spec *rspec.Spec, context *cli.Context) error { - var secc rspec.Seccomp - sd := context.String("seccomp-default") - sa := context.StringSlice("seccomp-arch") - ss := context.StringSlice("seccomp-syscalls") - - if sd == "" && len(sa) == 0 && len(ss) == 0 { - return nil - } - - // Set the DefaultAction of seccomp - err := addSeccompDefault(spec, sd, &secc) - if err != nil { - return err - } - - // Add the additional architectures permitted to be used for system calls - err = addSeccompArch(spec, sa, &secc) - if err != nil { - return err - } - - // Set syscall restrict in Seccomp - // The format of input syscall string is Name:Action:Args[1],Args[2],...,Args[n] - // The format of Args string is Index/Value/ValueTwo/Operator,and is parsed by function parseArgs() - err = addSeccompSyscall(spec, ss, &secc) - if err != nil { - return err - } - - for _, a := range context.StringSlice("seccomp-allow") { - sysCall := rspec.Syscall{ - Name: a, - Action: "SCMP_ACT_ALLOW", - } - secc.Syscalls = append(secc.Syscalls, sysCall) - } - - for _, a := range context.StringSlice("seccomp-errno") { - sysCall := rspec.Syscall{ - Name: a, - Action: "SCMP_ACT_ERRNO", - } - secc.Syscalls = append(secc.Syscalls, sysCall) - } - - spec.Linux.Seccomp = &secc - return nil -} - -func parseArgs(args2parse string) ([]*rspec.Arg, error) { - var Args []*rspec.Arg - argstrslice := strings.Split(args2parse, ",") - for _, argstr := range argstrslice { - args := strings.Split(argstr, "/") - if len(args) == 4 { - index, err := strconv.Atoi(args[0]) - value, err := strconv.Atoi(args[1]) - value2, err := strconv.Atoi(args[2]) - if err != nil { - return nil, err - } - switch args[3] { - case "": - case "SCMP_CMP_NE": - case "SCMP_CMP_LT": - case "SCMP_CMP_LE": - case "SCMP_CMP_EQ": - case "SCMP_CMP_GE": - case "SCMP_CMP_GT": - case "SCMP_CMP_MASKED_EQ": - default: - return nil, fmt.Errorf("seccomp-sysctl args must be empty or one of SCMP_CMP_NE|SCMP_CMP_LT|SCMP_CMP_LE|SCMP_CMP_EQ|SCMP_CMP_GE|SCMP_CMP_GT|SCMP_CMP_MASKED_EQ") - } - op := rspec.Operator(args[3]) - Arg := rspec.Arg{ - Index: uint(index), - Value: uint64(value), - ValueTwo: uint64(value2), - Op: op, - } - Args = append(Args, &Arg) - } else { - return nil, fmt.Errorf("seccomp-sysctl args error: %s", argstr) - } - } - return Args, nil -} - -func addIDMappings(spec *rspec.Spec, context *cli.Context) error { - for _, uidms := range context.StringSlice("uidmappings") { - idm := strings.Split(uidms, ":") - if len(idm) == 3 { - hid, err := strconv.Atoi(idm[0]) - cid, err := strconv.Atoi(idm[1]) - size, err := strconv.Atoi(idm[2]) - if err != nil { - return err - } - uidmapping := rspec.IDMapping{ - HostID: uint32(hid), - ContainerID: uint32(cid), - Size: uint32(size), - } - spec.Linux.UIDMappings = append(spec.Linux.UIDMappings, uidmapping) - } else { - return fmt.Errorf("uidmappings error: %s", uidms) - } - } - - for _, gidms := range context.StringSlice("gidmappings") { - idm := strings.Split(gidms, ":") - if len(idm) == 3 { - hid, err := strconv.Atoi(idm[0]) - cid, err := strconv.Atoi(idm[1]) - size, err := strconv.Atoi(idm[2]) - if err != nil { - return err - } - gidmapping := rspec.IDMapping{ - HostID: uint32(hid), - ContainerID: uint32(cid), - Size: uint32(size), - } - spec.Linux.GIDMappings = append(spec.Linux.GIDMappings, gidmapping) - } else { - return fmt.Errorf("gidmappings error: %s", gidms) - } - } - - return nil -} - -func addRootPropagation(spec *rspec.Spec, context *cli.Context) error { - rp := context.String("root-propagation") - switch rp { - case "": - case "private": - case "rprivate": - case "slave": - case "rslave": - case "shared": - case "rshared": - default: - return fmt.Errorf("rootfs-propagation must be empty or one of private|rprivate|slave|rslave|shared|rshared") - } - spec.Linux.RootfsPropagation = rp - return nil -} - -func addHooks(spec *rspec.Spec, context *cli.Context) error { - for _, pre := range context.StringSlice("prestart") { - parts := strings.Split(pre, ":") - args := []string{} - path := parts[0] - if len(parts) > 1 { - args = parts[1:] - } - spec.Hooks.Prestart = append(spec.Hooks.Prestart, rspec.Hook{Path: path, Args: args}) - } - for _, post := range context.StringSlice("poststop") { - parts := strings.Split(post, ":") - args := []string{} - path := parts[0] - if len(parts) > 1 { - args = parts[1:] - } - spec.Hooks.Poststop = append(spec.Hooks.Poststop, rspec.Hook{Path: path, Args: args}) - } - for _, poststart := range context.StringSlice("poststart") { - parts := strings.Split(poststart, ":") - args := []string{} - path := parts[0] - if len(parts) > 1 { - args = parts[1:] - } - spec.Hooks.Poststart = append(spec.Hooks.Poststart, rspec.Hook{Path: path, Args: args}) - } - return nil -} - -func addTmpfsMounts(spec *rspec.Spec, context *cli.Context) error { - for _, dest := range context.StringSlice("tmpfs") { - mnt := rspec.Mount{ - Destination: dest, - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "nodev", "mode=755"}, - } - spec.Mounts = append(spec.Mounts, mnt) - } - return nil -} - -func mountCgroups(spec *rspec.Spec, context *cli.Context) error { - mountCgroupOption := context.String("mount-cgroups") - switch mountCgroupOption { - case "ro": - case "rw": - case "no": - return nil - default: - return fmt.Errorf("--mount-cgroups should be one of (ro,rw,no)") - } - - mnt := rspec.Mount{ - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Source: "cgroup", - Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption}, - } - spec.Mounts = append(spec.Mounts, mnt) - - return nil -} - -func addBindMounts(spec *rspec.Spec, context *cli.Context) error { - for _, b := range context.StringSlice("bind") { - var source, dest string - options := "ro" - bparts := strings.SplitN(b, ":", 3) - switch len(bparts) { - case 2: - source, dest = bparts[0], bparts[1] - case 3: - source, dest, options = bparts[0], bparts[1], bparts[2] - default: - return fmt.Errorf("--bind should have format src:dest:[options]") - } - defaultOptions := []string{"bind"} - mnt := rspec.Mount{ - Destination: dest, - Type: "bind", - Source: source, - Options: append(defaultOptions, options), - } - spec.Mounts = append(spec.Mounts, mnt) - } - return nil -} - -func setupCapabilities(spec *rspec.Spec, context *cli.Context) error { - var finalCapList []string - - // Add all capabilities in privileged mode. - privileged := false - if context.IsSet("privileged") { - privileged = context.Bool("privileged") - } - if privileged { - for _, cap := range capability.List() { - finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) - } - spec.Process.Capabilities = finalCapList - spec.Process.SelinuxLabel = "" - spec.Process.ApparmorProfile = "" - spec.Linux.Seccomp = nil - return nil - } - - capMappings := make(map[string]bool) - for _, cap := range capability.List() { - key := strings.ToUpper(cap.String()) - capMappings[key] = true - } - - defaultCaps := spec.Process.Capabilities - - addedCapsMap := make(map[string]bool) - for _, cap := range defaultCaps { - addedCapsMap[cap] = true - } - - addCapList := make([]string, len(defaultCaps)) - copy(addCapList, defaultCaps) - addCaps := context.StringSlice("cap-add") - for _, c := range addCaps { - if !capMappings[c] { - return fmt.Errorf("Invalid value passed for adding capability") - } - cp := fmt.Sprintf("CAP_%s", c) - if !addedCapsMap[cp] { - addCapList = append(addCapList, cp) - addedCapsMap[cp] = true - } - } - dropCaps := context.StringSlice("cap-drop") - dropCapsMap := make(map[string]bool) - for _, c := range dropCaps { - if !capMappings[c] { - return fmt.Errorf("Invalid value passed for dropping capability") - } - cp := fmt.Sprintf("CAP_%s", c) - dropCapsMap[cp] = true - } - - for _, c := range addCapList { - if !dropCapsMap[c] { - finalCapList = append(finalCapList, c) - } - } - spec.Process.Capabilities = finalCapList - return nil -} - -func mapStrToNamespace(ns string, path string) rspec.Namespace { - switch ns { - case "network": - return rspec.Namespace{Type: rspec.NetworkNamespace, Path: path} - case "pid": - return rspec.Namespace{Type: rspec.PIDNamespace, Path: path} - case "mount": - return rspec.Namespace{Type: rspec.MountNamespace, Path: path} - case "ipc": - return rspec.Namespace{Type: rspec.IPCNamespace, Path: path} - case "uts": - return rspec.Namespace{Type: rspec.UTSNamespace, Path: path} - case "user": - return rspec.Namespace{Type: rspec.UserNamespace, Path: path} - case "cgroup": - return rspec.Namespace{Type: rspec.CgroupNamespace, Path: path} - default: - logrus.Fatalf("Should not reach here!") - } - return rspec.Namespace{} -} - -func setupNamespaces(spec *rspec.Spec, context *cli.Context) { - var needsNewUser = false - if len(context.StringSlice("uidmappings")) > 0 || len(context.StringSlice("gidmappings")) > 0 { - needsNewUser = true - } - - namespaces := []string{"network", "pid", "mount", "ipc", "uts", "user", "cgroup"} - for _, nsName := range namespaces { - if !context.IsSet(nsName) && !(needsNewUser && nsName == "user") { - continue - } - nsPath := context.String(nsName) - if nsPath == "host" { - ns := mapStrToNamespace(nsName, "") - removeNamespace(&spec.Linux.Namespaces, ns.Type) - continue - } - ns := mapStrToNamespace(nsName, nsPath) - replaceOrAppendNamespace(&spec.Linux.Namespaces, ns) - } -} - -func replaceOrAppendNamespace(namespaces *[]rspec.Namespace, namespace rspec.Namespace) { - for i, ns := range *namespaces { - if ns.Type == namespace.Type { - (*namespaces)[i] = namespace - return - } - } - new := append(*namespaces, namespace) - *namespaces = new -} - -func removeNamespace(namespaces *[]rspec.Namespace, namespaceType rspec.NamespaceType) { - for i, ns := range *namespaces { - if ns.Type == namespaceType { - *namespaces = append((*namespaces)[:i], (*namespaces)[i+1:]...) - return - } - } -} - -func sPtr(s string) *string { return &s } - -func getDefaultTemplate() *rspec.Spec { - spec := rspec.Spec{ - Version: rspec.Version, - Platform: rspec.Platform{ - OS: runtime.GOOS, - Arch: runtime.GOARCH, - }, - Root: rspec.Root{ - Path: "", - Readonly: false, - }, - Process: rspec.Process{ - Terminal: false, - User: rspec.User{}, - Args: []string{ - "sh", - }, - Env: []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "TERM=xterm", - }, - Cwd: "/", - Capabilities: []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - }, - Rlimits: []rspec.Rlimit{ - { - Type: "RLIMIT_NOFILE", - Hard: uint64(1024), - Soft: uint64(1024), - }, - }, - }, - Hostname: "shell", - Mounts: []rspec.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - Options: nil, - }, - { - Destination: "/dev", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - { - Destination: "/dev/shm", - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, - }, - { - Destination: "/dev/mqueue", - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "ro"}, - }, - }, - Linux: rspec.Linux{ - Resources: &rspec.Resources{ - Devices: []rspec.DeviceCgroup{ - { - Allow: false, - Access: sPtr("rwm"), - }, - }, - }, - Namespaces: []rspec.Namespace{ - { - Type: "pid", - }, - { - Type: "network", - }, - { - Type: "ipc", - }, - { - Type: "uts", - }, - { - Type: "mount", - }, - }, - Devices: []rspec.Device{}, - }, - } - - return &spec -} diff --git a/generate/default.go b/generate/default.go new file mode 100644 index 000000000..85a30ebf0 --- /dev/null +++ b/generate/default.go @@ -0,0 +1,21 @@ +package generate + +var ( + // DefaultCaps include the default capabilities. + DefaultCaps = []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + } +) diff --git a/generate/generate.go b/generate/generate.go new file mode 100644 index 000000000..8cf98179e --- /dev/null +++ b/generate/generate.go @@ -0,0 +1,1015 @@ +// Package generate implements functions generating container config files. +package generate + +import ( + "encoding/json" + "fmt" + "io" + "os" + "runtime" + "strconv" + "strings" + + rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" +) + +var ( + // Namespaces include the names of supported namespaces. + Namespaces = []string{"network", "pid", "mount", "ipc", "uts", "user", "cgroup"} +) + +// Generator represents a generator for a container spec. +type Generator struct { + spec *rspec.Spec +} + +// New creates a spec Generator with the default spec. +func New() Generator { + spec := rspec.Spec{ + Version: rspec.Version, + Platform: rspec.Platform{ + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, + Root: rspec.Root{ + Path: "", + Readonly: false, + }, + Process: rspec.Process{ + Terminal: false, + User: rspec.User{}, + Args: []string{ + "sh", + }, + Env: []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + }, + Cwd: "/", + Capabilities: []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + }, + Rlimits: []rspec.Rlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, + }, + Hostname: "mrsdalloway", + Mounts: []rspec.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + Options: nil, + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + }, + Linux: rspec.Linux{ + Resources: &rspec.Resources{ + Devices: []rspec.DeviceCgroup{ + { + Allow: false, + Access: strPtr("rwm"), + }, + }, + }, + Namespaces: []rspec.Namespace{ + { + Type: "pid", + }, + { + Type: "network", + }, + { + Type: "ipc", + }, + { + Type: "uts", + }, + { + Type: "mount", + }, + }, + Devices: []rspec.Device{}, + }, + } + return Generator{&spec} +} + +// NewFromSpec creates a spec Generator from a given spec. +func NewFromSpec(spec *rspec.Spec) Generator { + return Generator{spec} +} + +// NewFromFile loads the template specifed in a file into a spec Generator. +func NewFromFile(path string) (Generator, error) { + cf, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return Generator{}, fmt.Errorf("template configuration at %s not found", path) + } + } + defer cf.Close() + + return NewFromTemplate(cf) +} + +// NewFromTemplate loads the template from io.Reader into a spec Generator. +func NewFromTemplate(r io.Reader) (Generator, error) { + var spec rspec.Spec + if err := json.NewDecoder(r).Decode(&spec); err != nil { + return Generator{}, err + } + return Generator{&spec}, nil +} + +// SetSpec sets the spec in the Generator g. +func (g Generator) SetSpec(spec *rspec.Spec) { + g.spec = spec +} + +// GetSpec gets the spec in the Generator g. +func (g Generator) GetSpec() *rspec.Spec { + return g.spec +} + +// Save writes the spec into w. +func (g Generator) Save(w io.Writer) error { + data, err := json.MarshalIndent(g.spec, "", "\t") + if err != nil { + return err + } + + _, err = w.Write(data) + if err != nil { + return err + } + + return nil +} + +// SaveToFile writes the spec into a file. +func (g Generator) SaveToFile(path string) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return g.Save(f) +} + +// SetVersion sets g.spec.Version. +func (g Generator) SetVersion(version string) { + g.spec.Version = version +} + +// SetRootPath sets g.spec.Root.Path. +func (g Generator) SetRootPath(path string) { + g.spec.Root.Path = path +} + +// SetRootReadonly sets g.spec.Root.Readonly. +func (g Generator) SetRootReadonly(b bool) { + g.spec.Root.Readonly = b +} + +// SetHostname sets g.spec.Hostname. +func (g Generator) SetHostname(s string) { + g.spec.Hostname = s +} + +// ClearAnnotations clears g.spec.Annotations. +func (g Generator) ClearAnnotations() { + g.spec.Annotations = make(map[string]string) +} + +// AddAnnotation adds an annotation into g.spec.Annotations. +func (g Generator) AddAnnotation(s string) error { + if g.spec.Annotations == nil { + g.spec.Annotations = make(map[string]string) + } + + pair := strings.Split(s, "=") + if len(pair) != 2 { + return fmt.Errorf("incorrectly specified annotation: %s", s) + } + g.spec.Annotations[pair[0]] = pair[1] + return nil +} + +// RemoveAnnotation remove an annotation from g.spec.Annotations. +func (g Generator) RemoveAnnotation(key string) { + if g.spec.Annotations == nil { + return + } + delete(g.spec.Annotations, key) +} + +// SetPlatformOS sets g.spec.Process.OS. +func (g Generator) SetPlatformOS(os string) { + g.spec.Platform.OS = os +} + +// SetPlatformArch sets g.spec.Platform.Arch. +func (g Generator) SetPlatformArch(arch string) { + g.spec.Platform.Arch = arch +} + +// SetProcessUID sets g.spec.Process.User.UID. +func (g Generator) SetProcessUID(uid uint32) { + g.spec.Process.User.UID = uid +} + +// SetProcessGID sets g.spec.Process.User.GID. +func (g Generator) SetProcessGID(gid uint32) { + g.spec.Process.User.GID = gid +} + +// SetProcessCwd sets g.spec.Process.Cwd. +func (g Generator) SetProcessCwd(cwd string) { + g.spec.Process.Cwd = cwd +} + +// SetProcessNoNewPrivileges sets g.spec.Process.NoNewPrivileges. +func (g Generator) SetProcessNoNewPrivileges(b bool) { + g.spec.Process.NoNewPrivileges = b +} + +// SetProcessTerminal sets g.spec.Process.Terminal. +func (g Generator) SetProcessTerminal(b bool) { + g.spec.Process.Terminal = b +} + +// SetProcessApparmorProfile sets g.spec.Process.ApparmorProfile. +func (g Generator) SetProcessApparmorProfile(prof string) { + g.spec.Process.ApparmorProfile = prof +} + +// SetProcessArgs sets g.spec.Process.Args. +func (g Generator) SetProcessArgs(args []string) { + g.spec.Process.Args = args +} + +// ClearProcessEnv clears g.spec.Process.Env. +func (g Generator) ClearProcessEnv() { + g.spec.Process.Env = []string{} +} + +// AddProcessEnv adds env into g.spec.Process.Env. +func (g Generator) AddProcessEnv(env string) { + g.spec.Process.Env = append(g.spec.Process.Env, env) +} + +// ClearProcessAdditionalGids clear g.spec.Process.AdditionalGids. +func (g Generator) ClearProcessAdditionalGids() { + g.spec.Process.User.AdditionalGids = []uint32{} +} + +// AddProcessAdditionalGid adds an additional gid into g.spec.Process.AdditionalGids. +func (g Generator) AddProcessAdditionalGid(gid string) error { + groupID, err := strconv.Atoi(gid) + if err != nil { + return err + } + + for _, group := range g.spec.Process.User.AdditionalGids { + if group == uint32(groupID) { + return nil + } + } + g.spec.Process.User.AdditionalGids = append(g.spec.Process.User.AdditionalGids, uint32(groupID)) + return nil +} + +// SetProcessSelinuxLabel sets g.spec.Process.SelinuxLabel. +func (g Generator) SetProcessSelinuxLabel(label string) { + g.spec.Process.SelinuxLabel = label +} + +// SetLinuxCgroupsPath sets g.spec.Linux.CgroupsPath. +func (g Generator) SetLinuxCgroupsPath(path string) { + g.spec.Linux.CgroupsPath = strPtr(path) +} + +// SetLinuxMountLabel sets g.spec.Linux.MountLabel. +func (g Generator) SetLinuxMountLabel(label string) { + g.spec.Linux.MountLabel = label +} + +// ClearLinuxSysctl clears g.spec.Linux.Sysctl. +func (g Generator) ClearLinuxSysctl() { + g.spec.Linux.Sysctl = make(map[string]string) +} + +// AddLinuxSysctl adds a new sysctl config into g.spec.Linux.Sysctl. +func (g Generator) AddLinuxSysctl(s string) error { + if g.spec.Linux.Sysctl == nil { + g.spec.Linux.Sysctl = make(map[string]string) + } + + pair := strings.Split(s, "=") + if len(pair) != 2 { + return fmt.Errorf("incorrectly specified sysctl: %s", s) + } + g.spec.Linux.Sysctl[pair[0]] = pair[1] + return nil +} + +// RemoveLinuxSysctl removes a sysctl config from g.spec.Linux.Sysctl. +func (g Generator) RemoveLinuxSysctl(key string) { + if g.spec.Linux.Sysctl == nil { + return + } + delete(g.spec.Linux.Sysctl, key) +} + +// SetLinuxSeccompDefault sets g.spec.Linux.Seccomp.DefaultAction. +func (g Generator) SetLinuxSeccompDefault(sdefault string) error { + switch sdefault { + case "": + case "SCMP_ACT_KILL": + case "SCMP_ACT_TRAP": + case "SCMP_ACT_ERRNO": + case "SCMP_ACT_TRACE": + case "SCMP_ACT_ALLOW": + default: + return fmt.Errorf("seccomp-default must be empty or one of " + + "SCMP_ACT_KILL|SCMP_ACT_TRAP|SCMP_ACT_ERRNO|SCMP_ACT_TRACE|" + + "SCMP_ACT_ALLOW") + } + + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } + + g.spec.Linux.Seccomp.DefaultAction = rspec.Action(sdefault) + return nil +} + +func checkSeccompArch(arch string) error { + switch arch { + case "": + case "SCMP_ARCH_X86": + case "SCMP_ARCH_X86_64": + case "SCMP_ARCH_X32": + case "SCMP_ARCH_ARM": + case "SCMP_ARCH_AARCH64": + case "SCMP_ARCH_MIPS": + case "SCMP_ARCH_MIPS64": + case "SCMP_ARCH_MIPS64N32": + case "SCMP_ARCH_MIPSEL": + case "SCMP_ARCH_MIPSEL64": + case "SCMP_ARCH_MIPSEL64N32": + default: + return fmt.Errorf("seccomp-arch must be empty or one of " + + "SCMP_ARCH_X86|SCMP_ARCH_X86_64|SCMP_ARCH_X32|SCMP_ARCH_ARM|" + + "SCMP_ARCH_AARCH64SCMP_ARCH_MIPS|SCMP_ARCH_MIPS64|" + + "SCMP_ARCH_MIPS64N32|SCMP_ARCH_MIPSEL|SCMP_ARCH_MIPSEL64|" + + "SCMP_ARCH_MIPSEL64N32") + } + return nil +} + +// ClearLinuxSeccompArch clears g.spec.Linux.Seccomp.Architectures. +func (g Generator) ClearLinuxSeccompArch() { + if g.spec.Linux.Seccomp == nil { + return + } + + g.spec.Linux.Seccomp.Architectures = []rspec.Arch{} +} + +// AddLinuxSeccompArch adds sArch into g.spec.Linux.Seccomp.Architectures. +func (g Generator) AddLinuxSeccompArch(sArch string) error { + if err := checkSeccompArch(sArch); err != nil { + return err + } + + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } + + g.spec.Linux.Seccomp.Architectures = append(g.spec.Linux.Seccomp.Architectures, rspec.Arch(sArch)) + + return nil +} + +// RemoveSeccompArch removes sArch from g.spec.Linux.Seccomp.Architectures. +func (g Generator) RemoveSeccompArch(sArch string) error { + if err := checkSeccompArch(sArch); err != nil { + return err + } + + if g.spec.Linux.Seccomp == nil { + return nil + } + + for i, arch := range g.spec.Linux.Seccomp.Architectures { + if string(arch) == sArch { + g.spec.Linux.Seccomp.Architectures = append(g.spec.Linux.Seccomp.Architectures[:i], g.spec.Linux.Seccomp.Architectures[i+1:]...) + return nil + } + } + + return nil +} + +func checkSeccompSyscallAction(syscall string) error { + switch syscall { + case "": + case "SCMP_ACT_KILL": + case "SCMP_ACT_TRAP": + case "SCMP_ACT_ERRNO": + case "SCMP_ACT_TRACE": + case "SCMP_ACT_ALLOW": + default: + return fmt.Errorf("seccomp-syscall action must be empty or " + + "one of SCMP_ACT_KILL|SCMP_ACT_TRAP|SCMP_ACT_ERRNO|" + + "SCMP_ACT_TRACE|SCMP_ACT_ALLOW") + } + return nil +} + +func checkSeccompSyscallArg(arg string) error { + switch arg { + case "": + case "SCMP_CMP_NE": + case "SCMP_CMP_LT": + case "SCMP_CMP_LE": + case "SCMP_CMP_EQ": + case "SCMP_CMP_GE": + case "SCMP_CMP_GT": + case "SCMP_CMP_MASKED_EQ": + default: + return fmt.Errorf("seccomp-syscall args must be " + + "empty or one of SCMP_CMP_NE|SCMP_CMP_LT|" + + "SCMP_CMP_LE|SCMP_CMP_EQ|SCMP_CMP_GE|" + + "SCMP_CMP_GT|SCMP_CMP_MASKED_EQ") + } + return nil +} + +func parseSeccompSyscall(s string) (rspec.Syscall, error) { + syscall := strings.Split(s, ":") + if len(syscall) != 3 { + return rspec.Syscall{}, fmt.Errorf("seccomp sysctl must consist of 3 parameters") + } + name := syscall[0] + if err := checkSeccompSyscallAction(syscall[1]); err != nil { + return rspec.Syscall{}, err + } + action := rspec.Action(syscall[1]) + + var Args []rspec.Arg + if strings.EqualFold(syscall[2], "") { + Args = nil + } else { + argsslice := strings.Split(syscall[2], ",") + for _, argsstru := range argsslice { + args := strings.Split(argsstru, "/") + if len(args) == 4 { + index, err := strconv.Atoi(args[0]) + value, err := strconv.Atoi(args[1]) + value2, err := strconv.Atoi(args[2]) + if err != nil { + return rspec.Syscall{}, err + } + if err := checkSeccompSyscallArg(args[3]); err != nil { + return rspec.Syscall{}, err + } + op := rspec.Operator(args[3]) + Arg := rspec.Arg{ + Index: uint(index), + Value: uint64(value), + ValueTwo: uint64(value2), + Op: op, + } + Args = append(Args, Arg) + } else { + return rspec.Syscall{}, fmt.Errorf("seccomp-sysctl args error: %s", argsstru) + } + } + } + + return rspec.Syscall{ + Name: name, + Action: action, + Args: Args, + }, nil +} + +// ClearLinuxSeccompSyscall clears g.spec.Linux.Seccomp.Syscalls. +func (g Generator) ClearLinuxSeccompSyscall() { + if g.spec.Linux.Seccomp == nil { + return + } + + g.spec.Linux.Seccomp.Syscalls = []rspec.Syscall{} +} + +// AddLinuxSeccompSyscall adds sSyscall into g.spec.Linux.Seccomp.Syscalls. +func (g Generator) AddLinuxSeccompSyscall(sSyscall string) error { + f, err := parseSeccompSyscall(sSyscall) + if err != nil { + return err + } + + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } + + g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, f) + return nil +} + +// AddLinuxSeccompSyscallAllow adds seccompAllow into g.spec.Linux.Seccomp.Syscalls. +func (g Generator) AddLinuxSeccompSyscallAllow(seccompAllow string) { + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } + + syscall := rspec.Syscall{ + Name: seccompAllow, + Action: "SCMP_ACT_ALLOW", + } + g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, syscall) +} + +// AddLinuxSeccompSyscallErrno adds seccompErrno into g.spec.Linux.Seccomp.Syscalls. +func (g Generator) AddLinuxSeccompSyscallErrno(seccompErrno string) { + if g.spec.Linux.Seccomp == nil { + g.spec.Linux.Seccomp = &rspec.Seccomp{} + } + + syscall := rspec.Syscall{ + Name: seccompErrno, + Action: "SCMP_ACT_ERRNO", + } + g.spec.Linux.Seccomp.Syscalls = append(g.spec.Linux.Seccomp.Syscalls, syscall) +} + +// RemoveSeccompSyscallByName removes all the seccomp syscalls with the given +// name from g.spec.Linux.Seccomp.Syscalls. +func (g Generator) RemoveSeccompSyscallByName(name string) error { + if g.spec.Linux.Seccomp == nil { + return nil + } + + var r []rspec.Syscall + for _, syscall := range g.spec.Linux.Seccomp.Syscalls { + if strings.Compare(name, syscall.Name) != 0 { + r = append(r, syscall) + } + } + g.spec.Linux.Seccomp.Syscalls = r + return nil +} + +// RemoveSeccompSyscallByAction removes all the seccomp syscalls with the given +// action from g.spec.Linux.Seccomp.Syscalls. +func (g Generator) RemoveSeccompSyscallByAction(action string) error { + if g.spec.Linux.Seccomp == nil { + return nil + } + + if err := checkSeccompSyscallAction(action); err != nil { + return err + } + + var r []rspec.Syscall + for _, syscall := range g.spec.Linux.Seccomp.Syscalls { + if strings.Compare(action, string(syscall.Action)) != 0 { + r = append(r, syscall) + } + } + g.spec.Linux.Seccomp.Syscalls = r + return nil +} + +// RemoveSeccompSyscall removes all the seccomp syscalls with the given +// name and action from g.spec.Linux.Seccomp.Syscalls. +func (g Generator) RemoveSeccompSyscall(name string, action string) error { + if g.spec.Linux.Seccomp == nil { + return nil + } + + if err := checkSeccompSyscallAction(action); err != nil { + return err + } + + var r []rspec.Syscall + for _, syscall := range g.spec.Linux.Seccomp.Syscalls { + if !(strings.Compare(name, syscall.Name) == 0 && + strings.Compare(action, string(syscall.Action)) == 0) { + r = append(r, syscall) + } + } + g.spec.Linux.Seccomp.Syscalls = r + return nil +} + +func parseIDMapping(idms string) (rspec.IDMapping, error) { + idm := strings.Split(idms, ":") + if len(idm) != 3 { + return rspec.IDMapping{}, fmt.Errorf("idmappings error: %s", idms) + } + + hid, err := strconv.Atoi(idm[0]) + if err != nil { + return rspec.IDMapping{}, err + } + + cid, err := strconv.Atoi(idm[1]) + if err != nil { + return rspec.IDMapping{}, err + } + + size, err := strconv.Atoi(idm[2]) + if err != nil { + return rspec.IDMapping{}, err + } + + idMapping := rspec.IDMapping{ + HostID: uint32(hid), + ContainerID: uint32(cid), + Size: uint32(size), + } + return idMapping, nil +} + +// ClearLinuxUIDMappings clear g.spec.Linux.UIDMappings. +func (g Generator) ClearLinuxUIDMappings() { + g.spec.Linux.UIDMappings = []rspec.IDMapping{} +} + +// AddLinuxUIDMapping adds uidMap into g.spec.Linux.UIDMappings. +func (g Generator) AddLinuxUIDMapping(uidMap string) error { + r, err := parseIDMapping(uidMap) + if err != nil { + return err + } + + g.spec.Linux.UIDMappings = append(g.spec.Linux.UIDMappings, r) + return nil +} + +// ClearLinuxGIDMappings clear g.spec.Linux.GIDMappings. +func (g Generator) ClearLinuxGIDMappings() { + g.spec.Linux.GIDMappings = []rspec.IDMapping{} +} + +// AddLinuxGIDMapping adds gidMap into g.spec.Linux.GIDMappings. +func (g Generator) AddLinuxGIDMapping(gidMap string) error { + r, err := parseIDMapping(gidMap) + if err != nil { + return err + } + + g.spec.Linux.GIDMappings = append(g.spec.Linux.GIDMappings, r) + return nil +} + +// SetLinuxRootPropagation sets g.spec.Linux.RootfsPropagation. +func (g Generator) SetLinuxRootPropagation(rp string) error { + switch rp { + case "": + case "private": + case "rprivate": + case "slave": + case "rslave": + case "shared": + case "rshared": + default: + return fmt.Errorf("rootfs-propagation must be empty or one of private|rprivate|slave|rslave|shared|rshared") + } + g.spec.Linux.RootfsPropagation = rp + return nil +} + +func parseHook(s string) rspec.Hook { + parts := strings.Split(s, ":") + args := []string{} + path := parts[0] + if len(parts) > 1 { + args = parts[1:] + } + return rspec.Hook{Path: path, Args: args} +} + +// ClearPreStartHooks clear g.spec.Hooks.Prestart. +func (g Generator) ClearPreStartHooks() { + g.spec.Hooks.Prestart = []rspec.Hook{} +} + +// AddPreStartHook add a prestart hook into g.spec.Hooks.Prestart. +func (g Generator) AddPreStartHook(s string) error { + hook := parseHook(s) + g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook) + return nil +} + +// ClearPostStopHooks clear g.spec.Hooks.Poststop. +func (g Generator) ClearPostStopHooks() { + g.spec.Hooks.Poststop = []rspec.Hook{} +} + +// AddPostStopHook adds a poststop hook into g.spec.Hooks.Poststop. +func (g Generator) AddPostStopHook(s string) error { + hook := parseHook(s) + g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook) + return nil +} + +// ClearPostStartHooks clear g.spec.Hooks.Poststart. +func (g Generator) ClearPostStartHooks() { + g.spec.Hooks.Poststart = []rspec.Hook{} +} + +// AddPostStartHook adds a poststart hook into g.spec.Hooks.Poststart. +func (g Generator) AddPostStartHook(s string) error { + hook := parseHook(s) + g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook) + return nil +} + +// AddTmpfsMount adds a tmpfs mount into g.spec.Mounts. +func (g Generator) AddTmpfsMount(dest string) error { + mnt := rspec.Mount{ + Destination: dest, + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "nodev", "mode=755"}, + } + + g.spec.Mounts = append(g.spec.Mounts, mnt) + return nil +} + +// AddCgroupsMount adds a cgroup mount into g.spec.Mounts. +func (g Generator) AddCgroupsMount(mountCgroupOption string) error { + switch mountCgroupOption { + case "ro": + case "rw": + case "no": + return nil + default: + return fmt.Errorf("--mount-cgroups should be one of (ro,rw,no)") + } + + mnt := rspec.Mount{ + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"nosuid", "noexec", "nodev", "relatime", mountCgroupOption}, + } + g.spec.Mounts = append(g.spec.Mounts, mnt) + + return nil +} + +// AddBindMount adds a bind mount into g.spec.Mounts. +func (g Generator) AddBindMount(bind string) error { + var source, dest string + options := "ro" + bparts := strings.SplitN(bind, ":", 3) + switch len(bparts) { + case 2: + source, dest = bparts[0], bparts[1] + case 3: + source, dest, options = bparts[0], bparts[1], bparts[2] + default: + return fmt.Errorf("--bind should have format src:dest:[options]") + } + + defaultOptions := []string{"bind"} + mnt := rspec.Mount{ + Destination: dest, + Type: "bind", + Source: source, + Options: append(defaultOptions, options), + } + g.spec.Mounts = append(g.spec.Mounts, mnt) + return nil +} + +// SetupPrivileged sets up the priviledge-related fields inside g.spec. +func (g Generator) SetupPrivileged(privileged bool) { + if privileged { + // Add all capabilities in privileged mode. + var finalCapList []string + for _, cap := range capability.List() { + finalCapList = append(finalCapList, fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))) + } + g.spec.Process.Capabilities = finalCapList + g.spec.Process.SelinuxLabel = "" + g.spec.Process.ApparmorProfile = "" + g.spec.Linux.Seccomp = nil + } +} + +func checkCap(c string) error { + isValid := false + cp := strings.ToUpper(c) + + for _, cap := range capability.List() { + if cp == strings.ToUpper(cap.String()) { + isValid = true + break + } + } + + if !isValid { + return fmt.Errorf("Invalid value passed for adding capability") + } + return nil +} + +// ClearProcessCapabilities clear g.spec.Process.Capabilities. +func (g Generator) ClearProcessCapabilities() { + g.spec.Process.Capabilities = []string{} +} + +// AddProcessCapability adds a process capability into g.spec.Process.Capabilities. +func (g Generator) AddProcessCapability(c string) error { + if err := checkCap(c); err != nil { + return err + } + + cp := fmt.Sprintf("CAP_%s", strings.ToUpper(c)) + + for _, cap := range g.spec.Process.Capabilities { + if strings.ToUpper(cap) == cp { + return nil + } + } + + g.spec.Process.Capabilities = append(g.spec.Process.Capabilities, cp) + return nil +} + +// DropProcessCapability drops a process capability from g.spec.Process.Capabilities. +func (g Generator) DropProcessCapability(c string) error { + if err := checkCap(c); err != nil { + return err + } + + cp := fmt.Sprintf("CAP_%s", strings.ToUpper(c)) + + for i, cap := range g.spec.Process.Capabilities { + if strings.ToUpper(cap) == cp { + g.spec.Process.Capabilities = append(g.spec.Process.Capabilities[:i], g.spec.Process.Capabilities[i+1:]...) + return nil + } + } + + return nil +} + +func mapStrToNamespace(ns string, path string) (rspec.Namespace, error) { + switch ns { + case "network": + return rspec.Namespace{Type: rspec.NetworkNamespace, Path: path}, nil + case "pid": + return rspec.Namespace{Type: rspec.PIDNamespace, Path: path}, nil + case "mount": + return rspec.Namespace{Type: rspec.MountNamespace, Path: path}, nil + case "ipc": + return rspec.Namespace{Type: rspec.IPCNamespace, Path: path}, nil + case "uts": + return rspec.Namespace{Type: rspec.UTSNamespace, Path: path}, nil + case "user": + return rspec.Namespace{Type: rspec.UserNamespace, Path: path}, nil + case "cgroup": + return rspec.Namespace{Type: rspec.CgroupNamespace, Path: path}, nil + default: + return rspec.Namespace{}, fmt.Errorf("Should not reach here!") + } +} + +// ClearLinuxNamespaces clear g.spec.Linux.Namespaces. +func (g Generator) ClearLinuxNamespaces() { + g.spec.Linux.Namespaces = []rspec.Namespace{} +} + +// AddOrReplaceLinuxNamespace adds or replaces a namespace inside +// g.spec.Linux.Namespaces. +func (g Generator) AddOrReplaceLinuxNamespace(ns string, path string) error { + namespace, err := mapStrToNamespace(ns, path) + if err != nil { + return err + } + + for i, ns := range g.spec.Linux.Namespaces { + if ns.Type == namespace.Type { + g.spec.Linux.Namespaces[i] = namespace + return nil + } + } + g.spec.Linux.Namespaces = append(g.spec.Linux.Namespaces, namespace) + return nil +} + +// RemoveLinuxNamespace removes a namespace from g.spec.Linux.Namespaces. +func (g Generator) RemoveLinuxNamespace(ns string) error { + namespace, err := mapStrToNamespace(ns, "") + if err != nil { + return err + } + + for i, ns := range g.spec.Linux.Namespaces { + if ns.Type == namespace.Type { + g.spec.Linux.Namespaces = append(g.spec.Linux.Namespaces[:i], g.spec.Linux.Namespaces[i+1:]...) + return nil + } + } + return nil +} + +// strPtr returns the pointer pointing to the string s. +func strPtr(s string) *string { return &s } + +// FIXME: this function is not used. +func parseArgs(args2parse string) ([]*rspec.Arg, error) { + var Args []*rspec.Arg + argstrslice := strings.Split(args2parse, ",") + for _, argstr := range argstrslice { + args := strings.Split(argstr, "/") + if len(args) == 4 { + index, err := strconv.Atoi(args[0]) + value, err := strconv.Atoi(args[1]) + value2, err := strconv.Atoi(args[2]) + if err != nil { + return nil, err + } + switch args[3] { + case "": + case "SCMP_CMP_NE": + case "SCMP_CMP_LT": + case "SCMP_CMP_LE": + case "SCMP_CMP_EQ": + case "SCMP_CMP_GE": + case "SCMP_CMP_GT": + case "SCMP_CMP_MASKED_EQ": + default: + return nil, fmt.Errorf("seccomp-sysctl args must be empty or one of SCMP_CMP_NE|SCMP_CMP_LT|SCMP_CMP_LE|SCMP_CMP_EQ|SCMP_CMP_GE|SCMP_CMP_GT|SCMP_CMP_MASKED_EQ") + } + op := rspec.Operator(args[3]) + Arg := rspec.Arg{ + Index: uint(index), + Value: uint64(value), + ValueTwo: uint64(value2), + Op: op, + } + Args = append(Args, &Arg) + } else { + return nil, fmt.Errorf("seccomp-sysctl args error: %s", argstr) + } + } + return Args, nil +} diff --git a/generate/seccomp/doc.go b/generate/seccomp/doc.go new file mode 100644 index 000000000..481bd7004 --- /dev/null +++ b/generate/seccomp/doc.go @@ -0,0 +1,2 @@ +// Package seccomp includes functions for configuring seccomp. +package seccomp