Protecting GRUB on Ubuntu 22.04
GRUB2 password protection, secure Ubuntu boot loader, prevent unauthorised boot menu changes, enhance system security.
In previous posts in this blog I have explained how to protect the system by disk encryption with a password retrial from the TPM. In another post, I have given some wider overview of system security and protection, but there were a few topics not covered in details in these articles (only concepts) I feel that I need to go further into.
While looking for a way to protect Linux GRUB for Ubuntu, I found multiple articles, all without the answer I wanted. I will iterate: there are a few methods of protecting the boot loader:
- Protect specific entries (you cannot use them unless you have the password)
- Protect entry modifications (you can select a pre-existing entry, but you cannot modify its content, unless authenticated
In Ubuntu’s guides, the tendency is towards protecting all entries with a password, leaving the system is unable to boot into without the GRUB password.
As noted in my previous articles – I find typing password on every boot tiresome and limiting. I wanted something to be able to boot normally into the default entry, but, any modification of out-of-default selection would require a password.
I felt that password-protecting GRUB2 against an attempt to modify the boot menu was the best balance of security. When combined with encrypted disk and with BIOS password, this almost fully locks up a system against unauthorised access.
As a side note – I am not entirely happy with the process so far. It is missing some important chain-of-boot protection – the initial RAM disk should be signed and protected as well, but in the current tools available for us, I was still unable to do it correctly and without some major effort on every kernel update. So this is still open.
Back to the main dish – protecting GRUB against unauthorised change, while booting the defaults without prompting for password.
Initially, I followed Ubuntu GRUB2 password guide, which I found complex and inaccurate for the task described above. So I provide here a shorter and more to-the-point method, to allow for a quick and simple action.
Before everything
You need to run these tasks as root, so you can run ‘sudo bash’ beforehand to change your prompt to root.
Backing up your grub.cfg file, to a copy (you might have access to) in case we killed your grub conf, so run
cp /boot/grub/grub.cfg /boot/grub/grub.cfg.orig
Keep the disk and the BIOS passwords, in case of an emergency (such as booting into USB for recovery purposes).
Creating the Credentials
You will need to decide on the name of the GRUB2 admin user. Let’s call it ‘grubadmin’ in our example. It can be any name you desire (“root”, “grub”, “myself”, “admin”), but it is advisable to have a user you will remember (or document off-computer), while not having a too-easy-to-guess user.
Create the password by running the command:
grub-mkpasswd-pbkdf2
You will need to save everything starting at ‘grub.pbkdf2’ and until the end of the line. An example:
$ grub-mkpasswd-pbkdf2
Enter password:
Reenter password:
PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.13C23BB9579F19E265A20D29C628E0E1C0033152D3D9FA6F0A7F5EB00CB594CCA1CB7365F3797989664BC98F8C66F7DD3A163C5CCA0CB69F576E3415E4A97D79.C2B309B804EE42E510EB2793605FE5DBB7AC99CA70D97F2118F94508C2FDC655ED2AD81B8197478CFF1081C32980740A7F56CDBED0B4ED598A6748544C1F554B
The password is grub.pbkdf2.sha512.10000.13C23BB9579F19E265A20D29C628E0E1C0033152D3D9FA6F0A7F5EB00CB594CCA1CB7365F3797989664BC98F8C66F7DD3A163C5CCA0CB69F576E3415E4A97D79.C2B309B804EE42E510EB2793605FE5DBB7AC99CA70D97F2118F94508C2FDC655ED2AD81B8197478CFF1081C32980740A7F56CDBED0B4ED598A6748544C1F554B
Now, we need to create a file /etc/grub.d/00_credentials with the following contents:
cat << EOF
set superusers="grubadmin"
password_pbkdf2 grubadmin grub.pbkdf2.sha512.10000.13C23BB9579F19E265A20D29C628E0E1C0033152D3D9FA6F0A7F5EB00CB594CCA1CB7365F3797989664BC98F8C66F7DD3A163C5CCA0CB69F576E3415E4A97D79.C2B309B804EE42E510EB2793605FE5DBB7AC99CA70D97F2118F94508C2FDC655ED2AD81B8197478CFF1081C32980740A7F56CDBED0B4ED598A6748544C1F554B
EOF
Just to be clear – the file has 5 lines in total. The line starting with ‘password_pbkdf2 grubadmin’ continues with the hash provided by the previous command. If my blog formatting breaks this into two lines – do not break it yourself.
Give this file execution permissions, or else it will not work:
chmod +x /etc/grub.d/00_credentials
Enforcing password on every change
First – backup the file /etc/grub.d/10_linux to another directory – its backup should not reside in that directory, or else the entries might appear twice, with unknown result.
Then, edit the file. Find the line saying:
CLASS="--class gnu-linux --class gnu --class os"
and append to it (inside the quotes) ‘–unrestricted’. It should look like this:
CLASS="--class gnu-linux --class gnu --class os --unrestricted "
The result of this change is that existing entries will be handled without restriction, however – modifying an entry would require authentication.
Recreating GRUB configuration and testing
To finalise this task, we need to recreate GRUB configuration files, by running:
update-grub2
Following that, you can reboot and test. Note that unaltered boot should work successfully without any problem, however – an attempt to modify the entry (pressing Esc at the right time, and then trying to edit the entry) would require authentication.
Needless to say – keep the credentials safe outside the machine.
Recovery
If, for some reason, it doesn’t work as expected, the previous grub.cfg file still exists on your /boot. If you are unable to boot entirely, use a USB Linux (Ubuntu Live) to boot into the system, and copy the grub.cfg.orig to grub.cfg – overwriting the faulty file. It should allow you to boot back into your system and check the modifications, or revert them, if needed to.
Good luck, and drop a comment if things either work well or don’t.
It worked, thanks, good job!
Thank you, here is the automated version
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
cat <> /etc/grub.d/40_custom
set superusers=”admin”
password_pbkdf2 admin grub.pbkdf2.sha512.10000.D609FD58A7856B6AE38691155E92658ED586B23A85AFEA4B6B0B462488770E1D70A0A6D7BEF174B9048A41DDAC55600AF8164F7AF680AA52F93244B31FB4FF73.27DBFC9E6EE9D19B75385B74F4C24A40AB9F4B61CC7538ECCE3CBC948D32978E6DD320A586F2A79A04780E5F46CFBC97014860807EF3DF1E9070417D9504C630
EOF
sed -i ‘s/^#GRUB_DISABLE_RECOVERY=”true”/GRUB_DISABLE_RECOVERY=”true”/’ /etc/default/grub
password is admin
Thank you. Just a word to the readers – due to how WP works – the special characters, like quotes and tags – are in Unicode, and will do wonders to your code. Make them into simple ASCII characters before actually running the code.
Hi,
Thanks for sharing.
I got it to worked on Ubuntu 20.04 and 22.04 x64, but not the Ubuntu 22.04 ARM4.
I also tried several alternative methods on the ARM64 OS but it stuck with a password prompt upon startup.
Any suggestions?
22.04 arm is nothing I’ve ever tried. Could be that the execution is different or the target TPM addresses are different. I do not know. Can you test and pull the details manually? And if you run the answer script (which the initrd calls) – do you get the correct response?
If you saw my previous response, I apologise. I read it only in part and answered (not to the point). Now I believe my answer is more accurate, even if only partially helpful.
Hi,
I don’t see the answer script anywhere in here.
I tried this method too (https://askubuntu.com/questions/1088215/grub-2-avoid-unrestricted-boot-options-are-overwritten-with-kernel-updates) on the ARM64, but no luck.