SSH Permission denied (publickey), 개인 키 문제인가 서버 문제인가
개인 키 권한이 틀려도, 서버의 authorized_keys 권한이 틀려도 SSH 클라이언트에 남는 마지막 줄은 똑같이 Permission denied (publickey)입니다. EC2 접속이 이 메시지로 끝나면 키를 교체할지 서버 설정을 수정할지부터 고민하게 됩니다. 운영 서버의 SSH 접속 장애에 대응할 절차를 정리하려고, Debian 13에 띄운 별도 sshd에 같은 접속 명령을 보내면서 개인 키 id_demo와 서버 authorized_keys의 권한을 하나씩 바꿨습니다. 아래 출력과 캡처에는 두 경우의 인증 실패와, 권한을 되돌린 뒤 connected as box로 복구되는 장면이 같은 실행으로 남아 있습니다. 이번 재현에서는 UNPROTECTED PRIVATE KEY FILE 경고와 서버 로그를 나란히 읽어 실패 위치를 구분합니다. 같은 키와 접속 대상을 유지한 채 권한을 600으로 고쳐 성공하는 과정을 보면, 장애 때 먼저 확인할 파일을 정하기 수월합니다.
시험한 클라이언트와 sshd
인증 실패를 비교하기 위해 접속 대상과 설정을 고정합니다. 아래 표는 재현에 사용한 도구와 파일 위치를 정리한 내용입니다. 접속 대상은 127.0.0.1:2222이며, 이후 출력은 이 전용 서버에 대한 인증 결과로 읽습니다.
| 항목 | 테스트 환경 |
|---|---|
| OS | Debian GNU/Linux 13 (trixie) |
| 주요 도구 버전 | OpenSSH_10.0p2 Debian-7+deb13u4, OpenSSL 3.5.7 |
| 확인일 | 2026-10-15 |
| 접속 대상 | 일반 사용자 권한의 별도 sshd, 127.0.0.1:2222 |
| 실습 파일 | sshd/의 전용 설정·호스트 키·로그, authorized_keys |
운영 서버의 설정과 실습 조건을 섞지 않도록 별도 sshd를 사용합니다. 시스템 sshd와 ~/.ssh는 변경하지 않고, 전용 sshd_config에서 AuthorizedKeysFile을 authorized_keys로 지정합니다. 실습용 개인 키는 ssh-keygen -t ed25519로 새로 생성한 데모 키를 사용하며, 이어지는 권한 변경과 접속 확인에서는 해당 키를 사용합니다.
EC2 같은 클라우드 서버에 접속할 때도 .pem 키를 받은 직후 같은 메시지가 나오기도 합니다. 다만 여기서 다루는 실행 결과는 로컬 sshd에 접속한 기록으로 한정합니다. 클라우드 환경의 접속 문제를 함께 볼 때는 AWS EC2 접속 문제 해결 문서 (확인일 2026-10-15)를 참고합니다.
개인 키 644와 authorized_keys 666을 각각 재현하기
재현 조건과 실행 명령
전용 서버는 PasswordAuthentication no, UsePAM no, StrictModes yes, LogLevel VERBOSE로 구성합니다. 비밀번호 인증으로 넘어가서 결과를 혼동하지 않도록 하고, 서버 쪽 파일 권한 때문에 인증이 거절되는 이유를 로그에서 확인합니다. 다음은 common.sh의 핵심 설정으로, $R은 실습 디렉터리, $S는 그 아래 sshd 디렉터리를 가리킵니다. 설정 파일을 만드는 셸 구간에서 경로 변수를 확장해 사용합니다.
Port 2222
ListenAddress 127.0.0.1
HostKey $S/host_ed25519
PidFile $S/sshd.pid
AuthorizedKeysFile $R/authorized_keys
PasswordAuthentication no
UsePAM no
StrictModes yes
LogLevel VERBOSE
캡처의 $SSH는 접속 옵션을 묶은 셸 변수입니다. 아래 정의와 함께 -i id_demo로 개인 키를 지정합니다. UserKnownHostsFile도 실습 디렉터리의 파일을 사용하므로 접속 기록을 분리해 관리합니다. 이 옵션 묶음은 로컬 재현을 위한 설정으로 사용합니다.
SSH="ssh -p 2222 -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=$R/known_hosts -o IdentitiesOnly=yes"
재현 스크립트에서는 데모 키를 생성하고 공개 키를 authorized_keys에 복사한 다음, 서버 쪽 파일 권한을 600으로 맞춥니다. 그 상태에서 클라이언트 개인 키 id_demo만 644로 바꾸어 접속을 시도합니다. 서버 쪽 조건을 먼저 정리해 두면 실패 메시지를 읽을 때 개인 키 권한 변경의 영향을 구분하기 수월합니다.
ssh-keygen -q -t ed25519 -N "" -f id_demo
cp id_demo.pub authorized_keys
chmod 600 authorized_keys
chmod 644 id_demo
아래 출력은 서버의 공개 키 파일을 600으로 둔 채 개인 키만 644로 바꾼 결과입니다. 버전 표시 다음에 파일 권한과 접속 실패가 차례로 찍히니, 실행 환경과 실패 조건이 한 화면에서 맞물립니다. 마지막 종료 값만 읽지 말고, 개인 키를 무시한다는 경고가 그보다 먼저 나오는지 봅니다.
$ ssh -V
OpenSSH_10.0p2 Debian-7+deb13u4, OpenSSL 3.5.7 9 Jun 2026
$ chmod 644 id_demo && ls -l id_demo
-rw-r--r-- 1 box box 419 Oct 15 23:34 id_demo
$ $SSH -i id_demo [email protected] "echo connected as \$(whoami)"; echo "ssh_exit=$?"
Warning: Permanently added '[127.0.0.1]:2222' (ED25519) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'id_demo' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "id_demo": bad permissions
[email protected]: Permission denied (publickey).
ssh_exit=255
다음 캡처는 방금 제시한 출력과 같은 실행을 터미널에서 확인한 화면입니다. 개인 키 권한이 0644일 때 bad permissions가 표시되고 SSH 종료 값은 255입니다. 파일이 존재하더라도 클라이언트가 그 키를 사용하지 않으면 인증에 실패한다는 점이 드러납니다.

실제 출력에서 눈여겨볼 곳은 마지막 Permission denied (publickey)보다 앞에 나오는 경고입니다. Permissions 0644와 This private key will be ignored.가 이어지고, Load key “id_demo”: bad permissions가 표시됩니다. 클라이언트가 개인 키의 권한을 문제로 판단해 그 키를 쓰지 않는 단계에서 실패하는 것입니다. 그래서 이 재현에서는 서버가 인증에 쓸 키를 받지 못하고, 접속 명령은 ssh_exit=255로 끝납니다.
클라이언트 개인 키를 600으로
UNPROTECTED PRIVATE KEY FILE 경고가 보이면 -i로 지정한 개인 키 파일을 먼저 확인합니다. 해결 스크립트에서는 같은 id_demo에 chmod 600을 적용하고 ls -l로 권한 표시를 확인한 뒤 같은 대상으로 재접속합니다. 파일명만 비슷한 다른 키에 적용하지 않도록 실제 접속 명령의 경로와 수정할 경로를 맞춥니다.
chmod 600 id_demo
ls -l id_demo
아래 출력에서는 개인 키의 권한을 수정한 뒤 같은 키로 다시 접속합니다. ls -l의 -rw——- 표시는 앞서 보인 -rw-r–r–와 견줄 지점입니다. 권한 표시와 원격 명령의 응답을 이어서 읽으면, 파일 수정이 실제 인증 복구로 이어졌는지가 드러납니다.
$ chmod 600 id_demo && ls -l id_demo
-rw------- 1 box box 419 Oct 15 23:34 id_demo
$ $SSH -i id_demo [email protected] "echo connected as \$(whoami)"; echo "ssh_exit=$?"
connected as box
ssh_exit=0
다음 화면에서는 개인 키 경고 대신 connected as box가 나타납니다. SSH 종료 값도 0으로 바뀌어 같은 접속 대상에서 명령 실행까지 성공했습니다. 서버의 공개 키 파일은 앞서 정한 상태를 유지했으므로, 이번 비교에서 주목할 변경은 개인 키의 권한입니다.

권한 표시가 -rw——-로 바뀐 뒤 connected as box와 ssh_exit=0을 확인합니다. 이 결과로 개인 키 권한 수정이 이번 클라이언트 경고를 해소했고, 해당 실습 조건에서 인증까지 성공했음을 판단합니다. 권한 변경 명령만 끝났다는 사실과 SSH 접속에 성공했다는 사실은 구분해서 확인합니다. 운영 작업 기록에도 파일 권한과 접속 결과를 함께 남겨 다음 담당자가 복구 상태를 판단할 수 있게 합니다.
- 장점: 개인 키 파일의 권한을 수정하는 단계로 경고의 원인에 직접 대응합니다. 같은 접속 명령으로 성공 여부를 비교하기 쉽습니다.
- 단점: 서버 쪽 authorized_keys 권한이 문제인 경우에는 개인 키 권한 변경만으로 해결하지 못합니다. 마지막 접속 거부 메시지만 보고 적용하면 원인을 놓칠 수 있습니다.
서버 로그가 가리키는 authorized_keys 권한 고치기
점검 스크립트에서는 개인 키를 수정한 상태에서 서버 쪽 authorized_keys를 666으로 바꿉니다. 이번에도 클라이언트에는 Permission denied (publickey)가 표시되지만, 앞서 보였던 개인 키 경고는 나타나지 않습니다. 이어서 서버 로그의 Authentication refused 항목을 확인하고 authorized_keys를 600으로 되돌려 재접속합니다.
$ chmod 666 authorized_keys && ls -l authorized_keys
-rw-rw-rw- 1 box box 107 Oct 15 23:34 authorized_keys
$ $SSH -i id_demo [email protected] "echo connected"; echo "ssh_exit=$?"
[email protected]: Permission denied (publickey).
ssh_exit=255
$ grep -o "Authentication refused.*" $S/sshd.log | tail -1
Authentication refused: bad ownership or modes for file /home/user/ssh-key/authorized_keys
$ chmod 600 authorized_keys && $SSH -i id_demo [email protected] "echo connected as \$(whoami)"; echo "ssh_exit=$?"
connected as box
ssh_exit=0
다음 캡처에는 서버 공개 키 파일의 권한을 바꾸기 전후의 접속 결과가 함께 담겨 있습니다. authorized_keys가 666일 때 종료 값은 255이고, 600으로 복구한 뒤에는 0입니다. 중간에 찍힌 서버 로그가 파일 경로를 지목하므로, 클라이언트의 마지막 오류 문구만 볼 때보다 조치 대상이 분명해집니다.

sshd 로그 캡처의 authorized_keys 경로는 실험용 폴더 그대로이고, 본문은 그 앞부분을 /home/user/ssh-key로 줄였습니다. 거부 사유 문구는 같습니다.
서버 로그에는 Authentication refused: bad ownership or modes for file과 함께 실제 authorized_keys 경로가 기록됩니다. 이 실습에서는 StrictModes가 켜진 상태에서 해당 파일을 666으로 변경한 것이 거부 원인입니다. 로그 문구에 ownership이 들어 있어도, 이번 기록에서 바꾸고 되돌린 대상은 파일 권한입니다. 그러니 소유자 변경 결과는 추정하지 않고, 권한 수정과 접속 성공만 이어서 판단합니다.
chmod 600 authorized_keys
수정 대상은 서버 설정의 AuthorizedKeysFile이 가리키는 파일입니다. 여기서는 authorized_keys이므로 습관적으로 ~/.ssh의 파일을 바꾸는 대신 로그에 나온 경로와 설정을 대조합니다. 수정 후 출력의 connected as box와 ssh_exit=0을 함께 확인합니다. StrictModes 설정을 바꾸는 대신 거부 원인이 된 파일 권한을 바로잡는 흐름을 유지합니다.
- 장점: 서버가 남긴 거부 사유와 실제 파일 경로를 기준으로 조치합니다. 개인 키 권한이 이미 정상인 경우에도 다른 실패 지점을 구분할 수 있습니다.
- 단점: 서버 로그와 해당 파일에 접근할 수 있어야 합니다. 클라이언트에 표시된 Permission denied (publickey)만으로는 이 원인을 확정하기 어렵습니다.
경고가 시작되는 줄부터 읽기
접속 실패를 조사할 때는 출력의 마지막 줄부터 고치려 하지 말고 경고가 시작되는 부분을 읽습니다. 개인 키를 무시한다는 문장이 있으면 클라이언트 파일을, 그 경고 없이 접속이 거절되면 서버 로그를 봅니다. 같은 메시지라도 실패 지점이 다르다는 점을 작업 기록에 남겨 두면, 이미 고친 개인 키에 같은 조치를 되풀이할 일이 줄어듭니다.
어떤 키를 시도하는지 살펴볼 때는 ssh -v 옵션을 활용합니다. 여기서는 옵션의 용도를 소개하며, 위 캡처는 상세 출력 옵션을 추가한 결과로 해석하지 않습니다. 실습에 사용한 -o IdentitiesOnly=yes는 지정한 키만 시도하도록 하고, BatchMode=yes는 비밀번호 프롬프트 없이 실패하게 합니다. 옵션을 확인할 때는 ssh 매뉴얼 (확인일 2026-10-15)을 참고합니다.
서버 설정에서는 AuthorizedKeysFile과 StrictModes를 함께 확인합니다. 파일 이름이 authorized_keys로 같더라도 서버가 읽는 위치와 수정한 위치가 다르면, 이번 해결 절차를 그대로 적용했다고 보기 어렵습니다. 전용 설정 파일과 서버 로그의 경로를 대조하는 이유도 여기에 있습니다. 설정 항목은 sshd_config 매뉴얼 (확인일 2026-10-15)을 기준으로 봤습니다.
재현을 다시 구성할 때는 키 생성 단계와 권한 수정 단계를 구분합니다. 재현 스크립트의 키 생성 명령은 데모 환경을 만드는 절차이고, 해결 스크립트의 해결 절차는 기존 실습 키의 권한을 수정하는 과정입니다. 접속 실패가 발생했다고 해서 키를 다시 만드는 단계부터 반복하면 무엇이 달라져 성공했는지 비교하기 어려워집니다. 키 생성 명령의 옵션은 Debian trixie ssh-keygen 매뉴얼 (확인일 2026-10-15)을 참고합니다.
권한을 고친 뒤에도 남는 경로 문제
버전이나 설정을 갱신할 때는 접속 점검에 쓰는 키 경로와 서버의 AuthorizedKeysFile 경로도 함께 대조합니다. 이 실행 기록에서는 파일 권한을 바로잡은 뒤 접속이 복구되었으므로, 오류의 원인은 경고와 서버 로그를 기준으로 판단합니다. 운영 헬스체크가 SSH 인증까지 확인하는 구성이라면 실제 접속 명령의 키 경로와 서버 설정이 서로 맞는지도 점검 항목에 넣는 편이 좋습니다. 설정 파일을 옮긴 뒤 점검만 예전 경로를 참조하면 서비스 상태와 관리 접속 상태를 잘못 연결해 해석할 수 있으므로, 변경 기록에는 점검 대상 경로도 함께 남깁니다.
실패 위치별로 손댈 곳
아래 표는 같은 접속 거부 메시지로 끝난 실패를 파일 위치에 따라 비교한 내용입니다. 개인 키 문제는 클라이언트 경고가 단서이고, authorized_keys 문제는 서버 로그가 원인 파일을 알려 줍니다. 양쪽 모두 권한을 복구한 뒤 접속에 성공했지만, 수정할 파일을 고르는 근거는 서로 다릅니다.
| 구분 | 클라이언트 개인 키 권한 문제 | 서버 authorized_keys 권한 문제 |
|---|---|---|
| 재현 조건 | id_demo 권한 0644 | authorized_keys 권한 666, StrictModes yes |
| 클라이언트 단서 | UNPROTECTED PRIVATE KEY FILE 및 bad permissions | Permission denied (publickey) |
| 서버 로그 확인 | 클라이언트의 키 무시 경고를 먼저 확인합니다. | Authentication refused: bad ownership or modes를 확인합니다. |
| 수정 명령 | chmod 600 id_demo | chmod 600 authorized_keys |
| 실행 결과 | connected as box, ssh_exit=0 | connected as box, ssh_exit=0 |
경고가 어디에 찍혔는지에 따라 손댈 곳이 갈립니다. 개인 키가 너무 개방되어 있다는 경고가 있으면 클라이언트에서 -i로 지정한 파일을 확인하고 600으로 고칩니다. 개인 키 경고가 사라졌는데도 접속이 거절되면 서버 로그를 확인하고 AuthorizedKeysFile 경로의 권한을 대조합니다. 로컬 재현과 클라우드 접속은 환경을 구분하되, 경고문과 서버 로그로 실패 위치를 나눈 뒤 같은 접속 명령으로 성공을 확인하는 순서는 같습니다.
이번 재현에서 Permission denied (publickey)만 보고 키를 교체할 이유는 없었습니다. 저는 개인 키 경고를 먼저 읽고 서버 로그로 넘어가는 순서를 운영 절차로 추천합니다. 개인 키와 서버 공개 키 파일을 각각 600으로 관리하고, AuthorizedKeysFile이 가리키는 위치를 작업 기록에 남기는 구성이 이번 결과와 잘 맞습니다. 실시간 서비스의 관리 접속을 점검할 때도 SSH 인증 실패와 애플리케이션의 응답 상태를 따로 기록해야 복구 대상을 잘못 잡지 않습니다. 자동 점검에서는 권한 변경 성공만으로 작업을 끝내지 말고, 같은 접속 조건에서 원격 명령의 응답과 종료 값까지 확인하는 단계를 둡니다.






