- كل المقالات
- استخدم SSH بدل HTTPS مع Git
استخدم SSH بدل HTTPS مع Git
أوقفت GitHub قبول كلمات المرور مع Git عبر HTTPS في آب 2021. وcredential helper يريحك من الكتابة لا من انتهاء الصلاحية؛ أما المفتاح فيريحك منهما معًا، ويطال روابط HTTPS التي لم تكتبها أنت.
قبل عشرة أيام أوقفت GitHub قبول كلمات مرور الحسابات مع Git عبر HTTPS. فإن كنت تدفع تعديلاتك عبر HTTPS صرتَ اليوم تلصق personal access token بدل كلمة المرور: سرٌّ أطول، محفوظٌ حيث كانت كلمة المرور، تنتهي صلاحيته في موعدٍ عليك أن تتذكّره.
ويكفيك credential helper عناءَ الكتابة — osxkeychain أو Git Credential
Manager أو gh auth login — وإن كان الـtoken يناسبك فهو جوابٌ وجيه. لكنّ ما لا
يكفيك إياه انتهاءُ الصلاحية: فكل بضعة أشهر تنتهي صلاحيته، فتعود إلى صفحة
الإعدادات تولّد غيره وتحاول أن تتذكّر أيّ scopes كانت له.
أما مفتاح SSH فلا صلاحية له تنتهي. تُولّد زوجًا مرة واحدة، وتُبقي النصف الخاص على جهازك، وتُسلّم النصف العام إلى الخادم، ويبقى الأمر قائمًا حتى تُلغيه أنت. وهو يبلغ أيضًا ما لا يبلغه الـhelper: روابط HTTPS التي لم تكتبها أنت، في مستودعاتٍ وملفات تبعيّاتٍ يملكها غيرك.
والخطوتان الأوليان تجدهما في كل شرح. أما الخطوتان بعدهما فهما ما يجعل هذا الإعداد يثبت.
توليد المفتاح
ssh-keygen -t ed25519 -C "personal github" -f ~/.ssh/personal-github
Ed25519 بدل RSA: مفاتيح أقصر، ومصافحةٌ أسرع، ولا وسيطَ لحجم المفتاح تخطئ فيه.
وإن كان ما تتعامل معه أقدم من OpenSSH 6.5 فالبديل -t rsa -b 4096.
سمِّ المفتاح باسم ما يفتحه — personal-github أو work-gitlab — وشغّل
ssh-keygen مرةً لكل حساب. فـGitHub ترفض مفتاحًا عامًّا مسجَّلًا في حسابٍ آخر،
وحسابان يفرضان مفتاحين شئتَ أم أبيت. وولّد على كل جهازٍ مفتاحه، ولا تنقل نصفًا
خاصًّا من جهازٍ إلى جهاز: فمفتاحٌ يوجد في مكانٍ واحد مفتاحٌ تُلغيه من مكانٍ
واحد. أما id_ed25519_2 فلن يخبرك بشيء يوم تحتاج إلى إلغائه.
واستعمل عبارة مرور؛ فهي مع ssh-agent تكلّفك طلبًا واحدًا في الجلسة، لا طلبًا
مع كل دفعة.
إضافة المفتاح العام إلى الخادم
انسخ النصف العام، أي الملف المنتهي بـ .pub. أما الذي بلا امتدادٍ فيبقى حيث
وُلِّد:
# macOS
pbcopy < ~/.ssh/personal-github.pub
# Linux
xclip -selection clipboard < ~/.ssh/personal-github.pub
ثم ألصقه في إعدادات حسابك: GitHub أو GitLab أو Bitbucket.
إخبار SSH بأيّ مفتاح يستعمل
يجرّب SSH إن تُرك وشأنه الأسماءَ الافتراضية — id_ed25519 وid_rsa — فلا
يُعرَض مفتاحٌ اسمه personal-github أصلًا. في ملف ~/.ssh/config:
Host github.com
User git
IdentityFile ~/.ssh/personal-github
IdentitiesOnly yes
AddKeysToAgent yes
Host gitlab.com
User git
IdentityFile ~/.ssh/work-gitlab
IdentitiesOnly yes
AddKeysToAgent yes
وIdentitiesOnly yes سطرٌ لا غنى عنه. فبدونه يعرض SSH كل مفتاحٍ لدى
ssh-agent، وتوثّقك GitHub بالحساب الذي يطابق أولَ مفتاحٍ ينطبق — فيعود مستودعٌ
تملك صلاحية الوصول إليه بجواب «غير موجود». يبدو ذلك خللًا في الصلاحيات وليس
كذلك.
وعلى macOS يحفظ UseKeychain yes في كل كتلة عبارةَ المرور في Keychain فتبقى
بعد إعادة التشغيل. غير أنّ الخيار من نسخة آبل من OpenSSH لا من الأصل، فإن كانت
نسخة Homebrew من openssh أسبقَ في PATH رفض الإقلاع. وحمّل المفتاح إلى
Keychain مرةً واحدة بـ ssh-add -K ~/.ssh/personal-github.
واتّصل مرةً واحدة، لتتحقّق من المفتاح ولتجيب عن السؤال الذي يطرحه SSH أولَ مرةٍ يلتقي فيها خادمًا:
ssh -T git@github.com
يعرض السؤال بصمة مفتاح الخادم. قارنها بالبصمات التي تنشرها GitHub قبل أن تجيب
بـ yes: فهذا السؤال هو كلّ ما يفعله SSH من توثيقٍ للخادم، وهو أسهل ما في هذه
الصفحة أن تجيب عنه بلا تفكير. وبعده تعني تحيةٌ باسم مستخدمك أن المفتاح يعمل.
وإن تعلّق الاتصال بدل ذلك فالأرجح أن الشبكة تحجب المنفذ 22، وGitHub تستقبل SSH على المنفذ 443 أيضًا:
Host github.com
Hostname ssh.github.com
Port 443
# ...the rest of the block as above
والأولى أن تضبطه قبل أن تحتاجه، فبعد الخطوة التالية لن يبقى HTTPS متاحًا مخرجًا احتياطيًا.
إعادة كتابة روابط HTTPS إلى SSH
لا ينفع المفتاح ما دامت عناوين مستودعاتك البعيدة تبدأ بـ https://. وبدل
إصلاحها مستودعًا مستودعًا، اطلب من Git أن يستبدلها، في ملف ~/.gitconfig:
[url "ssh://git@github.com/"]
insteadOf = https://github.com/
[url "ssh://git@gitlab.com/"]
insteadOf = https://gitlab.com/
[url "ssh://git@bitbucket.org/"]
insteadOf = https://bitbucket.org/
الآن يُعاد كتابة كل رابط HTTPS لتلك المواقع قبل أن يغادر الطلبُ جهازك، بما في
ذلك الروابط التي لم تكتبها أنت. وsubmodules وحدها تبرّر هذا الإعداد: ملف
.gitmodules أودعه غيرك، يشير إلى HTTPS، في مستودعٍ لا رأي لك فيه. ومثله كل
تبعيّةٍ تسمّي مستودع Git برابط HTTPS — حزمة pod فيها سطر :git =>، أو
go get تحت GOPRIVATE، أو حزمة npm مكتوبة git+https://.
وهناك أمران لا يشملهما هذا الإعداد، وهذا هو الصحيح. فـpackage managers التي
تجلب أرشيفًا بدل أن تستنسخ لا تسلّم Git رابطًا أصلًا: انتقلت CocoaPods إلى CDN
في الإصدار 1.8، وGo proxy يجلب الوحدات العامة، واختصار npm بصيغة
github:user/repo ينتهي إلى أرشيفٍ من codeload. وهذا الملف ~/.gitconfig على
جهازك أنت، وCI runner له مستخدمه ومجلده الخاص ولن يراه أبدًا. وهذا هو المطلوب:
فالمفروض أن يوثّق CI نفسه بـtoken محدود الصلاحيات عبر HTTPS، لا بمفتاحك
الشخصي.
التحقق من إعادة الكتابة
يجري الاستبدال داخل Git قبل الشبكة، فيمكنك أن تراه دون أن تتصل بشيء:
git ls-remote --get-url https://github.com/omaralbeik/SwifterSwift.git
فإن طبع ssh://git@github.com/omaralbeik/SwifterSwift.git فالاستبدال يعمل.
والجأ إلى هذا بدل استنساخ مستودعٍ عام للاختبار: فاستنساخ المستودعات العامة عبر
HTTPS ينجح بلا بيانات اعتماد في الحالين، فلا يدلّك نجاحه على شيء.
وهذا هو الترتيب كلّه: Git يعيد كتابة الرابط، وSSH يختار المفتاح الذي يخصّ الخادم. لا صلاحية تنتهي، ولا شيء تلصقه.