잡지식 정리 정돈
자격증명이 없는 환경에서 git push 하기 _ Claude Cowork
클로드 코워크에 맥의 폴더 하나를 공유해 놓고 일을 시키다가, 결과물을 GitHub에 올리라고 했더니 push에서 막혔습니다. 커밋(바뀐 내용을 '내 쪽 기록'에 남기는 것)까지는 되는데 push(그 기록을 GitHub에 올리는 것)만 안 되는 이유와, 결국 어떤 자격증명을 넣어줬는지를 순서대로 정리합니다. 앞서 쓴 clone 방법 글에서 나눈 두 축(전송 경로, 자격증명)에 실제 사례 하나를 붙여보는 글입니다.
GitHub 속 git 저장소 Clone 방법 정리 _ 전송 경로와 자격증명저장소 하나 만들어놓고 초록색 Code 버튼을 눌렀더니 탭이 셋 나옵니다. 셋 다 처음 보면 왜 저 이름이고 왜 저렇게 생겼는지 알 수가 없지요. git이 뭔지부터 시작해서, 이름의 유래와 주소가 저 모양이 된 이유를 순서대로 뜯어봅니다.1. 깃허브 저장소 상단의 Code 버튼을 누르면 Local 탭 아래에 Clone 방식이 세 개 나옴. HTTPS, SSH, GitHub CLI 순임. 2.각 탭이 내주는 것은 아래 셋임.HTTPS — https://githubdwnc.me1. 상황은 이렇게 시작됨. 맥에 폴더 하나가 있고, 그 폴더를 클로드 코워크에 공유(연결)해 뒀음. 설명을 위해 폴더 이름은 "house ledger", 올릴 GitHub 저장소는 github.com/ledger-sample/house-ledger 라고 치겠음. 코워크는 그 폴더 안의 파일을 읽고 쓰고, 셸 명령(까만 창에서 키보드로 입력하는 컴퓨터 명령)도 실행함. git init(폴더를 git 저장소로 만드는 것)도 되고 commit도 잘 됨. 그런데 git push를 시키면 자격증명을 내는(제출하는) 데서 막힘.
2. 처음엔 왜 막히는지 몰랐음. 맥에는 git을 화면으로 다루는 프로그램인 소스트리가 깔려 있고 거기에 GitHub 계정도 로그인돼 있음. 맥의 ~/.ssh 폴더에는 깃허브에 등록해둔 SSH 키 파일도 있음. 같은 맥에서 도는데 왜 그게 다 없는 취급이냐는 것임. 처음엔 맥의 설정이 어딘가 꼬인 줄 알았음.
3. 코워크의 셸이 어디서 도는지부터 확인함. 맥 본체가 아니었음. 맥 안에서 따로 도는 격리된 리눅스 가상머신임. 홈 디렉터리는 코워크와의 대화(세션)가 새로 열릴 때마다 새로 만들어지고, 그 안에서 보이는 것은 내가 공유한 폴더 하나뿐임.
4. 그래서 맥 키체인(맥이 비밀번호와 토큰을 넣어두는 보관함), 소스트리에 저장된 로그인 정보, 맥의 ~/.ssh 폴더, 깃허브 명령줄 도구 gh가 전부 없음. 맥에 있는 것이지 가상머신에 있는 것이 아니기 때문임. 앞 글의 축으로 말하면, 전송 경로는 HTTPS든 SSH든 고를 수 있는데 자격증명 쪽이 통째로 비어 있는 상태임.
5. 자격증명은 "서버에 내가 누구인지 증명할 때 내미는 것"임. 비밀번호, 토큰, 키 파일이 다 여기 들어감. 커밋은 내 컴퓨터 안에서 끝나는 일이라 이게 필요 없고, push는 남의 서버에 쓰는 일이라 필요함. 이게 하나도 없으니 서버 입장에서는 모르는 사람이 push 하겠다고 온 것이고, 거절이 정상임.
6. 네트워크도 맥과 다름. 이 가상머신은 바깥으로 직접 나가는 통로가 없음. 이름을 입력해 숫자로 된 실제 인터넷 주소를 찾는 DNS도, 서버와 직접 붙는 TCP 연결도 안 됨. 모든 통신이 가상머신 안의 프록시 하나를 거쳐서만 나감. 프록시는 "내 대신 바깥에 다녀와 주는 창구"임.
7. SSH도 예외가 아님. 환경변수(셸이 실행하는 프로그램마다 넘겨주는 이름과 값의 설정) 중에 git이 SSH로 나갈 때 실행할 명령을 정하는 GIT_SSH_COMMAND가 있는데, 코워크 셸에서 보니 거기에 프록시를 거쳐 나가는 명령이 미리 들어 있음. 그 명령 안에는 프록시가 요구하는 인증 토큰이 있는데, 이 토큰은 셸 명령을 실행할 때마다 새 값으로 바뀜. 그래서 환경변수 값을 어딘가에 고정해 적어둘 수 없고, 지우거나 덮어쓰면 프록시 설정이 날아가서 깃허브까지 가지도 못함.
8. 코워크가 처음 내놓은 방법은 깃허브에서 발급받는 개인용 액세스 토큰(PAT)을, 저장소에 등록해둔 올릴 곳 주소(리모트 주소) 안에 박아넣는 것이었음. https://토큰@github.com/ledger-sample/house-ledger.git 같은 모양임. 이러면 따로 낼 것 없이 주소 자체가 자격증명이 됨.
9. 이게 싫었음. 리모트 주소는 저장소 폴더 안의 .git/config 파일에 적히는데, 그러면 토큰이 그 파일에 평문으로 남음. 그것도 가상머신 안이 아니라 맥에서 공유한 폴더에 남음. 게다가 PAT 중 흔히 쓰는 종류(classic PAT)는 계정 단위라, 저장소 하나 올리자고 내 계정이 접근할 수 있는 모든 저장소가 열리는 토큰을 평문 파일에 적어두는 셈임. 그래서 물은 것이 "주소에 토큰 박는 것 말고 다른 방법 없냐"였음.
10. 답은 배포키(deploy key)였음. 저장소 하나에만 붙는 SSH 키임. "건물 마스터키가 아니라 그 집 현관문 하나만 여는 열쇠"라고 보면 됨. SSH 키는 공개키와 개인키 한 쌍으로 만들어지는데, 공개키를 깃허브 저장소의 Settings 안 Deploy keys에 등록하고 쓰기 권한(Allow write access)을 켜면, 짝이 되는 개인키 파일을 가진 쪽만 그 저장소에 push가 됨.
11. 범위를 나란히 놓으면 이렇게 됨.
classic PAT / 내 계정이 닿는 모든 저장소
fine-grained PAT / 저장소 단위로 좁힐 수 있음. 2025년 3월 18일 정식 출시
배포키 / 저장소 하나. 계정과 묶이지 않음
※ 출처: GitHub Changelog, "Fine-grained PATs are now generally available" (2025.03.18)
12. fine-grained PAT로도 저장소 하나로 좁힐 수는 있음. 그래도 배포키를 고른 이유는 둘임. PAT는 어느 종류든 내 계정에 매달려 있어 새면 내 계정 이름으로 쓰이지만, 배포키는 계정이 아니라 저장소에 매달려 있음. 그리고 PAT는 HTTPS에서 쓰는 것이라 맥이면 키체인에 넣으면 되는데 이 가상머신에는 키체인이 없어서, git이 평문 파일에 적어두는 방식을 따로 설정하거나 주소 안에 박아야 함. 키 파일은 SSH가 원래 파일에서 읽도록 만들어진 것이라, 저장소 폴더 안에 두고 git이 무시할 파일 목록(.gitignore)에 올려 커밋에서 빼면 끝남. 맥의 공유 폴더에 파일로 남는 것은 같지만, 9번에서 꺼려했던 것은 '남는 것이 계정 전체 토큰'이라는 점이었고 키는 새 봤자 이 저장소 하나임.
13. 앞 글의 두 축으로 조합이 정해짐. 전송 경로는 SSH, 자격증명은 배포키, 두는 곳은 저장소 폴더 안의 키 파일(파일 이름은 .deploy_key로 함). 이제 되겠지 했는데 여기서부터 네 번 걸림.
14. 첫 번째. 키 파일은 SSH 명령에 -i 옵션으로 지정하는데, 7번의 환경변수 값 뒤에 이 옵션과 StrictHostKeyChecking=accept-new 옵션을 이어붙여 push 했더니 실패함. SSH는 전에 접속한 서버의 지문(서버를 알아보는 값)을 known_hosts라는 파일에 적어두는데, accept-new는 "처음 보는 서버면 지문을 적어두고 통과시켜라"는 뜻임. 그런데 그 파일을 만들 ~/.ssh 폴더 자체가 가상머신에 없어서 적지도 못하고 실패한 것임.
15. 두 번째. 폴더를 만들어도 known_hosts가 비어 있으면 첫 접속 때 적혀야 하는데, 이참에 미리 채워두자고 ssh-keyscan을 돌렸더니 빈 파일만 나옴. ssh-keyscan은 서버에 직접 붙어서 지문을 받아오는 도구인데, 6번에서 말한 대로 이 가상머신은 직접 붙는 통로가 없음. 프록시를 탈 줄 모르는 도구라 아무것도 못 받아온 것임. 결국 빈 known_hosts 파일을 하나 만들어 UserKnownHostsFile 옵션으로 그 위치를 지정하고, 14번의 accept-new로 첫 접속 때 적히게 두는 것으로 넘어감.
16. 세 번째. 이 옵션들을 매번 이어붙여 치기 싫어서 저장소 설정 core.sshCommand에 옮겨봤는데 통째로 무시됨. git은 환경변수 GIT_SSH_COMMAND가 있으면 저장소 설정보다 그쪽을 먼저 씀. 7번의 환경변수가 항상 들어 있는 가상머신에서는 저장소 설정이 영영 안 읽히는 것임. 환경변수를 덮어쓰면 프록시가 날아가니, 결국 14번처럼 기존 값 뒤에 이어붙이는 쪽으로 돌아감.
17. 네 번째. 폴더 이름의 공백임. 16번에서 환경변수 뒤에 이어붙인 문자열은 셸이 공백을 기준으로 낱말 단위로 잘라 명령에 넣음. "house ledger"에 공백이 있어서 키 경로 -i /경로/house ledger/.deploy_key가 house와 ledger 두 토막으로 쪼개져 들어감. 경로를 따옴표로 감싸도 되지만, 키 파일을 공백 없는 경로로 복사해 그 경로를 쓰는 쪽이 단순해서 그렇게 함.
18. 그렇게 push가 됐음. 최종 절차는 세 줄임. 저장소 폴더의 키를 공백 없는 곳으로 복사하고, 환경변수 뒤에 키 옵션을 이어붙이고, git push. 복사한 키는 가상머신의 홈 디렉터리에 놓이는데, 3번에서 말한 대로 홈 디렉터리가 세션마다 새로 생기니 복사는 세션마다, 환경변수 이어붙이기는 7번대로 값이 매번 바뀌니 push 명령마다 해야 함. 그래서 이 세 줄을 저장소 안에 문서로 남겨뒀고, 다음 세션의 코워크는 그 문서를 읽고 같은 방식으로 push 함.
19. 맥의 소스트리에서도 같은 키를 쓰게 해뒀음. 저장소 설정 core.sshCommand에 키 경로를 넣어둔 것임. 같은 폴더가 맥과 가상머신에서 서로 다른 절대경로로 보이므로, 저장소 폴더 기준 상대경로로 적었음. 맥은 프록시도 없고 환경변수도 설정돼 있지 않아서 저장소 설정이 그대로 먹음. 키 하나로 가상머신과 맥 양쪽에서 같은 저장소에 push 하는 구조가 됨.
20. AI에게 맡겨서 안 되는 것 아니냐고 볼 수도 있음. 그런데 push가 안 된 이유는 2번에서 짐작한 맥 쪽 설정 문제도 아니고, AI 도구라서 생기는 한계도 아님. 격리 가상머신에 프록시를 얹은 구조라서임. 코워크가 아니라 맥 사용자 계정 권한으로 맥에서 직접 도는 종류의 AI 도구였다면 3번의 격리가 없으니 ~/.ssh를 그냥 가져다 썼을 것임.
21. 문제는 AI가 아니라 이 가상머신임.
22. 뒤집어 보면 그 격리가 코워크에 일을 맡기는 이 구조의 장점임. 코워크에게 보이는 것은 폴더 하나이고, 넣어준 키도 저장소 하나짜리임. 뭘 시키든 사고가 나도 범위가 그 저장소 하나로 끝남.
한줄 코멘트. 클로드 코워크의 셸은 맥이 아니라 맥 안의 격리 가상머신이라 키체인도 키도 없고, 바깥으로는 프록시 하나로만 나감. 그 위에서 push를 세우려면 자격증명을 새로 넣어줘야 하는데, 계정 전체가 열리는 토큰을 주소에 박는 대신 저장소 하나짜리 배포키를 파일로 두는 쪽을 골랐음. 실제로 걸린 것은 키가 아니라 그 주변이었음. known_hosts를 적을 폴더가 없었고, 채울 도구가 프록시를 못 탔고, 환경변수가 저장소 설정을 덮었고, 폴더 이름의 공백이 경로를 쪼갰음. 앞 글에서 clone 방법을 두 축으로 나눈 것은 이런 처음 보는 조합을 만났을 때 자리를 찾기 위해서였고, 이번 조합은 SSH 위에 배포키를 얹은 자리에 들어감. 편한 쪽은 토큰을 주소에 박는 것이었지만, 사고가 나도 저장소 하나로 끝나는 쪽이 맞다고 봄.