-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
libct/cap: switch to moby/sys/capability, lazy init #4358
base: main
Are you sure you want to change the base?
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.
LGTM; left one suggestion (not blocking)
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.
LGTM, thanks!
This still LGTM. Let me know if it's ready, IMHO this is ready to merge. |
@kolyshkin was the last commit intended for this PR, or for a follow up? |
go.mod
Outdated
@@ -10,14 +10,14 @@ require ( | |||
github.com/cyphar/filepath-securejoin v0.2.4 | |||
github.com/docker/go-units v0.5.0 | |||
github.com/godbus/dbus/v5 v5.1.0 | |||
github.com/kolyshkin/capability v0.1.0 |
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.
Oh! I saw you work on this repository; should we try get this in either the opencontainers
or moby
org?
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.
Or maybe the author of https://github.com/syndtr/gocapability is open to transferring maintenance (I think it's largely unmaintained, but could of course be just "moving slow")
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.
@thaJeztah I was thinking of opencontainers but I'm not an org admin so can't create new repos here. Maybe moby is a good place; I guess I can transfer the repo.
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.
Yeah, would be good; where possible I try to reduce dependencies from personal accounts. No discredit to those authors (you in this case), but not everyone may have familiarity with the author. Having the code in an org also can be more flexible and future-proof (in addition to being able to benefit from such orgs usually being on a (sponsored) enterprise plan, which may come with additional features on GitHub).
Either one would be fine with me; doing a very cursory check on where it's used (and thus potential candidates to switch), I see these;
AFAICS it's no longer a direct dependency in any of the projects in the Moby org, but I'd not consider that a blocker for having it in that org (personally). Happy to hear from others though! If we prefer opencontainers
, I guess we can ask an owner to help transferring.
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 was talking in another PR about maybe not using the personal account (#4362 (comment)). IMHO moby or opencontainers seem 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.
Shall we review and merge this or do we want to wait until we sort out the repo ownership?
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.
Discussing with maintainers in the Moby call, and it looks like there's no objections against moving it in the moby org, so we may as well try to set that in motion (I can catch up with Kir on that)
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.
Yes, let's move this to org repo first.
@caniszczyk can you help me move https://github.com/kolyshkin/capability to under opencontainers org?
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.
opened kolyshkin/capability#14
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.
In general you need an OCI TOB vote to add projects to opencontainers, I suspect putting it moby
would be simpler. I'm open to proposing it to the TOB though.
I can split this PR into two, but really I want all this to be merged (and you don't have to re-review the first two commits). |
This comment was marked as outdated.
This comment was marked as outdated.
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 mostly LGTM, left some comments
// | ||
// If mustHave argument is not nil, caps that are not present in mustHave | ||
// are appended to ignoredCaps instead of the resulting slice. | ||
capSlice := func(caps []string, mustHave []capability.Cap) []capability.Cap { |
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.
A "global" list of ignoredCaps that can be modified by any call to this function doesn't play nice with the fact that we only want these capabilities when we call this with ambient/inheritable. All the other calls, we don't want them to modify this.
Right now all the other calls use nil as the second param and only ambient/inheritable use non nil for the second param, so this works fine. But it seems a little error-prone.
What if it returns two caps set, the current value and the slice of caps not present in mustHave? Then only the call for ambient/inh will not ignore it? Or maybe we don't want to modify this capSlice func, maybe we want another function and use the slices package, like https://pkg.go.dev/slices#Compare and maybe delete, to get the ignored caps?
I understand that the mustHave we probably only want it for ambient/inh now, so it should be safe now. But if the userns capabilities are merged upstream, or we do some change, this might cause some bug. I'm not sure the risk is big and worth changing this, but I think the alternatives I proposed seems also easier to reason about.
What do you think?
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.
At this point, I think it's best to document the capSlice
function making it clear it modifies some variables, letting future devs know about it.
I just tried doing the above and realized it is already documented ("caps ... are appended to ignoredCaps").
Since this function is scoped inside New
, its usage is quite limited. I understand the argument for being future proof, but it looks like adding more parameters (and, especially, return values) will decrease readability, which plays against us in "future proof" department.
A separate code to check that all ambient capabilities are also present in inheritable will be the best way, I guess. Using slices.Compare is not an option here, because:
- our slices are not sorted;
- inheritable set can contain capabilities that ambient set does not.
So we have to have a for
(or something like slices.DeleteFunc
). The problem here is we either compare a bunch of strings (which is inefficient) or deal with resulting slices of ints, but then we have to convert those capabilities back to string for the warning message (which is OK since the warning is rare, but it adds yet another complexity).
I spent some time trying to come up with something that sucks less than the code I have here, but I fail miserably. Perhaps you can suggest some code @rata?
For now, I just added cleaning the ignoredCaps map after use, hoping that if someone is going to reuse this, they will copy-paste the code 😁. Here's the diff for your review:
diff --git a/libcontainer/capabilities/capabilities.go b/libcontainer/capabilities/capabilities.go
index 1111aac3..8082b4f0 100644
--- a/libcontainer/capabilities/capabilities.go
+++ b/libcontainer/capabilities/capabilities.go
@@ -89,6 +89,7 @@ func New(capConfig *configs.Capabilities) (*Caps, error) {
ambient := capSlice(capConfig.Ambient, inheritable)
if len(ignoredCaps) > 0 {
logrus.Warn("unable to set Ambient capabilities which are not set in Inheritable; ignoring following Ambient capabilities: ", mapKeys(ignoredCaps))
+ clear(ignoredCaps)
}
c.caps = map[capability.CapType][]capability.Cap{
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.
True, it is not sorted so it would be more expensive, and it is not very expensive (n^2 in 15 elem lists is not too much), but it would imply allocations and so on.
Okay, I think the current comment is fine :-)
out = append(out, v) | ||
} | ||
} | ||
return out | ||
} | ||
inheritable := capSlice(capConfig.Inheritable, nil) | ||
// Ambient vector can not contain values not raised in the Inheritable vector, |
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 whole sentence from: https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/cap#section-readme is:
Note, the Ambient vector cannot contain values not raised in the Inh vector, so setting values directly in one vector may have the side effect of mirroring the value in the other vector to maintain this constraint
I guess we want to ignore it, instead of adding it, as we are doing for a long time now. But wanted to raise this, just in case.
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.
Yes, that library raises inheritable bits where the corresponding ambient bits are raised.
I agree that we should not change the behavior (and instead maybe document that if you want to raise ambient caps, you have to raise inheritable ones as well).
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.
SGTM
a4dfbc8
to
97b707f
Compare
Commit 98fe566 removed setting inheritable capabilities from runc exec --cap, but neglected to also remove ambient capabilities. An ambient capability could only be set if the same inheritable capability is set, so as a result of the above change ambient capabilities were not set (but due to a bug in gocapability package, those errors are never reported). Once we start using a library with the fix [1], that bug will become apparent. Alas, we do not have any tests for runc exec --cap, so add one. [1]: kolyshkin/capability#3 Fixes: 98fe566 ("runc: do not set inheritable capabilities") Signed-off-by: Kir Kolyshkin <[email protected]>
Commit 98fe566 removed inheritable capabilities from the example spec (used by runc spec) and from the libcontainer/integration test config, but neglected to also remove ambient capabilities. An ambient capability could only be set if the same inheritable capability is set, so as a result of the above change ambient capabilities were not set (but due to a bug in gocapability package, those errors are never reported). Once we start using a library with the fix [1], that bug will become apparent (both bats-based and libct/int tests will fail). [1]: kolyshkin/capability#3 Fixes: 98fe566 ("runc: do not set inheritable capabilities") Signed-off-by: Kir Kolyshkin <[email protected]>
The example is too long since it lists too many capabilities. Simplify it, leaving only two capabilities. Also, remove ambient capabilities from the set. Inheritable capabilities were removed earlier by commit 98fe566, but ambient capabilities can't be raised without inheritable ones. Fixes: 98fe566 Signed-off-by: Kir Kolyshkin <[email protected]>
Signed-off-by: Kir Kolyshkin <[email protected]>
A map which is created in func init is only used by capSlice, which is only used by New, which is only used by runc init. Switch to lazy init to slightly save on startup time. Signed-off-by: Kir Kolyshkin <[email protected]>
Move capSlice to be an internal function of New. This way, we don't have to pass most parameters. This is a preparation for the next commit. Signed-off-by: Kir Kolyshkin <[email protected]>
Fixing a long standing bug in github.com/syndtr/gocapability package (ignoring errors when setting ambient caps, see [1]) revealed that it's not possible to raise those ambient capabilities for which inheritable capabilities are not raised. In other words, "the Ambient vector cannot contain values not raised in the Inh vector" ([2]). The example spec in libct/specconv had a few ambient capabilities set but no inheritable ones. As a result, when capability package with fix from [1] is used, we get an error trying to start a container ("unable to apply caps: permission denied"). The only decent way to fix this is to ignore raised ambient capabilities for which inheritable capabilities are not raised (essentially mimicking the old behavior). Let's also add a warning about ignored capabilities. Fix the example spec (remove the ambient caps). This is in preparation to switch to github.com/kolyshkin/capability. [1]: kolyshkin/capability#3 [2]: https://pkg.go.dev/kernel.org/pub/linux/libs/security/libcap/cap#IAB.SetVector Signed-off-by: Kir Kolyshkin <[email protected]>
97b707f
to
26aaa82
Compare
This removes the last unversioned package in runc's direct dependencies. Signed-off-by: Kir Kolyshkin <[email protected]>
26aaa82
to
e63cec2
Compare
@kolyshkin I see you marked this as ready, but as you didn't re-request a review, it doesn't notify anyone. Is this ready for review? I see the first 3 commits here are also in #4367, which seems confusing (and splitting the threads of review for those). Do you want us to review the rest of the commits only here, but those are here to avoid conflicts/something? Can you explain a little bit what is your intention here? |
This has started as a simple way to reduce init() overhead in libcontainer/capabilities, but ended up switching to the fork of gocapability package, and also fixing a big issue in handling of ambient capabilities.
Please see individual commits for details; I will open an issue about ambient caps tomorrow.
This currently includes #4367; we can either merge it and rebase this PR, or just close that one in favor of this.