plugin_linux.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package v2 // import "github.com/docker/docker/plugin/v2"
  2. import (
  3. "os"
  4. "path/filepath"
  5. "runtime"
  6. "strings"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/oci"
  9. specs "github.com/opencontainers/runtime-spec/specs-go"
  10. "github.com/pkg/errors"
  11. )
  12. // InitSpec creates an OCI spec from the plugin's config.
  13. func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
  14. s := oci.DefaultSpec()
  15. s.Root = &specs.Root{
  16. Path: p.Rootfs,
  17. Readonly: false, // TODO: all plugins should be readonly? settable in config?
  18. }
  19. userMounts := make(map[string]struct{}, len(p.PluginObj.Settings.Mounts))
  20. for _, m := range p.PluginObj.Settings.Mounts {
  21. userMounts[m.Destination] = struct{}{}
  22. }
  23. execRoot = filepath.Join(execRoot, p.PluginObj.ID)
  24. if err := os.MkdirAll(execRoot, 0o700); err != nil {
  25. return nil, errors.WithStack(err)
  26. }
  27. if p.PluginObj.Config.PropagatedMount != "" {
  28. pRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
  29. s.Mounts = append(s.Mounts, specs.Mount{
  30. Source: pRoot,
  31. Destination: p.PluginObj.Config.PropagatedMount,
  32. Type: "bind",
  33. Options: []string{"rbind", "rw", "rshared"},
  34. })
  35. s.Linux.RootfsPropagation = "rshared"
  36. }
  37. mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
  38. Source: &execRoot,
  39. Destination: defaultPluginRuntimeDestination,
  40. Type: "bind",
  41. Options: []string{"rbind", "rshared"},
  42. })
  43. if p.PluginObj.Config.Network.Type != "" {
  44. // TODO: if net == bridge, use libnetwork controller to create a new plugin-specific bridge, bind mount /etc/hosts and /etc/resolv.conf look at the docker code (allocateNetwork, initialize)
  45. if p.PluginObj.Config.Network.Type == "host" {
  46. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("network"))
  47. }
  48. etcHosts := "/etc/hosts"
  49. resolvConf := "/etc/resolv.conf"
  50. mounts = append(mounts,
  51. types.PluginMount{
  52. Source: &etcHosts,
  53. Destination: etcHosts,
  54. Type: "bind",
  55. Options: []string{"rbind", "ro"},
  56. },
  57. types.PluginMount{
  58. Source: &resolvConf,
  59. Destination: resolvConf,
  60. Type: "bind",
  61. Options: []string{"rbind", "ro"},
  62. })
  63. }
  64. if p.PluginObj.Config.PidHost {
  65. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("pid"))
  66. }
  67. if p.PluginObj.Config.IpcHost {
  68. oci.RemoveNamespace(&s, specs.LinuxNamespaceType("ipc"))
  69. }
  70. for _, mnt := range mounts {
  71. m := specs.Mount{
  72. Destination: mnt.Destination,
  73. Type: mnt.Type,
  74. Options: mnt.Options,
  75. }
  76. if mnt.Source == nil {
  77. return nil, errors.New("mount source is not specified")
  78. }
  79. m.Source = *mnt.Source
  80. s.Mounts = append(s.Mounts, m)
  81. }
  82. for i, m := range s.Mounts {
  83. if strings.HasPrefix(m.Destination, "/dev/") {
  84. if _, ok := userMounts[m.Destination]; ok {
  85. s.Mounts = append(s.Mounts[:i], s.Mounts[i+1:]...)
  86. }
  87. }
  88. }
  89. if p.PluginObj.Config.Linux.AllowAllDevices {
  90. s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{{Allow: true, Access: "rwm"}}
  91. }
  92. for _, dev := range p.PluginObj.Settings.Devices {
  93. path := *dev.Path
  94. d, dPermissions, err := oci.DevicesFromPath(path, path, "rwm")
  95. if err != nil {
  96. return nil, errors.WithStack(err)
  97. }
  98. s.Linux.Devices = append(s.Linux.Devices, d...)
  99. s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, dPermissions...)
  100. }
  101. envs := make([]string, 1, len(p.PluginObj.Settings.Env)+1)
  102. envs[0] = "PATH=" + oci.DefaultPathEnv(runtime.GOOS)
  103. envs = append(envs, p.PluginObj.Settings.Env...)
  104. args := append(p.PluginObj.Config.Entrypoint, p.PluginObj.Settings.Args...)
  105. cwd := p.PluginObj.Config.WorkDir
  106. if len(cwd) == 0 {
  107. cwd = "/"
  108. }
  109. s.Process.Terminal = false
  110. s.Process.Args = args
  111. s.Process.Cwd = cwd
  112. s.Process.Env = envs
  113. caps := s.Process.Capabilities
  114. caps.Bounding = append(caps.Bounding, p.PluginObj.Config.Linux.Capabilities...)
  115. caps.Permitted = append(caps.Permitted, p.PluginObj.Config.Linux.Capabilities...)
  116. caps.Inheritable = append(caps.Inheritable, p.PluginObj.Config.Linux.Capabilities...)
  117. caps.Effective = append(caps.Effective, p.PluginObj.Config.Linux.Capabilities...)
  118. if p.modifyRuntimeSpec != nil {
  119. p.modifyRuntimeSpec(&s)
  120. }
  121. return &s, nil
  122. }