forcemax's

/etc/init.d/iptables를 다음과 같이 만든다. (permission은 775)

#!/bin/sh -e
### BEGIN INIT INFO
# Provides:          iptables
# Required-Start:    networking ifupdown $local_fs
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO

IPTABLES=/sbin/iptables
EXT_ETH=eth0

${IPTABLES} -F
${IPTABLES} -t nat -F
${IPTABLES} -X BLACKLISTCHECK

${IPTABLES} -P INPUT ACCEPT
${IPTABLES} -P OUTPUT ACCEPT
${IPTABLES} -P FORWARD ACCEPT

${IPTABLES} -A INPUT -j ACCEPT -i lo
${IPTABLES} -A INPUT -j ACCEPT -p icmp --icmp-type any
${IPTABLES} -A INPUT -j ACCEPT -m state --state ESTABLISHED,RELATED

${IPTABLES} -N BLACKLISTCHECK
${IPTABLES} -A INPUT -j BLACKLISTCHECK -i ${EXT_ETH} -m state --state NEW -p tcp --dport 22

for x in `cat /tmp/ssh_evil`; do
${IPTABLES} -I BLACKLISTCHECK -s $x -j DROP
done

# 80, 22번 포트만 오픈
${IPTABLES} -A INPUT -j ACCEPT -i ${EXT_ETH} -m state --state NEW -p tcp --dport 22
${IPTABLES} -A INPUT -j ACCEPT -i ${EXT_ETH} -m state --state NEW -p tcp --dport 80

${IPTABLES} -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
${IPTABLES} -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 6 -j LOG --log-prefix "SSH_brute_force "

${IPTABLES} -A INPUT -j REJECT --reject-with icmp-host-prohibited

/etc/init.d/swatch 파일이 없다면 다음의 내용으로 생성한다. (permission은 775)
#!/bin/sh -e
### BEGIN INIT INFO
# Provides:          swatch
# Required-Start:    networking ifupdown $local_fs
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO

PATH="/sbin:/bin:/usr/sbin:/usr/bin"
NAME="swatch"
DAEMON="/usr/bin/swatch"
DESC="simple watcher"
CONFFILE="/etc/swatch.conf"
#LOGFILE="/var/log/secure"
LOGFILE="/var/log/syslog"
PIDFILE="/var/run/swatch.pid"
OPTS="--config-file=$CONFFILE --tail-file=$LOGFILE --pid-file=$PIDFILE --awk-field-syntax --tail-args='-F' --daemon"

test -x $DAEMON || exit 0

start() {
    if [ ! -f "$CONFFILE" ]; then
        echo "Error: $CONFFILE does not exist."
        exit 1
    fi
    if [ ! -f "$LOGFILE" ]; then
        echo "Error: $LOGFILE does not exist."
        exit 1
    fi
    if [ -f "$PIDFILE" ]
    then
        echo "Error: $NAME is already running."
    else
        $DAEMON $OPTS >/dev/null 2>&1
    fi
}

stop() {
    if [ -f "$PIDFILE" ]
    then
        PID=`cat $PIDFILE`
        if ps h $PID > /dev/null
        then
            pkill -P $PID
            kill $PID
        else
            echo "Error: $NAME is not running, but PID file exists. Deleting it."
        fi
        rm -f $PIDFILE
    else
        echo "Error: $NAME is not running."
    fi
}

case "$1" in
  start)
    echo "Starting $DESC: $NAME"
    start
    ;;
  stop)
    echo "Stopping $DESC: $NAME"
    stop
    ;;
  restart)
    echo "Restarting $DESC: $NAME"
    stop
    sleep 1
    start
    ;;
  *)
    echo "Usage: $0 start|stop|restart"
    exit 1
    ;;
esac

exit 0

swatch.conf에는 다음을 추가한다.
perlcode my $sshscript = '/root/swatch-ssh';
watchfor /([a-zA-Z]{3})\s+([0-9]{1,2}) ([0-9]{2}:[0-9]{2}:[0-9]{2}) ([a-zA-Z0-9_-]+) kernel: \[.*\] SSH_brute_force .*SRC=(.*) DST=/
        exec "$sshscript '$*' >> /var/log/swatch.log"
        throttle 00:00:30

swatch에서 실행할 스크립트를 만든다.
/root/swatch-ssh 파일의 내용은 다음처럼 구성한다. (역시 permission은 775)
(다른 기능도 같이 하는 스크립트에서 일부만 발췌함)
#!/usr/bin/ruby1.8

if ARGV.length==0 && ARGV.length < 2
  puts "Error!! Argument must more than 1 words!!"
  puts "ex) swatch-ssh message"
  exit 1
else
  begin
    message = ""
    argv_msg = ARGV[0].split
    host = argv_msg[3]

    beginflag = false
    for qs in argv_msg do
      message = message + (message!=""?" ":"") + qs if beginflag == true
      beginflag = true if qs == host
    end

    # for 3ware modules message
    message.sub!(/kernel: \[[0-9\s]+\.[0-9]+\] /, '')
    message.sub!(/\(.*\)/, '')

    smsmessage = sprintf("[%s]%s\n", host, message)
#    smsmessage.gsub!(' ', '_')
    smsmessage.sub!(/\[mgm\]/, '')
    smsmessage.sub!(/SSH_brute_force .* SRC=/, '')
    smsmessage.sub!(/ DST=.*/, '')

    File.open("/tmp/ssh_deny", 'a') {|f| f.write(smsmessage) }
    command = sprintf("cat /tmp/ssh_evil /tmp/ssh_deny | sort -u > /tmp/ssh_uniq_evil ; rm /tmp/ssh_deny ; cp /tmp/ssh_uniq_evil /tmp/ssh_evil ; /etc/init.d/iptables")
#    printf("%s\n", command)
    system(command)
  end
end

exit 0

이제 모든 준비는 완료되었습니다.
iptables 스크립트를 실행시키고 swatch를 restart하면 끝.
iptables -N BLACKLISTCHECK
touch /tmp/ssh_evil
update-rc.d iptables defaults 20
update-rc.d swatch defaults 20
/etc/init.d/iptables
/etc/init.d/swatch restart

위에서 설정한 내용은 다음과 같습니다.

1. iptables를 설정하여 ssh 연결이 1분에 6번 이상 연결되면 kernel에 log를 남기게 됩니다.
2. swatch에서는 iptables가 남긴 kernel log를 발견하게 되면 /root/swatch-ssh 스크립트를 실행시킵니다.
3. /root/swatch-ssh 스크립트에서는 해당 IP Address를 /tmp/ssh_evil 파일에 남기는 동시에 /etc/init.d/iptables 스크립트를 실행시켜서 발견된 IP Addreess를 통해서 들어오는 연결을 차단합니다.

위의 1,2,3 과정을 반복하면서 ssh brute force attack이 발견되면 해당 IP Address를 지속적으로 차단합니다.

작성자 : 저입니다. ^^