Skip to content
This repository has been archived by the owner on Oct 13, 2023. It is now read-only.

Commit

Permalink
api: add configurable MaskedPaths and ReadOnlyPaths to the API
Browse files Browse the repository at this point in the history
This adds MaskedPaths and ReadOnlyPaths options to HostConfig for containers so
that a user can override the default values.

When the value sent through the API is nil the default is used.
Otherwise the default is overridden.

Adds integration tests for MaskedPaths and ReadonlyPaths.

Signed-off-by: Jess Frazelle <[email protected]>
Upstream-commit: 3694c1e34e40fa2e255a97b5541645cec9c8d1d5
Component: engine
  • Loading branch information
jessfraz committed Jun 5, 2018
1 parent 2452577 commit 67fe100
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 0 deletions.
10 changes: 10 additions & 0 deletions components/engine/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,16 @@ definitions:
- "default"
- "process"
- "hyperv"
MaskedPaths:
type: "array"
description: "The list of paths to be masked inside the container (this overrides the default set of paths)"
items:
type: "string"
ReadonlyPaths:
type: "array"
description: "The list of paths to be set as read-only inside the container (this overrides the default set of paths)"
items:
type: "string"

ContainerConfig:
description: "Configuration for a container that is portable between hosts"
Expand Down
6 changes: 6 additions & 0 deletions components/engine/api/types/container/host_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ type HostConfig struct {
// Mounts specs used by the container
Mounts []mount.Mount `json:",omitempty"`

// MaskedPaths is the list of paths to be masked inside the container (this overrides the default set of paths)
MaskedPaths []string

// ReadonlyPaths is the list of paths to be set as read-only inside the container (this overrides the default set of paths)
ReadonlyPaths []string

// Run a custom init inside the container, if null, use the daemon's configured settings
Init *bool `json:",omitempty"`
}
11 changes: 11 additions & 0 deletions components/engine/daemon/create_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
containertypes "github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/container"
"github.com/docker/docker/oci"
"github.com/docker/docker/pkg/stringid"
volumeopts "github.com/docker/docker/volume/service/opts"
"github.com/opencontainers/selinux/go-selinux/label"
Expand All @@ -29,6 +30,16 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
return err
}

// Set the default masked and readonly paths with regard to the host config options if they are not set.
if hostConfig.MaskedPaths == nil && !hostConfig.Privileged {
hostConfig.MaskedPaths = oci.DefaultSpec().Linux.MaskedPaths // Set it to the default if nil
container.HostConfig.MaskedPaths = hostConfig.MaskedPaths
}
if hostConfig.ReadonlyPaths == nil && !hostConfig.Privileged {
hostConfig.ReadonlyPaths = oci.DefaultSpec().Linux.ReadonlyPaths // Set it to the default if nil
container.HostConfig.ReadonlyPaths = hostConfig.ReadonlyPaths
}

for spec := range config.Volumes {
name := stringid.GenerateNonCryptoID()
destination := filepath.Clean(spec)
Expand Down
8 changes: 8 additions & 0 deletions components/engine/daemon/oci_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,14 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj
s.Linux.MountLabel = c.MountLabel

// Set the masked and readonly paths with regard to the host config options if they are set.
if c.HostConfig.MaskedPaths != nil {
s.Linux.MaskedPaths = c.HostConfig.MaskedPaths
}
if c.HostConfig.ReadonlyPaths != nil {
s.Linux.ReadonlyPaths = c.HostConfig.ReadonlyPaths
}

return &s, nil
}

Expand Down
164 changes: 164 additions & 0 deletions components/engine/integration/container/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ package container // import "github.com/docker/docker/integration/container"

import (
"context"
"encoding/json"
"fmt"
"strconv"
"testing"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
ctr "github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/internal/test/request"
"github.com/docker/docker/oci"
"github.com/gotestyourself/gotestyourself/assert"
is "github.com/gotestyourself/gotestyourself/assert/cmp"
"github.com/gotestyourself/gotestyourself/poll"
"github.com/gotestyourself/gotestyourself/skip"
)

Expand Down Expand Up @@ -137,3 +144,160 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
assert.Check(t, is.ErrorContains(err, tc.expectedError))
}
}
func TestCreateWithCustomMaskedPaths(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "linux")

defer setupTest(t)()
client := request.NewAPIClient(t)
ctx := context.Background()

testCases := []struct {
maskedPaths []string
expected []string
}{
{
maskedPaths: []string{},
expected: []string{},
},
{
maskedPaths: nil,
expected: oci.DefaultSpec().Linux.MaskedPaths,
},
{
maskedPaths: []string{"/proc/kcore", "/proc/keys"},
expected: []string{"/proc/kcore", "/proc/keys"},
},
}

checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
_, b, err := client.ContainerInspectWithRaw(ctx, name, false)
assert.NilError(t, err)

var inspectJSON map[string]interface{}
err = json.Unmarshal(b, &inspectJSON)
assert.NilError(t, err)

cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
assert.Check(t, is.Equal(true, ok), name)

maskedPaths, ok := cfg["MaskedPaths"].([]interface{})
assert.Check(t, is.Equal(true, ok), name)

mps := []string{}
for _, mp := range maskedPaths {
mps = append(mps, mp.(string))
}

assert.DeepEqual(t, expected, mps)
}

for i, tc := range testCases {
name := fmt.Sprintf("create-masked-paths-%d", i)
config := container.Config{
Image: "busybox",
Cmd: []string{"true"},
}
hc := container.HostConfig{}
if tc.maskedPaths != nil {
hc.MaskedPaths = tc.maskedPaths
}

// Create the container.
c, err := client.ContainerCreate(context.Background(),
&config,
&hc,
&network.NetworkingConfig{},
name,
)
assert.NilError(t, err)

checkInspect(t, ctx, name, tc.expected)

// Start the container.
err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
assert.NilError(t, err)

poll.WaitOn(t, ctr.IsInState(ctx, client, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))

checkInspect(t, ctx, name, tc.expected)
}
}

func TestCreateWithCustomReadonlyPaths(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "linux")

defer setupTest(t)()
client := request.NewAPIClient(t)
ctx := context.Background()

testCases := []struct {
doc string
readonlyPaths []string
expected []string
}{
{
readonlyPaths: []string{},
expected: []string{},
},
{
readonlyPaths: nil,
expected: oci.DefaultSpec().Linux.ReadonlyPaths,
},
{
readonlyPaths: []string{"/proc/asound", "/proc/bus"},
expected: []string{"/proc/asound", "/proc/bus"},
},
}

checkInspect := func(t *testing.T, ctx context.Context, name string, expected []string) {
_, b, err := client.ContainerInspectWithRaw(ctx, name, false)
assert.NilError(t, err)

var inspectJSON map[string]interface{}
err = json.Unmarshal(b, &inspectJSON)
assert.NilError(t, err)

cfg, ok := inspectJSON["HostConfig"].(map[string]interface{})
assert.Check(t, is.Equal(true, ok), name)

readonlyPaths, ok := cfg["ReadonlyPaths"].([]interface{})
assert.Check(t, is.Equal(true, ok), name)

rops := []string{}
for _, rop := range readonlyPaths {
rops = append(rops, rop.(string))
}
assert.DeepEqual(t, expected, rops)
}

for i, tc := range testCases {
name := fmt.Sprintf("create-readonly-paths-%d", i)
config := container.Config{
Image: "busybox",
Cmd: []string{"true"},
}
hc := container.HostConfig{}
if tc.readonlyPaths != nil {
hc.ReadonlyPaths = tc.readonlyPaths
}

// Create the container.
c, err := client.ContainerCreate(context.Background(),
&config,
&hc,
&network.NetworkingConfig{},
name,
)
assert.NilError(t, err)

checkInspect(t, ctx, name, tc.expected)

// Start the container.
err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
assert.NilError(t, err)

poll.WaitOn(t, ctr.IsInState(ctx, client, c.ID, "exited"), poll.WithDelay(100*time.Millisecond))

checkInspect(t, ctx, name, tc.expected)
}
}

0 comments on commit 67fe100

Please sign in to comment.