It turned out that when we stopped setting the kernel options in grubenv file, we also stopped setting them in /etc/default/grub under `GRUB_CMDLINE_LINUX`. This file is used by grub2-mkconfig when generating grub configuration. 10_linux script executed by grub2-mkconfig recently started to overwrite the /etc/kernel/cmdline, if its timestamp is older than the timestamp of /etc/default/grub [1]. As a result, all kernel options were wiped out from /etc/kernel/cmdline. Make sure that we always set the `KernelOptions` in the grub2 stage options, even if the `WriteCmdLine` is set to `false`. In addition, unify the way we concatenate kernel options set in the grub2 stage options. Some pipeline implementations were previously using space, other were using comma. Space is now used everywhere. Regenerate all affected image manifests. [1] https://src.fedoraproject.org/rpms/grub2/c/fc76aed5333f56dd05400521a35b944a5df52ebc Signed-off-by: Tomáš Hozza <thozza@redhat.com>
267 lines
6.9 KiB
Go
267 lines
6.9 KiB
Go
package manifest
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/common"
|
|
"github.com/osbuild/osbuild-composer/internal/disk"
|
|
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
|
"github.com/osbuild/osbuild-composer/internal/ostree"
|
|
"github.com/osbuild/osbuild-composer/internal/platform"
|
|
"github.com/osbuild/osbuild-composer/internal/users"
|
|
)
|
|
|
|
// OSTreeDeployment represents the filesystem tree of a target image based
|
|
// on a deployed ostree commit.
|
|
type OSTreeDeployment struct {
|
|
Base
|
|
|
|
Remote ostree.Remote
|
|
|
|
OSVersion string
|
|
|
|
commit ostree.CommitSpec
|
|
|
|
SysrootReadOnly bool
|
|
|
|
osName string
|
|
|
|
KernelOptionsAppend []string
|
|
Keyboard string
|
|
Locale string
|
|
|
|
Users []users.User
|
|
Groups []users.Group
|
|
|
|
platform platform.Platform
|
|
|
|
PartitionTable *disk.PartitionTable
|
|
|
|
// Whether ignition is in use or not
|
|
ignition bool
|
|
}
|
|
|
|
// NewOSTreeDeployment creates a pipeline for an ostree deployment from a
|
|
// commit.
|
|
func NewOSTreeDeployment(m *Manifest,
|
|
buildPipeline *Build,
|
|
commit ostree.CommitSpec,
|
|
osName string,
|
|
ignition bool,
|
|
platform platform.Platform) *OSTreeDeployment {
|
|
|
|
p := &OSTreeDeployment{
|
|
Base: NewBase(m, "image-tree", buildPipeline),
|
|
commit: commit,
|
|
osName: osName,
|
|
platform: platform,
|
|
ignition: ignition,
|
|
}
|
|
buildPipeline.addDependent(p)
|
|
m.addPipeline(p)
|
|
return p
|
|
}
|
|
|
|
func (p *OSTreeDeployment) getBuildPackages() []string {
|
|
packages := []string{
|
|
"rpm-ostree",
|
|
}
|
|
return packages
|
|
}
|
|
|
|
func (p *OSTreeDeployment) getOSTreeCommits() []ostree.CommitSpec {
|
|
return []ostree.CommitSpec{p.commit}
|
|
}
|
|
|
|
func (p *OSTreeDeployment) serialize() osbuild.Pipeline {
|
|
const repoPath = "/ostree/repo"
|
|
|
|
pipeline := p.Base.serialize()
|
|
|
|
pipeline.AddStage(osbuild.OSTreeInitFsStage())
|
|
pipeline.AddStage(osbuild.NewOSTreePullStage(
|
|
&osbuild.OSTreePullStageOptions{Repo: repoPath, Remote: p.Remote.Name},
|
|
osbuild.NewOstreePullStageInputs("org.osbuild.source", p.commit.Checksum, p.commit.Ref),
|
|
))
|
|
pipeline.AddStage(osbuild.NewOSTreeOsInitStage(
|
|
&osbuild.OSTreeOsInitStageOptions{
|
|
OSName: p.osName,
|
|
},
|
|
))
|
|
pipeline.AddStage(osbuild.NewMkdirStage(&osbuild.MkdirStageOptions{
|
|
Paths: []osbuild.MkdirStagePath{
|
|
{
|
|
Path: "/boot/efi",
|
|
Mode: common.ToPtr(os.FileMode(0700)),
|
|
},
|
|
},
|
|
}))
|
|
kernelOpts := osbuild.GenImageKernelOptions(p.PartitionTable)
|
|
kernelOpts = append(kernelOpts, p.KernelOptionsAppend...)
|
|
|
|
if p.ignition {
|
|
kernelOpts = append(kernelOpts,
|
|
"coreos.no_persist_ip", // users cannot add connections as we don't have a live iso, this prevents connections to bleed into the system from the ign initrd
|
|
"ignition.platform.id=metal",
|
|
"$ignition_firstboot",
|
|
)
|
|
}
|
|
|
|
pipeline.AddStage(osbuild.NewOSTreeDeployStage(
|
|
&osbuild.OSTreeDeployStageOptions{
|
|
OsName: p.osName,
|
|
Ref: p.commit.Ref,
|
|
Remote: p.Remote.Name,
|
|
Mounts: []string{"/boot", "/boot/efi"},
|
|
Rootfs: osbuild.Rootfs{
|
|
Label: "root",
|
|
},
|
|
KernelOpts: kernelOpts,
|
|
},
|
|
))
|
|
|
|
remoteURL := p.Remote.URL
|
|
if remoteURL == "" {
|
|
// if the remote URL for the image is not specified, use the source commit URL
|
|
remoteURL = p.commit.URL
|
|
}
|
|
pipeline.AddStage(osbuild.NewOSTreeRemotesStage(
|
|
&osbuild.OSTreeRemotesStageOptions{
|
|
Repo: "/ostree/repo",
|
|
Remotes: []osbuild.OSTreeRemote{
|
|
{
|
|
Name: p.Remote.Name,
|
|
URL: remoteURL,
|
|
ContentURL: p.Remote.ContentURL,
|
|
GPGKeyPaths: p.Remote.GPGKeyPaths,
|
|
},
|
|
},
|
|
},
|
|
))
|
|
|
|
pipeline.AddStage(osbuild.NewOSTreeFillvarStage(
|
|
&osbuild.OSTreeFillvarStageOptions{
|
|
Deployment: osbuild.OSTreeDeployment{
|
|
OSName: p.osName,
|
|
Ref: p.commit.Ref,
|
|
},
|
|
},
|
|
))
|
|
|
|
configStage := osbuild.NewOSTreeConfigStage(
|
|
&osbuild.OSTreeConfigStageOptions{
|
|
Repo: repoPath,
|
|
Config: &osbuild.OSTreeConfig{
|
|
Sysroot: &osbuild.SysrootOptions{
|
|
ReadOnly: &p.SysrootReadOnly,
|
|
Bootloader: "none",
|
|
},
|
|
},
|
|
},
|
|
)
|
|
configStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(configStage)
|
|
|
|
fstabOptions := osbuild.NewFSTabStageOptions(p.PartitionTable)
|
|
fstabStage := osbuild.NewFSTabStage(fstabOptions)
|
|
fstabStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(fstabStage)
|
|
|
|
if len(p.Users) > 0 {
|
|
usersStage, err := osbuild.GenUsersStage(p.Users, false)
|
|
if err != nil {
|
|
panic("password encryption failed")
|
|
}
|
|
usersStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(usersStage)
|
|
}
|
|
|
|
if len(p.Groups) > 0 {
|
|
grpStage := osbuild.GenGroupsStage(p.Groups)
|
|
grpStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(grpStage)
|
|
}
|
|
|
|
if p.ignition {
|
|
pipeline.AddStage(osbuild.NewIgnitionStage(&osbuild.IgnitionStageOptions{
|
|
// This is a workaround to make the systemd believe it's firstboot when ignition runs on real firstboot.
|
|
// Right now, since we ship /etc/machine-id, systemd thinks it's not firstboot and ignition depends on it
|
|
// to run on the real firstboot to enable services from presets.
|
|
// Since this only applies to artifacts with ignition and changing machineid-compat at commit creation time may
|
|
// have undesiderable effect, we're doing it here as a stopgap. We may revisit this in the future.
|
|
Network: []string{
|
|
"systemd.firstboot=off",
|
|
"systemd.condition-first-boot=true",
|
|
},
|
|
}))
|
|
}
|
|
|
|
// if no root password is set, lock the root account
|
|
hasRoot := false
|
|
for _, user := range p.Users {
|
|
if user.Name == "root" {
|
|
hasRoot = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasRoot {
|
|
userOptions := &osbuild.UsersStageOptions{
|
|
Users: map[string]osbuild.UsersStageOptionsUser{
|
|
"root": {
|
|
Password: common.ToPtr("!locked"), // this is treated as crypted and locks/disables the password
|
|
},
|
|
},
|
|
}
|
|
rootLockStage := osbuild.NewUsersStage(userOptions)
|
|
rootLockStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(rootLockStage)
|
|
}
|
|
|
|
if p.Keyboard != "" {
|
|
options := &osbuild.KeymapStageOptions{
|
|
Keymap: p.Keyboard,
|
|
}
|
|
keymapStage := osbuild.NewKeymapStage(options)
|
|
keymapStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(keymapStage)
|
|
}
|
|
|
|
if p.Locale != "" {
|
|
options := &osbuild.LocaleStageOptions{
|
|
Language: p.Locale,
|
|
}
|
|
localeStage := osbuild.NewLocaleStage(options)
|
|
localeStage.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(localeStage)
|
|
}
|
|
|
|
grubOptions := osbuild.NewGrub2StageOptionsUnified(p.PartitionTable,
|
|
strings.Join(kernelOpts, " "),
|
|
"",
|
|
p.platform.GetUEFIVendor() != "",
|
|
p.platform.GetBIOSPlatform(),
|
|
p.platform.GetUEFIVendor(), true)
|
|
grubOptions.Greenboot = true
|
|
grubOptions.Ignition = p.ignition
|
|
grubOptions.Config = &osbuild.GRUB2Config{
|
|
Default: "saved",
|
|
Timeout: 1,
|
|
TerminalOutput: []string{"console"},
|
|
}
|
|
bootloader := osbuild.NewGRUB2Stage(grubOptions)
|
|
bootloader.MountOSTree(p.osName, p.commit.Ref, 0)
|
|
pipeline.AddStage(bootloader)
|
|
|
|
pipeline.AddStage(osbuild.NewOSTreeSelinuxStage(
|
|
&osbuild.OSTreeSelinuxStageOptions{
|
|
Deployment: osbuild.OSTreeDeployment{
|
|
OSName: p.osName,
|
|
Ref: p.commit.Ref,
|
|
},
|
|
},
|
|
))
|
|
|
|
return pipeline
|
|
}
|