SSH Login Email Alerts in Linux Server
SSH 로그인 한 경우 shell script 따위를 실행하도록 하는 방법은 다음과 같다.
- pam_exec.so
- .profile 을 이용하는 방법
다음과 같은 명령어로 이메일을 보낼 수 있다.
- curl : 최신 버전에서만 지원한다.
- sendmail, exim 같은 MTA 나 mailx 유틸리티
이메일 보내는 방법에 대해서는 필자가 작성한 다른 글을 참조한다.
1. pam_exec.so 를 이용하는 방법
1.1. pam_exec.so 사용법
/etc/pam.d/sshd 에 다음을 추가하면, 로그인 혹은 로그아웃 할 때, /usr/local/bin/pam_exec_email.sh 파일이 실행된다.
session optional pam_exec.so seteuid debug log=/var/log/pam_exec_email.log /usr/local/bin/pam_exec_email.sh
/var/log/pam_exec_email.log 파일에는 /usr/local/bin/pam_exec_email.sh 에서 echo 로 남긴 것들이 쌓인다.
log 를 남기지 않고, 다음과 같이 간단하게 할 수도 있다.
session optional pam_exec.so /usr/local/bin/pam_exec_email.sh
1.2. pam_exec.so 가 내보내는 환경변수
/etc/pam.d/sshd 에 추가한 경우 다음과 같은 환경변수를 내보낸다.
- PAM_RHOST : 로그인한 IP
- PAM_USER : 로그인한 사용자 ID
- PAM_RUSER : sshd 에서 이 값은 없다.
- PAM_SERVICE : sshd
- PAM_TTY : ssh
- PAM_TYPE : open_session 혹은 close_session
1.3. /usr/local/bin/pam_exec_email.sh
/usr/local/bin/pam_exec_email.sh 파일을 만든다.
1.3.1. sendmail 혹은 exim 이용
다음의 예제는 sendmail 을 기준으로 작성되었다. exim 의 경우 sendmail 만 exim으로 변경하면 된다.
#!/bin/bash SENDER="보내는 사람 이메일 주소" RECIPIENT="받는 사람 이메일 주소" remote=${PAM_RHOST} user=${PAM_USER} host="$(hostname)" subject="" if [ ${PAM_TYPE} = "open_session" ]; then subject="SSH Login: $user from $remote on $host" else subject="SSH Logout: $user from $remote on $host" fi message=" Date : $(date +'%Y-%m-%d %H:%M:%S') SHLVL : $SHLVL TTY : ${PAM_TTY} Service : ${PAM_SERVICE} PAM_TYPE : ${PAM_TYPE} User : ${PAM_USER} PAM_RUSER : ${PAM_RUSER} Remote Host : ${PAM_RHOST} " sendmail -f ${SENDER} ${RECIPIENT} <<EOF From: <${SENDER}> To: <${RECIPIENT}> Subject: ${subject} ${message} EOF unset SENDER unset RECIPIENT unset remote unset user unset host unset subject unset message
로그인 / 로그아웃 한 경우 이메일을 보낸다.
로그인 한 경우에만 이메일을 보내고 싶다면, 다음과 같은 구문으로 전체를 감싸면 된다.
if [ ${PAM_TYPE} = "open_session" ]; then fi
1.3.2. curl 이용
#!/bin/bash SENDER="보내는 사람 이메일 주소" RECIPIENT="받는 사람 이메일 주소" remote=${PAM_RHOST} user=${PAM_USER} host="$(hostname)" subject="" if [ ${PAM_TYPE} = "open_session" ]; then subject="SSH Login: $user from $remote on $host" else subject="SSH Logout: $user from $remote on $host" fi message=" Date : $(date +'%Y-%m-%d %H:%M:%S') SHLVL : $SHLVL TTY : ${PAM_TTY} Service : ${PAM_SERVICE} PAM_TYPE : ${PAM_TYPE} User : ${PAM_USER} PAM_RUSER : ${PAM_RUSER} Remote Host : ${PAM_RHOST} " HOST=${RECIPIENT##*@} MXFOUND=N MXHOST="" Result=$( dig ${HOST} mx +short 2> /dev/null | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi if [ "$MXHOST" == "" ];then Result=$( host -t mx ${HOST} 2> /dev/null | grep mail | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi fi if [ "$MXHOST" == "" ];then Result=$( nslookup -type=mx ${HOST} 2> /dev/null | grep "mail exchanger" | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi fi if [[ "$MXHOST" == "" && "$MXFOUND" == "Y" ]]; then MXHOST=${HOST} fi if [ "$MXHOST" == "" ];then echo "dig|host|nslookup command not exists!" return 100 else curl -s --ssl \ --url "smtp://${MXHOST}" \ --mail-from ${SENDER} \ --mail-rcpt ${RECIPIENT} \ --upload-file - << EOF From: <${SENDER}> To: <${RECIPIENT}> Subject: ${subject} ${message} EOF fi unset SENDER unset RECIPIENT unset remote unset user unset host unset subject unset message unset HOST unset MXFOUND unset MXHOST unset Result unset RETVAL
로그인 / 로그아웃 한 경우 이메일을 보낸다.
로그인 한 경우에만 이메일을 보내고 싶다면, 다음과 같은 구문으로 전체를 감싸면 된다.
if [ ${PAM_TYPE} = "open_session" ]; then fi
1.4. 문제해결
로그는 /var/log/secure 혹은 /var/log/auth.log 에서 확인할 수 있다.
1.4.1. /usr/local/bin/pam_exec_email.sh failed: exit code 2
상세 로그는 다음과 같다.
pam_exec(sshd:session): Calling /usr/local/bin/pam_exec_email.sh ... pam_exec(sshd:session): execve(/usr/local/bin/pam_exec_email.sh,...) failed: No such file or directory pam_exec(sshd:session): /usr/local/bin/pam_exec_email.sh failed: exit code 2
pam_exec_email.sh 파일의 줄바꿈 문자가 DOS/Windows (\r\n) 이다.
Unix (\n) 으로 변경한다.
1.4.2. /usr/local/bin/pam_exec_email.sh failed: exit code 8
pam_exec(sshd:session): Calling /usr/local/bin/pam_exec_email.sh ... pam_exec(sshd:session): execve(/usr/local/bin/pam_exec_email.sh,...) failed: Exec format error pam_exec(sshd:session): /usr/local/bin/pam_exec_email.sh failed: exit code 8
pam_exec_email.sh 에서 #!/bin/bash
이 생략되었다.
#!/bin/bash
를 추가한다.
1.4.3. /etc/pam.scripts/pam_exec_email.sh failed: exit code 13
pam_exec(sshd:session): Calling /etc/pam.scripts/pam_exec_email.sh ... pam_exec(sshd:session): execve(/etc/pam.scripts/pam_exec_email.sh,...) failed: Permission denied pam_exec(sshd:session): /etc/pam.scripts/pam_exec_email.sh failed: exit code 13
/etc/pam.scripts/pam_exec_email.sh 파일을 실행할 수 없다. selinux 에서만 발생한다.
파일의 위치를 /usr/local/bin/ 같은 곳으로 변경한다.
1.4.4. CentOS 5.x 에서 버그
CentOS 5.x 에서는 PAM_TYPE 따위의 환경변수를 내보내지 않는 버그가 있다.
CentOS 5.11 updates 에서 다운로드 받은 pam 을 설치한다.
2. .profile 을 이용하는 방법
.bash_login
을 만들고 나서,
.bash_profile
혹은 .profile
파일의 끝에 다음의 내용을 추가한다.
if [ -f ~/.bash_login ]; then . ~/.bash_login fi
2.1. .bash_login
2.1.1. sendmail 혹은 exim 이용
#!/bin/bash if [ -n "$SSH_CONNECTION" -a "$SHLVL" = "1" ]; then SENDER="보내는 사람 이메일 주소" RECIPIENT="받는 사람 이메일 주소" remote=$(echo "$SSH_CONNECTION" | awk '{ print $1 }') user=$(id -un) host="$(hostname)" subject="SSH Login: $user from $remote on $host" message=" Date : $(date +'%Y-%m-%d %H:%M:%S') SHLVL : $SHLVL SSH_CONNECTION : $SSH_CONNECTION " sendmail -f ${SENDER} ${RECIPIENT} <<EOF From: <${SENDER}> To: <${RECIPIENT}> Subject: ${subject} ${message} EOF fi unset SENDER unset RECIPIENT unset remote unset user unset host unset subject unset message
2.1.2. curl 이용
#!/bin/bash if [ -n "$SSH_CONNECTION" -a "$SHLVL" = "1" ]; then SENDER="보내는 사람 이메일 주소" RECIPIENT="받는 사람 이메일 주소" remote=$(echo "$SSH_CONNECTION" | awk '{ print $1 }') user=$(id -un) host="$(hostname)" subject="SSH Login: $user from $remote on $host" message=" Date : $(date +'%Y-%m-%d %H:%M:%S') SHLVL : $SHLVL SSH_CONNECTION : $SSH_CONNECTION " HOST=${RECIPIENT##*@} MXFOUND=N MXHOST="" Result=$( dig ${HOST} mx +short 2> /dev/null | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi if [ "$MXHOST" == "" ];then Result=$( host -t mx ${HOST} 2> /dev/null | grep mail | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi fi if [ "$MXHOST" == "" ];then Result=$( nslookup -type=mx ${HOST} 2> /dev/null | grep "mail exchanger" | head -1 ) RETVAL=$? if [[ $RETVAL -eq 0 ]]; then if [ "$Result" != "" ];then MXHOST=$( echo ${Result} | awk '{ print $NF }' ) MXHOST=${MXHOST%.} fi MXFOUND=Y fi fi if [[ "$MXHOST" == "" && "$MXFOUND" == "Y" ]]; then MXHOST=${HOST} fi if [ "$MXHOST" == "" ];then echo "dig|host|nslookup command not exists!" return 100 else curl -s --ssl \ --url "smtp://${MXHOST}" \ --mail-from ${SENDER} \ --mail-rcpt ${RECIPIENT} \ --upload-file - << EOF From: <${SENDER}> To: <${RECIPIENT}> Subject: ${subject} ${message} EOF fi fi unset SENDER unset RECIPIENT unset remote unset user unset host unset subject unset message unset HOST unset MXFOUND unset MXHOST unset Result unset RETVAL
3. 잡담
.profile 을 이용하는 방법은 .profile 을 실행하지 않는 연결, 예를 들어 sftp 나 ant 의 scp 태스크나 curl 과 같은 프로그램적인 연결에 대한 알림을 발송하지 않는다.
pam_exec.so 를 이용하는 방법은 모든 연결에 대해 알림을 발송하지만, 너무 많은 알림이 발송될 가능성도 있다.