3. SSH Keys

원문 링크

OpenSSH 서버에 접속하기 위해 인증하는 흔한 방법으로 당신의 remote machine의 shell 비밀번호를 입력해서 접속하는 것이 있다.

tom@local:~$ ssh remote
The authenticity of host 'remote (192.168.0.64)' can't be established.
RSA key fingerprint is d1:35:45:a6:d1:b2:e4:08:f8:67:b1:19:fe:04:ca:1c.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'remote,192.168.0.64' (RSA) to the list of known hosts.
tom@remote's password:

tom@remote:~$

이러한 shell 비밀번호 인증은 OpenSSH sshd(8) 설치를 하고 나면 대부분 기본적으로 지원하는 방법으로, 서버에 최초 접속을 할 때라면 적당한 방법이다.

sshd(8)은 자동화된 공격의 주된 목표가 된다. 악한 봇들이 표준 SSH 포트인 tcp/22나 그 외 tcp/2222와 같이 흔하게 사용하는 포트들을 지켜보면서 서버에 연결하려고 지속적으로 시도하는 것이다. 당신의 시스템이 매우 복잡한 비밀번호를 강제한다면, 게다가 only appropriate users have shells 이고 특정 그룹과 계정에만 SSH 연결을 제한해두었다면 뭐 별 일이 아닐 수도 있겠다.

자동화된 공격에 대처할 수 있는 다른 방법도 있다. fail2ban과 같이 굉장히 많은 접속 시도를 하는 사람을 차단하는 시스템을 사용하는 것이다. 하지만 가장 효율적인 방법은 비밀번호 입력을 완전히 건너뛰고 대신 SSH key를 이용해서 서버와 관계가 있는 기기만이 접속할 수 있도록 하는 것이다.


어떻게 작동하는가

SSH keypair는 하나의 private key와 하나의 public key로 이루어져 있고 이 한 쌍은 서로 암호학적으로 연결되어 있는데, 이는 GnuPG key를 사용하기 위해 셋업하던 이전 글에서 언급한 내용과 비슷하다. 인증을 위한 key의 기본 기능은 이런 것이다. 만약 누군가 당신의 public key를 가지고 있다면, 이에 대응하는 private key를 통해 확인할 수 있는 작업을 요청함으로써 당신을 대신해 인증할 수 있다는 것이다. 이는 암호학적인 서명 (signing)과 비슷하게 작동하는 것이다.

이러한 방법이 매우 효과적인 이유는, 만약 당신이 인증을 하기 위해 올바른 public key가 필요하고, 그 public key는 충분히 길이가 길다면, 해커는 그 인증 정보를 때려맞추기가 거의 불가능하기 때문이다. 이 세상에 추측으로 알 수 있는 "공통된" private key 같은 건 없기 때문에, 해커들은 가능한 모든 private key를 시도해봐야 하고, 이는 심지어 원격으로 가능 (remotely practical) 하지도 않다.

물론 어떤 이유에서든 당신의 sshd(8) 시스템은 공격 당할 수 있겠지만, 그 인증 방법으로 오직 public key만을 사용하도록 한다면 해커가 순전히 때려 맞추는 식으로는 실질적으로 공격이 불가능할 것이기에 어느 정도 안전하다고 볼 수 있다. 하지만 알아두어야 할 것이 있는데, 이는 sshd(8) 자체의 보안 문제를 해결하는 것이 아니므로, 당신은 여전히 private key가 접근될 수 없도록 안전하게 보관해야 하며, 문제의 상황을 대비해서 passphrase 역시 필요하게 된다.

이제부터 보여줄 예시들은 이미 당신의 client와 server에 OpenSSH가 잘 설치되었음을 가정하는 것이다. Debian 계열의 시스템에서는 아래 명령어로 설치할 수 있다.

# apt-get install ssh
# apt-get install openssh-server

OpenBSD)와 같이 기본적으로 OpenSSH를 탑재해서 배포되는 client/server 시스템도 있다.


Key 생성하기

GnuPG 셋업할 때처럼 keypair를 생성할 건데, 연결을 시도할 client에서 ssh-keygen(1)를 사용해서 할 수 있다. 여기서 필자는 4096-bit RSA를 사용할 것이다. 왜냐하면 이 암호화 방식은 굉장히 오래된 시스템에서도 많이 지원하고 있고, 시간이 지나도 안전함을 보장하기 때문이다 (물론 RSA가 안전하지 않다고 밝혀졌을 때, 다른 새로운 key를 생성하는 것이 어려운 일은 아니다). 또한 OpenSSH의 최신 버전에서 기본으로 사용하는 ECDSA algorithm를 선호한다해도 문제 없을 것이다. 만약 여러 key들을 구분해야 할 필요가 있다면 key의 comment를 암호화되지 않은 식별자로 적어두는 것도 꿀팁인데, 이메일 주소로 comment를 달아도 좋더라.

$ ssh-keygen -t rsa -b 4096 -C tom@sanctum.geek.nz
Generating public/private rsa key pair.

첫 단계로, key 파일이 저장될 위치를 입력해야 한다. 그냥 Enter를 쳐서 기본값으로 진행하기를 추천한다. 그렇지 않으면 추가적으로 몇 스텝 해야할 일이 늘어난다.

Enter file in which to save the key (/home/tom/.ssh/id_rsa):

그 다음엔 passphrase를 입력해야 한다. Passphrase는 유출된 key가 사용될 수 없도록 하는 필수적인 것이다. passphrase를 어떻게 만들어야 안전한지에 대해서는 이전에 설명했으므로 생략한다. 그리고 GnuPG의 passphrase와는 완전히 다른 것으로 만들어야 한다.

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

이 과정이 끝나면 key가 생성되는데, 한 눈에 알아볼 수 있는 그림 표현도 같이 보여준다. 사실 필자는 이게 어디서 유용한지 잘 모르겠고, 그 대신 key fingerprint는 매우 도움이 된다.

Your identification has been saved in /home/tom/.ssh/id_rsa.
Your public key has been saved in /home/tom/.ssh/id_rsa.pub.
The key fingerprint is:
d5:81:8c:eb:c6:c5:a2:b9:6a:ae:32:cc:20:bf:cf:66 tom@local
The key's randomart image is:
+--[ RSA 4096]----+
|         o ..    |
|        . o. .   |
|         o. .    |
|        o.o      |
|       =So       |
|o     o +        |
|=.     o         |
|oo..E .          |
| ooO=.           |
+-----------------+

이제 key 파일들은 ~/.ssh 위치에서 볼 수 있어야 한다.

$ ls -l .ssh
-rw-------  1 tom  tom  3326 Apr  2 22:47 id_rsa
-rw-r--r--  1 tom  tom   754 Apr  2 22:47 id_rsa.pub

id_rsa 파일이 암호화된 private key이고, 어딘가 단단히 잠긴 금고에 안전하게 보관되어야 한다. 반면에 id_rsa.pub 파일은 public key이어서 PGP public key처럼 안심하고 배포해도 된다.


Key 기반 인증

이제 새롭게 생성한 public key로 비밀번호를 대신하는 인증할 수 있도록 설정할 것이다. 그 설정을 하기 전이므로, 일단은 비밀번호로는 원격 서버에 접속할 수 있어야 한다.

$ ssh remote
tom@remote's password:

접속했다면, 원격 서버에 ~/.ssh 디렉토리가 존재하는지, 그리고 이미 ~/.ssh/authorized_keys에 어떠한 key들이 있는지부터 먼저 체크하자. 체크하지 않고 진행한다면, 우리는 이미 존재하는 것들을 모두 덮어씌울 것이다.

$ mkdir -p ~/.ssh
$ chmod 0700 ~/.ssh

위 명령이 문제없이 완료됐다면, exit이나 Ctrl-D를 통해 원격 서버 접속을 끊고, scp(1)를 이용해서 원격 서버에 아까 생성했던 public key를 복사한다.

$ scp ~/.ssh/id_rsa.pub remote:.ssh/authorized_keys
tom@remote's password:
id_rsa.pub    100%    754    0.7KB/s    00:00

OpenSSH의 최신 버전에는 똑같은 작업을 해주는 ssh-copy-id(1)이라는 도구가 있음을 알아두자. 하지만 이 도구가 실제로 어떻게 작동을 하는지 배경을 알아두는 것이 좋을 것이다.

아무튼 여기까지 끝났다면 다시 원격 서버에 접속을 시도해보자. 그러면 비밀번호 대신, passphrase를 물어볼 것이다.

$ ssh remote
Enter passphrase for key '/home/tom/.ssh/id_rsa':


보안 상 이점

이렇게 설정을 끝낸 후에 보면, 이를 통해 정말로 보안이 훌륭해졌는지 의구심이 들 수 있다. 결국 원격 서버에 접속하기 위해 뭔가를 입력해야하는 것은 똑같기 때문이다. 보안적인 관점에서 봤을 때 첫 번째로 중요한 이점은 비밀번호도, passphrase도, private key도 접속하고자 하는 원격 서버로 전송되지 않는다는 것이다. 인증은 순전히 passphrase를 통해 복호화된 public-private key pair를 기반으로 진행되는 것이다.

This means that if the machine you’re connecting to were compromised, or your DNS had been poisoned, or some similar attack tricked you into connecting to a fake SSH daemon designed to collect credentials, your private key and your password remain safe. 만약 누군가 당신의 로컬 머신을 해킹했거나 혹은 DNS poisoning 등을 통해 비밀 정보를 중간에서 가로채기 위해 가짜 SSH daemon으로 접속하도록 당신을 속였다 하더라도, private key와 비밀번호는 애초에 전달되지조차 않는다는 것이다.

두 번째 이점은 원격 서버의 비밀번호 인증 방식을 아예 꺼버려서 모든 접속자가 public key 인증만을 사용하도록 할 때 생긴다. 그렇게 설정하려면 sshd_config(5)의 설정을 아래처럼 하면 되는데, 보통 원격 서버의 /etc/ssh/sshd_config에 위치해 있다.

PubkeyAuthentication yes
ChallengeResponseAuthentication no
PasswordAuthentication no

설정 변경 적용을 위해 SSH 서버를 재시작해야 한다.

$ sudo /etc/init.d/ssh restart

이제 어느 누구도 비밀번호로 접속할 수가 없다. 오직 private key로만 인증한다는 것은 brute-force 방식으로 공격에 성공하는 것이 사실상 불가능하다고 말했었다. 당신의 신분으로 서버에 접속하기 위해서 해커는 당신의 passphrase만 알아서는 안 되고, private key에도 접근할 수 있어야 가능하게 되는데, 이는 비밀번호를 때려맞추는 것보다 훨씬 어려운 일이 된다.

Public key 인증을 하게 되면 sshd(8)가 인증에 관해 좀 더 자세한 제어를 할 수 있다는 장점이 있다. 어느 접속자가 어떤 key로 접속하는지, TCP로 접속하는지 X11 tunnels로 접속하는지, 아니면 더 나아가 어떤 명령어로 접속했는지를 알 수 있는 것이다. authorized_keys(5) man 페이지에서 몇몇 예제들을 볼 수 있다.

마지막으로 SSH key 인증을 agent와 함께 사용해서 얻는 장점이 있는데, 이는 다음 글에서 이야기할 것이다.


Host Key와 Fingerprint

SSH 연결은 이상적으로는 양방향 인증 프로세스이어야 한다. 연결하고자 하는 서버가 당신이 누군지 확실하게 알아야 하고, 당신도 연결하고자 하는 host가 맞는지 확싫해야 하는 것이다. 터널링, 방화벽, DNS poisoning, NAT, 해킹된 시스템, 그 외 많은 속임수들이 있기 때문에, 당신은 항상 스스로 올바른 곳에 연결하려고 하는 것인지에 대해 조심해야 한다. 여기서 도움을 주는 것이 OpenSSH의 host key이다.

새로운 원격 서버에 처음 접속할 때면 아래와 같이 출력되는 것을 봤을 것이다.

$ ssh newremote
The authenticity of host 'newremote (192.168.0.65)' can't be established.
RSA key fingerprint is f4:4b:f4:8c:c5:50:f6:c8:d3:b2:e9:14:68:86:b5:7b.
Are you sure you want to continue connecting (yes/no)?

많은 관리자들이 이 메세지가 안 뜨도록 설정을 꺼버리는데, 그러면 안 된다! 이것은 매우 중요한 것이다.

Key fingerprint는 원격 서버의 OpenSSH가 사용하는 host key의 비교적 짧은 hash 값이다. 이 값은 SSH client에서 검증되고, 가짜를 만들기가 어렵다. 새로운 서버에 처음 접속한다면 이 때 보게 되는 host key fingerprint를 기억해 두고 항상 체크해봐야 하며, 그렇지 못했다면 시스템 관리자에게 물어봐서 그 fingerprint를 알고 있어야 한다.

Host key의 fingerprint는 SSH 서버에서 ssh-keygen(1)를 실행해서 확인해 볼 수 있다.

$ ssh-keygen -lf /etc/ssh/ssh_host_rsa_key
2048 f4:4b:f4:8c:c5:50:f6:c8:d3:b2:e9:14:68:86:b5:7b /etc/ssh/ssh_host_rsa_key.pub (RSA)

원한다면 client 쪽에서 서버에 연결하지 않고도 비슷한 명령을 사용해서 확인할 수 있다.

$ ssh-keygen -lF newremote
# Host 192.168.0.65 found: line 1 type RSA
2048 f4:4b:f4:8c:c5:50:f6:c8:d3:b2:e9:14:68:86:b5:7b newremote (RSA)

만약 스스로 host key를 확인할 수 없는 상황이라면 안전하고 믿을 수 있는 통로를 통해 시스템 관리자에게 전달 받아야 한다. 예를 들어 직접 구두로 알려주거나, PGP 서명된 메세지를 통해서 말이다. 만약 colon(;)으로 구분된 SSH fingerprint가 맞는 값이 아니라면, 당신의 연결을 속이고자 하는 누군가의 희생양이 될 수도 있을 것이다!

This is definitely overkill for new virtual machines and probably new machines on a trusted LAN, but for machines accessed over the public internet, it’s a very prudent practice. 물론 가상 머신이나 trusted LAN에서의 새로운 서버에서 이런 세팅을 하는 것은 지나친 작업임에 분명하다. 하지만 공공 인터넷을 통해 접근이 가능한 서버라면 이러한 설정은 필수적으로 생각해봐야 할 일이다.

마찬가지로 ssh(1)도 기본적으로 host key를 기록해두고 있어서, 갑자기 원래 값과 다른 host key를 마주치게 된다면 아래와 같은 경고를 받는다.

$ ssh newremote
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
d7:06:51:16:80:f6:32:b4:35:7c:53:8d:5a:49:69:ec
Please contact your system administrator.
Add correct host key in /home/tom/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/tom/.ssh/known_hosts:22
RSA host key for newremote has changed and you have requested strict checking.

이런 기능 또한 사용자들이 종종 꺼버리곤 하는데, 이 역시 꽤나 위험한 일이다. 특히나 비밀번호 인증일 경우, 악의적인 누군가에게 당신의 비밀번호를 바치는 꼴이 될 것이다.