lxc_template.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package lxc
  2. import (
  3. "fmt"
  4. "os"
  5. "strings"
  6. "text/template"
  7. log "github.com/Sirupsen/logrus"
  8. "github.com/docker/docker/daemon/execdriver"
  9. nativeTemplate "github.com/docker/docker/daemon/execdriver/native/template"
  10. "github.com/docker/docker/utils"
  11. "github.com/docker/libcontainer/label"
  12. "github.com/docker/libcontainer/security/capabilities"
  13. )
  14. const LxcTemplate = `
  15. {{if .Network.Interface}}
  16. # network configuration
  17. lxc.network.type = veth
  18. lxc.network.link = {{.Network.Interface.Bridge}}
  19. lxc.network.name = eth0
  20. lxc.network.mtu = {{.Network.Mtu}}
  21. lxc.network.flags = up
  22. {{else if .Network.HostNetworking}}
  23. lxc.network.type = none
  24. {{else}}
  25. # network is disabled (-n=false)
  26. lxc.network.type = empty
  27. lxc.network.flags = up
  28. lxc.network.mtu = {{.Network.Mtu}}
  29. {{end}}
  30. # root filesystem
  31. {{$ROOTFS := .Rootfs}}
  32. lxc.rootfs = {{$ROOTFS}}
  33. # use a dedicated pts for the container (and limit the number of pseudo terminal
  34. # available)
  35. lxc.pts = 1024
  36. # disable the main console
  37. lxc.console = none
  38. # no controlling tty at all
  39. lxc.tty = 1
  40. {{if .ProcessConfig.Privileged}}
  41. lxc.cgroup.devices.allow = a
  42. {{else}}
  43. # no implicit access to devices
  44. lxc.cgroup.devices.deny = a
  45. #Allow the devices passed to us in the AllowedDevices list.
  46. {{range $allowedDevice := .AllowedDevices}}
  47. lxc.cgroup.devices.allow = {{$allowedDevice.GetCgroupAllowString}}
  48. {{end}}
  49. {{end}}
  50. # standard mount point
  51. # Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385
  52. lxc.pivotdir = lxc_putold
  53. # NOTICE: These mounts must be applied within the namespace
  54. {{if .ProcessConfig.Privileged}}
  55. # WARNING: mounting procfs and/or sysfs read-write is a known attack vector.
  56. # See e.g. http://blog.zx2c4.com/749 and http://bit.ly/T9CkqJ
  57. # We mount them read-write here, but later, dockerinit will call the Restrict() function to remount them read-only.
  58. # We cannot mount them directly read-only, because that would prevent loading AppArmor profiles.
  59. lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
  60. lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
  61. {{if .AppArmor}}
  62. lxc.aa_profile = unconfined
  63. {{end}}
  64. {{else}}
  65. # In non-privileged mode, lxc will automatically mount /proc and /sys in readonly mode
  66. # for security. See: http://man7.org/linux/man-pages/man5/lxc.container.conf.5.html
  67. lxc.mount.auto = proc sys
  68. {{if .AppArmor}}
  69. lxc.aa_profile = .AppArmorProfile
  70. {{end}}
  71. {{end}}
  72. {{if .ProcessConfig.Tty}}
  73. lxc.mount.entry = {{.ProcessConfig.Console}} {{escapeFstabSpaces $ROOTFS}}/dev/console none bind,rw 0 0
  74. {{end}}
  75. lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts {{formatMountLabel "newinstance,ptmxmode=0666,nosuid,noexec" ""}} 0 0
  76. lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs {{formatMountLabel "size=65536k,nosuid,nodev,noexec" ""}} 0 0
  77. {{range $value := .Mounts}}
  78. {{$createVal := isDirectory $value.Source}}
  79. {{if $value.Writable}}
  80. lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none rbind,rw,create={{$createVal}} 0 0
  81. {{else}}
  82. lxc.mount.entry = {{$value.Source}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $value.Destination}} none rbind,ro,create={{$createVal}} 0 0
  83. {{end}}
  84. {{end}}
  85. # limits
  86. {{if .Resources}}
  87. {{if .Resources.Memory}}
  88. lxc.cgroup.memory.limit_in_bytes = {{.Resources.Memory}}
  89. lxc.cgroup.memory.soft_limit_in_bytes = {{.Resources.Memory}}
  90. {{with $memSwap := getMemorySwap .Resources}}
  91. lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
  92. {{end}}
  93. {{end}}
  94. {{if .Resources.CpuShares}}
  95. lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
  96. {{end}}
  97. {{if .Resources.Cpuset}}
  98. lxc.cgroup.cpuset.cpus = {{.Resources.Cpuset}}
  99. {{end}}
  100. {{end}}
  101. {{if .LxcConfig}}
  102. {{range $value := .LxcConfig}}
  103. lxc.{{$value}}
  104. {{end}}
  105. {{end}}
  106. {{if .Network.Interface}}
  107. {{if .Network.Interface.IPAddress}}
  108. lxc.network.ipv4 = {{.Network.Interface.IPAddress}}/{{.Network.Interface.IPPrefixLen}}
  109. {{end}}
  110. {{if .Network.Interface.Gateway}}
  111. lxc.network.ipv4.gateway = {{.Network.Interface.Gateway}}
  112. {{end}}
  113. {{if .ProcessConfig.Env}}
  114. lxc.utsname = {{getHostname .ProcessConfig.Env}}
  115. {{end}}
  116. {{if .ProcessConfig.Privileged}}
  117. # No cap values are needed, as lxc is starting in privileged mode
  118. {{else}}
  119. {{ with keepCapabilities .CapAdd .CapDrop }}
  120. {{range .}}
  121. lxc.cap.keep = {{.}}
  122. {{end}}
  123. {{else}}
  124. {{ with dropList .CapDrop }}
  125. {{range .}}
  126. lxc.cap.drop = {{.}}
  127. {{end}}
  128. {{end}}
  129. {{end}}
  130. {{end}}
  131. {{end}}
  132. `
  133. var LxcTemplateCompiled *template.Template
  134. // Escape spaces in strings according to the fstab documentation, which is the
  135. // format for "lxc.mount.entry" lines in lxc.conf. See also "man 5 fstab".
  136. func escapeFstabSpaces(field string) string {
  137. return strings.Replace(field, " ", "\\040", -1)
  138. }
  139. func keepCapabilities(adds []string, drops []string) ([]string, error) {
  140. container := nativeTemplate.New()
  141. log.Debugf("adds %s drops %s\n", adds, drops)
  142. caps, err := execdriver.TweakCapabilities(container.Capabilities, adds, drops)
  143. if err != nil {
  144. return nil, err
  145. }
  146. var newCaps []string
  147. for _, cap := range caps {
  148. log.Debugf("cap %s\n", cap)
  149. realCap := capabilities.GetCapability(cap)
  150. numCap := fmt.Sprintf("%d", realCap.Value)
  151. newCaps = append(newCaps, numCap)
  152. }
  153. return newCaps, nil
  154. }
  155. func dropList(drops []string) ([]string, error) {
  156. if utils.StringsContainsNoCase(drops, "all") {
  157. var newCaps []string
  158. for _, cap := range capabilities.GetAllCapabilities() {
  159. log.Debugf("drop cap %s\n", cap)
  160. realCap := capabilities.GetCapability(cap)
  161. if realCap == nil {
  162. return nil, fmt.Errorf("Invalid capability '%s'", cap)
  163. }
  164. numCap := fmt.Sprintf("%d", realCap.Value)
  165. newCaps = append(newCaps, numCap)
  166. }
  167. return newCaps, nil
  168. }
  169. return []string{}, nil
  170. }
  171. func isDirectory(source string) string {
  172. f, err := os.Stat(source)
  173. if err != nil {
  174. if os.IsNotExist(err) {
  175. return "dir"
  176. }
  177. return ""
  178. }
  179. if f.IsDir() {
  180. return "dir"
  181. }
  182. return "file"
  183. }
  184. func getMemorySwap(v *execdriver.Resources) int64 {
  185. // By default, MemorySwap is set to twice the size of RAM.
  186. // If you want to omit MemorySwap, set it to `-1'.
  187. if v.MemorySwap < 0 {
  188. return 0
  189. }
  190. return v.Memory * 2
  191. }
  192. func getLabel(c map[string][]string, name string) string {
  193. label := c["label"]
  194. for _, l := range label {
  195. parts := strings.SplitN(l, "=", 2)
  196. if strings.TrimSpace(parts[0]) == name {
  197. return strings.TrimSpace(parts[1])
  198. }
  199. }
  200. return ""
  201. }
  202. func getHostname(env []string) string {
  203. for _, kv := range env {
  204. parts := strings.SplitN(kv, "=", 2)
  205. if parts[0] == "HOSTNAME" && len(parts) == 2 {
  206. return parts[1]
  207. }
  208. }
  209. return ""
  210. }
  211. func init() {
  212. var err error
  213. funcMap := template.FuncMap{
  214. "getMemorySwap": getMemorySwap,
  215. "escapeFstabSpaces": escapeFstabSpaces,
  216. "formatMountLabel": label.FormatMountLabel,
  217. "isDirectory": isDirectory,
  218. "keepCapabilities": keepCapabilities,
  219. "dropList": dropList,
  220. "getHostname": getHostname,
  221. }
  222. LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
  223. if err != nil {
  224. panic(err)
  225. }
  226. }