seccomp_linux.go 6.8 KB

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