diff --git a/README.md b/README.md index d3f27a003277f75e7bb6f7632564a6f6c8e02206..b3a44450d848a15d1236b49f9df5f9756f9bc9b4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # atomicorp_modsec_update.sh -Update atomicorp modsecurity ruleset + +Update AtomiCorp modsecurity Enterprise ruleset + +Tested on Ubuntu LTS + +You must have a valid AtomiCorp licence (username-password) to access the Enterprise ruleset. +(otherwise, you may use AtomiCorp free ruleset) diff --git a/atomicorp_modsec_update.sh b/atomicorp_modsec_update.sh new file mode 100644 index 0000000000000000000000000000000000000000..f36cafc170412aec1c10d66f95fa1636a06ac886 --- /dev/null +++ b/atomicorp_modsec_update.sh @@ -0,0 +1,197 @@ +#!/bin/bash +# +# atomicorp_modsec_update.sh +# +# Update atomicorp enterprise modsecurity rules +# +DT=`date +%Y-%m-%d` +ASL_ROOT=/var/asl +ASL_UPDATES=/var/asl/updates +ASL_RULES=/var/asl/rules +ASL_RULES_BACKUP=/var/asl/rules_backup +VERSION_URL=https://updates.atomicorp.com/channels/rules/subscription/VERSION +VERSION_KEY="MODSEC_VERSION" +RULES_URL=https://updates.atomicorp.com/channels/rules/subscription +TMP=/tmp/.atomicorp_modsec.$$ +PROTECTED_CONFIG_FILES="modsec/bad_agents.txt modsec/domain-blacklist-local.txt modsec/domain-spam-whitelist.conf modsec/domain-spam-whitelist.txt modsec/dos_protected.txt modsec/honeypot-files.txt modsec/malware-blacklist-local.txt modsec/trusted-domains.conf modsec/trusted-domains.txt" + +# +# Base64 encoded values here, just to avoid eye contact (no cryptography) +# To setup your own user/pass value use the output of: +# "read value ; echo $value | base 64 ; unset value" (remember to escape special caracters) +# +ASL_USER=`echo REPLACE_ME | base64 -d` +ASL_PASS=`echo REPLACE_ME | base64 -d` +# + +# Nothing to be configured below this line +################################################################################ + +echo "$1" | grep -i -q "\-v" +if [ $? -eq 0 ]; then + VERBOSE=1 +else + VERBOSE=0 +fi + +cd $ASL_UPDATES || exit 1 +mkdir -p $ASL_RULES_BACKUP || exit 1 + +# +# function rollback +# So many things may go wrong, we need to make this a function... +# +function rollback { + echo "Rollback to previous ruleset... Warning: apache may be in inconsistent state" + echo "Sysadmin validation is recommended" + mv -v $ASL_RULES $ASL_RULES.rollback.$$ + mv -v $ASL_RULES.old $ASL_RULES + rm -f $TMP $TMP.exclude +} + +[ `expr $VERBOSE` ] && echo "Checking apache status..." +systemctl status apache2 1>/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Error: Apache is not running, can't take the risk to mess with config files on next startup. Aborting update." + exit 0 +fi + +rm -f VERSION +wget --user=$ASL_USER --password="$ASL_PASS" $VERSION_URL 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: can't get version file (double check your authentication settings)" + cat $TMP + rm -f $TMP + exit 1 +fi + +latest_version=`cat VERSION | grep $VERSION_KEY | sed 's/.*=//'` +current_version=`ls -1 modsec-*.tar.bz2 | tail -n 1 | sed 's/.*-//' | sed 's/.tar.bz2//'` + +[ `expr $VERBOSE` ] && echo "Latest version: $latest_version, current version: $current_version" + +if [ $current_version -lt $latest_version ]; then + echo "Ruleset is not up to date." +else + [ `expr $VERBOSE` ] && echo "Ruleset is up to date." + exit 0 +fi + +echo "Downloading modsec rules..." + +wget --user=$ASL_USER --password="$ASL_PASS" $RULES_URL/modsec-$latest_version.tar.bz2 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: can't get modsec rules tarball file" + cat $TMP + rm -f $TMP + exit 1 +fi + +echo "Downloading modsec rules sha2..." + +wget --user=$ASL_USER --password="$ASL_PASS" $RULES_URL/modsec-$latest_version.tar.bz2.sha512 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: can't get modsec rules hash file" + cat $TMP + rm -f $TMP + exit 1 +fi + +echo "Validating modsec rules sha2 hash..." + +sha512sum -c modsec-$latest_version.tar.bz2.sha512 +if [ $? -ne 0 ]; then + echo "Error: failed to verify rules checksum" + exit 1 +fi + +echo "Backing up current ruleset..." + +tar -cjf $ASL_RULES_BACKUP/rules-$DT.tar.bz2 $ASL_RULES 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: can't backup rules" + exit 1 +fi + +echo "Replacing rules..." +rm -fr $ASL_RULES.old +mkdir $ASL_RULES.old +cp -r $ASL_RULES/modsec $ASL_RULES.old/ + +cd $ASL_RULES +cat /dev/null > $TMP.exclude +for file in $PROTECTED_CONFIG_FILES; do + echo $file >> $TMP.exclude +done +tar --exclude-from $TMP.exclude -xjf $ASL_UPDATES/modsec-$latest_version.tar.bz2 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: can't deploy new rules" + cat $TMP + rollback + exit 1 +fi +rm -f $TMP.exclude + +# move the version file, just for reference +mv $ASL_UPDATES/VERSION $ASL_RULES/modsec/ + +# At this point we have a new ruleset in place +# Let's check if it suits apache mod_security +# In any case of warning or error: +# abort and rollback to previous (current) ruleset + +echo "Checking apache config files..." + +apache2ctl -S 1>$TMP $2>&1 +if [ $? -ne 0 ]; then + echo "Error: Apache check failed" + cat $TMP + rollback + exit 1 +fi + +echo "Checking apache status..." + +systemctl status apache2 1>$TMP 2>&1 +if [ $? -ne 0 ]; then + echo "Error: Apache is not running, can't take the risk to mess with config files on next startup" + cat $TMP + rollback + exit 1 +fi + +# looks good, let's restart apache +echo "Stopping apache..." + +systemctl stop apache2 1>$TMP $2>1 +if [ $? -ne 0 ]; then + echo "Error: Can't stop apache (not my fault)" + cat $TMP + rollback + exit 1 +fi + +echo "Starting apache..." + +systemctl start apache2 1>$TMP $2>1 +if [ $? -ne 0 ]; then + echo "Error: Can't start apache (maybe it's my fault)" + cat $TMP + rollback + exit 1 +fi + +# paranoid last check +echo "Checking apache status..." + +sleep 1 +systemctl status apache2 1>$TMP $2>1 +if [ $? -ne 0 ]; then + echo "Error: Apache started but now has a bad status (maybe it's my fault)" + cat $TMP + rollback + exit 1 +fi + +rm -f $TMP +exit 0