簡體   English   中英

git 從通過 ssh 訪問的遠程計算機推送給出“權限被拒絕(公鑰)”錯誤

[英]git push from a remote machine accessed through ssh gives “Permission denied (publickey)” error

為了解釋我的情況,假設我有 PC1 和 PC2。

我在 PC2 中有一個 git 存儲庫,並且我設置了一個 ssh 密鑰,因此在執行 git 推送時我不必輸入我的憑據。 無論如何,當我從 PC2 推送 git 時,它工作得很好。

現在,如果我從 PC1 到 PC2 的 ssh 到 PC2,我可以編輯文件和 git 添加和提交完美。 但是,奇怪的是,如果我按 git 推送,我會收到一條錯誤消息:權限被拒絕(公鑰)。 致命:無法從遠程存儲庫中讀取。

請確保您具有正確的訪問權限並且存儲庫存在。

如果我 go 返回 git 直接從 PC2 推送,則 git 推送再次起作用。

有沒有辦法讓 git 通過來自 PC1 的 ssh 連接進行推送?

Edit: As of 01/05/21, I still couldn't get this to work using an ssh connection to github, but a workaround that worked for me was to use an https connection instead: that is, change your remote repo url from ssh to https as described in https://docs.github.com/en/free-pro-team@latest/github/using-git/changing-a-remotes-url , and I could git push from an ssh session. 但是,仍然希望使用與 github 的 ssh 連接直接實現此目的。

當您遠程進入機器時,您可能無法訪問您設置的原始密鑰。 當您 ssh 進入 PC2 時,您可以生成另一個密鑰並將其注冊到您要推送到的服務器嗎?

您需要在 ssh session 中運行 ssh-keygen,然后將該密鑰添加到您在 git 服務器上的帳戶中。

編輯:以下解決方案僅在我做的 ssh session 中有效。 在一個新的ssh session中,我還是遇到了同樣的問題。 我將留下這篇文章,以便其他人可以加入並找出如何永久解決該問題。

這是一個適合我的解決方案。

cd ~/.ssh
ssh-keygen

對於提示“輸入要保存密鑰的文件”的提示,您可以按Enter鍵將select默認文件名,或輸入您想要的文件名。 然后按照提示詢問密碼。

cat [name of the key, such as "id_rsa"].pub

復制 cat 命令的 output,包括“ssh-rsa”,但最后不包括您的計算機名稱。 這是您的 ssh 密鑰。

然后,go 到https://github.com/settings/ssh並在那里添加復制的密鑰。

最后,

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/[name of the key without .pub, such as "id_rsa"]

您可以通過以下方式驗證您是否正確執行此操作

ssh -T git@github.com

您應該收到類似的消息

Hi [your GitHub username]! You've successfully authenticated, but GitHub does not provide shell access.

現在,你可以推git!

First, I would like to note that this has nothing to do with Git (except that Git can use ssh), and everything to do with ssh and the way GitHub use ssh to figure out who "you" are, when you come knocking.

ssh 和 ssh-agent(您在自己的答案中使用)的細節變得非常復雜,但原理很簡單。 ssh 的工作方式是使用公鑰/私鑰對

  • 私鑰只有您自己知道。
  • 公鑰是每個人都知道的。

任何擁有公鑰的人都可以加密消息並將其發送給您。 然后,您可以解密該消息並查看其內容。 在 GitHub 的例子中,你給他們公鑰並告訴他們這個公鑰是你的公鑰。

稍后,您的計算機(無論您使用哪一台)調用 GitHub 並為它們提供相同的公鑰 他們查找這個公鑰。 現在,任何人都可以向他們發送這個公鑰,所以他們還不能確定真的是你,但他們相信,因為你給了他們那個特定的公鑰,你就聲稱自己是你自己.

(要設置或添加聲稱是您的公鑰,請參閱此 GitHub 文檔頁面。請注意,在向您發送此頁面的內容之前,GitHub 會查詢您的瀏覽器以猜測您正在使用的操作系統。如果您傾向於使用 Mac瀏覽和Linux框進行編程工作,默認情況下會得到錯誤的指令集。頁面上有可點擊的項目來更改發送的指令集:一定要選擇正確的。)

現在,僅僅因為打電話給 GitHub 的人聲稱是你,並不意味着他們實際上就是你 因此,在接聽了 ssh 式互聯網“電話”並看到這個公鑰后,Git 現在將探測您的 ssh 連接,看看這是否真的是您。 他們生成一個“秘密”(隨機字節)消息,用公鑰加密,然后發送給你。 如果您有私鑰,您的計算機可以解密剛剛生成的“秘密消息”並將原始字節傳回給他們。 這樣做了,他們現在會相信你真的你。

所以這是底層機制:你有一個密鑰和一個公鑰 你給出了公鑰。 任何擁有它的人都可以聲稱是你。 但是你保守秘密,如果有人出現並聲稱是你,那么不確定這是否真的你的實體——在這種情況下是 GitHub——加密某些東西並挑戰你解密它。 如果可以,那么他們相信你就是你。

旁白:為什么你認為你調用訪問GitHub的計算機真的是他們? 萬一別人偷偷拿走了他們的電腦,邪惡帝國現在自稱是GitHub怎么辦? (答案不在本問答中,僅供參考。)

生成密鑰對

使用ssh-keygen 目前,這至少在 Linux、macOS 和 Windows 中很常見。 生成密鑰對后,您如何查找和分發公鑰會有所不同,但它們都使用ssh-keygen制作密鑰對。

你在哪里存儲你的秘密?

要使上述方法起作用,您需要讓您的計算機(筆記本電腦或其他任何東西)存儲一些秘密。 但如果它被存儲了,它有多秘密?

ssh 系統可以加密您的秘密數據,只有在您輸入密碼時才能對其進行解密。 因此,保護您的秘密的一種方法是將其隱藏在另一個秘密之下。 但是你如何存儲和保護這個秘密呢? 一路下來都是烏龜。 ( xkcd 版)

為避免必須一直輸入此密碼,您可以啟動一個代理 代理的工作是暫時記住密鑰,並以授權的方式使用它。 誰被授權,做什么? 嗯,這很棘手,我不會詳細介紹 go,但我們會回到這個。

如果你有多個秘密怎么辦?

GitHub 確實讓您可以存儲多個公鑰。 因此,您可以為每台計算機生成一個公鑰並將其存儲在那里,並且每台計算機都可以擁有自己的密鑰。

如果您有可能丟失您的計算機,並且您希望能夠撤銷該計算機的訪問權限,那么為每台計算機提供一個單獨的公鑰/私鑰對是一個不錯的計划。 所以,考慮這樣做。 但也有可能只有一個秘密,並在多台計算機上使用它。

要在多台計算機上共享公鑰/私鑰對,您通常需要至少將公鑰復制到多台計算機(以便它們都使用相同的公鑰)。 您可以將私鑰保密,具體取決於您是否以及如何使用代理; 見下文。

每種方法都有自己的優點和缺點。

更多關於代理

如果並且當使用 ssh 代理時,您可以並且應該執行以下一項或多項操作:

  • 在您的計算機上運行 session (需要定義session );
  • 如果可能,避免將私鑰存儲在台計算機上;
  • 如果您有多個鑰匙,請使用代理解鎖一些但不是全部鑰匙; 和/或
  • 使用您的 ssh 配置文件(通常在 `$HOME/.ssh/config 中)指定要發送給誰的公鑰

這是復雜和混亂的,各種操作系統的一些細節通過這里展示。 首先,讓我們定義一個session

抽象地說,session 背后的想法是,,在鍵盤上,在電腦上。 你打開多少 windows 都沒關系。 他們都是“你”。 如果您在筆記本電腦 L 上,並且從筆記本電腦遠程登錄到計算機 A 和 B,則計算機 A 和 B 通常出於面向計算機的原因需要啟動新的“會話”。 理想情況下,他們不需要這個,因此 ssh 有一個系統,代理(例如計算機 A 和 B 上的代理)可以通過該系統與其他代理對話。 這樣,您可以在 L(您的筆記本電腦)上啟動一個代理,然后讓 A 和 B 上的代理與 L 上的代理交談,以獲取他們對正確密鑰的臨時訪問權限。

這真的會變得非常混亂。 如果您只有一個密鑰,這至少可以降低復雜性,但您可能需要一個密鑰對來識別“您在家,而不是在工作”,而另一對密鑰對來識別“您在工作,而不是在工作”在家”,例如。 或者,您可能希望每個主機有一個(或多個)密鑰對,以便 L、A 和 B 都有不同的密鑰對。 以防三台機器中的一台被盜或被盜。 我在這里無法為選擇正確的方法。 沒有什么可以代替考慮自己的情況並為自己做出決定的。

無論如何,這里的要點是,如果您確實在一台機器上打開多個 windows,您可能需要告訴您不同的命令解釋器(shell,Linux 上的 bash 實例)來共享 Z21D6F40CFB511982E57424E250A Mac(通過 macOS)有一個非常好的系統,可以在您首次登錄時自動為您設置所有這些; Linux and Windows generally don't (though Linux window managers could be as clever as macOS—I just haven't used one like this myself; the Linux systems I use are often standalone machines that I have to ssh into anyway).

環境變量,或者 ssh-agent 如何判斷它的存在

注意:我不使用Windows,所以這里沒有Windows指導。

On a Linux system, you get a shell —bash, dash, fish, sh, tcsh, zsh, whatever you like—when you first log in. If you're running Linux on a laptop, it might have a window manager that does如上所述,像 macOS 這樣的花哨的東西。 rest 假設它沒有。

類 Unix 系統中的進程是嚴格分層的。 1每個進程都有我們所說的環境,由具有環境變量組成。 在我們使用的 shell 中,我們用這樣的結構來表達這些:

HOME=/home/username
USER=username
TERM=xterm-256color

等等。 傳統上,變量名都是大寫的,根據您的 shell,它們也必須是有效的 shell 變量名。 2任何給定進程中的環境變量集取決於該進程:一旦該進程已啟動並正在運行,它就可以更改、添加、刪除部分或全部等。 此時沒有其他進程可以更改它們:只有原始進程可以做到這一點。 但是,現在這個原始進程正在運行,它可以生成( fork -and- exec )一個進程,並為該新進程提供啟動進程喜歡的任何環境。

作為使用這些 shell(命令行解釋器)之一的人,這對您意味着什么,您可以通過某些環境變量向可以運行程序的程序提供初始環境設置:

$ FOO=bar command arg1 arg2

(假設sh -style shell)使用兩個 arguments 運行給定的命令,但首先將名為FOO的環境變量設置為值bar 因此,運行command的進程以及它運行的任何進程都會繼承此FOO=bar設置。

更具體地說,當使用 Git 時,例如,我們可以運行:

$ GIT_TRACE2=1 git status

獲取有關git前端在運行status子命令時所做的一些信息,以及各種操作需要多長時間。

這種特殊形式為單個命令的持續時間設置一個環境變量。 要為所有未來的命令設置它,直到 shell 本身退出(或更改值),我們使用 shell 的變量設置語法:

$ FOO=bar

但這只是設置了一個普通的shell變量,所以我們再添加一個命令:

$ export FOO

它告訴 shell 將該變量放入給每個命令的導出環境變量集中。 在大多數 shell 中,您可以組合這些:

$ export FOO=bar

它將FOO設置為bar並立即將其導出。

現在,請注意,我們運行的任何命令git statuslsssh等——都會繼承這些設置,但不能更改 shell 的設置 某些子命令所做的任何更改都可以保留在子命令和子命令本身運行的命令中,但是一旦進程退出,它的所有環境設置都會被丟棄(它們是進程本身的一部分,及其整個 memory 圖像被丟棄)。

也就是說,沒有任何命令可以直接設置shell的環境。 只有shell可以設置自己的環境。 但訣竅是命令可以打印 shell 命令(或將它們寫入文件或其他任何內容),然后我們可以要求 shell運行這些命令

If we print, for instance, the command git status , and ask the shell to run the command we printed, the shell will run git status :

$ eval `echo git status`
On branch master

(在我的 shell window 中查看 Git 的 Git 存儲庫)。 如果我們要求shell設置一些環境變量呢?

eval `echo FOO=bar; export FOO`

設置和導出FOO=bar是一種相當愚蠢的方式。 但是,如果我們將這些命令放入某個程序的 output 中並對其進行eval ,我們可以讓 shell 設置和導出環境變量,這些環境變量在將來從該 Z2591C98B701E4248B 實例運行的命令中仍然有效。

這就是ssh-agent所做的。 它將命令打印到其標准 output。 因此,當您在某些計算機上首次啟動代理時,例如在您登錄並擁有一個命令行 window 並已啟動並運行 shell 提示符之后:

$ eval `ssh-agent -s`

例如, ssh-agent所做的是:

  • 啟動代理;
  • 打印以下文本(取自手動運行ssh-agent -s ):
SSH_AUTH_SOCK=/tmp/ssh-ExiC7A6qilWW/agent.11761; export SSH_AUTH_SOCK;
SSH_AGENT_PID=11762; export SSH_AGENT_PID;
echo Agent pid 11762;

通過eval上面的文本,我們讓我們的 shell 設置兩個環境變量,這些變量在 window 本身關閉之前一直存在。


1現代 Linux 允許重新設置進程級,這使得這比以前不那么嚴格,但 inheritance 鏈仍然是自上而下的。 我知道這個陳述主要是行話,但我還沒有找到一個好的方法來真正表達我在這里的意思,除了堅持這是一個腳注的文本。

2一個有趣的技巧,目前我不知道沒有實際價值,是從可以執行此操作的代碼中導出,即通常不在 shell 中,而是在 Python 或 Z0D61F8374CAD1D412F804 的任何變量值中導出。錯誤的 forms:不是var=value的形式,或者使用了 shell 不支持的變量名。 設置好這個“不可能”的環境后,啟動 shell,看看它做了什么。 有些 shell 可能會刪除這些環境變量,有些可能會不理會它們,還有一些可能會做一些奇怪的事情,比如嘗試清理它們。


這種技術的缺陷

這里有一個明顯的缺陷。 代理在 window 中啟動,並打印使shell在 window 中保存SSH_AUTH_SOCK進程 ID 路徑名和SSH_AGENT_PID的內容但是當 window 完成時——當你關閉它或退出 shell 時——那些保存的值就會消失。 如果您已設置 shell 來殺死代理(使用ssh-agent -k ),代理本身也會消失。 現在沒有代理。

如果你想在其他一些 window 中使用代理,當然可以啟動一個新的 window,它啟動一個新的 shell,然后再次使用eval `ssh-agent -s` 這不是很好,但它有效:每個 window 都有一個代理。 每個人都需要輸入自己的密碼來解鎖對密鑰的訪問,這甚至更糟糕。

macOS 的技巧是在您登錄Mac 時啟動代理,然后再啟動任何終端 windows。 每個終端 window 然后繼承登錄級ssh 設置。 我們可以在 Linux 上模擬這個(有點混亂),方法是檢測一些現有的 window 中是否有代理運行,或者如果您的 ZEDC9F0A5A5D57797BF68E37364743 未配置啟動方式(未配置)或系統配置,則在啟動 X 服務器之前啟動代理。 它來自 xdm

代理轉發

最后一個相對較大的障礙與 ssh代理轉發有關。 如果您想將所有密鑰保存在一個系統上,例如筆記本電腦 L,但在從LAB運行ssh后能夠在機器 A 和 B 上使用它們,有一個很好的方法可以做到這一點代理轉發

要啟用代理轉發,請使用ssh -A或在 ssh 配置中設置ForwardAgent yes 這使機器 A 上的 ssh 代理(當您從筆記本電腦 L ssh -A machine-A時)訪問 ssh 代理在機器 L 上的機器上。所以現在, L(您的筆記本電腦)。 這樣,機器 A 可以像您一樣向 GitHub 進行身份驗證,即使機器 A 上只有您的公鑰 With machine A having claimed to be you, when GitHub come back to machine A and say prove it: decrypt this string of bytes , machine A's ssh agent asks machine L's ssh agent to decrypt the bytes, gets the decrypted bytes, hands them to GitHub ,而GitHub現在相信機器A其實就是你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM