要点だけ
期限切れのトークンが残っていて、そのせいで Denied となっている可能性あり。その場合は以下コマンドでログイン情報を破棄しよう。
docker logout [SERVER]
# Docker Hub なら
docker logout
# ghcr.io なら
docker logout ghcr.io
あるとき
GitHub Container Registry (ghcr.io) からイメージを pull しようとしたときのこと。
docker pull ghcr.io/doxygen/doxygen
こちら、Doxygen のイメージは Public で公開されており、誰でも自由に取得できる品物。
のはずが?
Error response from daemon: Head "https://ghcr.io/v2/doxygen/doxygen/manifests/latest": denied: denied
あれ?denied だって?
とりあえずサーバの状況確認
(本題のログイン情報の前に一応これもチェック)
GitHub Status を見てみよう。落ちてることたまにあるし。
確認したときは、たまたま 'Repository search is degraded' となっていましたが、イメージ pull とは関係なさそうだし、一方でそれと関係しそうな Packages は正常。
原因はログイン情報
どうしたものかと思っていたら、思い当たることが。
ghcr.io へイメージを push するには、当然ログインが必要になるわけですが、このログインのパスワードは GitHub アカウントのパスワードでは通りません。
$ docker login ghcr.io -u aKuad
Password: (ここで GitHub アカウントパスワードを入れても通らない)
Personal access token (classic) を生成して、それをパスワードとして入力する必要があります。
イメージの push にはトークンの権限に 'write:packages' が必要
ログイン時の Username は GitHub アカウントと同じまま
Classic でない方のトークン、Fine-grained personal access tokens はイメージの push に対応していないようなので注意
そしてここからが問題。このトークン、有効期限を無期限に設定することも可能ですが、「どうせ 1回 push するだけだし」と、最も短い 7日に設定していました。
期限が切れれば、当然そのトークンを使用しては認証が通りません。しかし docker クライアントは、そのパスワード (トークン) の有効期限が切れていることは知りませんから、期限切れ後も引き続きそのトークンで認証を試みるわけです。
Public イメージ、Private イメージ問わず、ghcr.io へアクセスする度に期限切れトークンで認証を試みて、結果 Denied となる。これだ。
じゃあログイン情報を破棄、つまりログアウトすれば良いわけだ。
$ docker logout ghcr.io
Removing login credentials for ghcr.io
晴れて、Public イメージを Pull できるようになりましたとさ。めでたしめでたし。

