talos/internal/integration/api/selinux.go
Dmitry Sharshakov 960a040491
feat: start enabling SELinux
Part of: #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.

Signed-off-by: Dmitry Sharshakov <dmitry.sharshakov@siderolabs.com>
2024-11-04 16:56:53 +01:00

147 lines
3.7 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build integration_api
package api
import (
"bytes"
"context"
"io"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/talos/internal/integration/base"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
// SELinuxSuite ...
type SELinuxSuite struct {
base.APISuite
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
// SuiteName ...
func (suite *SELinuxSuite) SuiteName() string {
return "api.SELinuxSuite"
}
// SetupTest ...
func (suite *SELinuxSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second)
if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU {
suite.T().Skip("skipping SELinux test since provisioner is not qemu")
}
}
// TearDownTest ...
func (suite *SELinuxSuite) TearDownTest() {
if suite.ctxCancel != nil {
suite.ctxCancel()
}
}
func (suite *SELinuxSuite) getLabel(nodeCtx context.Context, pid int32) string {
r, err := suite.Client.Read(nodeCtx, filepath.Join("/proc", strconv.Itoa(int(pid)), "attr/current"))
suite.Require().NoError(err)
value, err := io.ReadAll(r)
suite.Require().NoError(err)
suite.Require().NoError(r.Close())
return string(bytes.TrimSpace(value))
}
// TestProcessLabels reads labels of system processes from procfs
// to ensure SELinux labels for processes are correctly set
//
//nolint:gocyclo
func (suite *SELinuxSuite) TestProcessLabels() {
nodes := suite.DiscoverNodeInternalIPs(suite.ctx)
for _, node := range nodes {
nodeCtx := client.WithNode(suite.ctx, node)
cmdline := suite.ReadCmdline(nodeCtx)
seLinuxEnabled := pointer.SafeDeref(procfs.NewCmdline(cmdline).Get(constants.KernelParamSELinux).First()) != ""
if !seLinuxEnabled {
suite.T().Skip("skipping SELinux test since SELinux is disabled")
}
r, err := suite.Client.Processes(nodeCtx)
suite.Require().NoError(err)
for _, msg := range r.Messages {
procs := msg.Processes
for _, p := range procs {
switch p.Command {
case "systemd-udevd":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelUdevd,
)
case "dashboard":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelDashboard,
)
case "containerd":
if strings.Contains(p.Args, "/system/run/containerd") {
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelSystemRuntime,
)
} else {
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelPodRuntime,
)
}
case "init":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelMachined,
)
case "kubelet":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelKubelet,
)
case "apid":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelApid,
)
case "trustd":
suite.Require().Contains(
suite.getLabel(nodeCtx, p.Pid),
constants.SelinuxLabelTrustd,
)
}
}
}
}
}
// TODO: test for file labels
// TODO: test labels for unconfined system extensions, pods
// TODO: test for no avc denials in dmesg
// TODO: start a pod and ensure access to restricted resources is denied
func init() {
allSuites = append(allSuites, new(SELinuxSuite))
}