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
repack: do not include Volume paths in new layers #127
Conversation
@vrothberg PTAL |
f29c820
to
b150f72
Compare
Sorry, I did not forget about this. Hopefully I find time to check this PR next week. |
This is now actually recommended by opencontainers/image-spec#694 (yay, finally). |
Awesome! I put checking the code on tomorrow's to-do. |
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.
Somehow my guts tell me to invert the functionality and add an --unmask-volumes flag and mask volumes by default. Especially because "If a new image is created from a container based on the image described by this configuration, data in these paths SHOULD NOT be included in the new image."
pkg/mtreefilter/mask.go
Outdated
return func(path string) bool { | ||
// Convert path to relative-to-root. | ||
path = filepath.Clean(path) | ||
path = filepath.Join("/", path) |
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.
Do you mean an absolute path? If so, we should use filpath.Abs(path)
.
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.
filepath.Abs
can fail, and it also tries to do things we don't want from memory. Effectively what this does in this context is that it makes the path underneath the root. However, I've just realised that it shouldn't be necessary to have a Clean(path)
beforehand because the prefix path is /
(which is safe against Clean
issues).
pkg/mtreefilter/mask.go
Outdated
for _, mask := range masks { | ||
// Mask also needs to be relative-to-root. | ||
mask = filepath.Clean(mask) | ||
mask = filepath.Join("/", mask) |
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.
Absolute path?
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.
See above.
// FilterFunc is a function used when filtering deltas with FilterDeltas. | ||
type FilterFunc func(path string) bool | ||
|
||
// isParent returns whether the path a is lexically an ancestor of the path b. |
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 a very elegant function 👍
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.
Thanks. 😉
bundle-verify "$BUNDLE_A" | ||
|
||
# Create files in those volumes. | ||
mkdir -p "$BUNDLE_A/rootfs/some nutty/path name/" |
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 would expect unpack
to create volume paths. Is there a specific reason not to? Run-times should be able to deal with both cases?
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.
Hmmm. I'm not sure. VOLUME
in a Dockerfile creates the directory in the image (from memory) so I'm not sure if it makes sense for us to do that -- what should we do if a non-directory already exists there (for example)? I don't like the idea of erroring out in that case, but ignoring it also seems fishy.
I'll have to think about it some more, but can you open an issue for us to review at a later date?
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.
Good idea, let's move to an issue 👍
test/repack.bats
Outdated
! [ -e "$BUNDLE_B/rootfs/some nutty/path name" ] | ||
! [ -e "$BUNDLE_B/rootfs/some nutty/path name/ here" ] | ||
|
||
# TODO: We need to add some flags to repack to allow users to ignore VOLUME. |
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 am not sure I understand this comment correctly. What do you mean by "ignore"? In the sense that content will be copied regardless of the path being a volume?
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.
There's one hiding at the bottom :)
CHANGELOG.md
Outdated
@@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) | |||
and this project adheres to [Semantic Versioning](http://semver.org/). | |||
|
|||
## [Unreleased] | |||
### Added | |||
- `umoci repack` now supports `--mask-path` to ignore certain changes in the | |||
rootfs when generating new layers. |
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 prefer it to be more explicit: [...] to ignore changes in specified path of the rootfs when generating new layers.
cmd/umoci/repack.go
Outdated
if ctx.Bool("mask-volumes") { | ||
for v := range config.Volumes { | ||
volumes = append(volumes, v) | ||
} | ||
} |
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 suggest to rename volumes
to maskedPaths
.
test/repack.bats
Outdated
[ "$status" -eq 0 ] | ||
image-verify "${IMAGE}" | ||
|
||
# Re-extract to verify the volume changes weren't included. |
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 masked path changes...
test/repack.bats
Outdated
# Masked paths must not be included. | ||
! [ -e "$BUNDLE_C/rootfs/volume" ] | ||
! [ -e "$BUNDLE_C/rootfs/volume/test" ] | ||
# But volumes might not be included. |
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.
might BE included?
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.
Some responses enclosed.
As for the opt-in security (we already discussed this, I'm just putting it here for posterity):
I agree that opt-in security is bad (and my main argument for proposing adding this language to the spec was security). My main concern was that users like KIWI don't actually want to have this option be the default, and I'm not sure how we should approach changing our API (specifically adding flags that opt-out of new features) for such users.
We need to talk to the KIWI folks about what they want the default to be and come to an agreement on how they would like to be able to interface with us if we change our ABI. This will additionally be very important with the rc5
bump because in future I'm planning on changing how you reference tags (in somewhat minor ways, but I really want --layout
to die).
pkg/mtreefilter/mask.go
Outdated
return func(path string) bool { | ||
// Convert path to relative-to-root. | ||
path = filepath.Clean(path) | ||
path = filepath.Join("/", path) |
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.
filepath.Abs
can fail, and it also tries to do things we don't want from memory. Effectively what this does in this context is that it makes the path underneath the root. However, I've just realised that it shouldn't be necessary to have a Clean(path)
beforehand because the prefix path is /
(which is safe against Clean
issues).
// FilterFunc is a function used when filtering deltas with FilterDeltas. | ||
type FilterFunc func(path string) bool | ||
|
||
// isParent returns whether the path a is lexically an ancestor of the path b. |
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.
Thanks. 😉
pkg/mtreefilter/mask.go
Outdated
for _, mask := range masks { | ||
// Mask also needs to be relative-to-root. | ||
mask = filepath.Clean(mask) | ||
mask = filepath.Join("/", mask) |
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.
See above.
bundle-verify "$BUNDLE_A" | ||
|
||
# Create files in those volumes. | ||
mkdir -p "$BUNDLE_A/rootfs/some nutty/path name/" |
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.
Hmmm. I'm not sure. VOLUME
in a Dockerfile creates the directory in the image (from memory) so I'm not sure if it makes sense for us to do that -- what should we do if a non-directory already exists there (for example)? I don't like the idea of erroring out in that case, but ignoring it also seems fishy.
I'll have to think about it some more, but can you open an issue for us to review at a later date?
b150f72
to
1ee0585
Compare
I'm going to wait on merging this until I speak to @davidcassany about how he feels about |
This makes it much simpler to create filters for []mtree.InodeDelta given an arbitrary filter function. This is necessary to implement volume masking (as well as arbitrary path masking and possibly glob masking in the future). Signed-off-by: Aleksa Sarai <asarai@suse.de>
The reason for the split is that some users may wish to mask arbitrary paths that have not been set with --config.volumes (which therefore requires --mask-path). We make --no-mask-volumes an opt-out option (despite the backwards compatibility problem) because it's a security feature that really shouldn't be opt-in. I don't anticipate this will break current users of KIWI (and future users will hit this and realise it's a feature). However, this needs to be toggle-able because: 1. It is a backwards-incompatible change to the command-line API. Since KIWI has its own opinions about configuration and its relation to file unpacking, so older users of umoci need a way to switch to the old semantics. 2. Some users don't want to follow the suggestion of the spec that volumes should not be included in layers. While it's not clear when this would be a good idea (--no-mask-volumes is a security feature against credential exposure), we must retain it as an option. If nobody ends up using it we can remove the ability to disable it. Signed-off-by: Aleksa Sarai <asarai@suse.de>
1ee0585
to
f4153fe
Compare
I've rebased this and made it opt-out after discussions with @davidcassany. I agree with him that I severely doubt that any current users will be broken by this and in the future KIWI will have strict requirements on |
Signed-off-by: Aleksa Sarai <asarai@suse.de>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
f4153fe
to
1e52a94
Compare
LGTM. |
This is (unfortunately) not mandated in the specification, but in
order to avoid accidentally spilling private information into published
layers (which is one use of Volumes) we must ignore all layers included
in Config.Volumes.
In future we should also add some flags or alernative ways of masking
paths.
Signed-off-by: Aleksa Sarai asarai@suse.de