You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
14 KiB
390 lines
14 KiB
4 months ago
|
--- a/scripts/create_runtime_policy.sh 2024-01-30 18:17:19.000000000 +0100
|
||
|
+++ b/scripts/create_runtime_policy.sh 2024-08-16 17:25:50.871701853 +0200
|
||
|
@@ -1,282 +1,155 @@
|
||
|
-#!/usr/bin/env bash
|
||
|
+#!/usr/bin/bash
|
||
|
################################################################################
|
||
|
# SPDX-License-Identifier: Apache-2.0
|
||
|
# Copyright 2017 Massachusetts Institute of Technology.
|
||
|
################################################################################
|
||
|
|
||
|
-
|
||
|
-if [ $0 != "-bash" ] ; then
|
||
|
- pushd `dirname "$0"` > /dev/null 2>&1
|
||
|
-fi
|
||
|
-KCRP_BASE_DIR=$(pwd)
|
||
|
-if [ $0 != "-bash" ] ; then
|
||
|
- popd 2>&1 > /dev/null
|
||
|
-fi
|
||
|
-KCRP_BASE_DIR=$KCRP_BASE_DIR/..
|
||
|
-
|
||
|
-function detect_hash {
|
||
|
- local hashstr=$1
|
||
|
-
|
||
|
- case "${#hashstr}" in
|
||
|
- 32) hashalgo=md5sum ;;
|
||
|
- 40) hashalgo=sha1sum ;;
|
||
|
- 64) hashalgo=sha256sum ;;
|
||
|
- 128) hashalgo=sha512sum ;;
|
||
|
- *) hashalgo="na";;
|
||
|
- esac
|
||
|
-
|
||
|
- echo $hashalgo
|
||
|
-}
|
||
|
-
|
||
|
-function announce {
|
||
|
- # 1 - MESSAGE
|
||
|
-
|
||
|
- MESSAGE=$(echo "${1}" | tr '\n' ' ')
|
||
|
- MESSAGE=$(echo $MESSAGE | sed "s/\t\t*/ /g")
|
||
|
-
|
||
|
- echo "==> $(date) - ${0} - $MESSAGE"
|
||
|
-}
|
||
|
-
|
||
|
-function valid_algo {
|
||
|
- local algo=$1
|
||
|
-
|
||
|
- [[ " ${ALGO_LIST[@]} " =~ " ${algo} " ]]
|
||
|
-}
|
||
|
-
|
||
|
# Configure the installer here
|
||
|
INITRAMFS_TOOLS_GIT=https://salsa.debian.org/kernel-team/initramfs-tools.git
|
||
|
INITRAMFS_TOOLS_VER="master"
|
||
|
|
||
|
-# All defaults
|
||
|
-ALGO=sha1sum
|
||
|
-WORK_DIR=/tmp/kcrp
|
||
|
-OUTPUT_DIR=${WORK_DIR}/output
|
||
|
-ALLOWLIST_DIR=${WORK_DIR}/allowlist
|
||
|
-INITRAMFS_LOC="/boot/"
|
||
|
-INITRAMFS_STAGING_DIR=${WORK_DIR}/ima_ramfs/
|
||
|
-INITRAMFS_TOOLS_DIR=${WORK_DIR}/initramfs-tools
|
||
|
-BOOT_AGGREGATE_LOC="/sys/kernel/security/ima/ascii_runtime_measurements"
|
||
|
-ROOTFS_LOC="/"
|
||
|
-EXCLUDE_LIST="none"
|
||
|
-SKIP_PATH="none"
|
||
|
-ALGO_LIST=("sha1sum" "sha256sum" "sha512sum")
|
||
|
+WORKING_DIR=$(readlink -f "$0")
|
||
|
+WORKING_DIR=$(dirname "$WORKING_DIR")
|
||
|
|
||
|
# Grabs Debian's initramfs_tools from Git repo if no other options exist
|
||
|
if [[ ! `command -v unmkinitramfs` && ! -x "/usr/lib/dracut/skipcpio" ]] ; then
|
||
|
# Create temp dir for pulling in initramfs-tools
|
||
|
- announce "INFO: Downloading initramfs-tools: $INITRAMFS_TOOLS_DIR"
|
||
|
+ TMPDIR=`mktemp -d` || exit 1
|
||
|
+ echo "INFO: Downloading initramfs-tools: $TMPDIR"
|
||
|
|
||
|
- mkdir -p $INITRAMFS_TOOLS_DIR
|
||
|
# Clone initramfs-tools repo
|
||
|
- pushd $INITRAMFS_TOOLS_DIR > /dev/null 2>&1
|
||
|
- git clone $INITRAMFS_TOOLS_GIT initramfs-tools > /dev/null 2>&1
|
||
|
- pushd initramfs-tools > /dev/null 2>&1
|
||
|
- git checkout $INITRAMFS_TOOLS_VER > /dev/null 2>&1
|
||
|
- popd > /dev/null 2>&1
|
||
|
- popd > /dev/null 2>&1
|
||
|
+ pushd $TMPDIR
|
||
|
+ git clone $INITRAMFS_TOOLS_GIT initramfs-tools
|
||
|
+ pushd initramfs-tools
|
||
|
+ git checkout $INITRAMFS_TOOLS_VER
|
||
|
+ popd # $TMPDIR
|
||
|
+ popd
|
||
|
|
||
|
shopt -s expand_aliases
|
||
|
- alias unmkinitramfs=$INITRAMFS_TOOLS_DIR/initramfs-tools/unmkinitramfs
|
||
|
-
|
||
|
- which unmkinitramfs > /dev/null 2>&1 || exit 1
|
||
|
+ alias unmkinitramfs=$TMPDIR/initramfs-tools/unmkinitramfs
|
||
|
fi
|
||
|
|
||
|
+
|
||
|
if [[ $EUID -ne 0 ]]; then
|
||
|
echo "This script must be run as root" 1>&2
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
-USAGE=$(cat <<-END
|
||
|
- Usage: $0 -o/--output_file FILENAME [-a/--algo ALGO] [-x/--ramdisk-location PATH] [-y/--boot_aggregate-location PATH] [-z/--rootfs-location PATH] [-e/--exclude_list FILENAME] [-s/--skip-path PATH] [-h/--help]
|
||
|
+if [ $# -lt 1 ]
|
||
|
+then
|
||
|
+ echo "No arguments provided" >&2
|
||
|
+ echo "Usage: `basename $0` -o [filename] -h [hash-algo]" >&2
|
||
|
+ exit $NOARGS;
|
||
|
+fi
|
||
|
|
||
|
- optional arguments:
|
||
|
- -a/--algo (checksum algorithm to be used, default: $ALGO)
|
||
|
- -x/--ramdisk-location (path to initramdisk, default: $INITRAMFS_LOC, set to "none" to skip)
|
||
|
- -y/--boot_aggregate-location (path for IMA log, used for boot aggregate extraction, default: $BOOT_AGGREGATE_LOC, set to "none" to skip)
|
||
|
- -z/--rootfs-location (path to root filesystem, default: $ROOTFS_LOC, cannot be skipped)
|
||
|
- -e/--exclude_list (filename containing a list of paths to be excluded (i.e., verifier will not try to match checksums, default: $EXCLUDE_LIST)
|
||
|
- -s/--skip-path (comma-separated path list, files found there will not have checksums calculated, default: $SKIP_PATH)
|
||
|
- -h/--help (show this message and exit)
|
||
|
-END
|
||
|
-)
|
||
|
+ALGO=sha256sum
|
||
|
|
||
|
-while [[ $# -gt 0 ]]
|
||
|
-do
|
||
|
- key="$1"
|
||
|
+ALGO_LIST=("sha1sum" "sha256sum" "sha512sum")
|
||
|
+
|
||
|
+valid_algo() {
|
||
|
+ local algo=$1
|
||
|
|
||
|
- case $key in
|
||
|
- -a|--algo)
|
||
|
- ALGO="$2"
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -a=*|--algo=*)
|
||
|
- ALGO=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -x|--ramdisk-location)
|
||
|
- INITRAMFS_LOC="$2"
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -x=*|--ramdisk-location=*)
|
||
|
- INITRAMFS_LOC=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -y|--boot_aggregate-location)
|
||
|
- BOOT_AGGREGATE_LOC=$2
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -y=*|--boot_aggregate-location=*)
|
||
|
- BOOT_AGGREGATE_LOC=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -z|--rootfs-location)
|
||
|
- ROOTFS_LOC=$2
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -z=*|--rootfs-location=*)
|
||
|
- ROOTFS_LOC=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -e|--exclude_list)
|
||
|
- EXCLUDE_LIST=$2
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -e=*|--exclude_list=*)
|
||
|
- EXCLUDE_LIST=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -o=*|--output_file=*)
|
||
|
- OUTPUT=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -o|--output_file)
|
||
|
- OUTPUT=$2
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -s=*|--skip-path=*)
|
||
|
- SKIP_PATH=$(echo $key | cut -d '=' -f 2)
|
||
|
- ;;
|
||
|
- -s|--skip-path)
|
||
|
- SKIP_PATH=$2
|
||
|
- shift
|
||
|
- ;;
|
||
|
- -h|--help)
|
||
|
- printf "%s\n" "$USAGE"
|
||
|
- exit 0
|
||
|
- shift
|
||
|
- ;;
|
||
|
- *)
|
||
|
- # unknown option
|
||
|
- ;;
|
||
|
- esac
|
||
|
- shift
|
||
|
+ [[ " ${ALGO_LIST[@]} " =~ " ${algo} " ]]
|
||
|
+}
|
||
|
+
|
||
|
+while getopts ":o:h:" opt; do
|
||
|
+ case $opt in
|
||
|
+ o)
|
||
|
+ OUTPUT=$(readlink -f $OPTARG)
|
||
|
+ rm -f $OUTPUT
|
||
|
+ ;;
|
||
|
+ h)
|
||
|
+ if valid_algo $OPTARG; then
|
||
|
+ ALGO=$OPTARG
|
||
|
+ else
|
||
|
+ echo "Invalid hash function argument: use sha1sum, sha256sum, or sha512sum"
|
||
|
+ exit 1
|
||
|
+ fi
|
||
|
+ ;;
|
||
|
+ esac
|
||
|
done
|
||
|
|
||
|
-if ! valid_algo $ALGO
|
||
|
+if [ ! "$OUTPUT" ]
|
||
|
then
|
||
|
- echo "Invalid hash function argument: pick from \"${ALGO_LIST[@]}\""
|
||
|
+ echo "Missing argument for -o" >&2;
|
||
|
+ echo "Usage: $0 -o [filename] -h [hash-algo]" >&2;
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
-if [[ -z $OUTPUT ]]
|
||
|
-then
|
||
|
- printf "%s\n" "$USAGE"
|
||
|
- exit 1
|
||
|
+
|
||
|
+# Where to look for initramfs image
|
||
|
+INITRAMFS_LOC="/boot"
|
||
|
+if [ -d "/ostree" ]; then
|
||
|
+ # If we are on an ostree system change where we look for initramfs image
|
||
|
+ loc=$(grep -E "/ostree/[^/]([^/]*)" -o /proc/cmdline | head -n 1 | cut -d / -f 3)
|
||
|
+ INITRAMFS_LOC="/boot/ostree/${loc}/"
|
||
|
fi
|
||
|
|
||
|
-rm -rf $ALLOWLIST_DIR
|
||
|
-rm -rf $INITRAMFS_STAGING_DIR
|
||
|
-rm -rf $OUTPUT_DIR
|
||
|
|
||
|
-announce "Writing allowlist $ALLOWLIST_DIR/${OUTPUT} with $ALGO..."
|
||
|
-mkdir -p $ALLOWLIST_DIR
|
||
|
+echo "Writing allowlist to $OUTPUT with $ALGO..."
|
||
|
|
||
|
-if [[ $BOOT_AGGREGATE_LOC != "none" ]]
|
||
|
-then
|
||
|
- announce "--- Adding boot agregate from $BOOT_AGGREGATE_LOC on allowlist $ALLOWLIST_DIR/${OUTPUT} ..."
|
||
|
# Add boot_aggregate from /sys/kernel/security/ima/ascii_runtime_measurements (IMA Log) file.
|
||
|
# The boot_aggregate measurement is always the first line in the IMA Log file.
|
||
|
# The format of the log lines is the following:
|
||
|
# <PCR_ID> <PCR_Value> <IMA_Template> <File_Digest> <File_Name> <File_Signature>
|
||
|
# File_Digest may start with the digest algorithm specified (e.g "sha1:", "sha256:") depending on the template used.
|
||
|
- head -n 1 $BOOT_AGGREGATE_LOC | awk '{ print $4 " boot_aggregate" }' | sed 's/.*://' >> $ALLOWLIST_DIR/${OUTPUT}
|
||
|
+head -n 1 /sys/kernel/security/ima/ascii_runtime_measurements | awk '{ print $4 " boot_aggregate" }' | sed 's/.*://' >> $OUTPUT
|
||
|
|
||
|
- bagghash=$(detect_hash $(cat $ALLOWLIST_DIR/${OUTPUT} | cut -d ' ' -f 1))
|
||
|
- if [[ $ALGO != $bagghash ]]
|
||
|
- then
|
||
|
- announce "ERROR: \"boot aggregate\" has was calculated with $bagghash, but files will be calculated with $ALGO. Use option -a $bagghash"
|
||
|
- exit 1
|
||
|
- fi
|
||
|
-else
|
||
|
- announce "--- Skipping boot aggregate..."
|
||
|
-fi
|
||
|
-
|
||
|
-announce "--- Adding all appropriate files from $ROOTFS_LOC on allowlist $ALLOWLIST_DIR/${OUTPUT} ..."
|
||
|
# Add all appropriate files under root FS to allowlist
|
||
|
-pushd $ROOTFS_LOC > /dev/null 2>&1
|
||
|
-BASE_EXCLUDE_DIRS="\bsys\b\|\brun\b\|\bproc\b\|\blost+found\b\|\bdev\b\|\bmedia\b\|\bsnap\b\|\bmnt\b\|\bvar\b\|\btmp\b"
|
||
|
-ROOTFS_FILE_LIST=$(ls | grep -v $BASE_EXCLUDE_DIRS)
|
||
|
-if [[ $SKIP_PATH != "none" ]]
|
||
|
-then
|
||
|
- SKIP_PATH=$(echo $SKIP_PATH | sed -e "s#^$ROOTFS_LOC##g" -e "s#,$ROOTFS_LOC##g" -e "s#,#\\\|#g")
|
||
|
- ROOTFS_FILE_LIST=$(echo "$ROOTFS_FILE_LIST" | grep -v "$SKIP_PATH")
|
||
|
-fi
|
||
|
-find $ROOTFS_FILE_LIST \( -fstype rootfs -o -xtype f -type l -o -type f \) -uid 0 -exec $ALGO "$ROOTFS_LOC/{}" >> $ALLOWLIST_DIR/${OUTPUT} \;
|
||
|
-popd > /dev/null 2>&1
|
||
|
+cd /
|
||
|
+find `ls / | grep -v "\bsys\b\|\brun\b\|\bproc\b\|\blost+found\b\|\bdev\b\|\bmedia\b\|\bsnap\b\|mnt"` \( -fstype rootfs -o -xtype f -type l -o -type f \) -uid 0 -exec $ALGO '/{}' >> $OUTPUT \;
|
||
|
|
||
|
# Create staging area for init ram images
|
||
|
-mkdir -p $INITRAMFS_STAGING_DIR
|
||
|
-
|
||
|
-if [[ $INITRAMFS_LOC != "none" ]]
|
||
|
-then
|
||
|
- # Where to look for initramfs image
|
||
|
- if [[ -d "/ostree" ]]
|
||
|
- then
|
||
|
- X=$INITRAMFS_LOC
|
||
|
- # If we are on an ostree system change where we look for initramfs image
|
||
|
- loc=$(grep -E "/ostree/[^/]([^/]*)" -o /proc/cmdline | head -n 1 | cut -d / -f 3)
|
||
|
- INITRAMFS_LOC="/boot/ostree/${loc}/"
|
||
|
- announce "--- The location of initramfs was overriden from \"${X}\" to \"$INITRAMFS_LOC\""
|
||
|
- fi
|
||
|
+rm -rf /tmp/ima/
|
||
|
+mkdir -p /tmp/ima
|
||
|
|
||
|
- announce "--- Creating allowlist for init ram disks found under \"$INITRAMFS_LOC\" to $ALLOWLIST_DIR/${OUTPUT} ..."
|
||
|
- for i in $(ls ${INITRAMFS_LOC}/initr* 2> /dev/null)
|
||
|
- do
|
||
|
- announce " extracting $i"
|
||
|
- mkdir -p $INITRAMFS_STAGING_DIR/$i-extracted
|
||
|
- cd $INITRAMFS_STAGING_DIR/$i-extracted
|
||
|
-
|
||
|
- # platform-specific handling of init ram disk images
|
||
|
- if [[ `command -v unmkinitramfs` ]] ; then
|
||
|
- mkdir -p $INITRAMFS_STAGING_DIR/$i-extracted-unmk
|
||
|
- unmkinitramfs $i $INITRAMFS_STAGING_DIR/$i-extracted-unmk
|
||
|
- if [[ -d "$INITRAMFS_STAGING_DIR/$i-extracted-unmk/main/" ]] ; then
|
||
|
- cp -r $INITRAMFS_STAGING_DIR/$i-extracted-unmk/main/. /tmp/ima/$i-extracted
|
||
|
- else
|
||
|
- cp -r $INITRAMFS_STAGING_DIR/$i-extracted-unmk/. /tmp/ima/$i-extracted
|
||
|
- fi
|
||
|
- elif [[ -x "/usr/lib/dracut/skipcpio" ]] ; then
|
||
|
- /usr/lib/dracut/skipcpio $i | gunzip -c | cpio -i -d 2> /dev/null
|
||
|
+# Iterate through init ram disks and add files to allowlist
|
||
|
+echo "Creating allowlist for init ram disk"
|
||
|
+for i in `ls ${INITRAMFS_LOC}/initr*`
|
||
|
+do
|
||
|
+ echo "extracting $i"
|
||
|
+ mkdir -p /tmp/ima/$i-extracted
|
||
|
+ cd /tmp/ima/$i-extracted
|
||
|
+
|
||
|
+ # platform-specific handling of init ram disk images
|
||
|
+ if [[ `command -v unmkinitramfs` ]] ; then
|
||
|
+ mkdir -p /tmp/ima/$i-extracted-unmk
|
||
|
+ unmkinitramfs $i /tmp/ima/$i-extracted-unmk
|
||
|
+ if [[ -d "/tmp/ima/$i-extracted-unmk/main/" ]] ; then
|
||
|
+ cp -r /tmp/ima/$i-extracted-unmk/main/. /tmp/ima/$i-extracted
|
||
|
else
|
||
|
- announce "ERROR: No tools for initramfs image processing found!"
|
||
|
- exit 1
|
||
|
+ cp -r /tmp/ima/$i-extracted-unmk/. /tmp/ima/$i-extracted
|
||
|
fi
|
||
|
+ elif [[ -x "/usr/lib/dracut/skipcpio" ]] ; then
|
||
|
+ /usr/lib/dracut/skipcpio $i | gunzip -c 2> /dev/null | cpio -i -d 2> /dev/null
|
||
|
+ else
|
||
|
+ echo "ERROR: No tools for initramfs image processing found!"
|
||
|
+ break
|
||
|
+ fi
|
||
|
|
||
|
- find -type f -exec $ALGO "./{}" \; | sed "s| \./\./| /|" >> $ALLOWLIST_DIR/${OUTPUT}
|
||
|
- done
|
||
|
-fi
|
||
|
+ find -type f -exec $ALGO "./{}" \; | sed "s| \./\./| /|" >> $OUTPUT
|
||
|
+done
|
||
|
|
||
|
-# Non-critical cleanup on the resulting file (when ROOTFS_LOC = '/', the path starts on allowlist ends up with double '//' )
|
||
|
-sed -i "s^ //^ /^g" $ALLOWLIST_DIR/${OUTPUT}
|
||
|
-# A bit of cleanup on the resulting file (among other problems, sha256sum might output a hash with the prefix '\\')
|
||
|
-sed -i "s/^\\\//g" $ALLOWLIST_DIR/${OUTPUT}
|
||
|
-
|
||
|
-# Convert to runtime policy
|
||
|
-mkdir -p $OUTPUT_DIR
|
||
|
-announce "Converting created allowlist ($ALLOWLIST_DIR/${OUTPUT}) to Keylime runtime policy ($OUTPUT_DIR/${OUTPUT}) ..."
|
||
|
-CONVERT_CMD_OPTS="--allowlist $ALLOWLIST_DIR/${OUTPUT} --output_file $OUTPUT_DIR/${OUTPUT}"
|
||
|
-[ -f $EXCLUDE_LIST ] && CONVERT_CMD_OPTS="$CONVERT_CMD_OPTS --excludelist $EXCLUDE_LIST"
|
||
|
-
|
||
|
-pushd $KCRP_BASE_DIR > /dev/null 2>&1
|
||
|
-export PYTHONPATH=$KCRP_BASE_DIR:$PYTHONPATH
|
||
|
-# only 3 dependencies required: pip3 install cryptography lark packaging
|
||
|
-python3 ./keylime/cmd/convert_runtime_policy.py $CONVERT_CMD_OPTS; echo " "
|
||
|
-if [[ $? -eq 0 ]]
|
||
|
-then
|
||
|
- announce "Done, new runtime policy file present at ${OUTPUT_DIR}/$OUTPUT. It can be used on the tenant keylime host with \"keylime_tenant -c add --runtime-policy ${OUTPUT_DIR}/$OUTPUT <other options>"
|
||
|
-fi
|
||
|
-popd > /dev/null 2>&1
|
||
|
+# when ROOTFS_LOC = '/', the path starts on allowlist ends up with double '//'
|
||
|
+#
|
||
|
+# Example:
|
||
|
+#
|
||
|
+# b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c //bar
|
||
|
+#
|
||
|
+# Replace the unwanted '//' with a single '/'
|
||
|
+sed -i 's| /\+| /|g' ${OUTPUT}
|
||
|
+
|
||
|
+# When the file name contains newlines or backslashes, the output of sha256sum
|
||
|
+# adds a backslash at the beginning of the line.
|
||
|
+#
|
||
|
+# Example:
|
||
|
+#
|
||
|
+# $ echo foo > ba\\r
|
||
|
+# $ sha256sum ba\\r
|
||
|
+# \b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c ba\\r
|
||
|
+#
|
||
|
+# Remove the unwanted backslash prefix
|
||
|
+sed -i 's/^\\//g' ${OUTPUT}
|
||
|
+
|
||
|
+# Clean up
|
||
|
+rm -rf /tmp/ima
|