-
Notifications
You must be signed in to change notification settings - Fork 159
validate: add mounts whether nested check #256
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
Conversation
wking
left a comment
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 |
| 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
| } | ||
|
|
||
| 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 |
| } | ||
|
|
||
| // 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]