사내에 올린 Git 서버가 웹은 8081 이나 18081 같은 비표준 포트로 열려 있다. HTTP 로 클론은 되는데 SSH 로 바꾸면 Connection refused 가 나고, 어떤 때는 kex_exchange_identification: Connection closed by remote host 로 끊긴다.
GitLab · Gitea · Gogs 같은 서버는 HTTP(S) 와 SSH 를 서로 다른 리스너로 연다. 웹 UI 가 18081 이라고 해서 SSH 도 18081 인 것이 아니다. SSH 쪽은 보통 22 이거나, 호스트의 OpenSSH 와 충돌하지 않게 2222 · <SSH_PORT> 같은 포트를 쓴다.
실제 포트는 서버 웹 화면의 클론 주소 상자에서 SSH 탭을 고르면 그대로 보인다. 명령줄로 확인하려면 포트를 하나씩 두드린다.
nc -vz git.example.com 22
nc -vz git.example.com 2222
ssh -T -p 2222 git@git.example.com
Connection refused 는 그 포트에 아무도 안 듣고 있다는 뜻이고, 타임아웃은 방화벽이 막고 있다는 뜻이다. 둘은 다른 문제다.
SCP 문법(git@host:path)에는 포트를 적을 자리가 없다. 포트를 넣으려면 ssh:// 형식을 써야 한다.
# 포트를 못 적는 형식
git remote add origin git@git.example.com:team/app.git
# 포트를 적을 수 있는 형식
git remote add origin ssh://git@git.example.com:2222/team/app.git
ssh:// 형식에서는 호스트 뒤가 :포트 이고, 경로는 / 로 시작한다. SCP 문법의 : 를 그대로 남긴 채 포트를 끼워 넣으면 경로로 해석돼 실패한다.
이미 만든 원격은 갈아 끼운다.
git remote -v
git remote set-url origin ssh://git@git.example.com:2222/team/app.git
HTTP 로 클론했던 저장소를 SSH 로 옮길 때도 같은 명령이다. 커밋 이력은 그대로 남는다.
호스트마다 ~/.ssh/config 에 적어 두면 SCP 문법으로도 원하는 포트와 키를 쓴다.
Host git.example.com
HostName git.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519_git
IdentitiesOnly yes
이렇게 두면 git@git.example.com:team/app.git 이라고만 적어도 2222 로 붙는다. 저장소마다 다른 키를 쓰고 싶으면 Host 에 별칭을 주고 원격 주소에 그 별칭을 쓴다.
Host git-work
HostName git.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519_work
git remote set-url origin git-work:team/app.git
kex_exchange_identification: Connection closed by remote host 는 TCP 는 붙었는데 키 교환을 시작하기도 전에 상대가 끊었다는 뜻이다. 인증 실패가 아니므로 키나 비밀번호를 고쳐도 달라지지 않는다. 원인은 대개 넷 중 하나다.
첫째, SSH 가 아닌 포트에 붙었다. 웹 포트나 프록시 포트에 SSH 로 말을 걸면 상대가 즉시 끊는다. 무엇이 듣고 있는지 배너로 확인한다.
nc git.example.com 2222
# 정상이면 SSH-2.0-OpenSSH_9.x 같은 배너가 나온다
둘째, 서버의 접속 제한에 걸렸다. MaxStartups 를 넘긴 동시 접속이나 fail2ban · hosts.deny 가 원인이면 같은 메시지가 난다. 잠시 뒤 다시 되는 패턴이면 이쪽이다.
셋째, 중간의 L4 · 프록시가 유휴 연결을 끊는다. 이때는 ssh -v 로 어느 단계에서 끊기는지 본다.
GIT_SSH_COMMAND='ssh -vvv' git ls-remote origin
넷째, 서버가 AllowUsers · AllowGroups 로 접속 주체를 제한하고 있다.
방화벽 정책상 SSH 포트를 끝내 못 여는 경우가 있다. 이때는 무리하게 SSH 를 고집하지 말고 HTTP(S) 원격에 토큰을 쓰는 편이 낫다.
git remote set-url origin https://git.example.com:18081/team/app.git
비밀번호 대신 개인 액세스 토큰을 쓰고, 매번 묻지 않게 하려면 자격증명 도우미를 켠다. store 는 평문 파일에 남으므로 공용 서버에서는 쓰지 않는다.
git config --global credential.helper 'cache --timeout=3600'
사설 CA 로 만든 인증서를 쓰는 HTTPS 원격은 CA 를 시스템 신뢰 저장소에 넣는다. http.sslVerify=false 로 끄는 것은 검증을 통째로 버리는 것이라 임시 확인 외에는 쓰지 않는다.
known_hosts 는 포트를 함께 기록한다. 같은 호스트라도 포트가 다르면 [git.example.com]:2222 라는 별도 항목으로 잡히므로, 포트를 바꾼 뒤 지문 확인 문구가 다시 뜨는 것은 정상이다.