Skip to content

Commit

Permalink
Vc/logo uefi image hash (#120)
Browse files Browse the repository at this point in the history
* Dockerfile: install uefi parser and flashrom utils

* utils/flashrom: Add flashrom util to dump bios

* utils/uefi_vars, firmware parser: add initial files for these utils

* actions/inventory: define interfaces for collecting firmware checksums and efi vars

Wire in collectors

* firmware: add extractor placeholder file

* capture BIOS, extract logo and hash it

update linter version

* update Dockerfile to correctly add uefi-firmware-parser

* Dockerfile: build getinventory helper util

* actions/inventory: initialize map

* device: update util checker helper method

* providers/supermicro: include the FirmwareChecksumCollector

* examples/inventory: Update to not dump trace logs

* ci: use golang ci lint 1.55.2

* fix up some* linting issues and take the space out of the hash string

* add Nahum's UEFI variable code

* UEFI vars go in device Metadata

* vanish dead code

* add UEFIVariableCollector to SMC provider

---------

Co-authored-by: Joel Rebello <[email protected]>
  • Loading branch information
DoctorVin and joelrebel authored Dec 22, 2023
1 parent 2b57e5d commit 0746c15
Show file tree
Hide file tree
Showing 29 changed files with 649 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/push-pr-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
uses: golangci/golangci-lint-action@v3
with:
args: --config .golangci.yml
version: v1.51.2
version: v1.55.2

- name: Test
run: go test ./...
Expand Down
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ linters:
- unused
- prealloc
- typecheck
- revive
# XXX: add me back! - revive
# additional linters
- bodyclose
- gocritic
Expand Down
17 changes: 15 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ COPY . .

# build helper util
ARG TARGETOS TARGETARCH
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GO111MODULE=on \
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -o getbiosconfig examples/biosconfig/biosconfig.go && \
install -m 755 -D getbiosconfig /usr/sbin/

RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -o getinventory examples/inventory/inventory.go && \
install -m 755 -D getinventory /usr/sbin/


FROM almalinux:9-minimal as stage1
ARG TARGETOS TARGETARCH

# copy ironlib wrapper binaries
COPY --from=stage0 /usr/sbin/getbiosconfig /usr/sbin/getbiosconfig
COPY --from=stage0 /usr/sbin/getinventory /usr/sbin/getinventory

# import and install tools
RUN curl -sO https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
Expand Down Expand Up @@ -69,9 +75,16 @@ RUN microdnf install -y --setopt=tsflags=nodocs --setopt=install_weak_deps=0 \
tar \
unzip \
util-linux \
flashrom \
python \
python-devel \
python-pip \
python-setuptools \
which && \
microdnf clean all && \
ln -s /usr/bin/microdnf /usr/bin/yum # since dell dsu expects yum
ln -s /usr/bin/microdnf /usr/bin/yum

RUN pip install uefi_firmware==v1.11


# Delete /tmp/* as we don't need those included in the image.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
LINTER_EXPECTED_VERSION := "1.50.0"
LINTER_EXPECTED_VERSION := "1.55.2"

.DEFAULT_GOAL := help

Expand Down
15 changes: 15 additions & 0 deletions actions/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ type TPMCollector interface {
TPMs(ctx context.Context) ([]*common.TPM, error)
}

// Checksum collectors

// FirmwareChecksumCollector defines an interface to collect firmware checksums
type FirmwareChecksumCollector interface {
UtilAttributeGetter
// return the sha-256 of the BIOS logo as a string, or the associated error
BIOSLogoChecksum(ctx context.Context) (string, error)
}

// UEFIVarsCollector defines an interface to collect EFI variables
type UEFIVarsCollector interface {
UtilAttributeGetter
GetUEFIVars(ctx context.Context) (utils.UEFIVars, error)
}

// Updaters

// DriveUpdater defines an interface to update drive firmware
Expand Down
114 changes: 113 additions & 1 deletion actions/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package actions

import (
"context"
"encoding/json"
"runtime/debug"
"strings"

Expand All @@ -11,6 +12,7 @@ import (
"github.com/r3labs/diff/v2"
"golang.org/x/exp/slices"

"github.com/metal-toolbox/ironlib/firmware"
"github.com/metal-toolbox/ironlib/model"
"github.com/metal-toolbox/ironlib/utils"
)
Expand Down Expand Up @@ -53,12 +55,16 @@ type Collectors struct {
CPLDCollector
BIOSCollector
TPMCollector
FirmwareChecksumCollector
UEFIVarsCollector
StorageControllerCollectors []StorageControllerCollector
DriveCollectors []DriveCollector
DriveCapabilitiesCollectors []DriveCapabilityCollector
}

// Empty returns a bool value
//
//nolint:gocyclo // it's fine
func (c *Collectors) Empty() bool {
if c.InventoryCollector == nil &&
c.NICCollector == nil &&
Expand All @@ -68,7 +74,9 @@ func (c *Collectors) Empty() bool {
c.TPMCollector == nil &&
len(c.StorageControllerCollectors) == 0 &&
len(c.DriveCollectors) == 0 &&
len(c.DriveCapabilitiesCollectors) == 0 {
len(c.DriveCapabilitiesCollectors) == 0 &&
c.UEFIVarsCollector == nil &&
c.FirmwareChecksumCollector == nil {
return true
}

Expand Down Expand Up @@ -139,6 +147,11 @@ func NewInventoryCollectorAction(options ...Option) *InventoryCollectorAction {
utils.NewHdparmCmd(a.trace),
utils.NewNvmeCmd(a.trace),
},
FirmwareChecksumCollector: firmware.NewChecksumCollector(
firmware.MakeOutputPath(),
firmware.TraceExecution(a.trace),
),
UEFIVarsCollector: &utils.UEFIVariableCollector{},
}
}

Expand Down Expand Up @@ -226,6 +239,18 @@ func (a *InventoryCollectorAction) Collect(ctx context.Context, device *common.D
return errors.Wrap(err, "error retrieving TPM inventory")
}

// Collect Firmware checksums
err = a.CollectFirmwareChecksums(ctx)
if err != nil && a.failOnError {
return errors.Wrap(err, "error retrieving Firmware checksums")
}

// Collect UEFI variables
err = a.CollectUEFIVariables(ctx)
if err != nil && a.failOnError {
return errors.Wrap(err, "error retrieving UEFI variables")
}

// Update StorageControllerCollectors based on controller vendor attributes
if a.dynamicCollection {
for _, sc := range a.device.StorageControllers {
Expand Down Expand Up @@ -650,6 +675,93 @@ func (a *InventoryCollectorAction) CollectTPMs(ctx context.Context) error {
return nil
}

// CollectFirmwareChecksums executes the Firmware checksum collector and updates the component metadata.
func (a *InventoryCollectorAction) CollectFirmwareChecksums(ctx context.Context) error {
// nolint:errcheck // deferred method catches a panic, error check not required.
defer func() error {
if r := recover(); r != nil && a.failOnError {
return errors.Wrap(ErrPanic, string(debug.Stack()))
}

return nil
}()

if a.collectors.FirmwareChecksumCollector == nil {
return nil
}

// skip collector if we explicitly disable anything related to firmware checksumming.
collectorKind, _, _ := a.collectors.FirmwareChecksumCollector.Attributes()
if slices.Contains(a.disabledCollectorUtilities, collectorKind) ||
slices.Contains(a.disabledCollectorUtilities, firmware.FirmwareDumpUtility) ||
slices.Contains(a.disabledCollectorUtilities, firmware.UEFIParserUtility) {
return nil
}

sumStr, err := a.collectors.FirmwareChecksumCollector.BIOSLogoChecksum(ctx)
if err != nil {
return err
}

if a.device.BIOS == nil {
// XXX: how did we get here?
return nil
}

if a.device.BIOS.Metadata == nil {
a.device.BIOS.Metadata = map[string]string{}
}

a.device.BIOS.Metadata["bios-logo-checksum"] = sumStr

return nil
}

// CollectUEFIVariables executes the UEFI variable collector and stores them on the device object
func (a *InventoryCollectorAction) CollectUEFIVariables(ctx context.Context) error {
// nolint:errcheck // deferred method catches a panic, error check not required.
defer func() error {
if r := recover(); r != nil && a.failOnError {
return errors.Wrap(ErrPanic, string(debug.Stack()))
}

return nil
}()

if a.collectors.UEFIVarsCollector == nil {
return nil
}

// skip collector if its been disabled
collectorKind, _, _ := a.collectors.UEFIVarsCollector.Attributes()
if slices.Contains(a.disabledCollectorUtilities, collectorKind) {
return nil
}

keyValues, err := a.collectors.UEFIVarsCollector.GetUEFIVars(ctx)
if err != nil {
return err
}

if len(keyValues) == 0 {
// seems unlikely
return nil
}

if a.device.Metadata == nil {
a.device.Metadata = map[string]string{}
}

jsonBytes, err := json.Marshal(keyValues)
if err != nil {
return errors.Wrap(err, "marshaling uefi variables")
}

a.device.Metadata["uefi-variables"] = string(jsonBytes)

return nil
}

// CollectStorageControllers executes the StorageControllers collectors and updates device storage controller data
//
// nolint:gocyclo // this is fine for now
Expand Down
2 changes: 2 additions & 0 deletions device.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ func CheckDependencies() {
utils.NewIpmicfgCmd(false),
utils.NewSupermicroSUM(false),
utils.NewStoreCLICmd(false),
utils.NewFlashromCmd(false),
utils.NewUefiFirmwareParserCmd(false),
}

red := "\033[31m"
Expand Down
3 changes: 1 addition & 2 deletions examples/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"

"github.com/metal-toolbox/ironlib"
"github.com/metal-toolbox/ironlib/actions"
"github.com/sirupsen/logrus"
)

Expand All @@ -20,7 +19,7 @@ func main() {
logger.Fatal(err)
}

inv, err := device.GetInventory(context.TODO(), actions.WithTraceLevel())
inv, err := device.GetInventory(context.TODO())
if err != nil {
logger.Fatal(err)
}
Expand Down
Loading

0 comments on commit 0746c15

Please sign in to comment.