#!/bin/sh # Shell Script for Auto Updating the Nginx Bad Bot Blocker # Copyright: https://github.com/mitchellkrogza # Project Url: https://github.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker # Update script & Alpine Linux package by Stuart Cardall: https://github.com/itoffshore # MAKE SURE you have all the following files in /etc/nginx/bots.d/ folder # *********************************************************************** # whitelist-ips.conf # whitelist-domains.conf # blacklist-user-agents.conf # bad-referrer-words.conf # custom-bad-referrers.conf # blacklist-ips.conf # A major change to using include files was introduced in # https://github.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/commit/7e3ab02172dafdd524de5dd450a9732328622779 # ************************************************************************** # Nginx will fail a reload with [EMERG] without the presence of these files. # PLEASE READ UPDATED CONFIGURATION INSTRUCTIONS BEFORE USING THIS # Save this file as /usr/sbin/update-ngxblocker # cd /usr/sbin # sudo wget https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/update-ngxblocker -O update-ngxblocker # Make it Executable chmod 700 /usr/sbin/update-ngxblocker # RUN THE UPDATE # Here our script runs, pulls the latest update, reloads nginx and emails you a notification EMAIL="me@myemail.com" SEND_EMAIL="Y" CONF_DIR=/etc/nginx/conf.d BOTS_DIR=/etc/nginx/bots.d INSTALLER=/usr/sbin/install-ngxblocker ##### end user configuration ############################################################## BOLDGREEN="\033[1m\033[32m" BOLDMAGENTA="\033[1m\033[35m" BOLDRED="\033[1m\033[31m" BOLDYELLOW="\033[1m\033[33m" BOLDWHITE="\033[1m\033[37m" RESET="\033[0m" usage() { local script=$(basename $0) cat < $BOLDMAGENTA$remote_ver$RESET\n\n" else printf "\nLatest Blacklist Already Installed: $BOLDGREEN$version$RESET\n\n" fi else printf "${BOLDRED}ERROR${RESET}: Missing '$file' => ${BOLDWHITE}running $INSTALLER:${RESET}\n" $INSTALL_INC if [ -f $file ]; then check_version fi fi exit 0 } check_dirs() { local x= dirs=$@ for x in $dirs; do if [ ! -d $x ]; then printf "${BOLDRED}ERROR${RESET}: Missing directory: $x => ${BOLDWHITE}running $INSTALLER:${RESET}\n" $INSTALL_INC fi done } update_paths() { # variables in nginx include files not currently possible # updates hard coded bots.d path in globalblacklist.conf local blacklist=$1 include_paths= dir= x= if ! grep "$BOTS_DIR" $blacklist 1>/dev/null; then if [ -d $BOTS_DIR ]; then printf "${BOLDGREEN}Updating bots.d path${RESET}: ${BOLDWHITE}$BOTS_DIR => $blacklist${RESET}\n" include_paths=$(grep -E "include /.*.conf;$" $blacklist | awk '{print $2}' | tr -d ';') for x in $include_paths; do dir=$(dirname $x) sed -i "s|$dir|$BOTS_DIR|" $blacklist done else printf "${BOLDRED}ERROR${RESET}: '$BOTS_DIR' does not exist => ${BOLDWHITE}running $INSTALLER${RESET}.\n" $INSTALL_INC update_paths $blacklist fi fi } service_cmd() { # arch linux does not have a 'service' command local svc= svc_list="service systemctl rc-service" for svc in $svc_list; do svc=$(which $svc 2>/dev/null) if [ -n "$svc" ]; then echo $svc exit 0 fi done } wget_opts() { local opts= # Busybox wget gives less verbose output by default if [ -n "$(wget --help 2>/dev/null | grep "\-nv")" ]; then opts="-nv" fi echo $opts } sanitize_path() { echo $1 |tr -cd '[:alnum:] [=@=] [=.=] [=-=] [=/=] [=_=]' \ |tr -s '@.-/_' |awk '{print tolower($0)}' } sanitize_url() { echo $1 |tr -cd '[:alnum:] [=:=] [=.=] [=-=] [=/=]' \ |tr -s ':.-' |awk '{print tolower($0)}' } sanitize_email() { echo $1 |tr -cd '[:alnum:] [=@=] [=.=] [=-=] [=_=]' \ |tr -s '@-_.' |awk '{print tolower($0)}' } check_args() { local option=$1 type=$2 arg=$3 local msg="ERROR: option '-$option' argument '$arg' requires:" case "$type" in path) if ! echo $arg | grep ^/ 1>/dev/null; then printf "$msg absolute path.\n" exit 1 fi ;; email) if ! echo $arg | grep -E ^[-_[:alnum:]]+@[-_[:alnum:]]+[\.][\.a-z]+ 1>/dev/null; then printf "$msg email@domain.com\n" exit 1 fi ;; url) if ! echo $arg | grep -E ^http[s]?://[0-9a-zA-Z-]+[.]+[/0-9a-zA-Z.]+ 1>/dev/null; then printf "$msg url => http[s]://the.url\n" exit 1 fi ;; script) if [ ! -x $arg ]; then printf "$msg '$arg' is not executable / does not exist.\n" exit 1 fi ;; none) printf "$msg argument.\n"; exit 1;; esac } check_mail_depends() { if [ ! -f /usr/bin/mail ]; then # mailx + ssmtp are enough to send emails printf "${BOLDYELLOW}WARN${RESET}: missing /usr/bin/mail => ${BOLDWHITE}disabling emails${RESET}.\n\n" SEND_EMAIL="N" fi } check_depends() { # centos does not have wget installed by default if ! wget --help >/dev/null 2>&1; then printf "${BOLDRED}ERROR${RESET}: $0 requires: 'wget' => ${BOLDWHITE}cannot download files.${RESET}\n" exit 1 fi # centos also does not have which by default if [ ! -x /usr/bin/curl ]; then printf "${BOLDRED}ERROR${RESET}: $0 requires: 'curl' => ${BOLDWHITE}cannot check remote version.${RESET}\n" exit 1 fi # install-ngxblocker downloads updated scripts / missing includes as part of the update process if [ ! -x $INSTALLER ]; then printf "${BOLDRED}ERROR${RESET}: $0 requires: '$INSTALLER' => ${BOLDWHITE}cannot update includes.${RESET}\n" exit 1 fi } get_options() { local arg= opts= while getopts :c:b:u:r:e:nvh opts "$@" do if [ -n "${OPTARG}" ]; then case "$opts" in r) arg=$(sanitize_url ${OPTARG});; e) arg=$(sanitize_email ${OPTARG});; *) arg=$(sanitize_path ${OPTARG});; esac fi case "$opts" in c) CONF_DIR=$arg; check_args $opts path $arg ;; b) BOTS_DIR=$arg; check_args $opts path $arg ;; u) INSTALLER=$arg; check_args $opts script $arg ;; r) REPO=$arg; check_args $opts url $arg ;; e) EMAIL=$arg; check_args $opts email $arg ;; n) SEND_EMAIL=N ;; v) check_depends; check_version ;; h) usage ;; \?) usage ;; :) check_args $OPTARG none none ;; esac done INSTALL_INC="$INSTALLER -b $BOTS_DIR -c $CONF_DIR -x" } main() { local REPO=https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master local file=globalblacklist.conf remote_dir=conf.d url= output= update= status= # default to service (centos does not have 'which' by default) local service=${service_cmd:-"service"} # require root if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" 1>&2 exit 1 fi check_depends check_dirs $BOTS_DIR $CONF_DIR $SCRIPT_DIR # parse command line get_options $@ url=$REPO/$remote_dir/$file output=$CONF_DIR/$file # check for updated blacklist update=$(check_version | tail -n 2) printf "\n$update\n\n" if echo $update | grep ^Update 1>/dev/null; then # download globalblacklist update mkdir -p $CONF_DIR wget $url $(wget_opts) -O $output 2>&1 # download new bots.d / conf.d files $INSTALL_INC # re-read nginx configuration if ! grep "Not Found" $EMAIL_REPORT; then # set custom bots.d path update_paths $output $service nginx reload if [ $? = 0 ]; then status="${BOLDGREEN}[OK]${RESET}" else status="${BOLDRED}[FAILED]${RESET}" fi printf "\nReloading NGINX configuration...$status\n" else printf "\n${BOLDRED}Download failed${RESET}: not reloading NGINX config\n" fi else # set custom bots.d path update_paths $output fi # email report check_mail_depends case "$SEND_EMAIL" in y*|Y*) printf "Emailing report to: ${BOLDWHITE}$EMAIL${RESET}\n\n"; # remove ansi colour codes sed -i 's/\x1b\[[0-9;]*m//g' $EMAIL_REPORT cat $EMAIL_REPORT | mail -s "Nginx Bad Bot Blocker Updated" $EMAIL ;; esac rm -f $EMAIL_REPORT } ## start ## EMAIL_REPORT=$(mktemp) main $@ | tee $EMAIL_REPORT exit $? # Add this as a cron to run daily / weekly as you like # Here's a sample CRON entry to update every day at 10pm # 00 22 * * * sudo /usr/sbin/update-ngxblocker # Here's another example to run it daily at midday using a command line switch to set the email address for the notification # 00 12 * * * sudo /usr/sbin/update-ngxblocker -e yourname@youremailprovider.com # better logging for cron jobs: # https://serverfault.com/questions/137468/better-logging-for-cronjobs-send-cron-output-to-syslog