From 911b2b4ebbbbbd3dcf32eab850a974665367794d Mon Sep 17 00:00:00 2001 From: Sayan Chowdhury Date: Thu, 18 Jul 2024 16:27:29 +0530 Subject: [PATCH] kola: Add secureboot CI test Signed-off-by: Sayan Chowdhury --- build | 2 +- cmd/kola/options.go | 10 +++--- platform/machine/qemu/cluster.go | 10 ++++-- platform/machine/qemu/flight.go | 7 ++-- platform/machine/unprivqemu/cluster.go | 10 ++++-- platform/platform.go | 3 ++ platform/qemu.go | 44 +++++++++++++++++++++++--- 7 files changed, 69 insertions(+), 17 deletions(-) diff --git a/build b/build index ce232eef6..593f010b4 100755 --- a/build +++ b/build @@ -30,7 +30,7 @@ cross_build() { echo "Building $a/$1" mkdir -p "bin/$a" CGO_ENABLED=0 GOARCH=$a \ - go build -mod=vendor -ldflags "${ldflags}" \ + go build -mod=vendor -ldflags "${ldflags}" -gcflags="all=-N -l" \ -o "bin/$a/$1" "${REPO_PATH}/cmd/$1" done } diff --git a/cmd/kola/options.go b/cmd/kola/options.go index 2d9781080..85b02a388 100644 --- a/cmd/kola/options.go +++ b/cmd/kola/options.go @@ -54,7 +54,7 @@ var ( "rhcos": "v3", } - kolaDefaultBIOS = map[string]string{ + kolaDefaultFirmware = map[string]string{ "amd64-usr": "bios-256k.bin", "arm64-usr": sdk.BuildRoot() + "/images/arm64-usr/latest/flatcar_production_qemu_uefi_efi_code.fd", } @@ -87,6 +87,7 @@ func init() { sv(&kola.UpdatePayloadFile, "update-payload", "", "Path to an update payload that should be made available to tests") bv(&kola.ForceFlatcarKey, "force-flatcar-key", false, "Use the Flatcar production key to verify update payload") sv(&kola.Options.IgnitionVersion, "ignition-version", "", "Ignition version override: v2, v3") + bv(&kola.Options.EnableSecureboot, "enable-secureboot", false, "Instantiate a Secureboot Machine") iv(&kola.Options.SSHRetries, "ssh-retries", kolaSSHRetries, "Number of retries with the SSH timeout when starting the machine") dv(&kola.Options.SSHTimeout, "ssh-timeout", kolaSSHTimeout, "A timeout for a single try of establishing an SSH connection when starting the machine") @@ -222,8 +223,9 @@ func init() { // QEMU-specific options sv(&kola.QEMUOptions.Board, "board", defaultTargetBoard, "target board") sv(&kola.QEMUOptions.DiskImage, "qemu-image", "", "path to CoreOS disk image") - sv(&kola.QEMUOptions.BIOSImage, "qemu-bios", "", "BIOS to use for QEMU vm") + sv(&kola.QEMUOptions.Firmware, "qemu-firmware", "", "firmware image to use for QEMU vm") sv(&kola.QEMUOptions.VNC, "qemu-vnc", "", "VNC port (0 for 5900, 1 for 5901, etc.)") + sv(&kola.QEMUOptions.OVMFVars, "qemu-ovmf-vars", "", "OVMF vars file to use for QEMU vm") bv(&kola.QEMUOptions.UseVanillaImage, "qemu-skip-mangle", false, "don't modify CL disk image to capture console log") sv(&kola.QEMUOptions.ExtraBaseDiskSize, "qemu-grow-base-disk-by", "", "grow base disk by the given size in bytes, following optional 1024-based suffixes are allowed: b (ignored), k, K, M, G, T") bv(&kola.QEMUOptions.EnableTPM, "qemu-tpm", false, "enable TPM device in QEMU. Requires installing swtpm. Use only with 'kola spawn', test cases are responsible for creating a VM with TPM explicitly.") @@ -303,8 +305,8 @@ func syncOptions() error { kola.QEMUOptions.DiskImage = image } - if kola.QEMUOptions.BIOSImage == "" { - kola.QEMUOptions.BIOSImage = kolaDefaultBIOS[kola.QEMUOptions.Board] + if kola.QEMUOptions.Firmware == "" { + kola.QEMUOptions.Firmware = kolaDefaultFirmware[kola.QEMUOptions.Board] } units, _ := root.PersistentFlags().GetStringSlice("debug-systemd-units") for _, unit := range units { diff --git a/platform/machine/qemu/cluster.go b/platform/machine/qemu/cluster.go index 0072b171d..6c2ffcbf4 100644 --- a/platform/machine/qemu/cluster.go +++ b/platform/machine/qemu/cluster.go @@ -133,11 +133,15 @@ ExecStartPost=/usr/bin/ln -fs /run/metadata/flatcar /run/metadata/coreos // This uses path arguments with path values being // relative to the folder created for this machine - biosImage, err := filepath.Abs(qc.flight.opts.BIOSImage) + firmware, err := filepath.Abs(qc.flight.opts.Firmware) if err != nil { - return nil, fmt.Errorf("failed to canonicalize bios path: %v", err) + return nil, fmt.Errorf("failed to canonicalize firmware path: %v", err) } - qmCmd, extraFiles, err := platform.CreateQEMUCommand(qc.flight.opts.Board, qm.id, biosImage, qm.consolePath, confPath, qc.flight.diskImagePath, conf.IsIgnition(), options) + ovmfVars, err := filepath.Abs(qc.flight.opts.OVMFVars) + if err != nil { + return nil, fmt.Errorf("failed to canonicalize ovmf vars path: %v", err) + } + qmCmd, extraFiles, err := platform.CreateQEMUCommand(qc.flight.opts.Board, qm.id, firmware, ovmfVars, qm.consolePath, confPath, qc.flight.diskImagePath, qc.flight.opts.EnableSecureboot, conf.IsIgnition(), options) if err != nil { return nil, err } diff --git a/platform/machine/qemu/flight.go b/platform/machine/qemu/flight.go index 4af63b623..c638fc400 100644 --- a/platform/machine/qemu/flight.go +++ b/platform/machine/qemu/flight.go @@ -35,9 +35,12 @@ type Options struct { // DiskImage is the full path to the disk image to boot in QEMU. DiskImage string - // BIOSImage is name of the BIOS file to pass to QEMU. + // Firmware is name of the Firmware file to pass to QEMU. // It can be a plain name, or a full path. - BIOSImage string + Firmware string + + // OMVF Vars file to pass to QEMU UEFI + OVMFVars string // Don't modify CL disk images to add console logging UseVanillaImage bool diff --git a/platform/machine/unprivqemu/cluster.go b/platform/machine/unprivqemu/cluster.go index 6b80f38bf..7d9c33d4f 100644 --- a/platform/machine/unprivqemu/cluster.go +++ b/platform/machine/unprivqemu/cluster.go @@ -145,11 +145,15 @@ LinkLocalAddressing=no } // This uses path arguments with path values being // relative to the folder created for this machine - biosImage, err := filepath.Abs(qc.flight.opts.BIOSImage) + firmware, err := filepath.Abs(qc.flight.opts.Firmware) if err != nil { - return nil, fmt.Errorf("failed to canonicalize bios path: %v", err) + return nil, fmt.Errorf("failed to canonicalize firmware path: %v", err) } - qmCmd, extraFiles, err := platform.CreateQEMUCommand(qc.flight.opts.Board, qm.id, biosImage, qm.consolePath, confPath, qc.flight.diskImagePath, conf.IsIgnition(), options) + ovmfVars, err := filepath.Abs(qc.flight.opts.OVMFVars) + if err != nil { + return nil, fmt.Errorf("failed to canonicalize ovmf vars path: %v", err) + } + qmCmd, extraFiles, err := platform.CreateQEMUCommand(qc.flight.opts.Board, qm.id, firmware, ovmfVars, qm.consolePath, confPath, qc.flight.diskImagePath, qc.flight.opts.EnableSecureboot, conf.IsIgnition(), options) if err != nil { return nil, err } diff --git a/platform/platform.go b/platform/platform.go index dd9c01e28..d75bbafc8 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -163,6 +163,9 @@ type Options struct { // Board is the board used by the image Board string + // Toggle to instantiate a secureboot instance. + EnableSecureboot bool + // How many times to retry establishing an SSH connection when // creating a journal or when doing a machine check. SSHRetries int diff --git a/platform/qemu.go b/platform/qemu.go index 1475bee4c..a5e01662e 100644 --- a/platform/qemu.go +++ b/platform/qemu.go @@ -17,12 +17,14 @@ package platform import ( "errors" "fmt" + "io" "io/ioutil" "os" origExec "os/exec" "path/filepath" "regexp" "runtime" + "slices" "strconv" "strings" "time" @@ -293,7 +295,7 @@ func mkpath(basedir string) (string, error) { return f.Name(), nil } -func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImagePath string, isIgnition bool, options MachineOptions) ([]string, []*os.File, error) { +func CreateQEMUCommand(board, uuid, firmware, ovmfVars, consolePath, confPath, diskImagePath string, enableSecureboot, isIgnition bool, options MachineOptions) ([]string, []*os.File, error) { var qmCmd []string // As we expand this list of supported native + board @@ -307,7 +309,7 @@ func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImageP qmBinary = "qemu-system-x86_64" qmCmd = []string{ "qemu-system-x86_64", - "-machine", "accel=kvm", + "-machine", "q35,accel=kvm,smm=on", "-cpu", "host", "-m", "2512", } @@ -340,7 +342,6 @@ func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImageP } qmCmd = append(qmCmd, - "-bios", biosImage, "-smp", "4", "-uuid", uuid, "-display", "none", @@ -348,8 +349,38 @@ func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImageP "-serial", "chardev:log", "-object", "rng-random,filename=/dev/urandom,id=rng0", "-device", "virtio-rng-pci,rng=rng0", + "-drive", fmt.Sprintf("if=pflash,unit=0,file=%v,format=raw,readonly=on", firmware), ) + if enableSecureboot == true { + // Create a copy of the OVMF Vars + ovmfVarsSrc, err := os.Open(ovmfVars) + if err != nil { + return nil, nil, err + } + defer ovmfVarsSrc.Close() + + ovmfVarsCopy, err := ioutil.TempFile("/var/tmp/", "mantle-qemu") + if err != nil { + return nil, nil, err + } + + if _, err := io.Copy(ovmfVarsCopy, ovmfVarsSrc); err != nil { + return nil, nil, err + } + + _, err = ovmfVarsCopy.Seek(0, 0) + if err != nil { + return nil, nil, err + } + + qmCmd = append(qmCmd, + "-global", "ICH9-LPC.disable_s3=1", + "-global", "driver=cfi.pflash01,property=secure,value=on", + "-drive", fmt.Sprintf("if=pflash,unit=1,file=%v,format=raw", ovmfVarsCopy.Name()), + ) + } + if options.EnableTPM { var tpm string switch board { @@ -413,6 +444,11 @@ func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImageP fdset := 1 for _, disk := range allDisks { + bootIndexArg := "" + if slices.Contains(disk.DeviceOpts, "serial=primary-disk") { + bootIndexArg = ",bootindex=1" + } + optionsDiskFile, err := disk.setupFile() if err != nil { return nil, nil, err @@ -423,7 +459,7 @@ func CreateQEMUCommand(board, uuid, biosImage, consolePath, confPath, diskImageP id := fmt.Sprintf("d%d", fdnum) qmCmd = append(qmCmd, "-add-fd", fmt.Sprintf("fd=%d,set=%d", fdnum, fdset), "-drive", fmt.Sprintf("if=none,id=%s,format=qcow2,file=/dev/fdset/%d%s", id, fdset, autoReadOnly), - "-device", Virtio(board, "blk", fmt.Sprintf("drive=%s%s", id, disk.getOpts()))) + "-device", Virtio(board, "blk", fmt.Sprintf("drive=%s%s%s", id, disk.getOpts(), bootIndexArg))) fdnum += 1 fdset += 1 }