メンテナンス版(2026-09-01検証)。 2017年の原文は空ではなく、末尾に全文を保存した。当時のコマンド(未検証の
ssh-keyscan出力を追加する手順を含む)は保存資料であり、現在の手順ではない。以下では、何かを変更する前にホストの同一性、クライアント認証、agent署名、Gitの経路、サーバー認可を切り分ける。
Table of Contents
まず失敗の層を分類する
同時に表示される三つのメッセージが、別々の層を示すことがある。
| トレースの証拠 | 層 | 意味 |
|---|---|---|
REMOTE HOST IDENTIFICATION HAS CHANGED または Host key verification failed | サーバーの同一性 | クライアントがサーバー鍵を信頼できていない。ユーザー鍵の調査はまだ始めない。 |
Permission denied (publickey) | ユーザー認証 | 要求したユーザー名の公開鍵認証が完了しなかった。鍵が存在しないとは限らない。 |
sign_and_send_pubkey: ... agent refused operation | クライアント署名 | SSHはidentityを見つけたか提示したが、agent/providerが署名を拒否した。公開鍵の再登録では直らない場合がある。 |
fatal: Could not read from remote repository | Gitの要約 | GitはSSH transportの失敗を伝えるだけで、どのSSH層かは示さない。 |
known_hosts の削除、全鍵の再生成、サーバー権限の変更を一度に行わない。証拠を失い、一つの失敗を複数に増やすおそれがある。
読み取り中心の証拠採取から始める
Gitを実行するのと同じアカウント、ターミナル、コンテナ、IDE、CIジョブで実行する。sudo git push は別のホーム、設定、agentを選びうるため、権限昇格は避ける。
git remote -v
git remote get-url --all origin
git remote get-url --push --all origin
ssh -G git-prod |
awk '$1 ~ /^(hostname|user|port|identityfile|identitiesonly|identityagent)$/ { print }'
ssh -vvv -o BatchMode=yes -o ConnectTimeout=10 -T git-prod
ssh-add -l -E sha256
printf 'SSH_AUTH_SOCK=%sn' "${SSH_AUTH_SOCK:-<unset>}"
if [ -n "${SSH_AUTH_SOCK:-}" ] && [ -S "$SSH_AUTH_SOCK" ]; then
printf '%sn' 'agent socket exists'
else
printf '%sn' 'agent socket is absent or not a socket'
fi
git-prod はremoteで実際に使うSSHホストまたはaliasに置き換える。ssh -G は有効なクライアント設定を出力して終了する。ssh -vvv ... BatchMode=yes はパスワードやホスト確認を求めずに認証を試し、Gitデータはpushしない。通常のshellサーバーでは -T でも非対話セッションが開く場合があるため、自動終了しなければ診断出力を得たところで停止する。
デバッグログにはアカウント名、ホスト名、IPアドレス、ローカルパスが含まれうる。共有前に伏せる一方、アルゴリズム名、fingerprint、Offering public key、Server accepts key、失敗行の順番は残す。
詳細トレースを順番に読む
- 接続先:
Connecting to ...、port、Authenticating to ... as ...を確認する。正しい鍵でもホストまたはユーザー名が違えば失敗する。 - ホスト検証: 選択されたhost-key algorithmとfingerprintを見る。ホスト鍵エラーはユーザー認証より先に起こる。
- identityの発見:
identity file ... type -1はそのパスにファイルがないことを示す。Offering public keyが実際に提示したidentityを示す。 - サーバー応答: 提示後も
Authentications that can continue: publickeyが続く場合、サーバーがそのユーザー名/鍵の組合せまたはpolicyを受け入れていない可能性が高い。 - 署名:
Server accepts keyの後にsign_and_send_pubkey: ... agent refused operationが出るなら、選択agent、鍵制約、unlock/confirmation状態、hardware providerに絞る。 - repository access: SSH認証が成功してからGit repository pathと認可を調べる。
クライアント版と時刻を添えた伏字済みトレースを保存する。別のマシンからコピーした一行を、このsessionの証拠にしない。
ホスト鍵を安全に検証する
ssh-keyscan はnetwork endpointが提示する鍵を取得するだけで、その鍵を認証しない。OpenSSHは、未検証のscan出力から known_hosts を作ると中間者攻撃にさらされると警告している。
予期するSHA-256 fingerprintを、認証済みまたはout-of-bandの経路で得る。たとえばログイン済みprovider console/公式文書、別に検証済みの経路で連絡したserver administrator、server consoleそのものを使う。次に known_hosts を変更せず比較する。
ssh-keygen -F git.example.com
scan_file=$(mktemp)
ssh-keyscan -T 5 -t ed25519 git.example.com >"$scan_file"
ssh-keygen -lf "$scan_file" -E sha256
非標準portの場合、known-hosts名は通常 [git.example.com]:2222 になる。GitHubでは現在のGitHub公式fingerprintページと比較し、forumやこの記事からfingerprintをコピーして信用しない。
上の ed25519 scanは例であり、compatibilityを保証しない。認証済みsourceが別のhost-key typeを公開しているなら、推測でclient policyを弱めず、実際に提示されるそのtypeを取得して比較する。
out-of-bandの値と完全一致した後だけ、backupしてentryを変更する。
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
if [ -f "$HOME/.ssh/known_hosts" ]; then
known_hosts_backup="$HOME/.ssh/known_hosts.pre-change.$(date +%Y%m%d%H%M%S)"
cp -p -- "$HOME/.ssh/known_hosts" "$known_hosts_backup"
fi
cat "$scan_file" >>"$HOME/.ssh/known_hosts"
chmod 600 "$HOME/.ssh/known_hosts"
rm -f -- "$scan_file"
保存済み鍵が変わった場合、正規のrotationか侵害かを先に調査する。検証とbackupの後、ssh-keygen -R git.example.com(またはhostとportの角括弧形式)で該当する古い名前だけを消し、検証済みの鍵を追加する。StrictHostKeyChecking no、空の known_hosts、blind scan appendで解決してはならない。
identityとagentを確認する
private materialを表示せずfingerprintを一覧する。
ssh-add -l -E sha256
ssh-keygen -lf "$HOME/.ssh/id_ed25519_git_prod.pub" -E sha256
ls -ld "$HOME/.ssh" "$HOME/.ssh/config"
ls -l "$HOME/.ssh/id_ed25519_git_prod" "$HOME/.ssh/id_ed25519_git_prod.pub"
公開鍵fingerprintを、Gitアカウントに登録した鍵または対象ユーザーの authorized_keys と比較する。private keyをserver、ticket、chat、repository、shared directoryへコピーしてはならない。serverに必要なのは一行のpublic keyだけである。
対応するOpenSSHでは、agentがそのpublic keyに対応する署名を実行できるかテストできる。
ssh-add -T "$HOME/.ssh/id_ed25519_git_prod.pub"
ssh-add -l に表示されても、agentが広告していることしか証明しない。sign testや実際のSSHが拒否される原因には次がある。
- desktop keychain、password manager、managed agentがlock中;
- identityが毎回のconfirmationを要求するが、使えるpromptがない;
- FIDO/security keyが不在、lock中、touch/PIN待ち;
SSH_AUTH_SOCKが古い、または意図しないagentを指す;- effective configの
IdentityAgentがSSH_AUTH_SOCKを上書きする; - forwarded agentまたはdestination-constrained agentがこのpathを拒否する;
- providerが要求された署名操作をサポートしない。
identityを所有するagent/providerでunlockまたはconfirmし、再試行する。最初から全identityを削除したりdesktop agentをkillしたりしない。managed agentが本当に存在せず、isolated shell agentを試す場合だけ、そのlifecycleを明示する。
eval "$(ssh-agent -s)"
ssh-add "$HOME/.ssh/id_ed25519_git_prod"
ssh-add -l -E sha256
# When the isolated shell test is finished:
ssh-agent -k
hardware-backedまたは組織managed identityには適した修復ではない。そのproviderを直すか、正しいsocketを選ぶ。
正確なhost、user、keyを固定する
広い Host * exceptionではなく専用aliasを使う。
Host git-prod
HostName git.example.com
User git
Port 22
IdentityFile ~/.ssh/id_ed25519_git_prod
IdentitiesOnly yes
PubkeyAuthentication yes
IdentitiesOnly yes は、agentが多数のidentityを出しても、設定済みidentityを使うようOpenSSHに指示する。結果を確認する。
ssh -G git-prod |
awk '$1 ~ /^(hostname|user|port|identityfile|identitiesonly|identityagent)$/ { print }'
GitHubのSSH usernameはGitHub profile名ではなく git である。self-hosted Git serviceも git を要求する場合がある一方、EC2への直接loginはOS accountを使う。あるserviceのusernameを別のserviceへ流用しない。
client fileは他userから書き込み可能であってはならず、private identity fileは読み取り可能でもいけない。backupを保持し対象を確認した後の保守的なmodeは次のとおり。
chmod 700 "$HOME/.ssh"
chmod 600 "$HOME/.ssh/config"
chmod 600 "$HOME/.ssh/id_ed25519_git_prod"
chmod 644 "$HOME/.ssh/id_ed25519_git_prod.pub"
agent-onlyまたはhardware-backed identityでは、IdentityFile がpublic key fileを指し、agent内の対応private keyを選択できる。providerの公式設定に従うこと。hardware-key stubが設定対象identity fileの場合もある。この例のlayoutに合わせるためにprivate materialをexportしない。
必要な場合だけ新しい鍵を生成する
動作中のidentityを上書きしない。client/server/providerが対応するならEd25519は現代的なdefaultであり、対応環境ではFIDO-backed Ed25519がhardware protectionを提供する。固有のfilenameと環境に合うpassphraseを使う。
umask 077
ssh-keygen -t ed25519 -a 64
-f "$HOME/.ssh/id_ed25519_git_prod"
-C "git-prod"
ssh-keygen -lf "$HOME/.ssh/id_ed25519_git_prod.pub" -E sha256
対応するFIDO hardwareとserverでは:
umask 077
ssh-keygen -t ed25519-sk
-f "$HOME/.ssh/id_ed25519_sk_git_prod"
-C "git-prod hardware key"
serviceの認証済みkey-management pageまたはadministratorのcontrolled processを通じ、.pub の内容だけを登録する。新しい鍵を検証するまで古い鍵を有効にし、その後計画的に失効させる。新しい鍵では、未検証host key、誤ったusername/remote、壊れたagent socketは修復できない。
Git remoteを検証する
Gitはfetch URLとpush URLを別に持てる。insteadOf/pushInsteadOf rewriteが有効な接続先を変えることもある。すべて調べる。
git remote -v
git remote get-url --all origin
git remote get-url --push --all origin
git ls-remote --get-url origin
代表的なSSH形式:
ssh://git@git.example.com:22/team/project.git
git-prod:team/project.git
git@github.com:OWNER/REPOSITORY.git
scp-like URLのalias(git-prod:...)は Host git-prod blockと一致しなければならない。repository owner/pathと、serviceが .git suffixを必要とするかを確認する。旧値を記録するまでremoteを変更しない。修正が必要なら:
old_origin=$(git remote get-url origin)
printf 'old origin: %sn' "$old_origin"
git remote set-url origin git-prod:team/project.git
git remote get-url --all origin
git remote get-url --push --all origin
rollbackは git remote set-url origin "$old_origin"。fetchとpushが意図的に別のlocationを指すなら、それぞれをbackupして変更し、origin にURLが一つだけと仮定しない。
サーバー側を調査する
この節には既存console、recovery channel、または別のadministrator sessionが必要である。SSHを弱めてSSHを回復してはならない。managed Git hostではこの層をproviderが管理するため、filesystemを編集せずaccount/key audit toolを使う。
OpenSSH serverでは要求するOS accountとeffective daemon policyを確認する。
id USERNAME
sudo sshd -T -C user=USERNAME,addr=CLIENT_IP,host=CLIENT_HOST |
grep -E '^(pubkeyauthentication|authorizedkeysfile|strictmodes|pubkeyacceptedalgorithms) '
sudo -u USERNAME ls -ld
/home/USERNAME
/home/USERNAME/.ssh
/home/USERNAME/.ssh/authorized_keys
sudo ssh-keygen -lf /home/USERNAME/.ssh/authorized_keys -E sha256
sudo journalctl --since '-10 min' -u ssh -u sshd --no-pager
log unit名とfile locationはOSごとに異なるため、そのplatformのSSH authentication logを使う。authorized_keys 全文をsupport requestへ貼らない。
defaultの StrictModes では、home directory、.ssh、またはfileが他userから書き込み可能だと sshd は authorized_keys を拒否する。一般的なmodeは .ssh が 700、authorized_keys が 600 で、ownershipはlogin accountと一致させる。repair前にrecovery channelからbackupし、正しいgroupを確認する。
id USERNAME
sudo cp -p
/home/USERNAME/.ssh/authorized_keys
/home/USERNAME/.ssh/authorized_keys.pre-change
sudo chown USERNAME:USERGROUP /home/USERNAME/.ssh
sudo chown USERNAME:USERGROUP /home/USERNAME/.ssh/authorized_keys
sudo chmod 700 /home/USERNAME/.ssh
sudo chmod 600 /home/USERNAME/.ssh/authorized_keys
sudo sshd -t
permission workaroundとして StrictModes no にしない。既存admin sessionを開いたまま sshd -t を通し、OSの手順でreloadし、recovery sessionを閉じる前に別の新規接続を試す。意図した鍵またはaccess policyを誤って変えた場合はbackupを戻す。
EC2固有のusernameとaccessを確認する
EC2のSSH usernameはAWS account名ではなくAMIで決まる。AWSが現在記載する主なdefaultは次のとおり。
| AMI family | 主なdefault username |
|---|---|
| Amazon Linux | ec2-user |
| Ubuntu | ubuntu |
| Debian | admin |
| CentOS | centos または ec2-user |
| Fedora | fedora または ec2-user |
| RHEL/SUSE | ec2-user または root |
| Bitnami | bitnami |
名前を順に試すのではなく、正確なAMIとprovider documentを確認する。instance address、TCP 22(または設定port)へ到達できるsecurity-group route、private keyがinstanceへprovisionされたpublic keyと対応することも確認する。
EC2 machineは、ubuntu@host のようなOS loginと git@host のようなGit service endpointという二つのSSH identityを提供しうる。remote URLとserver designが要求する方を使う。EC2 Instance Connectはtemporary public-key accessも提供し、そのIAM ec2:osuser conditionはOS userに一致する必要がある。いずれもprivate keyをinstanceへコピーする必要はない。
algorithm errorを別に診断する
no matching host key type found、no matching key exchange method found、no mutual signature algorithm はnegotiationの証拠であり、key missingの一般的証明ではない。clientが対応するalgorithmとaliasが選ぶ値を調べる。
ssh -Q HostKeyAlgorithms
ssh -Q PubkeyAcceptedAlgorithms
ssh -Q kex
ssh -G git-prod |
awk '$1 ~ /^(hostkeyalgorithms|pubkeyacceptedalgorithms|kexalgorithms)$/ { print }'
HostKeyAlgorithms はserverを認証し、PubkeyAcceptedAlgorithms はuser authenticationに関わる。またRSA keyはobsoleteな ssh-rsa SHA-1 signature algorithmと同義ではない。両端が対応すればmodern RSA keyはRSA-SHA2を使える。
推奨するrepairは古いendpointのupdate、または対応するmodern keyのprovisionである。文書化され期限付きのcompatibility exceptionが不可避なら、検証したerrorが名指しした一つのdirectiveだけを一つの正確なaliasに限定する。Host * の下へ置かず、推測で両方を有効にしない。
Host legacy-git
HostName legacy-git.example.com
User git
# Temporary only, after verifying the exact negotiation error:
# HostKeyAlgorithms +ssh-rsa
# PubkeyAcceptedAlgorithms +ssh-rsa
ownerとremoval dateを記録する。永久化したtemporary exceptionは未解決のinfrastructure debtである。
rollbackと受入基準
client/server fileを変える前に、一意名でpermissionを保つbackupを作り、現在のfingerprintとremote URLを記録する。client config parsingは次で試す。
ssh -G -F "$HOME/.ssh/config" git-prod >/dev/null
server configはreload前に sudo sshd -t を実行し、動作するrecovery sessionを保持する。証拠が悪化したら一度に一変更だけ戻す。
該当するすべての条件を満たしたときだけrepairを受け入れる。
- server host-key fingerprintが認証済み/out-of-band sourceと一致する。
ssh -G git-prodが意図したhostname、port、username、agent、identity、identitiesonly yesを示す。- agentが広告するkey fingerprintがaccount/server registrationと一致し、必要なagent/providerが拒否せずsignできる。
- 新しい
ssh -vvv -o BatchMode=yes -T git-prodが意図したendpointへ達し、host-key、public-key、agent-refusal failureで終わらない。GitHubではshellを提供せずnonzero statusになる場合があっても、successful-authentication messageが証拠になる。 - 読み取り専用Git transport testが成功し、意図したrepositoryを指す。
git ls-remote --symref origin HEAD
- その後にのみ、review済みの意図したpushを再試行する。
ls-remoteの成功はread authenticationを証明するが、write authorizationやserver hook acceptanceまでは保証しない。実際の意図したwriteが受理されるまでpush修復完了とは言わない。
一次・公式文書
- OpenSSH `ssh(1)`
- OpenSSH `ssh_config(5)`
- OpenSSH `ssh-add(1)`
- OpenSSH `ssh-keyscan(1)` のsecurity warning
- OpenSSH `sshd(8)` と `authorized_keys` permission
- OpenSSH legacy algorithm guidance
- Git `remote` documentation
- Git `ls-remote` documentation
- GitHub:`Permission denied (publickey)` のtroubleshooting
- GitHubの現在のSSH host-key fingerprints
- AWS:EC2 default Linux usernamesと `authorized_keys`
- AWS:EC2 SSH connection errorのtroubleshooting
---
2017年英語原文(逐字保存)
以下は当初 PERMISSION DENIED (PUBLICKEY). という題で2017-05-13に公開され、書き出しメタデータで2023-09-30更新と記録されたWordPress本文の全体である。repository hygieneのため、三つのlist行「Generate your key」「Configure ssh to use the key」「Copy your key to your server」の目に見えない行末空白だけを除いた。見える語句、古いcommand、link、indentation、欠落はすべて変更していない。この保存部を現在のauthorityとして扱ってはならない。
A problem occurred while I `git push` to my git server on ec2.
I handled this by following this three guide of which their original links are:
http://stackoverflow.com/questions/13363553/git-error-host-key-verification-failed-when-connecting-to-remote-repository
https://chenhuachao.com/2016/05/26/ssh%E5%87%BA%E9%94%99-sign-and-send-pubkey-signing-failed-agent-refused-operation/
Sorry for missing out the second source link, I will add that later.
sign_and_send_pubkey: signing failed: agent refused operation
Permission denied (publickey).
fatal: Could not read from remote repository.
1. `mkdir ~/.ssh`
2. `vim known_hosts` – if you already have *known_hosts*, skip this.
3. `ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts`
4. `ssh-keygen -t rsa -C "user.email"`
5. Add the *id_rsa.pub* key to SSH keys list on your GitHub profile.
Table of Contents
Toggle
- [Set up your client](https://blog.lazying.art/en/html/computer_internet/git/29/permission-denied-publickey-2.html/#Set_up_your_client)
## Set up your client
1. Generate your key
- `ssh-keygen`
2. Configure ssh to use the key
- `vim ~/.ssh/config`
3. Copy your key to your server
- `ssh-copy-id -i /path/to/key.pub SERVERNAME`
Your config file from *step 2* should have something similar to the following:
Host SERVERNAME
Hostname ip-or-domain-of-server
User USERNAME
PubKeyAuthentication yes
IdentityFile ./path/to/key
eval “$(ssh-agent -s)”
ssh-add
