mirror of
https://github.com/falcosecurity/falco.git
synced 2026-01-17 07:57:15 +00:00
Pre-built kernel modules/eBPF probes for Flatcar use the value of the OS VERSION_ID field as KERNEL_RELEASE in the filename. A specific kernel release version does not uniquely identify a Flatcar configuration, because Flatcar is image-based instead of package-based. Here's a more specific example: the same kernel version can be part of various Flatcar releases (across channels alpha/beta/stable) with differences in configuration. This is why we use the VERSION_ID value during offline builds with driverkit. Flatcar version numbers are all higher than 1500.0.0, so there is no risk of collision with kernel version numbers. When locally building the kernel module on the system, we have access to the correct kernel build directory at /lib/modules/$(uname -r)/build with the right configuration and so for that branch, we need to reset KERNEL_RELEASE=$(uname -r). See also the driverkit PR that introduces a builder for Flatcar: https://github.com/falcosecurity/driverkit/pull/131 Signed-off-by: Jeremi Piotrowski <jpiotrowski@microsoft.com>
704 lines
20 KiB
Bash
Executable File
704 lines
20 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Copyright (C) 2022 The Falco Authors.
|
|
#
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
# Simple script that desperately tries to load the kernel instrumentation by
|
|
# looking for it in a bunch of ways. Convenient when running Falco inside
|
|
# a container or in other weird environments.
|
|
#
|
|
|
|
#
|
|
# Returns 1 if $cos_ver > $base_ver, 0 otherwise
|
|
#
|
|
cos_version_greater() {
|
|
if [[ $cos_ver == "${base_ver}" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
#
|
|
# COS build numbers are in the format x.y.z
|
|
#
|
|
a=$(echo "${cos_ver}" | cut -d. -f1)
|
|
b=$(echo "${cos_ver}" | cut -d. -f2)
|
|
c=$(echo "${cos_ver}" | cut -d. -f3)
|
|
|
|
d=$(echo "${base_ver}" | cut -d. -f1)
|
|
e=$(echo "${base_ver}" | cut -d. -f2)
|
|
f=$(echo "${base_ver}" | cut -d. -f3)
|
|
|
|
# Test the first component
|
|
if [[ $a -gt $d ]]; then
|
|
return 1
|
|
elif [[ $d -gt $a ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Test the second component
|
|
if [[ $b -gt $e ]]; then
|
|
return 1
|
|
elif [[ $e -gt $b ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Test the third component
|
|
if [[ $c -gt $f ]]; then
|
|
return 1
|
|
elif [[ $f -gt $c ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# If we get here, probably malformatted version string?
|
|
|
|
return 0
|
|
}
|
|
|
|
get_kernel_config() {
|
|
if [ -f /proc/config.gz ]; then
|
|
echo "* Found kernel config at /proc/config.gz"
|
|
KERNEL_CONFIG_PATH=/proc/config.gz
|
|
elif [ -f "/boot/config-${KERNEL_RELEASE}" ]; then
|
|
echo "* Found kernel config at /boot/config-${KERNEL_RELEASE}"
|
|
KERNEL_CONFIG_PATH=/boot/config-${KERNEL_RELEASE}
|
|
elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/boot/config-${KERNEL_RELEASE}" ]; then
|
|
echo "* Found kernel config at ${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
|
|
KERNEL_CONFIG_PATH="${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
|
|
elif [ -f "/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
|
|
echo "* Found kernel config at /usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
|
|
KERNEL_CONFIG_PATH="/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
|
|
elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
|
|
echo "* Found kernel config at ${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
|
|
KERNEL_CONFIG_PATH="${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
|
|
elif [ -f "/lib/modules/${KERNEL_RELEASE}/config" ]; then
|
|
# This code works both for native host and containers assuming that
|
|
# Dockerfile sets up the desired symlink /lib/modules -> $HOST_ROOT/lib/modules
|
|
echo "* Found kernel config at /lib/modules/${KERNEL_RELEASE}/config"
|
|
KERNEL_CONFIG_PATH="/lib/modules/${KERNEL_RELEASE}/config"
|
|
fi
|
|
|
|
if [ -z "${KERNEL_CONFIG_PATH}" ]; then
|
|
>&2 echo "Cannot find kernel config"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
|
|
HASH=$(zcat "${KERNEL_CONFIG_PATH}" | md5sum - | cut -d' ' -f1)
|
|
else
|
|
HASH=$(md5sum "${KERNEL_CONFIG_PATH}" | cut -d' ' -f1)
|
|
fi
|
|
}
|
|
|
|
get_target_id() {
|
|
if [ -f "${HOST_ROOT}/etc/os-release" ]; then
|
|
# freedesktop.org and systemd
|
|
# shellcheck source=/dev/null
|
|
source "${HOST_ROOT}/etc/os-release"
|
|
OS_ID=$ID
|
|
elif [ -f "${HOST_ROOT}/etc/debian_version" ]; then
|
|
# Older debian distros
|
|
# fixme > Can this happen on older Ubuntu?
|
|
OS_ID=debian
|
|
elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
|
|
# Older CentOS distros
|
|
OS_ID=centos
|
|
elif [ -f "${HOST_ROOT}/etc/VERSION" ]; then
|
|
OS_ID=minikube
|
|
else
|
|
>&2 echo "Detected an unsupported target system, please get in touch with the Falco community"
|
|
exit 1
|
|
fi
|
|
|
|
case "${OS_ID}" in
|
|
("amzn")
|
|
if [[ $VERSION_ID == "2" ]]; then
|
|
TARGET_ID="amazonlinux2"
|
|
else
|
|
TARGET_ID="amazonlinux"
|
|
fi
|
|
;;
|
|
("ubuntu")
|
|
if [[ $KERNEL_RELEASE == *"aws"* ]]; then
|
|
TARGET_ID="ubuntu-aws"
|
|
else
|
|
TARGET_ID="ubuntu-generic"
|
|
fi
|
|
;;
|
|
("flatcar")
|
|
KERNEL_RELEASE="${VERSION_ID}"
|
|
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
|
;;
|
|
(*)
|
|
TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
|
|
;;
|
|
esac
|
|
}
|
|
|
|
flatcar_relocate_tools() {
|
|
local -a tools=(
|
|
scripts/basic/fixdep
|
|
scripts/mod/modpost
|
|
tools/objtool/objtool
|
|
)
|
|
local -r hostld=$(ls /host/usr/lib64/ld-*.so)
|
|
local -r kdir=/lib/modules/$(ls /lib/modules/)/build
|
|
echo "** Found host dl interpreter: ${hostld}"
|
|
for host_tool in ${tools[@]}; do
|
|
t=${host_tool}
|
|
tool=$(basename $t)
|
|
tool_dir=$(dirname $t)
|
|
host_tool=${kdir}/${host_tool}
|
|
if [ ! -f ${host_tool} ]; then
|
|
continue
|
|
fi
|
|
umount ${host_tool} 2>/dev/null || true
|
|
mkdir -p /tmp/${tool_dir}/
|
|
cp -a ${host_tool} /tmp/${tool_dir}/
|
|
echo "** Setting host dl interpreter for $host_tool"
|
|
patchelf --set-interpreter ${hostld} --set-rpath /host/usr/lib64 /tmp/${tool_dir}/${tool}
|
|
mount -o bind /tmp/${tool_dir}/${tool} ${host_tool}
|
|
done
|
|
}
|
|
|
|
load_kernel_module_compile() {
|
|
# Skip dkms on UEK hosts because it will always fail
|
|
if [[ $(uname -r) == *uek* ]]; then
|
|
>&2 echo "Skipping because the dkms install always fail (on UEK hosts)"
|
|
return
|
|
fi
|
|
|
|
if ! hash dkms >/dev/null 2>&1; then
|
|
>&2 echo "This program requires dkms"
|
|
return
|
|
fi
|
|
|
|
if [ "${TARGET_ID}" == "flatcar" ]; then
|
|
KERNEL_RELEASE=$(uname -r)
|
|
echo "* Flatcar detected (version ${VERSION_ID}); relocating kernel tools"
|
|
flatcar_relocate_tools
|
|
fi
|
|
|
|
# Try to compile using all the available gcc versions
|
|
for CURRENT_GCC in $(which gcc) $(ls "$(dirname "$(which gcc)")"/gcc-* | grep 'gcc-[0-9]\+' | sort -n -r -k 2 -t -); do
|
|
echo "* Trying to dkms install ${DRIVER_NAME} module with GCC ${CURRENT_GCC}"
|
|
echo "#!/usr/bin/env bash" > /tmp/falco-dkms-make
|
|
echo "make CC=${CURRENT_GCC} \$@" >> /tmp/falco-dkms-make
|
|
chmod +x /tmp/falco-dkms-make
|
|
if dkms install --directive="MAKE='/tmp/falco-dkms-make'" -m "${DRIVER_NAME}" -v "${DRIVER_VERSION}" -k "${KERNEL_RELEASE}" 2>/dev/null; then
|
|
echo "* ${DRIVER_NAME} module installed in dkms"
|
|
KO_FILE="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}"
|
|
if [ -f "$KO_FILE.ko" ]; then
|
|
KO_FILE="$KO_FILE.ko"
|
|
elif [ -f "$KO_FILE.ko.gz" ]; then
|
|
KO_FILE="$KO_FILE.ko.gz"
|
|
elif [ -f "$KO_FILE.ko.xz" ]; then
|
|
KO_FILE="$KO_FILE.ko.xz"
|
|
elif [ -f "$KO_FILE.ko.zst" ]; then
|
|
KO_FILE="$KO_FILE.ko.zst"
|
|
else
|
|
>&2 echo "${DRIVER_NAME} module file not found"
|
|
return
|
|
fi
|
|
echo "* ${DRIVER_NAME} module found: ${KO_FILE}"
|
|
echo "* Trying insmod"
|
|
chcon -t modules_object_t "$KO_FILE" > /dev/null 2>&1 || true
|
|
if insmod "$KO_FILE" > /dev/null 2>&1; then
|
|
echo "* Success: ${DRIVER_NAME} module found and loaded in dkms"
|
|
exit 0
|
|
else
|
|
echo "* Unable to insmod ${DRIVER_NAME} module"
|
|
fi
|
|
else
|
|
DKMS_LOG="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/build/make.log"
|
|
if [ -f "${DKMS_LOG}" ]; then
|
|
echo "* Running dkms build failed, dumping ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
|
|
cat "${DKMS_LOG}"
|
|
else
|
|
echo "* Running dkms build failed, couldn't find ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
load_kernel_module_download() {
|
|
get_target_id
|
|
|
|
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
|
local URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)
|
|
|
|
echo "* Trying to download a prebuilt ${DRIVER_NAME} module from ${URL}"
|
|
if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
|
|
echo "* Download succeeded"
|
|
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
|
if insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}"; then
|
|
echo "* Success: ${DRIVER_NAME} module found and inserted"
|
|
exit 0
|
|
else
|
|
>&2 echo "Unable to insmod the prebuilt ${DRIVER_NAME} module"
|
|
fi
|
|
else
|
|
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} module"
|
|
return
|
|
fi
|
|
}
|
|
|
|
print_clean_termination() {
|
|
echo
|
|
echo "[SUCCESS] Cleaning phase correctly terminated."
|
|
echo
|
|
echo "================ Cleaning phase ================"
|
|
echo
|
|
}
|
|
|
|
clean_kernel_module() {
|
|
echo
|
|
echo "================ Cleaning phase ================"
|
|
echo
|
|
|
|
if ! hash lsmod > /dev/null 2>&1; then
|
|
>&2 echo "This program requires lsmod."
|
|
exit 1
|
|
fi
|
|
|
|
if ! hash rmmod > /dev/null 2>&1; then
|
|
>&2 echo "This program requires rmmod."
|
|
exit 1
|
|
fi
|
|
|
|
KMOD_NAME=$(echo "${DRIVER_NAME}" | tr "-" "_")
|
|
echo "* 1. Check if kernel module '${KMOD_NAME}' is still loaded:"
|
|
|
|
if ! lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}"; then
|
|
echo "- OK! There is no '${KMOD_NAME}' module loaded."
|
|
echo
|
|
fi
|
|
|
|
# Wait 50s = MAX_RMMOD_WAIT * 5s
|
|
MAX_RMMOD_WAIT=10
|
|
# Remove kernel module if is still loaded.
|
|
while lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}" && [ $MAX_RMMOD_WAIT -gt 0 ]; do
|
|
echo "- Kernel module '${KMOD_NAME}' is still loaded."
|
|
echo "- Trying to unload it with 'rmmod ${KMOD_NAME}'..."
|
|
if rmmod ${KMOD_NAME}; then
|
|
echo "- OK! Unloading '${KMOD_NAME}' module succeeded."
|
|
echo
|
|
else
|
|
echo "- Nothing to do...'falco-driver-loader' will wait until you remove the kernel module to have a clean termination."
|
|
echo "- Check that no process is using the kernel module with 'lsmod | grep ${KMOD_NAME}'."
|
|
echo "- Sleep 5 seconds..."
|
|
echo
|
|
((--MAX_RMMOD_WAIT))
|
|
sleep 5
|
|
fi
|
|
done
|
|
|
|
if [ ${MAX_RMMOD_WAIT} -eq 0 ]; then
|
|
echo "[WARNING] '${KMOD_NAME}' module is still loaded, you could have incompatibility issues."
|
|
echo
|
|
fi
|
|
|
|
if ! hash dkms >/dev/null 2>&1; then
|
|
echo "- Skipping dkms remove (dkms not found)."
|
|
print_clean_termination
|
|
return
|
|
fi
|
|
|
|
# Remove all versions of this module from dkms.
|
|
echo "* 2. Check all versions of kernel module '${KMOD_NAME}' in dkms:"
|
|
DRIVER_VERSIONS=$(dkms status -m "${KMOD_NAME}" | tr -d "," | tr -d ":" | tr "/" " " | cut -d' ' -f2)
|
|
if [ -z "${DRIVER_VERSIONS}" ]; then
|
|
echo "- OK! There are no '${KMOD_NAME}' module versions in dkms."
|
|
else
|
|
echo "- There are some versions of '${KMOD_NAME}' module in dkms."
|
|
echo
|
|
echo "* 3. Removing all the following versions from dkms:"
|
|
echo "${DRIVER_VERSIONS}"
|
|
echo
|
|
fi
|
|
|
|
for CURRENT_VER in ${DRIVER_VERSIONS}; do
|
|
echo "- Removing ${CURRENT_VER}..."
|
|
if dkms remove -m ${KMOD_NAME} -v "${CURRENT_VER}" --all; then
|
|
echo
|
|
echo "- OK! Removing '${CURRENT_VER}' succeeded."
|
|
echo
|
|
else
|
|
echo "[WARNING] Removing '${KMOD_NAME}' version '${CURRENT_VER}' failed."
|
|
fi
|
|
done
|
|
|
|
print_clean_termination
|
|
}
|
|
|
|
load_kernel_module() {
|
|
clean_kernel_module
|
|
|
|
echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"
|
|
|
|
get_target_id
|
|
|
|
local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"
|
|
|
|
if [ -f "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
|
|
echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
|
|
chcon -t modules_object_t "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
|
|
insmod "${HOME}/.falco/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
|
|
exit $?
|
|
fi
|
|
|
|
if [ -n "$ENABLE_DOWNLOAD" ]; then
|
|
load_kernel_module_download
|
|
fi
|
|
|
|
if [ -n "$ENABLE_COMPILE" ]; then
|
|
load_kernel_module_compile
|
|
fi
|
|
|
|
# Last try (might load a previous driver version)
|
|
echo "* Trying to load a system ${DRIVER_NAME} module, if present"
|
|
if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
|
|
echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
|
|
exit 0
|
|
fi
|
|
|
|
# Not able to download a prebuilt module nor to compile one on-the-fly
|
|
>&2 echo "Consider compiling your own ${DRIVER_NAME} driver and loading it or getting in touch with the Falco community"
|
|
exit 1
|
|
}
|
|
|
|
load_bpf_probe_compile() {
|
|
local BPF_KERNEL_SOURCES_URL=""
|
|
local STRIP_COMPONENTS=1
|
|
|
|
customize_kernel_build() {
|
|
if [ -n "${KERNEL_EXTRA_VERSION}" ]; then
|
|
sed -i "s/LOCALVERSION=\"\"/LOCALVERSION=\"${KERNEL_EXTRA_VERSION}\"/" .config
|
|
fi
|
|
make olddefconfig > /dev/null
|
|
make modules_prepare > /dev/null
|
|
}
|
|
|
|
if [ "${TARGET_ID}" == "cos" ]; then
|
|
echo "* COS detected (build ${BUILD_ID}), using COS kernel headers"
|
|
|
|
BPF_KERNEL_SOURCES_URL="https://storage.googleapis.com/cos-tools/${BUILD_ID}/kernel-headers.tgz"
|
|
KERNEL_EXTRA_VERSION="+"
|
|
STRIP_COMPONENTS=0
|
|
|
|
customize_kernel_build() {
|
|
pushd usr/src/* > /dev/null || exit
|
|
|
|
# Note: this overrides the KERNELDIR set while untarring the tarball
|
|
KERNELDIR=$(pwd)
|
|
export KERNELDIR
|
|
|
|
sed -i '/^#define randomized_struct_fields_start struct {$/d' include/linux/compiler-clang.h
|
|
sed -i '/^#define randomized_struct_fields_end };$/d' include/linux/compiler-clang.h
|
|
|
|
popd > /dev/null || exit
|
|
|
|
# Might need to configure our own sources depending on COS version
|
|
cos_ver=${BUILD_ID}
|
|
base_ver=11553.0.0
|
|
|
|
cos_version_greater
|
|
greater_ret=$?
|
|
|
|
if [[ greater_ret -eq 1 ]]; then
|
|
export KBUILD_EXTRA_CPPFLAGS=-DCOS_73_WORKAROUND
|
|
fi
|
|
}
|
|
fi
|
|
|
|
if [ "${TARGET_ID}" == "minikube" ]; then
|
|
MINIKUBE_VERSION="$(cat "${HOST_ROOT}/etc/VERSION")"
|
|
echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel"
|
|
local kernel_version
|
|
kernel_version=$(uname -r)
|
|
local -r kernel_version_major=$(echo "${kernel_version}" | cut -d. -f1)
|
|
local -r kernel_version_minor=$(echo "${kernel_version}" | cut -d. -f2)
|
|
local -r kernel_version_patch=$(echo "${kernel_version}" | cut -d. -f3)
|
|
|
|
if [ "${kernel_version_patch}" == "0" ]; then
|
|
kernel_version="${kernel_version_major}.${kernel_version_minor}"
|
|
fi
|
|
|
|
BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
|
|
fi
|
|
|
|
if [ -n "${BPF_USE_LOCAL_KERNEL_SOURCES}" ]; then
|
|
local -r kernel_version_major=$(uname -r | cut -d. -f1)
|
|
local -r kernel_version=$(uname -r | cut -d- -f1)
|
|
KERNEL_EXTRA_VERSION="-$(uname -r | cut -d- -f2)"
|
|
|
|
echo "* Using downloaded kernel sources for kernel version ${kernel_version}..."
|
|
|
|
BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
|
|
fi
|
|
|
|
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
|
|
get_kernel_config
|
|
|
|
echo "* Downloading ${BPF_KERNEL_SOURCES_URL}"
|
|
|
|
mkdir -p /tmp/kernel
|
|
cd /tmp/kernel || exit
|
|
cd "$(mktemp -d -p /tmp/kernel)" || exit
|
|
if ! curl -L -o kernel-sources.tgz --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" "${BPF_KERNEL_SOURCES_URL}"; then
|
|
>&2 echo "Unable to download the kernel sources"
|
|
return
|
|
fi
|
|
|
|
echo "* Extracting kernel sources"
|
|
|
|
mkdir kernel-sources && tar xf kernel-sources.tgz -C kernel-sources --strip-components "${STRIP_COMPONENTS}"
|
|
|
|
cd kernel-sources || exit
|
|
KERNELDIR=$(pwd)
|
|
export KERNELDIR
|
|
|
|
if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
|
|
zcat "${KERNEL_CONFIG_PATH}" > .config
|
|
else
|
|
cat "${KERNEL_CONFIG_PATH}" > .config
|
|
fi
|
|
|
|
echo "* Configuring kernel"
|
|
customize_kernel_build
|
|
fi
|
|
|
|
echo "* Trying to compile the eBPF probe (${BPF_PROBE_FILENAME})"
|
|
|
|
make -C "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf" > /dev/null
|
|
|
|
mkdir -p "${HOME}/.falco"
|
|
mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
|
|
|
if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
|
|
rm -r /tmp/kernel
|
|
fi
|
|
|
|
}
|
|
|
|
load_bpf_probe_download() {
|
|
local URL
|
|
URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
|
|
|
|
echo "* Trying to download a prebuilt eBPF probe from ${URL}"
|
|
|
|
if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${URL}"; then
|
|
>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe"
|
|
return
|
|
fi
|
|
}
|
|
|
|
load_bpf_probe() {
|
|
echo "* Mounting debugfs"
|
|
|
|
if [ ! -d /sys/kernel/debug/tracing ]; then
|
|
mount -t debugfs nodev /sys/kernel/debug
|
|
fi
|
|
|
|
get_target_id
|
|
|
|
BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"
|
|
|
|
if [ -n "$ENABLE_DOWNLOAD" ]; then
|
|
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
|
echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
|
else
|
|
load_bpf_probe_download
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$ENABLE_COMPILE" ]; then
|
|
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
|
echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
|
else
|
|
load_bpf_probe_compile
|
|
fi
|
|
fi
|
|
|
|
if [ -f "${HOME}/.falco/${BPF_PROBE_FILENAME}" ]; then
|
|
echo "* eBPF probe located in ${HOME}/.falco/${BPF_PROBE_FILENAME}"
|
|
|
|
ln -sf "${HOME}/.falco/${BPF_PROBE_FILENAME}" "${HOME}/.falco/${DRIVER_NAME}-bpf.o" \
|
|
&& echo "* Success: eBPF probe symlinked to ${HOME}/.falco/${DRIVER_NAME}-bpf.o"
|
|
exit $?
|
|
else
|
|
>&2 echo "Unable to load the ${DRIVER_NAME} eBPF probe"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
print_usage() {
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " falco-driver-loader [driver] [options]"
|
|
echo ""
|
|
echo "Available drivers:"
|
|
echo " module kernel module (default)"
|
|
echo " bpf eBPF probe"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --help show brief help"
|
|
echo " --clean try to remove an already present driver installation"
|
|
echo " --compile try to compile the driver locally (default true)"
|
|
echo " --download try to download a prebuilt driver (default true)"
|
|
echo " --source-only skip execution and allow sourcing in another script"
|
|
echo ""
|
|
echo "Environment variables:"
|
|
echo " DRIVERS_REPO specify a different URL where to look for prebuilt Falco drivers"
|
|
echo " DRIVER_NAME specify a different name for the driver"
|
|
echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
|
|
echo ""
|
|
echo "Versions:"
|
|
echo " Falco version ${FALCO_VERSION}"
|
|
echo " Driver version ${DRIVER_VERSION}"
|
|
echo ""
|
|
}
|
|
|
|
ARCH=$(uname -m)
|
|
|
|
KERNEL_RELEASE=$(uname -r)
|
|
|
|
if ! hash sed > /dev/null 2>&1; then
|
|
>&2 echo "This program requires sed"
|
|
exit 1
|
|
fi
|
|
KERNEL_VERSION=$(uname -v | sed 's/#\([[:digit:]]\+\).*/\1/')
|
|
|
|
DRIVERS_REPO=${DRIVERS_REPO:-"@DRIVERS_REPO@"}
|
|
|
|
if [ -n "$DRIVER_INSECURE_DOWNLOAD" ]
|
|
then
|
|
FALCO_DRIVER_CURL_OPTIONS=-fsSk
|
|
else
|
|
FALCO_DRIVER_CURL_OPTIONS=-fsS
|
|
fi
|
|
|
|
if [[ -z "$MAX_RMMOD_WAIT" ]]; then
|
|
MAX_RMMOD_WAIT=60
|
|
fi
|
|
|
|
DRIVER_VERSION="@DRIVER_VERSION@"
|
|
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}
|
|
FALCO_VERSION="@FALCO_VERSION@"
|
|
|
|
DRIVER="module"
|
|
if [ -v FALCO_BPF_PROBE ]; then
|
|
DRIVER="bpf"
|
|
fi
|
|
|
|
ENABLE_COMPILE=
|
|
ENABLE_DOWNLOAD=
|
|
|
|
clean=
|
|
has_args=
|
|
has_opts=
|
|
source_only=
|
|
while test $# -gt 0; do
|
|
case "$1" in
|
|
module|bpf)
|
|
if [ -n "$has_args" ]; then
|
|
>&2 echo "Only one driver per invocation"
|
|
print_usage
|
|
exit 1
|
|
else
|
|
DRIVER="$1"
|
|
has_args="true"
|
|
shift
|
|
fi
|
|
;;
|
|
-h|--help)
|
|
print_usage
|
|
exit 0
|
|
;;
|
|
--clean)
|
|
clean="true"
|
|
shift
|
|
;;
|
|
--compile)
|
|
ENABLE_COMPILE="yes"
|
|
has_opts="true"
|
|
shift
|
|
;;
|
|
--download)
|
|
ENABLE_DOWNLOAD="yes"
|
|
has_opts="true"
|
|
shift
|
|
;;
|
|
--source-only)
|
|
source_only="true"
|
|
shift
|
|
;;
|
|
--*)
|
|
>&2 echo "Unknown option: $1"
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
>&2 echo "Unknown driver: $1"
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$has_opts" ]; then
|
|
ENABLE_COMPILE="yes"
|
|
ENABLE_DOWNLOAD="yes"
|
|
fi
|
|
|
|
if [ -z "$source_only" ]; then
|
|
echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}"
|
|
|
|
if [ "$(id -u)" != 0 ]; then
|
|
>&2 echo "This program must be run as root (or with sudo)"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$clean" ]; then
|
|
if [ -n "$has_opts" ]; then
|
|
>&2 echo "Cannot use --clean with other options"
|
|
exit 1
|
|
fi
|
|
|
|
echo "* Running falco-driver-loader with: driver=$DRIVER, clean=yes"
|
|
case $DRIVER in
|
|
module)
|
|
clean_kernel_module
|
|
;;
|
|
bpf)
|
|
>&2 echo "--clean not supported for driver=bpf"
|
|
exit 1
|
|
esac
|
|
else
|
|
if ! hash curl > /dev/null 2>&1; then
|
|
>&2 echo "This program requires curl"
|
|
exit 1
|
|
fi
|
|
|
|
echo "* Running falco-driver-loader with: driver=$DRIVER, compile=${ENABLE_COMPILE:-"no"}, download=${ENABLE_DOWNLOAD:-"no"}"
|
|
case $DRIVER in
|
|
module)
|
|
load_kernel_module
|
|
;;
|
|
bpf)
|
|
load_bpf_probe
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|