seccomp_linux.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // +build linux,cgo,seccomp
  2. package seccomp
  3. import (
  4. "bufio"
  5. "fmt"
  6. "os"
  7. "strings"
  8. "syscall"
  9. "github.com/opencontainers/runc/libcontainer/configs"
  10. libseccomp "github.com/seccomp/libseccomp-golang"
  11. )
  12. var (
  13. actAllow = libseccomp.ActAllow
  14. actTrap = libseccomp.ActTrap
  15. actKill = libseccomp.ActKill
  16. actTrace = libseccomp.ActTrace.SetReturnCode(int16(syscall.EPERM))
  17. actErrno = libseccomp.ActErrno.SetReturnCode(int16(syscall.EPERM))
  18. // SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
  19. SeccompModeFilter = uintptr(2)
  20. )
  21. // Filters given syscalls in a container, preventing them from being used
  22. // Started in the container init process, and carried over to all child processes
  23. // Setns calls, however, require a separate invocation, as they are not children
  24. // of the init until they join the namespace
  25. func InitSeccomp(config *configs.Seccomp) error {
  26. if config == nil {
  27. return fmt.Errorf("cannot initialize Seccomp - nil config passed")
  28. }
  29. defaultAction, err := getAction(config.DefaultAction)
  30. if err != nil {
  31. return fmt.Errorf("error initializing seccomp - invalid default action")
  32. }
  33. filter, err := libseccomp.NewFilter(defaultAction)
  34. if err != nil {
  35. return fmt.Errorf("error creating filter: %s", err)
  36. }
  37. // Add extra architectures
  38. for _, arch := range config.Architectures {
  39. scmpArch, err := libseccomp.GetArchFromString(arch)
  40. if err != nil {
  41. return err
  42. }
  43. if err := filter.AddArch(scmpArch); err != nil {
  44. return err
  45. }
  46. }
  47. // Unset no new privs bit
  48. if err := filter.SetNoNewPrivsBit(false); err != nil {
  49. return fmt.Errorf("error setting no new privileges: %s", err)
  50. }
  51. // Add a rule for each syscall
  52. for _, call := range config.Syscalls {
  53. if call == nil {
  54. return fmt.Errorf("encountered nil syscall while initializing Seccomp")
  55. }
  56. if err = matchCall(filter, call); err != nil {
  57. return err
  58. }
  59. }
  60. if err = filter.Load(); err != nil {
  61. return fmt.Errorf("error loading seccomp filter into kernel: %s", err)
  62. }
  63. return nil
  64. }
  65. // IsEnabled returns if the kernel has been configured to support seccomp.
  66. func IsEnabled() bool {
  67. // Try to read from /proc/self/status for kernels > 3.8
  68. s, err := parseStatusFile("/proc/self/status")
  69. if err != nil {
  70. // Check if Seccomp is supported, via CONFIG_SECCOMP.
  71. if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
  72. // Make sure the kernel has CONFIG_SECCOMP_FILTER.
  73. if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. _, ok := s["Seccomp"]
  80. return ok
  81. }
  82. // Convert Libcontainer Action to Libseccomp ScmpAction
  83. func getAction(act configs.Action) (libseccomp.ScmpAction, error) {
  84. switch act {
  85. case configs.Kill:
  86. return actKill, nil
  87. case configs.Errno:
  88. return actErrno, nil
  89. case configs.Trap:
  90. return actTrap, nil
  91. case configs.Allow:
  92. return actAllow, nil
  93. case configs.Trace:
  94. return actTrace, nil
  95. default:
  96. return libseccomp.ActInvalid, fmt.Errorf("invalid action, cannot use in rule")
  97. }
  98. }
  99. // Convert Libcontainer Operator to Libseccomp ScmpCompareOp
  100. func getOperator(op configs.Operator) (libseccomp.ScmpCompareOp, error) {
  101. switch op {
  102. case configs.EqualTo:
  103. return libseccomp.CompareEqual, nil
  104. case configs.NotEqualTo:
  105. return libseccomp.CompareNotEqual, nil
  106. case configs.GreaterThan:
  107. return libseccomp.CompareGreater, nil
  108. case configs.GreaterThanOrEqualTo:
  109. return libseccomp.CompareGreaterEqual, nil
  110. case configs.LessThan:
  111. return libseccomp.CompareLess, nil
  112. case configs.LessThanOrEqualTo:
  113. return libseccomp.CompareLessOrEqual, nil
  114. case configs.MaskEqualTo:
  115. return libseccomp.CompareMaskedEqual, nil
  116. default:
  117. return libseccomp.CompareInvalid, fmt.Errorf("invalid operator, cannot use in rule")
  118. }
  119. }
  120. // Convert Libcontainer Arg to Libseccomp ScmpCondition
  121. func getCondition(arg *configs.Arg) (libseccomp.ScmpCondition, error) {
  122. cond := libseccomp.ScmpCondition{}
  123. if arg == nil {
  124. return cond, fmt.Errorf("cannot convert nil to syscall condition")
  125. }
  126. op, err := getOperator(arg.Op)
  127. if err != nil {
  128. return cond, err
  129. }
  130. return libseccomp.MakeCondition(arg.Index, op, arg.Value, arg.ValueTwo)
  131. }
  132. // Add a rule to match a single syscall
  133. func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error {
  134. if call == nil || filter == nil {
  135. return fmt.Errorf("cannot use nil as syscall to block")
  136. }
  137. if len(call.Name) == 0 {
  138. return fmt.Errorf("empty string is not a valid syscall")
  139. }
  140. // If we can't resolve the syscall, assume it's not supported on this kernel
  141. // Ignore it, don't error out
  142. callNum, err := libseccomp.GetSyscallFromName(call.Name)
  143. if err != nil {
  144. return nil
  145. }
  146. // Convert the call's action to the libseccomp equivalent
  147. callAct, err := getAction(call.Action)
  148. if err != nil {
  149. return err
  150. }
  151. // Unconditional match - just add the rule
  152. if len(call.Args) == 0 {
  153. if err = filter.AddRule(callNum, callAct); err != nil {
  154. return err
  155. }
  156. } else {
  157. // Conditional match - convert the per-arg rules into library format
  158. conditions := []libseccomp.ScmpCondition{}
  159. for _, cond := range call.Args {
  160. newCond, err := getCondition(cond)
  161. if err != nil {
  162. return err
  163. }
  164. conditions = append(conditions, newCond)
  165. }
  166. if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
  167. return err
  168. }
  169. }
  170. return nil
  171. }
  172. func parseStatusFile(path string) (map[string]string, error) {
  173. f, err := os.Open(path)
  174. if err != nil {
  175. return nil, err
  176. }
  177. defer f.Close()
  178. s := bufio.NewScanner(f)
  179. status := make(map[string]string)
  180. for s.Scan() {
  181. if err := s.Err(); err != nil {
  182. return nil, err
  183. }
  184. text := s.Text()
  185. parts := strings.Split(text, ":")
  186. if len(parts) <= 1 {
  187. continue
  188. }
  189. status[parts[0]] = parts[1]
  190. }
  191. return status, nil
  192. }