Skip to content

Commit

Permalink
feat: machined: initial SELinux bring-up
Browse files Browse the repository at this point in the history
Part of: siderolabs#9127

Label executables and processes, build, load and manage SELinux policy, enable audit support.

Labeling filesystems, devices and runtime files will be done in further changes, see the full PR.

TODO: label static pods

Signed-off-by: Dmitry Sharshakov <[email protected]>
  • Loading branch information
dsseng committed Nov 2, 2024
1 parent 9abf161 commit bc614cf
Show file tree
Hide file tree
Showing 41 changed files with 1,541 additions and 8 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
**
!api
!selinux
!cmd
!docs
!hack
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ node_modules
website/resources
website/public
website/.hugo_build.lock

# Files built for SELinux troubleshooting
policy.33
file_contexts
18 changes: 16 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ COPY --from=embed-abbrev-generate /src/pkg/machinery/gendata/data /pkg/machinery
COPY --from=embed-abbrev-generate /src/_out/talos-metadata /_out/talos-metadata
COPY --from=embed-abbrev-generate /src/_out/signing_key.x509 /_out/signing_key.x509

FROM tools as selinux
COPY ./selinux /selinux
RUN secilc -c 33 /selinux/**/*.cil -vvvvv -O

FROM scratch AS ipxe-generate
COPY --from=pkg-ipxe-amd64 /usr/libexec/snp.efi /amd64/snp.efi
COPY --from=pkg-ipxe-arm64 /usr/libexec/snp.efi /arm64/snp.efi
Expand Down Expand Up @@ -412,6 +416,7 @@ COPY --from=go-generate /src/pkg/machinery/config/types/ /pkg/machinery/config/t
COPY --from=go-generate /src/pkg/machinery/nethelpers/ /pkg/machinery/nethelpers/
COPY --from=go-generate /src/pkg/machinery/extensions/ /pkg/machinery/extensions/
COPY --from=ipxe-generate / /pkg/provision/providers/vm/internal/ipxe/data/ipxe/
COPY --from=selinux /policy.33 ./internal/pkg/mount/switchroot/
COPY --from=embed-abbrev / /
COPY --from=pkg-ca-certificates /etc/ssl/certs/ca-certificates /internal/app/machined/pkg/controllers/secrets/data/
COPY --from=microsoft-key-keys / /internal/pkg/secureboot/database/certs/
Expand All @@ -429,6 +434,7 @@ COPY --from=generate /pkg/imager/ ./pkg/imager/
COPY --from=generate /pkg/machinery/ ./pkg/machinery/
COPY --from=generate /internal/app/machined/pkg/controllers/secrets/data/ ./internal/app/machined/pkg/controllers/secrets/data/
COPY --from=generate /internal/pkg/secureboot/database/certs/ ./internal/pkg/secureboot/database/certs/
COPY --from=generate /internal/pkg/mount/switchroot/ ./internal/pkg/mount/switchroot/
COPY --from=embed / ./
RUN --mount=type=cache,target=/.cache go list all >/dev/null
WORKDIR /src/pkg/machinery
Expand Down Expand Up @@ -716,6 +722,7 @@ RUN <<END
ln -s /etc/ssl /rootfs/usr/local/share/ca-certificates
ln -s /etc/ssl /rootfs/etc/ca-certificates
END
RUN mkdir -p /rootfs/selinux

FROM build AS rootfs-base-arm64
COPY --link --from=pkg-fhs / /rootfs
Expand Down Expand Up @@ -788,6 +795,7 @@ RUN <<END
ln -s /etc/ssl /rootfs/usr/local/share/ca-certificates
ln -s /etc/ssl /rootfs/etc/ca-certificates
END
RUN mkdir -p /rootfs/selinux

FROM rootfs-base-${TARGETARCH} AS rootfs-base
RUN echo "true" > /rootfs/usr/etc/in-container
Expand All @@ -798,13 +806,19 @@ FROM rootfs-base-arm64 AS rootfs-squashfs-arm64
ARG ZSTD_COMPRESSION_LEVEL
RUN find /rootfs -print0 \
| xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}"
RUN mksquashfs /rootfs /rootfs.sqsh -all-root -noappend -comp zstd -Xcompression-level ${ZSTD_COMPRESSION_LEVEL} -no-progress
COPY --from=selinux /file_contexts /file_contexts
COPY ./hack/labeled-squashfs.sh /
ENV SHELL=/toolchain/bin/bash
RUN fakeroot /labeled-squashfs.sh /rootfs /rootfs.sqsh /file_contexts ${ZSTD_COMPRESSION_LEVEL}

FROM rootfs-base-amd64 AS rootfs-squashfs-amd64
ARG ZSTD_COMPRESSION_LEVEL
RUN find /rootfs -print0 \
| xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}"
RUN mksquashfs /rootfs /rootfs.sqsh -all-root -noappend -comp zstd -Xcompression-level ${ZSTD_COMPRESSION_LEVEL} -no-progress
COPY --from=selinux /file_contexts /file_contexts
COPY ./hack/labeled-squashfs.sh /
ENV SHELL=/toolchain/bin/bash
RUN fakeroot /labeled-squashfs.sh /rootfs /rootfs.sqsh /file_contexts ${ZSTD_COMPRESSION_LEVEL}

FROM scratch AS squashfs-arm64
COPY --from=rootfs-squashfs-arm64 /rootfs.sqsh /
Expand Down
6 changes: 6 additions & 0 deletions hack/labeled-squashfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/toolchain/bin/bash

set -e
# set SELinux labels for files according to file_contexts supplied
/toolchain/sbin/setfiles -r $1 -F -vv $3 $1
mksquashfs $1 $2 -all-root -noappend -comp zstd -Xcompression-level $4 -no-progress
9 changes: 9 additions & 0 deletions internal/app/machined/pkg/controllers/cri/runc_memfd_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"golang.org/x/sys/unix"

runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
"github.com/siderolabs/talos/internal/pkg/selinux"
)

// RuncMemFDBindController created a locked memfd bind for the runc binary, so that it can be used instead of copying the actual runc binary everytime.
Expand All @@ -40,6 +41,8 @@ func (ctrl *RuncMemFDBindController) Outputs() []controller.Output {
}

// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *RuncMemFDBindController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// This controller is only relevant in container mode.
if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer {
Expand Down Expand Up @@ -82,6 +85,12 @@ func (ctrl *RuncMemFDBindController) Run(ctx context.Context, r controller.Runti
return fmt.Errorf("mount: failed to mount memfd on top of runc binary path target: %w", err)
}

if selinux.IsEnabled() {
if err := unix.Fsetxattr(int(memfdFile.Fd()), "security.selinux", []byte("system_u:object_r:runc_memfd_t:s0"), 0); err != nil {
return err
}
}

// Clean up things we don't need...
_ = exeFile.Close() //nolint:errcheck
_ = memfdLink.Close() //nolint:errcheck
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ func WriteUdevRules(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
content.WriteByte('\n')
}

// TODO: SELinux label?
if err = os.WriteFile(constants.UdevRulesPath, []byte(content.String()), 0o644); err != nil {
return fmt.Errorf("failed writing custom udev rules: %w", err)
}
Expand Down
15 changes: 15 additions & 0 deletions internal/app/machined/pkg/system/runner/containerd/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/events"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/pkg/cgroup"
"github.com/siderolabs/talos/internal/pkg/selinux"
)

// containerdRunner is a runner.Runner that runs container in containerd.
Expand Down Expand Up @@ -341,6 +342,20 @@ func (c *containerdRunner) newOCISpecOpts(image oci.Image) []oci.SpecOpts {
)
}

if selinux.IsEnabled() {
if c.opts.SelinuxLabel != "" {
specOpts = append(
specOpts,
oci.WithSelinuxLabel(c.opts.SelinuxLabel),
)
} else {
specOpts = append(
specOpts,
oci.WithSelinuxLabel("system_u:system_r:unconfined_container_t:s0"),
)
}
}

return specOpts
}

Expand Down
20 changes: 20 additions & 0 deletions internal/app/machined/pkg/system/runner/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/fs"
"log"
"os"
"path"
"slices"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/events"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/pkg/cgroup"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/machinery/constants"
)

Expand Down Expand Up @@ -92,6 +94,7 @@ func (p *processRunner) Close() error {
type commandWrapper struct {
launcher *cap.Launcher
ctty optional.Optional[int]
selinuxLabel string
cgroupFile *os.File
stdin *os.File
stdout *os.File
Expand Down Expand Up @@ -159,6 +162,22 @@ func beforeExecCallback(pa *syscall.ProcAttr, data any) error {
pa.Sys.CgroupFD = int(wrapper.cgroupFile.Fd())
}

// Use /proc/thread-self (Linux 3.17+) to avoid races between current
// process threads leading to loss of the domain transition
if selinux.IsEnabled() {
if wrapper.selinuxLabel != "" {
err := os.WriteFile("/proc/thread-self/attr/exec", []byte(wrapper.selinuxLabel), 0o777)
if err != nil {
log.Fatalf("%s", err)
}
} else {
err := os.WriteFile("/proc/thread-self/attr/exec", []byte("system_u:system_r:unconfined_service_t:s0"), 0o777)
if err != nil {
log.Fatalf("%s", err)
}
}
}

return nil
}

Expand Down Expand Up @@ -266,6 +285,7 @@ func (p *processRunner) build() (commandWrapper, error) {
wrapper.afterStart = func() { xslices.Map(afterStartClosers, io.Closer.Close) }
wrapper.afterTermination = closeLogging
wrapper.ctty = p.opts.Ctty
wrapper.selinuxLabel = p.opts.SelinuxLabel

cgroupFdSupported := false

Expand Down
9 changes: 9 additions & 0 deletions internal/app/machined/pkg/system/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type Options struct {
OverrideSeccompProfile func(*specs.LinuxSeccomp)
// DroppedCapabilities is the list of capabilities to drop.
DroppedCapabilities []string
// SelinuxLabel is the SELinux label to be assigned
SelinuxLabel string
// StdinFile is the path to the file to use as stdin.
StdinFile string
// StdoutFile is the path to the file to use as stdout.
Expand Down Expand Up @@ -175,6 +177,13 @@ func WithCgroupPath(path string) Option {
}
}

// WithSelinuxLabel sets the SELinux label.
func WithSelinuxLabel(label string) Option {
return func(args *Options) {
args.SelinuxLabel = label
}
}

// WithCustomSeccompProfile sets the function to override seccomp profile.
func WithCustomSeccompProfile(override func(*specs.LinuxSeccomp)) Option {
return func(args *Options) {
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/apid.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithContainerdAddress(constants.SystemContainerdAddress),
runner.WithEnv(env),
runner.WithCgroupPath(constants.CgroupApid),
runner.WithSelinuxLabel(constants.SelinuxLabelApid),
runner.WithOCISpecOpts(
oci.WithDroppedCapabilities(cap.Known()),
oci.WithHostNamespace(specs.NetworkNamespace),
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/containerd.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func (c *Containerd) Runner(r runtime.Runtime) (runner.Runner, error) {
)),
runner.WithOOMScoreAdj(-999),
runner.WithCgroupPath(constants.CgroupSystemRuntime),
runner.WithSelinuxLabel(constants.SelinuxLabelSystemRuntime),
runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities),
),
restart.WithType(restart.Forever),
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/cri.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func (c *CRI) Runner(r runtime.Runtime) (runner.Runner, error) {
)),
runner.WithOOMScoreAdj(-500),
runner.WithCgroupPath(constants.CgroupPodRuntime),
runner.WithSelinuxLabel(constants.SelinuxLabelPodRuntime),
runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities),
),
restart.WithType(restart.Forever),
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (d *Dashboard) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithCtty(0),
runner.WithOOMScoreAdj(-400),
runner.WithDroppedCapabilities(capability.AllCapabilitiesSetLowercase()),
runner.WithSelinuxLabel(constants.SelinuxLabelDashboard),
runner.WithCgroupPath(constants.CgroupDashboard),
runner.WithUID(constants.DashboardUserID),
),
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ func (e *Etcd) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithContainerImage(e.imgRef),
runner.WithEnv(env),
runner.WithCgroupPath(constants.CgroupEtcd),
runner.WithSelinuxLabel(constants.SELinuxLabelEtcd),
runner.WithOCISpecOpts(
oci.WithDroppedCapabilities(cap.Known()),
oci.WithHostNamespace(specs.NetworkNamespace),
Expand Down
2 changes: 1 addition & 1 deletion internal/app/machined/pkg/system/services/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func (k *Kubelet) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithContainerImage(k.imgRef),
runner.WithEnv(environment.Get(r.Config())),
runner.WithCgroupPath(constants.CgroupKubelet),
runner.WithSelinuxLabel(constants.SelinuxLabelKubelet),
runner.WithOCISpecOpts(
containerd.WithRootfsPropagation("shared"),
oci.WithMounts(mounts),
Expand All @@ -168,7 +169,6 @@ func (k *Kubelet) Runner(r runtime.Runtime) (runner.Runner, error) {
oci.WithReadonlyPaths(nil),
oci.WithWriteableSysfs,
oci.WithWriteableCgroupfs,
oci.WithSelinuxLabel(""),
oci.WithApparmorProfile(""),
oci.WithAllDevicesAllowed,
oci.WithCapabilities(capability.AllGrantableCapabilities()), // TODO: kubelet doesn't need all of these, we should consider limiting capabilities
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/trustd.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func (t *Trustd) Runner(r runtime.Runtime) (runner.Runner, error) {
runner.WithContainerdAddress(constants.SystemContainerdAddress),
runner.WithEnv(env),
runner.WithCgroupPath(constants.CgroupTrustd),
runner.WithSelinuxLabel(constants.SelinuxLabelTrustd),
runner.WithOCISpecOpts(
oci.WithDroppedCapabilities(cap.Known()),
oci.WithHostNamespace(specs.NetworkNamespace),
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/udevd.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (c *Udevd) Runner(r runtime.Runtime) (runner.Runner, error) {
args,
runner.WithLoggingManager(r.Logging()),
runner.WithCgroupPath(constants.CgroupUdevd),
runner.WithSelinuxLabel(constants.SelinuxLabelUdevd),
runner.WithDroppedCapabilities(constants.UdevdDroppedCapabilities),
runner.WithEnv([]string{
// append a default value for XDG_RUNTIME_DIR for the services running on the host
Expand Down
Loading

0 comments on commit bc614cf

Please sign in to comment.