-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
validate: add mounts whether nested check #256
validate: add mounts whether nested check #256
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking for nesting and warning for covers both sound like good ideas, but the current implementation here still needs some work.
validate/validate.go
Outdated
if supportedTypes != nil && !supportedTypes[fMount.Type] { | ||
msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", fMount.Type)) | ||
} | ||
for j, bMount := range v.spec.Mounts { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why fMount
and bMount
? Maybe mount1
and mount2
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fMount means formerMount, bMount means backMount.
Maybe it's not easy to understand, I will modify them to mountA, mountB
validate/validate.go
Outdated
for j, bMount := range v.spec.Mounts { | ||
if nestedValid(bMount.Destination, fMount.Destination) { | ||
if v.spec.Platform.OS == "windows" { | ||
msgs = append(msgs, fmt.Sprintf("On Windows, %v nested within %v is forbidden", bMount.Destination, fMount.Destination)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking over the spec language again, I'm not clear that it's forbidding covering mounts (e.g. mounting c:\foo
after mounting c:\foo\bar
). If that's the intention, I think we need stronger/clearer spec language before forbidding them here (vs. just warning about them, like you're doing for Linux).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SPEC says "For the Windows operating system, one mount destination MUST NOT be nested within another mount (e.g., c:\foo and c:\foo\bar)."
I think it clearly says nested mounts are forbidden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nested yes. What I'm not clear on is whether covering mounts are also forbidden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's really a problem. The SEPC doesn't say.
I will add limit, let's just check nested mounts first.
validate/validate.go
Outdated
msgs = append(msgs, fmt.Sprintf("On Windows, %v nested within %v is forbidden", bMount.Destination, fMount.Destination)) | ||
} | ||
if v.spec.Platform.OS == "linux" && i > j { | ||
logrus.Warnf("On Linux, %v will be covered by %v", bMount.Destination, fMount.Destination) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like something we should be handling in and else
case so we also print warnings for Solaris and other OSes that don't forbid nesting/covering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with other OSes, which ones should we cover?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If warning for covering mounts is a good idea for Linux (and I think it is), I expect it is a good idea for all OSes where it's legal (and where it's illegal we should be erroring).
validate/validate.go
Outdated
return false | ||
} | ||
|
||
return strings.HasPrefix(bPath, fPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strings.HasPrefix
is not what you want to use (e.g. strings.HasPrefix("/ab", "/a")
is true, but /ab
and /a
are siblings). What you want is something like filepath.Rel
, but using the path syntax appropriate for the config.json
's declared OS instead of the path syntax for which oci-runtime-tool
was compiled. So windowsfilepath.Rel
or posixfilepath.Rel
, depending on platform.os
.
e619170
to
28af22a
Compare
@wking PTAL |
validate/validate.go
Outdated
return true | ||
} else if os != "windows" && strings.HasPrefix(splitedPaths[1], "/") { | ||
return true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Determining how the paths are related is complicated enough; I'd rather not mix in validation here. Can we have a ancestorPath(os string, pathA string, pathB string)
function that returns true if and only if pathB
is an ancestor of pathA
using the os
path syntax?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the function should probably be ancestorPath(os string, pathA string, pathB string, strict boolean) (isAncestor boolean)
, where when strict
is false, isAncestor
is true when pathA
and pathB
are equivalent or pathB
is a strict ancestor of pathA
.
validate/validate.go
Outdated
return false | ||
} else if strings.HasPrefix(purePathB, purePathA) { | ||
splitedPaths := strings.SplitN(purePathB, purePathA, 2) | ||
if os == "windows" && strings.HasPrefix(splitedPaths[1], "\\") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll also want to catch the case where splitedPaths
(pathComponents
?) has a length of one (because the initial path was /
or some such).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
validate/validate.go
Outdated
purePathB = strings.TrimSuffix(pathB, "\\") | ||
} else { | ||
purePathA = strings.TrimSuffix(pathA, "/") | ||
purePathB = strings.TrimSuffix(pathB, "/") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic throughout this function would probably be simpler if you open with:
var sep string
if os == "windows" {
sep = "\\"
} else {
sep = "/"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine.
validate/validate.go
Outdated
@@ -490,6 +499,30 @@ func namespaceValid(ns rspec.Namespace) bool { | |||
return true | |||
} | |||
|
|||
func nestedValid(os, pathA, pathB string) bool { | |||
var purePathA, purePathB string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The paths were passed by value, so you drop purePath*
and just clobber pathA
and pathB
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
validate/validate.go
Outdated
msgs = append(msgs, fmt.Sprintf("On Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination)) | ||
} | ||
if i > j { | ||
// FIXME: add erroring for OS which not support covering mounts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily a fixme, since covering may be legal on all OSes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine, will remove
validate/validate.go
Outdated
} | ||
if i > j { | ||
// FIXME: add erroring for OS which not support covering mounts | ||
logrus.Warnf("On %v, %v will be covered by %v", v.spec.Platform.OS, mountB.Destination, mountA.Destination) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd drop “On %v,”. Covering is surprising behavior worth warning about regardless of the target OS. The nesting error a few lines up, on the other hand, is a Windows-specific error, so opening with “On Windows,” is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
28af22a
to
eb43843
Compare
validate/validate.go
Outdated
purePathA = strings.TrimSuffix(pathA, sep) | ||
purePathB = strings.TrimSuffix(pathB, sep) | ||
|
||
if sep == "/" && pathA == sep && strings.HasPrefx(pathB, pathA) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validate/validate.go
Outdated
sep = "/" | ||
} | ||
purePathA = strings.TrimSuffix(pathA, sep) | ||
purePathB = strings.TrimSuffix(pathB, sep) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think these could be:
pathA = strings.TrimSuffix(pathA, sep)
and I don't think you need the sep == "/" && pathA == sep
qualifiers below.
bf64d9d
to
1b23a98
Compare
@opencontainers/runtime-tools-maintainers @wking PTAL |
validate/validate.go
Outdated
@@ -490,6 +504,89 @@ func namespaceValid(ns rspec.Namespace) bool { | |||
return true | |||
} | |||
|
|||
// Check whether pathB is nested whithin pathA | |||
func nestedValid(os, pathA, pathB string) (bool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't read through, according to your comments, isn't this something we can do with just filepath.Clean()
and filepath.Rel()
?
And I don't know how you handle with different oses other places in runtime-tools, but why not separate them in different files, distinguish with build tags or different command options. I'll be more extendable and make codes neat and clean.
On 11/04/2016 03:11 PM, Qiang Huang wrote:
Well, with Clan() and Rel(), we still can't find out invalid path
There are not too many places in runtime-tools different for different oses. |
In case it helps make my explicit-OS filepath idea 1 clearer, I've Instead of duplicating Go's (complicated and well-tested) stdlib |
Hi @wking I don't separating explicit-OS library and build with build tags can solve the question you raised about validating Windows container's config on Linux platform or validating Linux container's config on Windows platform. As you know, Go just does separating explicit-OS library, but can't solve the above question |
Path validation is orthogonal in this case, I don't think you should consider that, otherwise, do you do path validation in every case that a file path is involved? Even you want to do that, you can do it in a separate function named like
What do you mean about this, I don't get it. |
On 11/04/2016 05:32 PM, Qiang Huang wrote:
I think this is a good idea. I can separate it.
I mean Rel() is not very good to judge whether paths like /c and /b is nested or not. |
On Fri, Nov 04, 2016 at 01:52:23AM -0700, Ma Shimiao wrote:
Go has an implicit OS file/filepath using build tags to build it for IsAbs("windows", "C:\a\b\c") and get ‘true’ regardless of whether your $GOOS is windows or not. |
3957c6f
to
c071b26
Compare
Signed-off-by: Ma Shimiao <[email protected]>
ping @wking @opencontainers/runtime-tools-maintainers |
c071b26
to
4dcd484
Compare
1 similar comment
Go's path/filepath has lots of useful path operations, but there is a compile-time decision to select only the logic that applies to your $GOOS. That makes it hard to validate a Windows config from a Linux host (or vice versa) because Go's builtin tools won't tell you whether a path is absolute on the target platform (just whether the path is absolute on *your* platform). This commit adds a new package to do the same sorts of things but with an explicit OS argument. In some cases, there's also an explicit workding directory argument. For example, Go's Abs has [1]: If the path is not absolute it will be joined with the current working directory to turn it into an absolute path. but that doesn't make sense for a cross-platform Abs call because the real current working directory will be for the wrong platform. Instead, cross-platform calls to Abs and similar should fake a working directory as if they were being called from the other platform. The Windows implementation is not very complete, with IsAbs definitely missing a lot of stuff; Abs, Clean, and IsAncestor probably missing stuff; and a lack of Windows-path tests. But the current tools are broken for validating Windows configs anyway, so I've left completing Windows support to future work. The regular expression I've used is similar to what we used to use in pathValid, but has been adjusted based on [2]: The absolute path has the following format: LocalDrive:\Path\FileName I've assumed that c:\a is absolute as well, even though it doesn't quite match the above pattern. And I no longer test for colons, question marks, and other reserved characters [3], although it would be easy to add checks for that as well if we wanted. Besides adding the new package, I updated the config validation to use the new package where appropriate. For example checks for absolute hook paths (based on [4]) now appropriately account for the target platform (although Abs has limited Windows support at the moment, as mentioned above). There are still a number of config validation checks that use Go's stock filepath, because they're based around actual filesystem access (e.g. reading config.json off the disk, asserting that root.path exists on the disk, etc.). Some of those will need logic to convert between path platforms (which I'm leaving to future work). For example, if root.path is formed for another platform, then: * If root.path is absolute (on the target platform), there's no way to check whether root.path exists inside the bundle. * If root.path is relative, we should be converting it from the target platform to the host platform before joining it to our bundle path. For example, with a Windows bundle in "/foo" on a Linux host where root.path is "bar\baz", then runtime-tools should be checking for the root directory in /foo/bar/baz, not in /foo/bar\baz. The root.path example is somewhat guarded by the bundle requirement for siblinghood [5], but I'd rather enforce siblinghood independently from existence [6], since the spec has separate requirements for both. I'm not clear on why we were using pathValid for mount sources on Windows. We've been doing that since 4dcd484 (validate: add mounts whether nested check, 2016-10-25, opencontainers#256, [7]), but I see no absolute-path requirement in the spec [8], which only forbids UNC paths and mapped drives for source (which we don't yet have tooling to detect). Perhaps the idea was just to check for paths which contained reserved characters? In any case, I think source handling on Windows should not involve IsAbs and should be addressed more thoroughly in future work. [1]: https://golang.org/pkg/path/filepath/#Abs [2]: https://technet.microsoft.com/en-us/library/ee692607.aspx [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions [4]: https://github.com/opencontainers/runtime-spec/blame/v1.0.0/config.md#L375 [5]: https://github.com/opencontainers/runtime-spec/blame/v1.0.0/bundle.md#L17-L19 [6]: https://github.com/opencontainers/runtime-spec/blame/v1.0.0/config.md#L43 [7]: https://github.com/opencontainers/runtime-tools/pull/256/files#diff-8351286f57eb81e245c9c99c07f3b34fR413 [8]: https://github.com/opencontainers/runtime-spec/blob/v1.0.0/config.md#mounts Signed-off-by: W. Trevor King <[email protected]>
Signed-off-by: Ma Shimiao [email protected]