diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 8adcdfa8f2..b3253fe306 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -1409,9 +1409,17 @@ func updateCapabilities(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec ) if flags.Changed(flagCapAdd) { toAdd = opts.CapabilitiesMap(flags.Lookup(flagCapAdd).Value.(*opts.ListOpts).GetAll()) + if toAdd[opts.ResetCapabilities] { + capAdd = make(map[string]bool) + delete(toAdd, opts.ResetCapabilities) + } } if flags.Changed(flagCapDrop) { toDrop = opts.CapabilitiesMap(flags.Lookup(flagCapDrop).Value.(*opts.ListOpts).GetAll()) + if toDrop[opts.ResetCapabilities] { + capDrop = make(map[string]bool) + delete(toDrop, opts.ResetCapabilities) + } } // First remove the capabilities to "drop" from the service's exiting diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index cbae2ddd11..31021ebe7f 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -1522,6 +1522,50 @@ func TestUpdateCaps(t *testing.T) { expectedAdd: []string{"CAP_AAA", "CAP_BBB", "CAP_CCC", "CAP_DDD"}, expectedDrop: []string{"CAP_WWW", "CAP_XXX", "CAP_YYY", "CAP_ZZZ"}, }, + { + name: "Reset capabilities", + flagAdd: []string{"RESET"}, + flagDrop: []string{"RESET"}, + spec: &swarm.ContainerSpec{ + CapabilityAdd: []string{"CAP_AAA", "CAP_BBB", "CAP_CCC", "CAP_DDD"}, + CapabilityDrop: []string{"CAP_WWW", "CAP_XXX", "CAP_YYY", "CAP_ZZZ"}, + }, + expectedAdd: nil, + expectedDrop: nil, + }, + { + name: "Reset capabilities, and update after", + flagAdd: []string{"RESET", "CAP_ADD_ONE", "CAP_FOO"}, + flagDrop: []string{"RESET", "CAP_DROP_ONE", "CAP_FOO"}, + spec: &swarm.ContainerSpec{ + CapabilityAdd: []string{"CAP_AAA", "CAP_BBB", "CAP_CCC", "CAP_DDD"}, + CapabilityDrop: []string{"CAP_WWW", "CAP_XXX", "CAP_YYY", "CAP_ZZZ"}, + }, + expectedAdd: []string{"CAP_ADD_ONE", "CAP_FOO"}, + expectedDrop: []string{"CAP_DROP_ONE"}, + }, + { + name: "Reset capabilities, and add ALL", + flagAdd: []string{"RESET", "ALL"}, + flagDrop: []string{"RESET", "ALL"}, + spec: &swarm.ContainerSpec{ + CapabilityAdd: []string{"CAP_AAA", "CAP_BBB", "CAP_CCC", "CAP_DDD"}, + CapabilityDrop: []string{"CAP_WWW", "CAP_XXX", "CAP_YYY", "CAP_ZZZ"}, + }, + expectedAdd: []string{"ALL"}, + expectedDrop: nil, + }, + { + name: "Add ALL and RESET", + flagAdd: []string{"ALL", "RESET"}, + flagDrop: []string{"ALL", "RESET"}, + spec: &swarm.ContainerSpec{ + CapabilityAdd: []string{"CAP_AAA", "CAP_BBB", "CAP_CCC", "CAP_DDD"}, + CapabilityDrop: []string{"CAP_WWW", "CAP_XXX", "CAP_YYY", "CAP_ZZZ"}, + }, + expectedAdd: []string{"ALL"}, + expectedDrop: nil, + }, } for _, tc := range tests { diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f55c302f49..8d5f37a486 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -857,6 +857,7 @@ __docker_complete_capabilities_addable() { CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM + RESET ) COMPREPLY=( $( compgen -W "${capabilities[*]} ${capabilities[*]#CAP_}" -- "$cur" ) ) } @@ -881,6 +882,7 @@ __docker_complete_capabilities_droppable() { CAP_SETPCAP CAP_SETUID CAP_SYS_CHROOT + RESET ) COMPREPLY=( $( compgen -W "${capabilities[*]} ${capabilities[*]#CAP_}" -- "$cur" ) ) } diff --git a/opts/capabilities.go b/opts/capabilities.go index 1a2e19a89d..8b57870376 100644 --- a/opts/capabilities.go +++ b/opts/capabilities.go @@ -8,6 +8,10 @@ import ( const ( // AllCapabilities is a special value to add or drop all capabilities AllCapabilities = "ALL" + + // ResetCapabilities is a special value to reset capabilities when updating. + // This value should only be used when updating, not used on "create". + ResetCapabilities = "RESET" ) // NormalizeCapability normalizes a capability by upper-casing, trimming white space @@ -19,7 +23,7 @@ const ( // handled by the daemon. func NormalizeCapability(cap string) string { cap = strings.ToUpper(strings.TrimSpace(cap)) - if cap == AllCapabilities { + if cap == AllCapabilities || cap == ResetCapabilities { return cap } if !strings.HasPrefix(cap, "CAP_") { @@ -44,6 +48,9 @@ func CapabilitiesMap(caps []string) map[string]bool { // lists are removed from the list of capabilities to drop. The special "ALL" // capability is also taken into account. // +// Note that the special "RESET" value is only used when updating an existing +// service, and will be ignored. +// // Duplicates are removed, and the resulting lists are sorted. func EffectiveCapAddCapDrop(add, drop []string) (capAdd, capDrop []string) { var ( @@ -64,11 +71,15 @@ func EffectiveCapAddCapDrop(add, drop []string) (capAdd, capDrop []string) { // Adding a capability takes precedence, so skip dropping continue } - capDrop = append(capDrop, c) + if c != ResetCapabilities { + capDrop = append(capDrop, c) + } } for c := range addCaps { - capAdd = append(capAdd, c) + if c != ResetCapabilities { + capAdd = append(capAdd, c) + } } sort.Strings(capAdd)