Merge pull request #36644 from jessfraz/rawaccess
api: add MaskedPaths and ReadonlyPaths options
This commit is contained in:
commit
1fe0e49d20
5 changed files with 199 additions and 0 deletions
|
@ -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"
|
||||
|
|
|
@ -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,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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue