package fedora import ( "fmt" "math/rand" "strings" "github.com/osbuild/osbuild-composer/internal/blueprint" "github.com/osbuild/osbuild-composer/internal/common" "github.com/osbuild/osbuild-composer/internal/distro" "github.com/osbuild/osbuild-composer/internal/image" "github.com/osbuild/osbuild-composer/internal/manifest" "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/osbuild/osbuild-composer/internal/ostree" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/users" "github.com/osbuild/osbuild-composer/internal/workload" ) // HELPERS func osCustomizations( t *imageType, osPackageSet rpmmd.PackageSet, c *blueprint.Customizations) manifest.OSCustomizations { imageConfig := t.getDefaultImageConfig() osc := manifest.OSCustomizations{} if t.bootable || t.rpmOstree { osc.KernelName = c.GetKernel().Name var kernelOptions []string if t.kernelOptions != "" { kernelOptions = append(kernelOptions, t.kernelOptions) } if bpKernel := c.GetKernel(); bpKernel.Append != "" { kernelOptions = append(kernelOptions, bpKernel.Append) } osc.KernelOptionsAppend = kernelOptions } osc.ExtraBasePackages = osPackageSet.Include osc.ExcludeBasePackages = osPackageSet.Exclude osc.ExtraBaseRepos = osPackageSet.Repositories osc.GPGKeyFiles = imageConfig.GPGKeyFiles if imageConfig.ExcludeDocs != nil { osc.ExcludeDocs = *imageConfig.ExcludeDocs } if !t.bootISO { // don't put users and groups in the payload of an installer // add them via kickstart instead osc.Groups = users.GroupsFromBP(c.GetGroups()) osc.Users = users.UsersFromBP(c.GetUsers()) } osc.EnabledServices = imageConfig.EnabledServices osc.DisabledServices = imageConfig.DisabledServices if imageConfig.DefaultTarget != nil { osc.DefaultTarget = *imageConfig.DefaultTarget } osc.Firewall = c.GetFirewall() language, keyboard := c.GetPrimaryLocale() if language != nil { osc.Language = *language } else if imageConfig.Locale != nil { osc.Language = *imageConfig.Locale } if keyboard != nil { osc.Keyboard = keyboard } else if imageConfig.Keyboard != nil { osc.Keyboard = &imageConfig.Keyboard.Keymap } if hostname := c.GetHostname(); hostname != nil { osc.Hostname = *hostname } else { osc.Hostname = "localhost.localdomain" } timezone, ntpServers := c.GetTimezoneSettings() if timezone != nil { osc.Timezone = *timezone } else if imageConfig.Timezone != nil { osc.Timezone = *imageConfig.Timezone } if len(ntpServers) > 0 { osc.NTPServers = ntpServers } else if imageConfig.TimeSynchronization != nil { osc.NTPServers = imageConfig.TimeSynchronization.Timeservers } // Relabel the tree, unless the `NoSElinux` flag is explicitly set to `true` if imageConfig.NoSElinux == nil || imageConfig.NoSElinux != nil && !*imageConfig.NoSElinux { osc.SElinux = "targeted" } if oscapConfig := c.GetOpenSCAP(); oscapConfig != nil { if t.rpmOstree { panic("unexpected oscap options for ostree image type") } osc.OpenSCAPConfig = osbuild.NewOscapRemediationStageOptions( osbuild.OscapConfig{ Datastream: oscapConfig.DataStream, ProfileID: oscapConfig.ProfileID, }, ) } osc.Grub2Config = imageConfig.Grub2Config osc.Sysconfig = imageConfig.Sysconfig osc.SystemdLogind = imageConfig.SystemdLogind osc.CloudInit = imageConfig.CloudInit osc.Modprobe = imageConfig.Modprobe osc.DracutConf = imageConfig.DracutConf osc.SystemdUnit = imageConfig.SystemdUnit osc.Authselect = imageConfig.Authselect osc.SELinuxConfig = imageConfig.SELinuxConfig osc.Tuned = imageConfig.Tuned osc.Tmpfilesd = imageConfig.Tmpfilesd osc.PamLimitsConf = imageConfig.PamLimitsConf osc.Sysctld = imageConfig.Sysctld osc.DNFConfig = imageConfig.DNFConfig osc.SshdConfig = imageConfig.SshdConfig osc.AuthConfig = imageConfig.Authconfig osc.PwQuality = imageConfig.PwQuality return osc } // IMAGES func liveImage(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { img := image.NewLiveImage() img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], customizations) img.Environment = t.environment img.Workload = workload // TODO: move generation into LiveImage pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } img.PartitionTable = pt img.Filename = t.Filename() return img, nil } func containerImage(workload workload.Workload, t *imageType, c *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { img := image.NewBaseContainer() img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], c) img.Environment = t.environment img.Workload = workload img.Filename = t.Filename() return img, nil } func iotCommitImage(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { img := image.NewOSTreeArchive(options.OSTree.ImageRef) img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], customizations) img.Environment = t.environment img.Workload = workload if options.OSTree.FetchChecksum != "" && options.OSTree.URL != "" { img.OSTreeParent = &ostree.CommitSpec{ Checksum: options.OSTree.FetchChecksum, URL: options.OSTree.URL, ContentURL: options.OSTree.ContentURL, } } img.OSVersion = t.arch.distro.osVersion img.Filename = t.Filename() return img, nil } func iotContainerImage(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { img := image.NewOSTreeContainer(options.OSTree.ImageRef) img.Platform = t.platform img.OSCustomizations = osCustomizations(t, packageSets[osPkgsKey], customizations) img.ContainerLanguage = img.OSCustomizations.Language img.Environment = t.environment img.Workload = workload if options.OSTree.FetchChecksum != "" && options.OSTree.URL != "" { img.OSTreeParent = &ostree.CommitSpec{ Checksum: options.OSTree.FetchChecksum, URL: options.OSTree.URL, ContentURL: options.OSTree.ContentURL, } } img.OSVersion = t.arch.distro.osVersion img.ExtraContainerPackages = packageSets[containerPkgsKey] img.Filename = t.Filename() return img, nil } func iotInstallerImage(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { d := t.arch.distro commit := ostree.CommitSpec{ Ref: options.OSTree.ImageRef, URL: options.OSTree.URL, ContentURL: options.OSTree.ContentURL, Checksum: options.OSTree.FetchChecksum, } img := image.NewOSTreeInstaller(commit) img.Platform = t.platform img.ExtraBasePackages = packageSets[installerPkgsKey] img.Users = users.UsersFromBP(customizations.GetUsers()) img.Groups = users.GroupsFromBP(customizations.GetGroups()) img.ISOLabelTempl = d.isolabelTmpl img.Product = d.product img.Variant = "IoT" img.OSName = "fedora" img.OSVersion = d.osVersion img.Release = fmt.Sprintf("%s %s", d.product, d.osVersion) img.Filename = t.Filename() return img, nil } func iotRawImage(workload workload.Workload, t *imageType, customizations *blueprint.Customizations, options distro.ImageOptions, packageSets map[string]rpmmd.PackageSet, rng *rand.Rand) (image.ImageKind, error) { commit := ostree.CommitSpec{ Ref: options.OSTree.ImageRef, URL: options.OSTree.URL, ContentURL: options.OSTree.ContentURL, Checksum: options.OSTree.FetchChecksum, } img := image.NewOSTreeRawImage(commit) // Set sysroot read-only only for Fedora 37+ distro := t.Arch().Distro() if strings.HasPrefix(distro.Name(), "fedora") && !common.VersionLessThan(distro.Releasever(), "37") { img.SysrootReadOnly = true } img.Users = users.UsersFromBP(customizations.GetUsers()) img.Groups = users.GroupsFromBP(customizations.GetGroups()) img.KernelOptionsAppend = []string{"modprobe.blacklist=vc4", "rw"} img.Keyboard = "us" img.Locale = "C.UTF-8" img.Platform = t.platform img.Workload = workload img.Remote = ostree.Remote{ Name: "fedora-iot", URL: "https://ostree.fedoraproject.org/iot", ContentURL: "mirrorlist=https://ostree.fedoraproject.org/iot/mirrorlist", GPGKeyPaths: []string{"/etc/pki/rpm-gpg/"}, } img.OSName = "fedora-iot" // TODO: move generation into LiveImage pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng) if err != nil { return nil, err } img.PartitionTable = pt img.Filename = t.Filename() return img, nil }