Automating GRUB Configuration Across Linux Distributions

In this post, we’re going to explore the grub.sh script, which is designed to handle the configuration and hardening of GRUB across various Linux distributions. We’ll go over each line of the script to understand what it does and how it contributes to the overall configuration.

Automating GRUB Configuration Across Linux Distributions

In this post, we’re going to explore the grub.sh script, which is designed to handle the configuration and hardening of GRUB across various Linux distributions. This script is part of my Linux automation series on GitHub and is built to simplify boot-time settings for better security and system behavior. We’ll go over each line of the script to understand what it does and how it contributes to the overall configuration.

Why Customize GRUB?

The GRUB bootloader is a powerful tool that handles the boot process for Linux systems. Customizing GRUB settings can improve security, such as by disabling unnecessary services and protecting the boot menu with a password. It can also enhance system performance by removing verbose messages and setting timeout configurations to streamline booting. Our grub.sh script automates these customizations for several popular Linux distributions, so you can get consistent settings across different environments.

1. Step-by-Step Guide to grub.sh

Let’s break down each part of the script.

1.1 Check if the Current User is root

Since these scripts handle low-level configurations and essential Linux setup tasks, they need to run with root (Super-User) privileges. Thus, each script begins by checking if the current user has a user ID of 0, indicating root access.

check_root() {
    if [[ $EUID -ne 0 ]]; then
        echo "This script must be run as root" 
        exit 1
    fi
}

1.2 Detecting the GRUB Update Command

Different Linux distributions use different commands to update GRUB. The script begins by checking which command is available on your system and assigns it to the variable GRUB_UPDATE_CMD.

if command -v update-grub &> /dev/null; then
    GRUB_UPDATE_CMD="update-grub"
elif command -v grub2-mkconfig &> /dev/null; then
    GRUB_UPDATE_CMD="grub2-mkconfig -o /boot/grub2/grub.cfg"
else
    echo "No supported GRUB update command found."
    exit 1
fi

This section:

  • Checks if update-grub exists (typically found on Debian and Ubuntu) and uses it if available.
  • Checks for grub2-mkconfig (used by Red Hat-based systems like Fedora and CentOS) and assigns it if found.
  • Exits if no recognized GRUB update command is found, indicating that GRUB might not be installed. Maybe it is still LILO or something else.

1.3 Disabling IPv6 (Optional)

If you set the DISABLE_IPV6 variable to true or 1, this section will disable IPv6 system-wide. Disabling IPv6 is often a security measure in environments where IPv6 is not in use.

if [[ $DISABLE_IPV6 =~ true || $DISABLE_IPV6 =~ 1 ]]; then
    echo "==> Disabling IPv6"
    sed -i 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="ipv6.disable=1"/' /etc/default/grub
fi

This part of the script:

  • Checks for the DISABLE_IPV6 variable in the environment.
  • Uses sed to edit /etc/default/grub, appending ipv6.disable=1 to the GRUB_CMDLINE_LINUX option to disable IPv6 at the kernel level.

1.4 Configuring GRUB Boot Options

This section customizes how the GRUB boot menu behaves by modifying settings in /etc/default/grub. It hides unnecessary boot information and disables the GRUB boot menu for a cleaner boot process.

sed -i \
    -e '/^GRUB_TIMEOUT=/aGRUB_RECORDFAIL_TIMEOUT=0' \
    -e 's/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="quiet nosplash"/' \
    /etc/default/grub

Here’s what this section does:

  • Adds GRUB_RECORDFAIL_TIMEOUT=0, which prevents the boot menu from appearing in case of a previous boot failure.
  • Sets GRUB_CMDLINE_LINUX_DEFAULT="quiet nosplash", which hides verbose boot messages and the splash screen. This creates a cleaner, faster boot experience.

1.5 (Optional) Adding Password Protection for GRUB

Securing the GRUB menu with a password is a good way to prevent unauthorized access to the system’s kernel parameters. This section is disabled by default but can be enabled by providing USERNAME and PASSWORD_HASH as environment variables before the script execution.

# Check if PASSWORD_HASH and USERNAME are set in the environment
if [[ -n "$PASSWORD_HASH" && -n "$USERNAME" ]]; then
    echo "Setting GRUB superuser and password."

    # Write configuration to /etc/grub.d/40_custom
    echo "set superusers=\"$USERNAME\"" >> /etc/grub.d/40_custom
    echo "password_pbkdf2 $USERNAME $PASSWORD_HASH" >> /etc/grub.d/40_custom
else
    echo "Skipping GRUB superuser configuration as PASSWORD_HASH or USERNAME is not set."
fi

To use this feature, run the script as following:

USERNAME='your_user' ; PASSWORD_HASH='your_passowrd_hash' ; grub.sh

1.6. Applying the Configuration

After making all necessary changes, the script uses the $GRUB_UPDATE_CMD variable to apply the changes. This command updates the GRUB configuration so that changes are applied the next time the system boots.

echo "==> Applying GRUB configuration"
sudo $GRUB_UPDATE_CMD

This final step ensures that the GRUB settings are saved and ready to be enforced at the next boot.

2. The full overview of the script

#!/bin/bash -ex

# grub.sh         - Configure GRUB settings (this usually requires setting up after kernel updates).

# Function to check if running as root
check_root() {
    if [[ $EUID -ne 0 ]]; then
        echo "This script must be run as root" 
        exit 1
    fi
}

check_root

# Detect the distribution and set the update command for GRUB
if command -v update-grub &> /dev/null; then
    GRUB_UPDATE_CMD="update-grub"
elif command -v grub2-mkconfig &> /dev/null; then
    GRUB_UPDATE_CMD="grub2-mkconfig -o /boot/grub2/grub.cfg"
else
    echo "No supported GRUB update command found."
    exit 1
fi

# Disable IPv6 if requested
if [[ $DISABLE_IPV6 =~ true || $DISABLE_IPV6 =~ 1 ]]; then
    echo "==> Disabling IPv6"
    sed -i 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="ipv6.disable=1"/' /etc/default/grub
fi

# Disable GRUB boot menu and splash screen
sed -i \
    -e '/^GRUB_TIMEOUT=/aGRUB_RECORDFAIL_TIMEOUT=0' \
    -e 's/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="quiet nosplash"/' \
    /etc/default/grub

# Check if PASSWORD_HASH and USERNAME are set in the environment
if [[ -n "$PASSWORD_HASH" && -n "$USERNAME" ]]; then
    echo "Setting GRUB superuser and password."

    # Write configuration to /etc/grub.d/40_custom
    echo "set superusers=\"$USERNAME\"" >> /etc/grub.d/40_custom
    echo "password_pbkdf2 $USERNAME $PASSWORD_HASH" >> /etc/grub.d/40_custom
else
    echo "Skipping GRUB superuser configuration as PASSWORD_HASH or USERNAME is not set."
fi

# Apply the GRUB configuration changes
echo "==> Applying GRUB configuration"
$GRUB_UPDATE_CMD

echo "GRUB configuration applied successfully."

Conclusion

This grub.sh script streamlines GRUB configuration across different Linux distributions, automating essential settings for security and user experience. Whether you’re disabling IPv6, simplifying boot menus, or adding password protection, these modifications improve both the security and efficiency of the boot process. By making GRUB changes centrally managed and easily repeatable, this script provides consistency and control over your Linux systems.

Check out the script and the rest of the automation series on my GitHub, and feel free to adapt it for your specific needs. The next time you’re configuring a Linux system, give this GRUB script a try to handle your bootloader setup swiftly and effectively.

Read next

The NVLink Bridge Saga: Attempt Number Four

What started as a busy evening and a casual SMS turned into the most successful NVLink install I’ve ever had. After three failures, the fourth attempt finally worked — quietly, instantly, and without demanding my soul.