Microsoft 社는 2022년 6월 15일 Internet Explorer 11의 지원을 종료했습니다.

SSH Login Email Alerts in Linux Server

제목

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 를 이용하는 방법은 모든 연결에 대해 알림을 발송하지만, 너무 많은 알림이 발송될 가능성도 있다.

제목

첨부파일