Skip to content

Securing GRUB: Password-Protect the Bootloader the Right Way

Lock GRUB so nobody at the console can add rd.break or init=/bin/bash: PBKDF2 hashes, unrestricted boot entries, Debian vs RHEL setup, and what it cannot stop.

●By 10 min read

Anyone who can reach the GRUB menu of an unprotected Linux machine can own it in under a minute. Press e, append init=/bin/bash or rd.break to the kernel line, press Ctrl+X, and you are at a root shell without ever being asked for a password. That console might be a laptop on a desk, a server in a shared rack, or the web-based serial console of a cloud VM that more people have access to than you think.

A GRUB password closes that door. It is a small change, but it has one trap that has taken down plenty of servers: set it carelessly and every boot waits for a password, including the unattended reboot after tonight's kernel update. This guide covers the threat it addresses, the correct setup on Debian/Ubuntu and RHEL/Fedora, and the attacks it does nothing against.

What a GRUB Password Actually Blocks

GRUB has no concept of a password until you define at least one superuser. Once superusers is set, editing a menu entry and opening the GRUB command line both require a superuser login. Which entries can be booted without logging in is controlled separately, per entry.

Console attackStopped by GRUB password?Notes
Edit entry, add init=/bin/bashYesEditing requires superuser
Edit entry, add rd.break (RHEL) to reset root passwordYesSame: no edit, no kernel parameters
Boot systemd.unit=rescue.targetYesAlso needs an edit; rescue mode asks for root's password anyway
GRUB command line (c), read files with catYesCommand line requires superuser
Pick an older kernel from the menuOnly if entries are restrictedUnrestricted entries boot freely
Boot a USB stick or PXE imageNoNeeds a firmware password and locked boot order
Pull the disk and mount it elsewhereNoNeeds disk encryption (LUKS)

So the realistic goal is: anyone may boot the default entries, only an admin may change how they boot. That is what the rest of this guide sets up.

Debian and Ubuntu: grub-mkpasswd-pbkdf2

Never put a plaintext password line in GRUB's config. Generate a salted PBKDF2-SHA512 hash instead and store only that:

$ grub-mkpasswd-pbkdf2

Enter password:

Reenter password:

PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.8F1C0D3A...E92B.4A7D61...C0F3

# Add the superuser and hash to the custom snippet. 40_custom begins with

# 'exec tail -n +3 $0', so everything after line 2 is copied into grub.cfg verbatim.

$ sudo tee -a /etc/grub.d/40_custom >/dev/null <<'EOF'

set superusers="grubadmin"

password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.8F1C0D3A...E92B.4A7D61...C0F3

EOF

Do not run update-grub yet. On Debian and Ubuntu, the entries generated by /etc/grub.d/10_linux are restricted by default, so with only the change above the machine will stop at a username prompt on every boot. Mark the normal entries --unrestricted first:

# 10_linux builds each entry's options from the CLASS variable

$ grep -n '^CLASS=' /etc/grub.d/10_linux

CLASS="--class gnu-linux --class gnu --class os"

$ sudo sed -i 's/^CLASS="--class gnu-linux --class gnu --class os"/CLASS="--class gnu-linux --class gnu --class os --unrestricted"/' /etc/grub.d/10_linux

$ sudo update-grub

Generating grub configuration file ...

Found linux image: /boot/vmlinuz-6.8.0-45-generic

Found initrd image: /boot/initrd.img-6.8.0-45-generic

done

# Confirm the result

$ sudo grep -E '^set superusers|^password_pbkdf2|^menuentry' /boot/grub/grub.cfg | cut -c1-90

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option ...

# (other top-level entries trimmed)

set superusers="grubadmin"

password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.8F1C0D3A...

Submenus β€” the "Advanced options" submenu is a separate submenu block. Check whether it also carries --unrestricted on your version; if it does not, booting older kernels will need the password, which is often acceptable.

Package upgrades β€” files in /etc/grub.d/ are conffiles. When a grub package update ships a new 10_linux, dpkg asks whether to keep your modified version. Keep it, or redo the edit, before the next reboot.

Name the superuser anything β€” it does not need to match a Linux account. A non-obvious name like grubadmin is slightly better than root, since GRUB asks for both a username and a password.

RHEL, Fedora, Rocky and Alma: grub2-setpassword

Red Hat-family systems ship a helper that does the whole job. It writes the hash to user.cfg next to grub.cfg, and the stock /etc/grub.d/01_users snippet already in grub.cfg loads it at boot, so no config regeneration is needed:

$ sudo grub2-setpassword

Enter password:

Confirm password:

$ sudo cat /boot/grub2/user.cfg

GRUB2_PASSWORD=grub.pbkdf2.sha512.10000.5B2E...A1D9.77C4...0E6B

# The superuser name is fixed as root on these systems

$ sudo grep -B1 -A3 'set superusers' /boot/grub2/grub.cfg

if [ -n "${GRUB2_PASSWORD}" ]; then

set superusers="root"

export superusers

password_pbkdf2 root ${GRUB2_PASSWORD}

fi

The generated entries on RHEL and Fedora are unrestricted, so normal boots continue without a prompt and the password is only requested when someone presses e or c. To remove protection, delete /boot/grub2/user.cfg. On RHEL 8 and earlier UEFI systems, grub.cfg may live under /boot/efi/EFI/redhat/; check where your active config is before assuming the path.

Debian / UbuntuRHEL / Fedora
Hash toolgrub-mkpasswd-pbkdf2grub2-setpassword (or grub2-mkpasswd-pbkdf2)
Where the hash lives/etc/grub.d/40_custom, then grub.cfg/boot/grub2/user.cfg
Superuser nameYour choiceroot
Default entriesRestricted: add --unrestrictedUnrestricted
Regenerate configupdate-grubNot needed for the password
Config regeneration commandgrub-mkconfig -o /boot/grub/grub.cfggrub2-mkconfig -o /boot/grub2/grub.cfg
sponsored

Multiple Users and Per-Entry Restrictions

GRUB supports more than one account. Superusers can do everything; ordinary users can only boot the entries that list them with --users. This is useful for a recovery entry that the on-call team may boot but not edit, or a lab machine where only one entry should be bootable without a password.

# /etc/grub.d/40_custom (Debian/Ubuntu layout)

set superusers="grubadmin"

password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.8F1C...

password_pbkdf2 oncall grub.pbkdf2.sha512.10000.3D90...

menuentry "Recovery shell (on-call only)" --users oncall {

linux /vmlinuz root=/dev/mapper/vg0-root ro systemd.unit=rescue.target

initrd /initrd.img

}

Entry optionWho can boot itWho can edit it
--unrestrictedAnyoneSuperusers
(none, superusers set)SuperusersSuperusers
--users oncalloncall and superusersSuperusers
--users ""Superusers onlySuperusers

Edit is always superuser-only β€” non-superusers listed in --users can boot an entry exactly as written. They cannot change its kernel parameters, which is the whole point.

Paths depend on layout β€” the /vmlinuz and /initrd.img symlinks exist on Debian and Ubuntu when /boot is on the root filesystem. With a separate /boot partition, paths are relative to that partition. Copy them from an existing entry in grub.cfg rather than guessing.

Test Before You Need It

A bootloader mistake is the one change you cannot fix over SSH. Before rolling this out, prove three things on a VM or with console access in hand: the default entry boots with no prompt, pressing e asks for credentials, and those credentials work.

# 1. Reboot and watch the console. The default entry should boot untouched.

$ sudo systemctl reboot

# 2. At the menu, press e. You should see:

# Enter username:

# grubadmin

# Enter password:

# 3. After boot, confirm grub.cfg is not world-readable. The hash is salted,

# but anyone who can read it can try an offline brute-force attack.

$ sudo ls -l /boot/grub/grub.cfg

-rw------- 1 root root 9123 Oct 2 11:05 /boot/grub/grub.cfg

$ sudo chmod 600 /boot/grub/grub.cfg # only if it was not already

GRUB uses a US keyboard layout β€” at the boot prompt there is no localisation, so symbols on a German, French or UK keyboard may land on different keys. Pick a strong but layout-safe password (letters and digits, longer rather than more symbols); our password generator can produce one without special characters.

Store it where on-call can find it β€” a GRUB password that nobody can retrieve during an outage is a lockout waiting to happen. Put it in the team vault next to the root and firmware passwords.

Cloud images β€” test on a VM from the same image first. A restricted default entry on a cloud instance means the instance never comes back from a reboot until someone types the password into the serial console.

Automation β€” if you manage GRUB with Ansible or similar, template the 40_custom snippet and the 10_linux change together, and alert if update-grub output stops containing --unrestricted.

What GRUB Cannot Protect: The Rest of the Boot Chain

A GRUB password is enforced by GRUB. Anything that runs before GRUB, or bypasses it, ignores it completely. Treat it as one layer of a boot chain, not as protection on its own. Our Linux server hardening guide covers the operating system layer; these are the layers below it:

LayerControlCloses which gap
FirmwareUEFI/BIOS setup password, boot order locked to the internal diskBooting a USB stick or PXE image
Secure BootSigned shim, GRUB and kernelSwapping in a malicious bootloader or kernel
DiskLUKS on root (and /boot where supported)Pulling the disk, or editing grub.cfg from another OS
BootloaderGRUB superuser + unrestricted entriesEditing kernel parameters at the console
OSRoot password set, rescue/emergency mode requires itA console user reaching a shell after boot

Note the gap Secure Boot leaves: it verifies binaries, but grub.cfg is a plain text file on an unencrypted /boot. Anyone who can mount that partition from another system can delete the password lines. That is why physical-access threat models end at disk encryption, and why the GRUB password is mainly a defence against people who have console access but not the hardware: remote KVMs, IPMI, hypervisor consoles and cloud serial consoles.

Add the GRUB password and firmware password to whatever checklist you use for new hosts, such as our server hardening checklist, so they are set at build time rather than after an audit finds them missing.

Frequently Asked Questions

Why does my server ask for a GRUB password on every boot?

Because once superusers is set, any menu entry without --unrestricted requires a login to boot. On Debian and Ubuntu the generated entries are restricted by default, so add --unrestricted to the CLASS line in /etc/grub.d/10_linux and run update-grub. The password will then only be needed to edit entries or open the GRUB command line.

What is the difference between grub2-setpassword and grub-mkpasswd-pbkdf2?

grub-mkpasswd-pbkdf2 only prints a PBKDF2 hash, and you add the superusers and password_pbkdf2 lines to the config yourself. grub2-setpassword, shipped on RHEL, Fedora and their rebuilds, prompts for the password, generates the hash and writes it to user.cfg, which the stock config already loads with root as the superuser.

Does a GRUB password stop someone with physical access?

Only partly. It stops kernel parameter edits like init=/bin/bash or rd.break at the console, but not booting from USB, which needs a firmware password and locked boot order, and not removing the disk, which needs LUKS encryption. Anyone who can mount an unencrypted /boot can also delete the password from grub.cfg.

How do I remove a GRUB password?

On RHEL and Fedora, delete /boot/grub2/user.cfg. On Debian and Ubuntu, remove the set superusers and password_pbkdf2 lines from /etc/grub.d/40_custom and run update-grub. If you are locked out at the boot menu, boot rescue media, mount the system and make the same change from there.

Is it safe to use a plaintext password in grub.cfg?

No. The plain password command stores the secret in clear text in a file under /boot, which is readable by anyone who can mount that partition. Always use password_pbkdf2 with a hash from grub-mkpasswd-pbkdf2, and keep grub.cfg readable only by root.

sponsored

Related Tools

See also