mirror of https://github.com/containers/podman.git
Merge pull request #16545 from rhatdan/read-only
Add containers.conf read-only flag support
This commit is contained in:
commit
4a57cfb926
|
|
@ -30,7 +30,7 @@ don't take them beyond that.
|
|||
|
||||
WARNING: The items linked below all come from scripts in the `artifacts_task`
|
||||
map of `.cirrus.yml`. When adding or updating any item below, please ensure it
|
||||
matches cooresponding changes in the artifacts task.
|
||||
matches corresponding changes in the artifacts task.
|
||||
|
||||
-->
|
||||
|
||||
|
|
|
|||
|
|
@ -377,12 +377,12 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
createFlags.BoolVar(
|
||||
&cf.ReadOnly,
|
||||
"read-only", false,
|
||||
"read-only", podmanConfig.ContainersConfDefaultsRO.Containers.ReadOnly,
|
||||
"Make containers root filesystem read-only",
|
||||
)
|
||||
createFlags.BoolVar(
|
||||
&cf.ReadOnlyTmpFS,
|
||||
"read-only-tmpfs", cf.ReadOnlyTmpFS,
|
||||
&cf.ReadWriteTmpFS,
|
||||
"read-only-tmpfs", cf.ReadWriteTmpFS,
|
||||
"When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp",
|
||||
)
|
||||
requiresFlagName := "requires"
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func DefineCreateDefaults(opts *entities.ContainerCreateOptions) {
|
|||
opts.MemorySwappiness = -1
|
||||
opts.ImageVolume = podmanConfig.ContainersConfDefaultsRO.Engine.ImageVolumeMode
|
||||
opts.Pull = policy()
|
||||
opts.ReadOnlyTmpFS = true
|
||||
opts.ReadWriteTmpFS = true
|
||||
opts.SdNotifyMode = define.SdNotifyModeContainer
|
||||
opts.StopTimeout = podmanConfig.ContainersConfDefaultsRO.Engine.StopTimeout
|
||||
opts.Systemd = "true"
|
||||
|
|
|
|||
|
|
@ -409,6 +409,8 @@ func createPodIfNecessary(cmd *cobra.Command, s *specgen.SpecGenerator, netOpts
|
|||
infraOpts := entities.NewInfraContainerCreateOptions()
|
||||
infraOpts.Net = netOpts
|
||||
infraOpts.Quiet = true
|
||||
infraOpts.ReadOnly = true
|
||||
infraOpts.ReadWriteTmpFS = false
|
||||
infraOpts.Hostname, err = cmd.Flags().GetString("hostname")
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.C
|
|||
PublishAll: cc.HostConfig.PublishAllPorts,
|
||||
Quiet: false,
|
||||
ReadOnly: cc.HostConfig.ReadonlyRootfs,
|
||||
ReadOnlyTmpFS: true, // podman default
|
||||
ReadWriteTmpFS: true, // podman default
|
||||
Rm: cc.HostConfig.AutoRemove,
|
||||
SecurityOpt: cc.HostConfig.SecurityOpt,
|
||||
StopSignal: cc.Config.StopSignal,
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ type ContainerCreateOptions struct {
|
|||
Pull string
|
||||
Quiet bool
|
||||
ReadOnly bool
|
||||
ReadOnlyTmpFS bool
|
||||
ReadWriteTmpFS bool
|
||||
Restart string
|
||||
Replace bool
|
||||
Requires []string
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri
|
|||
ImageVolume: "bind",
|
||||
IsInfra: false,
|
||||
MemorySwappiness: -1,
|
||||
ReadOnly: true,
|
||||
ReadWriteTmpFS: false,
|
||||
// No need to spin up slirp etc.
|
||||
Net: &entities.NetOptions{Network: specgen.Namespace{NSMode: specgen.NoNetwork}},
|
||||
}
|
||||
|
|
@ -560,6 +562,8 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||
infraImage := util.DefaultContainerConfig().Engine.InfraImage
|
||||
infraOptions := entities.NewInfraContainerCreateOptions()
|
||||
infraOptions.Hostname = podSpec.PodSpecGen.PodBasicConfig.Hostname
|
||||
infraOptions.ReadOnly = true
|
||||
infraOptions.ReadWriteTmpFS = false
|
||||
infraOptions.UserNS = options.Userns
|
||||
podSpec.PodSpecGen.InfraImage = infraImage
|
||||
podSpec.PodSpecGen.NoInfra = false
|
||||
|
|
@ -605,6 +609,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||
}
|
||||
}
|
||||
|
||||
cfg, err := ic.Libpod.GetConfigNoCopy()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var readOnly types.OptionalBool
|
||||
if cfg.Containers.ReadOnly {
|
||||
readOnly = types.NewOptionalBool(cfg.Containers.ReadOnly)
|
||||
}
|
||||
|
||||
ctrNames := make(map[string]string)
|
||||
for _, initCtr := range podYAML.Spec.InitContainers {
|
||||
// Error out if same name is used for more than one container
|
||||
|
|
@ -643,6 +657,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||
PodInfraID: podInfraID,
|
||||
PodName: podName,
|
||||
PodSecurityContext: podYAML.Spec.SecurityContext,
|
||||
ReadOnly: readOnly,
|
||||
RestartPolicy: define.RestartPolicyNo,
|
||||
SeccompPaths: seccompPaths,
|
||||
SecretsManager: secretsManager,
|
||||
|
|
@ -697,6 +712,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||
PodInfraID: podInfraID,
|
||||
PodName: podName,
|
||||
PodSecurityContext: podYAML.Spec.SecurityContext,
|
||||
ReadOnly: readOnly,
|
||||
RestartPolicy: ctrRestartPolicy,
|
||||
SeccompPaths: seccompPaths,
|
||||
SecretsManager: secretsManager,
|
||||
|
|
|
|||
|
|
@ -472,6 +472,8 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||
if ro := opts.ReadOnly; ro != itypes.OptionalBoolUndefined {
|
||||
s.ReadOnlyFilesystem = (ro == itypes.OptionalBoolTrue)
|
||||
}
|
||||
// This should default to true for kubernetes yaml
|
||||
s.ReadWriteTmpfs = true
|
||||
|
||||
// Make sure the container runs in a systemd unit which is
|
||||
// stored as a label at container creation.
|
||||
|
|
|
|||
|
|
@ -159,14 +159,19 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
|
|||
// Check for conflicts between named volumes and mounts
|
||||
for dest := range baseMounts {
|
||||
if _, ok := baseVolumes[dest]; ok {
|
||||
return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest)
|
||||
return nil, nil, nil, fmt.Errorf("baseMounts conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest)
|
||||
}
|
||||
}
|
||||
for dest := range baseVolumes {
|
||||
if _, ok := baseMounts[dest]; ok {
|
||||
return nil, nil, nil, fmt.Errorf("conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest)
|
||||
return nil, nil, nil, fmt.Errorf("baseVolumes conflict at mount destination %v: %w", dest, specgen.ErrDuplicateDest)
|
||||
}
|
||||
}
|
||||
|
||||
if s.ReadWriteTmpfs {
|
||||
baseMounts = addReadWriteTmpfsMounts(baseMounts, s.Volumes)
|
||||
}
|
||||
|
||||
// Final step: maps to arrays
|
||||
finalMounts := make([]spec.Mount, 0, len(baseMounts))
|
||||
for _, mount := range baseMounts {
|
||||
|
|
@ -427,3 +432,29 @@ func InitFSMounts(mounts []spec.Mount) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addReadWriteTmpfsMounts(mounts map[string]spec.Mount, volumes []*specgen.NamedVolume) map[string]spec.Mount {
|
||||
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
|
||||
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
|
||||
for _, dest := range readonlyTmpfs {
|
||||
if _, ok := mounts[dest]; ok {
|
||||
continue
|
||||
}
|
||||
for _, m := range volumes {
|
||||
if m.Dest == dest {
|
||||
continue
|
||||
}
|
||||
}
|
||||
mnt := spec.Mount{
|
||||
Destination: dest,
|
||||
Type: define.TypeTmpfs,
|
||||
Source: define.TypeTmpfs,
|
||||
Options: options,
|
||||
}
|
||||
if dest != "/run" {
|
||||
mnt.Options = append(mnt.Options, "noexec")
|
||||
}
|
||||
mounts[dest] = mnt
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
|
|
|||
|
|
@ -384,6 +384,10 @@ type ContainerSecurityConfig struct {
|
|||
// ReadOnlyFilesystem indicates that everything will be mounted
|
||||
// as read-only
|
||||
ReadOnlyFilesystem bool `json:"read_only_filesystem,omitempty"`
|
||||
// ReadWriteTmpfs indicates that when running with a ReadOnlyFilesystem
|
||||
// mount temporary file systems
|
||||
ReadWriteTmpfs bool `json:"read_write_tmpfs,omitempty"`
|
||||
|
||||
// Umask is the umask the init process of the container will be run with.
|
||||
Umask string `json:"umask,omitempty"`
|
||||
// ProcOpts are the options used for the proc mount.
|
||||
|
|
|
|||
|
|
@ -592,10 +592,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||
s.DependencyContainers = c.Requires
|
||||
}
|
||||
|
||||
// TODO
|
||||
// outside of specgen and oci though
|
||||
// defaults to true, check spec/storage
|
||||
// s.readonly = c.ReadOnlyTmpFS
|
||||
// Only add ReadWrite tmpfs mounts iff the container is
|
||||
// being run ReadOnly and ReadWriteTmpFS is not disabled,
|
||||
// (user specifying --read-only-tmpfs=false.)
|
||||
s.ReadWriteTmpfs = c.ReadOnly && c.ReadWriteTmpFS
|
||||
|
||||
// TODO convert to map?
|
||||
// check if key=value and convert
|
||||
sysmap := make(map[string]string)
|
||||
|
|
@ -853,10 +854,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||
s.PasswdEntry = c.PasswdEntry
|
||||
}
|
||||
|
||||
if c.ReadOnly && c.ReadOnlyTmpFS {
|
||||
s.Mounts = addReadOnlyMounts(s.Mounts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -703,26 +703,3 @@ func validChownFlag(flag string) (bool, error) {
|
|||
func unixPathClean(p string) string {
|
||||
return path.Clean(p)
|
||||
}
|
||||
|
||||
func addReadOnlyMounts(mounts []spec.Mount) []spec.Mount {
|
||||
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
|
||||
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
|
||||
for _, dest := range readonlyTmpfs {
|
||||
for _, m := range mounts {
|
||||
if m.Destination == dest {
|
||||
continue
|
||||
}
|
||||
}
|
||||
mnt := spec.Mount{
|
||||
Destination: dest,
|
||||
Type: define.TypeTmpfs,
|
||||
Source: define.TypeTmpfs,
|
||||
Options: options,
|
||||
}
|
||||
if dest != "/run" {
|
||||
mnt.Options = append(mnt.Options, "noexec")
|
||||
}
|
||||
mounts = append(mounts, mnt)
|
||||
}
|
||||
return mounts
|
||||
}
|
||||
|
|
|
|||
|
|
@ -951,4 +951,17 @@ $IMAGE--c_ok" \
|
|||
run_podman stop -t 0 $cid
|
||||
}
|
||||
|
||||
@test "podman run read-only from containers.conf" {
|
||||
containersconf=$PODMAN_TMPDIR/containers.conf
|
||||
cat >$containersconf <<EOF
|
||||
[containers]
|
||||
read_only=true
|
||||
EOF
|
||||
|
||||
CONTAINERS_CONF="$containersconf" run_podman 1 run --rm $IMAGE touch /testro
|
||||
CONTAINERS_CONF="$containersconf" run_podman run --rm --read-only=false $IMAGE touch /testrw
|
||||
CONTAINERS_CONF="$containersconf" run_podman run --rm $IMAGE touch /tmp/testrw
|
||||
CONTAINERS_CONF="$containersconf" run_podman 1 run --rm --read-only-tmpfs=false $IMAGE touch /tmp/testro
|
||||
}
|
||||
|
||||
# vim: filetype=sh
|
||||
|
|
|
|||
|
|
@ -238,6 +238,41 @@ EOF
|
|||
run_podman 1 container exists pod1-test3
|
||||
}
|
||||
|
||||
@test "podman kube play read-only from containers.conf" {
|
||||
containersconf=$PODMAN_TMPDIR/containers.conf
|
||||
cat >$containersconf <<EOF
|
||||
[containers]
|
||||
read_only=true
|
||||
EOF
|
||||
|
||||
YAML=$PODMAN_TMPDIR/test.yml
|
||||
CONTAINERS_CONF="$containersconf" run_podman create --pod new:pod1 --read-only=false --name test1 $IMAGE touch /testrw
|
||||
CONTAINERS_CONF="$containersconf" run_podman create --pod pod1 --name test2 $IMAGE touch /testro
|
||||
CONTAINERS_CONF="$containersconf" run_podman create --pod pod1 --name test3 $IMAGE touch /tmp/testtmp
|
||||
CONTAINERS_CONF="$containersconf" run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' test1 test2 test3
|
||||
is "$output" "false.*true.*true" "Rootfs should be read/only"
|
||||
|
||||
# Now generate and run kube.yaml on a machine without the defaults set
|
||||
CONTAINERS_CONF="$containersconf" run_podman kube generate pod1 -f $YAML
|
||||
cat $YAML
|
||||
|
||||
run_podman kube play --replace $YAML
|
||||
run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' pod1-test1 pod1-test2 pod1-test3
|
||||
is "$output" "false.*true.*true" "Rootfs should be read/only"
|
||||
|
||||
run_podman inspect --format "{{.State.ExitCode}}" pod1-test1
|
||||
is "$output" "0" "Container / should be read/write"
|
||||
run_podman inspect --format "{{.State.ExitCode}}" pod1-test2
|
||||
is "$output" "1" "Container / should be read/only"
|
||||
run_podman inspect --format "{{.State.ExitCode}}" pod1-test3
|
||||
is "$output" "0" "/tmp in a read-only container should be read/write"
|
||||
|
||||
run_podman kube down - < $YAML
|
||||
run_podman 1 container exists pod1-test1
|
||||
run_podman 1 container exists pod1-test2
|
||||
run_podman 1 container exists pod1-test3
|
||||
}
|
||||
|
||||
@test "podman play with user from image" {
|
||||
TESTDIR=$PODMAN_TMPDIR/testdir
|
||||
mkdir -p $TESTDIR
|
||||
|
|
|
|||
Loading…
Reference in New Issue