Browse code

*: expose getResourcePath and getRootResourcePath wrappers

Due to the importance of path safety, the internal sanitisation wrappers
for volumes and containers should be exposed so other parts of Docker
can benefit from proper path sanitisation.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> (github: cyphar)

Aleksa Sarai authored on 2015/03/06 10:53:06
Showing 3 changed files
... ...
@@ -211,12 +211,37 @@ func (container *Container) LogEvent(action string) {
211 211
 	)
212 212
 }
213 213
 
214
-func (container *Container) getResourcePath(path string) (string, error) {
214
+// Evaluates `path` in the scope of the container's basefs, with proper path
215
+// sanitisation. Symlinks are all scoped to the basefs of the container, as
216
+// though the container's basefs was `/`.
217
+//
218
+// The basefs of a container is the host-facing path which is bind-mounted as
219
+// `/` inside the container. This method is essentially used to access a
220
+// particular path inside the container as though you were a process in that
221
+// container.
222
+//
223
+// NOTE: The returned path is *only* safely scoped inside the container's basefs
224
+//       if no component of the returned path changes (such as a component
225
+//       symlinking to a different path) between using this method and using the
226
+//       path. See symlink.FollowSymlinkInScope for more details.
227
+func (container *Container) GetResourcePath(path string) (string, error) {
215 228
 	cleanPath := filepath.Join("/", path)
216 229
 	return symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs)
217 230
 }
218 231
 
219
-func (container *Container) getRootResourcePath(path string) (string, error) {
232
+// Evaluates `path` in the scope of the container's root, with proper path
233
+// sanitisation. Symlinks are all scoped to the root of the container, as
234
+// though the container's root was `/`.
235
+//
236
+// The root of a container is the host-facing configuration metadata directory.
237
+// Only use this method to safely access the container's `container.json` or
238
+// other metadata files. If in doubt, use container.GetResourcePath.
239
+//
240
+// NOTE: The returned path is *only* safely scoped inside the container's root
241
+//       if no component of the returned path changes (such as a component
242
+//       symlinking to a different path) between using this method and using the
243
+//       path. See symlink.FollowSymlinkInScope for more details.
244
+func (container *Container) GetRootResourcePath(path string) (string, error) {
220 245
 	cleanPath := filepath.Join("/", path)
221 246
 	return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root)
222 247
 }
... ...
@@ -515,7 +540,7 @@ func (streamConfig *StreamConfig) StderrLogPipe() io.ReadCloser {
515 515
 }
516 516
 
517 517
 func (container *Container) buildHostnameFile() error {
518
-	hostnamePath, err := container.getRootResourcePath("hostname")
518
+	hostnamePath, err := container.GetRootResourcePath("hostname")
519 519
 	if err != nil {
520 520
 		return err
521 521
 	}
... ...
@@ -529,7 +554,7 @@ func (container *Container) buildHostnameFile() error {
529 529
 
530 530
 func (container *Container) buildHostsFiles(IP string) error {
531 531
 
532
-	hostsPath, err := container.getRootResourcePath("hosts")
532
+	hostsPath, err := container.GetRootResourcePath("hosts")
533 533
 	if err != nil {
534 534
 		return err
535 535
 	}
... ...
@@ -895,7 +920,7 @@ func (container *Container) Unmount() error {
895 895
 }
896 896
 
897 897
 func (container *Container) logPath(name string) (string, error) {
898
-	return container.getRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name))
898
+	return container.GetRootResourcePath(fmt.Sprintf("%s-%s.log", container.ID, name))
899 899
 }
900 900
 
901 901
 func (container *Container) ReadLog(name string) (io.Reader, error) {
... ...
@@ -907,11 +932,11 @@ func (container *Container) ReadLog(name string) (io.Reader, error) {
907 907
 }
908 908
 
909 909
 func (container *Container) hostConfigPath() (string, error) {
910
-	return container.getRootResourcePath("hostconfig.json")
910
+	return container.GetRootResourcePath("hostconfig.json")
911 911
 }
912 912
 
913 913
 func (container *Container) jsonPath() (string, error) {
914
-	return container.getRootResourcePath("config.json")
914
+	return container.GetRootResourcePath("config.json")
915 915
 }
916 916
 
917 917
 // This method must be exported to be used from the lxc template
... ...
@@ -981,7 +1006,7 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
981 981
 		}
982 982
 	}()
983 983
 
984
-	basePath, err := container.getResourcePath(resource)
984
+	basePath, err := container.GetResourcePath(resource)
985 985
 	if err != nil {
986 986
 		return nil, err
987 987
 	}
... ...
@@ -1083,7 +1108,7 @@ func (container *Container) setupContainerDns() error {
1083 1083
 	if err != nil {
1084 1084
 		return err
1085 1085
 	}
1086
-	container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf")
1086
+	container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
1087 1087
 	if err != nil {
1088 1088
 		return err
1089 1089
 	}
... ...
@@ -1244,7 +1269,7 @@ func (container *Container) initializeNetworking() error {
1244 1244
 			return err
1245 1245
 		}
1246 1246
 
1247
-		hostsPath, err := container.getRootResourcePath("hosts")
1247
+		hostsPath, err := container.GetRootResourcePath("hosts")
1248 1248
 		if err != nil {
1249 1249
 			return err
1250 1250
 		}
... ...
@@ -1375,7 +1400,7 @@ func (container *Container) setupWorkingDirectory() error {
1375 1375
 	if container.Config.WorkingDir != "" {
1376 1376
 		container.Config.WorkingDir = path.Clean(container.Config.WorkingDir)
1377 1377
 
1378
-		pth, err := container.getResourcePath(container.Config.WorkingDir)
1378
+		pth, err := container.GetResourcePath(container.Config.WorkingDir)
1379 1379
 		if err != nil {
1380 1380
 			return err
1381 1381
 		}
... ...
@@ -47,7 +47,7 @@ func (container *Container) createVolumes() error {
47 47
 			continue
48 48
 		}
49 49
 
50
-		realPath, err := container.getResourcePath(path)
50
+		realPath, err := container.GetResourcePath(path)
51 51
 		if err != nil {
52 52
 			return err
53 53
 		}
... ...
@@ -336,7 +336,7 @@ func (container *Container) mountVolumes() error {
336 336
 			return fmt.Errorf("could not find volume for %s:%s, impossible to mount", source, dest)
337 337
 		}
338 338
 
339
-		destPath, err := container.getResourcePath(dest)
339
+		destPath, err := container.GetResourcePath(dest)
340 340
 		if err != nil {
341 341
 			return err
342 342
 		}
... ...
@@ -347,7 +347,7 @@ func (container *Container) mountVolumes() error {
347 347
 	}
348 348
 
349 349
 	for _, mnt := range container.specialMounts() {
350
-		destPath, err := container.getResourcePath(mnt.Destination)
350
+		destPath, err := container.GetResourcePath(mnt.Destination)
351 351
 		if err != nil {
352 352
 			return err
353 353
 		}
... ...
@@ -360,7 +360,7 @@ func (container *Container) mountVolumes() error {
360 360
 
361 361
 func (container *Container) unmountVolumes() {
362 362
 	for dest := range container.Volumes {
363
-		destPath, err := container.getResourcePath(dest)
363
+		destPath, err := container.GetResourcePath(dest)
364 364
 		if err != nil {
365 365
 			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
366 366
 			continue
... ...
@@ -372,7 +372,7 @@ func (container *Container) unmountVolumes() {
372 372
 	}
373 373
 
374 374
 	for _, mnt := range container.specialMounts() {
375
-		destPath, err := container.getResourcePath(mnt.Destination)
375
+		destPath, err := container.GetResourcePath(mnt.Destination)
376 376
 		if err != nil {
377 377
 			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
378 378
 			continue
... ...
@@ -114,14 +114,38 @@ func (v *Volume) FromDisk() error {
114 114
 }
115 115
 
116 116
 func (v *Volume) jsonPath() (string, error) {
117
-	return v.getRootResourcePath("config.json")
117
+	return v.GetRootResourcePath("config.json")
118 118
 }
119
-func (v *Volume) getRootResourcePath(path string) (string, error) {
119
+
120
+// Evalutes `path` in the scope of the volume's root path, with proper path
121
+// sanitisation. Symlinks are all scoped to the root of the volume, as
122
+// though the volume's root was `/`.
123
+//
124
+// The volume's root path is the host-facing path of the root of the volume's
125
+// mountpoint inside a container.
126
+//
127
+// NOTE: The returned path is *only* safely scoped inside the volume's root
128
+//       if no component of the returned path changes (such as a component
129
+//       symlinking to a different path) between using this method and using the
130
+//       path. See symlink.FollowSymlinkInScope for more details.
131
+func (v *Volume) GetResourcePath(path string) (string, error) {
120 132
 	cleanPath := filepath.Join("/", path)
121
-	return symlink.FollowSymlinkInScope(filepath.Join(v.configPath, cleanPath), v.configPath)
133
+	return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path)
122 134
 }
123 135
 
124
-func (v *Volume) getResourcePath(path string) (string, error) {
136
+// Evalutes `path` in the scope of the volume's config path, with proper path
137
+// sanitisation. Symlinks are all scoped to the root of the config path, as
138
+// though the config path was `/`.
139
+//
140
+// The config path of a volume is not exposed to the container and is just used
141
+// to store volume configuration options and other internal information. If in
142
+// doubt, you probably want to just use v.GetResourcePath.
143
+//
144
+// NOTE: The returned path is *only* safely scoped inside the volume's config
145
+//       path if no component of the returned path changes (such as a component
146
+//       symlinking to a different path) between using this method and using the
147
+//       path. See symlink.FollowSymlinkInScope for more details.
148
+func (v *Volume) GetRootResourcePath(path string) (string, error) {
125 149
 	cleanPath := filepath.Join("/", path)
126
-	return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path)
150
+	return symlink.FollowSymlinkInScope(filepath.Join(v.configPath, cleanPath), v.configPath)
127 151
 }