Skip to content

Commit

Permalink
Use new mount API (github.com/docker/docker/api/types/mount)
Browse files Browse the repository at this point in the history
Depended by issue 309, as `BindOptions.ReadOnlyNonRecursive` has to be
set for using API v1.44 (Docker v25)

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Mar 9, 2024
1 parent 77d8e43 commit bf1a9b9
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 37 deletions.
26 changes: 25 additions & 1 deletion core/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"github.com/opencontainers/selinux/go-selinux/label"
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
)

Expand Down Expand Up @@ -64,6 +65,7 @@ func (ds *dockerService) CreateContainer(
image = iSpec.Image
}
containerName := makeContainerName(sandboxConfig, config)
mounts := config.GetMounts()
terminationMessagePath, _ := config.Annotations["io.kubernetes.container.terminationMessagePath"]
createConfig := types.ContainerCreateConfig{
Name: containerName,
Expand All @@ -85,13 +87,35 @@ func (ds *dockerService) CreateContainer(
},
},
HostConfig: &container.HostConfig{
Binds: libdocker.GenerateMountBindings(config.GetMounts(), terminationMessagePath),
Mounts: libdocker.GenerateMountBindings(mounts, terminationMessagePath),
RestartPolicy: container.RestartPolicy{
Name: "no",
},
},
}

// Only request relabeling if the pod provides an SELinux context. If the pod
// does not provide an SELinux context relabeling will label the volume with
// the container's randomly allocated MCS label. This would restrict access
// to the volume to the container which mounts it first.
if selinuxOpts := config.GetLinux().GetSecurityContext().GetSelinuxOptions(); selinuxOpts != nil {
mountLabel, err := selinuxMountLabel(selinuxOpts)
if err != nil {
return nil, fmt.Errorf("unable to generate SELinux mount label: %v", err)
}
if mountLabel != "" {
// Equates to "Z" in the old bind API
const shared = false
for _, m := range mounts {
if m.SelinuxRelabel {
if err := label.Relabel(m.HostPath, mountLabel, shared); err != nil {
return nil, fmt.Errorf("unable to relabel %q with %q: %v", m.HostPath, mountLabel, err)
}
}
}
}
}

hc := createConfig.HostConfig
err = ds.updateCreateConfig(
&createConfig,
Expand Down
19 changes: 10 additions & 9 deletions core/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/Mirantis/cri-dockerd/libdocker"

dockertypes "github.com/docker/docker/api/types"
dockermount "github.com/docker/docker/api/types/mount"
dockernat "github.com/docker/go-connections/nat"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -326,15 +327,15 @@ func TestGenerateMountBindings(t *testing.T) {
Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL,
},
}
expectedResult := []string{
"/mnt/1:/var/lib/mysql/1",
"/mnt/2:/var/lib/mysql/2:ro",
"/mnt/3:/var/lib/mysql/3:Z",
"/mnt/4:/var/lib/mysql/4",
"/mnt/5:/var/lib/mysql/5:rslave",
"/mnt/6:/var/lib/mysql/6:rshared",
"/mnt/7:/var/lib/mysql/7",
"/mnt/8:/var/lib/mysql/8:ro,Z,rshared",
expectedResult := []dockermount.Mount{
{Type: dockermount.TypeBind, Source: "/mnt/1", Target: "/var/lib/mysql/1", BindOptions: &dockermount.BindOptions{CreateMountpoint: true}},
{Type: dockermount.TypeBind, Source: "/mnt/2", Target: "/var/lib/mysql/2", ReadOnly: true, BindOptions: &dockermount.BindOptions{CreateMountpoint: true}},
{Type: dockermount.TypeBind, Source: "/mnt/3", Target: "/var/lib/mysql/3", BindOptions: &dockermount.BindOptions{CreateMountpoint: true}}, // Relabeling is not handled here
{Type: dockermount.TypeBind, Source: "/mnt/4", Target: "/var/lib/mysql/4", BindOptions: &dockermount.BindOptions{CreateMountpoint: true}},
{Type: dockermount.TypeBind, Source: "/mnt/5", Target: "/var/lib/mysql/5", BindOptions: &dockermount.BindOptions{CreateMountpoint: true, Propagation: dockermount.PropagationRSlave}},
{Type: dockermount.TypeBind, Source: "/mnt/6", Target: "/var/lib/mysql/6", BindOptions: &dockermount.BindOptions{CreateMountpoint: true, Propagation: dockermount.PropagationRShared}},
{Type: dockermount.TypeBind, Source: "/mnt/7", Target: "/var/lib/mysql/7", BindOptions: &dockermount.BindOptions{CreateMountpoint: true}},
{Type: dockermount.TypeBind, Source: "/mnt/8", Target: "/var/lib/mysql/8", ReadOnly: true, BindOptions: &dockermount.BindOptions{CreateMountpoint: true, Propagation: dockermount.PropagationRShared}}, // Relabeling is not handled here
}
result := libdocker.GenerateMountBindings(mounts, "")

Expand Down
24 changes: 24 additions & 0 deletions core/selinux_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package core
import (
"fmt"

"github.com/opencontainers/selinux/go-selinux/label"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
)

Expand Down Expand Up @@ -88,3 +89,26 @@ func modifySecurityOption(config []string, name, value string) []string {

return config
}

func selinuxMountLabel(selinuxOpts *runtimeapi.SELinuxOption) (string, error) {
var opts []string
if selinuxOpts != nil {
if selinuxOpts.User != "" {
opts = append(opts, "user:"+selinuxOpts.User)
}
if selinuxOpts.Role != "" {
opts = append(opts, "role:"+selinuxOpts.Role)
}
if selinuxOpts.Type != "" {
opts = append(opts, "type:"+selinuxOpts.Type)
}
if selinuxOpts.Level != "" {
opts = append(opts, "level:"+selinuxOpts.Level)
}
}
if len(opts) == 0 {
opts = []string{"disable"}
}
_, s, err := label.InitLabels(opts)
return s, err
}
15 changes: 15 additions & 0 deletions core/selinux_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package core
import (
"reflect"
"testing"

"github.com/opencontainers/selinux/go-selinux"
"github.com/stretchr/testify/assert"
)

func TestModifySecurityOptions(t *testing.T) {
Expand Down Expand Up @@ -57,3 +60,15 @@ func TestModifySecurityOptions(t *testing.T) {
}
}
}

func TestSELinuxMountLabel(t *testing.T) {
selinuxOpts := inputSELinuxOptions()
s, err := selinuxMountLabel(selinuxOpts)
assert.NoError(t, err)
t.Logf("mount label for %+v=%q", selinuxOpts, s)
if selinux.GetEnabled() {
assert.NotEmpty(t, s)
} else {
assert.Empty(t, s)
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/runc v1.1.12
github.com/opencontainers/selinux v1.10.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
Expand Down Expand Up @@ -95,7 +96,6 @@ require (
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78 // indirect
github.com/opencontainers/selinux v1.10.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
Expand Down
45 changes: 19 additions & 26 deletions libdocker/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ package libdocker

import (
"fmt"
"github.com/docker/go-connections/nat"
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubernetes/pkg/apis/core"
"runtime"
"strconv"
"strings"
"time"

dockerref "github.com/docker/distribution/reference"
dockertypes "github.com/docker/docker/api/types"
dockermount "github.com/docker/docker/api/types/mount"
"github.com/docker/go-connections/nat"
godigest "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubernetes/pkg/apis/core"
)

const windowsEtcHostsPath = "C:\\Windows\\System32\\drivers\\etc\\hosts"
Expand Down Expand Up @@ -202,34 +203,29 @@ func GenerateEnvList(envs []*v1.KeyValue) (result []string) {
return
}

// generateMountBindings converts the mount list to a list of strings that
// generateMountBindings converts the mount list to a list of [dockermount.Mount] that
// can be understood by docker.
// '<HostPath>:<ContainerPath>[:options]', where 'options'
// is a comma-separated list of the following strings:
// 'ro', if the path is read only
// 'Z', if the volume requires SELinux relabeling
// propagation mode such as 'rslave'
func GenerateMountBindings(mounts []*v1.Mount, terminationMessagePath string) []string {
// SELinux labels are not handled here.
func GenerateMountBindings(mounts []*v1.Mount, terminationMessagePath string) []dockermount.Mount {
if terminationMessagePath == "" {
terminationMessagePath = core.TerminationMessagePathDefault
}
result := make([]string, 0, len(mounts))
result := make([]dockermount.Mount, 0, len(mounts))
for _, m := range mounts {
if runtime.GOOS == "windows" && isSingleFileMount(m.HostPath, m.ContainerPath, terminationMessagePath) {
logrus.Debugf("skipping mount :%s:%s", m.HostPath, m.ContainerPath)
continue
}
bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath)
var attrs []string
if m.Readonly {
attrs = append(attrs, "ro")
bind := dockermount.Mount{
Type: dockermount.TypeBind,
Source: m.HostPath,
Target: m.ContainerPath,
BindOptions: &dockermount.BindOptions{
CreateMountpoint: true,
},
}
// Only request relabeling if the pod provides an SELinux context. If the pod
// does not provide an SELinux context relabeling will label the volume with
// the container's randomly allocated MCS label. This would restrict access
// to the volume to the container which mounts it first.
if m.SelinuxRelabel {
attrs = append(attrs, "Z")
if m.Readonly {
bind.ReadOnly = true
}
switch m.Propagation {
case v1.MountPropagation_PROPAGATION_PRIVATE:
Expand All @@ -244,17 +240,14 @@ func GenerateMountBindings(mounts []*v1.Mount, terminationMessagePath string) []
//
// This behavior was introduced in Docker 18.03: https://github.com/moby/moby/pull/36055
case v1.MountPropagation_PROPAGATION_BIDIRECTIONAL:
attrs = append(attrs, "rshared")
bind.BindOptions.Propagation = dockermount.PropagationRShared
case v1.MountPropagation_PROPAGATION_HOST_TO_CONTAINER:
attrs = append(attrs, "rslave")
bind.BindOptions.Propagation = dockermount.PropagationRSlave
default:
logrus.Infof("Unknown propagation mode for hostPath %s", m.HostPath)
// let dockerd decide the propagation
}

if len(attrs) > 0 {
bind = fmt.Sprintf("%s:%s", bind, strings.Join(attrs, ","))
}
result = append(result, bind)
}
return result
Expand Down
97 changes: 97 additions & 0 deletions vendor/github.com/opencontainers/selinux/go-selinux/label/label.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bf1a9b9

Please sign in to comment.