| // +build linux,cgo,seccomp |
| |
| package seccomp |
| |
| import ( |
| "fmt" |
| "log" |
| "syscall" |
| |
| "github.com/opencontainers/runc/libcontainer/configs" |
| libseccomp "github.com/seccomp/libseccomp-golang" |
| ) |
| |
| var ( |
| actAllow = libseccomp.ActAllow |
| actTrap = libseccomp.ActTrap |
| actKill = libseccomp.ActKill |
| actTrace = libseccomp.ActTrace.SetReturnCode(int16(syscall.EPERM)) |
| actErrno = libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM)) |
| ) |
| |
| // Filters given syscalls in a container, preventing them from being used |
| // Started in the container init process, and carried over to all child processes |
| // Setns calls, however, require a separate invocation, as they are not children |
| // of the init until they join the namespace |
| func InitSeccomp(config *configs.Seccomp) error { |
| if config == nil { |
| return fmt.Errorf("cannot initialize Seccomp - nil config passed") |
| } |
| |
| defaultAction, err := getAction(config.DefaultAction) |
| if err != nil { |
| return fmt.Errorf("error initializing seccomp - invalid default action") |
| } |
| |
| filter, err := libseccomp.NewFilter(defaultAction) |
| if err != nil { |
| return fmt.Errorf("error creating filter: %s", err) |
| } |
| |
| // Add extra architectures |
| for _, arch := range config.Architectures { |
| scmpArch, err := libseccomp.GetArchFromString(arch) |
| if err != nil { |
| return err |
| } |
| |
| if err := filter.AddArch(scmpArch); err != nil { |
| return err |
| } |
| } |
| |
| // Unset no new privs bit |
| if err := filter.SetNoNewPrivsBit(false); err != nil { |
| return fmt.Errorf("error setting no new privileges: %s", err) |
| } |
| |
| // Add a rule for each syscall |
| for _, call := range config.Syscalls { |
| if call == nil { |
| return fmt.Errorf("encountered nil syscall while initializing Seccomp") |
| } |
| |
| if err = matchCall(filter, call); err != nil { |
| return err |
| } |
| } |
| |
| if err = filter.Load(); err != nil { |
| return fmt.Errorf("error loading seccomp filter into kernel: %s", err) |
| } |
| |
| return nil |
| } |
| |
| // Convert Libcontainer Action to Libseccomp ScmpAction |
| func getAction(act configs.Action) (libseccomp.ScmpAction, error) { |
| switch act { |
| case configs.Kill: |
| return actKill, nil |
| case configs.Errno: |
| return actErrno, nil |
| case configs.Trap: |
| return actTrap, nil |
| case configs.Allow: |
| return actAllow, nil |
| case configs.Trace: |
| return actTrace, nil |
| default: |
| return libseccomp.ActInvalid, fmt.Errorf("invalid action, cannot use in rule") |
| } |
| } |
| |
| // Convert Libcontainer Operator to Libseccomp ScmpCompareOp |
| func getOperator(op configs.Operator) (libseccomp.ScmpCompareOp, error) { |
| switch op { |
| case configs.EqualTo: |
| return libseccomp.CompareEqual, nil |
| case configs.NotEqualTo: |
| return libseccomp.CompareNotEqual, nil |
| case configs.GreaterThan: |
| return libseccomp.CompareGreater, nil |
| case configs.GreaterThanOrEqualTo: |
| return libseccomp.CompareGreaterEqual, nil |
| case configs.LessThan: |
| return libseccomp.CompareLess, nil |
| case configs.LessThanOrEqualTo: |
| return libseccomp.CompareLessOrEqual, nil |
| case configs.MaskEqualTo: |
| return libseccomp.CompareMaskedEqual, nil |
| default: |
| return libseccomp.CompareInvalid, fmt.Errorf("invalid operator, cannot use in rule") |
| } |
| } |
| |
| // Convert Libcontainer Arg to Libseccomp ScmpCondition |
| func getCondition(arg *configs.Arg) (libseccomp.ScmpCondition, error) { |
| cond := libseccomp.ScmpCondition{} |
| |
| if arg == nil { |
| return cond, fmt.Errorf("cannot convert nil to syscall condition") |
| } |
| |
| op, err := getOperator(arg.Op) |
| if err != nil { |
| return cond, err |
| } |
| |
| return libseccomp.MakeCondition(arg.Index, op, arg.Value, arg.ValueTwo) |
| } |
| |
| // Add a rule to match a single syscall |
| func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error { |
| if call == nil || filter == nil { |
| return fmt.Errorf("cannot use nil as syscall to block") |
| } |
| |
| if len(call.Name) == 0 { |
| return fmt.Errorf("empty string is not a valid syscall") |
| } |
| |
| // If we can't resolve the syscall, assume it's not supported on this kernel |
| // Ignore it, don't error out |
| callNum, err := libseccomp.GetSyscallFromName(call.Name) |
| if err != nil { |
| log.Printf("Error resolving syscall name %s: %s - ignoring syscall.", call.Name, err) |
| return nil |
| } |
| |
| // Convert the call's action to the libseccomp equivalent |
| callAct, err := getAction(call.Action) |
| if err != nil { |
| return err |
| } |
| |
| // Unconditional match - just add the rule |
| if len(call.Args) == 0 { |
| if err = filter.AddRule(callNum, callAct); err != nil { |
| return err |
| } |
| } else { |
| // Conditional match - convert the per-arg rules into library format |
| conditions := []libseccomp.ScmpCondition{} |
| |
| for _, cond := range call.Args { |
| newCond, err := getCondition(cond) |
| if err != nil { |
| return err |
| } |
| |
| conditions = append(conditions, newCond) |
| } |
| |
| if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |