Adds the `--userns-remap` flag to the master build
Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
... | ... |
@@ -2,118 +2,7 @@ |
2 | 2 |
|
3 | 3 |
package daemon |
4 | 4 |
|
5 |
-import ( |
|
6 |
- "fmt" |
|
7 |
- "strconv" |
|
8 |
- "strings" |
|
9 |
- |
|
10 |
- "github.com/docker/docker/pkg/idtools" |
|
11 |
- flag "github.com/docker/docker/pkg/mflag" |
|
12 |
- "github.com/opencontainers/runc/libcontainer/user" |
|
13 |
-) |
|
5 |
+import flag "github.com/docker/docker/pkg/mflag" |
|
14 | 6 |
|
15 | 7 |
func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { |
16 |
- cmd.StringVar(&config.RemappedRoot, []string{"-userns-remap"}, "", usageFn("User/Group setting for user namespaces")) |
|
17 |
-} |
|
18 |
- |
|
19 |
-const ( |
|
20 |
- defaultIDSpecifier string = "default" |
|
21 |
- defaultRemappedID string = "dockremap" |
|
22 |
-) |
|
23 |
- |
|
24 |
-// Parse the remapped root (user namespace) option, which can be one of: |
|
25 |
-// username - valid username from /etc/passwd |
|
26 |
-// username:groupname - valid username; valid groupname from /etc/group |
|
27 |
-// uid - 32-bit unsigned int valid Linux UID value |
|
28 |
-// uid:gid - uid value; 32-bit unsigned int Linux GID value |
|
29 |
-// |
|
30 |
-// If no groupname is specified, and a username is specified, an attempt |
|
31 |
-// will be made to lookup a gid for that username as a groupname |
|
32 |
-// |
|
33 |
-// If names are used, they are verified to exist in passwd/group |
|
34 |
-func parseRemappedRoot(usergrp string) (string, string, error) { |
|
35 |
- |
|
36 |
- var ( |
|
37 |
- userID, groupID int |
|
38 |
- username, groupname string |
|
39 |
- ) |
|
40 |
- |
|
41 |
- idparts := strings.Split(usergrp, ":") |
|
42 |
- if len(idparts) > 2 { |
|
43 |
- return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp) |
|
44 |
- } |
|
45 |
- |
|
46 |
- if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil { |
|
47 |
- // must be a uid; take it as valid |
|
48 |
- userID = int(uid) |
|
49 |
- luser, err := user.LookupUid(userID) |
|
50 |
- if err != nil { |
|
51 |
- return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err) |
|
52 |
- } |
|
53 |
- username = luser.Name |
|
54 |
- if len(idparts) == 1 { |
|
55 |
- // if the uid was numeric and no gid was specified, take the uid as the gid |
|
56 |
- groupID = userID |
|
57 |
- lgrp, err := user.LookupGid(groupID) |
|
58 |
- if err != nil { |
|
59 |
- return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err) |
|
60 |
- } |
|
61 |
- groupname = lgrp.Name |
|
62 |
- } |
|
63 |
- } else { |
|
64 |
- lookupName := idparts[0] |
|
65 |
- // special case: if the user specified "default", they want Docker to create or |
|
66 |
- // use (after creation) the "dockremap" user/group for root remapping |
|
67 |
- if lookupName == defaultIDSpecifier { |
|
68 |
- lookupName = defaultRemappedID |
|
69 |
- } |
|
70 |
- luser, err := user.LookupUser(lookupName) |
|
71 |
- if err != nil && idparts[0] != defaultIDSpecifier { |
|
72 |
- // error if the name requested isn't the special "dockremap" ID |
|
73 |
- return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err) |
|
74 |
- } else if err != nil { |
|
75 |
- // special case-- if the username == "default", then we have been asked |
|
76 |
- // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid} |
|
77 |
- // ranges will be used for the user and group mappings in user namespaced containers |
|
78 |
- _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID) |
|
79 |
- if err == nil { |
|
80 |
- return defaultRemappedID, defaultRemappedID, nil |
|
81 |
- } |
|
82 |
- return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err) |
|
83 |
- } |
|
84 |
- userID = luser.Uid |
|
85 |
- username = luser.Name |
|
86 |
- if len(idparts) == 1 { |
|
87 |
- // we only have a string username, and no group specified; look up gid from username as group |
|
88 |
- group, err := user.LookupGroup(lookupName) |
|
89 |
- if err != nil { |
|
90 |
- return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err) |
|
91 |
- } |
|
92 |
- groupID = group.Gid |
|
93 |
- groupname = group.Name |
|
94 |
- } |
|
95 |
- } |
|
96 |
- |
|
97 |
- if len(idparts) == 2 { |
|
98 |
- // groupname or gid is separately specified and must be resolved |
|
99 |
- // to a unsigned 32-bit gid |
|
100 |
- if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil { |
|
101 |
- // must be a gid, take it as valid |
|
102 |
- groupID = int(gid) |
|
103 |
- lgrp, err := user.LookupGid(groupID) |
|
104 |
- if err != nil { |
|
105 |
- return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err) |
|
106 |
- } |
|
107 |
- groupname = lgrp.Name |
|
108 |
- } else { |
|
109 |
- // not a number; attempt a lookup |
|
110 |
- group, err := user.LookupGroup(idparts[1]) |
|
111 |
- if err != nil { |
|
112 |
- return "", "", fmt.Errorf("Error during gid lookup for %q: %v", idparts[1], err) |
|
113 |
- } |
|
114 |
- groupID = group.Gid |
|
115 |
- groupname = idparts[1] |
|
116 |
- } |
|
117 |
- } |
|
118 |
- return username, groupname, nil |
|
119 | 8 |
} |
... | ... |
@@ -79,6 +79,7 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin |
79 | 79 |
cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header")) |
80 | 80 |
cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API")) |
81 | 81 |
cmd.StringVar(&config.CgroupParent, []string{"-cgroup-parent"}, "", usageFn("Set parent cgroup for all containers")) |
82 |
+ cmd.StringVar(&config.RemappedRoot, []string{"-userns-remap"}, "", usageFn("User/Group setting for user namespaces")) |
|
82 | 83 |
|
83 | 84 |
config.attachExperimentalFlags(cmd, usageFn) |
84 | 85 |
} |
... | ... |
@@ -2,88 +2,8 @@ |
2 | 2 |
|
3 | 3 |
package daemon |
4 | 4 |
|
5 |
-import ( |
|
6 |
- "fmt" |
|
7 |
- "os" |
|
8 |
- "path/filepath" |
|
9 |
- "runtime" |
|
10 |
- |
|
11 |
- "github.com/Sirupsen/logrus" |
|
12 |
- "github.com/docker/docker/pkg/idtools" |
|
13 |
- "github.com/docker/engine-api/types/container" |
|
14 |
-) |
|
15 |
- |
|
16 |
-func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { |
|
17 |
- if runtime.GOOS != "linux" && config.RemappedRoot != "" { |
|
18 |
- return nil, nil, fmt.Errorf("User namespaces are only supported on Linux") |
|
19 |
- } |
|
20 |
- |
|
21 |
- // if the daemon was started with remapped root option, parse |
|
22 |
- // the config option to the int uid,gid values |
|
23 |
- var ( |
|
24 |
- uidMaps, gidMaps []idtools.IDMap |
|
25 |
- ) |
|
26 |
- if config.RemappedRoot != "" { |
|
27 |
- username, groupname, err := parseRemappedRoot(config.RemappedRoot) |
|
28 |
- if err != nil { |
|
29 |
- return nil, nil, err |
|
30 |
- } |
|
31 |
- if username == "root" { |
|
32 |
- // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op |
|
33 |
- // effectively |
|
34 |
- logrus.Warnf("User namespaces: root cannot be remapped with itself; user namespaces are OFF") |
|
35 |
- return uidMaps, gidMaps, nil |
|
36 |
- } |
|
37 |
- logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) |
|
38 |
- // update remapped root setting now that we have resolved them to actual names |
|
39 |
- config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) |
|
40 |
- |
|
41 |
- uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) |
|
42 |
- if err != nil { |
|
43 |
- return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err) |
|
44 |
- } |
|
45 |
- } |
|
46 |
- return uidMaps, gidMaps, nil |
|
47 |
-} |
|
48 |
- |
|
49 |
-func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { |
|
50 |
- config.Root = rootDir |
|
51 |
- // the docker root metadata directory needs to have execute permissions for all users (o+x) |
|
52 |
- // so that syscalls executing as non-root, operating on subdirectories of the graph root |
|
53 |
- // (e.g. mounted layers of a container) can traverse this path. |
|
54 |
- // The user namespace support will create subdirectories for the remapped root host uid:gid |
|
55 |
- // pair owned by that same uid:gid pair for proper write access to those needed metadata and |
|
56 |
- // layer content subtrees. |
|
57 |
- if _, err := os.Stat(rootDir); err == nil { |
|
58 |
- // root current exists; verify the access bits are correct by setting them |
|
59 |
- if err = os.Chmod(rootDir, 0701); err != nil { |
|
60 |
- return err |
|
61 |
- } |
|
62 |
- } else if os.IsNotExist(err) { |
|
63 |
- // no root exists yet, create it 0701 with root:root ownership |
|
64 |
- if err := os.MkdirAll(rootDir, 0701); err != nil { |
|
65 |
- return err |
|
66 |
- } |
|
67 |
- } |
|
68 |
- |
|
69 |
- // if user namespaces are enabled we will create a subtree underneath the specified root |
|
70 |
- // with any/all specified remapped root uid/gid options on the daemon creating |
|
71 |
- // a new subdirectory with ownership set to the remapped uid/gid (so as to allow |
|
72 |
- // `chdir()` to work for containers namespaced to that uid/gid) |
|
73 |
- if config.RemappedRoot != "" { |
|
74 |
- config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID)) |
|
75 |
- logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) |
|
76 |
- // Create the root directory if it doesn't exists |
|
77 |
- if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil { |
|
78 |
- return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) |
|
79 |
- } |
|
80 |
- } |
|
81 |
- return nil |
|
82 |
-} |
|
5 |
+import "github.com/docker/engine-api/types/container" |
|
83 | 6 |
|
84 | 7 |
func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.HostConfig, config *container.Config) ([]string, error) { |
85 |
- if hostConfig.Privileged && daemon.configStore.RemappedRoot != "" { |
|
86 |
- return nil, fmt.Errorf("Privileged mode is incompatible with user namespace mappings") |
|
87 |
- } |
|
88 | 8 |
return nil, nil |
89 | 9 |
} |
... | ... |
@@ -2,26 +2,7 @@ |
2 | 2 |
|
3 | 3 |
package daemon |
4 | 4 |
|
5 |
-import ( |
|
6 |
- "os" |
|
7 |
- |
|
8 |
- "github.com/docker/docker/pkg/idtools" |
|
9 |
- "github.com/docker/docker/pkg/system" |
|
10 |
- "github.com/docker/engine-api/types/container" |
|
11 |
-) |
|
12 |
- |
|
13 |
-func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { |
|
14 |
- return nil, nil, nil |
|
15 |
-} |
|
16 |
- |
|
17 |
-func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { |
|
18 |
- config.Root = rootDir |
|
19 |
- // Create the root directory if it doesn't exists |
|
20 |
- if err := system.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { |
|
21 |
- return err |
|
22 |
- } |
|
23 |
- return nil |
|
24 |
-} |
|
5 |
+import "github.com/docker/engine-api/types/container" |
|
25 | 6 |
|
26 | 7 |
func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.HostConfig, config *container.Config) ([]string, error) { |
27 | 8 |
return nil, nil |
... | ... |
@@ -7,6 +7,7 @@ import ( |
7 | 7 |
"net" |
8 | 8 |
"os" |
9 | 9 |
"path/filepath" |
10 |
+ "runtime" |
|
10 | 11 |
"strconv" |
11 | 12 |
"strings" |
12 | 13 |
"syscall" |
... | ... |
@@ -33,6 +34,7 @@ import ( |
33 | 33 |
"github.com/docker/libnetwork/types" |
34 | 34 |
blkiodev "github.com/opencontainers/runc/libcontainer/configs" |
35 | 35 |
"github.com/opencontainers/runc/libcontainer/label" |
36 |
+ "github.com/opencontainers/runc/libcontainer/user" |
|
36 | 37 |
) |
37 | 38 |
|
38 | 39 |
const ( |
... | ... |
@@ -42,6 +44,9 @@ const ( |
42 | 42 |
platformSupported = true |
43 | 43 |
// It's not kernel limit, we want this 4M limit to supply a reasonable functional container |
44 | 44 |
linuxMinMemory = 4194304 |
45 |
+ // constants for remapped root settings |
|
46 |
+ defaultIDSpecifier string = "default" |
|
47 |
+ defaultRemappedID string = "dockremap" |
|
45 | 48 |
) |
46 | 49 |
|
47 | 50 |
func getBlkioWeightDevices(config *containertypes.HostConfig) ([]*blkiodev.WeightDevice, error) { |
... | ... |
@@ -375,6 +380,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. |
375 | 375 |
warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") |
376 | 376 |
logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") |
377 | 377 |
} |
378 |
+ if hostConfig.Privileged && daemon.configStore.RemappedRoot != "" { |
|
379 |
+ return warnings, fmt.Errorf("Privileged mode is incompatible with user namespace mappings") |
|
380 |
+ } |
|
378 | 381 |
return warnings, nil |
379 | 382 |
} |
380 | 383 |
|
... | ... |
@@ -674,6 +682,171 @@ func setupInitLayer(initLayer string, rootUID, rootGID int) error { |
674 | 674 |
return nil |
675 | 675 |
} |
676 | 676 |
|
677 |
+// Parse the remapped root (user namespace) option, which can be one of: |
|
678 |
+// username - valid username from /etc/passwd |
|
679 |
+// username:groupname - valid username; valid groupname from /etc/group |
|
680 |
+// uid - 32-bit unsigned int valid Linux UID value |
|
681 |
+// uid:gid - uid value; 32-bit unsigned int Linux GID value |
|
682 |
+// |
|
683 |
+// If no groupname is specified, and a username is specified, an attempt |
|
684 |
+// will be made to lookup a gid for that username as a groupname |
|
685 |
+// |
|
686 |
+// If names are used, they are verified to exist in passwd/group |
|
687 |
+func parseRemappedRoot(usergrp string) (string, string, error) { |
|
688 |
+ |
|
689 |
+ var ( |
|
690 |
+ userID, groupID int |
|
691 |
+ username, groupname string |
|
692 |
+ ) |
|
693 |
+ |
|
694 |
+ idparts := strings.Split(usergrp, ":") |
|
695 |
+ if len(idparts) > 2 { |
|
696 |
+ return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp) |
|
697 |
+ } |
|
698 |
+ |
|
699 |
+ if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil { |
|
700 |
+ // must be a uid; take it as valid |
|
701 |
+ userID = int(uid) |
|
702 |
+ luser, err := user.LookupUid(userID) |
|
703 |
+ if err != nil { |
|
704 |
+ return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err) |
|
705 |
+ } |
|
706 |
+ username = luser.Name |
|
707 |
+ if len(idparts) == 1 { |
|
708 |
+ // if the uid was numeric and no gid was specified, take the uid as the gid |
|
709 |
+ groupID = userID |
|
710 |
+ lgrp, err := user.LookupGid(groupID) |
|
711 |
+ if err != nil { |
|
712 |
+ return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err) |
|
713 |
+ } |
|
714 |
+ groupname = lgrp.Name |
|
715 |
+ } |
|
716 |
+ } else { |
|
717 |
+ lookupName := idparts[0] |
|
718 |
+ // special case: if the user specified "default", they want Docker to create or |
|
719 |
+ // use (after creation) the "dockremap" user/group for root remapping |
|
720 |
+ if lookupName == defaultIDSpecifier { |
|
721 |
+ lookupName = defaultRemappedID |
|
722 |
+ } |
|
723 |
+ luser, err := user.LookupUser(lookupName) |
|
724 |
+ if err != nil && idparts[0] != defaultIDSpecifier { |
|
725 |
+ // error if the name requested isn't the special "dockremap" ID |
|
726 |
+ return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err) |
|
727 |
+ } else if err != nil { |
|
728 |
+ // special case-- if the username == "default", then we have been asked |
|
729 |
+ // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid} |
|
730 |
+ // ranges will be used for the user and group mappings in user namespaced containers |
|
731 |
+ _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID) |
|
732 |
+ if err == nil { |
|
733 |
+ return defaultRemappedID, defaultRemappedID, nil |
|
734 |
+ } |
|
735 |
+ return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err) |
|
736 |
+ } |
|
737 |
+ userID = luser.Uid |
|
738 |
+ username = luser.Name |
|
739 |
+ if len(idparts) == 1 { |
|
740 |
+ // we only have a string username, and no group specified; look up gid from username as group |
|
741 |
+ group, err := user.LookupGroup(lookupName) |
|
742 |
+ if err != nil { |
|
743 |
+ return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err) |
|
744 |
+ } |
|
745 |
+ groupID = group.Gid |
|
746 |
+ groupname = group.Name |
|
747 |
+ } |
|
748 |
+ } |
|
749 |
+ |
|
750 |
+ if len(idparts) == 2 { |
|
751 |
+ // groupname or gid is separately specified and must be resolved |
|
752 |
+ // to a unsigned 32-bit gid |
|
753 |
+ if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil { |
|
754 |
+ // must be a gid, take it as valid |
|
755 |
+ groupID = int(gid) |
|
756 |
+ lgrp, err := user.LookupGid(groupID) |
|
757 |
+ if err != nil { |
|
758 |
+ return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err) |
|
759 |
+ } |
|
760 |
+ groupname = lgrp.Name |
|
761 |
+ } else { |
|
762 |
+ // not a number; attempt a lookup |
|
763 |
+ group, err := user.LookupGroup(idparts[1]) |
|
764 |
+ if err != nil { |
|
765 |
+ return "", "", fmt.Errorf("Error during gid lookup for %q: %v", idparts[1], err) |
|
766 |
+ } |
|
767 |
+ groupID = group.Gid |
|
768 |
+ groupname = idparts[1] |
|
769 |
+ } |
|
770 |
+ } |
|
771 |
+ return username, groupname, nil |
|
772 |
+} |
|
773 |
+ |
|
774 |
+func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { |
|
775 |
+ if runtime.GOOS != "linux" && config.RemappedRoot != "" { |
|
776 |
+ return nil, nil, fmt.Errorf("User namespaces are only supported on Linux") |
|
777 |
+ } |
|
778 |
+ |
|
779 |
+ // if the daemon was started with remapped root option, parse |
|
780 |
+ // the config option to the int uid,gid values |
|
781 |
+ var ( |
|
782 |
+ uidMaps, gidMaps []idtools.IDMap |
|
783 |
+ ) |
|
784 |
+ if config.RemappedRoot != "" { |
|
785 |
+ username, groupname, err := parseRemappedRoot(config.RemappedRoot) |
|
786 |
+ if err != nil { |
|
787 |
+ return nil, nil, err |
|
788 |
+ } |
|
789 |
+ if username == "root" { |
|
790 |
+ // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op |
|
791 |
+ // effectively |
|
792 |
+ logrus.Warnf("User namespaces: root cannot be remapped with itself; user namespaces are OFF") |
|
793 |
+ return uidMaps, gidMaps, nil |
|
794 |
+ } |
|
795 |
+ logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) |
|
796 |
+ // update remapped root setting now that we have resolved them to actual names |
|
797 |
+ config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) |
|
798 |
+ |
|
799 |
+ uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) |
|
800 |
+ if err != nil { |
|
801 |
+ return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err) |
|
802 |
+ } |
|
803 |
+ } |
|
804 |
+ return uidMaps, gidMaps, nil |
|
805 |
+} |
|
806 |
+ |
|
807 |
+func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { |
|
808 |
+ config.Root = rootDir |
|
809 |
+ // the docker root metadata directory needs to have execute permissions for all users (o+x) |
|
810 |
+ // so that syscalls executing as non-root, operating on subdirectories of the graph root |
|
811 |
+ // (e.g. mounted layers of a container) can traverse this path. |
|
812 |
+ // The user namespace support will create subdirectories for the remapped root host uid:gid |
|
813 |
+ // pair owned by that same uid:gid pair for proper write access to those needed metadata and |
|
814 |
+ // layer content subtrees. |
|
815 |
+ if _, err := os.Stat(rootDir); err == nil { |
|
816 |
+ // root current exists; verify the access bits are correct by setting them |
|
817 |
+ if err = os.Chmod(rootDir, 0701); err != nil { |
|
818 |
+ return err |
|
819 |
+ } |
|
820 |
+ } else if os.IsNotExist(err) { |
|
821 |
+ // no root exists yet, create it 0701 with root:root ownership |
|
822 |
+ if err := os.MkdirAll(rootDir, 0701); err != nil { |
|
823 |
+ return err |
|
824 |
+ } |
|
825 |
+ } |
|
826 |
+ |
|
827 |
+ // if user namespaces are enabled we will create a subtree underneath the specified root |
|
828 |
+ // with any/all specified remapped root uid/gid options on the daemon creating |
|
829 |
+ // a new subdirectory with ownership set to the remapped uid/gid (so as to allow |
|
830 |
+ // `chdir()` to work for containers namespaced to that uid/gid) |
|
831 |
+ if config.RemappedRoot != "" { |
|
832 |
+ config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID)) |
|
833 |
+ logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) |
|
834 |
+ // Create the root directory if it doesn't exists |
|
835 |
+ if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil { |
|
836 |
+ return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) |
|
837 |
+ } |
|
838 |
+ } |
|
839 |
+ return nil |
|
840 |
+} |
|
841 |
+ |
|
677 | 842 |
// registerLinks writes the links to a file. |
678 | 843 |
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { |
679 | 844 |
if hostConfig == nil || hostConfig.Links == nil { |
... | ... |
@@ -4,6 +4,7 @@ import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"errors" |
6 | 6 |
"fmt" |
7 |
+ "os" |
|
7 | 8 |
"path/filepath" |
8 | 9 |
"runtime" |
9 | 10 |
"strings" |
... | ... |
@@ -18,6 +19,7 @@ import ( |
18 | 18 |
containertypes "github.com/docker/engine-api/types/container" |
19 | 19 |
// register the windows graph driver |
20 | 20 |
"github.com/docker/docker/daemon/graphdriver/windows" |
21 |
+ "github.com/docker/docker/pkg/idtools" |
|
21 | 22 |
"github.com/docker/docker/pkg/system" |
22 | 23 |
"github.com/docker/libnetwork" |
23 | 24 |
blkiodev "github.com/opencontainers/runc/libcontainer/configs" |
... | ... |
@@ -135,6 +137,19 @@ func (daemon *Daemon) cleanupMounts() error { |
135 | 135 |
return nil |
136 | 136 |
} |
137 | 137 |
|
138 |
+func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { |
|
139 |
+ return nil, nil, nil |
|
140 |
+} |
|
141 |
+ |
|
142 |
+func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { |
|
143 |
+ config.Root = rootDir |
|
144 |
+ // Create the root directory if it doesn't exists |
|
145 |
+ if err := system.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { |
|
146 |
+ return err |
|
147 |
+ } |
|
148 |
+ return nil |
|
149 |
+} |
|
150 |
+ |
|
138 | 151 |
// conditionalMountOnStart is a platform specific helper function during the |
139 | 152 |
// container start to call mount. |
140 | 153 |
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { |
... | ... |
@@ -99,7 +99,7 @@ if [ ! "$GOPATH" ]; then |
99 | 99 |
exit 1 |
100 | 100 |
fi |
101 | 101 |
|
102 |
-if [ "$DOCKER_EXPERIMENTAL" ] || [ "$DOCKER_REMAP_ROOT" ]; then |
|
102 |
+if [ "$DOCKER_EXPERIMENTAL" ]; then |
|
103 | 103 |
echo >&2 '# WARNING! DOCKER_EXPERIMENTAL is set: building experimental features' |
104 | 104 |
echo >&2 |
105 | 105 |
DOCKER_BUILDTAGS+=" experimental pkcs11" |
... | ... |
@@ -652,10 +652,14 @@ func (s *DockerSuite) TestContainerApiCreateWithDomainName(c *check.C) { |
652 | 652 |
c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname")) |
653 | 653 |
} |
654 | 654 |
|
655 |
-func (s *DockerSuite) TestContainerApiCreateNetworkMode(c *check.C) { |
|
655 |
+func (s *DockerSuite) TestContainerApiCreateBridgeNetworkMode(c *check.C) { |
|
656 | 656 |
testRequires(c, DaemonIsLinux) |
657 |
- UtilCreateNetworkMode(c, "host") |
|
658 | 657 |
UtilCreateNetworkMode(c, "bridge") |
658 |
+} |
|
659 |
+ |
|
660 |
+func (s *DockerSuite) TestContainerApiCreateOtherNetworkModes(c *check.C) { |
|
661 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace) |
|
662 |
+ UtilCreateNetworkMode(c, "host") |
|
659 | 663 |
UtilCreateNetworkMode(c, "container:web1") |
660 | 664 |
} |
661 | 665 |
|