리눅스 ssh-copy-id 사용법 - 공개키 등록과 authorized_keys 관리
이 명령어를 언제 쓰는가
ssh-keygen으로 키 쌍을 만들었다면 다음 할 일은 공개키를 서버에 등록하는 것이다. 서버의 ~/.ssh/authorized_keys 파일에 공개키 한 줄을 추가하면 그때부터 비밀번호 없이 접속된다.
손으로 해도 되지만 실수가 잦다. 디렉터리 권한을 잘못 주거나, >>를 >로 잘못 써서 남의 키를 다 날리거나, 개행이 빠져 두 키가 한 줄로 붙어 버리는 일이 흔하다. ssh-copy-id는 이 과정을 대신해 준다. 디렉터리 생성, 권한 설정, 이어쓰기, 중복 확인을 알아서 처리한다.
기본 형식
ssh-copy-id -i ~/.ssh/id_ed25519.pub 사용자@호스트
- -i — 등록할 공개키 파일. 반드시
.pub가 붙은 쪽을 지정한다. - 사용자@호스트 — 접속할 계정. 이 계정의 홈 디렉터리 아래
.ssh/authorized_keys에 들어간다.
이때는 아직 키 인증이 안 되므로 비밀번호를 한 번 입력해야 한다. 즉 비밀번호 인증이 켜져 있는 동안에만 쓸 수 있다.
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@10.0.1.20
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'deploy@10.0.1.20'"
and check to make sure that only the key(s) you wanted were added.
자주 쓰는 옵션
| 옵션 | 의미 | 예시 |
|---|---|---|
-i 파일 | 등록할 공개키를 명시. 사실상 필수 | -i ~/.ssh/id_ed25519.pub |
-p 포트 | SSH 포트가 22가 아닐 때 | -p 2222 |
-n | 드라이런. 실제로 넣지 않고 넣을 키만 보여준다 | ssh-copy-id -n -i key.pub host |
-f | 이미 등록됐는지 확인하지 않고 무조건 추가 | 중복 등록 위험이 있으니 신중히 |
-o 옵션 | ssh 옵션을 그대로 전달 | -o ProxyJump=bastion |
-s | 원격 명령 실행 대신 sftp로 처리 | 셸이 제한된 서버에서 |
실전 예제
1) 배포 계정에 공개키를 등록한다. 신규 서버를 받아 처음 하는 작업이다.
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@10.0.1.20
ssh deploy@10.0.1.20 'hostname; id'
두 번째 줄이 비밀번호를 묻지 않고 바로 결과를 내면 성공이다.
2) 무엇이 등록될지 먼저 확인한다. 운영 서버라면 드라이런을 먼저 돌리는 습관이 안전하다.
ssh-copy-id -n -i ~/.ssh/id_ed25519.pub deploy@10.0.1.20
3) 비표준 포트와 점프 호스트를 거친다. 배스천 뒤에 있는 내부 서버에 등록할 때다.
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub -p 2222 \
-o ProxyJump=hscho@bastion.example.com deploy@10.0.1.20
4) 여러 서버에 한 번에 등록한다. 서버가 늘어날 때 반복 작업을 줄인다.
for h in 10.0.1.20 10.0.1.21 10.0.1.22; do
echo "--- $h ---"
ssh-copy-id -i ~/.ssh/id_ed25519.pub "deploy@$h"
done
서버 수가 많고 반복될 작업이라면 Ansible의 authorized_key 모듈처럼 상태를 선언하는 도구로 넘어가는 것이 낫다. ssh-copy-id는 등록만 할 뿐 삭제를 못 하기 때문이다.
5) ssh-copy-id가 없는 환경에서 손으로 등록한다. 일부 최소 설치 이미지나 오래된 상용 유닉스에는 이 명령이 없다. 이 한 줄이 ssh-copy-id가 하는 일과 같다.
cat ~/.ssh/id_ed25519.pub | ssh deploy@10.0.1.20 \
'umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
>>가 두 개인지 반드시 확인한다. 하나면 기존 키를 전부 덮어써서 다른 사람의 접속을 끊어 버린다.
6) 등록이 끝나면 비밀번호 인증을 끈다. 키 인증을 켜는 진짜 목적이 이것이다.
sudo vi /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
sudo sshd -t && sudo systemctl reload sshd
sshd -t로 문법 검사를 먼저 하는 것이 핵심이다. 그리고 지금 열려 있는 세션은 그대로 둔 채 새 터미널에서 접속을 확인한다. 이 순서를 지키지 않아 자기 자신을 서버 밖에 가두는 사고가 정말 흔하다.
함정과 주의점
-i를 생략하면 원하지 않는 키까지 등록된다. 가장 자주 겪는 사고다. -i 없이 실행하면 ssh-copy-id는 ssh-agent에 올라와 있는 키를 전부, 에이전트가 없으면 ~/.ssh/의 기본 키 파일들을 대상으로 삼는다. 예전에 만들어 잊고 있던 키나 다른 프로젝트의 키가 운영 서버 authorized_keys에 조용히 추가된다. 나중에 그 키가 유출되면 누구도 이 서버가 노출됐다는 사실을 모른다. 항상 -i로 하나만 지정한다. 등록 후에는 안내 문구대로 실제로 확인한다.
ssh deploy@10.0.1.20 'cat ~/.ssh/authorized_keys'
권한이 조금이라도 느슨하면 키 인증이 조용히 실패한다. sshd의 StrictModes가 기본으로 켜져 있어서, 소유자 외에 쓰기 권한이 있으면 authorized_keys를 아예 읽지 않는다. 그런데 클라이언트에는 그냥 비밀번호를 다시 묻는 것으로만 보여서 원인을 찾기가 어렵다. 필요한 권한은 이렇다.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 755 ~ # 홈 디렉터리에 그룹/기타 쓰기 권한이 있으면 안 된다
홈 디렉터리가 775거나 소유자가 다른 계정이면 .ssh 권한이 맞아도 거부된다. 원인은 서버의 로그에 정확히 남는다. Ubuntu·Debian은 /var/log/auth.log, RHEL 계열은 /var/log/secure를 본다.
sudo journalctl -u sshd -n 50
# Authentication refused: bad ownership or modes for directory /home/deploy
RHEL 계열에서는 SELinux 컨텍스트도 확인한다. .ssh 디렉터리를 손으로 만들거나 다른 곳에서 복사해 오면 SELinux 레이블이 ssh_home_t가 아니라 다른 값이 되어, 권한이 완벽해도 인증이 거부된다. 로그에는 권한 이야기가 안 나와서 더 헷갈린다.
ls -lZ ~/.ssh
restorecon -R -v ~/.ssh
ssh-copy-id는 등록만 하고 삭제는 못 한다. 퇴사자 키나 폐기한 CI 러너 키를 지우는 기능이 없다. authorized_keys가 몇 년째 쌓이기만 하는 서버가 흔한 이유다. 정리는 손으로 하거나 구성 관리 도구로 상태를 선언해야 한다. 지울 때는 반드시 백업을 뜨고, 지문으로 대상을 확인한 뒤 지운다.
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak.$(date +%Y%m%d)
while read -r line; do
[ -n "$line" ] && echo "$line" | ssh-keygen -lf -
done < ~/.ssh/authorized_keys
공개키 대신 개인키를 지정하면 안 된다. -i ~/.ssh/id_ed25519처럼 .pub를 빼고 써도 ssh-copy-id가 알아서 공개키를 찾아 주긴 하지만, 이 습관이 다른 도구에서는 개인키를 그대로 올리는 사고로 이어진다. 등록 대상은 언제나 .pub다.
CI 계정 키에는 제한을 걸어라. 사람이 아닌 자동화 계정의 키는 패스프레이즈를 걸 수 없다. 대신 authorized_keys 항목 앞에 제한 옵션을 붙여 그 키로 할 수 있는 일을 좁힌다.
from="10.0.5.0/24",command="/usr/local/bin/deploy-only.sh",no-agent-forwarding,no-port-forwarding,no-pty ssh-ed25519 AAAAC3Nz... deploy@ci-runner
이렇게 하면 키가 유출돼도 지정한 대역에서 지정한 스크립트만 실행할 수 있다. 대화형 셸이 열리지 않는다.
함께 보면 좋은 명령어
- ssh-keygen — 등록할 키를 만드는 앞 단계.
-lf로 authorized_keys의 지문을 확인할 때도 쓴다. - ssh — 등록 후 반드시 새 세션으로 접속을 확인한다. 안 되면
ssh -vvv로 어느 단계에서 막히는지 본다. - systemctl — sshd 설정을 바꾼 뒤
sshd -t로 검사하고systemctl reload sshd로 반영한다. restart보다 reload가 안전하다.