Files
runc/script/check-config.sh
Kir Kolyshkin d87366f019 scripts/check-config: fix kernel version checks
Looking at the code, I found out that kernel_lt function was actually
doing le ("less than or equal") rather than lt ("less than") operation.
Let's fix this and do exactly what the name says.

A bigger issue is, the function use was not consistent (some uses
implied "less than or equal").

To fix the usage, find out all relevant kernel commits and kernel
versions that have them (the latter is done using "git describe
--contains $sha"), and fix the wrong cases. While at it, add references
to all these kernel commits for the future generations of
check-config.sh hackers.

Also, add kernel_ge function which is the opposite of kernel_lt,
and document both.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2023-12-20 10:07:43 -08:00

325 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -e -u
[ -t 1 ] || NO_COLOR=1
# bits of this were adapted from check_config.sh in docker
# see also https://github.com/docker/docker/blob/master/contrib/check-config.sh
possibleConfigs=(
'/proc/config.gz'
"/boot/config-$(uname -r)"
"/usr/src/linux-$(uname -r)/.config"
'/usr/src/linux/.config'
)
possibleConfigFiles=(
'config.gz'
"config-$(uname -r)"
'.config'
)
if ! command -v zgrep &>/dev/null; then
zgrep() {
zcat "$2" | grep "$1"
}
fi
kernelVersion="$(uname -r)"
kernelMajor="${kernelVersion%%.*}"
kernelMinor="${kernelVersion#"$kernelMajor".}"
kernelMinor="${kernelMinor%%.*}"
# Usage: to check if kernel version is < 4.8, use
# kernel_lt 4 8
# (here "lt" stands for "less than").
kernel_lt() {
[ "$kernelMajor" -lt "$1" ] && return
[ "$kernelMajor" -eq "$1" ] && [ "$kernelMinor" -lt "$2" ]
}
# Usage: to check if kernel version is >= 6.1, use
# kernel_ge 6 1
# (here "ge" stands for "greater or equal").
kernel_ge() {
! kernel_lt "$1" "$2"
}
is_set() {
zgrep "CONFIG_$1=[y|m]" "$CONFIG" >/dev/null
}
is_set_in_kernel() {
zgrep "CONFIG_$1=y" "$CONFIG" >/dev/null
}
is_set_as_module() {
zgrep "CONFIG_$1=m" "$CONFIG" >/dev/null
}
color() {
[ -n "${NO_COLOR:-}" ] && return
local codes=()
if [ "$1" = 'bold' ]; then
codes=("${codes[@]-}" '1')
shift
fi
if [ "$#" -gt 0 ]; then
local code=''
case "$1" in
# see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
black) code=30 ;;
red) code=31 ;;
green) code=32 ;;
yellow) code=33 ;;
blue) code=34 ;;
magenta) code=35 ;;
cyan) code=36 ;;
white) code=37 ;;
esac
if [ "$code" ]; then
codes=("${codes[@]-}" "$code")
fi
fi
local IFS=';'
echo -en '\033['"${codes[*]-}"'m'
}
wrap_color() {
text="$1"
shift
color "$@"
echo -n "$text"
color reset
echo
}
wrap_good() {
local name="$1"
shift
local val="$1"
shift
echo "$(wrap_color "$name" white): $(wrap_color "$val" green)" "$@"
}
wrap_bad() {
local name="$1"
shift
local val="$1"
shift
echo "$(wrap_color "$name" bold): $(wrap_color "$val" bold red)" "$@"
}
wrap_warning() {
wrap_color >&2 "$*" red
}
check_flag() {
if is_set_in_kernel "$1"; then
wrap_good "CONFIG_$1" 'enabled'
elif is_set_as_module "$1"; then
wrap_good "CONFIG_$1" 'enabled (as module)'
else
wrap_bad "CONFIG_$1" 'missing'
fi
}
check_flags() {
for flag in "$@"; do
echo "- $(check_flag "$flag")"
done
}
check_distro_userns() {
[ -r /etc/os-release ] || return 0
# shellcheck source=/dev/null
. /etc/os-release 2>/dev/null || return 0
if [[ "${ID}" =~ ^(centos|rhel)$ && "${VERSION_ID}" =~ ^7 ]]; then
# this is a CentOS7 or RHEL7 system
grep -q "user_namespace.enable=1" /proc/cmdline || {
# no user namespace support enabled
wrap_bad " (RHEL7/CentOS7" "User namespaces disabled; add 'user_namespace.enable=1' to boot command line)"
}
fi
}
is_config() {
local config="$1"
# Todo: more check
[[ -f "$config" ]] && return 0
return 1
}
search_config() {
local target_dir=("${1:-${possibleConfigs[@]}}")
local tryConfig
for tryConfig in "${target_dir[@]}"; do
is_config "$tryConfig" && {
CONFIG="$tryConfig"
return
}
[[ -d "$tryConfig" ]] && {
for tryFile in "${possibleConfigFiles[@]}"; do
is_config "$tryConfig/$tryFile" && {
CONFIG="$tryConfig/$tryFile"
return
}
done
}
done
wrap_warning "error: cannot find kernel config"
wrap_warning " try running this script again, specifying the kernel config:"
wrap_warning " CONFIG=/path/to/kernel/.config $0 or $0 /path/to/kernel/.config"
exit 1
}
CONFIG="${1:-}"
is_config "$CONFIG" || {
if [[ ! "$CONFIG" ]]; then
wrap_color "info: no config specified, searching for kernel config ..." white
search_config
elif [[ -d "$CONFIG" ]]; then
wrap_color "info: input is a directory, searching for kernel config in this directory..." white
search_config "$CONFIG"
else
wrap_warning "warning: $CONFIG seems not a kernel config, searching other paths for kernel config ..."
search_config
fi
}
wrap_color "info: reading kernel config from $CONFIG ..." white
echo
echo 'Generally Necessary:'
cgroup=""
echo -n '- '
if [ "$(stat -f -c %t /sys/fs/cgroup 2>/dev/null)" = "63677270" ]; then
wrap_good 'cgroup hierarchy' 'cgroupv2'
cgroup="v2"
else
cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)[, ]/ && $3 == "cgroup" { print $2 }' /proc/mounts | head -n1)"
cgroupDir="$(dirname "$cgroupSubsystemDir")"
if [ -d "$cgroupDir/cpu" ] || [ -d "$cgroupDir/cpuacct" ] || [ -d "$cgroupDir/cpuset" ] || [ -d "$cgroupDir/devices" ] || [ -d "$cgroupDir/freezer" ] || [ -d "$cgroupDir/memory" ]; then
wrap_good 'cgroup hierarchy' 'properly mounted' "[$cgroupDir]"
cgroup="v1"
else
if [ "$cgroupSubsystemDir" ]; then
wrap_bad 'cgroup hierarchy' 'single mountpoint!' "[$cgroupSubsystemDir]"
else
wrap_bad 'cgroup hierarchy' 'nonexistent??'
fi
wrap_color ' (see https://github.com/tianon/cgroupfs-mount)' yellow
fi
fi
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then
echo -n '- '
if command -v apparmor_parser &>/dev/null; then
wrap_good 'apparmor' 'enabled and tools installed'
else
wrap_bad 'apparmor' 'enabled, but apparmor_parser missing'
echo -n ' '
if command -v apt-get &>/dev/null; then
wrap_color '(use "apt-get install apparmor" to fix this)' yellow
elif command -v yum &>/dev/null; then
wrap_color '(your best bet is "yum install apparmor-parser")' yellow
else
wrap_color '(look for an "apparmor" package for your distribution)' yellow
fi
fi
fi
flags=(
NAMESPACES {NET,PID,IPC,UTS}_NS
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
KEYS
VETH BRIDGE BRIDGE_NETFILTER
IP_NF_FILTER IP_NF_TARGET_MASQUERADE
NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK,IPVS}
IP_NF_NAT NF_NAT
# required for bind-mounting /dev/mqueue into containers
POSIX_MQUEUE
)
check_flags "${flags[@]}"
# Linux kernel commit 3007098494be.
if kernel_ge 4 10 && [ $cgroup = "v2" ]; then
check_flags CGROUP_BPF
fi
# Linux kernel commit 3bf195ae6037.
if kernel_lt 5 1; then
check_flags NF_NAT_IPV4
fi
# Linux kernel commit 4806e975729f99c7.
if kernel_lt 5 2; then
check_flags NF_NAT_NEEDED
fi
echo
echo 'Optional Features:'
{
check_flags USER_NS
check_distro_userns
check_flags SECCOMP
check_flags SECCOMP_FILTER
check_flags CGROUP_PIDS
# Linux kernel commit e55b9f96860f.
if kernel_lt 6 1; then
check_flags MEMCG_SWAP
fi
# Linux kernel commit 2d1c498072de.
if kernel_lt 5 8; then
check_flags MEMCG_SWAP_ENABLED
if is_set MEMCG_SWAP && ! is_set MEMCG_SWAP_ENABLED; then
wrap_color ' (note that cgroup swap accounting is not enabled in your kernel config, you can enable it by setting boot option "swapaccount=1")' bold black
fi
fi
}
# Linux kernel commit d886f4e483ce.
if kernel_lt 4 5; then
check_flags MEMCG_KMEM
fi
# Linux kernel commit 5b1efc027c0b.
if kernel_lt 3 19; then
check_flags RESOURCE_COUNTERS
fi
# Linux kernel commit 86f8515f9721.
if kernel_lt 3 14; then
netprio=NETPRIO_CGROUP
else
netprio=CGROUP_NET_PRIO
fi
# Linux kernel commit f382fb0bcef4.
if kernel_lt 5 0; then
check_flags IOSCHED_CFQ CFQ_GROUP_IOSCHED
fi
flags=(
BLK_CGROUP BLK_DEV_THROTTLING
CGROUP_PERF
CGROUP_HUGETLB
NET_CLS_CGROUP "$netprio"
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
IP_NF_TARGET_REDIRECT
IP_VS
IP_VS_NFCT
IP_VS_PROTO_TCP
IP_VS_PROTO_UDP
IP_VS_RR
SECURITY_SELINUX
SECURITY_APPARMOR
CHECKPOINT_RESTORE
)
check_flags "${flags[@]}"