Updating Lynis
Lynis is an excellent security audit tool for Linux and various Unix derivatives. I have a small wrapper script that runs Lynis via a cron job, does a selective diff with the previous run’s output, and sends me an email.
Unfortunately, Lynis does not update itself automatically and this is the sort of application that needs to be up-to-date. I installed mine via a tarball – the most common and recommended way for this utility – and so it does not update when I patch the system.
Here is a, perhaps, unnecessarily convoluted script that checks the version of Lynis and updates it, if required. In a nutshell, all this script does is download the most recent version of Lynis and overwrites the existing version on your machine.
But there are a lot of checks in the background designed to detect a variety of issues. When writing this sort of scripts I try to hard-code as little as possible. Primarily, because the folks maintaining such online resources usually have little regard for automation and can make silly changes to names and URLs without notice.
The script is below for your review. If you’re going to use it, I suggest downloading it from my GitHub repo to avoid any copy-paste weirdness.
#!/bin/bash # # | # ___/"\___ # __________/ o \__________ # (I) (G) \___/ (O) (R) # Igor Os # igor@comradegeneral.com # 2020-02-29 # ---------------------------------------------------------------------------- # Update current installation of Lynis. Only use this script if Lynis was # installed via a tarball. If you have a version installed via RPM or another # package managements tool, uninstall it first. # ---------------------------------------------------------------------------- configure() { echo "Running script configuration" # Specify Lynis executable LYNIS=/usr/bin/lynis [ -x "${LYNIS}" ] || (echo "Lynis executable ${LYNIS} not found. Exiting..." && exit 1110) this_script=$(basename "$(test -L "$0" && readlink "$0" || echo "$0")") # Get update info echo "Getting update info" update_info="$(${LYNIS} update info)" [ -z "${update_info}" ] && (echo "Unable to get update info. Exiting..." && exit 1120) # Parse update info echo "Parsing update info" update_status="$(echo "${update_info}" | grep -E '^\s+Status' | awk '{print $NF}')" release_date="$(echo "${update_info}" | grep -E '^\s+Release' | awk '{print $NF}')" current_version="$(echo "${update_info}" | grep -E '^\s+Version' | awk '{print $NF}')" update_location="$(echo "${update_info}" | grep -E '^\s+Update location' | awk '{print $NF}' | sed 's/\/$//g')" update_host="$(echo "${update_location}" | grep -oP "(?<=:\/\/).*?(?=\/)")" ([ -z "${update_status}" ] || [ -z "${current_version}" ] || \ [ -z "${release_date}" ] || [ -z "${update_location}" ] || [ -z "${update_host}" ]) && \ (echo "Unable to parse update info. Exiting..." && exit 1130) # Resolve update host echo "Resolving update host" update_host_ip="$(dig +short "${update_host}" | \ grep -m1 -oE "([0-9]{1,3}\.){3}([0-9]{1,3})")" [ -z "${update_host_ip}" ] && \ (echo "Unable to resolve ${update_host}. Exiting..." && exit 1135) # Verify update host port access echo "Verifying update host port access" nc -v -i1 -w1 "${update_host_ip}" 443 2>/dev/null 1>&2 || \ (echo "Unable to access ${update_host_ip}:443. Exiting..." && exit 1137) # Verify access to update URL echo "Verifying update location availability" if [ ! $(curl -s0SfkL "${update_location}" 2>/dev/null | \ grep -oP "(?<=\<title\>).*?(?=\<\/title\>)" 2>/dev/null | \ grep -c Lynis) -gt 0 ] then echo "Unable to reach ${update_location}. Exiting..." exit 1140 fi # Determine download URL echo "Checking for download URL" base_url="$(awk -F'/' '{print $1"/"$2"/"$3}' <<<"${update_location}")" path_url="$(curl -s0SfkL "${update_location}" 2>/dev/null | awk 'BEGIN{ RS="</a>" IGNORECASE=1 } { for(o=1;o<=NF;o++){ if ( $o ~ /href/){ gsub(/.*href=2/,"",$o) gsub(/2.*/,"",$o) print $(o) } } }' | grep -m1 -iE "(lynis.*downloads)|(downloads.*lynis)")" [ -z "${path_url}" ] && (echo "Unable to determine download URL. Exiting..." && exit 1150) download_url="${base_url}/${path_url}" # Determine latest version download URL echo "Checking for the latest version download link" version_url="$(curl -s0SfkL "${download_url}" 2>/dev/null | awk 'BEGIN{ RS="</a>" IGNORECASE=1 } { for(o=1;o<=NF;o++){ if ( $o ~ /href/){ gsub(/.*href=2/,"",$o) gsub(/2.*/,"",$o) print $(o) } } }' | grep -m1 -iE "lynis.*\.(tar\.gz|tgz)")" [ -z "${version_url}" ] && (echo "Unable to determine latest version download link. Exiting..." && exit 1150) version_file="$(echo "${version_url}" | awk -F'/' '{print $NF}')" } lynis_update() { # Check if update is needed if [ "${update_status}" == "Outdated" ] then echo "Update is required." # Download the latest version echo "Downloading ${version_file}" curl -s0SfkL "${version_url}" > ~/"${version_file}" [ $(file --mime-type ~/"${version_file}" -F$'\t' | awk -F'\t *' '$2 ~/^application\/x-gzip/ { print $1 }') ] || \ (echo "Unable to download ${version_url}. Exiting..." && exit 1160) # Uncompress the latest version echo "Uncompressing ~/${version_file}" [ -d ~/lynis ] && /bin/mv ~/lynis ~/lynis_$(date +'%Y-%m-%d_%H%M') 2>/dev/null cd ~ && tar xfz ~/"${version_file}" || (echo "Unable to uncompress ~/${version_file}. Exiting..." && exit 1170) [ -d /usr/local/lynis ] && cd /usr/local && tar cfz ~/lynis_previous_$(date +'%Y-%m-%d_%H%M').tar.gz lynis && \ /bin/rm -rf lynis && cd ~ /bin/mv -f ~/lynis /usr/local/ && ln -s /usr/local/lynis/lynis ${LYNIS} 2>/dev/null /bin/rm ~/"${version_file}" 2>/dev/null ${LYNIS} update info echo "Update complete" else echo "Update is not required at this time. Exiting" exit 0 fi } # ---------------------------------------------------------------------------- # RUNTIME # \(^_^)/ __|__ # __|__ *---o0o---* # __|__ *---o0o---* # *---o0o---* # ---------------------------------------------------------------------------- configure lynis_update