Skip to content

Conversation

@thaJeztah
Copy link
Member

@thaJeztah thaJeztah commented Sep 26, 2024

Before this change, capabilities would be sent un-normalized, un-sorted, and could contain duplicates;

docker create --name foo --cap-add SYS_ADMIN --cap-add sys_admin --cap-add cap_sys_admin --cap-add ALL busybox
docker container inspect --format '{{json .HostConfig.CapAdd }}' foo
["SYS_ADMIN","sys_admin","cap_sys_admin","ALL"]

After this change, capabilities are sent in their normalized form, sorted, and with duplicates removed;

docker create --name foo --cap-add SYS_ADMIN --cap-add sys_admin --cap-add cap_sys_admin --cap-add ALL busybox
docker container inspect --format '{{json .HostConfig.CapAdd }}' foo
["ALL", "CAP_SYS_ADMIN"]

- What I did

- How I did it

- How to verify it

- Description for the changelog

`client.ContainerCreate` now normalizes `CapAdd` and `CapDrop` fields in `HostConfig` to their canonical form.

- A picture of a cute animal (not mandatory but encouraged)

@thaJeztah thaJeztah added area/api API area/cli Client status/2-code-review kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. impact/changelog area/go-sdk labels Sep 26, 2024
@thaJeztah thaJeztah added this to the 28.0.0 milestone Sep 26, 2024
@thaJeztah thaJeztah marked this pull request as draft September 26, 2024 12:59
Before this change, capabilities would be sent un-normalized, un-sorted,
and could contain duplicates;

    docker create --name foo --cap-add SYS_ADMIN --cap-add sys_admin --cap-add cap_sys_admin --cap-add ALL busybox
    docker container inspect --format '{{json .HostConfig.CapAdd }}' foo
    ["SYS_ADMIN","sys_admin","cap_sys_admin","ALL"]

After this change, capabilities are sent in their normalized form, sorted,
and with duplicates removed;

    docker create --name foo --cap-add SYS_ADMIN --cap-add sys_admin --cap-add cap_sys_admin --cap-add ALL busybox
    docker container inspect --format '{{json .HostConfig.CapAdd }}' foo
    ["ALL", "CAP_SYS_ADMIN"]

Signed-off-by: Sebastiaan van Stijn <[email protected]>
@thaJeztah thaJeztah marked this pull request as ready for review September 26, 2024 15:11
Comment on lines +123 to +125
// It is similar to [github.com/docker/docker/oci/caps.NormalizeLegacyCapabilities],
// but performs no validation based on supported capabilities.
func normalizeCapabilities(caps []string) []string {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I want to have a look if we can somehow unify these implementations, and put this code somewhere where it can be used. I didn't yet decide on the best place for this; also considering to put similar normalisation logic in the API endpoint (before we send it all to the daemon).

Pending that, I kept it as a non-exported function here.


unique := make(map[string]struct{})
for _, c := range caps {
c = normalizeCap(c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to move up the check for ALL capabilities from normalizeCap and just early return here?

Or is it a case of even with ALL, the rest should still be returned alongside it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, technically it would probably be fine to only keep ALL, but there's logic in the daemon to handle the ALL special case, and to hydrate the list of capabilities; see TweakCapabilities

func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) {

I tried to avoid (at least for now) putting too much logic on the client side (other than normalising the format) and to preserve information where possible; also having situations in the back of my mind where a user could pass a capability in the list that's not (yet) known by the engine, and I'd want it to produce an error in that case instead of silently taking ALL.

Copy link
Contributor

@austinvazquez austinvazquez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Can always rehome later 👏🏼

Copy link
Member

@laurazard laurazard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice! Appreciate the tests too :)

Wondering if we can put the whole implementation under something like container/capabilities.go or something of the sort.

@thaJeztah
Copy link
Member Author

Wondering if we can put the whole implementation under something like container/capabilities.go or something of the sort.

Yes, something along those lines, or even a separate package there. I need to look how we can slice-and-dice the code (as there's some existing code that's exported), and make sure that refactoring doesn't pull in other (unwanted) dependencies. #48551 (comment)

@thaJeztah
Copy link
Member Author

I'll bring this one in; thanks for review everyone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api API area/cli Client area/go-sdk impact/changelog kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants