| ... | ... |
@@ -48,7 +48,6 @@ type Container struct {
|
| 48 | 48 |
network *NetworkInterface |
| 49 | 49 |
NetworkSettings *NetworkSettings |
| 50 | 50 |
|
| 51 |
- SysInitPath string |
|
| 52 | 51 |
ResolvConfPath string |
| 53 | 52 |
HostnamePath string |
| 54 | 53 |
HostsPath string |
| ... | ... |
@@ -297,7 +296,11 @@ func (container *Container) generateEnvConfig(env []string) error {
|
| 297 | 297 |
if err != nil {
|
| 298 | 298 |
return err |
| 299 | 299 |
} |
| 300 |
- ioutil.WriteFile(container.EnvConfigPath(), data, 0600) |
|
| 300 |
+ p, err := container.EnvConfigPath() |
|
| 301 |
+ if err != nil {
|
|
| 302 |
+ return err |
|
| 303 |
+ } |
|
| 304 |
+ ioutil.WriteFile(p, data, 0600) |
|
| 301 | 305 |
return nil |
| 302 | 306 |
} |
| 303 | 307 |
|
| ... | ... |
@@ -681,6 +684,17 @@ func (container *Container) Start() (err error) {
|
| 681 | 681 |
} |
| 682 | 682 |
} |
| 683 | 683 |
|
| 684 |
+ mounts, err := runtime.getMounts(container) |
|
| 685 |
+ if err != nil {
|
|
| 686 |
+ return err |
|
| 687 |
+ } |
|
| 688 |
+ |
|
| 689 |
+ for _, m := range mounts {
|
|
| 690 |
+ if err := m.Mount(container.RootfsPath()); err != nil {
|
|
| 691 |
+ return err |
|
| 692 |
+ } |
|
| 693 |
+ } |
|
| 694 |
+ |
|
| 684 | 695 |
container.cmd = exec.Command(params[0], params[1:]...) |
| 685 | 696 |
|
| 686 | 697 |
// Setup logging of stdout and stderr to disk |
| ... | ... |
@@ -1358,6 +1372,18 @@ func (container *Container) GetImage() (*Image, error) {
|
| 1358 | 1358 |
} |
| 1359 | 1359 |
|
| 1360 | 1360 |
func (container *Container) Unmount() error {
|
| 1361 |
+ mounts, err := container.runtime.getMounts(container) |
|
| 1362 |
+ if err != nil {
|
|
| 1363 |
+ return err |
|
| 1364 |
+ } |
|
| 1365 |
+ for _, m := range mounts {
|
|
| 1366 |
+ if lastError := m.Unmount(container.RootfsPath()); lastError != nil {
|
|
| 1367 |
+ err = lastError |
|
| 1368 |
+ } |
|
| 1369 |
+ } |
|
| 1370 |
+ if err != nil {
|
|
| 1371 |
+ return err |
|
| 1372 |
+ } |
|
| 1361 | 1373 |
return container.runtime.Unmount(container) |
| 1362 | 1374 |
} |
| 1363 | 1375 |
|
| ... | ... |
@@ -1377,8 +1403,20 @@ func (container *Container) jsonPath() string {
|
| 1377 | 1377 |
return path.Join(container.root, "config.json") |
| 1378 | 1378 |
} |
| 1379 | 1379 |
|
| 1380 |
-func (container *Container) EnvConfigPath() string {
|
|
| 1381 |
- return path.Join(container.root, "config.env") |
|
| 1380 |
+func (container *Container) EnvConfigPath() (string, error) {
|
|
| 1381 |
+ p := path.Join(container.root, "config.env") |
|
| 1382 |
+ if _, err := os.Stat(p); err != nil {
|
|
| 1383 |
+ if os.IsNotExist(err) {
|
|
| 1384 |
+ f, err := os.Create(p) |
|
| 1385 |
+ if err != nil {
|
|
| 1386 |
+ return "", err |
|
| 1387 |
+ } |
|
| 1388 |
+ f.Close() |
|
| 1389 |
+ } else {
|
|
| 1390 |
+ return "", err |
|
| 1391 |
+ } |
|
| 1392 |
+ } |
|
| 1393 |
+ return p, nil |
|
| 1382 | 1394 |
} |
| 1383 | 1395 |
|
| 1384 | 1396 |
func (container *Container) lxcConfigPath() string {
|
| ... | ... |
@@ -6,6 +6,8 @@ import ( |
| 6 | 6 |
"github.com/dotcloud/docker/utils" |
| 7 | 7 |
"os" |
| 8 | 8 |
"path" |
| 9 |
+ "strings" |
|
| 10 |
+ "syscall" |
|
| 9 | 11 |
) |
| 10 | 12 |
|
| 11 | 13 |
type InitFunc func(root string) (Driver, error) |
| ... | ... |
@@ -31,6 +33,13 @@ type Differ interface {
|
| 31 | 31 |
DiffSize(id string) (bytes int64, err error) |
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 |
+type Mount struct {
|
|
| 35 |
+ Device string |
|
| 36 |
+ Target string |
|
| 37 |
+ Type string |
|
| 38 |
+ Options string |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 34 | 41 |
var ( |
| 35 | 42 |
DefaultDriver string |
| 36 | 43 |
// All registred drivers |
| ... | ... |
@@ -88,3 +97,91 @@ func New(root string) (driver Driver, err error) {
|
| 88 | 88 |
} |
| 89 | 89 |
return nil, err |
| 90 | 90 |
} |
| 91 |
+ |
|
| 92 |
+func (m *Mount) Mount(root string) error {
|
|
| 93 |
+ var ( |
|
| 94 |
+ flag int |
|
| 95 |
+ data []string |
|
| 96 |
+ target = path.Join(root, m.Target) |
|
| 97 |
+ ) |
|
| 98 |
+ |
|
| 99 |
+ if mounted, err := Mounted(target); err != nil || mounted {
|
|
| 100 |
+ return err |
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ flags := map[string]struct {
|
|
| 104 |
+ clear bool |
|
| 105 |
+ flag int |
|
| 106 |
+ }{
|
|
| 107 |
+ "defaults": {false, 0},
|
|
| 108 |
+ "ro": {false, syscall.MS_RDONLY},
|
|
| 109 |
+ "rw": {true, syscall.MS_RDONLY},
|
|
| 110 |
+ "suid": {true, syscall.MS_NOSUID},
|
|
| 111 |
+ "nosuid": {false, syscall.MS_NOSUID},
|
|
| 112 |
+ "dev": {true, syscall.MS_NODEV},
|
|
| 113 |
+ "nodev": {false, syscall.MS_NODEV},
|
|
| 114 |
+ "exec": {true, syscall.MS_NOEXEC},
|
|
| 115 |
+ "noexec": {false, syscall.MS_NOEXEC},
|
|
| 116 |
+ "sync": {false, syscall.MS_SYNCHRONOUS},
|
|
| 117 |
+ "async": {true, syscall.MS_SYNCHRONOUS},
|
|
| 118 |
+ "dirsync": {false, syscall.MS_DIRSYNC},
|
|
| 119 |
+ "remount": {false, syscall.MS_REMOUNT},
|
|
| 120 |
+ "mand": {false, syscall.MS_MANDLOCK},
|
|
| 121 |
+ "nomand": {true, syscall.MS_MANDLOCK},
|
|
| 122 |
+ "atime": {true, syscall.MS_NOATIME},
|
|
| 123 |
+ "noatime": {false, syscall.MS_NOATIME},
|
|
| 124 |
+ "diratime": {true, syscall.MS_NODIRATIME},
|
|
| 125 |
+ "nodiratime": {false, syscall.MS_NODIRATIME},
|
|
| 126 |
+ "bind": {false, syscall.MS_BIND},
|
|
| 127 |
+ "rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
|
| 128 |
+ "relatime": {false, syscall.MS_RELATIME},
|
|
| 129 |
+ "norelatime": {true, syscall.MS_RELATIME},
|
|
| 130 |
+ "strictatime": {false, syscall.MS_STRICTATIME},
|
|
| 131 |
+ "nostrictatime": {true, syscall.MS_STRICTATIME},
|
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ for _, o := range strings.Split(m.Options, ",") {
|
|
| 135 |
+ // If the option does not exist in the flags table then it is a |
|
| 136 |
+ // data value for a specific fs type |
|
| 137 |
+ if f, exists := flags[o]; exists {
|
|
| 138 |
+ if f.clear {
|
|
| 139 |
+ flag &= ^f.flag |
|
| 140 |
+ } else {
|
|
| 141 |
+ flag |= f.flag |
|
| 142 |
+ } |
|
| 143 |
+ } else {
|
|
| 144 |
+ data = append(data, o) |
|
| 145 |
+ } |
|
| 146 |
+ } |
|
| 147 |
+ |
|
| 148 |
+ if err := syscall.Mount(m.Device, target, m.Type, uintptr(flag), strings.Join(data, ",")); err != nil {
|
|
| 149 |
+ panic(err) |
|
| 150 |
+ } |
|
| 151 |
+ return nil |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func (m *Mount) Unmount(root string) error {
|
|
| 155 |
+ target := path.Join(root, m.Target) |
|
| 156 |
+ if mounted, err := Mounted(target); err != nil || !mounted {
|
|
| 157 |
+ return err |
|
| 158 |
+ } |
|
| 159 |
+ return syscall.Unmount(target, 0) |
|
| 160 |
+} |
|
| 161 |
+ |
|
| 162 |
+func Mounted(mountpoint string) (bool, error) {
|
|
| 163 |
+ mntpoint, err := os.Stat(mountpoint) |
|
| 164 |
+ if err != nil {
|
|
| 165 |
+ if os.IsNotExist(err) {
|
|
| 166 |
+ return false, nil |
|
| 167 |
+ } |
|
| 168 |
+ return false, err |
|
| 169 |
+ } |
|
| 170 |
+ parent, err := os.Stat(path.Join(mountpoint, "..")) |
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ return false, err |
|
| 173 |
+ } |
|
| 174 |
+ mntpointSt := mntpoint.Sys().(*syscall.Stat_t) |
|
| 175 |
+ parentSt := parent.Sys().(*syscall.Stat_t) |
|
| 176 |
+ |
|
| 177 |
+ return mntpointSt.Dev != parentSt.Dev, nil |
|
| 178 |
+} |
| ... | ... |
@@ -21,12 +21,6 @@ lxc.network.mtu = 1500 |
| 21 | 21 |
{{$ROOTFS := .RootfsPath}}
|
| 22 | 22 |
lxc.rootfs = {{$ROOTFS}}
|
| 23 | 23 |
|
| 24 |
-{{if and .HostnamePath .HostsPath}}
|
|
| 25 |
-# enable domain name support |
|
| 26 |
-lxc.mount.entry = {{escapeFstabSpaces .HostnamePath}} {{escapeFstabSpaces $ROOTFS}}/etc/hostname none bind,ro 0 0
|
|
| 27 |
-lxc.mount.entry = {{escapeFstabSpaces .HostsPath}} {{escapeFstabSpaces $ROOTFS}}/etc/hosts none bind,ro 0 0
|
|
| 28 |
-{{end}}
|
|
| 29 |
- |
|
| 30 | 24 |
# use a dedicated pts for the container (and limit the number of pseudo terminal |
| 31 | 25 |
# available) |
| 32 | 26 |
lxc.pts = 1024 |
| ... | ... |
@@ -74,32 +68,20 @@ lxc.cgroup.devices.allow = c 10:200 rwm |
| 74 | 74 |
# standard mount point |
| 75 | 75 |
# Use mnt.putold as per https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/986385 |
| 76 | 76 |
lxc.pivotdir = lxc_putold |
| 77 |
+ |
|
| 78 |
+# NOTICE: These mounts must be applied within the namespace |
|
| 79 |
+ |
|
| 77 | 80 |
# WARNING: procfs is a known attack vector and should probably be disabled |
| 78 | 81 |
# if your userspace allows it. eg. see http://blog.zx2c4.com/749 |
| 79 | 82 |
lxc.mount.entry = proc {{escapeFstabSpaces $ROOTFS}}/proc proc nosuid,nodev,noexec 0 0
|
| 80 |
-# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 81 |
-# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 83 |
+ |
|
| 84 |
+# WARNING: sysfs is a known attack vector and should probably be disabled |
|
| 85 |
+# if your userspace allows it. eg. see http://bit.ly/T9CkqJ |
|
| 82 | 86 |
lxc.mount.entry = sysfs {{escapeFstabSpaces $ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0
|
| 87 |
+ |
|
| 83 | 88 |
lxc.mount.entry = devpts {{escapeFstabSpaces $ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0
|
| 84 |
-#lxc.mount.entry = varrun {{escapeFstabSpaces $ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0
|
|
| 85 |
-#lxc.mount.entry = varlock {{escapeFstabSpaces $ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
|
|
| 86 | 89 |
lxc.mount.entry = shm {{escapeFstabSpaces $ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
|
| 87 | 90 |
|
| 88 |
-# Inject dockerinit |
|
| 89 |
-lxc.mount.entry = {{escapeFstabSpaces .SysInitPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerinit none bind,ro 0 0
|
|
| 90 |
- |
|
| 91 |
-# Inject env |
|
| 92 |
-lxc.mount.entry = {{escapeFstabSpaces .EnvConfigPath}} {{escapeFstabSpaces $ROOTFS}}/.dockerenv none bind,ro 0 0
|
|
| 93 |
- |
|
| 94 |
-# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container |
|
| 95 |
-lxc.mount.entry = {{escapeFstabSpaces .ResolvConfPath}} {{escapeFstabSpaces $ROOTFS}}/etc/resolv.conf none bind,ro 0 0
|
|
| 96 |
-{{if .Volumes}}
|
|
| 97 |
-{{ $rw := .VolumesRW }}
|
|
| 98 |
-{{range $virtualPath, $realPath := .Volumes}}
|
|
| 99 |
-lxc.mount.entry = {{escapeFstabSpaces $realPath}} {{escapeFstabSpaces $ROOTFS}}/{{escapeFstabSpaces $virtualPath}} none bind,{{ if index $rw $virtualPath }}rw{{else}}ro{{end}} 0 0
|
|
| 100 |
-{{end}}
|
|
| 101 |
-{{end}}
|
|
| 102 |
- |
|
| 103 | 91 |
{{if (getHostConfig .).Privileged}}
|
| 104 | 92 |
{{if (getCapabilities .).AppArmor}}
|
| 105 | 93 |
lxc.aa_profile = unconfined |
| ... | ... |
@@ -486,10 +486,8 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin |
| 486 | 486 |
hostConfig: &HostConfig{},
|
| 487 | 487 |
Image: img.ID, // Always use the resolved image id |
| 488 | 488 |
NetworkSettings: &NetworkSettings{},
|
| 489 |
- // FIXME: do we need to store this in the container? |
|
| 490 |
- SysInitPath: runtime.sysInitPath, |
|
| 491 |
- Name: name, |
|
| 492 |
- Driver: runtime.driver.String(), |
|
| 489 |
+ Name: name, |
|
| 490 |
+ Driver: runtime.driver.String(), |
|
| 493 | 491 |
} |
| 494 | 492 |
container.root = runtime.containerRoot(container.ID) |
| 495 | 493 |
// Step 1: create the container directory. |
| ... | ... |
@@ -790,6 +788,67 @@ func (runtime *Runtime) Close() error {
|
| 790 | 790 |
return nil |
| 791 | 791 |
} |
| 792 | 792 |
|
| 793 |
+func (runtime *Runtime) getMounts(container *Container) ([]*graphdriver.Mount, error) {
|
|
| 794 |
+ // Generate additional bind mounts |
|
| 795 |
+ envPath, err := container.EnvConfigPath() |
|
| 796 |
+ if err != nil {
|
|
| 797 |
+ return nil, err |
|
| 798 |
+ } |
|
| 799 |
+ mounts := []*graphdriver.Mount{
|
|
| 800 |
+ {
|
|
| 801 |
+ Device: runtime.sysInitPath, |
|
| 802 |
+ Target: "/.dockerinit", |
|
| 803 |
+ Type: "none", |
|
| 804 |
+ Options: "bind,ro", |
|
| 805 |
+ }, |
|
| 806 |
+ {
|
|
| 807 |
+ Device: envPath, |
|
| 808 |
+ Target: "/.dockerenv", |
|
| 809 |
+ Type: "none", |
|
| 810 |
+ Options: "bind,ro", |
|
| 811 |
+ }, |
|
| 812 |
+ // In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container |
|
| 813 |
+ {
|
|
| 814 |
+ Device: container.ResolvConfPath, |
|
| 815 |
+ Target: "/etc/resolv.conf", |
|
| 816 |
+ Type: "none", |
|
| 817 |
+ Options: "bind,ro", |
|
| 818 |
+ }, |
|
| 819 |
+ } |
|
| 820 |
+ |
|
| 821 |
+ if container.HostnamePath != "" && container.HostsPath != "" {
|
|
| 822 |
+ mounts = append(mounts, |
|
| 823 |
+ &graphdriver.Mount{
|
|
| 824 |
+ Device: container.HostnamePath, |
|
| 825 |
+ Target: "/etc/hostname", |
|
| 826 |
+ Type: "none", |
|
| 827 |
+ Options: "bind,ro", |
|
| 828 |
+ }, |
|
| 829 |
+ &graphdriver.Mount{
|
|
| 830 |
+ Device: container.HostsPath, |
|
| 831 |
+ Target: "/etc/hosts", |
|
| 832 |
+ Type: "none", |
|
| 833 |
+ Options: "bind,ro", |
|
| 834 |
+ }) |
|
| 835 |
+ } |
|
| 836 |
+ |
|
| 837 |
+ for r, v := range container.Volumes {
|
|
| 838 |
+ mountAs := "ro" |
|
| 839 |
+ if container.VolumesRW[v] {
|
|
| 840 |
+ mountAs = "rw" |
|
| 841 |
+ } |
|
| 842 |
+ |
|
| 843 |
+ mounts = append(mounts, |
|
| 844 |
+ &graphdriver.Mount{
|
|
| 845 |
+ Device: v, |
|
| 846 |
+ Target: r, |
|
| 847 |
+ Type: "none", |
|
| 848 |
+ Options: fmt.Sprintf("bind,%s", mountAs),
|
|
| 849 |
+ }) |
|
| 850 |
+ } |
|
| 851 |
+ return mounts, nil |
|
| 852 |
+} |
|
| 853 |
+ |
|
| 793 | 854 |
func (runtime *Runtime) Mount(container *Container) error {
|
| 794 | 855 |
dir, err := runtime.driver.Get(container.ID) |
| 795 | 856 |
if err != nil {
|