seccomp_linux.go 6.8 KB

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