สร้าง config ไฟล์สำหรับควบคุมการใช้ ssh key หลายๆคีย์

As buggy as chakkapan (byrd) rapeepunpienpen
caucus
Published in
5 min readFeb 2, 2017
“assorted padlocks hanged on wire” by Rubén Bagüés on Unsplash

วันนี้ขอแชร์ประสบการณ์การใช้การสร้าง config file เพื่อลองรับการใช้ ssh key หลาย key ในเครื่องเดียวกัน เพื่อให้ ssh agent รู้ว่าต้องใช้ key ไหน ในการยืนยันตัวตนกับเซอร์วิสต่างๆ

ใน tutorial นี้จะสร้าง ssh keys ทั้งหมด 4 keys

  • สำหรับ hosting digital ocean 1 key
  • สำหรับ gitlab account 1 key
  • สำหรับ github account 2 keys
    (เพราะผมมี 2 users — ของบริษัท 1 key ของส่วนตัว 1 key)

เริ่มสร้าง ssh key ทั้ง 4 กันเลย

Question: ปกติไม่ได้มีแค่ id_rsa หรอกเหรอ? ทำไมมีหลายคีย์ได้

สามารถมีได้ถ้าตอนสร้างระบุชื่อไฟล์ว่าชื่ออะไร
ถ้าไม่ระบุ ssh-keygen จะใช้ชื่อ id_rsa

เช่น

$ ssh-keygen -t rsa -b 4096 -C "chakkapanr@gmail.com"Generating public/private rsa key pair.
Enter file in which to save the key (/Users/chakkapanr/.ssh/id_rsa):

ขั้นตอนนี้ถ้ากด enter ไปเลย ก็จะได้ id_rsa กับ id_rsa.pub แต่ถ้าใครอยากได้ชื่อคีย์ที่อ่านแล้วเข้าใจง่ายว่าเป็นคีย์สำหรับโฮสท์ไหนก็พิมพ์ลงไปได้เลย (พาร์ทเต็ม)

Enter file in which to save the key (/Users/chakkapanr/.ssh/id_rsa): /Users/chakkapanr/.ssh/digitalocean_rsa

พาสเวิร์ดจะใส่ไม่ใส่ก็ได้ ถ้าไม่ใส่ก็กด enter ข้ามไปเลย

Enter passphrase (empty for no passphrase): 
Enter same passphrase again:
Your identification has been saved in /Users/chakkapanr/.ssh/digitalocean_rsa.
Your public key has been saved in /Users/chakkapanr/.ssh/digitalocean_rsa.pub.
The key fingerprint is: SHA256:uWeZ3gLrCeTEmlkhvbOqqLtz6P4zRLdlmFpkhXcUdgw chakkapanr@gmail.com
The key's randomart image is:
+---[RSA 4096]----+
| o..E+. |
| = ..... |
| + * . |
| . B = . |
| . + @ S |
| o X o.. o |
| .. + + .o= |
|o..o . ..=.. |
|OBoo+ .o ... |
+----[SHA256]-----+

ถึงตรงนี้ผมจะได้มา 2 ไฟล์ เป็นไปรเวทคีย์ และ พับบลิกคีย์

$ ls ~/.ssh/
digitalocean_rsa
digitalocean_rsa.pub

จากนั้นก็บอก ssh-agent ให้รู้จัก key ใหม่นี้

$ ssh-add ~/.ssh/digitalocean_rsa

ขอสารภาพว่า ไม่แน่ใจ ssh-add มีความสำคัญยังไง เหมือนว่าเคยไม่ใช้ ก็ไม่มีปัญหาอะไร?

สร้างแบบนี้ให้ครบทั้ง 4 keys

$ ls ~/.ssh/
digitalocean_rsa
digitalocean_rsa.pub
gitlab_rsa
gitlab_rsa.pub
github_birdbyrd_rsa
github_birdbyrd_rsa.pub
github_office_rsa
github_office_rsa.pub

นำ public key ไปรีจิสเตอร์ตามเซอร์วิสที่ต้องการใช้

เคสของผมก็จะเป็นที่ digital ocean, gitlab และ github x2 accounts

copy ข้อมูลในไฟล์ *.pub ไปแปะให้ตรงตามเซอร์วิส

  • digitalocean_rsa.pub ➡️️ digitalocean.com
  • gitlab_rsa.pub ➡️️ gitlab.com
  • github_birdbyrd_rsa.pub ➡️️ github.com (account ส่วนตัว)
  • github_office_rsa.pub ➡️️ github.com (account office)

เช่น ใน github.com

ถ้าลองใช้งาน โดยไม่มีการเซ็ต config file จะเกิดอะไรขึ้น?

ลองเรียกใช้เซอร์วิสต่างๆโดยไม่มี config file จะพบว่าไม่สามารถใช้งานได้
ได้ permission denied (publickey) ทั้งๆที่เราก็มี key ที่ถูกต้อง

gitlab & github

$ git clone git@gitlab.com:birdbyrd/hello-world.git
Cloning into 'hello-world'...
Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.

digital ocean

$ ssh root@xxx.xxx.xx.xx
root@xxx.xxx.xx.xxx's password:

ที่เป็นแบบนี้เพราะ โดยส่วนใหญ่คำสั่งต่างๆจะใช้ id_rsa (ส่วนที่เป็น private key) ในการยืนยันตัวตนโดยอัติโนมัติ ซึ่งในเคสนี้เราไม่ได้ register ssh key บน digital ocean, github และ gitlab ด้วย id_rsa ทำให้ยืนยันตัวตนไม่ได้

วิธีแก้คือ เราต้องสร้าง config เพื่อจะบอกคำสั่งต่างๆให้รู้ว่าต้องใช้ ssh key จากไฟล์ไหน

มาสร้าง config file กันเลย

เริ่มจากส่วนที่ง่ายกว่าก่อน นั่นคือ digital ocean และ gitlab เพราะแต่ละที่มีเพียง ssh key เดียวเท่านั้น

สามารถสร้างได้ง่ายมากๆด้วยเพิ่ม config ดังนี้

# gitlab
Host gitlab.com
HostName gitlab.com
IdentityFile ~/.ssh/gitlab_rsa
# digital ocean
Host xxx.xxx.xx.xxx
Hostname xxx.xxx.xx.xxx
IdentityFile ~/.ssh/digitalocean_rsa
User root

ค่อนข้างตรงไปตรงมา นั่นคือ

  • Host ชื่อเรียก (เปลี่ยนเป็นชื่อเล่นที่ตั้งไว้ใน host file ได้)
  • HostName ชื่อ host จริงๆ
  • IdentityFile พาร์ทของ ssh key private key ที่จะใช้กับ server นี้
  • User ถ้าหากมีหลาย user ใน server นี้ สามารถระบุ user ได้

ลองเรียกใช้ digital ocean และ gitlab อีกครั้ง พบว่าผ่านแล้ว

digital ocean

$ ssh root@xxx.xxx.xx.xxx
Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.13.0-43-generic x86_64)
root@xxx.xxx.xx.xxx:~#

gitlab

$ git clone git@gitlab.com:birdbyrd/hello-world.git
Cloning into 'hello-world'...
remote: Counting objects: 11405, done.
objects: 2% (117/5822)

github

มาที่ส่วนที่ซับซ้อนกันบ้าง นั่นคือ github ซึ่งมี 2 accounts และต้องใช้ 2 ssh-key
เราสามารถ config ssh ได้ดังนี้

# github office
Host github.com-office
HostName github.com
IdentityFile ~/.ssh/github_office_rsa
User git
# github birdbyrd
Host github.com-birdbyrd
HostName github.com
IdentityFile ~/.ssh/github_birdbyrd_rsa
User git

จะเห็นว่าที่ Host เราจะเพิ่ม user name เข้าไปข้างหลัง github.com

จากนั้นเวลาจะเช็คเอาท์โค๊ด ก็ให้เลือกใช้ hostname ให้ถูกต้องด้วย เช่น

โปรเจ็คที่ 1: account ส่วนตัว
ปกติทำแบบนี้

git clone git@github.com:birdbyrd/hello-world.git

ก็ให้เปลี่ยน host จาก github.com เป็น github.com-birdbyrd ตามที่เซ็ตไว้ใน config

git clone git@github.com-birdbyrd:birdbyrd/hello-world.git
------------

โปรเจ็คที่ 2: account office
ปกติทำแบบนี้

git clone git@github.com:tomserreutons/onkaidosktep.git

ก็ให้เปลี่ยน host จาก github.com เป็น github.com-office ตามที่เซ็ตไว้ใน config

git clone git@github.com-office:tomserreutons/onkaidosktep.git
------------

ทำแบบนี้ก็จะสามารถ checkout code ผ่านได้ด้วยดี

$ git clone git@github.com-office:tomserreutons/onkaidosktep.git
Cloning into 'onkaidosktep'...
remote: Counting objects: 11405, done.
objects: 2% (117/5822)

แล้วโปรเจ็คที่เคย checkout มาก่อนแล้วล่ะ? ตอนนั้นมีแค่ account เดียวเลยยังไม่มีปัญหาอะไร พอมาตอนนี้มี 2 account แล้ว (ส่วนตัว / ออฟฟิศ)
แบบนี้เวลามี commit / push git จะรู้ได้ไงว่าเราอยากใช้ account ไหน

คำตอบของคำถามนี้คือการใช้ git config ช่วยครับ

มาดู config github ของแต่ละโปรเจ็คกัน โดยเช็ค config ที่ชื่อว่า remote ก็จะเห็นว่าแต่ละโปรเจ็คจะยังใช้ Host เก่า (เวอร์ชันไม่มี username ต่อท้าย) ซึ่งแบบนี้ทำให้ git วิ่งมา ssh key ของเราไม่ถูก

วิธีแก้ก็คือ เข้าไปแก้ remote ให้ถูกต้อง

โปรเจ็คที่ 1: account ส่วนตัว

cd /to/birdbyrd/github/project/hello-world
nano .git/config

แล้วแก้ remote origin จาก git@github.com เป็น git@github.com-birdbyrd ตามที่ตั้งค่าไว้ใน ssh config ดังนี้

before

name = birdbyrd
email = chakkapanr@gmail.com
[remote "origin"]
url = git@github.com:birdbyrd/tutorials-dittaa.git
fetch = +refs/heads/*:refs/remotes/origin/*

after

[user]
name = birdbyrd
email = chakkapanr@gmail.com
[remote "origin"]
url = git@github.com-birdbyrd:birdbyrd/hello-world.git
------------
fetch = +refs/heads/*:refs/remotes/origin/*

โปรเจ็คที่ 2: account office

แก้ remote origin จาก git@github.com เป็น git@github.com-office ตามที่ตั้งค่าไว้ใน ssh config ดังนี้

cd /to/tomserreutons/onkaidosktep
nano .git/config

before

[user]
name = chakkapan.rapeepunpi
email = chakkapan.rapeepunpi@tomserreutons.com
[remote "origin"]
url = git@github.com:tomserreutons/onkaidosktep.git
fetch = +refs/heads/*:refs/remotes/origin/*

after

[user]
name = chakkapan.rapeepunpi
email = chakkapan.rapeepunpi@tomserreutons.com
[remote "origin"]
url = git@github.com-office:tomserreutons/onkaidosktep.git
----------
fetch = +refs/heads/*:refs/remotes/origin/*

ลองทดสอบด้วยการ push change ดู น่าจะผ่านได้ด้วยดีทั้ง 2 account และได้ user ที่ต้องในแต่ละ commit :D

อันดับถัดไป bower

bower — https://github.com/xxx/yyy
Please make sure you have the correct access rights and the repository exists.

นี่คือ error ที่คุณ bower ฟ้องเวลาไม่สามารถเข้าถึง component ที่ต้องการได้ ซึ่งสาเหตก็มาจากเรามีการใช้ bower repository จากหลายๆที่

  • บ้างก็คงมาจาก public github repo
  • บ้างก็มาจาก github ส่วนตัวของคุณ
  • ร้ายสุด ก็คงมาจาก github office ซึ่งเป็น private repository

แล้วแบบนี้ bower จะรู้ได้ไงว่าต้องใช้ ssh key ไหนในการดึง component จาก repository ดังกล่าว?

ตอบ bower จะสมารถรู้ได้ด้วย gitconfig อีกแล้วครับท่าน ด้วยการใช้ property พิเศษที่ชื่อว่า url > insteadOf

เช่น ใน project github office ของผม มีการดึง bower ของ internal component ใน github office

"dependencies": { "internal-awesome-js": "2.4.1" }

ถ้าเราสั่ง bower install เฉยๆ ก็น่าจะเห็นบรรทัดนี้ใน log

bower internal-awesome-js#2.4.1 resolved https://github.com/tomserreutons/internal-awesome-js.git#2.4.1

ซึ่งไม่ถูกต้อง

  1. เราอยากใช้ ssh ไม่ใช่ https
  2. เราต้องใช้ ssh ด้วย git url แบบนี้ git@github.com-office

มาลองแก้กันเลย เปิด git config ขึ้นมาโลด

cd /to/tomserreutons/onkaidosktep
nano .git/config

แก้ปัญหาที่ 1 ด้วยการเพิ่ม config ดังนี้

[url "ssh://git@github.com"]
----------
insteadOf = "https://github.com"
--------

ซึ่งหมายความว่า ถ้าเจอ url https://github.com ให้เปลี่ยนเป็น ssh://git@github.com แทน ทำแบบนี้ bower install ก็จะดึงของจาก git ด้วย ssh แทนครับ

จากนั้นแก้ปัญหาที่ 2 ด้วยการเพิ่ม url config อีกเช่นกัน

[url "ssh://git@github.com"]
insteadOf = "https://github.com"
[url "git@github.com-office:tomserreutons"]
------------------------
insteadOf = "git@github.com:tomserreutons"
-----------------

ซึ่งหมายความว่า ถ้ามีการดึงโค๊ดจาก git@github.com:onkaidosktep ให้เปลี่ยนเป็นดึงจาก git@github.com-office:onkaidosktep แทน ซึงก็จะพอเหมาะพอดีกับสิ่งที่เราตั้งค่าไว้ใน ssh config

เราสามารถใช้ git config นี้กับ global config ได้เช่นกัน เพื่อที่จะทำให้มีผลกับทุกโปรเจ็คไปเลย

สรุป

ssh config file สุดท้ายหน้าตาแบบนี้

# gitlab
Host gitlab.com
HostName gitlab.com
IdentityFile ~/.ssh/gitlab_rsa
# digital ocean
Host xxx.xxx.xx.xxx
Hostname xxx.xxx.xx.xxx
IdentityFile ~/.ssh/digitalocean_rsa
User root
# github office
Host github.com-office
HostName github.com
IdentityFile ~/.ssh/github_office_rsa
User git
# github birdbyrd
Host github.com-birdbyrd
HostName github.com
IdentityFile ~/.ssh/github_birdbyrd_rsa
User git

git config file สุดท้ายหน้าตาแบบนี้

# ส่วนนี้อยู่ใน global git config
# gitlab - มี account เดียว ไม่ต้องระบุ user
[url "ssh://git@gitlab.com"]
insteadOf = "https://gitlab.com"
# github - 2 accounts ต้องใช้ account ให้ถูกต้องกับ host
[url "ssh://git@github.com"]
insteadOf = "https://github.com"
[url "git@github.com-office:tomserreutons"]
insteadOf = "git@github.com:tomserreutons"
# ส่วนนี้อยู่ใน git config ของ office
[user]
name = chakkapan.rapeepunpi
email = chakkapan.rapeepunpi@tomserreutons.com
[remote "origin"]
url = git@github.com-office:tomserreutons/onkaidosktep.git
fetch = +refs/heads/*:refs/remotes/origin/*
# ส่วนนี้อยู่ใน git config ของ birdbyrd
[user]
name = birdbyrd
email = chakkapanr@gmail.com
[remote "origin"]
url = git@github.com-birdbyrd:birdbyrd/hello-world.git
fetch = +refs/heads/*:refs/remotes/origin/*

หวังว่าจะมีประโยชน์ โดยเฉพาะเดวีลอปเปอร์ที่มีหลายมิติ (work place)ในการทำงาน Happy Hacking จ้ะ :D

“MacBook Pro with images of computer language codes” by Caspar Rubin on Unsplash

Originally published at buggy.birdbyrd.com on February 2, 2017.

--

--