# sbctl: a Secure Boot key manager written in Go

> A single Go binary for enrolling your own Secure Boot keys on Linux, signing the binaries in your EFI system partition, and remembering which of them need signing.

**Foxboron/sbctl** — :computer: :lock: :key: Secure Boot key manager

- Repository: https://github.com/Foxboron/sbctl
- Stars: 2,276 · Forks: 109
- Language: Go
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/foxboron-sbctl

## What sbctl manages that shim and MokManager do not

Secure Boot on Linux is usually a two step dance with tools you did not choose. shim sits between the firmware and the kernel, and MokManager handles trust for unsigned modules through a one time password printed at boot. sbctl takes a different route. It writes your keys straight into the UEFI variable store through the go-uefi library, so there is no intermediate MOK list and no password to type into a blue screen.

That choice has consequences worth stating plainly. If you are used to MokManager, enrolling a kernel module in sbctl means signing the module with a key the firmware already trusts, not adding a hash to a list the bootloader checks. The `sbctl` command list in the README reads like a workflow rather than a tool bag: `create-keys`, `enroll-keys`, `export-enrolled-keys`, `list-enrolled-keys`, then `sign`, `sign-all` and `verify` on top of that, with `list-files`, `remove-file` and `list-bundles` for the bookkeeping side.

There is also a signing database, which the feature list calls a signing database to help keep track of files to sign. That is the part no other tool in this space does for you. sbctl remembers which files on your EFI system partition and in `/boot` were meant to be signed, so a kernel upgrade that drops a new unsigned binary into your ESP shows up in `sbctl verify` instead of quietly failing to boot.

The repository is not archived and the last push was on 2026-09-06, with 2267 stars and 79 open issues.

## Installing from source, or from a distribution package

The README leads with a source install because the tool is a single Go binary with a tidy entry point.

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

The dependencies list is short and mostly system tools rather than libraries: util-linux through `lsblk` for partition discovery, binutils through `objcopy` for the signing work, Go 1.20 or newer, and asciidoc only if you are building the man pages yourself. `go.mod` is more demanding about Go than the README, asking for 1.24.0, and pulls in `go-piv` for smart card support, `go-tpm-keyfiles` and `google/go-tpm` for TPM work, and `go-landlock` for the sandbox that the `--disable-landlock` flag exists to turn off.

Most people will not be building from source. The README lists packages for Arch, Alpine, Gentoo, openSUSE and an unofficial Fedora COPR, and points at Repology for the current list.

```bash
# pacman -S sbctl
# apk add sbctl
# emerge --ask app-crypt/sbctl
# zypper install sbctl
```

That packaging matters more than it looks. Because `Makefile` installs a kernel install hook at `lib/kernel/install.d/91-sbctl.install`, a packaged install can sign kernels automatically as they arrive, which is the entire difference between Secure Boot you maintain and Secure Boot you check.

## Creating keys, enrolling them, and the reboot nobody can automate

The enrollment sequence in the README is deliberately explicit, including the step it cannot perform for you.

```bash
# sbctl status
# sbctl create-keys
# sbctl enroll-keys
```

`sbctl status` reports three separate facts: whether sbctl is installed, whether setup mode is enabled, and whether Secure Boot is actually on. Before enrollment it says sbctl is not installed, setup mode is enabled and Secure Boot is disabled. After `create-keys` you get an owner UUID printed back, which is the identifier the firmware uses for this installation. After `enroll-keys` the EFI variables are written and setup mode flips to disabled.

Secure Boot itself still reads as disabled until you reboot and turn it on in the firmware menu, and the README inserts that reminder as a comment between the two status runs. The docs acknowledge the gap by linking `docs/workflow-example.md`, a walkthrough with screenshots of real firmware setup menus, which is the right place to find out what your own board calls the setting.

`sbctl setup` is the higher level entry point, and release 0.17 specifically made it produce a helpful output when called with no arguments, which tells you the authors expected people to try it that way. If you need to undo the whole thing, `sbctl reset` resets Secure Boot keys, and `export-enrolled-keys` pulls already enrolled keys out of the system before you do.

## The signing database behind sign, verify and list-files

This is where sbctl earns its place next to plain `sbsign` and `objcopy`, because it keeps a record rather than only producing a file.

```bash
# sbctl verify
# sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
# sbctl sign -s /efi/EFI/arch/fwupdx64.efi
# sbctl sign -s /usr/lib/fwupd/efi/fwupdx64.efi -o /usr/lib/fwupd/efi/fwupdx64.efi.signed
```

`verify` walks the ESP and the file database and prints a line per binary, signed or not. On the README's Arch example it catches six unsigned files in one run, including `/boot/vmlinuz-linux`, a `KeyTool-signed.efi` and the systemd boot loader. After signing the three EFI binaries, the same command shows them green while the kernel and the two remaining images are still flagged, which is the honest state of a machine that has been partly migrated.

`list-files` then gives you the database view, with each path, its signed state, and an output file line where a signed copy was written elsewhere rather than in place. That `-o` behaviour matters for `fwupd`, where overwriting the packaged binary would be undone by the next package update.

Two details in the release notes show the edge cases being handled. Version 0.17 stopped sbctl from wrongly comparing input and output files when signing, so the `-o` case above is treated correctly, and 0.18 fixed `verify` choking on non PE executables. 0.16 also made `sign` reuse a saved output path from the database when one exists.

## EFI stub bundles for unified kernel images

sbctl can assemble a unified kernel image itself, taking a kernel, an initramfs, a microcode blob and a splash bitmap and producing one signed EFI binary in the ESP. The command is `sbctl bundle`, and `list-bundles` prints the stored bundles with their ESP location, output path and the EFI stub image they were built from.

The README is unusually honest about this feature, calling it a second class citizen in sbctl and recommending the initramfs generator instead. It names the three alternatives directly: `mkinitcpio` and `dracut` through their respective `--uki` and `--uefi` flags, or `ukify` from systemd. systemd's own stub path shows up in the README output tree as an EFI stub image under `/usr/lib/systemd/boot/`.

For most distributions that advice is correct, since the initramfs generator already knows your kernel layout and can be wired into the package. `generate-bundles` regenerates every stored bundle, and `remove-bundle` drops one from the database. If you are assembling an image that nothing else in your stack can build, sbctl will do it.

## What the release notes add that the README leaves out

Version 0.18, published 2025-10-07, is the release with a real new capability: a `yubikey` backend for signing keys, selectable through `--keytype`, `--pk-keytype`, `--kek-keytype` and `--db-keytype` on `create-keys`. The same release carries an important warning that binary builds in CI were broken because of a new pcsclite dependency pulled in through cgo, so distributions shipping a 0.18 package may have patched around it.

The yubikey backend has a sharp edge the release notes spell out: sbctl carries no infrastructure for interactive prompts, so PINs have to come through the `SBCTL_YUBIKEY_PIN` environment variable. That is a scripting hazard and a non interactive deployment hazard in the same sentence, and worth planning for before you move your signing key onto a token.

The roadmap to 1.0 lists key rotation, TPM support, hardware token support, configuration files, and automatic boot chain signing through the systemd Boot Loader Interface. `rotate-keys` already appears in the command list, so part of that list is closer than it looks, while TPM work is visible in the dependencies and in a top level `tpm.go`.

The rest of the README is thin on governance. Support happens in `#sbctl` on libera.chat, and the docs directory holds the enrollment walkthrough and the asciidoc man page sources, but there is no contributing guide or security policy described there.

## Conclusion

sbctl is at its best on a distribution that ships it as a package and hands you keys without ceremony, because from there the work is signing binaries and keeping the database honest. The parts worth understanding before you adopt it are the two halves of its model: keys that live in EFI variables, and a local database of files that still need a signature, which is bookkeeping you own rather than something the tool infers. UKI bundling exists but the README calls it a second class citizen and points at mkinitcpio, dracut and ukify first, so treat it as the fallback rather than the reason to install. Start with `sbctl setup`, then read `docs/workflow-example.md` for the firmware screens, and run `sbctl verify` after every kernel update until the unsigned lines stop appearing.

## FAQ

### Does sbctl replace shim and MokManager on Linux?

It replaces the key management side, not the whole chain. sbctl writes your own keys into the UEFI variable store through go-uefi, so you no longer need MokManager's one time password flow to trust modules, but a shim can still be part of your boot chain if your distribution ships one. The README describes sbctl as not relying on existing secure boot tooling.

### Can sbctl sign an EFI binary to a separate file instead of overwriting it?

Yes. `sbctl sign` takes an output path flag, and the README signs fwupd's bundled image to a `.signed` copy that way. That matters for files owned by a package manager, since the original stays where the package expects it. `sbctl list-files` then records the output path alongside the signed state.

### What does sbctl need before it can enroll keys?

A Go toolchain for a source install, util-linux for `lsblk`, binutils for `objcopy`, and firmware access to the UEFI variables. Enrolling also puts the machine into a state where Secure Boot only becomes active after a reboot and a change in the firmware setup menu, which the README documents with `sbctl status` output on either side of that reboot.

### Does sbctl support signing keys on a hardware token?

Partly. Version 0.18 added a `yubikey` backend selectable through the `--keytype`, `--pk-keytype`, `--kek-keytype` and `--db-keytype` flags on `create-keys`, and the dependencies include a PIV library. Broader hardware token support is still on the roadmap to 1.0, and PINs must be passed in through `SBCTL_YUBIKEY_PIN` because sbctl has no interactive prompts.

## Sources

- [Foxboron/sbctl on GitHub](https://github.com/Foxboron/sbctl)
- [Issues](https://github.com/Foxboron/sbctl/issues)
- [License: MIT](https://github.com/Foxboron/sbctl/blob/master/LICENSE)
- [README](https://github.com/Foxboron/sbctl/blob/master/README.md)
- [Releases](https://github.com/Foxboron/sbctl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/foxboron-sbctl
