Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vc/logo uefi image hash #120

Merged
merged 18 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading