Skip to content
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

drivers/exec+java: reduce default set of linux capabilities #10600

Merged
merged 8 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ FEATURES:

__BACKWARDS INCOMPATIBILITIES:__
* csi: The `attachment_mode` and `access_mode` field are required for `volume` blocks in job specifications. Registering a volume requires at least one `capability` block with the `attachment_mode` and `access_mode` fields set. [[GH-10330](https://github.com/hashicorp/nomad/issues/10330)]
* drivers/exec+java: Reduce set of linux capabilities enabled by default [[GH-10600](https://github.com/hashicorp/nomad/pull/10600)]
* licensing: Enterprise licenses are no longer stored in raft or synced between servers. Loading the Enterprise license from disk or environment is required. The `nomad license put` command has been removed. [[GH-10458](https://github.com/hashicorp/nomad/issues/10458)]

SECURITY:
Expand Down
43 changes: 8 additions & 35 deletions drivers/docker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

docker "github.com/fsouza/go-dockerclient"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/drivers/shared/capabilities"
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
"github.com/hashicorp/nomad/helper/pluginutils/loader"
"github.com/hashicorp/nomad/plugins/base"
Expand Down Expand Up @@ -41,36 +42,6 @@ const (
dockerAuthHelperPrefix = "docker-credential-"
)

// nomadDefaultCaps is the subset of dockerDefaultCaps that Nomad enables by
// default and is used to compute the set of capabilities to add/drop given
// docker driver configuration.
func nomadDefaultCaps() []string {
return []string{
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_BIND_SERVICE",
"SETFCAP",
"SETGID",
"SETPCAP",
"SETUID",
"SYS_CHROOT",
}
}

// dockerDefaultCaps is a list of Linux capabilities enabled by docker by default
// and is used to compute the set of capabilities to add/drop given docker driver
// configuration, as well as Nomad built-in limitations.
//
// https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
func dockerDefaultCaps() []string {
return append(nomadDefaultCaps(), "NET_RAW")
}

func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
conf := map[string]interface{}{}
if v, ok := opts["docker.endpoint"]; ok {
Expand Down Expand Up @@ -287,7 +258,7 @@ var (
"allow_privileged": hclspec.NewAttr("allow_privileged", "bool", false),
"allow_caps": hclspec.NewDefault(
hclspec.NewAttr("allow_caps", "list(string)", false),
hclspec.NewLiteral(`["CHOWN","DAC_OVERRIDE","FSETID","FOWNER","MKNOD","SETGID","SETUID","SETFCAP","SETPCAP","NET_BIND_SERVICE","SYS_CHROOT","KILL","AUDIT_WRITE"]`),
hclspec.NewLiteral(capabilities.HCLSpecLiteral),
),
"nvidia_runtime": hclspec.NewDefault(
hclspec.NewAttr("nvidia_runtime", "string", false),
Expand Down Expand Up @@ -427,9 +398,9 @@ var (
"work_dir": hclspec.NewAttr("work_dir", "string", false),
})

// capabilities is returned by the Capabilities RPC and indicates what
// optional features this driver supports
capabilities = &drivers.Capabilities{
// driverCapabilities represents the RPC response for what features are
// implemented by the docker task driver
driverCapabilities = &drivers.Capabilities{
SendSignals: true,
Exec: true,
FSIsolation: drivers.FSIsolationImage,
Expand Down Expand Up @@ -788,8 +759,10 @@ func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) {
return taskConfigSpec, nil
}

// Capabilities is returned by the Capabilities RPC and indicates what optional
// features this driver supports.
func (d *Driver) Capabilities() (*drivers.Capabilities, error) {
return capabilities, nil
return driverCapabilities, nil
}

var _ drivers.InternalCapabilitiesDriver = (*Driver)(nil)
Expand Down
122 changes: 4 additions & 118 deletions drivers/docker/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"sync"
Expand All @@ -23,10 +22,9 @@ import (
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/nomad/client/taskenv"
"github.com/hashicorp/nomad/drivers/docker/docklog"
"github.com/hashicorp/nomad/drivers/shared/capabilities"
"github.com/hashicorp/nomad/drivers/shared/eventer"
"github.com/hashicorp/nomad/drivers/shared/executor"
"github.com/hashicorp/nomad/drivers/shared/resolvconf"
"github.com/hashicorp/nomad/helper"
nstructs "github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/base"
"github.com/hashicorp/nomad/plugins/drivers"
Expand Down Expand Up @@ -913,8 +911,9 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T
hostConfig.Privileged = driverConfig.Privileged

// set add/drop capabilities
hostConfig.CapAdd, hostConfig.CapDrop, err = d.getCaps(driverConfig)
if err != nil {
if hostConfig.CapAdd, hostConfig.CapDrop, err = capabilities.Delta(
capabilities.DockerDefaults(), d.config.AllowCaps, driverConfig.CapAdd, driverConfig.CapDrop,
); err != nil {
return c, err
}

Expand Down Expand Up @@ -1184,119 +1183,6 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T
}, nil
}

// getCaps computes the capabilities to supply to the --add-cap and --drop-cap
// options to the docker driver, which override the default capabilities enabled
// by docker itself.
func (d *Driver) getCaps(taskConfig *TaskConfig) ([]string, []string, error) {

// capabilities allowable by client docker plugin configuration
allowCaps := expandAllowCaps(d.config.AllowCaps)

// capabilities the task docker config is asking for based on the default
// capabilities allowable by nomad
desiredCaps, err := tweakCapabilities(nomadDefaultCaps(), taskConfig.CapAdd, taskConfig.CapDrop)
if err != nil {
return nil, nil, err
}

// capabilities the task is requesting that are NOT allowed by the docker plugin
if missing := missingCaps(allowCaps, desiredCaps); len(missing) > 0 {
return nil, nil, fmt.Errorf("Docker driver does not have the following caps allow-listed on this Nomad agent: %s", missing)
}

// capabilities that should be dropped relative to the docker default capabilities
dropCaps := capDrops(taskConfig.CapDrop, allowCaps)

return taskConfig.CapAdd, dropCaps, nil
}

// capDrops will compute the total dropped capabilities set
//
// {task cap_drop} U ({docker defaults} \ {driver allow caps})
func capDrops(dropCaps []string, allowCaps []string) []string {
dropSet := make(map[string]struct{})

for _, c := range normalizeCaps(dropCaps) {
dropSet[c] = struct{}{}
}

// if dropCaps includes ALL, no need to iterate every capability
if _, exists := dropSet["ALL"]; exists {
return []string{"ALL"}
}

dockerDefaults := helper.SliceStringToSet(normalizeCaps(dockerDefaultCaps()))
allowedCaps := helper.SliceStringToSet(normalizeCaps(allowCaps))

// find the docker default caps not in allowed caps
for dCap := range dockerDefaults {
if _, exists := allowedCaps[dCap]; !exists {
dropSet[dCap] = struct{}{}
}
}

drops := make([]string, 0, len(dropSet))
for c := range dropSet {
drops = append(drops, c)
}
sort.Strings(drops)
return drops
}

// expandAllowCaps returns the normalized set of allowable capabilities set
// for the docker plugin configuration.
func expandAllowCaps(allowCaps []string) []string {
if len(allowCaps) == 0 {
return nil
}

set := make(map[string]struct{}, len(allowCaps))

for _, rawCap := range allowCaps {
capability := strings.ToUpper(rawCap)
if capability == "ALL" {
for _, defCap := range normalizeCaps(executor.SupportedCaps(true)) {
set[defCap] = struct{}{}
}
} else {
set[capability] = struct{}{}
}
}

result := make([]string, 0, len(set))
for capability := range set {
result = append(result, capability)
}
sort.Strings(result)
return result
}

// missingCaps returns the set of elements in desired that are not present in
// allowed. The elements in desired are first upper-cased before comparison.
// The elements in allowed are assumed to be upper-cased.
func missingCaps(allowed, desired []string) []string {
_, missing := helper.SliceStringIsSubset(allowed, normalizeCaps(desired))
sort.Strings(missing)
return missing
}

// normalizeCaps returns a copy of caps with duplicate elements removed and all
// elements upper-cased.
func normalizeCaps(caps []string) []string {
set := make(map[string]struct{}, len(caps))
for _, c := range caps {
normal := strings.TrimPrefix(strings.ToUpper(c), "CAP_")
set[strings.ToUpper(normal)] = struct{}{}
}

result := make([]string, 0, len(set))
for c := range set {
result = append(result, c)
}
sort.Strings(result)
return result
}

func (d *Driver) toDockerMount(m *DockerMount, task *drivers.TaskConfig) (*docker.HostMount, error) {
hm, err := m.toDockerHostMount()
if err != nil {
Expand Down
21 changes: 0 additions & 21 deletions drivers/docker/driver_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,9 @@
package docker

import (
"github.com/docker/docker/oci/caps"
docker "github.com/fsouza/go-dockerclient"
)

func getPortBinding(ip string, port string) docker.PortBinding {
return docker.PortBinding{HostIP: ip, HostPort: port}
}

func tweakCapabilities(basics, adds, drops []string) ([]string, error) {
// Moby mixes 2 different capabilities formats: prefixed with "CAP_"
// and not. We do the conversion here to have a consistent,
// non-prefixed format on the Nomad side.
for i, cap := range basics {
basics[i] = "CAP_" + cap
}

effectiveCaps, err := caps.TweakCapabilities(basics, adds, drops, nil, false)
if err != nil {
return effectiveCaps, err
}

for i, cap := range effectiveCaps {
effectiveCaps[i] = cap[len("CAP_"):]
}

return effectiveCaps, nil
}
Loading