#!/bin/ksh PATH=/bin:/usr/bin export PATH # $Id: snort.server,v 1.2 2003/11/23 19:25:39 jeff Exp $ #-------------------------------------------------- # snort.server script Version 1.4 # # Copyright (C) 2002, 2003 Jeff Nathan # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #-------------------------------------------------- ################################################### # This script contains various shell functions # and a generic command line parser to make the # process of running and managing Snort easier. # # Consult the ksh(1) and snort(8) man pages for # questions on using any of the options below. # # Editing the variables below will allow you to # customize this scipt to your liking. For # any option listed below that you DO NOT wish # to use, place double quotes after the variable # in the form: typeset CHROOT="" # ################################################### # Part 1: User configurable section # # Location of the snort binary: #------------------------------ typeset SNORT=/usr/local/bin/snort # (If you chroot Snort, the snort.conf file does not have to be # inside the chroot) If an alternate snort.conf is used specify it here: #----------------------------------------------------------------------- typeset CONFFILE=/var/snort/snort.conf # (If you chroot Snort, the log directory must exist # within the chroot and the LOGDIR must contain the # CHROOT as part of the path) The logging/alerting directory to use: #------------------------------------------------------------------- typeset LOGDIR=/var/snort/log # Alert mode (console, fast, full, cmg, console, none or unsock): #---------------------------------------------------------------- typeset ALERTMODE=fast # To disable packet logging set LOGGING=off : #-------------------------------------------- typeset LOGGING=on # To display the interface name in # text/syslog alerts, set LOG_INTERFACE_NAME=on : #------------------------------------------------ typeset LOG_INTERFACE_NAME=off # To chroot Snort, specify the location of the chroot: #----------------------------------------------------- typeset CHROOT=/var/snort # The name of the user to run Snort as: #-------------------------------------- typeset USER=nobody # The name of the group to run Snort as: #--------------------------------------- typeset GROUP=nobody # Name of sudo or similar su-like program: #----------------------------------------- typeset SUDO="" # if interface is not spcified here it must # be specified on the command line The interface to run Snort on: #---------------------------------------------------------------- typeset INTERFACE=fxp0 # The umask to use: #------------------ typeset UMASK=077 # Define your custom BPF filter file: #------------------------------------ typeset BPF="" # Define your custom logging HOMENET (this is the argument # passed to -h, NOT the HOME_NET variable in snort.conf): #-------------------------------------------------------- typeset HOMENET="" # Email address to send failure notifications to: #------------------------------------------------ typeset ADMIN_EMAIL_ADDR="user@host.com" # Duration to wait (in minutes) between sending repeat failure notifications: #---------------------------------------------------------------------------- typeset -i INTERVAL=60 ################################################### # Part 2: This section contains the core of this script. # Reading through this section should describe how the # script functions. Edit below at your own risk. typeset _pdir= typeset MAILBIN= typeset SNORTPID= for _pdir in $(echo ${PATH} | sed 's/:/\ /g'); do if [[ -x ${_pdir}/mailx ]]; then MAILBIN=${_pdir}/mailx break elif [[ -x ${_pdir}/mail ]]; then MAILBIN=${_pdir}/mail break fi done snortpid () { # In weighing the options of whether to read the pid file(s) created by # Snort or to simply look for the process ID manually, it seems more # specific (and pedantic) to look for the process ID. typeset _iface=${1##*[ \\$/;()|\>\<& ]} if [[ -z ${_iface} ]]; then echo echo "ERROR: Interface not specified. Exiting." echo exit 1 fi SNORTPID=$(ps auxww | egrep "${SNORT}" | egrep ${_iface} | \ egrep -v 'snort.server' | egrep -v egrep | awk '{print $2}') if [[ -z ${SNORTPID} ]]; then return 1 else return 0 fi } start_snort () { if [[ -n ${1} ]]; then typeset _filters="${1}" fi ${SUDO} ${SNORT} ${CHROOT} ${USER} ${GROUP} -i ${INTERFACE} ${CONFFILE} \ ${HOMENET} ${LOGDIR} ${UMASK} ${ALERTMODE} ${LOGGING} \ ${LOG_INTERFACE_NAME} ${BPF} -D ${_filters} snortpid ${INTERFACE} if (($? == 0)); then echo "Starting Snort on interface ${INTERFACE}, pid: ${SNORTPID}." if [[ -f /tmp/snort.stop-${INTERFACE} ]]; then ${SUDO} rm -f /tmp/snort.stop-${INTERFACE} echo echo "Removing snort.stop-${INTERFACE} file." echo fi if [[ -f /tmp/snort.failure-${INTERFACE} ]]; then ${SUDO} rm -f /tmp/snort.failure-${INTERFACE} echo echo "Removing stale snort.failure-${INTERFACE} file." echo fi return else echo echo "Failed to start Snort on interface ${INTERFACE}." echo fi return } stop_snort () { typeset _pid=${1##*[ \\$/;()|\>\<& ]} ${SUDO} kill ${_pid} echo echo "Stopping Snort on interface ${INTERFACE}, pid: ${_pid}." echo touch /tmp/snort.stop-${INTERFACE} return } check_snort () { if [[ -z ${MAILBIN} ]]; then echo echo -n "ERROR: Unable to locate mail or mailx. The 'failcheck' " echo "execution mode" echo " requires either mail or mailx. Exiting." echo exit 1 fi snortpid ${INTERFACE} if (($? == 1)); then if [[ -f /tmp/snort.stop-${INTERFACE} ]]; then return elif [[ ! -f /tmp/snort.failure-${INTERFACE} ]]; then date +%s > /tmp/snort.failure-${INTERFACE} report_failure return fi # store the time in epoch format and record only the last 5 digits typeset -i FAILTIME=$(cat /tmp/snort.failure-${INTERFACE} | cut -c6-10) typeset -i NOW=$(date +%s | cut -c6-10) if ((((NOW - FAILTIME) / 60) % INTERVAL == 0)); then # if the failure occured some multiple of INTERVAL minutes ago # report the failure report_failure fi fi } report_failure () { typeset _failtime=$(date -r $(cat /tmp/snort.failure-${INTERFACE})) ${MAILBIN} -s "Snort failure detected" ${ADMIN_EMAIL_ADDR} << __EOT Detected Snort daemon failure on $(hostname) interface ${INTERFACE} at ${_failtime}. This is an automated message, DO NOT REPLY. __EOT } usage () { cat << __EOT | more Usage: snort.server [options] [BPF filters] execution modes: start (starts Snort and reports PID of new process) stop (reports PID of running Snort process and kills it) restart (restarts Snort if it is running, otherwise starts Snort) status (reports status of Snort process on specified interface) failcheck (send email messages when a Snort process fails) use in conjuction with cron with a crontab entry resembling: 0-59 * * * * snort.server failcheck options: -a -c -C -e -F -g -h (help/usage) -H -i -I -L (disable packet logging) -n (print our receiving interface name in text/syslog alerts) -s -S -u -U __EOT exit 1 } if [[ $# -lt 1 ]]; then usage fi typeset -i _cmdopts=0 typeset _opt= while getopts ":a:c:C:e:g:H:i:I:l:s:S:u:U:hLn" _opt; do case ${_opt} in a ) ALERTMODE=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; c ) CONFFILE=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; C ) CHROOT=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; e ) ADMIN_EMAIL_ADDR="${OPTARG##*[ \\$/;()|\>\<& ]}" ((_cmdopts++)) ;; F ) BPF="${OPTARG##*[ \\$/;()|\>\<& ]}" ((_cmdopts++)) ;; g ) GROUP=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; h ) usage ;; H ) HOMENET="${OPTARG##*[ \\$/;()|\>\<& ]}" ((_cmdopts++)) ;; i ) INTERFACE=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; I ) unset INTERVAL typeset -i INTERVAL=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; l ) LOGDIR=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; L ) LOGGING=off ;; n ) LOG_INTERFACE_NAME=on ;; s ) SNORT=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; S ) SUDO=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; u ) USER=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; U ) UMASK=${OPTARG##*[ \\$/;()|\>\<& ]} ((_cmdopts++)) ;; \? ) echo echo "ERROR: Invalid command line option: -${OPTARG}." echo sleep 3 usage ;; esac done shift $((${OPTIND} - 1)) if [[ -z ${SNORT} ]]; then echo echo "ERROR: location of Snort binary not specified. Exiting." echo exit 1 fi if ((_cmdopts > 0)); then echo echo "WARNING: Command line options override hardcoded values." echo sleep 3 fi if [[ -n ${ALERTMODE} ]]; then case $(echo ${ALERTMODE} | tr "[:upper:]" "[:lower:]") in (cmg|console|fast|full|none|unsock) ALERTMODE="-A ${ALERTMODE}" ;; * ) ALERTMODE="" ;; esac fi if [[ -n ${CHROOT} ]]; then CHROOT="-t ${CHROOT}" fi if [[ -n ${USER} ]]; then USER="-u ${USER}" fi if [[ -n ${GROUP} ]]; then GROUP="-g ${GROUP}" fi if [[ -n ${CONFFILE} ]]; then CONFFILE="-c ${CONFFILE}" fi if [[ -n ${LOGDIR} ]]; then if [[ -n ${CHROOT} ]]; then echo ${LOGDIR} | grep $(echo ${CHROOT} | sed 's/-t //') > /dev/null 2>&1 if (($? != 0)); then LOGDIR="$(echo ${CHROOT} | sed 's/-t //')/$(echo ${LOGDIR} | \ sed 's/^\///')" fi fi LOGDIR="-l ${LOGDIR}" fi if [[ -n ${LOGGING} ]]; then if [[ $(echo ${LOGGING} | tr "[:upper:]" "[:lower:]") == "off" ]]; then LOGGING="-N" else LOGGING="" fi fi if [[ -n ${LOG_INTERFACE_NAME} ]]; then if [[ $(echo ${LOG_INTERFACE_NAME} | tr "[:upper:]" "[:lower:]") == "on" ]]; then LOG_INTERFACE_NAME="-I" else LOG_INTERFACE_NAME="" fi fi if [[ -n ${UMASK} ]]; then UMASK="-m ${UMASK}" fi if [[ -n ${HOMENET} ]]; then HOMENET=" -h ${INTERFACE}" fi if [[ -n ${BPF} ]]; then BPF=" -F ${BPF}" fi if { ((INTERVAL < 1)) || [[ -z ${INTERVAL} ]] }; then unset INTERVAL typeset -i INTERVAL=60 fi if [[ -z ${INTERFACE} ]]; then echo echo "ERROR: Interface not specified. Exiting." echo exit 1 fi typeset _cmd=${1##*[ \\$/;()|\>\<& ]} case ${_cmd} in failcheck ) check_snort ;; restart ) snortpid ${INTERFACE} if (($? == 1)); then echo echo "Snort process not running on interface ${INTERFACE}." echo shift start_snort "$*" else shift stop_snort ${SNORTPID} start_snort "$*" fi ;; start ) snortpid ${INTERFACE} if (($? == 0)); then echo echo "Snort process already running on interface ${INTERFACE}, " echo -n "pid: ${SNORTPID}." echo exit 1 else shift start_snort "$*" fi ;; status ) snortpid ${INTERFACE} if (($? == 0)); then echo echo -n "Snort process running on interface ${INTERFACE}, " echo "pid: ${SNORTPID}." echo else echo echo "Snort process not running on interface ${INTERFACE}." echo exit 1 fi ;; stop ) snortpid ${INTERFACE} if (($? == 1)); then echo echo -n "Snort process not running, use 'snort.server start' " echo "to start Snort." echo exit 1 else stop_snort ${SNORTPID} fi ;; * ) echo echo "ERROR: Invalid execution mode: ${_cmd}." echo sleep 3 usage ;; esac exit 0