Skip to content

Commit

Permalink
feat: label system socket and runtime files
Browse files Browse the repository at this point in the history
Set SELinux labels so that services could gain access permissions.

Signed-off-by: Dmitry Sharshakov <[email protected]>
  • Loading branch information
dsseng committed Nov 6, 2024
1 parent 398f714 commit a867f85
Show file tree
Hide file tree
Showing 15 changed files with 359 additions and 32 deletions.
5 changes: 5 additions & 0 deletions internal/app/apid/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
apidbackend "github.com/siderolabs/talos/internal/app/apid/pkg/backend"
"github.com/siderolabs/talos/internal/app/apid/pkg/director"
"github.com/siderolabs/talos/internal/app/apid/pkg/provider"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/grpc/factory"
"github.com/siderolabs/talos/pkg/grpc/middleware/authz"
"github.com/siderolabs/talos/pkg/grpc/proxy/backend"
Expand Down Expand Up @@ -157,6 +158,10 @@ func apidMain() error {
return fmt.Errorf("error creating listner: %w", err)
}

if err = selinux.SetLabel(constants.APISocketPath, constants.APISocketLabel); err != nil {
return err
}

networkServer := func() *grpc.Server {
mode := authz.Disabled
if *rbacEnabled {
Expand Down
5 changes: 5 additions & 0 deletions internal/app/machined/pkg/controllers/etcd/pki.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/siderolabs/gen/optional"
"go.uber.org/zap"

"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/filetree"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/etcd"
Expand Down Expand Up @@ -92,6 +93,10 @@ func (ctrl *PKIController) Run(ctx context.Context, r controller.Runtime, _ *zap
return err
}

if err = selinux.SetLabel(constants.EtcdPKIPath, constants.EtcdPKISELinuxLabel); err != nil {
return err
}

if err = os.WriteFile(constants.EtcdCACert, rootScrts.TypedSpec().EtcdCA.Crt, 0o400); err != nil {
return fmt.Errorf("failed to write CA certificate: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
schedulerv1 "k8s.io/kube-scheduler/config/v1"

"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
)
Expand Down Expand Up @@ -124,17 +125,19 @@ func (ctrl *RenderConfigsStaticPodController) Run(ctx context.Context, r control
)

for _, pod := range []struct {
name string
directory string
uid int
gid int
configs []configFile
name string
directory string
selinuxLabel string
uid int
gid int
configs []configFile
}{
{
name: "kube-apiserver",
directory: constants.KubernetesAPIServerConfigDir,
uid: constants.KubernetesAPIServerRunUser,
gid: constants.KubernetesAPIServerRunGroup,
name: "kube-apiserver",
directory: constants.KubernetesAPIServerConfigDir,
selinuxLabel: constants.KubernetesAPIServerConfigDirSELinuxLabel,
uid: constants.KubernetesAPIServerRunUser,
gid: constants.KubernetesAPIServerRunGroup,
configs: []configFile{
{
filename: "admission-control-config.yaml",
Expand All @@ -147,10 +150,11 @@ func (ctrl *RenderConfigsStaticPodController) Run(ctx context.Context, r control
},
},
{
name: "kube-scheduler",
directory: constants.KubernetesSchedulerConfigDir,
uid: constants.KubernetesSchedulerRunUser,
gid: constants.KubernetesSchedulerRunGroup,
name: "kube-scheduler",
directory: constants.KubernetesSchedulerConfigDir,
selinuxLabel: constants.KubernetesSchedulerConfigDirSELinuxLabel,
uid: constants.KubernetesSchedulerRunUser,
gid: constants.KubernetesSchedulerRunGroup,
configs: []configFile{
{
filename: "scheduler-config.yaml",
Expand All @@ -163,6 +167,10 @@ func (ctrl *RenderConfigsStaticPodController) Run(ctx context.Context, r control
return fmt.Errorf("error creating config directory for %q: %w", pod.name, err)
}

if err = selinux.SetLabel(pod.directory, pod.selinuxLabel); err != nil {
return err
}

for _, configFile := range pod.configs {
var obj runtime.Object

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/siderolabs/gen/xslices"
"go.uber.org/zap"

"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
Expand Down Expand Up @@ -162,18 +163,20 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
}

for _, pod := range []struct {
name string
directory string
uid int
gid int
secrets []secret
templates []template
name string
directory string
selinuxLabel string
uid int
gid int
secrets []secret
templates []template
}{
{
name: "kube-apiserver",
directory: constants.KubernetesAPIServerSecretsDir,
uid: constants.KubernetesAPIServerRunUser,
gid: constants.KubernetesAPIServerRunGroup,
name: "kube-apiserver",
directory: constants.KubernetesAPIServerSecretsDir,
selinuxLabel: constants.KubernetesAPIServerSecretsDirSELinuxLabel,
uid: constants.KubernetesAPIServerRunUser,
gid: constants.KubernetesAPIServerRunGroup,
secrets: []secret{
{
getter: func() *x509.PEMEncodedCertificateAndKey { return rootEtcdSecrets.EtcdCA },
Expand Down Expand Up @@ -230,10 +233,11 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
},
},
{
name: "kube-controller-manager",
directory: constants.KubernetesControllerManagerSecretsDir,
uid: constants.KubernetesControllerManagerRunUser,
gid: constants.KubernetesControllerManagerRunGroup,
name: "kube-controller-manager",
directory: constants.KubernetesControllerManagerSecretsDir,
selinuxLabel: constants.KubernetesControllerManagerSecretsDirSELinuxLabel,
uid: constants.KubernetesControllerManagerRunUser,
gid: constants.KubernetesControllerManagerRunGroup,
secrets: []secret{
{
getter: func() *x509.PEMEncodedCertificateAndKey { return rootK8sSecrets.IssuingCA },
Expand All @@ -258,10 +262,11 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
},
},
{
name: "kube-scheduler",
directory: constants.KubernetesSchedulerSecretsDir,
uid: constants.KubernetesSchedulerRunUser,
gid: constants.KubernetesSchedulerRunGroup,
name: "kube-scheduler",
directory: constants.KubernetesSchedulerSecretsDir,
selinuxLabel: constants.KubernetesSchedulerSecretsDirSELinuxLabel,
uid: constants.KubernetesSchedulerRunUser,
gid: constants.KubernetesSchedulerRunGroup,
templates: []template{
{
filename: "kubeconfig",
Expand All @@ -274,6 +279,10 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
return fmt.Errorf("error creating secrets directory for %q: %w", pod.name, err)
}

if err = selinux.SetLabel(pod.directory, pod.selinuxLabel); err != nil {
return err
}

for _, secret := range pod.secrets {
certAndKey := secret.getter()

Expand Down
5 changes: 5 additions & 0 deletions internal/app/machined/pkg/system/services/apid.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
Expand Down Expand Up @@ -96,6 +97,10 @@ func (o *APID) PreFunc(ctx context.Context, r runtime.Runtime) error {
return err
}

if err := selinux.SetLabel(constants.APIRuntimeSocketPath, constants.APIRuntimeSocketLabel); err != nil {
return err
}

// chown the socket path to make it accessible to the apid
if err := os.Chown(constants.APIRuntimeSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions internal/app/machined/pkg/system/services/machined.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/health"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/grpc/factory"
"github.com/siderolabs/talos/pkg/grpc/middleware/authz"
Expand Down Expand Up @@ -160,6 +161,10 @@ func (s *machinedService) Main(ctx context.Context, r runtime.Runtime, logWriter
return err
}

if err := selinux.SetLabel(constants.MachineSocketPath, constants.MachineSocketLabel); err != nil {
return err
}

// chown the socket path to make it accessible to the apid
if err := os.Chown(constants.MachineSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions internal/app/machined/pkg/system/services/trustd.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd"
"github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart"
"github.com/siderolabs/talos/internal/pkg/environment"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/conditions"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
Expand Down Expand Up @@ -94,6 +95,10 @@ func (t *Trustd) PreFunc(ctx context.Context, r runtime.Runtime) error {
return err
}

if err := selinux.SetLabel(constants.TrustdRuntimeSocketPath, constants.TrustdRuntimeSocketLabel); err != nil {
return err
}

// chown the socket path to make it accessible to the apid
if err := os.Chown(constants.TrustdRuntimeSocketPath, constants.TrustdUserID, constants.TrustdUserID); err != nil {
return err
Expand Down
91 changes: 90 additions & 1 deletion internal/integration/api/selinux.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ import (
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/go-procfs/procfs"

"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
"github.com/siderolabs/talos/internal/integration/base"
machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/constants"
)

Expand Down Expand Up @@ -64,6 +67,92 @@ func (suite *SELinuxSuite) getLabel(nodeCtx context.Context, pid int32) string {
return string(bytes.TrimSpace(value))
}

// TestRuntimeFileLabels reads labels of runtime-created files from xattrs
// to ensure SELinux labels for files are set when they are created.
func (suite *SELinuxSuite) TestRuntimeFileLabels() {
workers := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker)
controlplanes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane)

expectedLabelsWorker := map[string]string{
constants.APIRuntimeSocketPath: constants.APIRuntimeSocketLabel,
constants.APISocketPath: constants.APISocketLabel,
constants.DBusClientSocketPath: constants.DBusClientSocketLabel,
constants.UdevRulesPath: constants.UdevRulesLabel,
constants.DBusServiceSocketPath: constants.DBusServiceSocketLabel,
constants.MachineSocketPath: constants.MachineSocketLabel,
}

expectedLabelsControlPlane := map[string]string{
constants.APIRuntimeSocketPath: constants.APIRuntimeSocketLabel,
constants.APISocketPath: constants.APISocketLabel,
constants.DBusClientSocketPath: constants.DBusClientSocketLabel,
constants.UdevRulesPath: constants.UdevRulesLabel,
constants.DBusServiceSocketPath: constants.DBusServiceSocketLabel,
constants.MachineSocketPath: constants.MachineSocketLabel,
// Only running on controlplane
constants.EtcdPKIPath: constants.EtcdPKISELinuxLabel,
constants.KubernetesAPIServerConfigDir: constants.KubernetesAPIServerConfigDirSELinuxLabel,
constants.KubernetesAPIServerSecretsDir: constants.KubernetesAPIServerSecretsDirSELinuxLabel,
constants.KubernetesControllerManagerSecretsDir: constants.KubernetesControllerManagerSecretsDirSELinuxLabel,
constants.KubernetesSchedulerConfigDir: constants.KubernetesSchedulerConfigDirSELinuxLabel,
constants.KubernetesSchedulerSecretsDir: constants.KubernetesSchedulerSecretsDirSELinuxLabel,
constants.TrustdRuntimeSocketPath: constants.TrustdRuntimeSocketLabel,
}

suite.checkFileLabels(workers, expectedLabelsWorker)
suite.checkFileLabels(controlplanes, expectedLabelsControlPlane)
}

func (suite *SELinuxSuite) checkFileLabels(nodes []string, expectedLabels map[string]string) {
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")
}

// We should check both folders and their contents for proper labels
for _, dir := range []bool{true, false} {
for path, label := range expectedLabels {
req := &machineapi.ListRequest{
Root: path,
ReportXattrs: true,
}
if dir {
req.Types = []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}
}

stream, err := suite.Client.LS(nodeCtx, req)

suite.Require().NoError(err)

suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error {
suite.Require().NotNil(info.Xattrs)

found := false

for _, l := range info.Xattrs {
if l.Name == "security.selinux" {
got := string(bytes.Trim(l.Data, "\x00\n"))
suite.Require().Equal(got, label, "expected %s to have label %s, got %s", path, label, got)

found = true

break
}
}

suite.Require().True(found)

return nil
}))
}
}
}
}

// TestProcessLabels reads labels of system processes from procfs
// to ensure SELinux labels for processes are correctly set
//
Expand Down Expand Up @@ -136,7 +225,7 @@ func (suite *SELinuxSuite) TestProcessLabels() {
}
}

// TODO: test for file labels
// TODO: test for volume 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
Expand Down
11 changes: 11 additions & 0 deletions internal/pkg/logind/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
"time"

"golang.org/x/sync/errgroup"

"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/machinery/constants"
)

// DBusBroker implements simplified D-Bus broker which allows to connect
Expand Down Expand Up @@ -48,11 +51,19 @@ func NewBroker(serviceSocketPath, clientSocketPath string) (*DBusBroker, error)
return nil, err
}

if err = selinux.SetLabel(serviceSocketPath, constants.DBusServiceSocketLabel); err != nil {
return nil, err
}

broker.listenClient, err = net.Listen("unix", clientSocketPath)
if err != nil {
return nil, err
}

if err = selinux.SetLabel(clientSocketPath, constants.DBusClientSocketLabel); err != nil {
return nil, err
}

return broker, nil
}

Expand Down
Binary file modified internal/pkg/selinux/policy/policy.33
Binary file not shown.
Loading

0 comments on commit a867f85

Please sign in to comment.