가끔 리눅스 서버에서 특정 IP 주소를 차단해야 할 일이 생기는데, 보통 iptables를 활용하겠지만 나는 라우팅 테이블을 활용하는 편이다.


며칠 전에 AWS를 좀 보려고 Amazon Linux 2 AMI로 EC2 인스턴스를 생성했다. 그런데 집에서 만들었던 서버를 회사에서 접속하려니 Private Key 파일이 집에 있어서 접속을 할 수 없었다. 그래서 보안상 좀 취약하더라도 패스워드 로그인 방식을 사용하도록 sshd_config 파일을 수정하기로 하였다.



그런데 며칠 뒤 확인해보니 중국 IP 등에서 ssh 접속 시도를 엄청 많이 하고 있는게 아닌가...



그래서 ip 명령어로 해당 IP들을 차단하기로 마음먹었다. 명령어 형식은 아래와 같다.

(x.x.x.x 자리에는 CIDR도 먹는다 예를 들어 112.85.42.0/24)


ip route add blackhole x.x.x.x


이걸 자동화 하기 위해 파이썬 스크립트를 만들어 보았다. 뭐 그냥 BASH로 해도 되는데 dictionary랑 산술연산 하기에 python이 편하니까...


#!/usr/bin/env python

import subprocess
from datetime import datetime

SECURE_LOG = '/var/log/secure'
IP_CMD = '/usr/sbin/ip'
BLOCK_COUNT = 30


def block_ip(ip):
cmd = '%s route add blackhole %s' % (IP_CMD, ip,)
subprocess.call(cmd, shell=True)

def get_blocked_ip():
cmd = "%s route | awk '/blackhole/ {print $NF}'" % (IP_CMD,)
res = subprocess.check_output(cmd, shell=True).strip()
res = res.split('\n')
res = set(res)
return res

def get_ip_count():
ret = {}
with open(SECURE_LOG) as f:
for line in f:
if 'Failed password' in line:
ip = line.split()[-4]
ret[ip] = ret.get(ip, 0) + 1
return ret


blocked_ip = get_blocked_ip()
ip_count = get_ip_count()
for ip, count in ip_count.items():
if count > BLOCK_COUNT and ip not in blocked_ip:
print '%s block %s because ssh password failed %d times' % (
datetime.now().strftime('%F %T.%f'), ip, count,)
block_ip(ip)


위 파이썬 스크립트 내용은 /var/log/secure에서 'Failed password' 찍힌 IP의 숫자를 세어 30번 넘으면 차단하는 것이다. (발코딩 양해바람) crontab에 걸어두고 매 시간 실행하게 해두었다.


그나저나 차단하기 전 며칠동안 4만번 이상 접속 시도한 놈들은 뭐하는 녀석들인지...



라우팅 테이블에서 해당 라인을 지우려면 아래와 같이 입력하면 된다.


ip route delete x.x.x.x


아래는 blackhole 라우팅 한번에 다 지우기


#!/bin/bash

ip route | awk '/blackhole/ {print $NF}' | while read line; do
    ip route delete $line
done