본문 바로가기
기타/툴 관련(리눅스)

SSH 자동 로그인(sshpass)

by WebHack 2009. 5. 8.
예전에 SSH로 많은 서버에 동시에 같은 명령내리기라 는 포스팅을 통해 많은 서버에 일괄적으로 명령을 내리는 방법을 살펴보았다. 거기서 소개된 각종 프로그램들과 테크닉은 ssh을 접속할 때 인증을 위해 암호입력과정을 생략하기 위해 클라이언트쪽 공개키를 서버 쪽에 미리 다 등록해놓아야 한다는 문제가 있다.

만 약 대상이 몇 대가 안된다면 수동으로 각 서버들에 로그인 해서 키를 등록하면 되겠지만 그 대상이 수십 대 단위를 넘어간다면 키를 등록작업을 위해 그 많은 서버에 손수 계정과 암호를 쳐서 로그인 하고 키를 등록하는 작업을 한다는 건 생각만 해도 지겹고 짜증 나는 일이 될 것이다.

네이버에서 만들었다는 자칭 대규모 시스템 운영/관리도구 라는 dist도 사용을 위해서는 모든 서버에 키를 등록해놓아야 한다. 그래서 어떤 사용자는 http://dev.naver.com/tracker/?func=detail&aid=521&group_id=14&atid=120 와 같은 문제점을 제기했는데 일리가 있는 말이다. 키를 등록하고 나면 로그인 때마다 계정/암호 입력 없이 바로 작업이 가능하지만 그 작업을 위한 준비과정에는 어차피 대상서버들에 계정/암호를 수동으로 입력해서 들어가서 키를 등록하는 노가다성 작업을 거쳐야 한다는 것이다. 이야말로 중이 제 머리를 못 깎는 황당한 시츄에이션이 아닌가?

위에서 질문한 사용자는 키 등록 방식이 아닌 계정/암호를 내부적으로 입력해줘서 ssh에 접근하는 python parakamio같은 모듈을 사용해서 그러한 상황에서 작업할 수 있도록 하는 게 좋을 것 같다고 얘기하고 있는데 그 외에 Perl에서도 Net::OpenSSH 같은 모듈을 사용하면 계정/암호를 지정해서 ssh로 접속해서 어떤 작업을 수행할 수 있다. 하지만 어떤 프로그래밍 언어들이 제공하는 모듈들을 사용하면 대상서버에 어떤 일괄 작업을 하려고 할 때 그런 작업들을 단편화 시켜 차례로 적용해가야 하는 번거로움이 있어 간단하고 빨리 일련의 작업들을 각 대상서버에 적용하고 싶을 때 사용하려면 그 역시 귀찮다. 역시나 커맨드라인의 일괄작업처리,안정성,편리성의 강점을 벗어버리기는 어렵다고나 할까?

그러면 ssh로그인 할 때 키보드로 인터렉티브하게 암호를 입력해야 하는 작업을 자동화시키는 방법은 없을까?

그 해답은 바로 sshpass 라는 작은 유틸리티에 있다. 이것은 많이 쓰는 ubuntu리눅스의 경우 따로 소스를 받아서 컴파일하고 설치하지 않아도 sudo apt-get install sshpass 라는 명령을 내리면 미리 만들어진 패키지를 설치할 수 있다.

그럼 sshpass가 어떻게 동작하는지를 살펴보자. 만약 어떤 유저로 어떤 대상서버에 ssh로 로그인해서 ls 라는 명령을 내리고 싶다고 하면 키가 등록되었을 때

ssh user@somehost ls

하면 된다. 하지만 키가등록되어 있지 않다면 암호를 수동으로 입력해야 할 것이다. 이런 암호입력 과정을 자동화시키고 싶으면 sshpass를 사용하여 다음과 같이 명령을 내린다.

sshpass -p password ssh user@somehost ls

잘 보면 기본 명령어에서 sshpass -p password 만 앞에 추가되었음을 볼 수 있다. sshpass는 이런 식으로 암호입력과정을 자동화시켜주며 이것은 ssh뿐만 아니라 scp,sftp같은 명령에도 똑같이 쓸 수 있다. expect같은 걸 써서 해도 되지 않느냐고 하는 사람도 있겠지만 그 편리성에서 비교도 안 됨은 말 안 해도 알 것이다. 게다가 커맨드 라인상에서 입력한 암호는 보안상 ps -ef 같은 명령으로 프로세스상태를 봐도 보이지 않게 숨겨주며 커맨드 라인상에서 암호를 직접 쓰기 싫으면 별도의 텍스트 파일에 암호를 저장해 놓고

sshpass -f pass.txt ssh user@somehost ls

처럼 쓸 수도 있다.( 이 경우 암호파일은 chmod명령어로 자신외에 읽기 권한을 빼서 보안에 주의하자.)

그런데 대상 서버에 처음 접속하는 경우에는 다음처럼

The authenticity of host 'somehost (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
Are you sure you want to continue connecting (yes/no)?

known host에 추가할 것이냐는 질문을 하게 되는데 이런 경우에 sshpass는 동작하지 않으므로 이것을 암묵적으로 yes하고 넘어가려면 ssh에 -oStrictHostKeyChecking=no 옵션을 (이것은 한번 접속이 일어난 후에는 know_hosts에 등록되어서 다시 사용할 필요가 없다.) 그리고 사용하다 보면

Pseudo-terminal will not be allocated because stdin is not a terminal.

같은 경고메시지가 나오는 경우가 있는데 이것을 해결하려면 ssh에 -T 옵션을 줘서 다음 처럼하면 된다.

sshpass -p password ssh -T -oStrictHostKeyChecking=no user@somehost ls

그러면 지금까지 본 것을 종합하여 로컬에서 ssh 키를 생성하여 대상서버들에 키를 등록하는 과정을 자동화 시키는 스크립트를 한 번 만들어보자.

키를 만드는 과정은 http://www.xinublog.com/340 혹은  http://people.kldp.org/~eunjea/ssh/x87.html 를 참고하기 바란다.

key 를 만들 때 pass phrase를 정하지 않고 그냥 엔터 치면 나중에 원격서버에 키를 등록하고 나서 해당 서버에 접속하면 바로 로그인 돼서 편리하지만 pass phrase를 어떤 값으로 입력하면 자신의 키가 등록된 서버에 접속하려면 그 pass phrase를 물어본다. 이것은 pass phrase를 빈 암호로 생성했을 때 자신의 비밀키가 유출되거나 해당 계정이 뚫렸을 경우 키를 등록해놓은 모든 서버들에 대한 제어권을 넘겨주게 되어 보안상 치명적이므로 그것을 방지하기 위한 것이다. 그래서 보안을 강화하고 싶으면 pass phrase를 사용하는 것이 좋다. 하지만 그렇게 되면 원격 서버에 접속할 때 pass phrase를 입력하는 작업도 수동으로 해야 하는데 그것을 자동화 시키고 싶으면 위의 링크의 마지막에 설명되어 있는 ssh-agent과 ssh-add를 사용하여 작업 전 한 번만 등록해놓으면 ssh-agent가 알아서 그 인증과정을 대리해준다. ssh-agent에 대한 더 자세한 사용법은 http://gypark.pe.kr/wiki/SSH 을 참고하기 바란다.

pass.txt에 암호가 담겨 있고 서버들의 리스트가 들어 있는 파일이 다음과 같다면

<hosts.txt>
somehost1
#somehost2
somehost3
somehost4

    .
    .
  계속

다음과 같은 스크립트로 대상 서버들에 자신의 키 등록 작업을 일괄 처리할 수 있다.

<ssh_key_dist.sh>
#!/bin/bash

for m in `cat hosts.txt`
do

[[ ${m:0:1} == "#" ]] && continue  # skip if comment

echo $m =======================================
sshpass -f pass.txt ssh -T -oStrictHostKeyChecking=no user@$m <<EOF1
if [ ! -e ".ssh" ]
then
    mkdir .ssh
    chmod 700 ~/.ssh
fi
EOF1

sshpass -f pass.txt scp ~/.ssh/id_dsa.pub user@$m:

sshpass -f pass.txt ssh -T user@$m <<EOF2
cat ~/id_dsa.pub >> ~/.ssh/authorized_keys
chmod 644 ~/.ssh/authorized_keys
rm -f ~/id_dsa.pub
EOF2

done


위 쉘스크립트는 hosts.txt에서 접속대상 서버를 한 줄씩 읽어서 첫 문자가 #로 코멘트가 되어 있지 않으면 ssh로 접속해서 .ssh디렉토리가 존재하는지 채크해서 없으면 만들고 로컬에 생성된 공개키(여기서는 dsa방식을 사용했다 키파일 이름이 다르면 적절하게 고칠 것)를 접속대상 서버에 scp하고 다시 대상서버에서 로그인하여 복사한 공개키를 .ssh/authorized_keys 파일의 뒤쪽에 붙여서 키를 등록하고 복사했던 파일을 지우는 작업을 반복하는 것이다.

다시 생각해보니 ssh 연결을 3번씩이나 할 필요가 없어서 1번에 처리하도록 한 개정판 추가. (2009-02-16)

<개정판 ssh_key_dist.sh>
#!/bin/bash

PUBKEY=`cat ~/.ssh/id_dsa.pub`

for m in `cat hosts.txt`
do

[[ ${m:0:1} == "#" ]] && continue # skip if comment

echo $m =============================

sshpass -f pass.txt ssh -T -oStrictHostKeyChecking=no user@$m <<EOF1
if [ ! -e ".ssh" ]
then
    mkdir .ssh
    chmod 700 ~/.ssh
fi

umask 022
echo $PUBKEY >> .ssh/authorized_keys
EOF1

done