iptables와 swatch로 ssh brute force attack 막기
Linux2010. 5. 19. 23:27
/etc/init.d/iptables를 다음과 같이 만든다. (permission은 775)
/etc/init.d/swatch 파일이 없다면 다음의 내용으로 생성한다. (permission은 775)
swatch.conf에는 다음을 추가한다.
swatch에서 실행할 스크립트를 만든다.
/root/swatch-ssh 파일의 내용은 다음처럼 구성한다. (역시 permission은 775)
(다른 기능도 같이 하는 스크립트에서 일부만 발췌함)
이제 모든 준비는 완료되었습니다.
iptables 스크립트를 실행시키고 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를 지속적으로 차단합니다.
작성자 : 저입니다. ^^
#!/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
### 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
### 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
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
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
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를 지속적으로 차단합니다.
작성자 : 저입니다. ^^