Skip to content
This repository has been archived by the owner on Feb 8, 2021. It is now read-only.

readonly rootfs support #638

Merged
merged 5 commits into from
Jun 12, 2017
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
6 changes: 4 additions & 2 deletions client/common_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type CommonFlags struct {
Name string `long:"name" value-name:"\"\"" description:"Assign a name to the container"`
Workdir string `long:"workdir" value-name:"\"\"" default-mask:"-" description:"Working directory inside the container"`
Tty bool `short:"t" long:"tty" default-mask:"-" description:"the run command in tty, such as bash shell"`
ReadOnly bool `long:"read-only" default-mast:"-" description:"Create container with read-only rootfs"`
Cpu int `long:"cpu" default:"1" value-name:"1" default-mask:"-" description:"CPU number for the VM"`
Memory int `long:"memory" default:"128" value-name:"128" default-mask:"-" description:"Memory size (MB) for the VM"`
Env []string `long:"env" value-name:"[]" default-mask:"-" description:"Set environment variables"`
Expand Down Expand Up @@ -59,7 +60,7 @@ func (cli *HyperClient) ParseCommonOptions(opts *CommonFlags, container bool, ar
return nil, fmt.Errorf("%s: this command requires a minimum of 1 argument, please provide the image.", os.Args[0])
}
specJson, err = cli.JsonFromCmdline(container, args, opts.Env, opts.Portmap, opts.LogDriver, opts.LogOpts,
opts.Name, opts.Workdir, opts.RestartPolicy, opts.Cpu, opts.Memory, opts.Tty, opts.Labels, opts.EntryPoint, opts.Volumes)
opts.Name, opts.Workdir, opts.RestartPolicy, opts.Cpu, opts.Memory, opts.Tty, opts.ReadOnly, opts.Labels, opts.EntryPoint, opts.Volumes)
}

if err != nil {
Expand Down Expand Up @@ -90,7 +91,7 @@ func (cli *HyperClient) JsonFromFile(filename string, container, yaml, k8s bool)
}

func (cli *HyperClient) JsonFromCmdline(container bool, cmdArgs, cmdEnvs, cmdPortmaps []string, cmdLogDriver string, cmdLogOpts []string,
cmdName, cmdWorkdir, cmdRestartPolicy string, cpu, memory int, tty bool, cmdLabels []string, entrypoint string, cmdVols []string) (string, error) {
cmdName, cmdWorkdir, cmdRestartPolicy string, cpu, memory int, tty, readonly bool, cmdLabels []string, entrypoint string, cmdVols []string) (string, error) {

var (
name = cmdName
Expand Down Expand Up @@ -175,6 +176,7 @@ func (cli *HyperClient) JsonFromCmdline(container bool, cmdArgs, cmdEnvs, cmdPor
Files: []*apitype.UserFileReference{},
RestartPolicy: cmdRestartPolicy,
Tty: tty,
ReadOnly: readonly,
}

var body interface{} = c
Expand Down
11 changes: 6 additions & 5 deletions daemon/daemonbuilder/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (d Docker) ContainerCreate(params types.ContainerCreateConfig) (types.Conta
podId := fmt.Sprintf("buildpod-%s", utils.RandStr(10, "alpha"))
// Hack here, container created by ADD/COPY only has Config
if params.HostConfig != nil {
spec, err = MakeBasicPod(podId, params.Config)
spec, err = MakeBasicPod(podId, params.Config, params.HostConfig)
} else {
spec, err = MakeCopyPod(podId, params.Config)
}
Expand Down Expand Up @@ -273,14 +273,14 @@ func MakeCopyPod(podId string, config *containertypes.Config) (*apitypes.UserPod
fmt.Fprintf(copyshell, "#!/bin/sh\n")
copyshell.Close()

return MakePod(podId, tempSrcDir, shellDir, config, []string{"/bin/sh", "/tmp/shell/exec-copy.sh"}, []string{})
return MakePod(podId, tempSrcDir, shellDir, config, []string{"/bin/sh", "/tmp/shell/exec-copy.sh"}, []string{}, false)
}

func MakeBasicPod(podId string, config *containertypes.Config) (*apitypes.UserPod, error) {
return MakePod(podId, "", "", config, config.Cmd.Slice(), config.Entrypoint.Slice())
func MakeBasicPod(podId string, config *containertypes.Config, hostConfig *containertypes.HostConfig) (*apitypes.UserPod, error) {
return MakePod(podId, "", "", config, config.Cmd.Slice(), config.Entrypoint.Slice(), hostConfig.ReadonlyRootfs)
}

func MakePod(podId, src, shellDir string, config *containertypes.Config, cmds, entrys []string) (*apitypes.UserPod, error) {
func MakePod(podId, src, shellDir string, config *containertypes.Config, cmds, entrys []string, readonlyRoot bool) (*apitypes.UserPod, error) {
if config.Image == "" {
return nil, fmt.Errorf("image can not be null")
}
Expand Down Expand Up @@ -326,6 +326,7 @@ func MakePod(podId, src, shellDir string, config *containertypes.Config, cmds, e
Workdir: config.WorkingDir,
Entrypoint: entrys,
Tty: config.Tty,
ReadOnly: readonlyRoot,
Ports: []*apitypes.UserContainerPort{},
Envs: env,
Volumes: cVols,
Expand Down
7 changes: 4 additions & 3 deletions daemon/pod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,9 @@ func (c *Container) createByEngine() (*dockertypes.ContainerJSON, error) {
}

ccs, err = c.p.factory.engine.ContainerCreate(dockertypes.ContainerCreateConfig{
Name: c.spec.Name,
Config: config,
Name: c.spec.Name,
Config: config,
HostConfig: &container.HostConfig{ReadonlyRootfs: c.spec.ReadOnly},
})

if err != nil {
Expand Down Expand Up @@ -882,7 +883,7 @@ func (c *Container) addToSandbox() error {
}
}

root, err := c.p.factory.sd.PrepareContainer(c.descript.MountId, c.p.sandboxShareDir())
root, err := c.p.factory.sd.PrepareContainer(c.descript.MountId, c.p.sandboxShareDir(), c.spec.ReadOnly)
if err != nil {
c.Log(ERROR, "failed to prepare rootfs: %v", err)
return err
Expand Down
2 changes: 1 addition & 1 deletion daemon/pod/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type ContainerEngine interface {
type PodStorage interface {
Type() string

PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error)
PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error)
CleanupContainer(id, sharedDir string) error
InjectFile(src io.Reader, containerId, target, baseDir string, perm, uid, gid int) error
CreateVolume(podId string, spec *apitypes.UserVolume) error
Expand Down
82 changes: 47 additions & 35 deletions daemon/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Storage interface {
Init() error
CleanUp() error

PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error)
PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error)
CleanupContainer(id, sharedDir string) error
InjectFile(src io.Reader, containerId, target, baseDir string, perm, uid, gid int) error
CreateVolume(podId string, spec *apitypes.UserVolume) error
Expand Down Expand Up @@ -121,7 +121,7 @@ func (dms *DevMapperStorage) CleanUp() error {
return dm.DMCleanup(dms.DmPoolData)
}

func (dms *DevMapperStorage) PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error) {
func (dms *DevMapperStorage) PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
if err := dm.CreateNewDevice(mountId, dms.DevPrefix, dms.RootPath()); err != nil {
return nil, err
}
Expand All @@ -136,10 +136,11 @@ func (dms *DevMapperStorage) PrepareContainer(mountId, sharedDir string) (*runv.
}

vol := &runv.VolumeDescription{
Name: devFullName,
Source: devFullName,
Fstype: fstype,
Format: "raw",
Name: devFullName,
Source: devFullName,
Fstype: fstype,
Format: "raw",
ReadOnly: readonly,
}

return vol, nil
Expand Down Expand Up @@ -285,19 +286,20 @@ func (*AufsStorage) Init() error { return nil }

func (*AufsStorage) CleanUp() error { return nil }

func (a *AufsStorage) PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error) {
_, err := aufs.MountContainerToSharedDir(mountId, a.RootPath(), sharedDir, "")
func (a *AufsStorage) PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
_, err := aufs.MountContainerToSharedDir(mountId, a.RootPath(), sharedDir, "", readonly)
if err != nil {
glog.Error("got error when mount container to share dir ", err.Error())
return nil, err
}

containerPath := "/" + mountId
vol := &runv.VolumeDescription{
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
ReadOnly: readonly,
}

return vol, nil
Expand All @@ -308,7 +310,7 @@ func (a *AufsStorage) CleanupContainer(id, sharedDir string) error {
}

func (a *AufsStorage) InjectFile(src io.Reader, containerId, target, baseDir string, perm, uid, gid int) error {
_, err := aufs.MountContainerToSharedDir(containerId, a.RootPath(), baseDir, "")
_, err := aufs.MountContainerToSharedDir(containerId, a.RootPath(), baseDir, "", false)
if err != nil {
glog.Error("got error when mount container to share dir ", err.Error())
return err
Expand Down Expand Up @@ -356,19 +358,20 @@ func (*OverlayFsStorage) Init() error { return nil }

func (*OverlayFsStorage) CleanUp() error { return nil }

func (o *OverlayFsStorage) PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error) {
_, err := overlay.MountContainerToSharedDir(mountId, o.RootPath(), sharedDir, "")
func (o *OverlayFsStorage) PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
_, err := overlay.MountContainerToSharedDir(mountId, o.RootPath(), sharedDir, "", readonly)
if err != nil {
glog.Error("got error when mount container to share dir ", err.Error())
return nil, err
}

containerPath := "/" + mountId
vol := &runv.VolumeDescription{
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
ReadOnly: readonly,
}

return vol, nil
Expand All @@ -379,7 +382,7 @@ func (o *OverlayFsStorage) CleanupContainer(id, sharedDir string) error {
}

func (o *OverlayFsStorage) InjectFile(src io.Reader, mountId, target, baseDir string, perm, uid, gid int) error {
_, err := overlay.MountContainerToSharedDir(mountId, o.RootPath(), baseDir, "")
_, err := overlay.MountContainerToSharedDir(mountId, o.RootPath(), baseDir, "", false)
if err != nil {
glog.Error("got error when mount container to share dir ", err.Error())
return err
Expand Down Expand Up @@ -431,7 +434,7 @@ func (*BtrfsStorage) Init() error { return nil }

func (*BtrfsStorage) CleanUp() error { return nil }

func (s *BtrfsStorage) PrepareContainer(containerId, sharedDir string) (*runv.VolumeDescription, error) {
func (s *BtrfsStorage) PrepareContainer(containerId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
btrfsRootfs := s.subvolumesDirID(containerId)
mountPoint := filepath.Join(sharedDir, containerId, "rootfs")

Expand All @@ -443,13 +446,20 @@ func (s *BtrfsStorage) PrepareContainer(containerId, sharedDir string) (*runv.Vo
if err := syscall.Mount(btrfsRootfs, mountPoint, "bind", syscall.MS_BIND, ""); err != nil {
return nil, fmt.Errorf("failed to mount %s to %s: %v", btrfsRootfs, mountPoint, err)
}
if readonly {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so there may be a security problem among these two mount operations. The early existing container can possible write to the roofs of this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are different subvolumes. It needs to break its own chroot sandbox to access another container's rootfs, no?

if err := syscall.Mount(btrfsRootfs, mountPoint, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil {
syscall.Unmount(mountPoint, syscall.MNT_DETACH)
return nil, fmt.Errorf("failed to mount %s to %s readonly: %v", btrfsRootfs, mountPoint, err)
}
}

containerPath := "/" + containerId
vol := &runv.VolumeDescription{
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
Name: containerPath,
Source: containerPath,
Fstype: "dir",
Format: "vfs",
ReadOnly: readonly,
}

return vol, nil
Expand Down Expand Up @@ -506,14 +516,15 @@ func (s *RawBlockStorage) Init() error {

func (*RawBlockStorage) CleanUp() error { return nil }

func (s *RawBlockStorage) PrepareContainer(containerId, sharedDir string) (*runv.VolumeDescription, error) {
func (s *RawBlockStorage) PrepareContainer(containerId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
devFullName := filepath.Join(s.RootPath(), "blocks", containerId)

vol := &runv.VolumeDescription{
Name: devFullName,
Source: devFullName,
Fstype: "xfs",
Format: "raw",
Name: devFullName,
Source: devFullName,
Fstype: "xfs",
Format: "raw",
ReadOnly: readonly,
}

return vol, nil
Expand Down Expand Up @@ -569,18 +580,19 @@ func (*VBoxStorage) Init() error { return nil }

func (*VBoxStorage) CleanUp() error { return nil }

func (v *VBoxStorage) PrepareContainer(mountId, sharedDir string) (*runv.VolumeDescription, error) {
func (v *VBoxStorage) PrepareContainer(mountId, sharedDir string, readonly bool) (*runv.VolumeDescription, error) {
devFullName, err := vbox.MountContainerToSharedDir(mountId, v.RootPath(), "")
if err != nil {
glog.Error("got error when mount container to share dir ", err.Error())
return nil, err
}

vol := &runv.VolumeDescription{
Name: devFullName,
Source: devFullName,
Fstype: "ext4",
Format: "vdi",
Name: devFullName,
Source: devFullName,
Fstype: "ext4",
Format: "vdi",
ReadOnly: readonly,
}

return vol, nil
Expand Down
14 changes: 14 additions & 0 deletions hack/lib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,17 @@ hyper::test::container_logs_no_newline() {
echo logs result $res
test x$res = "xfoobar"
}

hyper::test::container_readonly_rootfs_and_volume() {
echo "Container rootfs and volume readonly test"
id=$(sudo hyperctl run -p ${HYPER_ROOT}/hack/pods/readonly-rootfs.pod | sed -ne 's/POD id is \(.*\)/\1/p')
sudo hyperctl exec -t $id mount
res1=$(sudo hyperctl exec $id touch /foobar || true)
echo res1 test result is ${res1}
res2=$(sudo hyperctl exec $id touch /tmp/foobar || true)
echo res2 test result is ${res2}
res1=$(echo res1 test result is ${res1} | grep 'Read-only file system' > /dev/null 2>&1; echo $?)
res2=$(echo res2 test result is ${res2} | grep 'Read-only file system' > /dev/null 2>&1; echo $?)
sudo hyperctl rm $id
test $res1 -eq 0 -a $res2 -eq 0
}
18 changes: 18 additions & 0 deletions hack/pods/readonly-rootfs.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"containers" : [{
"name": "busybox-readonly",
"image": "busybox",
"command": ["/bin/sh"],
"readonly": true,
"volumes": [{
"volume": "tmp",
"path": "/tmp",
"readOnly": true
}]
}],
"volumes": [{
"name": "tmp",
"source": "",
"format": ""
}]
}
1 change: 1 addition & 0 deletions hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ __EOF__
hyper::test::imagevolume
hyper::test::force_kill_container
hyper::test::container_logs_no_newline
hyper::test::container_readonly_rootfs_and_volume

stop_hyperd
}
Expand Down
12 changes: 8 additions & 4 deletions storage/aufs/aufs.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (

const MsRemount = syscall.MS_REMOUNT

func MountContainerToSharedDir(containerId, rootDir, sharedDir, mountLabel string) (string, error) {
func MountContainerToSharedDir(containerId, rootDir, sharedDir, mountLabel string, readonly bool) (string, error) {
var (
//mntPath = path.Join(rootDir, "mnt")
//layersPath = path.Join(rootDir, "layers")
Expand All @@ -54,7 +54,7 @@ func MountContainerToSharedDir(containerId, rootDir, sharedDir, mountLabel strin
return "", err
}

if err := aufsMount(diffs, path.Join(diffPath, containerId), mountPoint, mountLabel); err != nil {
if err := aufsMount(diffs, path.Join(diffPath, containerId), mountPoint, mountLabel, readonly); err != nil {
return "", fmt.Errorf("Fail to mount aufs dir to %s: %v", mountPoint, err)
}

Expand All @@ -75,7 +75,7 @@ func getParentDiffPaths(id, rootPath string) ([]string, error) {
return layers, nil
}

func aufsMount(ro []string, rw, target, mountLabel string) (err error) {
func aufsMount(ro []string, rw, target, mountLabel string, readonly bool) (err error) {
defer func() {
if err != nil {
aufsUnmount(target)
Expand All @@ -89,8 +89,12 @@ func aufsMount(ro []string, rw, target, mountLabel string) (err error) {
if useDirperm() {
offset += len(",dirperm1")
}
perm := "rw"
if readonly {
perm = "ro"
}
b := make([]byte, syscall.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel
bp := copy(b, fmt.Sprintf("br:%s=rw", rw))
bp := copy(b, fmt.Sprintf("br:%s=%s", rw, perm))

firstMount := true
i := 0
Expand Down
2 changes: 1 addition & 1 deletion storage/aufs/aufs_nonsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package aufs

func MountContainerToSharedDir(containerId, rootDir, sharedDir, mountLabel string) (string, error) {
func MountContainerToSharedDir(containerId, rootDir, sharedDir, mountLabel string, readonly bool) (string, error) {
return "", nil
}

Expand Down
Loading