diff --git a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go index 7a17ed0187..1a59a4f32f 100644 --- a/api/selinuxprofile/v1alpha2/selinuxprofile_types.go +++ b/api/selinuxprofile/v1alpha2/selinuxprofile_types.go @@ -18,11 +18,13 @@ package v1alpha2 import ( "context" + "sort" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" profilebasev1alpha1 "sigs.k8s.io/security-profiles-operator/api/profilebase/v1alpha1" + "sigs.k8s.io/security-profiles-operator/internal/pkg/util" ) const ( @@ -77,11 +79,31 @@ func (lk LabelKey) String() string { type ObjectClassKey string +func (ock ObjectClassKey) String() string { + return string(ock) +} + type PermissionSet []string // Allow defines the allow policy for the profile. type Allow map[LabelKey]map[ObjectClassKey]PermissionSet +func SortLabelKeys(allow Allow) []LabelKey { + keys := util.MapKeys(allow) + sort.SliceStable(keys, func(i, j int) bool { + return keys[i].String() < keys[j].String() + }) + return keys +} + +func SortObjectClassKeys(ock map[ObjectClassKey]PermissionSet) []ObjectClassKey { + keys := util.MapKeys(ock) + sort.SliceStable(keys, func(i, j int) bool { + return keys[i].String() < keys[j].String() + }) + return keys +} + // SelinuxProfileStatus defines the observed state of SelinuxProfile. type SelinuxProfileStatus struct { // Common status fields for all profiles. diff --git a/internal/pkg/daemon/selinuxprofile/common_controller.go b/internal/pkg/daemon/selinuxprofile/common_controller.go index d43a29aec2..2a68d8b13d 100644 --- a/internal/pkg/daemon/selinuxprofile/common_controller.go +++ b/internal/pkg/daemon/selinuxprofile/common_controller.go @@ -320,9 +320,7 @@ func (r *ReconcileSelinux) reconcilePolicyFile( } policyContent := []byte(cil) - l.Info("Writing to policy file", "policyPath", policyPath) - - if err := writeFileIfDiffers(policyPath, policyContent); err != nil { + if err := writeFileIfDiffers(policyPath, policyContent, l); err != nil { return fmt.Errorf("writing policy file: %w", err) } @@ -475,7 +473,7 @@ func selinuxdGetRequest(ctx context.Context, url string) (*http.Response, error) // Reopening the same file may seem wasteful and even look like a TOCTOU issue, but the policy // drop dir is private to this pod, but mostly just calling a single write is much easier codepath // than mucking around with seeks and truncates to account for all the corner cases. -func writeFileIfDiffers(filePath string, contents []byte) error { +func writeFileIfDiffers(filePath string, contents []byte, l logr.Logger) error { const filePermissions = 0o600 file, err := os.OpenFile(filePath, os.O_RDONLY, filePermissions) if os.IsNotExist(err) { @@ -495,5 +493,7 @@ func writeFileIfDiffers(filePath string, contents []byte) error { return nil } + l.Info("Writing to policy file", "policyPath", filePath) + return os.WriteFile(filePath, contents, filePermissions) } diff --git a/internal/pkg/translator/obj2cil.go b/internal/pkg/translator/obj2cil.go index 71e57424b9..27c562266a 100644 --- a/internal/pkg/translator/obj2cil.go +++ b/internal/pkg/translator/obj2cil.go @@ -58,11 +58,13 @@ func Object2CIL( cilbuilder.WriteString(typePermissive) cilbuilder.WriteString("\n") } - for ttype, tclassMap := range sp.Spec.Allow { - for tclass, perms := range tclassMap { - cilbuilder.WriteString(getCILAllowLine(sp, ttype, tclass, perms)) + + for _, ttype := range selxv1alpha2.SortLabelKeys(sp.Spec.Allow) { + for _, tclass := range selxv1alpha2.SortObjectClassKeys(sp.Spec.Allow[ttype]) { + cilbuilder.WriteString(getCILAllowLine(sp, ttype, tclass, sp.Spec.Allow[ttype][tclass])) } } + cilbuilder.WriteString(getCILEnd()) return cilbuilder.String() } diff --git a/internal/pkg/util/maps.go b/internal/pkg/util/maps.go new file mode 100644 index 0000000000..56037ca225 --- /dev/null +++ b/internal/pkg/util/maps.go @@ -0,0 +1,12 @@ +package util + +// To avoid having to use experimental library imports, the below is taken from https://cs.opensource.google/go/x/exp/+/master:maps/maps.go +// Keys returns the keys of the map m. +// The keys will be in an indeterminate order +func MapKeys[M ~map[K]V, K comparable, V any](m M) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +}