SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor

Dùng một SSH key cho cả GitHub và GitLab

Dán đúng file .pub lên GitHub và GitLab, đổi remote HTTPS sang SSH bằng git remote set-url, rồi viết một file ~/.ssh/config có IdentitiesOnly để không bị mời sai key.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, September 25, 2026.

Dán file nào khi GitHub và GitLab hỏi thêm SSH key

Khi GitHub hay GitLab hỏi bạn thêm một SSH key, file bạn dán vào là file có đuôi .pub. Một SSH key là cặp hai file: ~/.ssh/id_ed25519.pub là public key, phần bạn đem cho người khác, còn ~/.ssh/id_ed25519 là private key, phần ở lại máy bạn và không đi đâu cả. Cùng một cặp key dùng được cho cả hai nơi, vì mỗi nhà cung cấp chỉ giữ bản public và dùng nó để kiểm tra chữ ký mà máy bạn tạo ra lúc kết nối.

Sau khi dán key xong, còn hai việc nữa. Bạn đổi remote của repo từ HTTPS sang SSH bằng git remote set-url, và bạn viết một file ~/.ssh/config khai báo IdentityFile cùng IdentitiesOnly cho từng host. Việc thứ hai hay bị bỏ qua, nhưng nó chính là thứ giữ cho ssh không mời sai key khi trong máy bạn có nhiều hơn một key.

Bài này giả định bạn đã có sẵn một cặp key trong ~/.ssh. Nếu chưa có, hãy sinh key bằng ssh-keygen và nắm cách quản lý key trên nhiều máy trước, rồi quay lại từ mục dưới đây.

Xem trong ~/.ssh đang có những file nào

ls -l ~/.ssh

Chạy lệnh trên và đọc danh sách mà máy bạn in ra. Mỗi key xuất hiện thành hai file cùng tên, một file có đuôi .pub và một file không có. Tên mặc định của ssh-keygen hiện nay là id_ed25519 (Ed25519, thuật toán mặc định), còn id_rsa là tên của key RSA cũ hơn. GitHub và GitLab nhận cả hai loại, nên nếu bạn đã có key RSA đủ dài thì không cần tạo key mới chỉ để đọc bài này.

In nội dung public key ra để copy:

cat ~/.ssh/id_ed25519.pub

Lệnh này in ra một dòng duy nhất. Hãy copy đúng toàn bộ dòng mà máy bạn in ra, từ ký tự đầu tiên đến ký tự cuối cùng, không thêm dấu cách và không cắt xuống dòng. Phần đầu dòng cho biết đây là loại key gì, nên copy thiếu phần đầu thì ô nhập trên web sẽ không nhận.

File không có .pub dài hơn nhiều và nằm giữa hai dòng đánh dấu BEGIN và END. Nếu bạn mở ra và thấy khối đó, bạn đang mở private key. Đóng lại và đừng copy nó.

Sửa quyền cho ~/.ssh trước khi làm tiếp

ssh từ chối dùng một private key mà người dùng khác trên cùng máy đọc được, vì với ssh, một key đã bị người khác đọc thì coi như đã mất. Nó không tự sửa quyền giúp bạn. Nó chỉ lặng lẽ bỏ key đó ra khỏi danh sách và đi tiếp, nên bạn thấy máy chủ từ chối xác thực mà không hiểu vì sao key có sẵn ở đó mà không dùng được.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
ls -l ~/.ssh

700 cho thư mục nghĩa là chỉ bạn vào được. 600 cho private key nghĩa là chỉ bạn đọc và ghi được. Public key để 644 không sao, nó vốn là phần công khai. Chạy lại ls -l ~/.ssh và đọc cột quyền của từng file trên máy bạn để chắc là lệnh đã có tác dụng. Nếu bạn muốn hiểu vì sao ba con số đó mang nghĩa như vậy, xem chmod dạng số so với dạng chữ.

Thêm public key vào GitHub

Đường đi: ảnh đại diện ở góc trên bên phải, Settings, rồi SSH and GPG keys trong cột bên trái, rồi nút New SSH key.

Ô Title là tên để sau này bạn nhận ra key thuộc máy nào, ví dụ laptop-dell-ubuntu. Đặt tên theo máy, không đặt theo dự án, vì khi mất máy bạn cần biết ngay phải xoá dòng nào. Ô Key type để Authentication Key. Ô Key là nơi dán dòng bạn vừa copy.

Authentication Key là loại dùng để git push và git pull. Nếu bạn còn muốn ký commit bằng chính key này, bạn phải thêm nó lần thứ hai với Key type là Signing Key. Cùng một public key, nhưng GitHub giữ hai danh sách riêng cho hai mục đích, nên thêm một lần không tự có cả hai.

Thêm public key vào GitLab

Đường đi: ảnh đại diện, Edit profile, rồi SSH Keys trong cột bên trái, rồi Add new key. Dán vào ô Key và GitLab tự điền Title từ phần chú thích có trong key. Ô Usage type để Authentication & Signing là dùng được cho cả push và ký commit, nên ở đây bạn không phải thêm key hai lần như bên GitHub.

Có một ô cần để ý: Expiration date. GitLab điền sẵn một ngày trong tương lai. Hết ngày đó, key ngừng hoạt động, và triệu chứng rất dễ gây hoang mang: git push bỗng nhiên không được nhận nữa dù bạn không sửa gì trên máy. Nếu bạn không muốn có hạn, hãy xoá trống ô đó ngay lúc thêm key. Nếu bạn để hạn, hãy ghi ngày đó vào lịch.

Đổi remote từ HTTPS sang SSH bằng git remote set-url

Nếu mỗi lần push bạn lại phải dán một personal access token, nguyên nhân là remote của repo đang trỏ tới URL HTTPS, và với HTTPS thì Git bắt buộc phải hỏi thông tin đăng nhập. Chuyển remote sang SSH là hết, và bạn không cần clone lại repo.

Trước tiên xem repo đang trỏ đi đâu:

cd ~/du-an/repo-cua-toi
git remote -v

Lệnh này liệt kê tên remote, thường là origin, kèm URL của nó. Hãy đọc kết quả trên máy bạn để biết tên remote chính xác trước khi sửa. Sau đó đổi:

git remote set-url origin git@github.com:ten-tai-khoan/ten-repo.git
git remote -v

GitLab dùng đúng cú pháp đó, chỉ khác tên host. Nếu dự án nằm trong subgroup thì đường dẫn có nhiều tầng, và bạn phải ghi đủ các tầng:

git remote set-url origin git@gitlab.com:nhom-cua-toi/nhom-con/ten-du-an.git
git remote -v

Chạy git remote -v lần cuối và đọc URL mà repo của bạn báo về. git remote set-url chỉ sửa file .git/config bên trong repo. Nó không chạm vào commit, branch hay file bạn đang sửa, nên đổi qua đổi lại bao nhiêu lần cũng an toàn. Đổi lại cũng bằng chính lệnh đó với URL https://.

Hai điều hay làm người ta mất thời gian. Lệnh này chỉ sửa một repo, nên mỗi bản clone cũ trên máy phải đổi riêng. Và nếu bạn fork một dự án, git remote -v sẽ liệt kê cả origin lẫn upstream: đổi origin thôi thì git fetch upstream vẫn đi qua HTTPS và vẫn hỏi token.

Một file ~/.ssh/config cho github.com và gitlab.com

Viết file ~/.ssh/config như sau. Nếu bạn dùng một key cho cả hai nơi, cả hai block trỏ tới cùng một IdentityFile.

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Host gitlab.com
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes
chmod 600 ~/.ssh/config

Host là mẫu khớp với tên host bạn viết trong URL remote. User git vì cả GitHub và GitLab đều cho bạn đăng nhập dưới user git, danh tính thật nằm ở key chứ không ở tên user. IdentityFile trỏ tới private key, ssh tự tìm file .pub cùng tên. Dòng đáng chú ý nhất là IdentitiesOnly yes.

IdentitiesOnly yes ngăn lỗi Too many authentication failures thế nào

Không có IdentitiesOnly yes, ssh mời lần lượt mọi key nó biết: mọi key đã nạp trong ssh-agent, cộng thêm các key có tên mặc định trong ~/.ssh. Máy chủ tính mỗi lần mời là một lần thử xác thực, và nó chỉ cho một số lần thử trong mỗi kết nối, mặc định của OpenSSH là 6. Nên nếu agent của bạn đang giữ bảy key và key đúng nằm cuối danh sách, máy chủ đóng kết nối trước khi tới lượt nó. Đó là lý do lỗi Too many authentication failures thường xuất hiện đúng vào ngày bạn thêm key thứ hai, và key mới không hề có lỗi gì.

IdentitiesOnly yes nói với ssh: chỉ dùng đúng IdentityFile tôi ghi trong block này, đừng mời gì khác. Một host, một key, một lần thử.

Nếu bạn có hai tài khoản trên cùng một nhà cung cấp, ví dụ một GitLab cá nhân và một GitLab công ty, hãy tạo thêm một block với tên host riêng:

Host gitlab-work
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes
git remote set-url origin git@gitlab-work:nhom-cong-ty/ten-du-an.git
git remote -v

gitlab-work không phải tên miền thật và không cần tồn tại trên DNS. ssh tra tên đó trong file config, thấy HostName gitlab.com, rồi kết nối tới GitLab với key bạn đã chỉ định. Repo nào của công ty thì đặt remote theo tên đó, repo cá nhân giữ git@gitlab.com:... như thường.

Đọc lại cấu hình mà ssh thực sự sẽ dùng

ssh -G github.com
ssh -G gitlab.com
ssh -G gitlab-work

ssh -G in ra toàn bộ tuỳ chọn có hiệu lực cho một host sau khi ssh đã đọc file config, và nó không mở kết nối nào, không cần mạng. Hãy chạy từng lệnh rồi tìm trong kết quả máy bạn in ra các dòng hostname, port, user, identityfile và identitiesonly, sau đó so với những gì bạn vừa viết trong file. Đây là cách nhanh nhất để biết ssh có đọc đúng file config của bạn hay không, thay vì đoán.

Nếu một giá trị không giống, nguyên nhân thường là thứ tự trong file. ssh lấy giá trị đầu tiên nó gặp cho mỗi tuỳ chọn, chứ không lấy giá trị cuối. Vì vậy block cụ thể phải nằm trên, còn block chung Host * phải nằm ở cuối file. Một IdentityFile đặt trong Host * ở đầu file sẽ thắng mọi block phía dưới, và mọi thứ bạn viết sau đó không có tác dụng.

Thử kết nối tới GitHub và GitLab

Hai lệnh dưới đây cần mạng ra ngoài, nên đây là phần bạn tự chạy trên máy mình:

ssh -T git@github.com
ssh -T git@gitlab.com

-T nghĩa là không yêu cầu terminal, vì bạn không định mở shell trên máy của họ, chỉ kiểm tra xác thực. Lần đầu nối tới một host mới, ssh hỏi bạn có chấp nhận fingerprint của máy chủ hay không. Hãy mở trang tài liệu chính thức của GitHub hoặc của GitLab, so fingerprint hiện trên màn hình với fingerprint họ công bố, rồi mới trả lời yes. Bấm yes theo quán tính là bỏ đi lớp bảo vệ duy nhất chống việc có ai chen vào giữa.

Nếu vẫn bị từ chối, chạy lại với -v để xem ssh làm gì:

ssh -vT git@github.com

Kết quả chi tiết cho bạn thấy ssh đọc file config nào, nó quyết định dùng key nào, và nó mời key theo thứ tự ra sao. Nhờ đó bạn biết ssh có dùng đúng key bạn nghĩ hay không, thay vì chỉ biết là thất bại.

Khi port 22 bị chặn, đi qua cổng 443

Nhiều mạng công ty và một số đường di động chặn kết nối ra ngoài trên port 22. Triệu chứng rất đặc trưng: trình duyệt vào github.com bình thường, còn ssh -T git@github.com treo im rồi hết thời gian chờ. Không phải key sai, mà là gói tin không ra được khỏi mạng.

GitHub có một endpoint SSH chạy trên port 443 cho đúng trường hợp này, là ssh.github.com. GitLab.com có altssh.gitlab.com, cũng trên 443. Sửa lại hai block trong ~/.ssh/config:

Host github.com
  HostName ssh.github.com
  Port 443
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Host gitlab.com
  HostName altssh.gitlab.com
  Port 443
  User git
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Vì tên trong dòng Host vẫn là github.com và gitlab.com, bạn không phải sửa URL remote của bất kỳ repo nào: ssh đọc config rồi tự đổi host và port khi kết nối. Muốn thử trước khi sửa file thì gọi thẳng:

ssh -T -p 443 git@ssh.github.com

Chỉ đổi sang 443 khi port 22 thật sự không đi được. Vẫn là giao thức SSH, vẫn cùng một key và cùng một tài khoản, chỉ khác đường đi và khác cổng.

Tạo SSH key trên Windows

Windows 10 và Windows 11 có sẵn OpenSSH, nên bạn không cần cài PuTTY nữa. Mở PowerShell và làm gần như y hệt Linux:

ssh-keygen -t ed25519 -C "laptop-windows"
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | clip

Key nằm trong %USERPROFILE%\.ssh, tức là C:\Users\<ten-ban>\.ssh. File cấu hình cũng ở đó, là %USERPROFILE%\.ssh\config, và nội dung giống hệt các ví dụ phía trên, viết ~/.ssh/id_ed25519 vẫn được vì ssh trên Windows hiểu dấu ~. Dòng clip đưa public key vào clipboard để bạn dán thẳng vào ô Key trên GitHub hoặc GitLab. Việc dán thì không khác gì trên Linux, vì phía họ chỉ thấy một dòng văn bản.

Windows không có chmod. Quyền ở đây do ACL của NTFS quyết định, và key sinh ra trong thư mục profile của bạn thì mặc định chỉ bạn đọc được, nên bạn bỏ qua hai lệnh chmod ở trên. Nếu không muốn nhập passphrase mỗi lần push, bật service ssh-agent:

Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519

Có một cái bẫy riêng của Windows: Git for Windows mang theo bản ssh của nó, nên bạn nạp key vào agent trong PowerShell mà Git vẫn nói là không có key, vì Git đang gọi một ssh khác và đọc một thư mục khác. Buộc Git dùng ssh của Windows thì cả hai bên nhìn cùng một file config:

git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"

Public key đem cho ai cũng được, private key thì không

Bạn có thể dán cùng một public key lên GitHub, GitLab, Bitbucket, VPS riêng của bạn và máy của đồng nghiệp. Số lượng nơi nhận không làm nó yếu đi. Public key không cho ai đăng nhập vào đâu cả, nó chỉ cho phép bên kia kiểm tra rằng người đang kết nối có giữ private key tương ứng. Nếu bạn vẫn còn lo, đọc public key có thật sự an toàn khi chia sẻ hay không.

Private key thì ngược lại hoàn toàn. Đó là danh tính của bạn, nên nó không rời khỏi máy: không commit vào repo, không gửi qua Zalo hay chat nhóm, không upload lên Drive "để backup". Cần dùng ở máy thứ hai thì sinh một cặp key mới ngay trên máy đó và thêm public key mới vào tài khoản. Làm vậy thì mất một máy bạn chỉ phải xoá một dòng trong danh sách key, thay vì đổi key trên mọi máy bạn có.

Nếu bạn đã dán key, đã đổi remote, đã viết config mà push vẫn bị từ chối, đi tiếp theo các nguyên nhân của Permission denied (publickey) rồi lần lượt loại trừ.

FAQ

Dùng một SSH key cho cả GitHub và GitLab có an toàn không?

An toàn. Hai bên chỉ lưu bản public và không biết gì về nhau. Rủi ro thật nằm ở private key: nếu nó bị lấy, mọi nơi bạn đã thêm public key đều bị ảnh hưởng cùng lúc. Vì vậy hãy chia key theo máy, mỗi laptop một cặp, thay vì chia theo nhà cung cấp. Mất laptop thì bạn xoá đúng một key ở mỗi nơi và không phải cấu hình lại các máy còn lại.

Đổi remote sang SSH rồi có phải clone lại repo không?

Không. git remote set-url origin git@github.com:ten/repo.git chỉ sửa file .git/config, còn commit, branch và file bạn đang sửa không bị ảnh hưởng. Chạy git remote -v sau đó để đọc URL mà repo báo về. Lưu ý mỗi bản clone trên máy phải đổi riêng, và nếu repo có thêm remote upstream thì đổi cả tên đó.

Vì sao git push vẫn báo Permission denied (publickey)?

Hai nguyên nhân thường gặp: ssh đang mời một key khác với key bạn đã thêm vào tài khoản, hoặc quyền của private key quá rộng nên ssh bỏ qua chính key đó. Chạy ssh -G github.com để xem identityfile nào có hiệu lực, thêm IdentitiesOnly yes vào block của host, rồi kiểm tra quyền của ~/.ssh và của private key. Nếu chưa xong, xem danh sách nguyên nhân của Permission denied (publickey).

Mạng công ty chặn port 22 thì làm sao?

Dùng endpoint SSH trên port 443. Với GitHub, đặt HostName ssh.github.com và Port 443. Với GitLab.com, đặt HostName altssh.gitlab.com và Port 443. Giữ nguyên tên trong dòng Host là github.com và gitlab.com thì URL remote không cần sửa. Đây vẫn là SSH với cùng một key, chỉ đi qua cổng mà tường lửa thường để mở.

Tôi có hai tài khoản GitLab, cấu hình ra sao?

Tạo hai key và hai block trong ~/.ssh/config. Một block giữ Host gitlab.com cho tài khoản cá nhân, block kia đặt tên riêng như Host gitlab-work với HostName gitlab.com và IdentityFile trỏ tới key thứ hai. Repo của công ty thì đặt remote là git@gitlab-work:nhom/du-an.git. Cả hai block đều cần IdentitiesOnly yes, nếu không ssh mời cả hai key và GitLab nhận bạn theo key nào tới trước.