2024.11.29 - [서버 구축기] - [Ubuntu] ssh 원격접속 보안 강화
위 글에서 uwf, fail2ban 등 기본적인 보안 설정을 마쳤습니다.
ssh포트로 접속하는 요청이 해외에서 들어올 때 이를 차단을 하면 보안이 더 향상될 것이라고 생각을 하였습니다.
1차적으로 해외 아이피를 차단하고, 2차적으로 fail2ban을 통해서 한국 아이피로 지속적인 접속 요청을 차단하는 방식을 생각하였습니다.
기본 설치 및 세팅
설치
geoip-bin과 geoip-database를 설치한다.
sudo apt-get install geoip-bin geoip-database
주기적인 데이터베이스 업데이트를 위해서 geoipupdate도 설치한다.
sudo apt-get install geoipupdate
정상적으로 설치되었는지 확인하기 위해서 아래의 명령어를 입력
geoiplookup 8.8.8.8
출력 결과가 GeoIP Country Edition: US, United States 가 나온다면 정상적인 것임.
MaxMind 회원 가입 및 데이터베이스 자동 업데이트 설정
이후 MaxMind에서 간단한 회원가입을 진행하여야 한다.
https://www.maxmind.com/en/home
회원 가입을 마쳤다면 라이센스 키를 생성한다.
한번 생성한 키는 다시 확인할 수 없으니 Account ID와 License key 둘 다 어딘가에 기록해두어야 한다.
이후 아래의 명령어를 통해서 설정 파일을 수정해야한다.
sudo nano /etc/GeoIP.conf
Account ID와 License Key를 입력한다.
AccountID 발급받은 아이디 코드
LicenseKey 발급받은 라이센스키
# 아래 부분은 수정 X
EditionIDs GeoLite2-City GeoLite2-Country
설정 파일을 저장한 후 데이터베이스를 업데이트 한다.
sudo geoipupdate
데이터베이스를 주기적으로 자동 업데이트하려면 geoipupdate.timer를 활성화한다.
sudo systemctl enable geoipupdate.timer
자동 업데이트가 활성화 되었는지 확인하려면 아래의 명령어를 입력
systemctl is-enabled geoipupdate.timer
enable이 출력된다면 타이머가 정상적으로 활성화 된 것이다.
쉘 스크립트 작성
스크립트 작성
해외 아이피를 자동으로 차단하려면 아래의 스크립트를 작성해야 한다.
sudo nano /usr/local/bin/sshfilter.sh
아래의 내용을 입력한다.
#!/bin/bash
ALLOW_COUNTRIES="KR"
LOG_FILE="/var/log/sshfilter.log"
# 디버깅 로그 기록
exec > >(tee -a $LOG_FILE) 2>&1
if [ $# -ne 1 ]; then
echo "Usage: $(basename $0) <ip>"
exit 1
fi
IP=$1
# IPv6 주소를 IPv4 주소로 변환
if [[ $IP =~ ^::ffff: ]]; then
IP=$(echo $IP | sed 's/^::ffff://')
fi
# GeoIP 데이터로 국가 정보 조회
COUNTRY=$(/usr/bin/geoiplookup $IP | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1)
# GeoIP 데이터가 없는 경우 처리
if [[ "$COUNTRY" = "IP Address not found" ]]; then
echo "$(date) - IP $IP is not found in GeoIP database. Allowing access."
exit 0
fi
# 허용된 국가 여부 확인
if [[ "$ALLOW_COUNTRIES" =~ "$COUNTRY" ]]; then
echo "$(date) - IP $IP is from allowed country ($COUNTRY). Access granted."
exit 0
else
# 이미 차단된 IP인지 확인
if sudo iptables -C INPUT -s $IP -j DROP 2>/dev/null; then
exit 0
fi
# 허용되지 않은 국가일 경우 차단
echo "$(date) - IP $IP is from denied country ($COUNTRY). Blocking access."
if sudo iptables -I INPUT -s $IP -j DROP; then
exit 0 # 차단 성공 시 정상 종료
else
echo "$(date) - Failed to block IP $IP. Please check iptables configuration."
exit 1 # 차단 실패 시 오류 종료
fi
fi
그리고 파일의 권한과 실행 권한을 변경해준다.
sudo chown root:root /usr/local/bin/sshfilter.sh
sudo chmod 775 /usr/local/bin/sshfilter.sh
정상적으로 작동되는지 확인하려면 아래의 단계를 따른다.
1. 8.8.8.8(구글 아이피)을 통해서 sshfilter 스크립트 처리
/usr/local/bin/sshfilter.sh 8.8.8.8
2. 로그에 8.8.8.8에 대한 아이피 처리 내용 확인
sudo grep "8.8.8.8 is from denied country" /var/log/sshfilter.log
이 명령어를 실행해서 출력 결과가 있다면 성공적으로 반영된 것이다.
SSH 필터링에 sshfilter 추가
기본 거부 정책
기본 거부 정책 파일을 수정한다.
sudo nano /etc/hosts.deny
아래의 내용을 추가하여 /etc/hosts.allow에서 명시적으로 허용되지 않은 모든 클라이언트를 차단
sshd: ALL
명시적 허용 항목 수정
명시적으로 허용할 항목을 아래의 명령어로 수정한다.
sudo nano /etc/hosts.allow
아래의 내용을 추가한다.
sshd: ALL: aclexec /usr/local/bin/sshfilter.sh %a
- aclexec: 접근 요청 시 지정된 스크립트를 실행한다.
- /usr/local/bin/sshfilter.sh: 실행할 스크립트 파일의 경로.
- %a: 접근하려는 클라이언트의 IP 주소를 스크립트에 인자로 전달.
설명
클라이언트가 SSH로 서버에 접속을 시도하면 다음 순서로 검토한다.
- /etc/hosts.allow에서 허용 조건을 확인
- /usr/local/bin/sshfilter.sh 스크립트가 실행된다.
- 이 스크립트는 클라이언트 IP를 기준으로 허용 또는 차단 여부를 결정한다.
- 허용할 경우 스크립트는 성공(exit code 0)으로 종료하여 접근을 허용.
- 거부할 경우 스크립트는 실패(exit code 1)로 종료하여 접근을 차단. - /etc/hosts.deny에서 거부 조건을 확인
- /etc/hosts.allow에서 특별한 설정이 없다면 기본적으로 모든 접근이 거부.
차단 목록 확인
sshfilter 스크립트 로그 조회
sudo cat /var/log/sshfilter.log
iptables 에 저장된 차단 목록
sudo iptables -L INPUT -v -n | awk '/DROP/ && $8 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {print $8}'
차단 IP 목록 저장(선택)
지금까지 내용을 모두 수행하였다면, iptables에 해외에서 접근한 ip는 차단된 상태이다.
하지만 iptables는 디스크에 저장하는 것이 아닌 램에 저장한다.
따라서 재부팅이 된다면 sshfilter 스크립트로 차단된 ip 목록이 사라진다.
재부팅 후에도 해당 스크립트로 차단한 ip를 유지하고 싶다면 iptables-persistent 를 이용해야 한다.
iptables-persistent 설치
sudo apt-get install iptables-persistent
이 명령어를 실행하면 화면이 바뀌면서 두 개의 물음이 나타난다.
- Save current IPv4 rules? : 현재 시스템에 설정된 IPv4 규칙을 저장할지 묻는다.
- Save current IPv6 rules? : 현재 시스템에 설정된 IPv6 규칙을 저장할지 묻는다.
디스크에 차단 ip 저장
sudo netfilter-persistent save
위 명령어를 입력해야 부팅 후 차단 목록이 디스크에 저장된다.
저장하지 않고 우분투를 종료한다면 차단 목록이 디스크에 저장되지 않는다.
저장을 한다면, 차단 목록과 규칙은 아래의 위치에 저장된다.
- /etc/iptables/rules.v4
- /etc/iptables/rules.v6
개인적인 의견으로는 해외에서 하루에 수십, 수백건씩 공격이 들어오는데 이를 모두 저장할 필요는 없는 것 같다.
지금까지의 내용 정리
sshfilter의 역할
- sshfilter는 GeoIP 데이터베이스를 기반으로 해외 IP를 차단하는 스크립트이다.
- 이 스크립트는 즉각적으로 해외 IP를 차단하고, 추가적으로 iptables 규칙을 사용해 해당 IP의 접근을 막는다.
- fail2ban에서 밴을 하기 이전에 해외에서 ssh 포트로 접근하는 1차적 방어막 역할을 한다.
Fail2Ban과의 조합
- sshfilter로 해외 IP를 차단한 후, Fail2Ban은 반복적으로 접속 시도하는 한국 IP를 차단.
- 국내에서 ssh 포트로 접근하는 2차적 방어막 역할을 한다.
기타
삽질하다 알게된 것인데, geoipupdate.timer 를 통한 GeoIP 데이터베이스 업데이트가 아니라 스크립트를 통한 업데이트를 적용하려면 아래와 같이 스크립트를 작성할 수 있다.
sudo nano /etc/cron.monthly/updatingGeoIP
아래의 내용을 입력
#!/bin/bash
# 작업 디렉토리 설정
WORK_DIR="/tmp/geoip_update"
DB_PATH="/usr/share/GeoIP"
LICENSE_KEY="라이센스키" # 발급받은 라이선스 키를 입력하세요.
GEOIP_URL="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${LICENSE_KEY}&suffix=tar.gz"
LOGFILE="/var/log/updatingGeoIP.log"
# 로그 시작
echo "$(date): Starting GeoIP database update" >> $LOGFILE
# 작업 디렉토리 생성
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"
# 데이터베이스 다운로드
wget -q "$GEOIP_URL" -O GeoLite2-Country.tar.gz
if [ $? -ne 0 ]; then
# 다운로드 실패
echo "$(date): Failed to download GeoIP database. Keeping the existing database." >> $LOGFILE
rm -rf "$WORK_DIR"
exit 1
fi
# 압축 해제
tar -xzf GeoLite2-Country.tar.gz
if [ $? -ne 0 ]; then
# 압축 해제 실패
echo "$(date): Failed to extract GeoIP database. Keeping the existing database." >> $LOGFILE
rm -rf "$WORK_DIR"
exit 1
fi
# GeoLite2-Country.mmdb 파일 확인
MMDB_FILE=$(find . -name "*.mmdb")
if [ -z "$MMDB_FILE" ]; then
# mmdb 파일을 찾을 수 없음
echo "$(date): GeoIP database file not found in archive. Keeping the existing database." >> $LOGFILE
rm -rf "$WORK_DIR"
exit 1
fi
# 기존 데이터베이스 삭제 및 업데이트
rm -f "${DB_PATH}/GeoLite2-Country.mmdb"
mv -f "$MMDB_FILE" "${DB_PATH}/GeoLite2-Country.mmdb"
echo "$(date): GeoIP database updated successfully" >> $LOGFILE
# 작업 디렉토리 정리
rm -rf "$WORK_DIR"
스크립트 실행권한 추가
sudo chmod +x /etc/cron.monthly/updatingGeoIP
geoipupdate.timer를 이용한다면 이 스크립트를 굳이 작성할 필요는 없다.
참고 자료
'서버 구축기' 카테고리의 다른 글
[GitHub Actions] Self-Hosted Runner (0) | 2025.01.09 |
---|---|
[Nginx] Nginx 기본 보안 설정 (1) | 2024.12.28 |
[MySQL] MySQL 보안 설정 (1) | 2024.12.27 |
[Ubuntu] 외장 SSD ext4 초기화 + 마운트 (0) | 2024.12.26 |
[Ubuntu] ssh 원격접속 보안 강화 (0) | 2024.11.29 |