diff --git a/docker/driver-loader-legacy/docker-entrypoint.sh b/docker/driver-loader-legacy/docker-entrypoint.sh index cea5eac24b0..5c7352d83da 100755 --- a/docker/driver-loader-legacy/docker-entrypoint.sh +++ b/docker/driver-loader-legacy/docker-entrypoint.sh @@ -26,4 +26,5 @@ do ln -s "$i" "/usr/src/$base" done -/usr/bin/falco-driver-loader "$@" +/usr/bin/falcoctl driver config "$@" +/usr/bin/falcoctl driver install diff --git a/docker/driver-loader/docker-entrypoint.sh b/docker/driver-loader/docker-entrypoint.sh index cea5eac24b0..5c7352d83da 100755 --- a/docker/driver-loader/docker-entrypoint.sh +++ b/docker/driver-loader/docker-entrypoint.sh @@ -26,4 +26,5 @@ do ln -s "$i" "/usr/src/$base" done -/usr/bin/falco-driver-loader "$@" +/usr/bin/falcoctl driver config "$@" +/usr/bin/falcoctl driver install diff --git a/docker/falco/docker-entrypoint.sh b/docker/falco/docker-entrypoint.sh index dbf9fcef9e2..e2e619dcef8 100755 --- a/docker/falco/docker-entrypoint.sh +++ b/docker/falco/docker-entrypoint.sh @@ -29,9 +29,10 @@ if [[ -z "${SKIP_DRIVER_LOADER}" ]]; then done # convert the optional space-separated env variable FALCO_DRIVER_LOADER_OPTIONS to array, prevent - # shell expansion and use it as argument list for falco-driver-loader + # shell expansion and use it as argument list for falcoctl read -a falco_driver_loader_option_arr <<< $FALCO_DRIVER_LOADER_OPTIONS - /usr/bin/falco-driver-loader "${falco_driver_loader_option_arr[@]}" + /usr/bin/falcoctl driver config "${falco_driver_loader_option_arr[@]}" + /usr/bin/falcoctl driver install fi exec "$@" diff --git a/docker/no-driver/Dockerfile b/docker/no-driver/Dockerfile index 2ec4384c4b6..ac5fca93bab 100644 --- a/docker/no-driver/Dockerfile +++ b/docker/no-driver/Dockerfile @@ -15,7 +15,7 @@ RUN curl -L -o falco.tar.gz \ tar -xvf falco.tar.gz && \ rm -f falco.tar.gz && \ mv falco-${FALCO_VERSION}-$(uname -m) falco && \ - rm -rf /falco/usr/src/falco-* /falco/usr/bin/falco-driver-loader + rm -rf /falco/usr/src/falco-* /falco/usr/bin/falcoctl RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \ && mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml diff --git a/docker/no-driver/Dockerfile.distroless b/docker/no-driver/Dockerfile.distroless index 810d2d34485..3d72a6046e6 100644 --- a/docker/no-driver/Dockerfile.distroless +++ b/docker/no-driver/Dockerfile.distroless @@ -16,7 +16,7 @@ RUN FALCO_VERSION_URLENCODED=$(echo -n ${FALCO_VERSION}|jq -sRr @uri) && \ tar -xvf falco.tar.gz && \ rm -f falco.tar.gz && \ mv falco-${FALCO_VERSION}-$(uname -m) falco && \ - rm -rf /falco/usr/src/falco-* /falco/usr/bin/falco-driver-loader + rm -rf /falco/usr/src/falco-* /falco/usr/bin/falcoctl RUN sed -e 's/time_format_iso_8601: false/time_format_iso_8601: true/' < /falco/etc/falco/falco.yaml > /falco/etc/falco/falco.yaml.new \ && mv /falco/etc/falco/falco.yaml.new /falco/etc/falco/falco.yaml diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 4acf81d01d9..9aa72a8a536 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -41,11 +41,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux") configure_file(rpm/postinstall.in rpm/postinstall COPYONLY) configure_file(rpm/postuninstall.in rpm/postuninstall COPYONLY) configure_file(rpm/preuninstall.in rpm/preuninstall COPYONLY) - - # driver loader - configure_file(falco-driver-loader falco-driver-loader @ONLY) - install(PROGRAMS ${PROJECT_BINARY_DIR}/scripts/falco-driver-loader - DESTINATION ${FALCO_BIN_DIR} COMPONENT "${FALCO_COMPONENT_NAME}") endif() # Install Falcoctl config file @@ -53,5 +48,6 @@ if (NOT WIN32 AND NOT APPLE AND NOT EMSCRIPTEN AND NOT MUSL_OPTIMIZED_BUILD) if(NOT DEFINED FALCOCTL_ETC_DIR) set(FALCOCTL_ETC_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/falcoctl") endif() - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/falcoctl/falcoctl.yaml.in ${PROJECT_BINARY_DIR}/scripts/falcoctl/falcoctl.yaml) + install(FILES ${PROJECT_BINARY_DIR}/scripts/falcoctl/falcoctl.yaml DESTINATION "${FALCOCTL_ETC_DIR}" COMPONENT "${FALCO_COMPONENT_NAME}") endif() diff --git a/scripts/debian/postinst.in b/scripts/debian/postinst.in index b0d5b3050e7..bc43a07102a 100755 --- a/scripts/debian/postinst.in +++ b/scripts/debian/postinst.in @@ -95,16 +95,18 @@ set -e echo "[POST-INSTALL] Trigger deamon-reload:" systemctl --system daemon-reload || true -# If needed, try to load/compile the driver through falco-driver-loader +# If needed, try to load/compile the driver through falcoctl +echo "[POST-INSTALL] Configure falcoctl driver type:" +falcoctl driver config --type $chosen_driver case "$chosen_driver" in "kmod") # Only compile for kmod, in this way we use dkms - echo "[POST-INSTALL] Call 'falco-driver-loader --compile module':" - falco-driver-loader --compile module + echo "[POST-INSTALL] Call 'falcoctl driver install for kmod:" + falcoctl driver install --download=false ;; "bpf") - echo "[POST-INSTALL] Call 'falco-driver-loader bpf':" - falco-driver-loader bpf + echo "[POST-INSTALL] Call 'falcoctl driver install for bpf':" + falcoctl driver install ;; esac diff --git a/scripts/debian/prerm.in b/scripts/debian/prerm.in index 6015aeee402..7eca20c7f97 100755 --- a/scripts/debian/prerm.in +++ b/scripts/debian/prerm.in @@ -31,7 +31,7 @@ case "$1" in systemctl --system stop 'falco-custom.service' || true systemctl --system stop 'falcoctl-artifact-follow.service' || true - echo "[PRE-REMOVE] Call 'falco-driver-loader --clean:'" - falco-driver-loader --clean + echo "[PRE-REMOVE] Call 'falcoctl driver cleanup:'" + falcoctl driver cleanup ;; esac diff --git a/scripts/falco-driver-loader b/scripts/falco-driver-loader deleted file mode 100755 index 1006fda01d3..00000000000 --- a/scripts/falco-driver-loader +++ /dev/null @@ -1,866 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: Apache-2.0 -# -# Copyright (C) 2023 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/redhat-release" ]; then - # Older RHEL distros - OS_ID=rhel - else - # No target id can be determinand - TARGET_ID="undetermined" - return - fi - - # Overwrite the OS_ID if /etc/VERSION file is present. - # Not sure if there is a better way to detect minikube. - if [ -f "${HOST_ROOT}/etc/VERSION" ]; then - OS_ID=minikube - fi - - case "${OS_ID}" in - ("amzn") - case "${VERSION_ID}" in - ("2") - TARGET_ID="amazonlinux2" - ;; - ("2022") - TARGET_ID="amazonlinux2022" - ;; - ("2023") - TARGET_ID="amazonlinux2023" - ;; - (*) - TARGET_ID="amazonlinux" - ;; - esac - ;; - ("debian") - # Workaround: debian kernelreleases might now be actual kernel running; - # instead, they might be the Debian kernel package - # providing the compatible kernel ABI - # See https://lists.debian.org/debian-user/2017/03/msg00485.html - # Real kernel release is embedded inside the kernel version. - # Moreover, kernel arch, when present, is attached to the former, - # therefore make sure to properly take it and attach it to the latter. - # Moreover, we support 3 flavors for debian kernels: cloud, rt and normal. - # KERNEL-RELEASE will have a `-rt`, or `-cloud` if we are in one of these flavors. - # Manage it to download the correct driver. - # - # Example: KERNEL_RELEASE="5.10.0-0.deb10.22-rt-amd64" and `uname -v`="5.10.178-3" - # should lead to: KERNEL_RELEASE="5.10.178-3-rt-amd64" - TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]') - local ARCH_extra="" - if [[ $KERNEL_RELEASE =~ -?(rt-|cloud-|)(amd64|arm64) ]]; - then - ARCH_extra="-${BASH_REMATCH[1]}${BASH_REMATCH[2]}" - fi - if [[ ${DRIVER_KERNEL_VERSION} =~ ([0-9]+\.[0-9]+\.[0-9]+\-[0-9]+) ]]; - then - KERNEL_RELEASE="${BASH_REMATCH[1]}${ARCH_extra}" - fi - ;; - ("ubuntu") - # Extract the flavor from the kernelrelease - # Examples: - # 5.0.0-1028-aws-5.0 -> ubuntu-aws - # 5.15.0-1009-aws -> ubuntu-aws - if [[ $KERNEL_RELEASE =~ -([a-zA-Z]+)(-.*)?$ ]]; - then - TARGET_ID="ubuntu-${BASH_REMATCH[1]}" - else - TARGET_ID="ubuntu-generic" - fi - - - # In the case that the kernelversion isn't just a number - # we keep also the remaining part excluding `-Ubuntu`. - # E.g.: - # from the following `uname -v` result - # `#26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:15 UTC 2023` - # we obtain the kernelversion`26~22.04.1` - if [[ ${DRIVER_KERNEL_VERSION} =~ (^\#[0-9]+\~[^-]*-Ubuntu .*$) ]]; - then - KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([^-\\ ]*\).*/\1/g') - fi - ;; - ("flatcar") - KERNEL_RELEASE="${VERSION_ID}" - TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]') - ;; - ("minikube") - TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]') - # Extract the minikube version. Ex. With minikube version equal to "v1.26.0-1655407986-14197" the extracted version - # will be "1.26.0" - if [[ $(cat ${HOST_ROOT}/etc/VERSION) =~ ([0-9]+(\.[0-9]+){2}) ]]; then - # kernel version for minikube is always in "1_minikubeversion" format. Ex "1_1.26.0". - KERNEL_VERSION="1_${BASH_REMATCH[1]}" - else - echo "* Unable to extract minikube version from ${HOST_ROOT}/etc/VERSION" - exit 1 - fi - ;; - ("bottlerocket") - TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]') - # variant_id has been sourced from os-release. Get only the first variant part - if [[ -n ${VARIANT_ID} ]]; then - # take just first part (eg: VARIANT_ID=aws-k8s-1.15 -> aws) - VARIANT_ID_CUT=${VARIANT_ID%%-*} - fi - # version_id has been sourced from os-release. Build a kernel version like: 1_1.11.0-aws - KERNEL_VERSION="1_${VERSION_ID}-${VARIANT_ID_CUT}" - ;; - ("talos") - TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]') - # version_id has been sourced from os-release. Build a kernel version like: 1_1.4.1 - KERNEL_VERSION="1_${VERSION_ID}" - ;; - (*) - 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-linux-*.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 [[ ${DRIVER_KERNEL_RELEASE} == *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=${DRIVER_KERNEL_RELEASE} - 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 $(ls "$(dirname "$(which gcc)")"/gcc*); do - # Filter away gcc-{ar,nm,...} - # Only gcc compiler has `-print-search-dirs` option. - ${CURRENT_GCC} -print-search-dirs 2>&1 | grep "install:" - if [ "$?" -ne "0" ]; then - continue - fi - echo "* Trying to dkms install ${DRIVER_NAME} module with GCC ${CURRENT_GCC}" - echo "#!/usr/bin/env bash" > "${TMPDIR}/falco-dkms-make" - echo "make CC=${CURRENT_GCC} \$@" >> "${TMPDIR}/falco-dkms-make" - chmod +x "${TMPDIR}/falco-dkms-make" - if dkms install --directive="MAKE='${TMPDIR}/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 to 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 - fi - echo "* Unable to insmod ${DRIVER_NAME} module" - 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() { - local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko" - local URL=$(echo "${1}/${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/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then - echo "* Download succeeded" - chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true - if insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}"; then - echo "* Success: ${DRIVER_NAME} module found and inserted" - exit 0 - fi - >&2 echo "Unable to insmod the prebuilt ${DRIVER_NAME} module" - 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 -} - -print_filename_components() { - echo " - driver name: ${DRIVER_NAME}" - echo " - target identifier: ${TARGET_ID}" - echo " - kernel release: ${KERNEL_RELEASE}" - echo " - kernel version: ${KERNEL_VERSION}" -} - -print_as_env_vars() { - echo "ARCH=\"${ARCH}\"" - echo "KERNEL_RELEASE=\"${KERNEL_RELEASE}\"" - echo "KERNEL_VERSION=\"${KERNEL_VERSION}\"" - echo "ENABLE_COMPILE=\"${ENABLE_COMPILE}\"" - echo "ENABLE_DOWNLOAD=\"${ENABLE_DOWNLOAD}\"" - echo "TARGET_ID=\"${TARGET_ID}\"" - echo "DRIVER=\"${DRIVER}\"" - echo "DRIVERS_REPO=\"${DRIVERS_REPO}\"" - echo "DRIVER_VERSION=\"${DRIVER_VERSION}\"" - echo "DRIVER_NAME=\"${DRIVER_NAME}\"" - echo "FALCO_VERSION=\"${FALCO_VERSION}\"" -} - -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})" - - local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko" - echo "* Filename '${FALCO_KERNEL_MODULE_FILENAME}' is composed of:" - print_filename_components - - if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" ]; then - echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}, loading it" - chcon -t modules_object_t "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true - insmod "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted" - exit $? - fi - - if [ -n "$ENABLE_DOWNLOAD" ]; then - IFS=", " read -r -a urls <<< "${DRIVERS_REPO}" - for url in "${urls[@]}"; do - load_kernel_module_download $url - done - 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}" == "flatcar" ]; then - KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE} - echo "* Flatcar detected (version ${VERSION_ID}); relocating kernel tools" - flatcar_relocate_tools - fi - - 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=${DRIVER_KERNEL_RELEASE} - 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=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d. -f1) - local -r kernel_version=$(echo "${DRIVER_KERNEL_RELEASE}" | cut -d- -f1) - KERNEL_EXTRA_VERSION="-$(echo "${DRIVER_KERNEL_RELEASE}" | 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/${DRIVER_VERSION}/${ARCH}" - mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" - - if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then - rm -r /tmp/kernel - fi - -} - -load_bpf_probe_download() { - local URL - URL=$(echo "${1}/${DRIVER_VERSION}/${ARCH}/${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/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" "${URL}"; then - >&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe" - return 1 - fi - return 0 -} - -load_bpf_probe() { - - if [ ! -d /sys/kernel/debug/tracing ]; then - echo "* Mounting debugfs" - mount -t debugfs nodev /sys/kernel/debug - fi - - BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o" - echo "* Filename '${BPF_PROBE_FILENAME}' is composed of:" - print_filename_components - - if [ -n "$ENABLE_DOWNLOAD" ]; then - if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then - echo "* Skipping download, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" - else - IFS=", " read -r -a urls <<< "${DRIVERS_REPO}" - for url in "${urls[@]}"; do - load_bpf_probe_download $url - if [ $? -eq 0 ]; then - break - fi - done - fi - fi - - if [ -n "$ENABLE_COMPILE" ]; then - if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then - echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" - else - load_bpf_probe_compile - fi - fi - - if [ -f "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" ]; then - echo "* eBPF probe located in ${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${BPF_PROBE_FILENAME}" - - ln -sf "${HOME}/.falco/${DRIVER_VERSION}/${ARCH}/${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 using `. falco-driver-loader`" - echo " --print-env skip execution and print env variables for other tools to consume" - echo "" - echo "Environment variables:" - echo " DRIVERS_REPO specify different URL(s) where to look for prebuilt Falco drivers (comma separated)" - echo " DRIVER_NAME specify a different name for the driver" - echo " DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not" - echo " DRIVER_CURL_OPTIONS specify additional options to be passed to curl command used to download Falco drivers" - echo " DRIVER_KERNEL_RELEASE specify the kernel release for which to download/build the driver in the same format used by 'uname -r' (e.g. '6.1.0-10-cloud-amd64')" - echo " DRIVER_KERNEL_VERSION specify the kernel version for which to download/build the driver in the same format used by 'uname -v' (e.g. '#1 SMP PREEMPT_DYNAMIC Debian 6.1.38-2 (2023-07-27)')" - echo "" - echo "Versions:" - echo " Falco version ${FALCO_VERSION}" - echo " Driver version ${DRIVER_VERSION}" - echo "" -} - -ARCH=$(uname -m) - -DRIVER_KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE:-$(uname -r)} -KERNEL_RELEASE=${DRIVER_KERNEL_RELEASE} - -if ! hash sed > /dev/null 2>&1; then - >&2 echo "This program requires sed" - exit 1 -fi - -DRIVER_KERNEL_VERSION=${DRIVER_KERNEL_VERSION:-$(uname -v)} -KERNEL_VERSION=$(echo "${DRIVER_KERNEL_VERSION}" | sed 's/#\([[:digit:]]\+\).*/\1/') - -DRIVERS_REPO=${DRIVERS_REPO:-"@DRIVERS_REPO@"} - -FALCO_DRIVER_CURL_OPTIONS="-fsS --connect-timeout 5 --max-time 60 --retry 3 --retry-max-time 120" - -if [ -n "$DRIVER_INSECURE_DOWNLOAD" ] -then - FALCO_DRIVER_CURL_OPTIONS+=" -k" -fi - -FALCO_DRIVER_CURL_OPTIONS+=" "${DRIVER_CURL_OPTIONS} - -if [[ -z "$MAX_RMMOD_WAIT" ]]; then - MAX_RMMOD_WAIT=60 -fi - -DRIVER_VERSION=${DRIVER_VERSION:-"@DRIVER_VERSION@"} -DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"} -FALCO_VERSION="@FALCO_VERSION@" - -TARGET_ID= -get_target_id - -DRIVER="module" -if [ -v FALCO_BPF_PROBE ]; then - DRIVER="bpf" -fi - -TMPDIR=${TMPDIR:-"/tmp"} - -ENABLE_COMPILE= -ENABLE_DOWNLOAD= - -clean= -has_args= -has_opts= -print_env= -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 - ;; - --print-env) - print_env="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 [ -n "$source_only" ]; then - # Return or exit, depending if we've been sourced. - (return 0 2>/dev/null) && return || exit 0 -fi - -if [ -n "$print_env" ]; then - print_as_env_vars - exit 0 -fi - -echo "* Running falco-driver-loader for: falco version=${FALCO_VERSION}, driver version=${DRIVER_VERSION}, arch=${ARCH}, kernel release=${KERNEL_RELEASE}, kernel version=${KERNEL_VERSION}" - -if [ "$(id -u)" != 0 ]; then - >&2 echo "This program must be run as root (or with sudo)" - exit 1 -fi - -if [ "$TARGET_ID" = "undetermined" ]; then - if [ -n "$ENABLE_COMPILE" ]; then - ENABLE_DOWNLOAD= - >&2 echo "Detected an unsupported target system, please get in touch with the Falco community. Trying to compile anyway." - else - >&2 echo "Detected an unsupported target system, please get in touch with the Falco community." - exit 1 - fi -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 diff --git a/scripts/falcoctl/falcoctl.yaml b/scripts/falcoctl/falcoctl.yaml.in similarity index 60% rename from scripts/falcoctl/falcoctl.yaml rename to scripts/falcoctl/falcoctl.yaml.in index 05f7814d296..adaed604e89 100644 --- a/scripts/falcoctl/falcoctl.yaml +++ b/scripts/falcoctl/falcoctl.yaml.in @@ -1,3 +1,10 @@ +driver: + type: "kmod" + name: "@DRIVER_NAME@" + repos: + - "@DRIVERS_REPO@" + version: "@DRIVER_VERSION@" + hostroot: "" artifact: follow: every: 6h0m0s diff --git a/scripts/rpm/postinstall.in b/scripts/rpm/postinstall.in index dbdf2d4699b..c89a9c1543e 100755 --- a/scripts/rpm/postinstall.in +++ b/scripts/rpm/postinstall.in @@ -94,16 +94,18 @@ set -e echo "[POST-INSTALL] Trigger deamon-reload:" systemctl --system daemon-reload || true -# If needed, try to load/compile the driver through falco-driver-loader +# If needed, try to load/compile the driver through falcoctl +echo "[POST-INSTALL] Configure falcoctl driver type:" +falcoctl driver config --type $chosen_driver case "$chosen_driver" in "kmod") # Only compile for kmod, in this way we use dkms - echo "[POST-INSTALL] Call 'falco-driver-loader --compile module':" - falco-driver-loader --compile module + echo "[POST-INSTALL] Call 'falcoctl driver install for kmod:" + falcoctl driver install --download=false ;; "bpf") - echo "[POST-INSTALL] Call 'falco-driver-loader bpf':" - falco-driver-loader bpf + echo "[POST-INSTALL] Call 'falcoctl driver install for bpf':" + falcoctl driver install ;; esac diff --git a/scripts/rpm/preuninstall.in b/scripts/rpm/preuninstall.in index 7d2f457f372..3579bc87cbe 100755 --- a/scripts/rpm/preuninstall.in +++ b/scripts/rpm/preuninstall.in @@ -25,8 +25,8 @@ systemctl --system stop 'falco-modern-bpf.service' || true systemctl --system stop 'falco-custom.service' || true systemctl --system stop 'falcoctl-artifact-follow.service' || true -echo "[PRE-REMOVE] Call 'falco-driver-loader --clean:'" -falco-driver-loader --clean +echo "[PRE-REMOVE] Call 'falcoctl driver cleanup:'" +falcoctl driver cleanup # validate rpm macros by `rpm -qp --scripts ` # RPM scriptlets: https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd