Skip to content

Commit

Permalink
Add SELinux label types support to quadlet
Browse files Browse the repository at this point in the history
Add support for disabeling SELinux process separation in the container.
Add support for setting the process type of the container.
Add support for setting the process MCS level of the container.
Add support for setting the file type of the container.

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed Feb 6, 2023
1 parent 928d589 commit acaab3f
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 75 deletions.
16 changes: 16 additions & 0 deletions docs/source/markdown/podman-systemd.unit.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,22 @@ container that forwards signals and reaps processes.
Set the seccomp profile to use in the container. If unset, the default podman profile is used.
Set to either the pathname of a json file, or `unconfined` to disable the seccomp filters.

#### `SecurityLabelDisable=`

Turn off label separation for the container.

#### `SecurityLabelFileType=`

Set the label file type for the container files.

#### `SecurityLabelLevel=`

Set the label process level for the container processes.

#### `SecurityLabelType=`

Set the label process type for the container processes.

#### `Timezone=` (if unset uses system-configured default)

The timezone to run the container in.
Expand Down
178 changes: 103 additions & 75 deletions pkg/systemd/quadlet/quadlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,88 +31,96 @@ const (

// All the supported quadlet keys
const (
KeyContainerName = "ContainerName"
KeyImage = "Image"
KeyEnvironment = "Environment"
KeyEnvironmentFile = "EnvironmentFile"
KeyEnvironmentHost = "EnvironmentHost"
KeyExec = "Exec"
KeyNoNewPrivileges = "NoNewPrivileges"
KeyDropCapability = "DropCapability"
KeyAddCapability = "AddCapability"
KeyReadOnly = "ReadOnly"
KeyRemapUsers = "RemapUsers"
KeyRemapUID = "RemapUid"
KeyRemapGID = "RemapGid"
KeyRemapUIDSize = "RemapUidSize"
KeyRootfs = "Rootfs"
KeyNotify = "Notify"
KeyExposeHostPort = "ExposeHostPort"
KeyPublishPort = "PublishPort"
KeyUser = "User"
KeyGroup = "Group"
KeyDevice = "Device"
KeyType = "Type"
KeyOptions = "Options"
KeyCopy = "Copy"
KeyVolume = "Volume"
KeyPodmanArgs = "PodmanArgs"
KeyLabel = "Label"
KeyAnnotation = "Annotation"
KeyRunInit = "RunInit"
KeyVolatileTmp = "VolatileTmp"
KeyTimezone = "Timezone"
KeySeccompProfile = "SeccompProfile"
KeyAddDevice = "AddDevice"
KeyNetwork = "Network"
KeyYaml = "Yaml"
KeyNetworkDisableDNS = "DisableDNS"
KeyNetworkDriver = "Driver"
KeyNetworkGateway = "Gateway"
KeyNetworkInternal = "Internal"
KeyNetworkIPRange = "IPRange"
KeyNetworkIPAMDriver = "IPAMDriver"
KeyNetworkIPv6 = "IPv6"
KeyNetworkOptions = "Options"
KeyNetworkSubnet = "Subnet"
KeyConfigMap = "ConfigMap"
KeyContainerName = "ContainerName"
KeyImage = "Image"
KeyEnvironment = "Environment"
KeyEnvironmentFile = "EnvironmentFile"
KeyEnvironmentHost = "EnvironmentHost"
KeyExec = "Exec"
KeyNoNewPrivileges = "NoNewPrivileges"
KeyDropCapability = "DropCapability"
KeyAddCapability = "AddCapability"
KeyReadOnly = "ReadOnly"
KeyRemapUsers = "RemapUsers"
KeyRemapUID = "RemapUid"
KeyRemapGID = "RemapGid"
KeyRemapUIDSize = "RemapUidSize"
KeyRootfs = "Rootfs"
KeyNotify = "Notify"
KeyExposeHostPort = "ExposeHostPort"
KeyPublishPort = "PublishPort"
KeyUser = "User"
KeyGroup = "Group"
KeyDevice = "Device"
KeyType = "Type"
KeyOptions = "Options"
KeyCopy = "Copy"
KeyVolume = "Volume"
KeyPodmanArgs = "PodmanArgs"
KeyLabel = "Label"
KeyAnnotation = "Annotation"
KeyRunInit = "RunInit"
KeyVolatileTmp = "VolatileTmp"
KeyTimezone = "Timezone"
KeySeccompProfile = "SeccompProfile"
KeySecurityLabelDisable = "SecurityLabelDisable"
KeySecurityLabelFileType = "SecurityLabelFileType"
KeySecurityLabelType = "SecurityLabelType"
KeySecurityLabelLevel = "SecurityLabelLevel"
KeyAddDevice = "AddDevice"
KeyNetwork = "Network"
KeyYaml = "Yaml"
KeyNetworkDisableDNS = "DisableDNS"
KeyNetworkDriver = "Driver"
KeyNetworkGateway = "Gateway"
KeyNetworkInternal = "Internal"
KeyNetworkIPRange = "IPRange"
KeyNetworkIPAMDriver = "IPAMDriver"
KeyNetworkIPv6 = "IPv6"
KeyNetworkOptions = "Options"
KeyNetworkSubnet = "Subnet"
KeyConfigMap = "ConfigMap"
)

var (
validPortRange = regexp.Delayed(`\d+(-\d+)?(/udp|/tcp)?$`)

// Supported keys in "Container" group
supportedContainerKeys = map[string]bool{
KeyContainerName: true,
KeyImage: true,
KeyEnvironment: true,
KeyEnvironmentFile: true,
KeyEnvironmentHost: true,
KeyExec: true,
KeyNoNewPrivileges: true,
KeyDropCapability: true,
KeyAddCapability: true,
KeyReadOnly: true,
KeyRemapUsers: true,
KeyRemapUID: true,
KeyRemapGID: true,
KeyRemapUIDSize: true,
KeyRootfs: true,
KeyNotify: true,
KeyExposeHostPort: true,
KeyPublishPort: true,
KeyUser: true,
KeyGroup: true,
KeyVolume: true,
KeyPodmanArgs: true,
KeyLabel: true,
KeyAnnotation: true,
KeyRunInit: true,
KeyVolatileTmp: true,
KeyTimezone: true,
KeySeccompProfile: true,
KeyAddDevice: true,
KeyNetwork: true,
KeyContainerName: true,
KeyImage: true,
KeyEnvironment: true,
KeyEnvironmentFile: true,
KeyEnvironmentHost: true,
KeyExec: true,
KeyNoNewPrivileges: true,
KeyDropCapability: true,
KeyAddCapability: true,
KeyReadOnly: true,
KeyRemapUsers: true,
KeyRemapUID: true,
KeyRemapGID: true,
KeyRemapUIDSize: true,
KeyRootfs: true,
KeyNotify: true,
KeyExposeHostPort: true,
KeyPublishPort: true,
KeyUser: true,
KeyGroup: true,
KeyVolume: true,
KeyPodmanArgs: true,
KeyLabel: true,
KeyAnnotation: true,
KeyRunInit: true,
KeyVolatileTmp: true,
KeyTimezone: true,
KeySeccompProfile: true,
KeySecurityLabelDisable: true,
KeySecurityLabelFileType: true,
KeySecurityLabelType: true,
KeySecurityLabelLevel: true,
KeyAddDevice: true,
KeyNetwork: true,
}

// Supported keys in "Volume" group
Expand Down Expand Up @@ -353,6 +361,26 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
podman.add("--security-opt=no-new-privileges")
}

securityLabelDisable := container.LookupBooleanWithDefault(ContainerGroup, KeySecurityLabelDisable, false)
if securityLabelDisable {
podman.add("--security-opt", "label:disable")
}

securityLabelType, _ := container.Lookup(ContainerGroup, KeySecurityLabelType)
if len(securityLabelType) > 0 {
podman.add("--security-opt", fmt.Sprintf("label=type:%s", securityLabelType))
}

securityLabelFileType, _ := container.Lookup(ContainerGroup, KeySecurityLabelFileType)
if len(securityLabelFileType) > 0 {
podman.add("--security-opt", fmt.Sprintf("label=filetype:%s", securityLabelFileType))
}

securityLabelLevel, _ := container.Lookup(ContainerGroup, KeySecurityLabelLevel)
if len(securityLabelLevel) > 0 {
podman.add("--security-opt", fmt.Sprintf("label=level:%s", securityLabelLevel))
}

// But allow overrides with AddCapability
devices := container.LookupAllStrv(ContainerGroup, KeyAddDevice)
for _, device := range devices {
Expand Down
5 changes: 5 additions & 0 deletions test/e2e/quadlet/disableselinux.container
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## assert-podman-args "--security-opt" "label:disable"

[Container]
Image=localhost/imagename
SecurityLabelDisable=true
9 changes: 9 additions & 0 deletions test/e2e/quadlet/selinux.container
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## assert-podman-args "--security-opt" "label=type:foobar_t"
## assert-podman-args "--security-opt" "label=level:s0:c1000,c10001"
## assert-podman-args "--security-opt" "label=filetype:foobar_file_t"

[Container]
Image=localhost/imagename
SecurityLabelType=foobar_t
SecurityLabelFileType=foobar_file_t
SecurityLabelLevel=s0:c1000,c10001
2 changes: 2 additions & 0 deletions test/e2e/quadlet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ var _ = Describe("quadlet system generator", func() {
Entry("basepodman.container", "basepodman.container"),
Entry("capabilities.container", "capabilities.container"),
Entry("capabilities2.container", "capabilities2.container"),
Entry("disableselinux.container", "disableselinux.container"),
Entry("devices.container", "devices.container"),
Entry("env.container", "env.container"),
Entry("escapes.container", "escapes.container"),
Expand All @@ -461,6 +462,7 @@ var _ = Describe("quadlet system generator", func() {
Entry("notify.container", "notify.container"),
Entry("oneshot.container", "oneshot.container"),
Entry("rootfs.container", "rootfs.container"),
Entry("selinux.container", "selinux.container"),
Entry("other-sections.container", "other-sections.container"),
Entry("podmanargs.container", "podmanargs.container"),
Entry("ports.container", "ports.container"),
Expand Down
52 changes: 52 additions & 0 deletions test/system/252-quadlet.bats
Original file line number Diff line number Diff line change
Expand Up @@ -424,5 +424,57 @@ EOF
is "$output" '.*STARTED CONTAINER.*'
}

@test "quadlet - selinux disable" {
skip_if_no_selinux
local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
cat > $quadlet_file <<EOF
[Container]
Image=$IMAGE
SecurityLabelDisable=true
Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top"
EOF

run_quadlet "$quadlet_file"
service_setup $QUADLET_SERVICE_NAME

# Ensure we have output. Output is synced via sd-notify (socat in Exec)
run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME"
is "$output" '.*STARTED CONTAINER.*'

run_podman container inspect --format "{{.ProcessLabel}}" $QUADLET_CONTAINER_NAME
is "$output" "" "container should be started without specifying a Process Label"

service_cleanup $QUADLET_SERVICE_NAME failed
}

@test "quadlet - selinux labels" {
skip_if_no_selinux
NAME=$(random_string)
local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
cat > $quadlet_file <<EOF
[Container]
ContainerName=$NAME
Image=$IMAGE
SecurityLabelType=spc_t
SecurityLabelLevel=s0:c100,c200
SecurityLabelFileType=container_ro_file_t
Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top"
EOF

run_quadlet "$quadlet_file"
service_setup $QUADLET_SERVICE_NAME

# Ensure we have output. Output is synced via sd-notify (socat in Exec)
run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME"
is "$output" '.*STARTED CONTAINER.*'

run_podman container ps
run_podman container inspect --format "{{.ProcessLabel}}" $NAME
is "$output" "system_u:system_r:spc_t:s0:c100,c200" "container should be started with correct Process Label"
run_podman container inspect --format "{{.MountLabel}}" $NAME
is "$output" "system_u:object_r:container_ro_file_t:s0:c100,c200" "container should be started with correct Mount Label"

service_cleanup $QUADLET_SERVICE_NAME failed
}

# vim: filetype=sh

0 comments on commit acaab3f

Please sign in to comment.