sbctl

package module
v0.0.0-...-c74a6a3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2025 License: MIT Imports: 31 Imported by: 1

README

sbctl - Secure Boot Manager

Build Status

sbctl intends to be a user-friendly secure boot key manager capable of setting up secure boot, offer key management capabilities, and keep track of files that needs to be signed in the boot chain.

It is written top-to-bottom in Golang using go-uefi for the API layer and doesn't rely on existing secure boot tooling. It also tries to sport some integration testing towards tianocore utilizing vmtest.

Features

  • User-friendly
  • Manages secure boot keys
  • Live enrollment of keys
  • Signing database to help keep track of files to sign
  • Verify ESP of files missing signatures
  • EFI stub generation
  • JSON output

Roadmap to 1.0

  • Key rotation
  • TPM support
  • Hardware token support
  • Configuration Files
  • Automatic boot chain signing using the Boot Loader Interface

Dependencies

  • util-linux (using lsblk)
  • binutils (using objcopy)
  • Go >= 1.20
  • asciidoc (only for building)

Installation

To fetch, build and install sbctl from the Github source:

$ go install github.com/foxboron/sbctl/cmd/sbctl@latest
$ $(go env GOPATH)/bin/sbctl

To install through git:

$ git clone https://github.com/foxboron/sbctl.git
$ cd sbctl
$ make
$ ./sbctl
Available packages

For Arch Linux:

# pacman -S sbctl

For Alpine Linux:

# apk add sbctl

For Gentoo Linux:

# emerge --ask app-crypt/sbctl

For openSUSE:

# zypper install sbctl

For Fedora Linux (unofficial package):

# dnf copr enable chenxiaolong/sbctl
# dnf install sbctl

You can find a updated list of sbctl packages on Repology.

In addition, sbctl is also available for Ubuntu (unofficial). Follow the Expert Download links to find installation instructions according to your operating system.

Support and development channel

Development discussions and support happens in #sbctl on the libera.chat IRC network.

Usage

$ sbctl
Secure Boot Key Manager

Usage:
  sbctl [command]

Available Commands:
  bundle               Bundle the needed files for an EFI stub image
  create-keys          Create a set of secure boot signing keys
  enroll-keys          Enroll the current keys to EFI
  export-enrolled-keys Export already enrolled keys from the system
  generate-bundles     Generate all EFI stub bundles
  help                 Help about any command
  import-keys          Import keys into sbctl
  list-bundles         List stored bundles
  list-enrolled-keys   List enrolled keys on the system
  list-files           List enrolled files
  remove-bundle        Remove bundle from database
  remove-file          Remove file from database
  reset                Reset Secure Boot Keys
  rotate-keys          Rotate secure boot keys with new keys.
  setup                Setup sbctl
  sign                 Sign a file with secure boot keys
  sign-all             Sign all enrolled files with secure boot keys
  status               Show current boot status
  verify               Find and check if files in the ESP are signed or not

Flags:
      --config string      Path to configuration file
      --debug              debug logging
      --disable-landlock   disable landlock
  -h, --help               help for sbctl
      --json               Output as json
      --quiet              Mute info from logging

Use "sbctl [command] --help" for more information about a command.

Key creation and enrollment

See example enrollment for a workflow with screenshots of real firmware setup menus.

# sbctl status
Installed:	✘ Sbctl is not installed
Setup Mode:	✘ Enabled
Secure Boot:	✘ Disabled

# sbctl create-keys
Created Owner UUID a9fbbdb7-a05f-48d5-b63a-08c5df45ee70
Creating secure boot keys...✔
Secure boot keys created!

# sbctl enroll-keys
Enrolling keys to EFI variables...✔
Enrolled keys to the EFI variables!

# sbctl status
Installed:	✔ Sbctl is installed
Owner GUID:	a9fbbdb7-a05f-48d5-b63a-08c5df45ee70
Setup Mode:	✔ Disabled
Secure Boot:	✘ Disabled

// Reboot and enable secure boot in the bios!
# sbctl status
Installed:	✔ Sbctl is installed
Owner GUID:	a9fbbdb7-a05f-48d5-b63a-08c5df45ee70
Setup Mode:	✔ Disabled
Secure Boot:	✔ Enabled

Signatures

# sbctl verify
Verifying file database and EFI images in /efi...
✘ /boot/vmlinuz-linux is not signed
✘ /efi/EFI/BOOT/BOOTX64.EFI is not signed
✘ /efi/EFI/BOOT/KeyTool-signed.efi is not signed
✘ /efi/EFI/Linux/linux-linux.efi is not signed
✘ /efi/EFI/arch/fwupdx64.efi is not signed
✘ /efi/EFI/systemd/systemd-bootx64.efi is not signed

# sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
✔ Signed /efi/EFI/BOOT/BOOTX64.EFI...

# sbctl sign -s /efi/EFI/arch/fwupdx64.efi
✔ Signed /efi/EFI/arch/fwupdx64.efi...

# sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
✔ Signed /efi/EFI/systemd/systemd-bootx64.efi...

# sbctl sign -s /usr/lib/fwupd/efi/fwupdx64.efi -o /usr/lib/fwupd/efi/fwupdx64.efi.signed
✔ Signed /usr/lib/fwupd/efi/fwupdx64.efi...

# sbctl verify
Verifying file database and EFI images in /efi...
✔ /usr/lib/fwupd/efi/fwupdx64.efi.signed is signed
✔ /efi/EFI/BOOT/BOOTX64.EFI is signed
✔ /efi/EFI/arch/fwupdx64.efi is signed
✔ /efi/EFI/systemd/systemd-bootx64.efi is signed
✘ /boot/vmlinuz-linux is not signed
✘ /efi/EFI/BOOT/KeyTool-signed.efi is not signed
✘ /efi/EFI/Linux/linux-linux.efi is not signed

# sbctl list-files
/boot/vmlinuz-linux
Signed:		✘ Not Signed

/efi/EFI/BOOT/KeyTool-signed.efi
Signed:		✘ Not Signed

/efi/EFI/Linux/linux-linux.efi
Signed:		✘ Not Signed

/efi/EFI/arch/fwupdx64.efi
Signed:		✔ Signed

/efi/EFI/BOOT/BOOTX64.EFI
Signed:		✔ Signed

/usr/lib/fwupd/efi/fwupdx64.efi
Signed:		✔ Signed
Output File:	/usr/lib/fwupd/efi/fwupdx64.efi.signed

/efi/EFI/systemd/systemd-bootx64.efi
Signed:		✔ Signed

Generate Unified Kernel Images (UKI)

Note: It is generally recommended to use the initramfs generator for this. mkinitcpio and dracut support this through their respective --uki and --uefi flags, or the ukify tool from systemd.

This feature is considered a second class citizen in sbctl.

# sbctl bundle -s -i /boot/intel-ucode.img \
      -l /usr/share/systemd/bootctl/splash-arch.bmp \
      -k /boot/vmlinuz-linux \
      -f /boot/initramfs-linux.img \
      /efi/EFI/Linux/linux-linux.efi
Wrote EFI bundle /efi/EFI/Linux/linux-linux.efi

# sbctl list-bundles
Enrolled bundles:

/efi/EFI/Linux/linux-linux.efi
	Signed:		✔ Signed
	ESP Location:	/efi
	Output:		└─/EFI/Linux/linux-linux.efi
	EFI Stub Image:	  └─/usr/lib/systemd/boot/efi/linuxx64.efi.stub
	Splash Image:	    ├─/usr/share/systemd/bootctl/splash-arch.bmp
	Cmdline:	    ├─/etc/kernel/cmdline
	OS Release:	    ├─/usr/lib/os-release
	Kernel Image:	    ├─/boot/vmlinuz-linux
	Initramfs Image:    └─/boot/initramfs-linux.img
	Intel Microcode:      └─/boot/intel-ucode.img


# sbctl generate-bundles
Generating EFI bundles....
Wrote EFI bundle /efi/EFI/Linux/linux-linux.efi

Documentation

Index

Constants

View Source
const (
	// from /usr/include/linux/fs.h
	FS_SECRM_FL        = 0x00000001 /* Secure deletion */
	FS_UNRM_FL         = 0x00000002 /* Undelete */
	FS_COMPR_FL        = 0x00000004 /* Compress file */
	FS_SYNC_FL         = 0x00000008 /* Synchronous updates */
	FS_IMMUTABLE_FL    = 0x00000010 /* Immutable file */
	FS_APPEND_FL       = 0x00000020 /* writes to file may only append */
	FS_NODUMP_FL       = 0x00000040 /* do not dump file */
	FS_NOATIME_FL      = 0x00000080 /* do not update atime */
	FS_DIRTY_FL        = 0x00000100
	FS_COMPRBLK_FL     = 0x00000200 /* One or more compressed clusters */
	FS_NOCOMP_FL       = 0x00000400 /* Don't compress */
	FS_ECOMPR_FL       = 0x00000800 /* Compression error */
	FS_BTREE_FL        = 0x00001000 /* btree format dir */
	FS_INDEX_FL        = 0x00001000 /* hash-indexed directory */
	FS_IMAGIC_FL       = 0x00002000 /* AFS directory */
	FS_JOURNAL_DATA_FL = 0x00004000 /* Reserved for ext3 */
	FS_NOTAIL_FL       = 0x00008000 /* file tail should not be merged */
	FS_DIRSYNC_FL      = 0x00010000 /* dirsync behaviour (directories only) */
	FS_TOPDIR_FL       = 0x00020000 /* Top of directory hierarchies*/
	FS_EXTENT_FL       = 0x00080000 /* Extents */
	FS_DIRECTIO_FL     = 0x00100000 /* Use direct i/o */
	FS_NOCOW_FL        = 0x00800000 /* Do not cow file */
	FS_PROJINHERIT_FL  = 0x20000000 /* Create with parents projid */
	FS_RESERVED_FL     = 0x80000000 /* reserved for ext2 lib */
)

Variables

View Source
var (
	// TODO: Remove this at some point
	//       Only here for legacy reasons to denote the old path
	DatabasePath = "/usr/share/secureboot/"
	Version      = "unknown"
)
View Source
var (
	ErrOprom      = errors.New("uefi has oprom")
	ErrNoEventlog = errors.New("no eventlog found")
)
View Source
var EfivarFSFiles = []string{
	"/sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c",
	"/sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c",
	"/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f",
}
View Source
var ErrAlreadySigned = errors.New("already signed file")
View Source
var ErrImmutable = errors.New("file is immutable")
View Source
var ErrNoESP = errors.New("failed to find EFI system partition")
View Source
var ErrNotImmutable = errors.New("file is not immutable")
View Source
var Immutable = false
View Source
var SecureBootKeys = []struct {
	Key         string
	Description string
}{
	{
		Key:         "PK",
		Description: "Platform Key",
	},
	{
		Key:         "KEK",
		Description: "Key Exchange Key",
	},
	{
		Key:         "db",
		Description: "Database Key",
	},
}

Map up our default keys in a struct

Functions

func AddChecked

func AddChecked(path string)

func BundleIter

func BundleIter(state *config.State, fn func(s *Bundle) error) error

func CheckEventlogOprom

func CheckEventlogOprom(vfs afero.Fs, eventlog string) error

func CheckIfKeysInitialized

func CheckIfKeysInitialized(vfs afero.Fs, output string) bool

Check if we have already intialized keys in the given output directory

func CheckImmutable

func CheckImmutable(vfs afero.Fs) error

Check if any files in efivarfs has the immutable bit set

func CheckMSDos

func CheckMSDos(r io.Reader) (bool, error)

func CombineFiles

func CombineFiles(vfs afero.Fs, microcode, initramfs string) (afero.File, error)

func CopyDirectory

func CopyDirectory(vfs afero.Fs, src, dst string) error

CopyDirectory moves files and creates directories

func CopyFile

func CopyFile(vfs afero.Fs, src, dst string) error

func CreateBundle

func CreateBundle(state *config.State, bundle Bundle) error

func CreateDirectory

func CreateDirectory(vfs afero.Fs, path string) error

func CreateGUID

func CreateGUID(vfs afero.Fs, guidPath string) ([]byte, error)

func CreateUUID

func CreateUUID() []byte

func DetectTPMEventlog

func DetectTPMEventlog(sb *signature.SignatureDatabase) bool

func EnrollCustom

func EnrollCustom(customBytes []byte, efivar string) error

func GenerateBundle

func GenerateBundle(vfs afero.Fs, bundle *Bundle) (bool, error)

func GetAttr

func GetAttr(f *os.File) (int32, error)

GetAttr retrieves the attributes of a file on a linux filesystem

func GetESP

func GetESP(vfs afero.Fs) (string, error)

Slightly more advanced check

func GetEfistub

func GetEfistub(vfs afero.Fs) (string, error)

func GetEnrolledVendorCerts

func GetEnrolledVendorCerts() []string

func GetEventlogChecksums

func GetEventlogChecksums(vfs afero.Fs, eventlog string) (*signature.SignatureDatabase, error)

func GetEventlogEvents

func GetEventlogEvents(vfs afero.Fs, eventlog string) ([]attest.Event, error)

func InChecked

func InChecked(path string) bool

func IsImmutable

func IsImmutable(vfs afero.Fs, file string) error

Check if a given file has the immutable bit set

func LandlockFromFileDatabase

func LandlockFromFileDatabase(state *config.State) error

func ReadOrCreateFile

func ReadOrCreateFile(vfs afero.Fs, filePath string) ([]byte, error)

func SetAttr

func SetAttr(f *os.File, attr int32) error

SetAttr sets the attributes of a file on a linux filesystem to the given value

func Sign

func Sign(state *config.State, keys *backend.KeyHierarchy, file, output string, enroll bool) error

func SignFile

func SignFile(state *config.State, kh *backend.KeyHierarchy, ev hierarchy.Hierarchy, file, output string) error

func SigningEntryIter

func SigningEntryIter(state *config.State, fn func(s *SigningEntry) error) error

func VerifyFile

func VerifyFile(state *config.State, kh *backend.KeyHierarchy, ev hierarchy.Hierarchy, file string) (bool, error)

func WriteBundleDatabase

func WriteBundleDatabase(vfs afero.Fs, dbpath string, bundles Bundles) error

func WriteFileDatabase

func WriteFileDatabase(vfs afero.Fs, dbpath string, files SigningEntries) error

Types

type Bundle

type Bundle struct {
	Output         string `json:"output"`
	IntelMicrocode string `json:"intel_microcode"`
	AMDMicrocode   string `json:"amd_microcode"`
	KernelImage    string `json:"kernel_image"`
	Initramfs      string `json:"initramfs"`
	Cmdline        string `json:"cmdline"`
	Splash         string `json:"splash"`
	OSRelease      string `json:"os_release"`
	EFIStub        string `json:"efi_stub"`
	ESP            string `json:"esp"`
}

func NewBundle

func NewBundle(vfs afero.Fs) (bundle *Bundle, err error)

type Bundles

type Bundles map[string]*Bundle

func ReadBundleDatabase

func ReadBundleDatabase(vfs afero.Fs, dbpath string) (Bundles, error)

type EFIVariables

type EFIVariables struct {
	PK  *signature.SignatureDatabase
	KEK *signature.SignatureDatabase
	Db  *signature.SignatureDatabase
	Dbx *signature.SignatureDatabase
	// contains filtered or unexported fields
}

func NewEFIVariables

func NewEFIVariables(fs *efivarfs.Efivarfs) *EFIVariables

func SystemEFIVariables

func SystemEFIVariables(fs *efivarfs.Efivarfs) (*EFIVariables, error)

func (*EFIVariables) EnrollAllKeys

func (e *EFIVariables) EnrollAllKeys(hier *backend.KeyHierarchy) error

func (*EFIVariables) EnrollKey

func (e *EFIVariables) EnrollKey(ev efivar.Efivar, hier *backend.KeyHierarchy) error

func (*EFIVariables) GetSiglist

type LsblkEntry

type LsblkEntry struct {
	Parttype    string        `json:"parttype"`
	Mountpoint  string        `json:"mountpoint"`
	Mountpoints []string      `json:"mountpoints"`
	Pttype      string        `json:"pttype"`
	Fstype      string        `json:"fstype"`
	Children    []*LsblkEntry `json:"children"`
}

type LsblkRoot

type LsblkRoot struct {
	Blockdevices []*LsblkEntry `json:"blockdevices"`
}

type SigningEntries

type SigningEntries map[string]*SigningEntry

func ReadFileDatabase

func ReadFileDatabase(vfs afero.Fs, dbpath string) (SigningEntries, error)

type SigningEntry

type SigningEntry struct {
	File       string `json:"file"`
	OutputFile string `json:"output_file"`
}

Directories

Path Synopsis
cmd
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL