gitlab-runnerにrefusing to work with credential missing host fieldと言われたら

GitLabでCIしていたら特定のホストで動いたときだけ fatal: refusing to work with credential missing host field というエラーが発生した。
あまり情報も整理していないのでメモ程度だが、残さないよりマシと思って書き残すことにした。

結論:

  • GitLabにHTTPSでアクセスしている、かつ、HTTPSでクライアント証明書の提示を要求していて、gitlab-runner側に使う証明書と鍵を設定している。(この時点でたぶん、大多数の人は当て嵌まらないハズ。)
  • クライアント側証明書に使われている署名のハッシュアルゴリズムがSHA-1になっていて、opensslに蹴られていた。

とはいえ、opensslがなんらかの理由でイヤと言えば全部このエラーになってしまう気がするので、調べ方も重要であろう。

gitlab-runnerはいろんな環境変数やらなんやらを設定してgitを叩くので、手動で実行しても同じ問題は再現しづらい。
そこで、gitlab-runnerに細工をして実行することにする。
具体的には、以下を実施した。

  • 環境変数を設定した状態でgitlab-runnerを起動する。GIT_TRACE=1, GIT_CURL_VERBOSE=1あたりが特に有効。詳しくは Git-Internals-Environment-Variables を読まれたし。
  • runnerが作る一時ファイルはすぐに消されてしまうものがあるので、必要ならshell scriptなどで書いた偽のgitを作り、PATHを通しておく。

今回だと、GIT_TRACE=1GIT_CURL_VERBOSE=1 を設定することで、GitLab側のJob出力で以下を得た。

16:23:14.638471 http.c:845              == Info: could not load PEM client certificate from /var/tmp/gitlab_runner/builds/(中略)/ほげほげ.tmp/CI_SERVER_TLS_CERT_FILE, OpenSSL error error:0A00018E:S

このファイルを調べようとしたが、普段は存在しない。実行中だけ存在するものと考えて偽gitにファイルコピーを仕込んだ結果、この証明書はgitlab-runnerに設定しているものと同じと分かった。
また、opensslのエラーコードから、これはどうやらmd too weakというエラーらしいことが分かり、ハッシュアルゴリズムを疑った。
(実際、このエラーはFreeBSD 14なシステム上のgitlab-runnerだけで起きていて、13では起きなかった。opensslのバージョン違いによる差であったのだろう。)

謝辞: manを読んでおきながら読み飛ばして GIT_CURL_VERBOSE に気づいてなかったとき、教えていただいたzunda氏に感謝します。

余談: 今回の問題発生時は最初にGitLabサーバのHTTPSポートに接続してTCP handshakeを行い、直後にgitlab-runner側が切断するという不可解な動作をしていた。
どうやら証明書のチェックはTCP connect後にやっているらしい。
いずれにせよ、切断(先にFIN送出)しているのがgitlab-runner側なので、gitlab-runner側の問題であることは明白だったのだが、少々混乱もした。