SSH 란


SSH(Secure Shell) = 원격 셸·파일 전송·터널링을 단일 암호화된 TCP 채널(기본 포트 22) 위에서 다루는 프로토콜. 1995 Tatu Ylönen 이 평문 트로이카(telnet, rlogin, rsh, FTP) 대체로 설계.

  • 텔넷 대체 맞음. 정확히는 telnet/rlogin/rsh/FTP 를 한꺼번에 갈아엎음.
  • 모든 게 한 TCP 22 연결 안의 다중 채널로 흐름 (shell, exec, sftp, port-forwarding).

“telnet 대체” 가 정확히 어떤 역할?

telnet 은 두 가지로 쓰임:

  1. 원격 셸 프로토콜 (본업) — 평문으로 원격 로그인. SSH 가 대체한 게 이 부분.
  2. Generic TCP probe (부수 사용) — 임의 포트에 raw TCP 연결해서 “포트 열렸나·서비스 listening 중인가” 확인. telnet host port → “Connected to …” 뜨면 OK.

SSH 는 #1 만 대체. SSH 클라이언트는 22 포트의 SSH 서버하고만 말함. ssh google.com 80 같은 건 TCP 연결까지는 되지만 곧장 SSH 핸드셰이크 강제 → HTTP 서버가 SSH banner 응답 안 함 → kex_exchange_identification: read: Connection reset by peer 에러. 결과가 “SSH 가 거기서 도냐” 로 오염돼 진짜 포트 listen 여부 확인 못 함.

도구TCP 연결 후포트 probe 용도?
telnet host portraw stdin/stdout
nc host portraw stdin/stdout (telnet 후속·현대 표준)
ssh host portSSH 프로토콜 핸드셰이크 강제

→ telnet 의 #2 역할은 nc (netcat) 가 가져감. SSH 는 generic TCP client 가 아님. (자세한 nc 사용은 07. Linux Networking### telnet — 포트 listen 확인 참고.)

동작 단계


  1. TCP 22 핸드셰이크 = 평범한 3-way handshake.
  2. SSH 버전 교환 = 양쪽이 SSH-2.0-OpenSSH_9.6 같은 배너를 평문으로 주고받음.
  3. KEX (Key Exchange) = Diffie-Hellman 계열(요즘 Curve25519) 로 임시 세션 키 합의. 도청자가 못 알아내는 공유 비밀.
  4. 서버 인증 = 클라이언트가 “너 진짜 그 서버 맞아?” 검증. host key 사용.
  5. 사용자 인증 = 서버가 “너 진짜 그 사람 맞아?” 검증. password / publickey / GSSAPI 등.
  6. 채널 오픈 = 그 위에 shell, exec, sftp, port-forwarding 채널이 다중화돼서 흐름.

서버 인증 — host key + TOFU


  • 서버는 영구 host key 쌍을 가진다 (/etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key).
  • 첫 접속 시 클라이언트가 host key fingerprint 를 보여주고 ~/.ssh/known_hosts 에 기록 = TOFU (Trust On First Use).
  • 이후 host key 가 바뀌면 경고 (MITM 의심):
    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
    
  • CA 기반 검증은 옵션 — 아래 ### SSH CA 참고.

사용자 인증 — password vs publickey


password

  • 채널은 암호화돼 있지만 안에 비번 평문이 흐름. 서버가 받아 /etc/shadow 와 대조.
  • 약점 = 약한 비번·재사용·brute force. publickey
  • 클라이언트가 priv key 로 챌린지(랜덤 nonce + 세션 ID) 에 서명해서 보냄.
  • 서버는 ~/.ssh/authorized_keys 의 pub key 로 서명 검증.
  • 비번 자체가 서버에 안 감. priv key 는 평생 디스크 밖으로 안 나옴.
  • 흐름:
    1. ssh-keygen -t ed25519 로 키 쌍 생성.
    2. ssh-copy-id user@host 로 pub 만 서버에 등록.
    3. 다음부터 비번 없이 접속.

pub/priv key 의 정체 — TLS·SSH·GPG 비교


수학은 같다 (RSA / ECDSA / Ed25519). 묶음 형식과 신뢰 모델이 다르다.

시스템묶음신뢰 모델
TLS / HTTPSX.509 인증서CA-PKI (브라우저 root store)
SSH (기본)raw keyTOFU (known_hosts)
SSH CA (옵션)OpenSSH certCA 키 자체를 신뢰
GPGOpenPGP packetWeb of Trust
본질 공통 = “priv 를 가졌음을 pub 으로 검증할 수 있다”. 무엇으로 묶고 누가 보증하느냐의 차이.

SSH CA — 옵션


  • 기본 SSH 는 CA 없이 TOFU 로 충분 (개인·소규모).
  • 호스트·사용자 수가 늘면 known_hosts·authorized_keys 가 폭주 → CA 도입 가치.
  • 한 번 CA 만 신뢰하면 그 CA 가 사인한 모든 host/user cert 자동 신뢰.
  • 발급:
    ssh-keygen -s ca_key -I id -n principals -V +1h user_key.pub
  • 서버가 신뢰 = sshd_configTrustedUserCAKeys /path/to/ca.pub.
  • 클라이언트가 신뢰 = ~/.ssh/known_hosts@cert-authority *.example.com ssh-ed25519 ....
  • 사례 = Netflix BLESS, Facebook SSH CA, GitHub SSH certificate authority.

SCP / SFTP / HTTPS 와의 관계


  • HTTPS = HTTP + TLS. SSH 와 완전 별개 (포트 443). 공통점은 “그 위에 암호화 채널” 뿐.
  • SFTP = SSH 채널 안에서 도는 파일 전송 프로토콜. FTP 와 무관 (이름만 비슷). 22 포트.
  • SCP = 옛 단순 파일 복사 명령. 원래는 SSH 위 자체 프로토콜. OpenSSH 9.0(2022)부터 내부적으로 SFTP 사용 — SCP 프로토콜의 보안 문제(CVE-2020-15778 등) 때문. 외부 명령 인터페이스만 동일, 내부는 SFTP.
  • 요약 = SFTP·SCP 는 SSH 의 자식. HTTPS 는 .

SCP 파일 전송


scp -rp -i ssh-key-2023-08-09.key {파일} opc@192.0.2.231:/home/opc

TTY 와 PTY


  • TTY = teletype, 옛 종이 단말 추상화. 오늘날 = 리눅스 커널의 “터미널 디바이스” 인터페이스 (/dev/tty*, /dev/pts/*). echo, line buffering, signal(Ctrl-C → SIGINT) 처리.
  • PTY = pseudo-TTY. 실제 하드웨어 없이 소프트웨어로 만든 TTY 페어.
  • SSH 가 interactive shell 줄 때 서버 쪽에 PTY 할당.
  • 동작:
    • ssh user@host (인자 없음) → 자동 PTY, interactive shell.
    • ssh user@host 'ls -l' → 명령 한 줄, PTY 없음. 빠르고 script 친화.
    • -t = 강제 할당 (ssh -t host 'sudo top'sudo·top 이 TTY 요구).
    • -T = 강제 비할당 (input 을 파이프로 줄 때).

접근 제어


인증 (누구냐) = password / publickey / CA — 위에 다룸. 인가 (뭘 할 수 있냐) — sshd_config

  • AllowUsers alice bob, AllowGroups devs — 화이트리스트.
  • Match User deploy — 사용자별 다른 정책 블록.
  • ForceCommand /usr/bin/git-shell — 그 사용자는 정해진 명령만 (케이지).
  • PermitRootLogin no, PasswordAuthentication no — 흔한 하드닝.

키별 제약 — authorized_keys 옵션 prefix

from="10.0.0.0/8",command="rsync ...",no-pty,no-port-forwarding ssh-ed25519 AAAA... user
  • from= = 그 IP 대역에서만 그 키 사용 가능.
  • command= = 그 키로 들어오면 무조건 그 명령만.
  • no-pty, no-port-forwarding = 권한 좁힘.

운영 레이어 = bastion(jump host), MaxAuthTries, LoginGraceTime, fail2ban, audit log.

AWS EC2 key pair


  • EC2 콘솔/CLI 에서 key pair 생성 → AWS 가 그 자리에서 키 쌍 생성.
    • public = AWS 보관 (콘솔에서 다시 조회 가능).
    • private = 그 순간 1회만 다운로드. 다시는 못 받음.
  • 다운로드 포맷:
    • .pem = OpenSSH·표준 도구용. 텍스트 base64.
    • .ppk = PuTTY 전용.
  • .pem = 클라이언트 priv key. EC2 인스턴스 안 ~/.ssh/authorized_keys 가 대응 pub key.
  • pub 이 어떻게 거기 들어갔냐 = cloud-init 이 첫 부팅 시 EC2 IMDS(http://169.254.169.254/latest/meta-data/public-keys/) 에서 받아 적음. AWS 가 직접 SSH 로 넣는 게 아니라 인스턴스가 스스로 받아 적는 모델.
  • 흔한 함정
    • 퍼미션 느슨 → Permissions 0644 for 'mykey.pem' are too open.chmod 400 mykey.pem. 04. Linux Permissions 노트의 user 권한 규칙이 보안의 마지막 선.
    • 사용자 이름 틀림 — AL2023·Amazon Linux = ec2-user, Ubuntu = ubuntu, RHEL = ec2-user/root, Debian = admin/debian.
  • .pem 분실 시 회복 = (a) EC2 Instance Connect 임시 키 푸시, (b) SSM Session Manager 우회, (c) 인스턴스 stop → 루트 볼륨 다른 인스턴스에 붙여 authorized_keys 수정 → 다시 붙임.

요즘 추세 = .pem 자체를 안 쓰는 방향

  • EC2 Instance Connect = IAM 권한으로 임시 pub key(60초 유효) 푸시. 영구 키 분실·유출 리스크 줄임. 22 여전히 필요.
  • SSM Session Manager = SSH 포트 자체를 안 열고 SSM agent ↔ AWS 백본 ↔ 콘솔·aws ssm start-session. 요즘 production 베스트 프랙티스.
  • SSH CA 자체 운영 = Bastion + IAM 연동 CA + 짧게 유효한 user cert. Netflix BLESS, Teleport.

Lima 키 페어 해부 (실습)


Lima 가 첫 실행 시 호스트(맥) 에 키 쌍을 자동 생성하고 VM authorized_keys 에 pub 만 박는다 = 정확히 publickey 인증 모델.

VM 안의 pub key

root@lima-default:/home/meatsby.linux/.ssh# cat authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6xYhk4UCjq4ihWu6OLcHtdBmRQGXQcyQ5h8T6m+KiB lima

authorized_keys 한 줄 = <algorithm> <base64-pub-key> <comment>.

  • ssh-ed25519 = 알고리즘.
  • base64 는 length-prefixed wire 포맷:
    • AAAAC3 = 00 00 00 0B (이름 11 바이트)
    • NzaC1lZDI1NTE5 = ssh-ed25519
    • AAAAIE = 00 00 00 20 (키 32 바이트)
    • 그 뒤 32 바이트 = Ed25519 공개점
  • lima = comment. 라벨일 뿐, 검증에 안 씀.

호스트의 priv key

$ ls -al ~/.lima/_config/user*
-rw-------@ 1 meatsby  staff  387 Dec  1 22:02 /Users/meatsby/.lima/_config/user
-rw-r--r--@ 1 meatsby  staff   86 Dec  1 22:02 /Users/meatsby/.lima/_config/user.pub
 
$ cat ~/.lima/_config/user
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBOsWIZOFAo6uIoVruji3B7XQZkUBl0HMkOYfE+pviogQAAAIheOuQMXjrk
... (priv key 본문 truncate 노출 방지) ...
BmRQGXQcyQ5h8T6m+KiBAAAABGxpbWEB
-----END OPENSSH PRIVATE KEY-----
  • 권한 600 (-rw-------) = sshd 가 요구하는 최소 보호.
  • @ = 맥 확장속성(xattr). 무해.

priv key 포맷 — PEM 인데 종류가 다름

  • BEGIN RSA PRIVATE KEY = 옛 PKCS#1 (RSA 전용).
  • BEGIN PRIVATE KEY = PKCS#8 (알고리즘 무관 표준).
  • BEGIN OPENSSH PRIVATE KEY = OpenSSH 자체 포맷 (2014~). Ed25519 같은 신 알고리즘 지원, bcrypt 기반 passphrase 암호화 옵션.
  • AWS 가 주는 .pem 은 보통 PKCS#1. OpenSSH 가 둘 다 읽음.

passphrase 없음 priv 파일 첫 base64 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQ 디코드 → 매직 openssh-key-v1\0 다음에 none none = (cipher, KDF). 암호화 안 됨. 누구든 이 파일 읽으면 그대로 키 주인.

  • Lima 는 자동화를 위해 일부러 passphrase 없이 생성.
  • ssh-keygen -p -f keyfile 로 사후 추가 가능.

pub key 가 priv 파일 안에 들어있다 (원본 기준 3 번) 원본 priv 파일 base64 안에 E6xYhk4UCjq4ihWu6OLcHtdBmRQGXQcyQ5h8T6m+KiB (.pub 의 32 바이트) 가 세 군데 나타남:

  1. priv 파일의 pub 섹션 (위에 보이는 line 2 ...OsWIZOFAo...+pviogQ 부분).
  2. priv 섹션 헤더 안에 (중복 저장 — truncate 한 가운데).
  3. 진짜 비밀 부분의 끝 32 바이트 — Ed25519 가 빠른 서명을 위해 priv 64 바이트를 secret(32) || public(32) 형태로 보관 (truncate 한 가운데 + 위 마지막 줄 BmRQGXQcyQ5h8T6m+KiB 가 그 꼬리).

AAAABGxpbWEB = lima comment 가 priv 안에도 박힘 (마지막 줄).

매칭 검증

# 두 키가 같은 쌍이면 출력 없음 (단, -y 는 comment 누락 가능)
diff <(ssh-keygen -y -f ~/.lima/_config/user) ~/.lima/_config/user.pub
 
# fingerprint
ssh-keygen -lf ~/.lima/_config/user.pub
# 256 SHA256:xxxxx... lima (ED25519)

VM 안에서 ssh-keygen -lf /home/meatsby.linux/.ssh/authorized_keys 와 같은 fingerprint 나와야 정합.

실제 흐름 보기

ssh -F ~/.lima/default/ssh.config -v lima-default

-v 출력에서 6 단계가 한 화면에:

  • debug1: Local version string SSH-2.0-OpenSSH_... — 버전 교환
  • debug1: kex: algorithm: curve25519-sha256 — KEX
  • debug1: Server host key: ... — 서버 인증
  • debug1: Offering public key: ~/.lima/_config/user ED25519 SHA256:... — publickey 인증 시도
  • debug1: Authentication succeeded (publickey). — 통과

SSH 접속


ssh -i ssh-key-2023-08-09.key opc@192.0.2.231

SSH Config


~/.ssh/
├── config                ← SSH 설정 파일
├── id_rsa                ← 기본 개인 키 (기본값)
├── id_rsa.pub            ← 기본 공개 키
├── company_id_rsa        ← 회사용 개인 키
├── company_id_rsa.pub    ← 회사용 공개 키
├── personal_server_id    ← 개인 서버용 키
├── personal_server_id.pub
└── known_hosts           ← 접속한 호스트 정보 저장
# ~/.ssh/config

# 회사 서버
Host company-server
    HostName server.company.com
    User ec2-user
    IdentityFile ~/.ssh/company_id_rsa
    IdentitiesOnly yes

# 개인 서버
Host my-vps
    HostName 123.45.67.89
    User ubuntu
    IdentityFile ~/.ssh/personal_server_id
    IdentitiesOnly yes
# 개인 서버 접속
ssh my-vps

# 회사 서버 접속
ssh company-server

References