From e899fb37fde7ec2b39d4b0fa77c7a6b1ac9d9f16 Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Thu, 21 Nov 2024 20:55:36 +0100 Subject: [PATCH] feat: label created files in /etc Implement SELinux labeling support in EtcFileController, label both squashfs and runtime-created files in /etc and /system/etc. Add corresponding test cases. Signed-off-by: Dmitry Sharshakov --- api/resource/definitions/files/files.proto | 1 + .../pkg/controllers/cluster/node_identity.go | 1 + .../pkg/controllers/files/cri_config_parts.go | 1 + .../controllers/files/cri_registry_config.go | 1 + .../machined/pkg/controllers/files/etcfile.go | 14 ++++-- .../pkg/controllers/network/etcfile.go | 5 +- .../v1alpha1/v1alpha1_sequencer_tasks.go | 1 + internal/app/machined/pkg/startup/tasks.go | 2 +- internal/integration/api/selinux.go | 5 +- internal/pkg/selinux/policy/file_contexts | 2 + internal/pkg/selinux/policy/policy.33 | Bin 26819 -> 26812 bytes .../selinux/policy/selinux/common/files.cil | 12 +++-- .../resource/definitions/files/files.pb.go | 42 ++++++++++------- .../definitions/files/files_vtproto.pb.go | 43 ++++++++++++++++++ pkg/machinery/constants/constants.go | 4 +- pkg/machinery/resources/files/etcfile_spec.go | 5 +- website/content/v1.9/reference/api.md | 1 + 17 files changed, 108 insertions(+), 32 deletions(-) diff --git a/api/resource/definitions/files/files.proto b/api/resource/definitions/files/files.proto index 5c14d4c2f7..832e2f501d 100755 --- a/api/resource/definitions/files/files.proto +++ b/api/resource/definitions/files/files.proto @@ -9,6 +9,7 @@ option java_package = "dev.talos.api.resource.definitions.files"; message EtcFileSpecSpec { bytes contents = 1; uint32 mode = 2; + string selinux_label = 3; } // EtcFileStatusSpec describes status of rendered secrets. diff --git a/internal/app/machined/pkg/controllers/cluster/node_identity.go b/internal/app/machined/pkg/controllers/cluster/node_identity.go index db4e9a9678..e1906adf69 100644 --- a/internal/app/machined/pkg/controllers/cluster/node_identity.go +++ b/internal/app/machined/pkg/controllers/cluster/node_identity.go @@ -115,6 +115,7 @@ func (ctrl *NodeIdentityController) Run(ctx context.Context, r controller.Runtim r.TypedSpec().Contents, err = clusteradapter.IdentitySpec(&localIdentity).ConvertMachineID() r.TypedSpec().Mode = 0o444 + r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return err }); err != nil { diff --git a/internal/app/machined/pkg/controllers/files/cri_config_parts.go b/internal/app/machined/pkg/controllers/files/cri_config_parts.go index b0734a3ab1..3d93b63d7d 100644 --- a/internal/app/machined/pkg/controllers/files/cri_config_parts.go +++ b/internal/app/machined/pkg/controllers/files/cri_config_parts.go @@ -83,6 +83,7 @@ func (ctrl *CRIConfigPartsController) Run(ctx context.Context, r controller.Runt spec.Contents = out spec.Mode = 0o600 + spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { diff --git a/internal/app/machined/pkg/controllers/files/cri_registry_config.go b/internal/app/machined/pkg/controllers/files/cri_registry_config.go index 21f77abd19..560f07dcdd 100644 --- a/internal/app/machined/pkg/controllers/files/cri_registry_config.go +++ b/internal/app/machined/pkg/controllers/files/cri_registry_config.go @@ -118,6 +118,7 @@ func (ctrl *CRIRegistryConfigController) Run(ctx context.Context, r controller.R spec.Contents = criRegistryContents spec.Mode = 0o600 + spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { diff --git a/internal/app/machined/pkg/controllers/files/etcfile.go b/internal/app/machined/pkg/controllers/files/etcfile.go index fa95287436..b45a993d81 100644 --- a/internal/app/machined/pkg/controllers/files/etcfile.go +++ b/internal/app/machined/pkg/controllers/files/etcfile.go @@ -18,6 +18,7 @@ import ( "go.uber.org/zap" "golang.org/x/sys/unix" + "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) @@ -133,7 +134,7 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, lo logger.Debug("writing file contents", zap.String("dst", dst), zap.Stringer("version", spec.Metadata().Version())) - if err = UpdateFile(dst, spec.TypedSpec().Contents, spec.TypedSpec().Mode); err != nil { + if err = UpdateFile(dst, spec.TypedSpec().Contents, spec.TypedSpec().Mode, spec.TypedSpec().SelinuxLabel); err != nil { return fmt.Errorf("error updating %q: %w", dst, err) } @@ -194,11 +195,16 @@ func createBindMount(src, dst string, mode os.FileMode) (err error) { // UpdateFile is like `os.WriteFile`, but it will only update the file if the // contents have changed. -func UpdateFile(filename string, contents []byte, mode os.FileMode) error { +func UpdateFile(filename string, contents []byte, mode os.FileMode, selinuxLabel string) error { oldContents, err := os.ReadFile(filename) if err == nil && bytes.Equal(oldContents, contents) { - return nil + return selinux.SetLabel(filename, selinuxLabel) } - return os.WriteFile(filename, contents, mode) + err = os.WriteFile(filename, contents, mode) + if err != nil { + return err + } + + return selinux.SetLabel(filename, selinuxLabel) } diff --git a/internal/app/machined/pkg/controllers/network/etcfile.go b/internal/app/machined/pkg/controllers/network/etcfile.go index 0cb0f245f4..140d72e3eb 100644 --- a/internal/app/machined/pkg/controllers/network/etcfile.go +++ b/internal/app/machined/pkg/controllers/network/etcfile.go @@ -27,6 +27,7 @@ import ( efiles "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" + "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/network" @@ -150,6 +151,7 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, _ func(r *files.EtcFileSpec) error { r.TypedSpec().Contents = renderResolvConf(pickNameservers(hostDNSCfg, resolverStatus), hostnameStatusSpec, cfgProvider) r.TypedSpec().Mode = 0o644 + r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { @@ -173,7 +175,7 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, _ return fmt.Errorf("error creating pod resolv.conf dir: %w", err) } - err = efiles.UpdateFile(ctrl.PodResolvConfPath, conf, 0o644) + err = efiles.UpdateFile(ctrl.PodResolvConfPath, conf, 0o644, constants.EtcSelinuxLabel) if err != nil { return fmt.Errorf("error writing pod resolv.conf: %w", err) } @@ -184,6 +186,7 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, _ func(r *files.EtcFileSpec) error { r.TypedSpec().Contents, err = ctrl.renderHosts(hostnameStatus.TypedSpec(), nodeAddressStatus.TypedSpec(), cfgProvider) r.TypedSpec().Mode = 0o644 + r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return err }); err != nil { diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index 2b922a8e3d..ab5a21aa90 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -825,6 +825,7 @@ func injectCRIConfigPatch(ctx context.Context, st state.State, content []byte) e etcFileSpec := resourcefiles.NewEtcFileSpec(resourcefiles.NamespaceName, constants.CRICustomizationConfigPart) etcFileSpec.TypedSpec().Mode = 0o600 etcFileSpec.TypedSpec().Contents = content + etcFileSpec.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel if err := st.Create(ctx, etcFileSpec); err != nil { return err diff --git a/internal/app/machined/pkg/startup/tasks.go b/internal/app/machined/pkg/startup/tasks.go index cd4744e782..00a84c2388 100644 --- a/internal/app/machined/pkg/startup/tasks.go +++ b/internal/app/machined/pkg/startup/tasks.go @@ -40,7 +40,7 @@ func SetupSystemDirectories(ctx context.Context, log *zap.Logger, rt runtime.Run switch path { case constants.SystemEtcPath: - label = constants.SystemEtcSelinuxLabel + label = constants.EtcSelinuxLabel case constants.SystemVarPath: label = constants.SystemVarSelinuxLabel default: // /system/state is another mount diff --git a/internal/integration/api/selinux.go b/internal/integration/api/selinux.go index 7c07e841e5..a28749043e 100644 --- a/internal/integration/api/selinux.go +++ b/internal/integration/api/selinux.go @@ -81,7 +81,6 @@ func (suite *SELinuxSuite) TestFileMountLabels() { constants.SystemPath: constants.SystemSelinuxLabel, constants.EphemeralMountPoint: constants.EphemeralSelinuxLabel, constants.StateMountPoint: constants.StateSelinuxLabel, - constants.SystemEtcPath: constants.SystemEtcSelinuxLabel, constants.SystemVarPath: constants.SystemVarSelinuxLabel, constants.RunPath: constants.RunSelinuxLabel, "/var/run": constants.RunSelinuxLabel, @@ -102,6 +101,9 @@ func (suite *SELinuxSuite) TestFileMountLabels() { // Directories "/var/lib/containerd": "system_u:object_r:containerd_state_t:s0", "/var/lib/kubelet": "system_u:object_r:kubelet_state_t:s0", + // Mounts and runtime-generated files + constants.SystemEtcPath: constants.EtcSelinuxLabel, + "/etc": constants.EtcSelinuxLabel, } // Only running on controlplane @@ -254,7 +256,6 @@ func (suite *SELinuxSuite) TestProcessLabels() { } } -// TODO: test for all machined-created files // TODO: test for system and CRI container labels // TODO: test labels for unconfined system extensions, pods // TODO: test for no avc denials in dmesg diff --git a/internal/pkg/selinux/policy/file_contexts b/internal/pkg/selinux/policy/file_contexts index 47dd3491c2..6e34ca45f8 100644 --- a/internal/pkg/selinux/policy/file_contexts +++ b/internal/pkg/selinux/policy/file_contexts @@ -1,3 +1,4 @@ +/etc(/.*)? system_u:object_r:etc_t:s0 /opt(/.*)? system_u:object_r:opt_t:s0 /sbin(/.*)? system_u:object_r:sbin_exec_t:s0 /etc/cni(/.*)? system_u:object_r:cni_conf_t:s0 @@ -6,6 +7,7 @@ /usr/lib/udev(/.*)? system_u:object_r:udev_exec_t:s0 /etc/kubernetes(/.*)? system_u:object_r:k8s_conf_t:s0 /opt/containerd(/.*)? system_u:object_r:containerd_plugin_t:s0 +/usr/share/zoneinfo(/.*)? system_u:object_r:etc_t:s0 /usr/lib/udev/rules.d(/.*)? system_u:object_r:udev_rules_t:s0 /usr/libexec/kubernetes(/.*)? system_u:object_r:k8s_plugin_t:s0 / system_u:object_r:rootfs_t:s0 diff --git a/internal/pkg/selinux/policy/policy.33 b/internal/pkg/selinux/policy/policy.33 index 9c2fd604ce053d1d69489fa4e6f0c7d99b651d66..fa193b47cf72a5225d3e5023ca7554d39fb9dda2 100644 GIT binary patch delta 39 pcmX?nk#WyO#tkdAg;^OG7?^;V5r{z`wIq4-a_vm!%{uziEdkD63C;ij delta 48 ycmdmUk@4_F#tkdAH}BI 0 { + i -= len(m.SelinuxLabel) + copy(dAtA[i:], m.SelinuxLabel) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SelinuxLabel))) + i-- + dAtA[i] = 0x1a + } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- @@ -117,6 +124,10 @@ func (m *EtcFileSpecSpec) SizeVT() (n int) { if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } + l = len(m.SelinuxLabel) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -217,6 +228,38 @@ func (m *EtcFileSpecSpec) UnmarshalVT(dAtA []byte) error { break } } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SelinuxLabel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SelinuxLabel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/pkg/machinery/constants/constants.go b/pkg/machinery/constants/constants.go index 52913712c3..752a0acbba 100644 --- a/pkg/machinery/constants/constants.go +++ b/pkg/machinery/constants/constants.go @@ -723,8 +723,8 @@ const ( // SystemEtcPath is the path to the system etc directory. SystemEtcPath = SystemPath + "/etc" - // SystemEtcSelinuxLabel is the SELinux label for the system etc directory. - SystemEtcSelinuxLabel = "system_u:object_r:system_etc_t:s0" + // EtcSelinuxLabel is the SELinux label for the /etc and /system/etc directories. + EtcSelinuxLabel = "system_u:object_r:etc_t:s0" // SystemLibexecPath is the path to the system libexec directory. SystemLibexecPath = SystemPath + "/libexec" diff --git a/pkg/machinery/resources/files/etcfile_spec.go b/pkg/machinery/resources/files/etcfile_spec.go index c0178a858a..e509141b3e 100644 --- a/pkg/machinery/resources/files/etcfile_spec.go +++ b/pkg/machinery/resources/files/etcfile_spec.go @@ -27,8 +27,9 @@ type EtcFileSpec = typed.Resource[EtcFileSpecSpec, EtcFileSpecExtension] // //gotagsrewrite:gen type EtcFileSpecSpec struct { - Contents []byte `yaml:"contents" protobuf:"1"` - Mode fs.FileMode `yaml:"mode" protobuf:"2"` + Contents []byte `yaml:"contents" protobuf:"1"` + Mode fs.FileMode `yaml:"mode" protobuf:"2"` + SelinuxLabel string `yaml:"selinux_label" protobuf:"3"` } // NewEtcFileSpec initializes a EtcFileSpec resource. diff --git a/website/content/v1.9/reference/api.md b/website/content/v1.9/reference/api.md index 2676ec226c..2c5ea5bf62 100644 --- a/website/content/v1.9/reference/api.md +++ b/website/content/v1.9/reference/api.md @@ -2213,6 +2213,7 @@ EtcFileSpecSpec describes status of rendered secrets. | ----- | ---- | ----- | ----------- | | contents | [bytes](#bytes) | | | | mode | [uint32](#uint32) | | | +| selinux_label | [string](#string) | | |