Grub2 is the OS boot loader used by RHEL7. Its configuration files are:
– /boot/grub2/grub.cfg on BIOS systems
– /boot/efi/EFI/redhat/grub.cfg on UEFI systems
– /etc/default/grub
– /etc/grub.d/*
The grub.cfg files are the actual files read by the bootloader which state what OSes can be booted.
The /etc/default/grub sets some default values that are read by the /etc/grub.d/* scripts when we run the grub2-mkconfig command to recreate the grub.cfg file.
# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=”$(sed ‘s, release .*$,,g’ /etc/system-release)”
GRUB_DEFAULT=0
GRUB_SAVEDEFAULT=false
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT=”console”
GRUB_CMDLINE_LINUX=”vconsole.font=latarcyrheb-sun16 vconsole.keymap=us rd.lvm.lv=vg/swap crashkernel=auto rd.lvm.lv=vg/root rhgb quiet”
GRUB_DISABLE_RECOVERY=”true”
GRUB_TIMEOUT boots to the default OS/kernel after n seconds.
GRUB_DEFAULT default menu entry to boot. Usually set to “saved” (last booted option) but it can be set to any numeric value starting with 0 for first entry.
GRUB_SAVEDEFAULT saves as default the last booted entry if set to true.
GRUB_DISABLE_SUBMENU changes the layout listing all menu entries.
GRUB_CMDLINE_LINUX command line arguments to add to menu entries for Linux kernel.
GRUB_DISABLE_RECOVERY enables/disables the generation of recovery entries for each kernel.
We can change any values in the file above but we would need to regenerate the grub.cfg file with either of the following commands:
# grub2-mkconfig -o /boot/grub2/grub.cfg
# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
If we look at the grub.cfg file and the files in /etc/grub.d, we can see that to generate the grub.cfg file all the executable scripts in the grub.d directory are executed by alphabetical order. We can generally ignore the “header” section (generated by the 00_header script) as we should not have to change anything on it.
The section we should be most interested on is the one containing the “menuentry” lines. Let’s have a look at a typical menu entry section of a grub.cfg file:
### BEGIN /etc/grub.d/10_linux ###
menuentry ‘Fedora (4.1.8-100.fc21.x86_64) 21 (Twenty One)’ --
class fedora --
class gnu-linux --
class gnu --
class os --
unrestricted $menuentry_id_option ‘gnulinux-3.17.4-301.fc21.x86_64-advanced-85ec848a-c542-4a3a-96ac-f2810edf310b’ {
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod diskfilter
insmod mdraid1x
insmod lvm
insmod xfs
set root=’lvmid/Le6HLw-AXBf-zWGw-aa5W-P3uc-QWYv-Bt9qIp/hNUXz8-DHGy-MYQF-jo1Q-FtIN-7E3V-sw9erY’
if [ x$feature_platform_search_hint = xy ]; then
search --
no-floppy --
fs-uuid --
set=root --
hint=’lvmid/Le6HLw-AXBf-zWGw-aa5W-P3uc-QWYv-Bt9qIp/hNUXz8-DHGy-MYQF-jo1Q-FtIN-7E3V-sw9erY’ 85ec848a-c542-4a3a-96ac-f2810edf310b
else
search --
no-floppy --
fs-uuid --
set=root 85ec848a-c542-4a3a-96ac-f2810edf310b
fi
linuxefi /boot/vmlinuz-4.1.8-100.fc21.x86_64 root=/dev/mapper/vg-root ro rd.md.uuid=649d37a8:575bfcf3:1089f2fc:70a9c7f5 rd.lvm.lv=vg/root rhgb quiet LANG=en_US.UTF-8
initrdefi /boot/initramfs-4.1.8-100.fc21.x86_64.img
}
### END /etc/grub.d/10_linux ###
In the first line the “menuentry” text is what shows in the GRUB menu at boot time. The “--
class” tags are for informational purposes only whereas the “--
unrestricted” flag states that any user can boot this OS without being prompted for a password.
The next 2 lines specify the graphics mode and the following 7 load the modules to extract the kernel, read the disk partition table and mount the root filesystem.
Next we see that the root filesystem is a logical volume and is identified by its UUID.
The line starting with linuxefi instructs to boot the linux kernel 4.1.8-100, mount the root filesystem found in /dev/mapper/vg-root, set the text output to have colour (rhgb) but not be too verbose (quiet) and set the keymap and encoding (LANG=en_US.UTF-8).
In the last line we specify the file that will act as the RAM disk image during the boot process (initramfs-*).
This grub.cfg file we have just seen is the result of a series of scripts in the /etc/grub.d directory. Let’s have a look at those scripts:
# cd /etc/grub.d
# ls -l
-rwxr-xr-x. 1 root root 8698 Sep 11 2014 00_header
-rwxr-xr-x. 1 root root 0 Apr 22 18:28 01_user
-rwxr-xr-x. 1 root root 9517 Sep 11 2014 10_linux
-rwxr-xr-x. 1 root root 10275 Sep 11 2014 20_linux_xen
-rwxr-xr-x. 1 root root 2559 Sep 11 2014 20_ppc_terminfo
-rwxr-xr-x. 1 root root 11110 Sep 11 2014 30_os-prober
-rwxr-xr-x. 1 root root 214 Sep 11 2014 40_custom
-rwxr-xr-x. 1 root root 216 Sep 11 2014 41_custom
-rw-r--
r--
. 1 root root 483 Sep 11 2014 README
If we need to make a trivial change to grub.cfg nothing stops us from directly modifying the file. But if we make a mistake, GRUB won’t load and we are going to waste time recovering the system. It is safer to modify the scripts above to perform any desired changes. Generally, we will only need one of these 2 changes:
– add users and passwords to protect certain GRUB menu entries
– add new entries or modify the existing ones
To execute the former we would need to modify the 01_users script. To execute changes to the latter, we would need to modify the 40_custom script.
Let’s say we want to add 2 users: root and marc. Root will have access to everything whereas marc should only be able to boot the 1st menu entry. We need to add the usernames and passwords in the 01_users file as follows:
# cat /etc/grub.d/01_users
cat <<EOF
set superusers=”root”
password root root9_1
password marc marc9!1
EOF
The passwords above are obviously in plain-text but as long as the file can only be read by root, we should be safe. However, if we want the extra security of encrypted passwords, we can achieve that by hashing them with the grub2-mkpasswd-pbkdf2 command and pasting the password. The 01_users file above would look as follows with encrypted passwords:
# cat /etc/grub.d/01_users
cat <<EOF
set superusers=”root”
password_pbkdf2 root
grub.pbkdf2.sha512.10000.DBD0DADD0FF4407E5FB938335C76EFFFAA11266194720ED20D253CE07F7F6
B18CABDC7BFA98E448579F3FC4C63028CBAC41E64B811EFD6E840E11650AD19198C.FDC8DA284F6D5
2A55327E74C75E743A29C6BF8CC40FA7A89D217B3E95EDE90C8691E272CE461625F8F321BD44478C9B
B39C9929685EC3E55A40D6875B86A462D
password_pbkdf2 marc
grub.pbkdf2.sha512.10000.376609D4FE646D30A6992B1910F2B5FBE86B6E6E743852CD670E1DDA06F159
682D665B6C1D673EA057F55D700A1FDA33E10F2C743ED20EA47158628A3077DD12.1389550F1C3643B8
9C1B3879388C27B74E0301B1DCD945EABE2C6032B08BCAC1DAF94A060D96E42DF31EB46E97F6B380B
E276DD6C59859849A74384D8D886E1C
EOF
We just replaced the “password” keyword with “password_pbkdf2” and the actual password with its hash (everything in a single line).
Now we will add 2 new menu entries: one to boot the same kernel in single-user mode and one to boot the system in emergency mode. To achieve that we append the menu entry copied from grub.cfg onto /etc/grub.d/40_custom. It is extremely important to copy the menu entry and nothing else from grub.cfg. And it is equally important to append to the 40_custom script without removing the first few lines already there. Let’s see how it would look:
# cat /etc/grub.d/40_custom
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the ‘exec tail’ line above.
menuentry ‘Scientific Linux, with Linux 3.10.0-123.el7.x86_64 (single-user mode)’ --
class scientific --
class gnu-linux --
class gnu --
class os --
users marc $menuentry_id_option ‘gnulinux-3.10.0-123.el7.x86_64-recovery-ef18f912-574e-49f8-8aea-40ecddfc5b67’
{
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod xfs
set root=’hd0,msdos1′
if [ x$feature_platform_search_hint = xy ]; then
search --
no-floppy --
fs-uuid --
set=root --
hint-bios=hd0,msdos1 --
hint-efi=hd0,msdos1 --
hint-baremetal=ahci0,msdos1 --
hint=’hd0,msdos1′ f495f8c9-deec-4caa-b1de-3b464bc108c4
else
search --
no-floppy --
fs-uuid --
set=root f495f8c9-deec-4caa-b1de-3b464bc108c4
fi
linux16 /vmlinuz-3.10.0-123.el7.x86_64 root=UUID=ef18f912-574e-49f8-8aea-40ecddfc5b67 ro vconsole.font=latarcyrheb-sun16 vconsole.keymap=us rd.lvm.lv=vg/swap crashkernel=auto rd.lvm.lv=vg/root single
initrd16 /initramfs-3.10.0-123.el7.x86_64.img
}
.
menuentry ‘Scientific Linux, with Linux 3.10.0-123.el7.x86_64 (emergency mode)’ --
class scientific --
class gnu-linux --
class gnu --
class os $menuentry_id_option ‘gnulinux-3.10.0-123.el7.x86_64-recovery-ef18f912-574e-49f8-8aea-40ecddfc5b67’
{
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod xfs
set root=’hd0,msdos1′
if [ x$feature_platform_search_hint = xy ]; then
search --
no-floppy --
fs-uuid --
set=root --
hint-bios=hd0,msdos1 --
hint-efi=hd0,msdos1 --
hint-baremetal=ahci0,msdos1 --
hint=’hd0,msdos1′ f495f8c9-deec-4caa-b1de-3b464bc108c4
else
search --
no-floppy --
fs-uuid --
set=root f495f8c9-deec-4caa-b1de-3b464bc108c4
fi
linux16 /vmlinuz-3.10.0-123.el7.x86_64 root=UUID=ef18f912-574e-49f8-8aea-40ecddfc5b67 ro vconsole.font=latarcyrheb-sun16 vconsole.keymap=us rd.lvm.lv=vg/swap crashkernel=auto rd.lvm.lv=vg/root emergency
initrd16 /initramfs-3.10.0-123.el7.x86_64.img
}
When we have completed the changes we can proceed to run the grub2-mkconfig command to regenerate grub.cfg.
# grub2-mkconfig -o /boot/grub2/grub.cfg -> on BIOS systems
# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg -> on UEFI systems
The next time the system boots we will see the original GRUB2 menu entry as well as the 2 customised ones. Any user will be able to boot in normal mode (option 1) as the --
unrestricted option has been specified. Only user marc and the superuser root will be able to boot in single-user mode (--
users marc). And only root will be able to start the kernel in emergency mode as not specifying “--
unrestricted” or “--
users” equates to superuser access only.
Grub2 is installed with the OS and rarely needs manual installation. But in cases where it is necessary (e.g. replaced hard drive) installing it on BIOS systems (it won’t work on UEFI !) is as easy as:
# grub2-install /dev/sda
The command above installs the boot loader in the first 2048 sectors of the hard drive (specifying partitions like /dev/sda3 might cause problems!) and copies the grub images to /boot/grub2.
Given enough time, as in every walk of life, things will break. What do you do if GRUB2 fails to boot the desired kernel?
If we find ourselves with a grub rescue> prompt staring at us, that means that GRUB2 could not find the grub folder and was thus unable to load the required modules and environment variables.
If we see the grub> prompt, then the grub folder was found and the modules & environment variables loaded. But the grub.cfg file could not be read or parsed.
In any case, the first thing we should do is to enable scrolling to avoid missing the output that cannot fit in the small screen we will have:
grub> set pager=1 -> no spaces around the equal sign !
We then list the devices and partitions available in the system:
grub> ls
(hd0) (hd0,gpt5) (hd0,gpt4) (hd0,gpt3) (hd0,gpt2) (hd0,gpt1) (hd1) (hd1,gpt4) (hd1,gpt3) (hd1,gpt2) (hd1,gpt1) (lvm/vg1-backup) (lvm/vg1-ora12client) (lvm/vg1-photos) (lvm/vg1-documentation) (lvm/vg1-software) (md/pv00)
We can see above that we have 2 hard drives (hd0 & hd1 corresponding to /dev/sda & /dev/sdb) as well as a mirrored device (md/pv00) and a bunch of logical volumes. The boot partition should be in the first partition of either hard drive. So let’s have a look…
grub> ls (hd0)
(hd0): Filesystem is unknown.
grub> ls (hd1)
(hd1): Filesystem is unknown.
grub> ls (hd0,gpt1)
(hd0,gpt1): Filesystem is fat.
grub> ls (hd1,gpt1)
(hd1,gpt1): Filesystem is ext2.
grub> ls (hd0,gpt1)/
efi/ System/ mach_kernel
grub> ls (hd1,gpt1)/
./ ../ lost+found/ efi/ extlinux/ grub2/ config-4.8.10-300.fc25.x86_64 elf-memtest86+-5.01 initramfs-4.8.10-300.fc25.x86_64.img memtest86+-5.01 vmlinuz-0-rescue-3279ec9d8dca4542ab3671b72bd082b9
config-4.8.11-300.fc25.x86_64 initramfs-4.8.11-300.fc25.x86_64.img System.map-4.8.10-300.fc25.x86_64 vmlinuz-4.8.10-300.fc25.x86_64 config-4.8.12-300.fc25.x86_64 initramfs-4.8.12-300.fc25.x86_64.img System.map-4.8.11-300.fc25.x86_64 vmlinuz-4.8.11-300.fc25.x86_64 initramfs-0-rescue-3279ec9d8dca4542ab3671b72bd082b9.img System.map-4.8.12-300.fc25.x86_64 vmlinuz-4.8.12-300.fc25.x86_64
In this particular system we have the /boot partition in dev/sdb1 (hd1,gpt1) formatted as FAT and the /boot/efi partition in /dev/sda1 (hd0,gpt1) formatted as ext2. The easiest thing now is to point GRUB2 to the grub.cfg or a backup of that file:
grub> ls (hd0,gpt1)/efi/fedora/
BOOT.CSV fonts fw fwupx64.efi gcdx64.efi grub.bak.cfg grubenv grubx64.efi MokManager.efi shim.efi shim-fedora.efi
We can see above that the grub.cfg file is not where it was supposed to be but we happened to perform a backup of its latest version in grub.bak.cfg. So if we point GRUB2 to this file we will see a menu in which we can choose what kernel to boot.
grub> configfile (hd0,gpt1)/efi/fedora/grub.bak.cfg
We can view the contents of the grub.bak.cfg with the cat command if we want to:
grub> cat (hd0,gpt1)/efi/fedora/grub.bak.cfg
And we can also edit any of the menu choices by pressing the “e” key if we need to perform any changes.
What if we actually do not have any grub.cfg or backup file available? Then we will have to:
1- load the necessary modules
2- set the root device where the Linux kernels are to be found
3- choose the specific kernel we want to load
4- choose the initrd image we are to use
5- boot the kernel
The modules that have already been loaded can be listed with the lsmod command:
grub> lsmod
Name Ref Count Dependencies
usbserial_usbdebug 1 usbserial_common,usb_serial
usb 8
backtrace 1
xfs 1
tftp 1
test 1
syslinuxcfg 1
sleep 1
serial 8
search 1
[…]
In most cases we won’t need to manually load specific modules, but we shall do this now as we know exactly which modules are required:
grub> insmod gzio -> required to decompress the kernel
grub> insmod part_gpt -> required to deal with GPT disks
grub> insmod ext2 -> required as the /boot partition is ext2
grub> set root=’hd1,gpt1′
grub> linuxefi /vmlinuz-4.8.12-300.fc25.x86_64 root=/dev/mapper/vg0-root ro
grub> initrdefi /initramfs-4.8.12-300.fc25.x86_64.img
grub> boot
After loading the 3 required modules, we set the root variable to where the kernels are. Then we choose what exact kernel we want to load and point to the physical or logical partition where the root filesystem is located. Finally, we have to choose the initramfs file with the exact same kernel version as we did with linux / linuxefi. After that, we are ready to boot.
What if we get the grub rescue> prompt instead? If that happens, it means GRUB2 could not locate the grub folder with the needed modules. So we look around with the ls command shown before and identify the grub directory. Once spotted…
grub rescue> ls (hd0,gpt1)/
efi/ System/ mach_kernel
grub rescue> set prefix=(hd0,gpt1)/efi
grub rescue> insmod normal
grub rescue> normal
grub> insmod linux
grub> insmod gzio
grub> insmod part_gpt
grub> insmod ext2
grub> set root=’hd1,gpt1′
grub> linuxefi /vmlinuz-4.8.12-300.fc25.x86_64 root=/dev/mapper/vg0-root ro
grub> initrdefi /initramfs-4.8.12-300.fc25.x86_64.img
grub> boot
After reading these section you should feel pretty comfortable using, customising and trouble-shooting GRUB2. However, for a thorough explanation of all the ins and outs of GRUB2 it is best to read…
https://www.gnu.org/software/grub/manual/grub.html