이 글에서는 SSH 접속에 관련된 보안 설정을 다뤄보려고 한다. 대단한것들은 아닐뿐더러 이미 알고 있는 내용도 많을것이다.

들어가기전에 말을 하나 하자면, 서비스(프로그램)을 돌린다면 반드시 root가 아닌, 별도의 서비스 계정을 만들어서 돌리라는 것이다. 아래와 같이 설정을 해봤자, root 권한을 가진 서비스의 취약점에 의해 공격이 뚫리게 되면 아무런 의미도 없어진다. 서비스(특히 외부에 노출되는)를 돌릴때는 반드시 root 미만의 계정에서 실행하자.

가장 기본적인 설정들

sshd의 설정은 /etc/ssh/sshd_config 에서 할 수 있다. 아래의 설정들도 해당 파일에 존재한다.

Port 22388
ListenAddress 172.30.1.1
PermitRootLogin no
AllowUsers blog-esukmean

첫번째 줄은 sshd의 포트를 바꾸는 것이다. 두번째 줄은 sshd를 오픈할 주소를 설정하는 것이다. 세번째 줄은 sshd로 root 계정으로는 로그인 하지 못하도록 한 것이며, 마지막 줄은 blog-esukmean 계정으로만 접속할 수 있게 한 것이다.

일단 포트를 22에서 다른 번호로 바꾸는것만으로도 자그마한 공격들을 막을 수 있다. 22번 포트를 향해 Brute-force가 엄청나게 들어온다. Brute-force가 아니더라도 3분요리 수준의 먹잇감이 될 수 있다. sshd는 최초 접속시에 자신의 버전 정보를 상대에게 보낸다. 이것을 이용하여 해당 서버의 정보를 캐낼수 있다. 더 나아가 sshd버전의 취약점을 찾아오거나 시도할 수도 있다. 그런 상황에서 기본 포트인 22를 사용하는것은 상대보고 덤비라고 하는 꼴이다.

CentOS에서의 SSHd 접속정보
Ubuntu에서의 SSHd 접속정보

기기에 IP가 여러개 있다면 특정 IP에서만 수신 대기 하는것도 상당한 효과가 있다. 서비스용(Public exposure) IP주소와 관리용 IP주소를 분리한다면 공격자가 쉽게 공격하지 못할 것이다. 관리용 IP를 또 찾아내야 하기 때문이다. 외부에 알려져있는 IP에 전체 포트로 스캔을 해봤자 알려져있는 포트만 보일 것이다. 별도의 내부망(Private IP)이 있는 경우, 내부망을 통해서만 접속할 수 있게 하여 외부에서의 접속을 원천적으로 차단할 수도 있다

서버 로그를 찍어보면 대부분의 Brute-force 공격은 root 계정에 대해 발생한다. Brute-force를 제쳐놓고, 애초에 보안상 root로 직접 접근하게 허용하는것 자체가 좋지않다. 나는 필요할 때만 sudo로 권한 상승하여 작업을 하는게 좋다고 생각한다. 어쨋든 보안상, 그리고 나의 철학(?)상 Root로그인은 못하도록 하였다. 그에 더 나아가서 (마지막 줄을 통해) 특정 유저만 sshd 로그인을 할 수 있게 하였다.

접속 가능한 IP 설정하기

대부분의 시스템에 탑제되어 있는 sshd는 libwarp을 지원한다. libwrap은 IP(Hostname)기반의 TCP 접근제어 라이브러리이다. 아래와 같이 /etc/hosts.allow 에 넣는것만으로 IP 접근제어가 가능하다.

sshd: 192.168.0.37
sshd: ALL: DENY

특정 장소·위치·IP에서만 접속을 한다면 IP 화이트리스트가 매우 유용하다. 당장에 지정된 IP외에서는 접속 자체가 안되기 때문이다. IP대신에 hostname을 넣을 수도 있다. PTR레코드를 이용하여 리버스 도메인을 구성하면 hostname을 사용할 수 있다. hostname을 이용하면 IP 주소가 회수당해도 빠른 시간내에 재접속 할 여지가 생긴다.

한편, 이 부분은 libwrap에 의해 제공되는 부분이다. 패키지 관리자로 (apt든 yum이든) 설치되는 대부분의 sshd에서는 같이 딸려온다. 만약 IP 접근제어가 작동하지 않는것 같다면 아래의 명령어로 지원여부를 확인해 보자. libwrap.so.0 부분이 아무것도 표시되지 않는다면 지원되지 않을 가능성이 높다. (static linking의 경우도 있기에 확정 선고는 아니다.)

$ ldd /usr/sbin/sshd | grep libwrap
   libwrap.so.0 => /lib/x86_64-linux-gnu/libwrap.so.0 (0x00007efe7e958000)

PAM을 이용한 추가 보안

PAM은 리눅스의 사용자 인증에 관련된 작업에 쓰이는 플러그인들이다. PAM 자체가 Pluggable Authentication Module의 약자로 “땟따 붙였다 할 수 있는 인증 모듈”이란 뜻이다. PAM을 이용하면 사용자가 인증을 시도할 때 Google OTP등의 수단으로 추가인증 요구, 또는 인증 거절할 수 있다. 심지어는 접속 성공시 별도의 스크립트를 실행하여 이메일이나 텔레그램등으로 알림을 보낼 수도 있다.

PAM은 말 그대로 모듈이기에 .so 파일로 존재한다. 이들 라이브러리는 주로 /lib/security, /lib64/security 또는 lib/*/security/등에 존재한다. 위의 디렉토리에 pam_어쩌고.so가 보이지 않는다면 ls /lib/security/*/pam_*.so 로 조회해 보자. 아래와 같이 pam 라이브러리가 보일것이다.

$ ls /lib/*/security/pam_*.so 의 결과

혹시 그래도 보이지 않는다면 아래의 폴더에 들어가 보자:

/lib/security
/lib64/security
/lib/i386-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/i386-linux-gnu
/usr/lib/x86_64-linux-gnu

인증시 이메일 전송

여기서는 PAM을 이용한 가장 간단한 예로 인증시 이메일로 로그인 알림을 붙여보고자 한다. 위의 많고많은 PAM 라이브러리중 pam_exec를 이용한 예다. pam_exec 모듈은 지정된 상황에서 특정 스크립트를 실행한다. 이것을 응용해 로그인 성공시 이메일 전송 스크립트를 실행 하도록 구성할 것이다. 만약에 pam 모듈 목록에서 pam_exec.so가 보이지 않는다면 별도로 컴파일 또는 설치 해야한다.

우선 /etc/pam.d/sshd를 열어서 아래와 같은 줄을 맨 마지막에 집어넣는다:

session optional pam_exec.so /usr/local/bin/send-mail-on-ssh-login.sh

여기서 맨 앞의 session 부분은 어떤 작업에서 이 플러그인을 실행할지 지정하는것이다. 여기서는 session이 열렸을때, 즉 로그인에 성공했을때 이 모듈이 실행되도록 했다. 다음의 optional은 “작업 결과가 실패해도 상관없다”를 뜻한다. optional 플레그를 지정했기에, 스크립트가 알 수 없는 이유로 실행에 실패해도 다음 단계로 넘어간다. optional 대신에 required 등을 입력하면 이메일 전송에 성공해야만 세션이 열리도록 설정할 수도 있다. 마지막 /usr/local/bin/send-mail-on-ssh-login.sh는 실행할 스크립트를 의미한다. 이것을 수정하여 다른 특정 스크립트를 실행 할 수도 있다.

이제는 /usr/local/bin/send-mail-on-ssh-login.sh를 만들어서 아래의 내용을 집어넣는다. 잊지말고 실행권한도 잘 챙겨주자.

#!/bin/sh
if [ "$PAM_TYPE" != "open_session" ]
then
  exit 0
else
  {
    echo "User: $PAM_USER"
    echo "Remote Host: $PAM_RHOST"
    echo "Service: $PAM_SERVICE"
    echo "TTY: $PAM_TTY"
    echo "Date: `date`"
    echo "Server: `uname -a`"
  } | mail -s "$PAM_SERVICE login on `hostname -s` for account $PAM_USER" root
fi
exit 0

이러한 구조로 script를 수정하면 다른 작업을 수행할 수도 있다. (예: 텔레그램 알람보내기 or SMS알람전송)

정리

이 글에서는 SSH 보안을 향상하기 위한 잘 알려진 몇가지 방법들을 소개했다. 맨 처음에도 언급했지만, 시스템이 root권한으로 탈취된 후에는 아무런 의미가 없다. 그렇기에 SSH에만 집중할 것이 아니라 서버 전반의 보안을 올리는것이 중요하다. 외부에 공개되어 있는 서비스들에게는 귀찮더라도 절대 root 권한으로 실행하지 말자.