SSH의 비밀번호 인증은 평범한 서버가 가진 가장 큰 노출면입니다. 그것에 맞서서는 Fail2ban도 어떤 임계값도 도움이 되지 않습니다. 한 시간에 한 번씩 시도하는 천 개의 주소는 결코 한도에 닿지 않으며, 추측이 가능한 한 계속 추측합니다. 키는 그 가능성 자체를 없앱니다. hardening 목록 전체에서 상황을 질적으로 바꾸는 설정은 이것 하나뿐입니다.
두려운 것은 한 가지뿐입니다. 비밀번호를 끄고 자신이 밖에 남는 것. 아래는 그런 일이 일어날 수 없는 순서입니다.
키
서버가 아니라 자기 컴퓨터에서 만듭니다.
ssh-keygen -t ed25519 -C "work laptop"
RSA가 아니라 ed25519를. 더 짧고 빠르며 길이에 관한 문제도 없습니다. RSA는 아주 오래된 무언가에 닿아야 할 때만 필요합니다. 그때는 -t rsa -b 4096.
키에 암호구절을 설정할 가치가 있습니다. 키 파일은 노트북에서 도난당할 수 있고, 암호구절이 없으면 그것은 즉시 작동합니다. 매번 입력할 필요는 없습니다 — 그건 ssh-agent가 맡고, macOS와 대부분의 데스크톱 Linux에서는 이미 돌고 있습니다.
비밀번호가 아직 통하는 동안 서버로 복사하는 데는 명령 하나면 됩니다.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@203.0.113.25
ssh-copy-id가 없으면 .pub 파일의 내용을 서버의 ~/.ssh/authorized_keys에 한 줄로 덧붙입니다. 권한이 중요합니다. .ssh 디렉터리는 700, 파일은 600, 소유자는 해당 사용자. 다른 값이면 sshd는 조용히 파일 읽기를 거부하고, 그것이 「키가 안 된다」의 가장 흔한 원인입니다.
모든 것을 결정하는 확인
무언가를 끄기 전에, 첫 연결을 닫지 말고 두 번째를 여세요.
ssh -o PasswordAuthentication=no user@203.0.113.25
이 옵션은 클라이언트가 비밀번호로 되돌아가는 것을 금지합니다 — 즉 로그인이 됐다면 키로 된 것입니다. 이 명령이 통할 때까지 다음으로 넘어가지 마세요. 그리고 첫 연결은 끝까지 열어 두세요. 당신이 망가뜨릴 수도 있는 것을 고쳐 주는 것이 바로 그것입니다.
비밀번호 끄기, 그리고 눈에 띄지 않는 함정
설정은 패키지 업데이트가 덮어쓰지 못하도록 별도 파일에 둡니다. 다만 파일 이름이 중요합니다.
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf <<'EOF'
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LogLevel VERBOSE
EOF
요점은 OpenSSH가 매개변수에 대해 거의 모든 곳에서처럼 마지막 값이 아니라 처음 얻은 값을 쓴다는 것입니다. sshd_config.d의 파일은 알파벳순으로 읽히고, Ubuntu의 클라우드 이미지는 거기에 50-cloud-init.conf를 두는데 그 안에 PasswordAuthentication yes가 들어 있는 경우가 잦습니다. 그런 상황에서 90번을 붙인 당신의 파일은 아무것도 하지 못합니다. 설정은 된 것처럼 보이는데 비밀번호는 계속 통합니다. 숫자 10은 여기서 나옵니다 — 그쪽이 더 먼저 읽히니까요.
실제로 무엇이 적용됐는지는 추측하지 않고도 볼 수 있습니다.
sudo sshd -T | grep -Ei 'passwordauth|permitrootlogin|pubkeyauth'
이 명령은 모든 include를 거친 뒤의 실효 설정을 출력합니다. 파일의 내용이 아니라 이것을 믿으세요.
그다음은 문법 확인과 서비스의 부드러운 재적용입니다.
sudo sshd -t && sudo systemctl reload ssh
sshd -t는 필수입니다. 설정의 오타에 restart가 겹치면 서비스는 죽고 당신은 밖에 남습니다. reload는 열려 있는 세션도 끊지 않습니다.
Ubuntu 24.04: 포트는 당신이 생각하는 곳에 없다
최근 Ubuntu에는 별도의 함정이 있습니다. 거기서 sshd는 systemd 소켓을 통해 시작됩니다. 그 모드에서는 sshd_config의 Port 줄이 아무 일도 하지 않습니다 — 대기하는 것은 데몬이 아니라 소켓입니다. 포트를 바꾼다면 고쳐야 할 것은 이것입니다.
sudo systemctl edit ssh.socket
그리고 ListenStream=(빈 값이 이전 값을 초기화합니다)에 새 포트를 적어야 합니다. 이를 드러내는 증상은, 설정을 바꾸고 서비스를 재시작했는데 서버가 여전히 22에서 응답하는 것입니다.
포트 변경 이야기가 나온 김에: 방어책으로서 그것은 작동하지 않습니다. 스캐너는 어떤 포트에서든 몇 분이면 서비스를 찾아냅니다. 유일한 효과는 로그가 조용해지는 것입니다. 대량 추측은 22로만 오니까요. 편리하긴 하지만 안전성과 혼동하지 마세요.
애초에 누가 로그인해도 되는가
유용한 보완은 명시적인 목록입니다.
AllowUsers deploy admin
목록에 없는 것은 키가 검사되기도 전에 잘려 나갑니다. 이는 어떤 패키지가 셸과 홈 디렉터리를 가진 시스템 사용자를 만들어 버리는 경우도 덤으로 덮습니다.
예비 출입구
노트북 한 대의 키 하나는 단일 장애점입니다. 디스크가 죽고 노트북을 잃으면 서버는 영원히 닿을 수 없게 됩니다. 합리적인 최소한은:
authorized_keys에 다른 기기의 두 번째 키;- 비밀 키의 사본을 비밀번호 관리자나 암호화된 USB에;
- 시험해 본 업체의 콘솔 접근 — VNC 또는 시리얼. 시험해 봤다는 것은 적어도 한 번은 그것으로 로그인해 봤다는 뜻이지, 「패널 어딘가에 버튼이 있다」가 아닙니다.
그 김에 authorized_keys에 이미 무엇이 있는지도 보세요 — 자기 사용자의 것도, root의 것도. 거기 놓인 남의 키는 어떤 비밀번호 변경도 견디고 유효한 접근 권한으로 남습니다.
그 이후
비밀번호 추측은 사라지지 않고 그저 쓸모없어질 뿐입니다. 로그에는 Failed password 줄이 수천 개 계속 남지만 그중 어느 것도 성공으로 끝날 수 없습니다. 이제 볼 가치가 있는 것은 성공한 로그인입니다. 어떤 주소에서, 어떤 사용자로, 몇 시에. 낯선 주소에서 키로 성공한 로그인은 백만 번의 실패보다 훨씬 중요한 사건이며, 전체 흐름 속에서는 훨씬 알아차리기 어렵습니다. 아래 데모 페이지가 보여 주는 것이 바로 그 그림입니다.