OpenSSH

SSH 키와 중계 서버 설정을 확인합니다

접속 경로, 사용자 인증, 서버의 신원 확인을 각각 점검합니다.

어느 키로 어느 계정에 접속하는지 확인합니다

ssh -G example-host
ssh -v -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 user@example.org

example-host와 example.org는 설명용 이름입니다. -G는 적용한 설정을 출력하고, -v는 실제 접속 시의 선택 과정을 보여 줍니다. 공개키가 대상 계정에 등록되어 있는지, 사용하려는 개인키가 맞는지, 서버가 해당 방식을 허용하는지 확인합니다.

키는 생성한 호스트와 다른 호스트에서 사용했다는 이유만으로 자동 무효가 되지는 않습니다. 개인키 접근 권한과 서버의 authorized_keys·상위 디렉터리 검사 조건은 별도로 맞춰야 합니다. 디버그 로그에는 사용자·호스트 정보가 있으므로 그대로 공개하지 않습니다.

Host 패턴과 실제 접속 대상을 구분합니다

Debian 12의 OpenSSH 9.2p1에서 Host 뒤에는 공백으로 구분한 패턴을 여러 개 적을 수 있습니다. 이것은 명령줄에 입력한 이름에 따라 같은 설정 블록을 선택한다는 뜻입니다. HostName은 선택된 블록에서 실제로 접속할 서버 이름 또는 주소를 지정합니다. 두 항목은 역할이 다릅니다. ssh_config(5)의 Host·HostName

아래는 설명용 주소를 사용한 클라이언트 설정입니다. HostName을 생략했으므로 두 입력은 계정과 키 설정을 공유하면서도 각각 입력한 이름 또는 IP 주소를 접속 대상으로 유지합니다.

Host lab.example.org 192.0.2.10
    User learner
    IdentityFile ~/.ssh/id_ed25519

반대로 이 블록에 HostName lab.example.org를 추가하면, 192.0.2.10을 입력해도 접속 대상은 lab.example.org로 바뀝니다. IP 주소로 직접 접속하려는 목적이라면 이 차이 때문에 예상과 다른 서버에 연결하거나 이름 해석 실패를 겪을 수 있습니다. 클라이언트 설정의 앞선 블록에서 이미 정한 값도 적용될 수 있으므로 실제 결과를 확인합니다.

ssh -G lab.example.org
ssh -G 192.0.2.10

각 출력의 hostname, user, identityfile을 비교합니다. ssh -G는 설정을 평가해 출력하고 종료하므로 실제 네트워크 연결이나 서버 인증 성공을 증명하지 않습니다. 접속을 시도할 때는 서버 신원 확인과 대상 계정의 인증을 별도로 점검합니다. ssh(1)의 -G

중계 서버를 거치는 경로를 명시합니다

Host relay
    HostName relay.example.org
    User learner

Host lab
    HostName lab.example.org
    User learner
    ProxyJump relay
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

~/.ssh/config의 예입니다. ssh lab은 relay를 경유하는 연결을 만듭니다. 중계 서버 로그인과 최종 서버 로그인은 각각의 인증 단계입니다. 이를 위해 개인키를 중계 서버에 복사해야 하는 것은 아닙니다.

ProxyJump 연결의 역할
  1. 로컬 SSH 클라이언트접속 설정과 인증할 키를 선택합니다.
  2. 중계 서버최종 서버로 가는 연결을 전달합니다.
  3. 최종 서버대상 계정의 인증과 세션을 처리합니다.

상자는 연결 경로를 나타냅니다. 두 서버가 같은 키나 계정 설정을 자동 공유한다는 뜻은 아닙니다.

Debian 12의 OpenSSH 9.2p1에서 ProxyJump는 먼저 중계 서버에 연결한 뒤, 그 서버에서 최종 대상의 TCP 포트로 연결을 전달합니다. 따라서 중계 서버가 최종 서버 이름을 해석하고 해당 포트에 도달할 수 있어야 합니다. 중계 서버 로그인에 성공해도 이 경로가 막혀 있으면 최종 서버에 접속할 수 없습니다. ssh_config(5)의 ProxyJump

최종 서버용 설정이 중계 서버에 자동으로 적용되지는 않습니다. 위 예처럼 relay와 lab의 Host 블록을 따로 두고 각 서버의 사용자와 키 설정을 확인합니다. ssh -G relay와 ssh -G lab은 각각 적용된 클라이언트 설정을 보여 주지만, 경유지에서 최종 서버까지의 실제 연결을 시험하지는 않습니다. ssh(1)의 -G·-J

같은 대상에 ProxyCommand도 설정했다면 두 지시어 가운데 먼저 적용된 것이 사용되고 나중 지시어는 효력을 갖지 않습니다. 일반 Host 블록과 특정 Host 블록을 함께 쓸 때는 설정 순서까지 확인합니다. ssh_config(5)의 설정 우선순위

로컬 포워딩의 양쪽 주소를 나눠 봅니다

Debian 12의 OpenSSH 9.2p1을 기준으로, 다음 첫 명령은 SSH 클라이언트를 실행한 컴퓨터의 127.0.0.1:3307에서 듣고 SSH 서버 컴퓨터의 127.0.0.1:3306으로 TCP 연결을 보냅니다. 두 127.0.0.1은 서로 다른 컴퓨터를 가리킵니다. SSH 서버와 데이터베이스가 같은 컴퓨터에 있고 데이터베이스가 그 서버의 loopback에서 듣는 예입니다. OpenSSH ssh(1)의 -L·-N

ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:3307:127.0.0.1:3306 learner@db-gateway.example.org
mysql -h 127.0.0.1 -P 3307 -u appuser -p

첫 줄은 SSH 연결을 열어 둔 터미널에서 실행하고, 둘째 줄은 같은 로컬 컴퓨터의 다른 터미널에서 실행합니다. -N은 원격 명령을 실행하지 않는다는 뜻이지 SSH 연결을 즉시 닫는다는 뜻은 아닙니다. 로컬 포트 3307이 이미 사용 중이면 포워딩을 만들 수 없습니다. ExitOnForwardFailure=yes는 포워딩 설정 실패 시 연결을 끝내지만, 이후 데이터베이스가 연결을 거절하는 것까지 검증하지는 않습니다. ExitOnForwardFailure와 LocalForward

로컬 포워딩에서 연결이 이동하는 방향
  1. 로컬 MySQL 클라이언트로컬 컴퓨터의 127.0.0.1:3307에 접속합니다.
  2. SSH 클라이언트와 암호화 채널로컬 포트로 들어온 TCP 연결을 SSH 서버까지 전달합니다.
  3. SSH 서버 쪽 연결서버 자신이 보는 127.0.0.1:3306의 데이터베이스에 접속합니다.

번호는 클라이언트에서 데이터베이스로 향하는 연결 순서입니다. 양 끝의 loopback 주소가 같은 컴퓨터를 뜻하지는 않습니다.

데이터베이스가 SSH 서버와 다른 컴퓨터에 있다면 -L의 오른쪽 목적지 주소를 서버가 실제로 접근할 수 있는 주소로 바꿔야 합니다. SSH 인증·로컬 포트 생성·원격 목적지 접속·데이터베이스 인증은 각각 다른 단계이므로, 접속 실패 시 어느 단계에서 멈췄는지 확인합니다. 127.0.0.1을 -L의 왼쪽에도 명시해 포워딩을 로컬에서만 받도록 합니다.

새 공개키를 설치한 뒤 인증 경로를 검증합니다

먼저 로컬에서 개인키와 공개키를 만듭니다. 첫 명령의 -t는 키 종류, -f는 개인키 저장 경로, -C는 키를 알아볼 주석입니다. 같은 이름의 파일이 있으면 덮어쓰기 전에 사용처를 확인하십시오. 두 번째 명령은 설치할 공개키의 지문을 보여 줍니다.

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_lab -C 'lab login'
ssh-keygen -lf ~/.ssh/id_ed25519_lab.pub

공개키 로그인을 위해 개인키의 passphrase를 비울 필요는 없습니다. 개인키는 로컬에 두고 보호하며, 반복 입력을 줄여야 한다면 잠금을 푼 키를 ssh-agent에 등록할 수 있습니다. 개인키 passphrase와 서버 계정 암호는 서로 다른 입력입니다.

복사 전에는 대상 계정에 새 공개키가 없습니다. 현재 허용된 암호 로그인, 기존 키 또는 관리자 등록 경로가 있어야 최초 설치가 가능합니다. 서버의 호스트 키가 처음 보이거나 바뀌었다면 별도로 확인한 지문과 비교한 뒤 인증을 계속하십시오.

ssh-copy-id -i ~/.ssh/id_ed25519_lab.pub learner@lab.example.org

이 명령은 로컬의 공개키 파일을 지정한 서버의 learner 계정에 설치합니다. -i 뒤에 공개키 경로를, 마지막에 대상 계정과 서버를 모두 적으면 agent에 여러 키가 있을 때도 선택이 분명합니다. 설치 명령에서 계정 암호를 묻는다면 그것은 새 키가 성공했다는 확인이 아니라 현재 허용된 방법으로 접속하는 단계일 수 있습니다.

2026년 10월에 확인한 OpenSSH portable의 ssh-copy-id 스크립트는 -i를 생략하면 agent의 ssh-add -L 키를 먼저 사용하고, 없으면 최근 ~/.ssh/id*.pub 파일을 선택합니다. 인증 시도로 이미 작동하는 키를 걸러내며, -f는 이 검사를 건너뛰어 중복 항목을 만들 수 있습니다. 2008년 사례의 identity.pub 단독 기본값·빈 agent 문구 복사·항상 중복된다는 설명을 현재 배포판의 규칙으로 적용하지 마십시오. 설치된 스크립트와 매뉴얼의 동작을 확인하십시오. OpenSSH portable ssh-copy-id(1)

일반 경로는 대상 계정의 ~/.ssh/authorized_keys에 공개키를 넣지만, 서버의 AuthorizedKeysFile이나 다른 키 공급자 설정에 따라 실제 조회 경로가 달라질 수 있습니다. ssh-copy-id가 사용자 정의 저장소나 홈 디렉터리 소유권까지 모두 고쳐 준다고 가정하지 마십시오. OpenSSH sshd_config(5)

공개키 설치 전후의 상태
공개키만 서버의 대상 계정 허용 목록으로 이동합니다. 개인키는 로컬에 남습니다.

그림의 화살표는 공개키 설치 방향이며 개인키의 이동이나 인증 성공을 뜻하지 않습니다.

ssh -v -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_lab learner@lab.example.org

설치 뒤에는 개인키를 명시해 실제 접속을 시도합니다. -v 로그에서 어느 키를 제시했고 서버가 받아들였는지, 어느 계정으로 로그인했는지 확인하십시오. passphrase 질문은 서버 계정 암호 인증으로 돌아갔다는 뜻이 아닙니다. 실패하면 호스트 키, 대상 계정, 지정한 키, 서버의 허용 키 경로와 StrictModes 권한 검사를 순서대로 좁힙니다. 로그에 들어 있는 계정·호스트 정보는 공개 전에 지우십시오. OpenSSH ssh(1)

호스트 키 검사와 사용자 인증은 별개입니다

known_hosts는 접속하는 서버의 신원 확인에 쓰이고 authorized_keys는 서버가 사용자 인증에 허용할 공개키를 담습니다. 이름이 비슷한 문제가 생겨도 두 파일의 역할을 혼동하지 않습니다. 서버 키가 바뀌었다면 변경 사유와 실제 지문을 확인해야 합니다.

자동화에서는 암호를 명령줄이나 스크립트에 넣기보다 적절히 관리한 키·agent와 BatchMode 등의 동작을 설계합니다. BatchMode는 자동으로 인증을 성공시키는 옵션이 아니라 대화형 질문을 하지 않도록 하는 옵션입니다.

키를 agent에 등록해도 서버 접속이 끝난 것은 아닙니다

OpenSSH 9.2p1에서 ssh-add는 실행 중인 인증 agent에 키를 등록합니다. SSH_AUTH_SOCK이 agent의 소켓을 가리켜야 합니다. 암호화된 키를 읽을 때는 passphrase 입력이 필요할 수 있으므로 “자동 등록”이 곧 “암호 입력 없음”을 뜻하지 않습니다. ssh-add 매뉴얼

작업바뀌거나 확인되는 대상
ssh-add -lagent에 등록된 키의 지문을 조회합니다.
ssh-add -L등록된 키의 공개키 정보를 출력합니다.
ssh-add -d key.pub지정한 키를 agent에서 제거합니다. 원래 키 파일 삭제가 아닙니다.
ssh-add -t 600 key등록할 키의 최대 보유 시간을 600초로 제한합니다.
등록 상태와 인증 결과를 나누어 확인합니다
  1. 로컬 agent에 키 등록이후 클라이언트가 인증에 사용할 수 있도록 준비합니다.
  2. SSH 클라이언트가 인증 요청agent의 키를 사용할 수 있어도 서버가 허용하는지 확인해야 합니다.
  3. 서버의 인증 결과대상 계정과 허용한 공개키 등의 조건에 따라 성공하거나 실패합니다.

번호는 등록 이후 접속을 시도하는 순서입니다. 키 파일을 서버로 전송하는 경로를 나타내지 않습니다.

agent는 개인키를 보유하고 인증 요청에 사용합니다. 일반적인 agent 메모리의 등록 상태를 재부팅 뒤에도 보존되는 키 파일과 혼동하지 않습니다. 세션이 끝날 때 agent가 종료되는지는 실행 방식에 따라 다르므로, 새 셸마다 agent를 추가로 실행하는 코드를 그대로 넣기보다 기존 세션의 agent를 확인합니다. ssh-agent 매뉴얼

공개키를 등록하는 것과 인증 방법을 제한하는 것은 다릅니다

이 절은 Debian 12의 OpenSSH 9.2p1을 기준으로 합니다. authorized_keys에 키를 넣어도 다른 인증 방식이 자동으로 꺼지지는 않습니다. 공개키만 허용하려면 PubkeyAuthentication yes와 AuthenticationMethods publickey처럼 허용할 방법을 명시해야 합니다. 계정·주소에 적용되는 Match도 함께 확인합니다. 인증 방법과 Match

PasswordAuthentication no만으로는 PAM을 통한 keyboard-interactive 인증까지 막았다고 볼 수 없습니다. 그 경로도 사용하지 않으려면 KbdInteractiveAuthentication no를 확인합니다. UsePAM yes를 유지하면 공개키 인증에도 PAM의 계정·세션 처리가 적용됩니다. PAM 전체를 끄는 것과는 다릅니다. Debian OpenSSH 설정 파일의 PAM 설명

공개키 등록 뒤에도 남는 서버 검사
  1. 사용할 키가 등록되어 있습니까?대상 계정의 키 파일을 확인합니다.
  2. 그 인증 방식이 허용됩니까?계정·주소에 적용된 서버 설정을 확인합니다.
  3. 계정과 세션 조건도 맞습니까?인증 성공 이후의 접근 조건을 확인합니다.

번호는 점검 순서입니다. 모든 서버가 내부에서 이 순서대로 함수를 호출한다는 뜻은 아닙니다.

서버에 실제로 적용되는 설정을 확인합니다

sudo /usr/sbin/sshd -t
sudo /usr/sbin/sshd -T -C user=learner,addr=192.0.2.10,host=client.example.org

서버에서 확인할 때 사용하는 예입니다. 첫 줄은 설정과 호스트 키의 유효성을 검사합니다. 둘째 줄은 예시 접속 조건에 적용될 설정을 출력하고 종료합니다. 서버를 다시 시작하는 명령이 아닙니다. 앞 절의 ssh -G는 클라이언트 설정을 출력하므로 서로 대체할 수 없습니다. sshd의 -t·-T·-C

Include로 읽는 파일과 Match 때문에 파일 마지막에 적은 값이 항상 최종값은 아닙니다. -C에는 실제로 확인할 사용자와 접속 주소를 넣습니다. 설정 변경 전에는 현재 연결을 유지하고, 검사 후 별도 연결로 성공 여부를 확인합니다.

root의 SSH 로그인을 금지하는 PermitRootLogin no와 비밀번호·keyboard-interactive 인증을 제한하는 prohibit-password도 구분합니다. PAM을 확인할 때는 SSH 서비스가 사용하는 /etc/pam.d/sshd를 봅니다. /etc/pam.d/login을 고쳤다고 SSH에 적용된다고 가정하지 않습니다. PermitRootLogin · OpenSSH의 PAM 서비스 이름

키 파일의 위치와 소유권도 맞아야 합니다

AuthorizedKeysFile의 상대 경로는 서버의 대상 계정 홈을 기준으로 합니다. 클라이언트 홈과 혼동하지 않습니다. ~/.ssh의 700과 authorized_keys의 600은 권장 설정이며, 이 숫자만이 유일하게 가능한 조합이라는 뜻은 아닙니다. StrictModes가 켜져 있으면 키 파일뿐 아니라 디렉터리의 소유권과 다른 사용자의 쓰기 가능 여부도 확인합니다. 거부될 때는 서버 로그와 실제 경로를 함께 살펴봅니다. sshd의 키 파일·권한 설명

Fail2Ban은 SSH 인증 설정을 대신하지 않습니다

SSH의 접속 제한은 적용 단위가 다릅니다. Debian 12 OpenSSH 9.2p1에서 MaxAuthTries는 한 연결의 인증 시도 횟수, MaxStartups는 서버 전체의 인증 전 동시 연결 수, PerSourceMaxStartups는 출발지별 인증 전 동시 연결 수를 제한합니다. LoginGraceTime은 인증을 마칠 때까지 허용하는 시간입니다. 이 네 설정은 계정별 누적 실패 횟수를 세는 같은 기능이 아닙니다. 접속 제한의 단위와 주소 묶음

PerSourceNetBlockSize의 기본값 32:128은 개별 IPv4·IPv6 주소를 구분하며, 설정을 바꾸면 주소를 네트워크 단위로 묶을 수 있습니다. NAT·프록시 뒤의 관리자가 같은 출발지 주소를 공유하면 정상 접속도 영향을 받습니다. OpenSSH 9.8에 추가된 PerSourcePenalties는 이 9.2p1 예시에 섞지 않습니다. 현재 접속을 유지하고 유효 설정과 별도 새 연결을 확인한 뒤에만 제한을 조정합니다. OpenSSH 9.8 변경 사항 · 유효 설정과 새 연결 확인

이 절은 Debian 12의 Fail2Ban 1.0.2 계열을 기준으로 합니다. Fail2Ban은 인증 실패 기록을 필터로 찾고 jail의 조건에 맞으면 action으로 주소를 일시 차단합니다. 설치만 하거나 systemctl enable fail2ban.service를 실행한 것만으로 현재 서비스와 SSH jail이 동작한다고 판단할 수 없습니다. enable은 다음 부팅을 위한 unit 연결을 만들며, 현재 바로 시작하려면 --now를 함께 사용합니다. Debian 12 systemctl(1)

sudo apt-get install fail2ban
sudo fail2ban-client -t
systemctl is-active fail2ban.service
sudo fail2ban-client status
sudo fail2ban-client status sshd

설치 뒤 둘째 줄은 현재 설정을 검사하고, 셋째 줄은 서버 프로세스의 현재 상태를 확인합니다. 넷째 줄에서 실제로 활성화된 jail 이름을 본 뒤에만 마지막 줄의 sshd 상태를 확인하십시오. 설정을 바꾼 뒤에도 서비스가 실행 중인지, SSH 실패 기록이 선택한 로그 파일 또는 journal backend에 들어오는지, 차단 action이 현재 방화벽에서 작동하는지를 따로 검증해야 합니다. Debian 12 fail2ban-client(1) · jail.conf(5)

패키지가 제공한 jail.conf를 직접 덮어쓰기보다 현재 jail.d/ 설정과 jail.local을 읽고 필요한 값만 jail.d/*.local에 둡니다. Debian 12의 읽기 순서는 jail.conf, jail.d/*.conf, jail.local, jail.d/*.local이며 뒤 파일이 같은 값을 덮습니다. backend = systemd는 journal의 journalmatch를 사용하므로 파일 기반 logpath를 그대로 적용하지 않습니다. 실제 관리 접속을 유지한 채 jail과 차단 동작을 확인하고, 반복 실패 차단을 공개키 인증·허용 계정·서버 설정의 대체물로 취급하지 않습니다.

확인한 문서

OpenSSH