블로그 릴레이 ~ ec2 인스턴스의 SSH 접속의 작동 방법을 알아봤다

2024.06.24

안녕하세요! AWS 사업 본부의 임채정입니다.

본 블로그는 당사의 한국어 블로그 릴레이의 두 번째 블로그입니다.
이번 블로그의 주제는「ec2 인스턴스의 SSH 접속의 작동 방법」입니다.

EC2 인스턴스를 생성할 때 키페어를 설정하면 ssh 접속을 할 때 그 키페어를 사용해서 접속을 합니다.
키페어를 사용해서 접속한다는 정도만 알고 있었는데 실제로는 그 키페어가어떤 작동을 하고 있는 건지 궁금해져서 알아봤습니다.

키페어 생성

먼저 인스턴스를 생성하기 전에 키페어를 생성합니다.

키페어란 EC2 인스턴스에 연결할 때 자격 증명 입증에 사용하는 보안 자격 증명 집합입니다.
리눅스는 SSH 접속 시에, 윈도우는 RDP 접속 시에 필요한 로그인/비밀번호의 취득을 위해 사용됩니다.

키페어는 퍼블릭 키와 프라이빗 키로 구성되는데
퍼블릭 키는 데이터를 암호화하는 데 사용되고, 프라이빗 키는 데이터를 해독하는 데 사용됩니다.

① 키 페어 생성

블로그에서는 접속 방법 테스트를 위해 임의의 키페어를 생성했습니다.

  • 키페어 이름: test-key
  • 유형: RSA
  • 프라이빗 키 파일 형식: .pem

② 프라이빗 키 다운로드

키페어를 생성하면 키페어의 프라이빗 키를 클라이언트(유저)가 다운로드 할 수 있습니다.
해당 키는 재발급이 불가하며 EC2 인스턴스에 접속할 때 반드시 필요한 파일이기 때문에 잃어버리지 않도록 잘 관리해야 합니다.

다운받은 키를 확인해보면 시각적으로 봤을때는 의미를 알 수 없는 값이 들어가 있습니다.
이 프라이빗는 나중에 서버를 접속할 때 암호화된 난수값을 복호화하는 데 사용됩니다.

EC2 인스턴스 생성

키페어의 생성이 완료되었으니 이번에는 EC2 인스턴스를 생성했습니다.

생성한 인스턴스의 설정값은 다음과 같습니다.

  • 인스턴스 이름: test-server
  • OS: Amazon Linux 2023
  • 인스턴스 유형: t2.micro
  • 키페어: test-key
  • 세큐리티 그룹
    • 유형-SSH, port-22, 소스-내IP
  • 네트워크 설정: (생략)

③ EC2인스턴스를 생성시 퍼블릭 키를 인스턴스에 저장

EC2 인스턴스를 생성할 때 키페어를 test.key 로 생성했습니다.
그러면 인스턴스애는 키페어의 퍼블릭 키가 저장됩니다.

퍼블릭 키는 ~/.ssh/authorized_keys 파일에 저장되고 실제로 확인해보면 다음과 같습니다.
결과 값의 마지막을 보면 키페어의 이름인 test-key 도 적혀있습니다.

$ cat ~/.ssh/authorized_keys

# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxaHBQ/M4dMdLecB/ test-key

서버 인증

서버 인증은 클라이언트가 접속하려는 서버가 실제로 신뢰할 수 있는 서버인지 확인하는 과정입니다.
클라이언트가 접속하려는 서버가 실제로 의도한 서버임을 확인하는 것과 동시에 인증되지 않은 서버와의 연결을 방지하여, 데이터 탈취, 위조 및 기타 악의적인 활동을 차단할 수 있습니다.

④ SSH 명령어 실행

클라이언트(유저)에서 EC2 인스턴스로 접속하고 싶다는 SSH 명령어를 실행합니다.

# ssh -i <비밀키> ec2-user@<EC2의 Public IP>
> ssh -i .ssh/test.key ec2-user@xx.xx.xx.xx

⑤ 서버에서 생성한 퍼블릭 키 전달

서버에서는 서버 인증용 키페어를 생성하는데 그 중에 퍼블릭 키를 EC2 인스턴스를 전달합니다.

⑥ 서버 퍼블릭 키가 없다면 저장

만약 서버에서 전달한 퍼블릭 키가 EC2 인스턴스에 저장되어 있지 않으면 새롭게 저장합니다.
이 때, 클라이언트는 퍼블릭 키는 전달받겠냐는 질문에 yes 라고 답합니다.

The authenticity of host '13.115.246.214 (13.115.246.214)' can't be established.
ED25519 key fingerprint is SHA256:3gHzEsSjwi/0EvlRlSOIQGBXa216NDOOwxhEi4cBEzI.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:103: yy.yy.yy.yy
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

퍼블릭 키는 클라이언트 측의 ~/.ssh/known_hosts 파일에 저장됩니다.
실제로 확인해보면 다음과 같습니다.
파일의 가장 밑부분에 접속한 서버의 IP주소와 함께 퍼블릭 키가 저장됩니다.

⑦ 난수값 생성 + 난수값의 해쉬값 생성후 저장

클라이언트에서는 난수값을 만듭니다. 난수값은 무작위로 만들어진 수열로, 다음에 나올 값를 절대 예측할 수 없는 수입니다.

그리고 그 난수값에 대한 해쉬값을 생성한 후에 저장해둡니다.

⑧ 난수값을 암호화

그리고 난수값은 전달받은 퍼블릭키를 사용해서 암호화를 합니다.

⑨ 암호화한 난수값 전달

암호화한 난수값은 서버로 전달합니다.

⑩ 암호화된 난수값을 복호화

서버에서는 전달받은 암호화된 난수값을 서버 키페어의 프라이빗키를 통해서 복호화를 합니다.
그러면 암호화가 풀린 난수값을 얻을 수 있습니다.

⑪ 난수의 해쉬값 생성

복호화한 난수값에 대한 해쉬값을 생성합니다.

⑫ 해쉬값이 같은 값인지 확인

그렇게 얻은 해쉬값을 클라이언트로 보내서 클라이언트가 가지고 있는 해쉬값과 비교해서 같으면 클라이언트가 접속하려고 한 서버(인스턴스)임을 인증할 수 있습니다.

※ 인스턴스에서 로그 확인

인스턴스 안에서 서버 인증이 처리되는 과정을 확인하기 위해 SSH 인증 시도와 관련된 로그입니다.
난수 생성 및 암호화 과정은 실제로는 매우 복잡하지만 SSH 로그에 중요 단계만 기록됩니다. (로그에 자세히 나타나지 않는 부분은 SSH 프로토콜이 자동으로 처리)

로그

해당 로그는 다음의 명령어를 사용했습니다.
또한, SSH 서버 인증 관련 로그를 자세하게 확인하기 위해 LogLevel 를 DEBUG 로 변경해서 확인했습니다.

# LogLevel 변경을 위해 파일 수정
sudo nano /etc/ssh/sshd_config
# 주석화 되어 있던 부분을 지우고 LogLevel DEBUG 로 변경 후 파일 저장

# SSH 서버 인증 관련 로그 확인
sudo journalctl -u sshd

로그 결과

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: list_hostkey_types: ecdsa-sha2-nistp256,ssh-ed25519 [preauth]
# 호스트 키 유형 목록: ecdsa-sha2-nistp256, ssh-ed25519

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: SSH2_MSG_KEXINIT sent [preauth]
Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: SSH2_MSG_KEXINIT received [preauth]
# SSH 키 교환 초기화 메시지(SSH2_MSG_KEXINIT)를 송수신

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: algorithm: curve25519-sha256 [preauth]
# 키 교환 알고리즘으로 curve25519-sha256 선택

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: host key algorithm: ssh-ed25519 [preauth]
# 호스트 키 알고리즘으로 ssh-ed25519 선택

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none [preauth]
Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none [preauth]
# 암호화 방식은 chacha20-poly1305@openssh.com

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: curve25519-sha256 need=64 dh_need=64 [preauth]
Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: kex: curve25519-sha256 need=64 dh_need=64 [preauth]
# Curve25519은 ECDH (Elliptic Curve Diffie-Hellman) 알고리즘의 일종으로 서버와 클라이언트 간에 사용될 키 교환 알고리즘

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: expecting SSH2_MSG_KEX_ECDH_INIT [preauth]
Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: SSH2_MSG_KEX_ECDH_INIT received [preauth]
# SSH2_MSG_KEX_ECDH_INIT 메시지 송수신
# 키 교환 과정이 진행되고 있다

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: rekey out after 134217728 blocks [preauth]

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: SSH2_MSG_NEWKEYS sent [preauth]
# SSH2_MSG_NEWKEYS 메시지 전송
# 서버가 자신을 인증하는 과정

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: Sending SSH2_MSG_EXT_INFO [preauth]
# SSH2_MSG_EXT_INFO 메시지 전송

Jun 07 09:26:10 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: expecting SSH2_MSG_NEWKEYS [preauth]
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: SSH2_MSG_NEWKEYS received [preauth]
# SSH2_MSG_NEWKEYS 메시지 수신

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: rekey in after 134217728 blocks [preauth]
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: KEX done [preauth]
# 키 교환 과정 완료, 서버 인증 완료

번외1) ~/.ssh/known_hosts 파일에 있는 퍼블릭 키를 삭제하면?

EC2 인스턴스를 사용해보신 분들은 아시겠지만 인스턴스에 처음 접속하면 다음과 같은 질문이 나오고 그 다음부터는 나오지 않습니다.(IP가 변경되면 다시 나오지만)
그러면 클라이언트 측에 있는 ~/.ssh/known_hosts 파일에서 해당 IP정보를 삭제하면 메시지가 다시 나올지 궁금해서 해봤습니다.

Are you sure you want to continue connecting (yes/no/[fingerprint])?

~/.ssh/known_hosts 파일에서 아까 마지막 줄에 추가된 퍼블릭 키를 삭제하고 저장합니다.

그리고 다시 접속을 해보면 다음과 같이 다시 질문이 나오고 ~/.ssh/known_hosts 파일에 삭제했었던 퍼블릭키가 다시 작성됩니다. 이 질문이 서버 인증을 위해 퍼블릭 키를 전달받겠다는 의미라는 걸 알 수 있습니다.

> ssh -i .ssh/test.key ec2-user@xx.xx.xx.xx
The authenticity of host '13.115.246.214 (13.115.246.214)' can't be established.
ED25519 key fingerprint is SHA256:3gHzEsSjwi/0EvlRlSOIQGBXa216NDOOwxhEi4cBEzI.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:103: yy.yy.yy.yy
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '13.115.246.214' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Fri Jun  7 08:55:40 2024 from xx.xx.xx.xx
[ec2-user@ip-10-11-0-175 ~]$

다시 추가된 퍼블릭 키

번외2) ~/.ssh/known_hosts 파일에 있는 퍼블릭 키의 내용을 변경해보면?

이번에는 ~/.ssh/known_hosts 파일에 있는 퍼블릭 키의 내용을 변경해보겠습니다.
퍼블릭 키를 완전히 삭제하는게 아니라 퍼블릭 키의 내용을 변경(일부분 삭제)하고 파일을 저장합니다.

그리고 인스턴스에 접속합니다.
다시 퍼블릭 키를 받겠냐는 질문이 나오고 yes 를 입력하면 문제 없이 접속이 됩니다.

> ssh -i .ssh/test.key ec2-user@xx.xx.xx.xx
The authenticity of host '13.115.246.214 (13.115.246.214)' can't be established.
ED25519 key fingerprint is SHA256:3gHzEsSjwi/0EvlRlSOIQGBXa216NDOOwxhEi4cBEzI.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:103: yy.yy.yy.yy
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '13.115.246.214' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Fri Jun  7 19:10:01 2024 from xx.xx.xx.xx
[ec2-user@ip-10-11-0-175 ~]$

~/.ssh/known_hosts 파일을 다시 확인해보면 변경된 퍼블릭 키 밑에 변경 전의 문제없는 퍼블릭키가 다시 생성되어 있습니다.
퍼블릭키가 없다고 판단하고 새롭게 퍼블릭키를 저장한것같습니다.

사용자 인증

사용자 인증은 서버가 접속을 시도하는 클라이언트 사용자가 실제로 권한이 있는 사용자임을 확인하는 과정입니다.
인증되지 않은 사용자가 서버에 접근하여 데이터를 읽거나 수정하는 것을 방지하고 적절한 권한을 가지고 있는 사용자가 서버에 접근할 수 있도록 합니다.
또한, 사용자 인증을 통해 누가 언제 서버에 접속했는지 기록하고 문제가 발생했을 시 이를 토대로 원인을 추적할 수도 있습니다.

⑬ 사용자 인증 요청

서버 인증이 완료되면 다음으로는 사용자 인증을 합니다.

⑭ 난수값 생성 + 난수값의 해쉬값 생성후 저장

인스턴스에서는 사용자 증명을 위해 난수값을 생성합니다.
그리고 그 난수값에 대한 해쉬값도 생성해서 저장해둡니다.

⑮ 난수값을 암호화

EC2 인스턴스를 생성할 때 설정한 키페어의 퍼블릭 키가 이미 ~/.ssh/authorized_keys 파일 안에 저장되어 있습니다.
퍼블릭 키를 사용해서 위에서 생성한 난수값을 암호화합니다.

⑯ 암호화된 난수값 전달

암호화된 난수값을 클라이언트에 전달합니다.

⑰ 암호화된 난수값을 복호화

클라이언트에서는 암호화된 난수값을 받아서 클라이언트가 가지고 있는 프라이빗 키를 사용해서 난수값을 복호화합니다.
그러면 암호가 풀린 난수값을 얻을 수 있습니다.

⑱ 난수의 해쉬값 생성

그렇게 얻은 난수값을 해쉬값을 생성합니다.

⑲ 해쉬값이 같은 값인지 확인

해쉬값을 인스턴스로 전달해서 인스턴스에 있는 해쉬값과 비교해서 같으면 서버에 접속할 수 있는 권한이 있는 사용자라는 것의 인증을 성공할 수 있습니다.
사용자 인증까지 성공을 하면 인스턴스에 정상적으로 접속되었을 것입니다.

※ 인스턴스에서 로그 확인

인스턴스 안에서 처리되는 과정을 확인하기 위해 SSH 인증 시도와 관련된 로그를 확인했습니다.

로그
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: userauth-request for user ec2-user service ssh-connection method none [preauth]
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: attempt 0 failures 0 [preauth]
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: PAM: initializing for "ec2-user"
# 사용자 ec2-user에 대한 PAM(Pluggable Authentication Modules)을 초기화

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: PAM: setting PAM_RHOST to "xx.xx.xx.xx"
# PAM_RHOST 값을 xx.xx.xx.xx으로 설정

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: PAM: setting PAM_TTY to "ssh"
# PAM_TTY 값을 ssh로 설정

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: userauth-request for user ec2-user service ssh-connection method publickey [preauth]
# 사용자 ec2-user에 대한 공개 키(public key) 인증 요청을 받음

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: attempt 1 failures 0 [preauth]
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: temporarily_use_uid: 1000/1000 (e=0/0)
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: trying public key file /home/ec2-user/.ssh/authorized_keys
# SSH 서버가 클라이언트로부터 받은 공개 키를 검증하기 위해, 사용자 ec2-user의 홈 디렉터리 내에 있는 authorized_keys 파일을 찾아 매칭되는지 확인


Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: fd 5 clearing O_NONBLOCK
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: /home/ec2-user/.ssh/authorized_keys:1: matching key found: RSA SHA256:f4YBBAzzL0b6qUpyzD4jhRK117ebud+orJ4AWmSBo/s
# SSH 서버가 클라이언트로부터 제출된 공개 키를 확인하고, 해당 공개 키의 경로와 해시 값을 표시하고 /home/ec2-user/.ssh/authorized_keys 파일과 RSA 공개 키가 일치하는 것을 확인

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: /home/ec2-user/.ssh/authorized_keys:1: key options: agent-forwarding port-forwarding pty user-rc x11-forwarding
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: Accepted key RSA SHA256:f4YBBAzzL0b6qUpyzD4jhRK117ebud+orJ4AWmSBo/s found at /home/ec2-user/.ssh/authorized_keys:1
# /home/ec2-user/.ssh/authorized_keys 파일의 공개 키와 발견된 RSA 공개 키가 일치하여 인증에 성공

Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: restore_uid: 0/0
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: auth_activate_options: setting new authentication options
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: debug1: do_pam_account: called
Jun 07 09:26:12 ip-10-11-0-175.ap-northeast-1.compute.internal sshd[3842]: Accepted publickey for ec2-user from xx.xx.xx.xx port 54983 ssh2: RSA SHA256:f4YBBAzzL0b6qUpyzD4jhRK117ebud+orJ4AWmSBo/s
# xx.xx.xx.xx IP 주소에서 ec2-user 사용자의 공개 키를 사용한 인증이 승인

번외3) 인스턴스 안에 있는 ~/.ssh/authorized_keys 파일의 내용을 변경해보면?

이번에는 인스턴스 안의 ~/.ssh/authorized_keys 파일에 있는 퍼블릭 키의 내용을 변경해보겠습니다.

$ vi  ~/.ssh/authorized_keys

그리고 인스턴스에서 로그아웃하고 다시 접속해보겠습니다.
역시 이번에는 사용자 인증에서 사용되는 퍼블릭 키가 변경되어 암호키를 복호화 할 수 없어 접속이 되지 않았습니다.

> ssh -i .ssh/test.key ec2-user@xx.xx.xx.xx
ec2-user@xx.xx.xx.xx: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

마무리

한국어 블로그 릴레이의 두 번째 블로그「ec2 인스턴스의 SSH 접속의 작동 방법」편이었습니다.
다음 세 번째 블로그 릴레이는 7월 첫째주에 공개됩니다!

블로그 릴레이의 다음 블로그를 읽어보고 싶으신 분들은 아래 페이지를 참고해주세요.

끝까지 읽어주셔서 감사합니다! AWS 사업 본부 임채정이었습니다.