DEV Community

RJ
RJ

Posted on

2 2

My initial post. This will help those dealing with FIPS on Linux

My initial post.

### for those that use FIPS with CentOS or RHEL 7, here's a script

# that I made and posted at the Red Hat Discussion Forum 
https://access.redhat.com/discussions/3487481
#!/bin/bash
#
# 10/17/2018 changed uname directives to use "uname -r" which works better in some environments.  Additionally ensured quotes were paired (some were not in echo statements)
#
# this script was posted originally at https://access.redhat.com/discussions/3487481 and the most current edition is most likely (maybe) posted there... maybe.  
# updated 8/24/2018 (thanks for those who  provided inputs for update)
# 
# Purpose, implement FIPS 140-2 compliance using the below article as a reference
# See Red Hat Article https://access.redhat.com/solutions/137833
##   --  I suspect Red-Hatter Ryan Sawhill https://access.redhat.com/user/2025843 put that solution together (Thanks Ryan).
# see original article, consider "yum install dracut-fips-aesni"
# --> And special thanks to Dusan Baljevic who identified typos and tested this on UEFI
# NOTE: You can create a Red Hat Login for free if you are a developer, 
# - Go to access.redhat.com make an account and then sign into 
# - developers.redhat.com with the same credentials and then check your email and accept the Developer's agreement.
# Risks...  1) Make sure ${mygrub} (defined in script) is backed up as expected and the directives are in place prior to reboot
# Risks...  2) Make sure /etc/default/grub is backed up as expected and the proper directives are in place prior to reboot
# Risks...  3) Check AFTER the next kernel upgrade to make sure the ${mygrub} (defined in script) is properly populated with directives
# Risks...  4) Be warned that some server roles either do not work with FIPS enabled (like a Satellite Server) or of other issues, and you've done your research
# Risks...  5) There are more risks, use of this script is at your own risk and without any warranty
# Risks...  6) The above list of risks is -not- exhaustive and you might have other issues, use at your own risk.
# Recommend using either tmux or screen session if you are using a remote session, in case your client gets disconnected. 
#

##### Where I found most of the directives... some was through my own pain with the cross of having to do stig compliance.
rhsolution="https://access.redhat.com/solutions/137833"
manualreview="Please manually perform the steps found at $rhsolution"

####### check if root is running this script, and bail if not root
# be root or exit
if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi

### bail if command sysctl crypto.fips_enable returns with "1" with the variable $answer below

configured="The sysctl crypto.fips_enabled command has detected fips is already configured, Bailing...."
notconfigured="fips not currently activated, so proceeding with script."

## Dusan's good suggestion...
answer=`sysctl crypto.fips_enabled`
yes='crypto.fips_enabled = 1'

if [ "$answer" == "$yes" ] ; then
        echo -e "\n\t $configured \n"
        exit 1
    else
        echo -e "\n\t $notconfigured \n"
fi

##### uefi check, bail if uefi (I do not have a configured uefi system to test this on)
######- Added 7/5/2018, do not proceed if this is a UEFI system... until we can test it reliably
[ -d /sys/firmware/efi ] && fw="UEFI" || fw="BIOS"
echo -e "$fw"
if [ "$fw" == "UEFI" ] ; then
        echo -e "\n\tUEFI detected, this is a ($fw) system.\n\setting \$fw variable to ($fw)..."
        mygrub='/boot/efi/EFI/redhat/grub.cfg'  
        ### Thanks Dusan Baljevic for testing this.  
        ### exit 1
    else
        echo -e "\n\t($fw) system detected, proceeding...\n"
    mygrub='/boot/grub2/grub.cfg'
fi

##### rhel6 check really don't run this on a rhel6 box... and bail if it is rhel 6
myrhel6check=`uname -r | egrep 'el6'`
if [ "$myrhel6check" != "" ] ; then
        echo -e "\n\tThis system is not RHEL 7, and Red Hat 6 is detected, \n\tThis script is intended for RHEL 7 systems only, bailing!!!\n"
        exit 1
   else
        echo -e "\n\tRHEL 7 detectd, proceeding\n"
fi

##### rhel5 check really don't run this on a rhel5 box... and bail if it is rhel5
myrhel5check=`uname -r | egrep el5`
if [ "$myrhel5check" != "" ] ; then
        echo -e "\n\tThis system is not RHEL 7, and Red Hat 5 is detected, \n\tThis script is intended for RHEL 7 systems only, bailing!!!\n"
        exit 1
   else
        echo -e "\n\tNot RHEL 5, so proceeding...\n"
fi

##### only run if this returns  el7 in the grep
# overkill? you bet, don't run unless this is rhel7
myrhel7check=`uname -r | grep el7`
if [ "$myrhel7check" != "" ] ; then
        echo "RHEL 7 detected, Proceeding"
   else
        echo -e "\n\tThis system is not rhel7, \n\tBailing..."
        echo exit 1
fi

######- add a second to $mydate variable
sleep 1
mydate=`date '+%Y%m%d_%H_%M_%S'`;echo $mydate

##### make backup copy $mygrub defined earlier
cp -v ${mygrub}{,.$mydate}

##### check fips in grub, if it's there, bail, if not proceed
myfipscheckingrub=`grep fips $mygrub | grep linux16 | egrep -v \# | head -1`
if [ "$myfipscheckingrub" != "" ] ; then
        echo -e "FIPS directives detected in ($mygrub), \n\t\t($myfipscheckingrub)\n\tSo, recommend AGAINST running this script\n\t$manualreview"
        exit 1
    else
        echo -e "\n\tFIPS directives not detected in ($mygrub)\n\tproceeding..."
fi

##### fips should not be in /etc/default/grub, if so, bail
etcdefgrub='/etc/default/grub'
myfipschecketcdefgrub=`grep fips $etcdefgrub | grep -v \#`
if [ "$myfipschecketcdefgrub" != "" ] ; then
        echo -e "FIPS directives detected in ($etcdefgrub), \n\t\t($myfipschecketcdefgrub)\n\tSo, recommend AGAINST running this script\n\t$manualreview"
        echo exit 1
    else
        echo -e "\n\tFIPS directives not detected in ($etcdefgrub)\n\tproceeding..."
fi

##### verify that this system is actually in the same kernel as we're going to install this in..., or bail
# if they don't match, the script bails.
mydefkern=`grubby --default-kernel | sed 's/.*vmlinuz\-//g'| awk '{print $1}'`
myuname=`uname -r`
if [ "$mydefkern" != "$myuname" ] ; then
   echo -e "\n\tKernel Mismatch between running and installed kernel...\n\tThe default kernel is: $mydefkern\n\tThe running kernel is $myuname\n\n\tPlease reboot this system and then re-run this script\n\tBailing...\n"
   exit 1
  else
 echo "Default Kernel ($mydefkern) and Current Running Kernel ($myuname) match, proceeding"
fi

##### overkill, yes
# yes, there's an number of checks above, but I'm still persisting with this, just in case someone runs this script twice.  
# it will never reach this if it fails any of the previous checks, but I'll leave it.
#####  a file named "/root/fipsinstalled" is created at the end of this script.  So I'll check for it at the beginning so that this script is only ran once.
if [ -f /root/fipsinstalled ] ; then
   sysctl crypto.fips_enabled
   echo -e "\tThis script was ran previously,\n\t nothing to do, \n\texiting..."
   exit 1
 else
   echo "continuing" >/dev/null
   echo proceeding...
fi
############################################################################################
############################################################################################
############################################################################################

##### this is where the script actually begins to make modifications.  
# -- everything before was either a check, or a backup of a config
# Only install dracut-fips if it is not installed (that's the "||" below)
rpm -q dracut-fips > /dev/null || yum -y install dracut-fips

##### warn people not to bail at this point, pause 4 seconds so they might see it if they're watching the screen.
echo -e "\n\n\n\tWARNING!!!: \n\tWARNING!!!DO NOT INTERRUPT THIS SCRIPT OR IT CAN CAUSE \n\tTHE SYSTEM TO BECOME UNBOOTABLE!!!!\n\tPlease be patient it will take some time...\n\tWARNING!!!\n\tWARNING\n\n\n"
sleep 4
##### next disable prelinking
rpm -q prelink >/dev/null && grep PRELINKING /etc/sysconfig/prelink 

##### slightly lesser known use of sed, it only flips PRELINKING to "no"
# this flips "yes" to "no" in the prelink config file, next kills prelinking
rpm -q prelink >/dev/null && sed -i '/^PRELINKING/s,yes,no,' /etc/sysconfig/prelink
rpm -q prelink >/dev/null && prelink -uav 2>/tmp/err
/bin/cp -v /etc/aide.conf{,.undofips}
rpm -q prelink >/dev/null && sed -i 's/^NORMAL.*/NORMAL = FIPSR+sha512/' /etc/aide.conf

##### update the $mydate variable which is used to copy off backups of various configs throughout the rest of this script.
mydate=`date '+%Y%m%d_%H_%M_%S'`;echo $mydate

###-----###
# back up existing initramfs
mv -v /boot/initramfs-$(uname -r).img{,.$mydate}

##### warn people not to bail at this point, pause 4 seconds so they might see it if they're watching the screen.
##### really, don't interrupt this portion.
echo -e "\n\n\n\tWARNING!!!: \n\tWARNING!!!DO NOT INTERRUPT THIS SCRIPT OR IT CAN CAUSE \n\tTHE SYSTEM TO BECOME UNBOOTABLE!!!!\n\tPlease be patient it will take some time...\n\tWARNING!!!\n\tWARNING!!!\n\n\n"
# this pauses as before so the person running this script gets a chance to see the above, it also is to allow the $mydate variable below to get a new value
sleep 3
# run dracut
dracut
mydate=`date '+%Y%m%d_%H_%M_%S'`
###-----###

###### The Red Hat solution I cited earlier in the comments, this is where this came from
# this section below updates /boot/grub/grub.cfg with fips and the uuid of the boot device
# first back it up
/bin/cp ${mygrub}{,.$mydate}
grubby --update-kernel=$(grubby --default-kernel) --args=fips=1

###### this displays the kernel lines in grub with fips
grep fips ${mygrub} | grep linux16

###### that Red Hat solution I cited earlier in the comments, this is where this came from
# set the uuid variable to be used later
uuid=$(findmnt -no uuid /boot)
echo -e "\n\t Just for reference, the /boot uuid is: ($uuid)\n"

###### that Red Hat solution I cited earlier in the comments, this is where this came from
# update  the boot uuid for fips in ${mygrub}
# the 2nd line is to satisfy the disa stig checker which checks every single menu entry linux16 line.  without it, the check fails.
[[ -n $uuid ]] && grubby --update-kernel=$(grubby --default-kernel) --args=boot=UUID=${uuid}
# update 7/23/2019.  The next line is excessive.  The impact of the next line, when the system goes to emergency mode, and you select **any** kernel at grub, you are faced with a system that **will not** accept any password.  I've removed it for the rescue kernel.
## so maybe your security people require this. **IF** the do, then know that when you go to emergency mode, you **will** require the grub password (know it in advance!) and you ought to set **one time only** the grub line to fips=0 **for a one time only boot**
# 
#sed -i "/linux16 \/vmlinuz-0-rescue/ s/$/ fips=1 boot=UUID=${uuid}/"  ${mygrub}

###### that Red Hat solution I cited earlier in the comments, this is where this came from
# update /etc/default/grub for subsequent kernel updates. this APPENDS to the end of the line.  
sed -i "/^GRUB_CMDLINE_LINUX/ s/\"$/  fips=1 boot=UUID=${uuid}\"/" /etc/default/grub
grep -q GRUB_CMDLINE_LINUX_DEFAULT /etc/default/grub || echo 'GRUB_CMDLINE_LINUX_DEFAULT="fips=1"' >> /etc/default.grub
echo -e "\n\tThe next line shows the new grub line with fips in the two locations below:\n"
grep $uuid ${mygrub} | grep linux16
echo;grep $uuid /etc/default/grub

### warning ### warning ###
### Note, if you do not change Ciphers and MACs prior to reboot, you will NOT be able to ssh to the system.  That could be a problem depending on the distance or difficulty of getting a console or physical access to fix after reboot.  Be warned.
###
mydate=`date '+%Y%m%d_%H_%M_%S'`;echo $mydate
cp -v /etc/ssh/sshd_config{,.$mydate}

# without this, no ssh, really, ask me how I know
sed -i 's/^Cipher.*/Ciphers aes128-ctr,aes192-ctr,aes256-ctr/' /etc/ssh/sshd_config
sed -i 's/^MACs.*/MACs hmac-sha2-256,hmac-sha2-512/' /etc/ssh/sshd_config

# bread crumbs
touch /root/fipsinstalled
chattr +i /root/fipsinstalled

###### the command to check this after reboot is: sysctl crypto.fips_enabled
echo -e "\n\tScript has completed.  \n\tSystem must be rebooted for fips to be enabled.  \n\tPlease check the following 2 files for sane entries:\n\t/etc/default/grub \n\t${mygrub}.  \n\n\tAlso, --AFTER--REBOOT--as-root-- run sysctl crypto.fips_enabled and the output must be \n\t'crypto.fips_enabled = 1' \n"

##### without this, the disa provided stig checker fails fips compliance, you're welcome
echo 'GRUB_CMDLINE_LINUX_DEFAULT="fips=1"' >> /etc/default/grub
rpm -q prelink > /dev/null && rpm -e prelink > /dev/null
##### Same with this...
/bin/chmod 0600 /etc/ssh/ssh_host*key
###

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More