Script to implement FIPS (script updated 10/17/2018)

Latest response

I made a script to implement FIPS 140-2 compliance based on what I found at https://access.redhat.com/solutions/137833. I suspect Red-Hatter Ryan Sawhill put that solution together.

added 6/5/2019

By the way, for those of you who need efi/uefi partition, use this in your kickstart:

part /boot/efi --fstype="vfat" --size=3100

Yeah, the 3100 is overkill, I'm good with that.

  • I have initially used uname -r to detect the version of Linux (i.e. 7).
  • I did not use /etc/redhat-release as part of a conditional test because I have seen too many admins in various environments malign/edit /etc/redhat-release file for a number of reasons, so I personally can not trust the integrity of that file, and shall opt for a less-fragile method
  • Hoever, I may transition this to an rpm check for redhat-release-server however, we use this script for both rhel 7.x workstation & server.
  • I am going to test a method for CentOS as well.

I realize the script can certainly be improved on.

Also, if anyone believes this script misses anything, I welcome feedback

Thanks Dusan Baljevic for finding errant typos and testing on UEFI systems (8/24/2018)

Regards,

-RJ

#!/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}
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


Numerous updates 8/23/2018

sha256sum File name
08383d4290d10bb55feac1deaacf3a3ab929445d3c1f7023df5548a9a93ea4f9 enablefips_20180823.bash
06f59213eafa2d09a55f49e55156dac32e0e99f70bd7edda8a154280b6ef4d76 enablefips_20180823.bash.tar
a1525a33ec30f64ad1eb397fdc42ab3a55c253a26e317a7a9ecc8ed843668300 enablefips_20180824.bash
9277aa4e4ab00c9581fd9c9b22691298ed9ed97285626139add034e9c8c3a7ae enablefips_20180824.bash_.tar

Attachments

Responses

Hi,

Your script worked well on RHEL 7.5 VM (VMware). The only issue was a slight typing error:

...
        Script has completed.  
        System must be rebooted for fips to be enabled.  
        Please check the following 2 files for sane entries:
        /etc/default/grub 
        /boot/grub2/grub.conf.  

With GRUB2, the config is grub.cfg, not grub.conf.

This was the check on my test system:

# diff /etc/default/grub /etc/default/grub.20180702_10_00_29 
6c6
< GRUB_CMDLINE_LINUX="nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0"  fips=1 boot=UUID=34ddebd6-f281-401b-9ad8-e009b08337f1
---
> GRUB_CMDLINE_LINUX="nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0"

# diff /boot/grub2/grub.cfg /boot/grub2/grub.cfg.20180702_10_00_29 
114c114
<       linux16 /vmlinuz-3.10.0-862.3.2.el7.x86_64 root=/dev/mapper/os-root ro nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0 LANG=en_US.UTF-8 fips=1 boot=UUID=34ddebd6-f281-401b-9ad8-e009b08337f1
---
>       linux16 /vmlinuz-3.10.0-862.3.2.el7.x86_64 root=/dev/mapper/os-root ro nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0 LANG=en_US.UTF-8

# diff /etc/ssh/sshd_config /etc/ssh/sshd_config.20180702_10_00_29 
146c146
< MACs hmac-sha2-256,hmac-sha2-512
---
> MACs hmac-sha1,umac-64@openssh.com,hmac-ripemd160

# sysctl crypto.fips_enabled 
crypto.fips_enabled = 1

Best wishes,

Dusan Baljevic (amateur radio VK2COT)

Something I forgot to mention...

It would be good to be careful about proceeding in each step if it fails.

For example, abort if the dracut-fips cannot be installed (for whatever reason):

yum -y install dracut-fips || exit

If it is run on a system which already has it, it will proceed safely:

# yum -y install dracut-fips || exit
Loaded plugins: langpacks, product-id, rhnplugin, search-disabled-repos, subscription-manager
This system is receiving updates from RHN Classic or Red Hat Satellite.
Package dracut-fips-033-535.el7.x86_64 already installed and latest version
Nothing to do

Also, I would prefer to check if FIPS was already installed without relying on a template file /root/fipsinstalled. Something like this:

if [ "$(sysctl crypto.fips_enabled | awk -F= '{print $2}' | sed -e 's/^[[:space:]]*//')" == 1 ]; then
   echo -e "\tThis script was ran previously,\n\t nothing to do, \n\texiting..."
   exit 1
fi

Regards,

Dusan Baljevic (amateur radio VK2COT)

... And on physical servers (in this case HP blade), the config file is:

/boot/efi/EFI/redhat/grub.cfg

... so it would be good to preserve the original version before installation. Something like:

/boot/efi/EFI/redhat/grub.cfg.20180702_14_24_02

Regards,

Dusan Baljevic (amateur radio VK2COT)

Dusan,

I like your suggestions, I fixed the typo.

I think I'll edit the script with some modifications in light of your good feedback.

I don't have A UEFI (EFI) system, so I'll have to research that.

Thanks Dusan

RJ

Hi Dusan,

EFI boot is not hardware specific, some Virtualization Platforms support it too. Still a valid hint to RJ.

Regards,

Jan Gerrit

I've seen EFI even available on some workstations.

Another edit I detected last week, but had not entered here...

The script now reads with this:

#next line edited 7/2/2018 to make the quote be part of the sed replacement, else the directives are put AFTER the quote.  The below resolves this.
# update /etc/default/grub for subsequent kernel updates
sed -i "/^GRUB_CMDLINE_LINUX/ s/\"$/  fips=1 boot=UUID=${uuid}\"/" /etc/default/grub

Dusan (and anyone else) edit your /etc/default/grub so that your quote comes after the directives.

Example: Dusan, your /etc/default/grub above reads as:

GRUB_CMDLINE_LINUX="nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0"  fips=1 boot=UUID=34ddebd6-f281-401b-9ad8-e009b08337f1

Change it to read as follows:

GRUB_CMDLINE_LINUX="nofb splash=quiet crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap rd.lvm.lv=os/usr rhgb quiet audit=1 net.ifnames=0 biosdevname=0  fips=1 boot=UUID=34ddebd6-f281-401b-9ad8-e009b08337f1"

The difference is the placement of the quote.

More changes to come (as discussed above).

Hi,

Good point about EFI. I only mentioned it for physical servers because that is what I could test :)

In regards how to check it, here are some possibilities:

  1. Verify if directory /sys/firmware/efi exists.

  2. Verify results of command:

efibootmgr

It is nice to see friendly co-operation in the forum.

Regards,

Dusan Baljevic (amateur radio VK2COT)

I haven't had a chance to copy over my changes here yet, I shall.

Thanks for your input Dusan (and appreciate all inputs, to make this better).

Nice, Well done.

Dusan, Wade, Ryan Sawhill, Jan, and anyone else... I updated the script (actually a while ago, finally got around to posting it here, and thanks Wade for the comment prior to this).

I'm open to any improvements or things that ought to be changed (it's not the prettiest script, and it could be made cleaner, but it's functional)

The next edit I'll probably also add the other suggestion Dusan made about checing sysctl crypto.fips_enabled (however, the other checks are the foundation for this to be in place).

Regards,

RJ

Hi RJ,

Very good effort on your side.

I just ran the updated script on one of my RHEL 7.5 VMs and it worked, with two small errors:

a) It complained about prelink missing, but kept on trying to use it or refer to it five times.

b) There is a slight typing error on line 98. The variable is not "$etcdefgub" but should be "$etcdefgrub".

if [ "$myfipschecketcdefgrub" != "" ] ; then
     95         echo -e "FIPS directives detected in ($etcdefgrub), \n\t\t($myfipschecketcdefgrub)\n\tSo, recommend AGAINST running this script\n\t$manualreview"
     96         echo exit 1
     97     else
     98         echo -e "\n\tFIPS directives not detected in ($etcdefgub)\n\tproceeding..."
     99 fi

As a consequence, one gets ambiguous result of the run:

...
        FIPS directives not detected in (/boot/grub2/grub.cfg)
        proceeding...

        FIPS directives not detected in ()
        proceeding...
...

I hope you do not mind me being pedantic :)

Regards,

Dusan Baljevic (amateur radio VK2COT)

Dusan,

Thanks for the valuable feedback, I updated the script,

Cheers RJ

Dusan, I added your suggestion, albeit with a different method... - and thanks

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

File added for direct download. sha256 sums included. I had to make it a tar file in order to upload it.

Next stop, I'll make an edition to include UEFI. However, I have no UEFI systems so perhaps someone with a UEFI system might test it and give feedback.

Hi R. Hinton,

Good work, as always.

I ran the script on RHEL 7.5 HP Blade:

dmidecode | awk '/Product Name|UEFI/ {print}' | sed -e 's/^[[:space:]]*//g' && sysctl crypto.fips_enabled

UEFI is supported
Product Name: ProLiant BL460c Gen9
UEFI: No
crypto.fips_enabled = 0

In the first attempt, the script failed due to the following:

         fips not currently activated, so proceeding with script. 

UEFI

        This system is a UEFI system and this script requires further testing before production with UEFI.
        Perform yourown tests implementing FIPS on this (UEFI) system.
        Bailing...

After I removed the "exit 1" on line 55, it worked:

if [ "$fw" == "UEFI" ] ; then
        echo -e "\n\tThis system is a UEFI system and this script requires further testing before production with UEFI.\n\tPerform yourown tests implementing FIPS on this ($fw) system.\n\tBailing..."
        mygrub='/boot/efi/EFI/redhat/grub.cfg'  
        exit 1

A few suggestions:

a) Typing error:

"yourown" should probably be "your own"

b) At least on my UEFI server, both of these files exist:

ls -als /boot/efi/EFI/redhat/grub.cfg /boot/grub2/grub.cfg
16 -rwx------ 1 root root 10008 Aug 24 13:28 /boot/efi/EFI/redhat/grub.cfg
 8 -rw-r--r-- 1 root root  6619 Aug 24 13:28 /boot/grub2/grub.cfg

c) There is no "/boot/grub2/grub.conf" in my UEFI server. Instead, there is "/boot/grub2/grub.cfg".

Therefore, this line might need to be changed:

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/boot/grub2/grub.conf.  \n\n\tAlso, --AFTER--REBOOT--as-root-- run sysctl crypto.fips_enabled and the output must be \n\t'crypto.fips_enabled = 1' \n"

d) Maybe you could also add the option to remove FIPS. Something like options to the script.

This worked for me:

yum -y remove dracut-fips\*
dracut --force
grubby --update-kernel=ALL --remove-args=fips=1
sed -i 's/ fips=1//' /etc/default/grub
chattr -i /root/fipsinstalled && rm /root/fipsinstalled

Final result after running your script shows success:

dmidecode | awk '/Product Name|UEFI/ {print}' | sed -e 's/^[[:space:]]*//g' && sysctl crypto.fips_enabled

UEFI is supported
Product Name: ProLiant BL460c Gen9
UEFI: No
crypto.fips_enabled = 1

Regards,

Dusan Baljevic (Amateur Radio Vk2COT)

Dusan,

Thank you very much for the valuable feedback, and for testing this on UEFI. I've made the useful changes you presented. If you or others find anything else that ought to be resolved, let me know.

Regards,

RJ

Hi RJ,

In my free time, I added support for uninstalling FIPS.

Maybe you find it useful in the final version :)

#!/bin/bash
# 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 9/18/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. 
#

# Set PATH varaible to secure directories
#
PATH=/sbin:/bin:/usr/bin:/usr/sbin; export PATH

# Variables
sshdconf="/etc/ssh/sshd_config"
defgrub="/etc/default/grub"
aideconf="/etc/aide.conf"

# Default options
#
opt1=""
opt2=""

##### rectives... 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"

## 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
fi

# Usage reminder
#
usage() {
   cat <<EOF
USAGE: \$0 [-i|-d|-h] 
       -d     Deinstall FIPS   
       -h     Help Menu 
       -i     Implement FIPS   
EOF
}

# Process command-line arguments
#
while getopts "dhi" o; do
   case "${o}" in
           i) opt1="INSTALL" ;;
           d) opt2="DEINSTALL" ;;
           h) usage ;;
           *) echo "ERROR: Wrong command-line arguments"
              usage
              exit 1 ;;
   esac
done
shift $((OPTIND-1))

selectedoption="${opt1:-$opt2}"

case "$selectedoption" in
       INSTALL)   ;; 
       DEINSTALL) if [ "$answer" == "$yes" ] ; then 
                          yum -y remove dracut-fips\*
                          dracut --force
                          grubby --update-kernel=ALL --remove-args=fips=1
                          chattr -i /root/fipsinstalled && rm /root/fipsinstalled
                          echo "Check MACs and Ciphers in $sshdconf"
                          exit 0
                  else
                          echo -e "\n\t $notconfigured - no action required\n"
                  fi
                  ;; 
       *)         exit 1 ;;
esac

##### 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 -a | 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 -a | 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 -a | 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 ${aideconf}{,.undofips}
rpm -q prelink >/dev/null && sed -i 's/^NORMAL.*/NORMAL = FIPSR+sha512/' $aideconf

##### 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}
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}\"/" $defgrub
grep -q GRUB_CMDLINE_LINUX_DEFAULT $defgrub || echo 'GRUB_CMDLINE_LINUX_DEFAULT="fips=1"' >>$defgrub
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 $defgrub

### 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 ${sshdconf}{,.$mydate}

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

# 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${defgrub} \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"' >> $defgrub
rpm -q prelink > /dev/null && rpm -e prelink > /dev/null
##### Same with this...
/bin/chmod 0600 /etc/ssh/ssh_host*key

Regards,

Dusan Baljevic (Amateur radio VK2COT)

Just a heads up that anyone trying to use this on AWS, it will not work. In AWS there is not a dedicated /boot partition. AWS uses /boot and /root as the same thing. I made a few changes to the script to get it to work. I think you could add a check to see if a /boot and /root partition exist and if they do do what you currently have written, otherwise the changes would be:

# set the uuid variable to be used later
uuid=$(findmnt -no uuid /root)
echo -e "\n\t Just for reference, the /root uuid is: ($uuid)\n"

(changed /boot to /root)

# update /etc/default/grub for subsequent kernel updates. this APPENDS to the end of the line.  
sed -i "/^GRUB_CMDLINE_LINUX/ s/\"$/  fips=1 root=UUID=${uuid}\"/" $defgrub

(changed boot= to root=)

Note this will probably fail on a DISA stig check as the checker looks for literally 'boot' in the lines but it would just be a false positive.

Thanks Tyler.

Is there a way to definitively determine (command line) an AWS system? perhaps dmidecode or something? If so, I could incorporate it into the script.

Let me know, thanks

RJ

Dusan Baljevic,

Thank you so much, I'll look to add this sometime soon-ish.

Thanks much!

RJ

Dusan,

I had another look, that script you wrote seems complete. I'll run it against some of my crash test victims & clobber mine after the test, and it can be something we both wrote. Thanks much Dusan

RJ

RJ,

You are right, there has to be a better way of determining if a instance is hosted in AWS rather than the no /boot entry.

The proper way to search would be to do a:

cat /sys/hypervisor/uuid

AWS hosted things will have that file and it will start with ec2(then have a string) e.g. ec242583-2ceb-df5b-c9be-d47ceba67912 (no, that is not a real UUID but it is the correct format)

The other option is to do:

sudo cat /sys/devices/virtual/dmi/id/product_uuid

That will also return a string that starts with EC2 e.g. EC258316-3CDB-DG7C-C9BC-E04CEBA37537 (no, that is not a real UUID but it is the correct format)

Hi Tyler, so just to verify, it will always begin with "ec2" then?

Do you believe that to be a valid test? - In other words, can we definitively rule out that non-AWS systems will really never have "ec2" leading the UUID?

Thanks

RJ

RJ,

The first line, cat /sys/hypervisor/uuid, is probably the best check as thats something that AWS puts into their instances. If you run that on any instance not on AWS you will get 'No such file or directory' the second check is just another failsafe. Since you have a few different bail out statements, I think it wouldn't be bad to have 2 checks for AWS.

Here is also a link to an AWS articles that says how to check if an instance is EC2 hosted. It says basically the same thing I listed: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html

Hey thanks Tyler, I'll look into this

Hi,

I can confirm that Tyler's suggestion is fine.

From one instance just checked by my friend:

$ sudo cat /sys/hypervisor/uuid
ec24635b-1f10-733b-bea6-6221ce4e9e60

$ sudo cat /sys/devices/virtual/dmi/id/product_uuid
EC24635B-1F10-733B-BEA6-6221CE4E9E60

And indeed, there is no /boot file system. By default, the EC2 instances only have one file system:

/dev/xvda1      /

Regards,

Dusan Baljevic (Amateur Radio VK2COT)

Dusan, Tyler,

Thanks for the good feedback. I'll rework this. It will require changing several stanzas namely for the /etc/default/grub and the $mygrub location.

RJ

Still intend on adding the good tips Dusan/Tyler

Today I fixed the unpaired quotes in this script (whoops), and also changed checks with uname to use uname -r instead of uname -a because uname -r works more predictably in these tests in some environments.

Excellent script. One comment: from what I've seen checking "crypto.fips_enabled = 1" is not sufficient for determining if a system is FIPS enabled. Also should check if "dracut-fips" package is installed, system behavior changes with this package installed (and UUID set). I've noticed this with respect to apache and gnome. Also the DISA SCAP tool checks for dracut-fips package installation as part of it's FIPS STIG check.