apparmor.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // +build linux
  2. package native
  3. import (
  4. "bufio"
  5. "fmt"
  6. "io"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "strings"
  11. "text/template"
  12. "github.com/opencontainers/runc/libcontainer/apparmor"
  13. )
  14. const (
  15. apparmorProfilePath = "/etc/apparmor.d/docker"
  16. )
  17. type data struct {
  18. Name string
  19. Imports []string
  20. InnerImports []string
  21. }
  22. const baseTemplate = `
  23. {{range $value := .Imports}}
  24. {{$value}}
  25. {{end}}
  26. profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
  27. {{range $value := .InnerImports}}
  28. {{$value}}
  29. {{end}}
  30. network,
  31. capability,
  32. file,
  33. umount,
  34. signal (receive) peer=/usr/bin/docker,
  35. signal (receive) peer=docker-unconfined,
  36. deny @{PROC}/sys/fs/** wklx,
  37. deny @{PROC}/fs/** wklx,
  38. deny @{PROC}/sysrq-trigger rwklx,
  39. deny @{PROC}/mem rwklx,
  40. deny @{PROC}/kmem rwklx,
  41. deny @{PROC}/kore rwklx,
  42. deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
  43. deny @{PROC}/sys/kernel/*/** wklx,
  44. deny mount,
  45. deny ptrace,
  46. deny /sys/[^f]*/** wklx,
  47. deny /sys/f[^s]*/** wklx,
  48. deny /sys/fs/[^c]*/** wklx,
  49. deny /sys/fs/c[^g]*/** wklx,
  50. deny /sys/fs/cg[^r]*/** wklx,
  51. deny /sys/firmware/efi/efivars/** rwklx,
  52. deny /sys/kernel/security/** rwklx,
  53. }
  54. profile docker-unconfined flags=(attach_disconnected,mediate_deleted,complain) {
  55. #include <abstractions/base>
  56. network,
  57. capability,
  58. file,
  59. umount,
  60. mount,
  61. pivot_root,
  62. change_profile -> *,
  63. ptrace,
  64. signal,
  65. }
  66. `
  67. func generateProfile(out io.Writer) error {
  68. compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
  69. if err != nil {
  70. return err
  71. }
  72. data := &data{
  73. Name: "docker-default",
  74. }
  75. if tunablesExists() {
  76. data.Imports = append(data.Imports, "#include <tunables/global>")
  77. } else {
  78. data.Imports = append(data.Imports, "@{PROC}=/proc/")
  79. }
  80. if abstractionsExists() {
  81. data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
  82. }
  83. if err := compiled.Execute(out, data); err != nil {
  84. return err
  85. }
  86. return nil
  87. }
  88. // check if the tunables/global exist
  89. func tunablesExists() bool {
  90. _, err := os.Stat("/etc/apparmor.d/tunables/global")
  91. return err == nil
  92. }
  93. // check if abstractions/base exist
  94. func abstractionsExists() bool {
  95. _, err := os.Stat("/etc/apparmor.d/abstractions/base")
  96. return err == nil
  97. }
  98. func installAppArmorProfile() error {
  99. if !apparmor.IsEnabled() {
  100. return nil
  101. }
  102. // Make sure /etc/apparmor.d exists
  103. if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
  104. return err
  105. }
  106. f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  107. if err != nil {
  108. return err
  109. }
  110. if err := generateProfile(f); err != nil {
  111. f.Close()
  112. return err
  113. }
  114. f.Close()
  115. cmd := exec.Command("/sbin/apparmor_parser", "-r", "-W", "docker")
  116. // to use the parser directly we have to make sure we are in the correct
  117. // dir with the profile
  118. cmd.Dir = "/etc/apparmor.d"
  119. output, err := cmd.CombinedOutput()
  120. if err != nil {
  121. return fmt.Errorf("Error loading docker apparmor profile: %s (%s)", err, output)
  122. }
  123. return nil
  124. }
  125. func hasAppArmorProfileLoaded(profile string) error {
  126. file, err := os.Open("/sys/kernel/security/apparmor/profiles")
  127. if err != nil {
  128. return err
  129. }
  130. r := bufio.NewReader(file)
  131. for {
  132. p, err := r.ReadString('\n')
  133. if err != nil {
  134. return err
  135. }
  136. if strings.HasPrefix(p, profile+" ") {
  137. return nil
  138. }
  139. }
  140. }