diff --git a/script/check/README.md b/script/check/README.md new file mode 100644 index 0000000..65fe47e --- /dev/null +++ b/script/check/README.md @@ -0,0 +1,4 @@ +runAllChecks.sh +=============== + +This script processes all checks of a host to verify the right configuration. diff --git a/script/check/host/all/app_docker-compose_is_installed.check.sh b/script/check/host/all/app_docker-compose_is_installed.check.sh new file mode 100755 index 0000000..fe8ac50 --- /dev/null +++ b/script/check/host/all/app_docker-compose_is_installed.check.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +_CURRENT_APP='docker compose version' + +${_CURRENT_APP} > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/app_docker_is_installed.check.sh b/script/check/host/all/app_docker_is_installed.check.sh new file mode 100755 index 0000000..28c5f94 --- /dev/null +++ b/script/check/host/all/app_docker_is_installed.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker --version > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/app_nginx_is_installed.check.sh b/script/check/host/all/app_nginx_is_installed.check.sh new file mode 100755 index 0000000..3ab5370 --- /dev/null +++ b/script/check/host/all/app_nginx_is_installed.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +nginx -v > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/app_nginx_starts_reliable.check.sh b/script/check/host/all/app_nginx_starts_reliable.check.sh new file mode 100755 index 0000000..83498a8 --- /dev/null +++ b/script/check/host/all/app_nginx_starts_reliable.check.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Fail because of unnecessary custom config +grep "Wants=network-online.target" /lib/systemd/system/nginx.service > /dev/null 2>&1 \ + && [ -f "/etc/systemd/system/nginx.service" ] \ + && exit 1 + +# Success if system config is ok +grep "Wants=network-online.target" /lib/systemd/system/nginx.service > /dev/null 2>&1 \ + && exit 0 + +# Success if custom config fixes system config +grep "Wants=network-online.target" /etc/systemd/system/nginx.service > /dev/null 2>&1 \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/core_cron_starts_setup_as_fallback.check.sh b/script/check/host/all/core_cron_starts_setup_as_fallback.check.sh new file mode 100755 index 0000000..32ef863 --- /dev/null +++ b/script/check/host/all/core_cron_starts_setup_as_fallback.check.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +crontab -l | grep -E "[0-9]{1,2}[ \*]{8}[[:blank:]]*\/cis\/setupCoreOntoThisHost.sh" > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/core_git_app_is_installed.check.sh b/script/check/host/all/core_git_app_is_installed.check.sh new file mode 100755 index 0000000..bf8339d --- /dev/null +++ b/script/check/host/all/core_git_app_is_installed.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +git --version > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/core_hostname_is_long.check.sh b/script/check/host/all/core_hostname_is_long.check.sh new file mode 100755 index 0000000..d2ee709 --- /dev/null +++ b/script/check/host/all/core_hostname_is_long.check.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +_CURRENT_FILE='/etc/hostname' + +#The file must be readable, then +#the number of lines containing a '.' must be zero. +[ -r "${_CURRENT_FILE}" ] \ + && [ "$(grep -cF '.' "${_CURRENT_FILE}")" -gt 0 ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/core_ssh_app_is_installed.check.sh b/script/check/host/all/core_ssh_app_is_installed.check.sh new file mode 100755 index 0000000..7a810e1 --- /dev/null +++ b/script/check/host/all/core_ssh_app_is_installed.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +ssh -V > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/core_ssh_authorized_keys_of_jenkins_points_to_definitions.check.sh b/script/check/host/all/core_ssh_authorized_keys_of_jenkins_points_to_definitions.check.sh new file mode 100755 index 0000000..bc6618e --- /dev/null +++ b/script/check/host/all/core_ssh_authorized_keys_of_jenkins_points_to_definitions.check.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +_CURRENT_FILE='/home/jenkins/.ssh/authorized_keys' + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +#File has to be readable, then +#search for '/definitions/' in the path of current file, after readlink expanded a potential symlink. +[ -r "${_CURRENT_FILE}" ] \ + && readlink -f "${_CURRENT_FILE}" | grep -q "/definitions/" \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/core_ssh_authorized_keys_of_root_is_empty_or_points_to_definitions.check.sh b/script/check/host/all/core_ssh_authorized_keys_of_root_is_empty_or_points_to_definitions.check.sh new file mode 100755 index 0000000..99dbc59 --- /dev/null +++ b/script/check/host/all/core_ssh_authorized_keys_of_root_is_empty_or_points_to_definitions.check.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +_CURRENT_FILE='/root/.ssh/authorized_keys' + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +#No file is ok +[ ! -e "${_CURRENT_FILE}" ] \ + && exit 0 + +#The file must be readable, then +#all comments and all blank lines are removed, after which the number of remaining lines must be zero. +[ -r "${_CURRENT_FILE}" ] \ + && [ "0" == "$(cat "${_CURRENT_FILE}" | sed 's/[[:blank:]]*#.*//' | sed '/^$/d' | grep -c .)" ] \ + && exit 0 + +#File has to be readable, then +#search for '/definitions/' in the path of current file, after readlink expanded a potential symlink. +[ -r "${_CURRENT_FILE}" ] \ + && readlink -f "${_CURRENT_FILE}" | grep -q "/definitions/" \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/core_ssh_key_of_root_exists_as_expected.check.sh b/script/check/host/all/core_ssh_key_of_root_exists_as_expected.check.sh new file mode 100755 index 0000000..c42dfd5 --- /dev/null +++ b/script/check/host/all/core_ssh_key_of_root_exists_as_expected.check.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +_CURRENT_FILE='/root/.ssh/id_ed25519' + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +#File has to be readable and no passphrase should be needed. +ssh-keygen -y -P "" -f "${_CURRENT_FILE}" &> /dev/null \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/core_sudoers_file_of_jenkins_points_to_definitions.check.sh b/script/check/host/all/core_sudoers_file_of_jenkins_points_to_definitions.check.sh new file mode 100755 index 0000000..5e0e97e --- /dev/null +++ b/script/check/host/all/core_sudoers_file_of_jenkins_points_to_definitions.check.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +_CURRENT_FILE='/etc/sudoers.d/allow-jenkins-updateRepositories' + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +#File has to be readable, then +#search for '/definitions/' in the path of current file, after readlink expanded a potential symlink. +[ -r "${_CURRENT_FILE}" ] \ + && readlink -f "${_CURRENT_FILE}" | grep -q "/definitions/" \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/core_user_jenkins_exists.check.sh b/script/check/host/all/core_user_jenkins_exists.check.sh new file mode 100755 index 0000000..38a255d --- /dev/null +++ b/script/check/host/all/core_user_jenkins_exists.check.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +_CURRENT_USER='jenkins' + +[ "$(id -u)" != "0" ] \ + && printf "(INSUFFICENT RIGHTS) " \ + && exit 1 + +id -u "${_CURRENT_USER}" > /dev/null 2>&1 \ + && exit 0 + +exit 1 diff --git a/script/check/host/all/system_localtime_contains_cet_and_cest.check.sh b/script/check/host/all/system_localtime_contains_cet_and_cest.check.sh new file mode 100755 index 0000000..2db8c02 --- /dev/null +++ b/script/check/host/all/system_localtime_contains_cet_and_cest.check.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +_CURRENT_FILE='/etc/localtime' + +#The file must be readable, then +#the number of lines containing "CET" must be greater than zero, and +#the number of lines containing "CEST" must also be greater than zero. +[ -r "${_CURRENT_FILE}" ] \ + && [ "$(zdump -v "${_CURRENT_FILE}" | head -n 10 | grep 'CET' | grep -c .)" -gt "0" ] \ + && [ "$(zdump -v "${_CURRENT_FILE}" | head -n 10 | grep 'CEST' | grep -c .)" -gt "0" ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_timezone_is_berlin.check.sh b/script/check/host/all/system_timezone_is_berlin.check.sh new file mode 100755 index 0000000..f50a315 --- /dev/null +++ b/script/check/host/all/system_timezone_is_berlin.check.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +_CURRENT_FILE='/etc/timezone' + +#The file must be readable, then +#the number of lines containing "Europe/Berlin" must be one. +[ -r "${_CURRENT_FILE}" ] \ + && [ "1" == "$(cat "${_CURRENT_FILE}" | grep 'Europe/Berlin' | grep -c .)" ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_unattended_upgrades_are_disabled.check.sh b/script/check/host/all/system_unattended_upgrades_are_disabled.check.sh new file mode 100755 index 0000000..e05d3c2 --- /dev/null +++ b/script/check/host/all/system_unattended_upgrades_are_disabled.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +! systemctl is-enabled unattended-upgrades.service > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_zfs_app_is_installed.check.sh b/script/check/host/all/system_zfs_app_is_installed.check.sh new file mode 100755 index 0000000..ec01989 --- /dev/null +++ b/script/check/host/all/system_zfs_app_is_installed.check.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +zfs --version > /dev/null 2>&1 \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_zfs_atime_of_rootfs_zpool1.check.sh b/script/check/host/all/system_zfs_atime_of_rootfs_zpool1.check.sh new file mode 100755 index 0000000..54c4fd4 --- /dev/null +++ b/script/check/host/all/system_zfs_atime_of_rootfs_zpool1.check.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +_CURRENT_POOL='zpool1' + +#Check if the tool 'zfs' is available, then +#retrieve the property 'atime' from 'zpool1', without header and compare the result with 'off' +#because this the feature 'atime' logs each access, there are many avoidable writes. + +#Set with: 'zfs set atime=off zpool1' +zfs version &> /dev/null \ + && [ "$(zfs get atime -Ho value ${_CURRENT_POOL} 2> /dev/null)" == "off" ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_zfs_compression_of_rootfs_zpool1.check.sh b/script/check/host/all/system_zfs_compression_of_rootfs_zpool1.check.sh new file mode 100755 index 0000000..6a368e4 --- /dev/null +++ b/script/check/host/all/system_zfs_compression_of_rootfs_zpool1.check.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +_CURRENT_POOL='zpool1' + +#Check if the tool 'zfs' is available, then +#retrieve the property 'compression' from 'zpool1', without header and compare the result with 'lz4' + +#Set with: 'zfs set compression=lz4 zpool1' +zfs version &> /dev/null \ + && [ "$(zfs get compression -Ho value ${_CURRENT_POOL} 2> /dev/null)" == "lz4" ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_zfs_mountpoint_of_rootfs_zpool1.check.sh b/script/check/host/all/system_zfs_mountpoint_of_rootfs_zpool1.check.sh new file mode 100755 index 0000000..666a949 --- /dev/null +++ b/script/check/host/all/system_zfs_mountpoint_of_rootfs_zpool1.check.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +_CURRENT_ZFS='zpool1' + +#Check if the tool 'zfs' is available, then +#retrieve the property 'mountpoint' from 'zpool1', without header and compare the result with '/zpool1' + +#Set with: 'zfs set mountpount=default' +zfs version &> /dev/null \ + && [ "$(zfs get mountpoint -Ho value ${_CURRENT_ZFS} 2> /dev/null)" == "/${_CURRENT_ZFS}" ] \ + && exit 0 +exit 1 diff --git a/script/check/host/all/system_zpool_alignment_of_pool.check.sh b/script/check/host/all/system_zpool_alignment_of_pool.check.sh new file mode 100755 index 0000000..2bc7e67 --- /dev/null +++ b/script/check/host/all/system_zpool_alignment_of_pool.check.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +_CURRENT_POOL='zpool1' + +#Check if the tool 'zpool' is available, then +#retrieve the property 'ashift' from 'zpool1', without header and compare the result with '12' +zpool version &> /dev/null \ + && [ "$(zpool get ashift -Ho value ${_CURRENT_POOL} 2> /dev/null)" == "12" ] \ + && exit 0 +exit 1 diff --git a/script/check/runAllChecks.sh b/script/check/runAllChecks.sh new file mode 100755 index 0000000..246dd9f --- /dev/null +++ b/script/check/runAllChecks.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +_SCRIPT="$(readlink -f "${0}" 2> /dev/null)" + +# Folders always ends with an tailing '/' +_CIS_ROOT="${_SCRIPT%%/script/check/*}/" #Removes longest matching pattern '/script/check/*' from the end +_SCRIPT_PATH="${_CIS_ROOT:?"Missing CIS_ROOT"}script/" +_OWN_DOMAIN="$(${_CIS_ROOT}core/printOwnDomain.sh)" +_OWN_DEFINITIONS="${_CIS_ROOT}definitions/${_OWN_DOMAIN:?"Missing OWN_DOMAIN"}/" + + + +function run_as_root() { + [ "0" == "$(id -u)" ] \ + && echo OK \ + && return 0 + + echo FAIL + return 1 +} + +function scripts_are_updateable_by_git() { + git -C "${_SCRIPT_PATH:?"Missing SCRIPT_PATH"}" pull > /dev/null 2>&1 \ + && echo OK \ + && return 0 + + echo FAIL + return 1 +} + +function allChecks() { + local _CHECK_PATH _MODE_PATH + _CHECK_PATH="${1:?"allChecks(): Missing first parameter CHECK_PATH"}check/" + _MODE_PATH="${2:-all}/" + readonly _CHECK_PATH _MODE_PATH + + echo " - ${_CHECK_PATH}host/${_MODE_PATH}*.check.sh" + [ "$(ls -1 ${_CHECK_PATH}host/${_MODE_PATH}*.check.sh 2> /dev/null | grep -cE '.*')" == "0" ] \ + && echo " nothing to do" \ + && return 0 + + for _CURRENT_CHECK in ${_CHECK_PATH}host/${_MODE_PATH}*.check.sh; do + _NAME="$(basename ${_CURRENT_CHECK} | cut -d'.' -f1)" + _CONTEXT="$(echo ${_NAME} | cut -d'_' -f1)" + _CHECK="$(echo ${_NAME} | cut -d'_' -f2- | tr '_' ' ')" + _RESULT="$("${_CURRENT_CHECK}" && echo OK || echo FAIL)" + echo " ${_CONTEXT^^} ${_CHECK}: ${_RESULT}" + done +} + +echo "PRECONDITION run as root: $(run_as_root)" +echo "PRECONDITION scripts are updateable by git: $(scripts_are_updateable_by_git)" +echo +echo "Check all (common):" +allChecks "${_SCRIPT_PATH}" +echo "Check all (own):" +allChecks "${_OWN_DEFINITIONS}" +echo "Check this host:" +allChecks "${_OWN_DEFINITIONS}" "$(hostname -s)"