Merge pull request #36644 from jessfraz/rawaccess

api: add MaskedPaths and ReadonlyPaths options
This commit is contained in:
Sebastiaan van Stijn 2018-06-06 18:16:16 -07:00 committed by GitHub
commit 1fe0e49d20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 0 deletions

View file

@ -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"

View file

@ -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"`
}

View file

@ -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"
@ -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)

View file

@ -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
}

View file

@ -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"
)
@ -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)
}
}