From d4ff36dfb2700c0904a3d844f1ca1e76b8159d91 Mon Sep 17 00:00:00 2001 From: Djebran Lezzoum Date: Thu, 1 Feb 2024 10:37:29 +0100 Subject: [PATCH] create ostree user sudoers file In the context of compatibility with edge-management when creating image ISO artifact. When creating an ostree user, the user is created with username and ssh-key without a password, the user has no possibility to manage the system as has no password to enter when using sudo command. Create a sudoer file at first boot stage. FIXES: https://issues.redhat.com/browse/THEEDGE-3837 --- pkg/distro/image_config.go | 2 ++ pkg/distro/image_config_test.go | 8 +++++ pkg/distro/rhel8/edge.go | 1 + pkg/distro/rhel8/images.go | 4 +++ pkg/distro/rhel9/edge.go | 1 + pkg/distro/rhel9/images.go | 4 +++ pkg/image/anaconda_ostree_installer.go | 4 ++- pkg/manifest/anaconda_installer_iso_tree.go | 33 ++++++++++++++++++++- 8 files changed, 55 insertions(+), 2 deletions(-) diff --git a/pkg/distro/image_config.go b/pkg/distro/image_config.go index da62dde5fe..7e200b08db 100644 --- a/pkg/distro/image_config.go +++ b/pkg/distro/image_config.go @@ -20,6 +20,8 @@ type ImageConfig struct { DisabledServices []string DefaultTarget *string Sysconfig []*osbuild.SysconfigStageOptions + // whether to create sudoer file for wheel group with NOPASSWD option + WheelNoPasswd *bool // List of files from which to import GPG keys into the RPM database GPGKeyFiles []string diff --git a/pkg/distro/image_config_test.go b/pkg/distro/image_config_test.go index 07f8e36e27..15c53f655e 100644 --- a/pkg/distro/image_config_test.go +++ b/pkg/distro/image_config_test.go @@ -70,6 +70,7 @@ func TestImageConfigInheritFrom(t *testing.T) { }, LeapsecTz: common.ToPtr(""), }, + WheelNoPasswd: common.ToPtr(true), }, expectedConfig: &ImageConfig{ Timezone: common.ToPtr("UTC"), @@ -92,6 +93,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), Sysconfig: []*osbuild.SysconfigStageOptions{ { Kernel: &osbuild.SysconfigKernelOptions{ @@ -133,6 +135,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, imageConfig: &ImageConfig{}, expectedConfig: &ImageConfig{ @@ -147,6 +150,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, }, { @@ -164,6 +168,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, expectedConfig: &ImageConfig{ Timezone: common.ToPtr("America/New_York"), @@ -177,6 +182,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, }, { @@ -194,6 +200,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, expectedConfig: &ImageConfig{ Timezone: common.ToPtr("America/New_York"), @@ -207,6 +214,7 @@ func TestImageConfigInheritFrom(t *testing.T) { EnabledServices: []string{"sshd"}, DisabledServices: []string{"named"}, DefaultTarget: common.ToPtr("multi-user.target"), + WheelNoPasswd: common.ToPtr(true), }, }, } diff --git a/pkg/distro/rhel8/edge.go b/pkg/distro/rhel8/edge.go index 73963f29d2..472f74913f 100644 --- a/pkg/distro/rhel8/edge.go +++ b/pkg/distro/rhel8/edge.go @@ -101,6 +101,7 @@ func edgeInstallerImgType(rd distribution) imageType { }, defaultImageConfig: &distro.ImageConfig{ EnabledServices: edgeServices(rd), + WheelNoPasswd: common.ToPtr(true), }, rpmOstree: true, bootISO: true, diff --git a/pkg/distro/rhel8/images.go b/pkg/distro/rhel8/images.go index ffc0f4caf6..540cdb89e9 100644 --- a/pkg/distro/rhel8/images.go +++ b/pkg/distro/rhel8/images.go @@ -424,6 +424,7 @@ func edgeInstallerImage(workload workload.Workload, rng *rand.Rand) (image.ImageKind, error) { d := t.arch.distro + imageConfig := t.getDefaultImageConfig() commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef()) if err != nil { @@ -436,6 +437,9 @@ func edgeInstallerImage(workload workload.Workload, img.ExtraBasePackages = packageSets[installerPkgsKey] img.Users = users.UsersFromBP(customizations.GetUsers()) img.Groups = users.GroupsFromBP(customizations.GetGroups()) + if imageConfig.WheelNoPasswd != nil { + img.WheelNoPasswd = *imageConfig.WheelNoPasswd + } img.SquashfsCompression = "xz" img.AdditionalDracutModules = []string{"prefixdevname", "prefixdevname-tools"} diff --git a/pkg/distro/rhel9/edge.go b/pkg/distro/rhel9/edge.go index 280cc747b6..902c1bc60a 100644 --- a/pkg/distro/rhel9/edge.go +++ b/pkg/distro/rhel9/edge.go @@ -99,6 +99,7 @@ var ( defaultImageConfig: &distro.ImageConfig{ Locale: common.ToPtr("en_US.UTF-8"), EnabledServices: edgeServices, + WheelNoPasswd: common.ToPtr(true), }, rpmOstree: true, bootISO: true, diff --git a/pkg/distro/rhel9/images.go b/pkg/distro/rhel9/images.go index 88e86f82d9..e04bc95d5e 100644 --- a/pkg/distro/rhel9/images.go +++ b/pkg/distro/rhel9/images.go @@ -377,6 +377,7 @@ func edgeInstallerImage(workload workload.Workload, rng *rand.Rand) (image.ImageKind, error) { d := t.arch.distro + imageConfig := t.getDefaultImageConfig() commit, err := makeOSTreePayloadCommit(options.OSTree, t.OSTreeRef()) if err != nil { @@ -389,6 +390,9 @@ func edgeInstallerImage(workload workload.Workload, img.ExtraBasePackages = packageSets[installerPkgsKey] img.Users = users.UsersFromBP(customizations.GetUsers()) img.Groups = users.GroupsFromBP(customizations.GetGroups()) + if imageConfig.WheelNoPasswd != nil { + img.WheelNoPasswd = *imageConfig.WheelNoPasswd + } img.SquashfsCompression = "xz" img.AdditionalDracutModules = []string{ diff --git a/pkg/image/anaconda_ostree_installer.go b/pkg/image/anaconda_ostree_installer.go index 9487915e4d..01d33ffc30 100644 --- a/pkg/image/anaconda_ostree_installer.go +++ b/pkg/image/anaconda_ostree_installer.go @@ -21,6 +21,8 @@ type AnacondaOSTreeInstaller struct { ExtraBasePackages rpmmd.PackageSet Users []users.User Groups []users.Group + // whether to create sudoer file for wheel group with NOPASSWD option + WheelNoPasswd bool SquashfsCompression string @@ -108,7 +110,7 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest, isoTreePipeline.Remote = img.Remote isoTreePipeline.Users = img.Users isoTreePipeline.Groups = img.Groups - + isoTreePipeline.WheelNoPasswd = img.WheelNoPasswd isoTreePipeline.SquashfsCompression = img.SquashfsCompression // For ostree installers, always put the kickstart file in the root of the ISO diff --git a/pkg/manifest/anaconda_installer_iso_tree.go b/pkg/manifest/anaconda_installer_iso_tree.go index 5a42cb9ab7..b3214772f8 100644 --- a/pkg/manifest/anaconda_installer_iso_tree.go +++ b/pkg/manifest/anaconda_installer_iso_tree.go @@ -26,6 +26,8 @@ type AnacondaInstallerISOTree struct { Remote string Users []users.User Groups []users.Group + // whether to create sudoer file for wheel group with NOPASSWD option + WheelNoPasswd bool PartitionTable *disk.PartitionTable @@ -330,9 +332,16 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline { osbuild.NewOstreePullStageInputs("org.osbuild.source", p.ostreeCommitSpec.Checksum, p.ostreeCommitSpec.Ref), )) + baseksPath := p.KSPath + if p.WheelNoPasswd { + // move the base kickstart to another file so we can write the + // %post kickstart snippet in the default location and include the + // base + baseksPath = "/osbuild-base.ks" + } // Configure the kickstart file with the payload and any user options kickstartOptions, err := osbuild.NewKickstartStageOptionsWithOSTreeCommit( - p.KSPath, + baseksPath, p.Users, p.Groups, makeISORootPath(p.PayloadPath), @@ -345,6 +354,28 @@ func (p *AnacondaInstallerISOTree) serialize() osbuild.Pipeline { } pipeline.AddStage(osbuild.NewKickstartStage(kickstartOptions)) + + if p.WheelNoPasswd { + // Because osbuild core only supports a subset of options, + // we append to the base here with hardcoded wheel group with NOPASSWD option + hardcodedKickstartBits := ` +%include /run/install/repo/osbuild-base.ks + +%post +echo -e "%wheel\tALL=(ALL)\tNOPASSWD: ALL" > "/etc/sudoers.d/wheel" +chmod 0440 /etc/sudoers.d/wheel +restorecon -rvF /etc/sudoers.d +%end +` + kickstartFile, err := fsnode.NewFile(p.KSPath, nil, nil, nil, []byte(hardcodedKickstartBits)) + if err != nil { + panic(err) + } + + p.Files = []*fsnode.File{kickstartFile} + + pipeline.AddStages(osbuild.GenFileNodesStages(p.Files)...) + } } if p.containerSpec != nil {