WireGuard + 공개키 + 2FA로 외부 SSH 접근 안전하게 구성하기
이 글에서 다루는 내용
외부 사용자에게 서버 접근 권한을 줄 때 SSH 포트를 그냥 열어두는 경우가 많다. 하지만 이는 생각보다 훨씬 위험한 방식이다. 이 글에서는 WireGuard VPN + SSH + 공개키 + 2FA를 조합한 안전한 외부 접근 구성 방법을 정리한다.
SSH 포트 직접 개방의 문제점
공유기에서 SSH 포트(22)를 직접 열면 인터넷 전체에 서비스가 노출된다. 포트를 열고 수분 내에 전 세계 봇이 자동으로 브루트포스 공격을 시작한다.
# 실제 auth.log 예시
Failed password for root from 185.234.x.x
Failed password for admin from 45.142.x.x
Failed password for ubuntu from 103.99.x.x
# 하루에 수천~수만 건
주요 문제점은 다음과 같다.
- 공격 표면 노출 — 봇넷이 24시간 브루트포스 및 스캐닝 시도
- 취약점 리스크 — OpenSSH 취약점 발생 시 즉시 공격 대상
- 포트 스캐닝 — nmap 등으로 누구나 열린 포트 탐지 가능
WireGuard가 더 안전한 이유
WireGuard는 올바른 키 없이 패킷을 보내면 서버가 아무 응답도 하지 않는다. 포트가 열려 있는지조차 알 수 없는 스텔스 방식이다.
| 항목 | SSH 포트 직접 개방 | WireGuard VPN |
|---|---|---|
| 공격 표면 | 큼 (전체 인터넷 노출) | 작음 (UDP 무응답 스텔스) |
| 인증 방식 | 비밀번호 or 키 | 공개키 only |
| 포트 스캐닝 | 탐지됨 | 응답 없음 (존재 자체가 안 보임) |
| 코드 크기 | 수십만 줄 | 약 4,000줄 |
| 브루트포스 | 가능 | 구조적으로 불가 |
권장 구성 전체 흐름
외부 사용자
↓ WireGuard 연결 (공개키 인증) ← 1차 관문
공유기 VPN
↓ AllowedIPs = 192.168.1.100/32 (특정 장비만)
서버 A ← SSH 접속 가능
서버 B, NAS, PC 등 ← 아예 안 보임
↓ SSH 공개키 인증 ← 2차 관문
↓ OTP 2FA 인증 ← 3차 관문
서버 접근 완료
보안 레이어를 정리하면 아래와 같다.
- WireGuard — 네트워크 접근 자체를 차단
- AllowedIPs 제한 — 특정 서버만 보이도록 격리
- SSH 공개키 — 비밀번호 탈취 방어
- 2FA OTP — 공개키 탈취 시 추가 방어
WireGuard 설정
공유기 Peer 설정 (사용자별 접근 제한)
[Peer]
PublicKey = 외부사용자_공개키
AllowedIPs = 192.168.1.100/32 # 딱 이 서버만 접근 허용
외부 사용자에게 전달할 wg0.conf
[Interface]
PrivateKey = 사용자_개인키
Address = 10.0.0.2/24
[Peer]
PublicKey = 공유기_공개키
Endpoint = 공인IP:51820
AllowedIPs = 192.168.1.100/32
사용자는 이 파일을 WireGuard 앱에 import하고 토글 ON만 하면 연결된다.
플랫폼별 WireGuard 클라이언트
| 플랫폼 | 설치 방법 |
|---|---|
| macOS | App Store에서 "WireGuard" 검색 |
| Windows | wireguard.com에서 installer 다운로드 |
| iOS | App Store |
| Android | Play Store |
| Linux | apt install wireguard |
SSH 설정
# /etc/ssh/sshd_config
AllowUsers 외부사용자계정
PermitRootLogin no
PasswordAuthentication no
Port 2222 # 기본 포트 변경 권장
비밀번호 대신 공개키 인증을 사용해야 하는 이유
비밀번호는 로그인할 때마다 네트워크로 전송되지만, 공개키는 개인키가 절대 기기 밖으로 나가지 않는다. 수학적으로 서명값만 주고받기 때문에 탈취 자체가 구조적으로 어렵다.
비밀번호 방식: 클라이언트 → [비밀번호 전송] → 서버
공개키 방식: 클라이언트 → [서명값 전송] → 서버 (개인키는 이동 안 함)
| 항목 | 비밀번호 | 공개키 |
|---|---|---|
| 브루트포스 | 가능 | 구조적으로 불가 |
| 탈취 가능성 | 피싱/키로거로 탈취 가능 | 개인키 파일 자체를 훔쳐야 함 |
| 네트워크 전송 | 매번 전송됨 | 전송 안 됨 |
| 복잡도 관리 | 사람이 기억해야 함 | 수학적으로 복잡도 보장 |
| 키 길이 | 보통 8~20자 | 2048~4096bit |
공개키 생성 및 등록
# 클라이언트에서 키 생성 (ed25519 권장)
ssh-keygen -t ed25519 -C "your_email"
# ~/.ssh/id_ed25519 ← 개인키 (절대 공유 금지)
# ~/.ssh/id_ed25519.pub ← 공개키 (서버에 등록)
# 서버에 공개키 등록
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@192.168.1.100
# 또는 수동으로 등록
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
비밀번호 로그인 비활성화
공개키 등록 후 반드시 비밀번호 로그인을 비활성화해야 한다.
# /etc/ssh/sshd_config
PasswordAuthentication no
# 재시작
systemctl restart sshd
2FA 설정 (Google Authenticator)
WireGuard 자체에는 2FA를 적용할 수 없다. 공개키 기반이라 키가 있으면 연결되고 없으면 차단되는 이분법적 구조이기 때문이다. 2FA는 SSH 로그인 단계에 적용한다.
설치
apt install libpam-google-authenticator
사용자 설정
google-authenticator
# 질문들에 y 입력
# QR코드 출력 → Google Authenticator 앱으로 스캔
PAM 설정
# /etc/pam.d/sshd 맨 위에 추가
auth required pam_google_authenticator.so
sshd_config 수정
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# 재시작
systemctl restart sshd
적용 후 로그인 흐름
$ ssh enes@192.168.1.100
# SSH 공개키 자동 인증
# ↓
Verification code: ______ ← Google Authenticator 앱에서 6자리 입력
# ↓
접속 완료
2FA 앱은 Google Authenticator보다 Authy를 추천한다. 기기 교체 시 백업이 가능해서 실용적으로 더 편리하다.
외부 사용자에게 전달할 정보 정리
WireGuard 설정 파일 : wg0.conf
SSH 접속 명령어 : ssh -p 2222 user@192.168.1.100
OTP 앱 : Google Authenticator 또는 Authy
권한 회수 방법
사용자 접근을 차단하고 싶을 때는 공유기에서 해당 Peer만 삭제하면 즉시 내부망 진입이 불가능해진다. 별도로 SSH 계정을 삭제할 필요도 없다.
보안 수준별 구성 추천
| 구성 | 대상 |
|---|---|
| WireGuard + SSH 비밀번호 | 신뢰할 수 있는 외부인 |
| WireGuard + SSH 공개키 | 외부인 접근 기본 권장 ✅ |
| WireGuard + SSH 공개키 + 2FA | 중요 서버, 팀 운영 ✅✅ |
| WireGuard + SSH 공개키 + 2FA + IP 제한 | 금융/의료급 |