From 875bae8341beb7e79601ac5282d72a9d339a6520 Mon Sep 17 00:00:00 2001 From: Sanne Raymaekers Date: Fri, 25 Jul 2025 12:32:14 +0200 Subject: [PATCH 1/2] common: add ValueOrEmpty helper Derefs a pointer, if the pointer is nil returns the default value. --- internal/common/pointers.go | 9 +++++++++ internal/common/pointers_test.go | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/internal/common/pointers.go b/internal/common/pointers.go index 58be2aab1b..aa8efb8947 100644 --- a/internal/common/pointers.go +++ b/internal/common/pointers.go @@ -3,3 +3,12 @@ package common func ToPtr[T any](x T) *T { return &x } + +// ValueOrEmpty returns the value from a given pointer. If ref is nil, +// a zero value of type T will be returned. +func ValueOrEmpty[T any](ref *T) (value T) { + if ref != nil { + value = *ref + } + return +} diff --git a/internal/common/pointers_test.go b/internal/common/pointers_test.go index 49ce018295..43c6d7073c 100644 --- a/internal/common/pointers_test.go +++ b/internal/common/pointers_test.go @@ -24,3 +24,37 @@ func TestToPtr(t *testing.T) { assert.Equal(t, valueStr, *gotStr) } + +func TestValueOrEmpty(t *testing.T) { + var ptrInt *int + valueInt := ValueOrEmpty(ptrInt) + assert.Equal(t, 0, valueInt) + helperInt := 20 + ptrInt = &helperInt + valueInt = ValueOrEmpty(ptrInt) + assert.Equal(t, 20, valueInt) + + var ptrBool *bool + valueBool := ValueOrEmpty(ptrBool) + assert.Equal(t, false, valueBool) + helperBool := true + ptrBool = &helperBool + valueBool = ValueOrEmpty(ptrBool) + assert.Equal(t, true, valueBool) + + var ptrUint64 *uint64 + valueUint64 := ValueOrEmpty(ptrUint64) + assert.Equal(t, uint64(0), valueUint64) + helperUint64 := uint64(20) + ptrUint64 = &helperUint64 + valueUint64 = ValueOrEmpty(ptrUint64) + assert.Equal(t, uint64(20), valueUint64) + + var ptrString *string + valueString := ValueOrEmpty(ptrString) + assert.Equal(t, "", valueString) + helperString := "the-greatest-test-value" + ptrString = &helperString + valueString = ValueOrEmpty(ptrString) + assert.Equal(t, "the-greatest-test-value", valueString) +} From 41d5ec7d01aee39f4313b58d01ca89ce3fb1f637 Mon Sep 17 00:00:00 2001 From: Sanne Raymaekers Date: Fri, 18 Jul 2025 17:58:03 +0200 Subject: [PATCH 2/2] distro: permissive RHC per rhel version in subscription service On rhel 9 and 10 rhc's selinux policy is set managed in the post-install script of the rpm. In the post-install script rhcd_t is set to permissive. As osbuild executes all builds in a bubblewrap container, selinux is not seen as enabled, and the semanage call has no effect. This is why rhcd_t is set to permissive in the registration script, where semange would be effective, as it is executed on the (non-container) host. On rhel 8 the rhcd_t domain is missing, and thus cannot be set to permissive. Thus only set rhcd_t to permissive on rhel 9 and 10. This only applies when a subscription is set in the ImageOptions, and will determine whether or not `semanage permissive --add rhcd_t` is added to the subscription script. --- pkg/distro/defs/rhel-10/distro.yaml | 1 + pkg/distro/defs/rhel-8/distro.yaml | 1 + pkg/distro/defs/rhel-9/distro.yaml | 1 + pkg/distro/generic/images.go | 1 + pkg/distro/image_config.go | 3 ++ pkg/manifest/os.go | 11 ++++- pkg/manifest/os_test.go | 1 + pkg/manifest/subscription.go | 15 +++++-- pkg/manifest/subscription_test.go | 63 +++++++++++++++++++++++++++-- 9 files changed, 89 insertions(+), 8 deletions(-) diff --git a/pkg/distro/defs/rhel-10/distro.yaml b/pkg/distro/defs/rhel-10/distro.yaml index 4d70351ef7..37c396aef5 100644 --- a/pkg/distro/defs/rhel-10/distro.yaml +++ b/pkg/distro/defs/rhel-10/distro.yaml @@ -628,6 +628,7 @@ image_config: default_oscap_datastream: "/usr/share/xml/scap/ssg/content/ssg-rhel10-ds.xml" install_weak_deps: true locale: "C.UTF-8" + permissive_rhc: true sysconfig: networking: true no_zero_conf: true diff --git a/pkg/distro/defs/rhel-8/distro.yaml b/pkg/distro/defs/rhel-8/distro.yaml index 9630e7dcff..c2cbe62e4f 100644 --- a/pkg/distro/defs/rhel-8/distro.yaml +++ b/pkg/distro/defs/rhel-8/distro.yaml @@ -1196,6 +1196,7 @@ image_config: install_weak_deps: true kernel_options_bootloader: true locale: "en_US.UTF-8" + permissive_rhc: false sysconfig: networking: true no_zero_conf: true diff --git a/pkg/distro/defs/rhel-9/distro.yaml b/pkg/distro/defs/rhel-9/distro.yaml index 1eadb2e1b9..5365bdcada 100644 --- a/pkg/distro/defs/rhel-9/distro.yaml +++ b/pkg/distro/defs/rhel-9/distro.yaml @@ -1057,6 +1057,7 @@ image_config: default_oscap_datastream: "/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml" install_weak_deps: true locale: "C.UTF-8" + permissive_rhc: true sysconfig: networking: true no_zero_conf: true diff --git a/pkg/distro/generic/images.go b/pkg/distro/generic/images.go index 88c7260137..4ff575a458 100644 --- a/pkg/distro/generic/images.go +++ b/pkg/distro/generic/images.go @@ -243,6 +243,7 @@ func osCustomizations(t *imageType, osPackageSet rpmmd.PackageSet, options distr if options.Subscription.Proxy != "" { osc.InsightsClientConfig = &osbuild.InsightsClientConfigStageOptions{Config: osbuild.InsightsClientConfig{Proxy: options.Subscription.Proxy}} } + osc.PermissiveRHC = imageConfig.PermissiveRHC } else { subscriptionStatus = subscription.RHSMConfigNoSubscription } diff --git a/pkg/distro/image_config.go b/pkg/distro/image_config.go index 6a646fc30a..26181c36c2 100644 --- a/pkg/distro/image_config.go +++ b/pkg/distro/image_config.go @@ -142,6 +142,9 @@ type ImageConfig struct { // ISOBootType defines what type of bootloader is used for the iso ISOBootType *manifest.ISOBootType `yaml:"iso_boot_type,omitempty"` + // Indicates if rhc should be set to permissive when creating the registration script + PermissiveRHC *bool `yaml:"permissive_rhc,omitempty"` + // VersionlockPackges uses dnf versionlock to lock a package to the version // that is installed during image build, preventing it from being updated. // This is only supported for distributions that use dnf4, because osbuild diff --git a/pkg/manifest/os.go b/pkg/manifest/os.go index 5fca07c44c..dacfdbea12 100644 --- a/pkg/manifest/os.go +++ b/pkg/manifest/os.go @@ -140,6 +140,9 @@ type OSCustomizations struct { OpenSCAPRemediationConfig *oscap.RemediationConfig Subscription *subscription.ImageOptions + // Indicates if rhc should be set to permissive when creating the registration script + PermissiveRHC *bool + // The final RHSM config to be applied to the image RHSMConfig *subscription.RHSMConfig RHSMFacts *facts.ImageOptions @@ -700,7 +703,13 @@ func (p *OS) serialize() osbuild.Pipeline { } if p.OSCustomizations.Subscription != nil { - subStage, subDirs, subFiles, subServices, err := subscriptionService(*p.OSCustomizations.Subscription, &subscriptionServiceOptions{InsightsOnBoot: p.OSTreeRef != ""}) + subStage, subDirs, subFiles, subServices, err := subscriptionService( + *p.OSCustomizations.Subscription, + &subscriptionServiceOptions{ + InsightsOnBoot: p.OSTreeRef != "", + PermissiveRHC: common.ValueOrEmpty(p.OSCustomizations.PermissiveRHC), + }, + ) if err != nil { panic(err) } diff --git a/pkg/manifest/os_test.go b/pkg/manifest/os_test.go index 1e059e6ec4..1bcc5dbae3 100644 --- a/pkg/manifest/os_test.go +++ b/pkg/manifest/os_test.go @@ -114,6 +114,7 @@ func TestRhcInsightsCommands(t *testing.T) { Insights: false, Rhc: true, } + os.OSCustomizations.PermissiveRHC = common.ToPtr(true) pipeline := os.Serialize() CheckSystemdStageOptions(t, pipeline.Stages, []string{ "/usr/bin/rhc connect --organization=${ORG_ID} --activation-key=${ACTIVATION_KEY} --server subscription.rhsm.redhat.com", diff --git a/pkg/manifest/subscription.go b/pkg/manifest/subscription.go index a9edad96d5..c3f641249a 100644 --- a/pkg/manifest/subscription.go +++ b/pkg/manifest/subscription.go @@ -77,6 +77,9 @@ type subscriptionServiceOptions struct { // UnitPath controls the path where the systemd unit will be created, // /usr/lib/systemd or /etc/systemd. UnitPath osbuild.SystemdUnitPath + + // Indicates if rhc should be set to permissive when creating the registration script + PermissiveRHC bool } // subscriptionService creates the necessary stage and modifications to the @@ -86,18 +89,22 @@ type subscriptionServiceOptions struct { // - Register the system with rhc and enable Insights // - Register with subscription-manager, no Insights or rhc // - Register with subscription-manager and enable Insights, no rhc -func subscriptionService(subscriptionOptions subscription.ImageOptions, serviceOptions *subscriptionServiceOptions) (*osbuild.Stage, []*fsnode.Directory, []*fsnode.File, []string, error) { +func subscriptionService( + subscriptionOptions subscription.ImageOptions, + serviceOptions *subscriptionServiceOptions) (*osbuild.Stage, []*fsnode.Directory, []*fsnode.File, []string, error) { dirs := make([]*fsnode.Directory, 0) files := make([]*fsnode.File, 0) services := make([]string, 0) insightsOnBoot := false unitPath := osbuild.UsrUnitPath + permissiveRHC := false if serviceOptions != nil { insightsOnBoot = serviceOptions.InsightsOnBoot if serviceOptions.UnitPath != "" { unitPath = serviceOptions.UnitPath } + permissiveRHC = serviceOptions.PermissiveRHC } // Write a key file that will contain the org ID and activation key to be sourced in the systemd service. @@ -126,8 +133,10 @@ func subscriptionService(subscriptionOptions subscription.ImageOptions, serviceO rhcConnect += fmt.Sprintf(" --content-template=\"%s\"", subscriptionOptions.TemplateName) } commands = append(commands, rhcConnect) - // execute the rhc post install script as the selinuxenabled check doesn't work in the buildroot container - commands = append(commands, "/usr/sbin/semanage permissive --add rhcd_t") + if permissiveRHC { + // execute the rhc post install script as the selinuxenabled check doesn't work in the buildroot container + commands = append(commands, "/usr/sbin/semanage permissive --add rhcd_t") + } // register to template if template uuid is specified if curlToAssociateSystem != "" { commands = append(commands, curlToAssociateSystem) diff --git a/pkg/manifest/subscription_test.go b/pkg/manifest/subscription_test.go index 4b7cc415ba..2b30fde8c2 100644 --- a/pkg/manifest/subscription_test.go +++ b/pkg/manifest/subscription_test.go @@ -186,7 +186,9 @@ func TestSubscriptionService(t *testing.T) { Insights: false, Rhc: true, }, - srvcOpts: nil, + srvcOpts: &subscriptionServiceOptions{ + PermissiveRHC: true, + }, expectedStage: &osbuild.Stage{ Type: stageType, Options: &osbuild.SystemdUnitCreateStageOptions{ @@ -232,7 +234,9 @@ func TestSubscriptionService(t *testing.T) { Insights: true, Rhc: true, }, - srvcOpts: nil, + srvcOpts: &subscriptionServiceOptions{ + PermissiveRHC: true, + }, expectedStage: &osbuild.Stage{ Type: stageType, Options: &osbuild.SystemdUnitCreateStageOptions{ @@ -269,6 +273,53 @@ func TestSubscriptionService(t *testing.T) { expectedDirs: make([]*fsnode.Directory, 0), expectedServices: []string{serviceFilename}, }, + "with-rhc-no-permissive": { + subOpts: subscription.ImageOptions{ + Organization: "theorg-wr", + ActivationKey: "thekey-wr", + ServerUrl: "theserverurl-wr", + BaseUrl: "thebaseurl-wr", + Insights: false, + Rhc: true, + }, + srvcOpts: &subscriptionServiceOptions{ + PermissiveRHC: false, + }, + expectedStage: &osbuild.Stage{ + Type: stageType, + Options: &osbuild.SystemdUnitCreateStageOptions{ + Filename: serviceFilename, + UnitType: unitType, + UnitPath: osbuild.UsrUnitPath, + Config: osbuild.SystemdUnit{ + Unit: &osbuild.UnitSection{ + Description: serviceDescription, + ConditionPathExists: []string{ + subkeyFilepath, + }, + Wants: serviceWants, + After: serviceAfter, + }, + Service: &osbuild.ServiceSection{ + Type: osbuild.OneshotServiceType, + ExecStart: []string{ + "/usr/bin/rhc connect --organization=${ORG_ID} --activation-key=${ACTIVATION_KEY} --server theserverurl-wr", + "/usr/bin/rm " + subkeyFilepath, + }, + EnvironmentFile: []string{ + subkeyFilepath, + }, + }, + Install: &osbuild.InstallSection{ + WantedBy: serviceWantedBy, + }, + }, + }, + }, + expectedFiles: []*fsnode.File{mkKeyfile("theorg-wr", "thekey-wr")}, + expectedDirs: make([]*fsnode.Directory, 0), + expectedServices: []string{serviceFilename}, + }, "with-insights-rhc-template-name": { subOpts: subscription.ImageOptions{ Organization: "theorg-wir", @@ -279,7 +330,9 @@ func TestSubscriptionService(t *testing.T) { Rhc: true, TemplateName: "template name", }, - srvcOpts: nil, + srvcOpts: &subscriptionServiceOptions{ + PermissiveRHC: true, + }, expectedStage: &osbuild.Stage{ Type: stageType, Options: &osbuild.SystemdUnitCreateStageOptions{ @@ -329,7 +382,9 @@ func TestSubscriptionService(t *testing.T) { Proxy: "proxy-url", PatchURL: "https://cert.console.redhat.com/api/patch/v3/", }, - srvcOpts: nil, + srvcOpts: &subscriptionServiceOptions{ + PermissiveRHC: true, + }, expectedStage: &osbuild.Stage{ Type: stageType, Options: &osbuild.SystemdUnitCreateStageOptions{