diff --git a/go.mod b/go.mod index dc09979cf..24d273600 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/labstack/gommon v0.4.2 github.com/openshift-online/ocm-sdk-go v0.1.438 github.com/oracle/oci-go-sdk/v54 v54.0.0 - github.com/osbuild/images v0.82.0 + github.com/osbuild/images v0.83.0 github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d github.com/osbuild/pulp-client v0.1.0 github.com/prometheus/client_golang v1.20.2 diff --git a/go.sum b/go.sum index 0ce331e9f..dba2c8b0e 100644 --- a/go.sum +++ b/go.sum @@ -510,8 +510,8 @@ github.com/openshift-online/ocm-sdk-go v0.1.438 h1:tsLCCUzbLCTL4RZG02y9RuopmGCXp github.com/openshift-online/ocm-sdk-go v0.1.438/go.mod h1:CiAu2jwl3ITKOxkeV0Qnhzv4gs35AmpIzVABQLtcI2Y= github.com/oracle/oci-go-sdk/v54 v54.0.0 h1:CDLjeSejv2aDpElAJrhKpi6zvT/zhZCZuXchUUZ+LS4= github.com/oracle/oci-go-sdk/v54 v54.0.0/go.mod h1:+t+yvcFGVp+3ZnztnyxqXfQDsMlq8U25faBLa+mqCMc= -github.com/osbuild/images v0.82.0 h1:bWfcGHHQR6pYZnv4jAxmLWxEkw669Zb6C2ADcyuf49g= -github.com/osbuild/images v0.82.0/go.mod h1:1kJyvTtEbJfRv00phwd9Dlkai4/V05JhNACglxFTxS8= +github.com/osbuild/images v0.83.0 h1:TFz9/nlueUK0dI3HpRCeUuT+2CeNTnejR/vGlzel1lE= +github.com/osbuild/images v0.83.0/go.mod h1:1kJyvTtEbJfRv00phwd9Dlkai4/V05JhNACglxFTxS8= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d h1:r9BFPDv0uuA9k1947Jybcxs36c/pTywWS1gjeizvtcQ= github.com/osbuild/osbuild-composer/pkg/splunk_logger v0.0.0-20240814102216-0239db53236d/go.mod h1:zR1iu/hOuf+OQNJlk70tju9IqzzM4ycq0ectkFBm94U= github.com/osbuild/pulp-client v0.1.0 h1:L0C4ezBJGTamN3BKdv+rKLuq/WxXJbsFwz/Hj7aEmJ8= diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go index 884ff24cd..7a20febcc 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/images.go @@ -145,7 +145,7 @@ func osCustomizations( } if t.IsRHEL() && options.Facts != nil { - osc.FactAPIType = &options.Facts.APIType + osc.RHSMFacts = options.Facts } var err error @@ -467,6 +467,7 @@ func EdgeInstallerImage(workload workload.Workload, img.Platform = t.platform img.ExtraBasePackages = packageSets[InstallerPkgsKey] + img.Subscription = options.Subscription if t.Arch().Distro().Releasever() == "8" { // NOTE: RHEL 8 only supports the older Anaconda configs diff --git a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/ami.go b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/ami.go index 830cd4d30..22026d27e 100644 --- a/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/ami.go +++ b/vendor/github.com/osbuild/images/pkg/distro/rhel/rhel10/ami.go @@ -96,15 +96,6 @@ func baseEc2ImageConfig() *distro.ImageConfig { }, }, }, - // COMPOSER-1807 - DracutConf: []*osbuild.DracutConfStageOptions{ - { - Filename: "sgdisk.conf", - Config: osbuild.DracutConfigFile{ - Install: []string{"sgdisk"}, - }, - }, - }, SystemdUnit: []*osbuild.SystemdUnitStageOptions{ // RHBZ#1822863 { diff --git a/vendor/github.com/osbuild/images/pkg/image/anaconda_live_installer.go b/vendor/github.com/osbuild/images/pkg/image/anaconda_live_installer.go index 9455fac42..b1acc99b5 100644 --- a/vendor/github.com/osbuild/images/pkg/image/anaconda_live_installer.go +++ b/vendor/github.com/osbuild/images/pkg/image/anaconda_live_installer.go @@ -65,6 +65,9 @@ func (img *AnacondaLiveInstaller) InstantiateManifest(m *manifest.Manifest, livePipeline.Variant = img.Variant livePipeline.Biosdevname = (img.Platform.GetArch() == arch.ARCH_X86_64) + // The live installer has SElinux enabled and targeted + livePipeline.SElinux = "targeted" + livePipeline.Checkpoint() rootfsImagePipeline := manifest.NewISORootfsImg(buildPipeline, livePipeline) diff --git a/vendor/github.com/osbuild/images/pkg/image/anaconda_ostree_installer.go b/vendor/github.com/osbuild/images/pkg/image/anaconda_ostree_installer.go index a88edaebe..cba994e7e 100644 --- a/vendor/github.com/osbuild/images/pkg/image/anaconda_ostree_installer.go +++ b/vendor/github.com/osbuild/images/pkg/image/anaconda_ostree_installer.go @@ -9,6 +9,7 @@ import ( "github.com/osbuild/images/pkg/artifact" "github.com/osbuild/images/pkg/customizations/anaconda" "github.com/osbuild/images/pkg/customizations/kickstart" + "github.com/osbuild/images/pkg/customizations/subscription" "github.com/osbuild/images/pkg/manifest" "github.com/osbuild/images/pkg/osbuild" "github.com/osbuild/images/pkg/ostree" @@ -24,6 +25,9 @@ type AnacondaOSTreeInstaller struct { Kickstart *kickstart.Options + // Subscription options to include + Subscription *subscription.ImageOptions + SquashfsCompression string ISOLabel string @@ -119,6 +123,12 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest, // enable ISOLinux on x86_64 only isoLinuxEnabled := img.Platform.GetArch() == arch.ARCH_X86_64 + var subscriptionPipeline *manifest.Subscription + if img.Subscription != nil { + // pipeline that will create subscription service and key file to be copied out + subscriptionPipeline = manifest.NewSubscription(buildPipeline, img.Subscription) + } + isoTreePipeline := manifest.NewAnacondaInstallerISOTree(buildPipeline, anacondaPipeline, rootfsImagePipeline, bootTreePipeline) isoTreePipeline.PartitionTable = efiBootPartitionTable(rng) isoTreePipeline.Release = img.Release @@ -132,6 +142,7 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest, if img.FIPS { isoTreePipeline.KernelOpts = append(isoTreePipeline.KernelOpts, "fips=1") } + isoTreePipeline.SubscriptionPipeline = subscriptionPipeline isoPipeline := manifest.NewISO(buildPipeline, isoTreePipeline, img.ISOLabel) isoPipeline.SetFilename(img.Filename) diff --git a/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer.go b/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer.go index 6a326280f..bb82f5556 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer.go @@ -84,6 +84,11 @@ type AnacondaInstaller struct { // Uses the old, deprecated, Anaconda config option "kickstart-modules". // Only for RHEL 8. UseLegacyAnacondaConfig bool + + // SELinux policy, when set it enables the labeling of the installer + // tree with the selected profile and selects the required package + // for depsolving + SElinux string } func NewAnacondaInstaller(installerType AnacondaInstallerType, @@ -159,14 +164,24 @@ func (p *AnacondaInstaller) getBuildPackages(Distro) []string { ) } + if p.SElinux != "" { + packages = append(packages, "policycoreutils", fmt.Sprintf("selinux-policy-%s", p.SElinux)) + } + return packages } func (p *AnacondaInstaller) getPackageSetChain(Distro) []rpmmd.PackageSet { packages := p.anacondaBootPackageSet() + if p.Biosdevname { packages = append(packages, "biosdevname") } + + if p.SElinux != "" { + packages = append(packages, fmt.Sprintf("selinux-policy-%s", p.SElinux)) + } + return []rpmmd.PackageSet{ { Include: append(packages, p.ExtraPackages...), @@ -306,6 +321,13 @@ func (p *AnacondaInstaller) payloadStages() []*osbuild.Stage { stages = append(stages, osbuild.NewSELinuxConfigStage(&osbuild.SELinuxConfigStageOptions{State: osbuild.SELinuxStatePermissive})) + // SElinux is not supported on the non-live-installers (see the previous + // stage setting SELinux to permissive. It's an error to set it to anything + // that isn't an empty string + if p.SElinux != "" { + panic("payload installers do not support SELinux policies") + } + if p.InteractiveDefaults != nil { var ksUsers []users.User var ksGroups []users.Group @@ -370,7 +392,11 @@ func (p *AnacondaInstaller) liveStages() []*osbuild.Stage { dracutOptions.AddDrivers = p.AdditionalDrivers stages = append(stages, osbuild.NewDracutStage(dracutOptions)) - stages = append(stages, osbuild.NewSELinuxConfigStage(&osbuild.SELinuxConfigStageOptions{State: osbuild.SELinuxStatePermissive})) + if p.SElinux != "" { + stages = append(stages, osbuild.NewSELinuxStage(&osbuild.SELinuxStageOptions{ + FileContexts: fmt.Sprintf("etc/selinux/%s/contexts/files/file_contexts", p.SElinux), + })) + } return stages } diff --git a/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer_iso_tree.go b/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer_iso_tree.go index 269d248f4..3ebe27c55 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer_iso_tree.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/anaconda_installer_iso_tree.go @@ -3,6 +3,7 @@ package manifest import ( "fmt" "path" + "path/filepath" "sort" "strings" @@ -58,6 +59,10 @@ type AnacondaInstallerISOTree struct { Kickstart *kickstart.Options Files []*fsnode.File + + // Pipeline object where subscription-related files are created for copying + // onto the ISO. + SubscriptionPipeline *Subscription } func NewAnacondaInstallerISOTree(buildPipeline Build, anacondaPipeline *AnacondaInstaller, rootfsPipeline *ISORootfsImg, bootTreePipeline *EFIBootTree) *AnacondaInstallerISOTree { @@ -446,7 +451,7 @@ func (p *AnacondaInstallerISOTree) bootcInstallerKickstartStages() []*osbuild.St if err != nil { panic(err) } - p.Files = []*fsnode.File{kickstartFile} + p.Files = append(p.Files, kickstartFile) return append(stages, osbuild.GenFileNodesStages(p.Files)...) } @@ -516,8 +521,7 @@ bootc switch --mutate-in-place --transport %s %s panic(err) } - p.Files = []*fsnode.File{kickstartFile} - + p.Files = append(p.Files, kickstartFile) return append(stages, osbuild.GenFileNodesStages(p.Files)...) } @@ -570,9 +574,7 @@ func (p *AnacondaInstallerISOTree) makeKickstartStages(stageOptions *osbuild.Kic panic(err) } - p.Files = []*fsnode.File{kickstartFile} - - stages = append(stages, osbuild.GenFileNodesStages(p.Files)...) + p.Files = append(p.Files, kickstartFile) } } @@ -610,7 +612,44 @@ func (p *AnacondaInstallerISOTree) makeKickstartStages(stageOptions *osbuild.Kic stages = append(stages, osbuild.NewKickstartStage(stageOptions)) - hardcodedKickstartBits := makeKickstartSudoersPost(kickstartOptions.SudoNopasswd) + hardcodedKickstartBits := "" + hardcodedKickstartBits += makeKickstartSudoersPost(kickstartOptions.SudoNopasswd) + + if p.SubscriptionPipeline != nil { + subscriptionPath := "/subscription" + stages = append(stages, osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{Paths: []osbuild.MkdirStagePath{{Path: subscriptionPath, Parents: true, ExistOk: true}}})) + inputName := "subscription-tree" + copyInputs := osbuild.NewPipelineTreeInputs(inputName, p.SubscriptionPipeline.Name()) + copyOptions := &osbuild.CopyStageOptions{} + copyOptions.Paths = append(copyOptions.Paths, + osbuild.CopyStagePath{ + From: fmt.Sprintf("input://%s/", inputName), + To: fmt.Sprintf("tree://%s/", subscriptionPath), + }, + ) + stages = append(stages, osbuild.NewCopyStageSimple(copyOptions, copyInputs)) + systemPath := "/mnt/sysimage" + if p.ostreeCommitSpec != nil || p.containerSpec != nil { + // ostree based system: use /mnt/sysroot instead + systemPath = "/mnt/sysroot" + + } + hardcodedKickstartBits += makeKickstartSubscriptionPost(subscriptionPath, systemPath) + + // include a readme file on the ISO in the subscription path to explain what it's for + subscriptionReadme, err := fsnode.NewFile( + filepath.Join(subscriptionPath, "README"), + nil, nil, nil, + []byte(`Subscription services and credentials + +This directory contains files necessary for registering the system on first boot after installation. These files are copied to the installed system and services are enabled to activate the subscription on boot.`), + ) + if err != nil { + panic(err) + } + p.Files = append(p.Files, subscriptionReadme) + } + if hardcodedKickstartBits != "" { // Because osbuild core only supports a subset of options, // we append to the base here with hardcoded wheel group with NOPASSWD option @@ -619,10 +658,10 @@ func (p *AnacondaInstallerISOTree) makeKickstartStages(stageOptions *osbuild.Kic panic(err) } - p.Files = []*fsnode.File{kickstartFile} - - stages = append(stages, osbuild.GenFileNodesStages(p.Files)...) + p.Files = append(p.Files, kickstartFile) } + stages = append(stages, osbuild.GenFileNodesStages(p.Files)...) + return stages } @@ -660,3 +699,17 @@ restorecon -rvF /etc/sudoers.d return fmt.Sprintf(kickstartSudoersPost, strings.Join(entries, "\n")) } + +func makeKickstartSubscriptionPost(source, dest string) string { + // we need to use --nochroot so the command can access files on the ISO + fullSourcePath := filepath.Join("/run/install/repo", source, "etc/*") + kickstartSubscriptionPost := ` +%%post --nochroot +cp -r %s %s +%%end +%%post +systemctl enable osbuild-subscription-register.service +%%end +` + return fmt.Sprintf(kickstartSubscriptionPost, fullSourcePath, dest) +} diff --git a/vendor/github.com/osbuild/images/pkg/manifest/os.go b/vendor/github.com/osbuild/images/pkg/manifest/os.go index 7d6181bdf..91911f84c 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/os.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/os.go @@ -5,6 +5,8 @@ import ( "path/filepath" "strings" + "github.com/google/uuid" + "github.com/osbuild/images/internal/common" "github.com/osbuild/images/internal/environment" "github.com/osbuild/images/internal/workload" @@ -124,7 +126,6 @@ type OSCustomizations struct { UdevRules *osbuild.UdevRulesStageOptions WSLConfig *osbuild.WSLConfStageOptions LeapSecTZ *string - FactAPIType *facts.APIType Presets []osbuild.Preset ContainersStorage *string @@ -134,6 +135,7 @@ type OSCustomizations struct { Subscription *subscription.ImageOptions // The final RHSM config to be applied to the image RHSMConfig *subscription.RHSMConfig + RHSMFacts *facts.ImageOptions // Custom directories and files to create in the image Directories []*fsnode.Directory @@ -424,6 +426,13 @@ func (p *OS) serialize() osbuild.Pipeline { if p.OSTreeRef != "" { rpmOptions.OSTreeBooted = common.ToPtr(true) rpmOptions.DBPath = "/usr/share/rpm" + // The dracut-config-rescue package will create a rescue kernel when + // installed. This creates an issue with ostree-based images because + // rpm-ostree requires that only one kernel exists in the image. + // Disabling dracut for ostree-based systems resolves this issue. + // Dracut will be run by rpm-ostree itself while composing the image. + // https://github.com/osbuild/images/issues/624 + rpmOptions.DisableDracut = true } pipeline.AddStage(osbuild.NewRPMStage(rpmOptions, osbuild.NewRpmStageSourceFilesInputs(p.packageSpecs))) @@ -580,74 +589,15 @@ func (p *OS) serialize() osbuild.Pipeline { pipeline.AddStage(osbuild.NewPwqualityConfStage(p.PwQuality)) } - // If subscription settings are included there are 3 possible setups: - // - 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 if p.Subscription != nil { - // Write a key file that will contain the org ID and activation key to be sourced in the systemd service. - // The file will also act as the ConditionFirstBoot file. - subkeyFilepath := "/etc/osbuild-subscription-register.env" - subkeyContent := fmt.Sprintf("ORG_ID=%s\nACTIVATION_KEY=%s", p.Subscription.Organization, p.Subscription.ActivationKey) - if subkeyFile, err := fsnode.NewFile(subkeyFilepath, nil, "root", "root", []byte(subkeyContent)); err == nil { - p.Files = append(p.Files, subkeyFile) - } else { + subStage, subDirs, subFiles, subServices, err := subscriptionService(*p.Subscription, &subscriptionServiceOptions{InsightsOnBoot: p.OSTreeRef != ""}) + if err != nil { panic(err) } - - var commands []string - if p.Subscription.Rhc { - // TODO: replace org ID and activation key with env vars - // Use rhc for registration instead of subscription manager - commands = []string{fmt.Sprintf("/usr/bin/rhc connect --organization=${ORG_ID} --activation-key=${ACTIVATION_KEY} --server %s", p.Subscription.ServerUrl)} - // insights-client creates the .gnupg directory during boot process, and is labeled incorrectly - commands = append(commands, "restorecon -R /root/.gnupg") - // 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 p.OSTreeRef != "" { - p.runInsightsClientOnBoot() - } - } else { - commands = []string{fmt.Sprintf("/usr/sbin/subscription-manager register --org=${ORG_ID} --activationkey=${ACTIVATION_KEY} --serverurl %s --baseurl %s", p.Subscription.ServerUrl, p.Subscription.BaseUrl)} - - // Insights is optional when using subscription-manager - if p.Subscription.Insights { - commands = append(commands, "/usr/bin/insights-client --register") - // insights-client creates the .gnupg directory during boot process, and is labeled incorrectly - commands = append(commands, "restorecon -R /root/.gnupg") - if p.OSTreeRef != "" { - p.runInsightsClientOnBoot() - } - } - } - - commands = append(commands, fmt.Sprintf("/usr/bin/rm %s", subkeyFilepath)) - - subscribeServiceFile := "osbuild-subscription-register.service" - regServiceStageOptions := &osbuild.SystemdUnitCreateStageOptions{ - Filename: subscribeServiceFile, - UnitType: "system", - UnitPath: osbuild.Usr, - Config: osbuild.SystemdServiceUnit{ - Unit: &osbuild.Unit{ - Description: "First-boot service for registering with Red Hat subscription manager and/or insights", - ConditionPathExists: []string{subkeyFilepath}, - Wants: []string{"network-online.target"}, - After: []string{"network-online.target"}, - }, - Service: &osbuild.Service{ - Type: osbuild.Oneshot, - RemainAfterExit: false, - ExecStart: commands, - EnvironmentFile: []string{subkeyFilepath}, - }, - Install: &osbuild.Install{ - WantedBy: []string{"default.target"}, - }, - }, - } - pipeline.AddStage(osbuild.NewSystemdUnitCreateStage(regServiceStageOptions)) - p.EnabledServices = append(p.EnabledServices, subscribeServiceFile) + pipeline.AddStage(subStage) + p.Directories = append(p.Directories, subDirs...) + p.Files = append(p.Files, subFiles...) + p.EnabledServices = append(p.EnabledServices, subServices...) } if p.RHSMConfig != nil { @@ -740,11 +690,21 @@ func (p *OS) serialize() osbuild.Pipeline { pipeline.AddStage(bootloader) } - if p.FactAPIType != nil { + if p.RHSMFacts != nil { + rhsmFacts := osbuild.RHSMFacts{ + ApiType: p.RHSMFacts.APIType.String(), + } + + if p.RHSMFacts.OpenSCAPProfileID != "" { + rhsmFacts.OpenSCAPProfileID = p.RHSMFacts.OpenSCAPProfileID + } + + if p.RHSMFacts.CompliancePolicyID != uuid.Nil { + rhsmFacts.CompliancePolicyID = p.RHSMFacts.CompliancePolicyID.String() + } + pipeline.AddStage(osbuild.NewRHSMFactsStage(&osbuild.RHSMFactsStageOptions{ - Facts: osbuild.RHSMFacts{ - ApiType: p.FactAPIType.String(), - }, + Facts: rhsmFacts, })) } @@ -919,43 +879,3 @@ func (p *OS) getInline() []string { return inlineData } - -// For ostree-based systems, creates a drop-in file for the insights-client -// service to run on boot and enables the service. This is only meant for -// ostree-based systems. -func (p *OS) runInsightsClientOnBoot() { - // Insights-client collection must occur at boot time so - // that the current ostree commit hash can be reflected - // after upgrade. Otherwise, the upgrade shows as failed in - // the console UI. - // Add a drop-in file that enables insights-client.service to - // run on successful boot. - // See https://issues.redhat.com/browse/HMS-4031 - // - // NOTE(akoutsou): drop-in files can normally be created with the - // org.osbuild.systemd.unit stage but the stage doesn't support - // all the options we need. This is a temporary workaround - // until we get the stage updated to support everything we need. - icDropinFilepath, icDropinContents := insightsClientDropin() - if icDropinDirectory, err := fsnode.NewDirectory(filepath.Dir(icDropinFilepath), nil, "root", "root", true); err == nil { - p.Directories = append(p.Directories, icDropinDirectory) - } - if icDropinFile, err := fsnode.NewFile(icDropinFilepath, nil, "root", "root", []byte(icDropinContents)); err == nil { - p.Files = append(p.Files, icDropinFile) - } else { - panic(err) - } - // Enable the service now that it's "enable-able" - p.EnabledServices = append(p.EnabledServices, "insights-client.service") -} - -// Filename and contents for the insights-client service drop-in. -// This is a temporary workaround until the org.osbuild.systemd.unit stage -// gains support for all the options we need. -func insightsClientDropin() (string, string) { - return "/etc/systemd/system/insights-client.service.d/override.conf", `[Unit] -Requisite=greenboot-healthcheck.service -After=network-online.target greenboot-healthcheck.service osbuild-first-boot.service -[Install] -WantedBy=multi-user.target` -} diff --git a/vendor/github.com/osbuild/images/pkg/manifest/ostree_deployment.go b/vendor/github.com/osbuild/images/pkg/manifest/ostree_deployment.go index 084610337..a6886013e 100644 --- a/vendor/github.com/osbuild/images/pkg/manifest/ostree_deployment.go +++ b/vendor/github.com/osbuild/images/pkg/manifest/ostree_deployment.go @@ -521,7 +521,7 @@ func createMountpointService(serviceName string, mountpoints []string) *osbuild. After: []string{"ostree-remount.service"}, } service := osbuild.Service{ - Type: osbuild.Oneshot, + Type: osbuild.OneshotServiceType, RemainAfterExit: false, // compatibility with composefs, will require transient rootfs to be enabled too. ExecStartPre: []string{"/bin/sh -c \"if grep -Uq composefs /run/ostree-booted; then echo 'Warning: composefs enabled! ensure transient rootfs is enabled too.'; else chattr -i /; fi\""}, @@ -547,7 +547,7 @@ func createMountpointService(serviceName string, mountpoints []string) *osbuild. } options := osbuild.SystemdUnitCreateStageOptions{ Filename: serviceName, - UnitPath: osbuild.Etc, + UnitPath: osbuild.EtcUnitPath, UnitType: osbuild.System, Config: osbuild.SystemdServiceUnit{ Unit: &unit, diff --git a/vendor/github.com/osbuild/images/pkg/manifest/subscription.go b/vendor/github.com/osbuild/images/pkg/manifest/subscription.go new file mode 100644 index 000000000..1d37858b0 --- /dev/null +++ b/vendor/github.com/osbuild/images/pkg/manifest/subscription.go @@ -0,0 +1,223 @@ +package manifest + +import ( + "fmt" + "path/filepath" + + "github.com/osbuild/images/pkg/customizations/fsnode" + "github.com/osbuild/images/pkg/customizations/subscription" + "github.com/osbuild/images/pkg/osbuild" +) + +type Subscription struct { + Base + + Subscription *subscription.ImageOptions + + // Custom directories and files to create in the pipeline + Directories []*fsnode.Directory + Files []*fsnode.File +} + +// NewSubscription creates a new subscription pipeline for creating files +// required to register a system on first boot. +// The pipeline is intended to be used to create the files necessary for +// registering a system, but outside the OS tree, so they can be copied to +// other locations in the tree after they're created (for example, to an ISO). +func NewSubscription(buildPipeline Build, subOptions *subscription.ImageOptions) *Subscription { + name := "subscription" + p := &Subscription{ + Base: NewBase(name, buildPipeline), + Subscription: subOptions, + } + buildPipeline.addDependent(p) + return p +} + +func (p *Subscription) serialize() osbuild.Pipeline { + pipeline := p.Base.serialize() + if p.Subscription != nil { + serviceDir, err := fsnode.NewDirectory("/etc/systemd/system", nil, nil, nil, true) + if err != nil { + panic(err) + } + p.Directories = append(p.Directories, serviceDir) + + subStage, subDirs, subFiles, _, err := subscriptionService(*p.Subscription, &subscriptionServiceOptions{InsightsOnBoot: true, UnitPath: osbuild.EtcUnitPath}) + if err != nil { + panic(err) + } + p.Directories = append(p.Directories, subDirs...) + p.Files = append(p.Files, subFiles...) + + pipeline.AddStages(osbuild.GenDirectoryNodesStages(p.Directories)...) + pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...) + pipeline.AddStage(subStage) + } + return pipeline +} + +func (p *Subscription) getInline() []string { + inlineData := []string{} + + // inline data for custom files + for _, file := range p.Files { + inlineData = append(inlineData, string(file.Data())) + } + + return inlineData +} + +type subscriptionServiceOptions struct { + // InsightsOnBoot controls whether the insights client service will be + // modified (with a drop-in) to run on boot as well as on a timer. + InsightsOnBoot bool + + // UnitPath controls the path where the systemd unit will be created, + // /usr/lib/systemd or /etc/systemd. + UnitPath osbuild.SystemdUnitPath +} + +// subscriptionService creates the necessary stage and modifications to the +// pipeline for activating a system on first boot. +// +// If subscription settings are included there are 3 possible setups: +// - 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) { + dirs := make([]*fsnode.Directory, 0) + files := make([]*fsnode.File, 0) + services := make([]string, 0) + + insightsOnBoot := false + unitPath := osbuild.UsrUnitPath + if serviceOptions != nil { + insightsOnBoot = serviceOptions.InsightsOnBoot + if serviceOptions.UnitPath != "" { + unitPath = serviceOptions.UnitPath + } + } + + // Write a key file that will contain the org ID and activation key to be sourced in the systemd service. + // The file will also act as the ConditionFirstBoot file. + subkeyFilepath := "/etc/osbuild-subscription-register.env" + subkeyContent := fmt.Sprintf("ORG_ID=%s\nACTIVATION_KEY=%s", subscriptionOptions.Organization, subscriptionOptions.ActivationKey) + + // NOTE: Ownership is left as nil:nil, which implicitly creates files as + // root:root. Adding an explicit owner requires chroot to run the + // org.osbuild.chown stage, which we can't run in the subscription pipeline + // since it has no packages. + if subkeyFile, err := fsnode.NewFile(subkeyFilepath, nil, nil, nil, []byte(subkeyContent)); err == nil { + files = append(files, subkeyFile) + } else { + return nil, nil, nil, nil, err + } + + var commands []string + if subscriptionOptions.Rhc { + // Use rhc for registration instead of subscription manager + commands = []string{fmt.Sprintf("/usr/bin/rhc connect --organization=${ORG_ID} --activation-key=${ACTIVATION_KEY} --server %s", subscriptionOptions.ServerUrl)} + // insights-client creates the .gnupg directory during boot process, and is labeled incorrectly + commands = append(commands, "restorecon -R /root/.gnupg") + // 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 insightsOnBoot { + icDir, icFile, err := runInsightsClientOnBoot() + if err != nil { + return nil, nil, nil, nil, err + } + dirs = append(dirs, icDir) + files = append(files, icFile) + } + } else { + commands = []string{fmt.Sprintf("/usr/sbin/subscription-manager register --org=${ORG_ID} --activationkey=${ACTIVATION_KEY} --serverurl %s --baseurl %s", subscriptionOptions.ServerUrl, subscriptionOptions.BaseUrl)} + + // Insights is optional when using subscription-manager + if subscriptionOptions.Insights { + commands = append(commands, "/usr/bin/insights-client --register") + // insights-client creates the .gnupg directory during boot process, and is labeled incorrectly + commands = append(commands, "restorecon -R /root/.gnupg") + if insightsOnBoot { + icDir, icFile, err := runInsightsClientOnBoot() + if err != nil { + return nil, nil, nil, nil, err + } + dirs = append(dirs, icDir) + files = append(files, icFile) + } + } + } + + commands = append(commands, fmt.Sprintf("/usr/bin/rm %s", subkeyFilepath)) + + subscribeServiceFile := "osbuild-subscription-register.service" + regServiceStageOptions := &osbuild.SystemdUnitCreateStageOptions{ + Filename: subscribeServiceFile, + UnitType: "system", + UnitPath: unitPath, + Config: osbuild.SystemdServiceUnit{ + Unit: &osbuild.Unit{ + Description: "First-boot service for registering with Red Hat subscription manager and/or insights", + ConditionPathExists: []string{subkeyFilepath}, + Wants: []string{"network-online.target"}, + After: []string{"network-online.target"}, + }, + Service: &osbuild.Service{ + Type: osbuild.OneshotServiceType, + RemainAfterExit: false, + ExecStart: commands, + EnvironmentFile: []string{subkeyFilepath}, + }, + Install: &osbuild.Install{ + WantedBy: []string{"default.target"}, + }, + }, + } + services = append(services, subscribeServiceFile) + unitStage := osbuild.NewSystemdUnitCreateStage(regServiceStageOptions) + return unitStage, dirs, files, services, nil +} + +// Creates a drop-in file for the insights-client service to run on boot and +// enables the service. This is only meant for ostree-based systems. +func runInsightsClientOnBoot() (*fsnode.Directory, *fsnode.File, error) { + // Insights-client collection must occur at boot time so + // that the current ostree commit hash can be reflected + // after upgrade. Otherwise, the upgrade shows as failed in + // the console UI. + // Add a drop-in file that enables insights-client.service to + // run on successful boot. + // See https://issues.redhat.com/browse/HMS-4031 + // + // NOTE(akoutsou): drop-in files can normally be created with the + // org.osbuild.systemd.unit stage but the stage doesn't support + // all the options we need. This is a temporary workaround + // until we get the stage updated to support everything we need. + icDropinFilepath, icDropinContents := insightsClientDropin() + + // NOTE: Ownership is left as nil:nil, which implicitly creates files as + // root:root. Adding an explicit owner requires chroot to run the + // org.osbuild.chown stage, which we can't run in the subscription pipeline + // since it has no packages. + icDropinDirectory, err := fsnode.NewDirectory(filepath.Dir(icDropinFilepath), nil, nil, nil, true) + if err != nil { + return nil, nil, err + } + icDropinFile, err := fsnode.NewFile(icDropinFilepath, nil, nil, nil, []byte(icDropinContents)) + if err != nil { + return nil, nil, err + } + return icDropinDirectory, icDropinFile, nil +} + +// Filename and contents for the insights-client service drop-in. +// This is a temporary workaround until the org.osbuild.systemd.unit stage +// gains support for all the options we need. +func insightsClientDropin() (string, string) { + return "/etc/systemd/system/insights-client.service.d/override.conf", `[Unit] +Requisite=greenboot-healthcheck.service +After=network-online.target greenboot-healthcheck.service osbuild-first-boot.service +[Install] +WantedBy=multi-user.target` +} diff --git a/vendor/github.com/osbuild/images/pkg/osbuild/rhsm_facts_stage.go b/vendor/github.com/osbuild/images/pkg/osbuild/rhsm_facts_stage.go index e4984338e..de839fa80 100644 --- a/vendor/github.com/osbuild/images/pkg/osbuild/rhsm_facts_stage.go +++ b/vendor/github.com/osbuild/images/pkg/osbuild/rhsm_facts_stage.go @@ -5,7 +5,9 @@ type RHSMFactsStageOptions struct { } type RHSMFacts struct { - ApiType string `json:"image-builder.osbuild-composer.api-type"` + ApiType string `json:"image-builder.osbuild-composer.api-type"` + OpenSCAPProfileID string `json:"image-builder.insights.openscap-profile-id,omitempty"` + CompliancePolicyID string `json:"image-builder.insights.compliance-policy-id,omitempty"` } func (RHSMFactsStageOptions) isStageOptions() {} diff --git a/vendor/github.com/osbuild/images/pkg/osbuild/systemd_unit_create_stage.go b/vendor/github.com/osbuild/images/pkg/osbuild/systemd_unit_create_stage.go index 8b0c39f31..74263e5fb 100644 --- a/vendor/github.com/osbuild/images/pkg/osbuild/systemd_unit_create_stage.go +++ b/vendor/github.com/osbuild/images/pkg/osbuild/systemd_unit_create_stage.go @@ -5,20 +5,21 @@ import ( "regexp" ) -type serviceType string -type unitPath string +type SystemdServiceType string +type SystemdUnitPath string const ( - Simple serviceType = "simple" - Exec serviceType = "exec" - Forking serviceType = "forking" - Oneshot serviceType = "oneshot" - Dbus serviceType = "dbus" - Notify serviceType = "notify" - NotifyReloadservice serviceType = "notify-reload" - Idle serviceType = "idle" - Etc unitPath = "etc" - Usr unitPath = "usr" + SimpleServiceType SystemdServiceType = "simple" + ExecServiceType SystemdServiceType = "exec" + ForkingServiceType SystemdServiceType = "forking" + OneshotServiceType SystemdServiceType = "oneshot" + DbusServiceType SystemdServiceType = "dbus" + NotifyServiceType SystemdServiceType = "notify" + NotifyReloadServiceType SystemdServiceType = "notify-reload" + IdleServiceType SystemdServiceType = "idle" + + EtcUnitPath SystemdUnitPath = "etc" + UsrUnitPath SystemdUnitPath = "usr" ) type Unit struct { @@ -33,7 +34,7 @@ type Unit struct { } type Service struct { - Type serviceType `json:"Type,omitempty"` + Type SystemdServiceType `json:"Type,omitempty"` RemainAfterExit bool `json:"RemainAfterExit,omitempty"` ExecStartPre []string `json:"ExecStartPre,omitempty"` ExecStopPost []string `json:"ExecStopPost,omitempty"` @@ -56,7 +57,7 @@ type SystemdServiceUnit struct { type SystemdUnitCreateStageOptions struct { Filename string `json:"filename"` UnitType unitType `json:"unit-type,omitempty"` // unitType defined in ./systemd_unit_stage.go - UnitPath unitPath `json:"unit-path,omitempty"` + UnitPath SystemdUnitPath `json:"unit-path,omitempty"` Config SystemdServiceUnit `json:"config"` } diff --git a/vendor/github.com/osbuild/images/pkg/rhsm/facts/facts.go b/vendor/github.com/osbuild/images/pkg/rhsm/facts/facts.go index af0a1db8b..19786d83e 100644 --- a/vendor/github.com/osbuild/images/pkg/rhsm/facts/facts.go +++ b/vendor/github.com/osbuild/images/pkg/rhsm/facts/facts.go @@ -1,6 +1,10 @@ package facts -import "fmt" +import ( + "fmt" + + "github.com/google/uuid" +) type APIType uint64 @@ -25,5 +29,7 @@ const ( // The ImageOptions specify things to be stored into the Insights facts // storage. This mostly relates to how the build of the image was performed. type ImageOptions struct { - APIType APIType + APIType APIType + OpenSCAPProfileID string + CompliancePolicyID uuid.UUID } diff --git a/vendor/modules.txt b/vendor/modules.txt index 63e30955d..9cbfca2ad 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -947,7 +947,7 @@ github.com/oracle/oci-go-sdk/v54/identity github.com/oracle/oci-go-sdk/v54/objectstorage github.com/oracle/oci-go-sdk/v54/objectstorage/transfer github.com/oracle/oci-go-sdk/v54/workrequests -# github.com/osbuild/images v0.82.0 +# github.com/osbuild/images v0.83.0 ## explicit; go 1.21.0 github.com/osbuild/images/internal/common github.com/osbuild/images/internal/environment