Codebuild と Gitlab 連携
CodeBuild と Gitlab
AWS Codebuild はネイティブな Gitlab 連携をサポートしていません。
一方で、Github Enterprise を選択することで Gitlab と連携する方法が紹介されています。 qiita.com qiita.com
こちらの方法を試してみようと思い、CodeBuild -> ALB -> Gitlab 経由で接続を試みましたが、「CLIENT_ERROR: authentication required for primary source」 というエラーに悩まされました...
これは CodeBuild が Gitlab リポジトリからソースをダウンロードする際に発生する認証エラーになります。
設定ミスかな~?などの自問自答を繰り返し試行錯誤していましたが、接続先を Public なリポジトリにしても認証エラーが発生するのでメダパニーマ状態。
今回はこのエラーで調査した内容をまとめます。
環境
nginx をプロキシとして利用して、CodeBuild-> nginx -> Gitlab の経路で接続します。 nginx を間に挟む理由は、エラーの内容をデバッグするためです。
Gitlab
認証 Token の動きをみるため、まず初めに Internal / Private なリポジトリで検証を進めていきます。
nginx の設定
以下のように設定します。
upstream backend { server [gitlab host]:9010 weight=1; } server { listen 8888; server_name [proxy host]; location / { proxy_pass http://backend; } }
nginx のログ
nginx のログ level を debug にしておきます。
Gitlab Deploy Token
公式の手順に従ってユーザーと Token を作成します。 ここでは、ユーザー名を "hoge"、作成された Token を "m21md2SLq3o3ueKwcVYM" として進めていきます。
Github Enterprise 個人用アクセストークン
Github Enterprise では個人用アクセストークンを入力するフィールドがあります。 この入力内容は Authorization Header の Basic 認証 Token として利用されることになります(後述)。 ここでは以下のように、ユーザー名を "hoge"、パスワードを "m21md2SLq3o3ueKwcVYM" として設定します。
CodeBuild の実行とエラーログ確認
この状態で CodeBuild を実行すると、nginx の access.log は以下のようになります。
[NATGW_IP] - hoge [13/Jun/2020:18:47:33 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-" [NATGW_IP] - hoge [13/Jun/2020:18:47:34 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-" [NATGW_IP] - hoge [13/Jun/2020:18:47:35 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-" [NATGW_IP] - hoge [13/Jun/2020:18:47:36 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-" [NATGW_IP] - hoge [13/Jun/2020:18:47:37 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-" [NATGW_IP] - hoge [13/Jun/2020:18:47:38 +0000] "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "-" "git/1.0" "-"
検証ということもあり、root ユーザーでリポジトリ(git-test)を作成しています。[NATGW_IP] は Private Subnet で CodeBuild を実行したときのソース IP(NAT Gateway の IP) となります。
nginx のデフォルトのログフォーマットでは、"hoge" が $remote_user になります。 この $remote_user は、Basic 認証時に利用されるユーザー名になり、リターンコードが 401 で返却されており、認証に失敗していることがわかります。 nginx の error.log を確認すると、以下のようなログが出力されています。
2020/06/13 19:31:06 [debug] 1089#0: *23 http proxy header: "User-Agent: git/1.0" 2020/06/13 19:31:06 [debug] 1089#0: *23 http proxy header: "Accept: */*" 2020/06/13 19:31:06 [debug] 1089#0: *23 http proxy header: "Authorization: Basic aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTTp4LW9hdXRoLWJhc2lj" 2020/06/13 19:31:06 [debug] 1089#0: *23 http proxy header: "Accept-Encoding: gzip" 2020/06/13 19:31:06 [debug] 1089#0: *23 http proxy header: "GET /root/git-test.git/info/refs?service=git-upload-pack HTTP/1.0 Host: backend Connection: close User-Agent: git/1.0 Accept: */* Authorization: Basic aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTTp4LW9hdXRoLWJhc2lj Accept-Encoding: gzip "
この Autorization の Token をデコードしてみます。
$ echo -n "aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTTp4LW9hdXRoLWJhc2lj" | base64 -d hoge:m21md2SLq3o3ueKwcVYM:x-oauth-basic
...あれ?末尾に ":x-oauth-basic" が付与されていますね。 上記 Deploy Token で作成したユーザーと Token は "hoge:m21md2SLq3o3ueKwcVYM" ですが、CodeBuild のリクエストでは "hoge:m21md2SLq3o3ueKwcVYM:x-oauth-basic" で Gitlab の認証を行うため、401 のエラーを返していました。
対策
"hoge:m21md2SLq3o3ueKwcVYM" を base64 エンコードすると以下になります。
$ echo -n "hoge:m21md2SLq3o3ueKwcVYM" | base64 aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTQ==
これより nginx の設定を以下のように変更します。
upstream backend { server [gitlab-host]:9010 weight=1; } server { listen 8888; server_name [proxy-host]; location / { set $authorization_value $http_authorization; if ($http_authorization = "Basic aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTTp4LW9hdXRoLWJhc2lj") { set $authorization_value "Basic aG9nZTptMjFtZDJTTHEzbzN1ZUt3Y1ZZTQ=="; } proxy_set_header Authorization $authorization_value; proxy_pass http://backend; } }
nginx を再起動して、CodeBuild を実行すると... 失敗を繰り返していた DOWNLOAD_SOURCE に成功しました!
Public リポジトリに接続するには?
上記は Private / Internal のリポジトリを対象としていましたが、Public なリポジトリに接続するにはどうしたらよいでしょうか? 想定では、nginx の設定で空の Authorization Header に置換すればできそうな気がします。 これを実際に試してみます。
まずは CodeBuild のソース編集画面から、Github Enterprise 個人用アクセストークンで hoge のみを指定。
ここでは hoge ユーザーのみを指定します。 ユーザーもパスワードも指定しない、ということもできます。
CodeBuild から Gitlab への Authorization Header リクエスト内容は以下のようになります。
$ echo -n "hoge:x-oauth-basic" |base64 aG9nZTp4LW9hdXRoLWJhc2lj
今回は Public なリポジトリから Pull したいので認証は不要です。nginx の設定で空の Authorization Header に置換するようにします。
upstream backend { server [gitlab-host]:9010 weight=1; } server { listen 8888; server_name [proxy-host]; location / { set $authorization_value $http_authorization; if ($http_authorization = "Basic aG9nZTp4LW9hdXRoLWJhc2lj") { set $authorization_value ""; } proxy_set_header Authorization $authorization_value; proxy_pass http://backend; } }
nginx を再起動して、CodeBuild を実行すると...無事に Public なリポジトリに接続することができました!
ちなみに nginx には以下のように Authorization Header が書き換えられたログが出力されています。
(略) 2020/06/13 21:57:25 [debug] 1879#0: *5 http script complex value 2020/06/13 21:57:25 [debug] 1879#0: *5 http script var: "Basic aG9nZTp4LW9hdXRoLWJhc2lj" 2020/06/13 21:57:25 [debug] 1879#0: *5 http script set $authorization_value 2020/06/13 21:57:25 [debug] 1879#0: *5 http script var 2020/06/13 21:57:25 [debug] 1879#0: *5 http script var: "Basic aG9nZTp4LW9hdXRoLWJhc2lj" 2020/06/13 21:57:25 [debug] 1879#0: *5 http script value: "Basic aG9nZTp4LW9hdXRoLWJhc2lj" 2020/06/13 21:57:25 [debug] 1879#0: *5 http script equal 2020/06/13 21:57:25 [debug] 1879#0: *5 http script if 2020/06/13 21:57:25 [debug] 1879#0: *5 http script value: "" 2020/06/13 21:57:25 [debug] 1879#0: *5 http script set $authorization_value (略)
SSL 通信の場合でも CodeBuild から接続できる?
上記は全て HTTP プロトコルで検証を進めてきました。 本番環境では Gitlab や nginx へのアクセスも、SSL 通信を意識することが多いでしょう。 ここでは以下のような構成でもう少し動作確認を進めてみます。
- CodeBuild
- nginx
- 今回は自己証明書を作成(server.crt / server.key)
- SSL のリバースプロキシ
upstream backend { server [gitlab host]:6443 weight=1; } # HTTP -> HTTPS リダイレクトなども考える(今回は入れませんでした) server { listen 6443 ssl; server_name [nginx host]; ssl_protocols TLSv1.2; ssl_ciphers EECDH+AESGCM:EECDH+AES; ssl_ecdh_curve prime256v1; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; location / { if ($http_authorization = "Basic aG9nZTp4LW9hdXRoLWJhc2lj") { set $authorization_value ""; } proxy_set_header Authorization $authorization_value; proxy_pass https://backend; } }
都合上 6443 ポートを使用するにようしています(gitlab 起動時に 6443 を SSL の Port に指定しています)
- gitlab
- 今回は自己証明書を作成(gitlab.crt / gitlab.key)
Gtilab リポジトリを HTTPS プロトコルで指定(https://[nginx host]:6443/root/git-test.git)して CodeBuild を実行します。 nginx で SSL 終端が行われ、これまでと同様に Authorization Header が削除されます。 その後、gitlab から無事にソースをダウンロードすることができました。
実際に proxy_pass を指定する場合は、DNS cache に注意する必要があります。 www.subthread.co.jp
まとめ
- CodeBuild から Gitlab を直接呼び出そうとすると、401 の 認証エラーが発生します
- CodeBuild から ALB などを経由して Gitlab に接続しても同様のエラーが発生します
- これは、CodeBuild の Github Enterprise で Gitlab リポジトリに接続しようとすると、Authorization Header に想定外の文字列(":x-oauth-basic")が付与されるからです
- CodeBuild から Gitlab に接続するためには、Authorization Header を適切な形式に変換する必要があります(":x-oauth-basic" を削除)
- nginx などのプロキシを用いることで、Authorizaiton Header を変換して CodeBuild から Gitlab の接続ができるようになります