mirror of
https://codeberg.org/andersonarc/reliant-system.git
synced 2025-12-30 23:40:32 -05:00
303 lines
9.2 KiB
Bash
Executable file
303 lines
9.2 KiB
Bash
Executable file
#!/usr/bin/sh
|
|
set -e
|
|
. /usr/local/share/scripts/reliant-common.sh
|
|
reliant_print_help() {
|
|
echo "usage: $0 [SYSTEM ROOT]"
|
|
echo
|
|
echo "Verifies the state of system security."
|
|
}
|
|
|
|
# Check if we're inside the initramfs
|
|
if [ -z "$RELIANT_INITRAMFS" ]; then
|
|
RELIANT_INITRAMFS=$RELIANT_FALSE
|
|
fi
|
|
|
|
# No more variable checks needed
|
|
set -u
|
|
|
|
# Require superuser permissions
|
|
if [ "$EUID" -ne 0 ]; then
|
|
reliant_fail "must be superuser"
|
|
fi
|
|
|
|
# Kernel command line check
|
|
reliant_security_check_cmdline() {
|
|
# Check if /proc/cmdline exists
|
|
if [ ! -f /proc/cmdline ]; then
|
|
reliant_error "reliant_security_check_cmdline: cannot read /proc/cmdline"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Check it for the target string
|
|
if ! grep -q "systemd.volatile=overlay" /proc/cmdline; then
|
|
reliant_error "reliant_security_check_cmdline: systemd.volatile=overlay not found in kernel cmdline"
|
|
return $RELIANT_SECURITY_FAIL
|
|
fi
|
|
|
|
# Pass
|
|
return $RELIANT_OK
|
|
}
|
|
|
|
# No swap files must be active
|
|
reliant_security_check_swap() {
|
|
# Check if /proc/swaps exists
|
|
if [ ! -f /proc/swaps ]; then
|
|
reliant_error "reliant_security_check_swap: cannot read /proc/swaps"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Check if any swapfiles are enabled
|
|
if [ "$(wc -l < /proc/swaps)" -gt 1 ]; then
|
|
reliant_error "reliant_security_check_swap: swap is enabled"
|
|
return $RELIANT_SECURITY_FAIL
|
|
fi
|
|
|
|
# Pass
|
|
return $RELIANT_OK
|
|
}
|
|
|
|
# No network interfaces must be active except for loopback
|
|
reliant_security_check_network() {
|
|
# Check if /sys/class/net exists
|
|
if [ ! -d /sys/class/net ]; then
|
|
reliant_error "reliant_security_check_network: cannot access /sys/class/net"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Check every interface individually
|
|
for interface_path in /sys/class/net/*; do
|
|
if [ ! -d "$interface_path" ]; then
|
|
reliant_error "reliant_security_check_network: cannot access network interface $interface_path"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Determine the interface's name
|
|
interface="${interface_path##*/}"
|
|
|
|
# Allow for the loopback interface to be UP
|
|
if [ "$interface" = "lo" ]; then continue; fi
|
|
|
|
# Check the interface's operational state
|
|
operstate_file="$interface_path/operstate"
|
|
if [ ! -f "$operstate_file" ]; then
|
|
reliant_error "reliant_security_check_network: operstate unavailable for network interface $interface"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Forbid any other interface to be UP
|
|
operstate=$(cat "$interface_path/operstate")
|
|
if [ "$operstate" = "up" ]; then
|
|
reliant_error "reliant_security_check_network: network interface $interface is up"
|
|
return $RELIANT_SECURITY_FAIL
|
|
fi
|
|
done
|
|
|
|
# Pass
|
|
return $RELIANT_OK
|
|
}
|
|
|
|
# Persistent, real filesystems must be readonly
|
|
reliant_security_check_filesystems() {
|
|
# Check each mountpoint
|
|
mount | while read -r line; do
|
|
# Determine the filesystem type
|
|
filesystem_type=$(echo "$line" | awk '{ print $5 }' )
|
|
|
|
# Skip virtual filesystems
|
|
case "$filesystem_type" in
|
|
'rootfs'|'proc'|'sysfs'|'tmpfs'|'devtmpfs'|'securityfs'|'devpts') continue ;;
|
|
'cgroup'|'cgroup2'|'pstore'|'efivarfs'|'bpf'|'configfs'|'overlay') continue ;;
|
|
'autofs'|'mqueue'|'debugfs'|'tracefs'|'xenfs'|'fusectl') continue ;;
|
|
esac
|
|
|
|
# Determine the filesystem flags
|
|
filesystem_flags=$(echo "$line" | awk '{ print $6 }')
|
|
|
|
# Check for readonly flag
|
|
case "$filesystem_flags" in
|
|
"(ro"*) : ;;
|
|
*) reliant_error "reliant_security_check_filesystems: persistent non-readonly filesystem: $line"; return $RELIANT_SECURITY_FAIL ;;
|
|
esac
|
|
done
|
|
|
|
# Pass status from subshell
|
|
return $?
|
|
}
|
|
|
|
# Check if a filesystem is overlay and volatile
|
|
reliant_security_check_overlay() {
|
|
# Validate arguments
|
|
if [ "$#" -ne 1 ]; then
|
|
reliant_error "reliant_security_check_overlay: mountpoint not specified"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
if [ -z "$1" ]; then
|
|
reliant_error "reliant_security_check_overlay: empty mountpoint specified"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Verify that /run is a tmpfs
|
|
run_line=$(mount | grep "tmpfs on /run type tmpfs")
|
|
if [ -z "$run_line" ]; then
|
|
reliant_error "reliant_security_check_overlay: /run is not a temporary filesystem"
|
|
return $RELIANT_SECURITY_FAIL
|
|
fi
|
|
|
|
# Find the mountpoint
|
|
mount | {
|
|
found=$RELIANT_FALSE
|
|
while read -r line; do
|
|
# Filter by mountpoint
|
|
filesystem_mountpoint=$(echo "$line" | awk '{ print $3 }')
|
|
if [ "$filesystem_mountpoint" != "$1" ]; then continue; fi
|
|
|
|
# Filter by filesystem type
|
|
filesystem_type=$(echo "$line" | awk '{ print $5 }' )
|
|
if [ "$filesystem_type" != "overlay" ]; then continue; fi
|
|
|
|
# Determine the filesystem flags
|
|
filesystem_flags=$(echo "$line" | awk '{ print $6 }')
|
|
|
|
# Iterate the flags
|
|
upper_found=$RELIANT_FALSE
|
|
work_found=$RELIANT_FALSE
|
|
IFS=','
|
|
for flag in $filesystem_flags; do
|
|
case $flag in
|
|
'upperdir=/run/systemd/overlay-sysroot/upper') upper_found=$RELIANT_TRUE ;;
|
|
'workdir=/run/systemd/overlay-sysroot/work' ) work_found=$RELIANT_TRUE ;;
|
|
esac
|
|
done
|
|
|
|
# Check if they have been validated
|
|
if [ $work_found -ne $RELIANT_TRUE ]; then
|
|
reliant_error "reliant_security_check_overlay: workdir for $1 could not be validated"
|
|
exit $RELIANT_SECURITY_FAIL
|
|
fi
|
|
if [ $upper_found -ne $RELIANT_TRUE ]; then
|
|
reliant_error "reliant_security_check_overlay: upperdir for $1 could not be validated"
|
|
exit $RELIANT_SECURITY_FAIL
|
|
fi
|
|
|
|
# Indicate that the mountpoint has been found
|
|
found=$RELIANT_TRUE
|
|
break
|
|
done
|
|
|
|
# Check if the mountpoint has been found within the loop
|
|
if [ $found -ne $RELIANT_TRUE ]; then
|
|
reliant_error "reliant_security_check_overlay: mountpoint for overlay root on $1 not found"
|
|
exit $RELIANT_SECURITY_FAIL
|
|
else
|
|
exit $RELIANT_OK
|
|
fi
|
|
}
|
|
|
|
# Forward from subshell
|
|
return $?
|
|
}
|
|
|
|
# Checksum verification
|
|
reliant_security_check_devices() {
|
|
IFS=$'\n'
|
|
for device_path in $(lsblk -rnpo name); do
|
|
device="${device_path##*/}"
|
|
|
|
# Check if it is a valid block device
|
|
if [ ! -b "$device_path" ]; then return $RELIANT_BUG; fi
|
|
|
|
# Some devices must be skipped
|
|
if [ "$device_path" = "$RELIANT_SECURE_DEVICE" ]; then continue; fi
|
|
if [[ "$device_path" =~ ^/dev/mapper/.* ]]; then continue; fi
|
|
if [[ "$device" =~ ^loop[0-9]+ ]] || [[ "$device" =~ ^dm-.* ]]; then continue; fi
|
|
|
|
# We do not want ovelapping checksums, so only checksum partitions
|
|
if [[ "$device" =~ [a-zA-Z]$ ]]; then continue; fi
|
|
|
|
# User-specified devices will be excluded
|
|
should_skip=$RELIANT_FALSE
|
|
IFS=' '
|
|
for skip_device in $RELIANT_SKIP_CHECKSUM; do
|
|
if [ "$skip_device" = "$device_path" ]; then
|
|
should_skip=$RELIANT_TRUE
|
|
fi
|
|
done
|
|
if [ $should_skip -eq $RELIANT_TRUE ]; then continue; fi
|
|
|
|
# We're good to go, but first verify if the device has been set to read-only
|
|
if [ "$(blockdev --getro "$device_path")" -eq 0 ]; then
|
|
reliant_warn "reliant_security_check_devices: attempted to checksum a non-readonly device $device_path, which will likely lead to false positive hash mismatches"
|
|
fi
|
|
blockdev --setro "$device_path" 2>/dev/null
|
|
|
|
# Manual verification
|
|
hash=$(reliant-hash "$device_path")
|
|
|
|
# Inside initramfs, use Plymouth, otherwise, use read
|
|
prompt="$device_path matches $hash? [Y/N]"
|
|
if [ "$RELIANT_INITRAMFS" -eq $RELIANT_TRUE ]; then
|
|
REPLY=$(plymouth ask-question --prompt="$prompt")
|
|
else
|
|
read -r -p "$prompt: "
|
|
fi
|
|
|
|
# Check the user-provided reply
|
|
if [[ ! $REPLY =~ ^[Yy] ]]; then
|
|
reliant_error "reliant_security_checksum_devices: hash mismatch reported for $device_path"
|
|
return $RELIANT_SECURITY_FAIL
|
|
fi
|
|
done
|
|
|
|
# Forward from subshell
|
|
return $?
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
# Validate arguments
|
|
if [ "$#" -ne 1 ]; then
|
|
reliant_error "root mountpoint not specified"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
if [ -z "$1" ]; then
|
|
reliant_error "empty root mountpoint specified"
|
|
return $RELIANT_FAIL
|
|
fi
|
|
|
|
# Status array
|
|
status_cmdline=$RELIANT_OK
|
|
status_swap=$RELIANT_OK
|
|
status_network=$RELIANT_OK
|
|
status_filesystems=$RELIANT_OK
|
|
status_overlay=$RELIANT_OK
|
|
status_devices=$RELIANT_OK
|
|
|
|
# Run the checks
|
|
reliant_security_check_cmdline || status_cmdline=$?
|
|
reliant_security_check_swap || status_swap=$?
|
|
reliant_security_check_network || status_network=$?
|
|
reliant_security_check_filesystems || status_filesystems=$?
|
|
reliant_security_check_overlay "$1" || status_overlay=$?
|
|
reliant_security_check_devices || status_devices=$?
|
|
|
|
# Print the report
|
|
echo "[REPORT]"
|
|
echo "[ROOT]: $(reliant_err2str $status_overlay)"
|
|
echo "[SWAP]: $(reliant_err2str $status_swap)"
|
|
echo "[DEVICES]: $(reliant_err2str $status_devices)"
|
|
echo "[NETWORK]: $(reliant_err2str $status_network)"
|
|
echo "[CMDLINE]: $(reliant_err2str $status_cmdline)"
|
|
echo "[FILESYSTEMS]: $(reliant_err2str $status_filesystems)"
|
|
echo -n "[SUMMARY]: "
|
|
|
|
# Detemine the final security state
|
|
if [ $status_cmdline -eq 0 ] && [ $status_swap -eq 0 ] && [ $status_network -eq 0 ] && [ $status_filesystems -eq 0 ] && [ $status_overlay -eq 0 ] && [ $status_devices -eq 0 ]; then
|
|
echo "VERIFIED"
|
|
return 0
|
|
else
|
|
echo "FAILED"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
main "$@"
|