Skip to content

Commit

Permalink
image,manifest: add support for user customization (well, not really)
Browse files Browse the repository at this point in the history
This adds support for being able to add user customization. In
practise we can only handle adding root user key(s) for now until
we have more discussion about how to support adding users in a
bootc supported way.

This support for keys is essential to allow testing the images
without play gustfish or similar tricks (which is hard on a
bootc deploy because bootc will bind mount the deploy `etc`
over the `sysroot/etc` on first boot so anything we do on the
root of the disk will not work for /etc (/root/.authorized_keys
might work actually maybe?).

This also adds support for kernel-args to the bootc install-to-fs
stage.
  • Loading branch information
mvo5 committed Mar 18, 2024
1 parent 481a659 commit 0831da3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 7 deletions.
6 changes: 6 additions & 0 deletions pkg/image/bootc_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/rand"

"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
Expand All @@ -18,6 +19,10 @@ type BootcDiskImage struct {
Platform platform.Platform
PartitionTable *disk.PartitionTable

// This is a bit of a lie, only root and it's ssh key is supported
// today because that is all that bootc gives us by default.
Users []users.User

Filename string

ContainerSource *container.SourceSpec
Expand Down Expand Up @@ -48,6 +53,7 @@ func (img *BootcDiskImage) InstantiateManifestFromContainers(m *manifest.Manifes
// and very basic user (root only?) should be supported
baseImage := manifest.NewRawBootcImage(buildPipeline, containers, img.Platform)
baseImage.PartitionTable = img.PartitionTable
baseImage.Users = img.Users

// In BIB, we export multiple images from the same pipeline so we use the
// filename as the basename for each export and set the extensions based on
Expand Down
20 changes: 19 additions & 1 deletion pkg/manifest/raw_bootc.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package manifest

import (
"fmt"

"github.com/osbuild/images/pkg/artifact"
"github.com/osbuild/images/pkg/container"
"github.com/osbuild/images/pkg/customizations/users"
"github.com/osbuild/images/pkg/disk"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/ostree"
Expand All @@ -25,6 +28,10 @@ type RawBootcImage struct {
// tree, with `bootc install to-filesystem` we can only work
// with the image itself
PartitionTable *disk.PartitionTable

// This is a bit of a lie, only root and it's ssh key is supported
// today because that is all that bootc gives us by default.
Users []users.User
}

func (p RawBootcImage) Filename() string {
Expand Down Expand Up @@ -77,18 +84,29 @@ func (p *RawBootcImage) serialize() osbuild.Pipeline {
panic("no partition table in live image")
}

if len(p.Users) > 1 {
panic(fmt.Sprintf("raw bootc image only supports a single root key for user customization, got %v", p.Users))
}
if len(p.Users) == 1 && p.Users[0].Name != "root" {
panic(fmt.Sprintf("raw bootc image only supports the root user, got %v", p.Users))
}

for _, stage := range osbuild.GenImagePrepareStages(pt, p.filename, osbuild.PTSfdisk) {
pipeline.AddStage(stage)
}

opts := &osbuild.BootcInstallToFilesystemOptions{}
if len(p.Users) == 1 && p.Users[0].Key != nil {
opts.RootSSHAuthorizedKeys = []string{*p.Users[0].Key}
}
inputs := osbuild.ContainerDeployInputs{
Images: osbuild.NewContainersInputForSources(p.containerSpecs),
}
devices, mounts, err := osbuild.GenBootupdDevicesMounts(p.filename, p.PartitionTable)
if err != nil {
panic(err)
}
st, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
st, err := osbuild.NewBootcInstallToFilesystemStage(opts, inputs, devices, mounts)
if err != nil {
panic(err)
}
Expand Down
12 changes: 11 additions & 1 deletion pkg/osbuild/bootc_install_to_filesystem_stage.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import (
"fmt"
)

type BootcInstallToFilesystemOptions struct {
// options for --root-ssh-authorized-keys
RootSSHAuthorizedKeys []string `json:"root-ssh-authorized-keys,omitempty"`
// options for --karg
Kargs []string `json:"kernel-args,omitempty"`
}

func (BootcInstallToFilesystemOptions) isStageOptions() {}

// NewBootcInstallToFilesystem creates a new stage for the
// org.osbuild.bootc.install-to-filesystem stage.
//
Expand All @@ -12,7 +21,7 @@ import (
// bootc/bootupd find and install all required bootloader bits.
//
// The mounts input should be generated with GenBootupdDevicesMounts.
func NewBootcInstallToFilesystemStage(inputs ContainerDeployInputs, devices map[string]Device, mounts []Mount) (*Stage, error) {
func NewBootcInstallToFilesystemStage(options *BootcInstallToFilesystemOptions, inputs ContainerDeployInputs, devices map[string]Device, mounts []Mount) (*Stage, error) {
if err := validateBootupdMounts(mounts); err != nil {
return nil, err
}
Expand All @@ -23,6 +32,7 @@ func NewBootcInstallToFilesystemStage(inputs ContainerDeployInputs, devices map[

return &Stage{
Type: "org.osbuild.bootc.install-to-filesystem",
Options: options,
Inputs: inputs,
Devices: devices,
Mounts: mounts,
Expand Down
12 changes: 7 additions & 5 deletions pkg/osbuild/bootc_install_to_filesystem_stage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ func TestBootcInstallToFilesystemStageNewHappy(t *testing.T) {

expectedStage := &osbuild.Stage{
Type: "org.osbuild.bootc.install-to-filesystem",
Options: (*osbuild.BootcInstallToFilesystemOptions)(nil),
Inputs: inputs,
Devices: devices,
Mounts: mounts,
}
stage, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
stage, err := osbuild.NewBootcInstallToFilesystemStage(nil, inputs, devices, mounts)
require.Nil(t, err)
assert.Equal(t, stage, expectedStage)
}
Expand All @@ -45,7 +46,7 @@ func TestBootcInstallToFilesystemStageNewNoContainers(t *testing.T) {
mounts := makeOsbuildMounts("/", "/boot", "/boot/efi")
inputs := osbuild.ContainerDeployInputs{}

_, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
_, err := osbuild.NewBootcInstallToFilesystemStage(nil, inputs, devices, mounts)
assert.EqualError(t, err, "expected exactly one container input but got: 0 (map[])")
}

Expand All @@ -61,7 +62,7 @@ func TestBootcInstallToFilesystemStageNewTwoContainers(t *testing.T) {
},
}

_, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
_, err := osbuild.NewBootcInstallToFilesystemStage(nil, inputs, devices, mounts)
assert.EqualError(t, err, "expected exactly one container input but got: 2 (map[1:{} 2:{}])")
}

Expand All @@ -70,7 +71,7 @@ func TestBootcInstallToFilesystemStageMissingMounts(t *testing.T) {
mounts := makeOsbuildMounts("/")
inputs := makeFakeContainerInputs()

stage, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
stage, err := osbuild.NewBootcInstallToFilesystemStage(nil, inputs, devices, mounts)
// XXX: rename error
assert.ErrorContains(t, err, "required mounts for bootupd stage [/boot /boot/efi] missing")
require.Nil(t, stage)
Expand All @@ -81,7 +82,7 @@ func TestBootcInstallToFilesystemStageJsonHappy(t *testing.T) {
mounts := makeOsbuildMounts("/", "/boot", "/boot/efi")
inputs := makeFakeContainerInputs()

stage, err := osbuild.NewBootcInstallToFilesystemStage(inputs, devices, mounts)
stage, err := osbuild.NewBootcInstallToFilesystemStage(nil, inputs, devices, mounts)
require.Nil(t, err)
stageJson, err := json.MarshalIndent(stage, "", " ")
require.Nil(t, err)
Expand All @@ -98,6 +99,7 @@ func TestBootcInstallToFilesystemStageJsonHappy(t *testing.T) {
}
}
},
"options": null,
"devices": {
"dev-for-/": {
"type": "org.osbuild.loopback"
Expand Down

0 comments on commit 0831da3

Please sign in to comment.