Dockerイメージの脆弱性アラートをrenovateで自動で対処したい

この記事はFLINTERS Advent Calendar 2022の7日目です。

ECRの自動イメージスキャン、dependabotによるアラートなどコンテナに限ったことではないですが脆弱性を自動検知なんてしてくれる便利な世の中ですが日々のタスクに追われるとなかなか対処まで手を回せないこともよくあるかと思います（ブーメラン）

脆弱性として指摘されそうなDockerfile内の依存関係を自動更新したい....
そこで今回は依存関係自動更新ツールのrenovateをDockerイメージにおける利用方法にフォーカスして調べました。

https://github.com/renovatebot/renovate

# 脆弱性として指摘されそうな依存関係
まずrenovateの前にDockerfile内で脆弱性として指摘されそうな依存関係を整理してみます、↓の`amazoncorretto:18.0.1-alpine3.15`をbaseにjqを追加したDockerfileを例にします。

``` Dockerfile
FROM amazoncorretto:18.0.1-alpine3.15

RUN apk update
RUN apk add --no-cache curl

RUN curl -o /usr/local/bin/jq -L https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64\
  && chmod +x /usr/local/bin/jq
```

今後脆弱性として指摘されそうな依存関係として今回のDockerfileには4種類ありそうに見えます。
* baseimageのversion部分(`amazoncorretto:18.0.1-alpine3.15`の`18.0.1`のところ)
* baseimageのdistributionのversion部分(`amazoncorretto:18.0.1-alpine3.15`の`alpine3.15`のところ)distributionのinstall済みpackageで検出されそう。
* apkによるインストールしたcurlパッケージ部分
* curlでダウンロードした実行バイナリ部分（ecrイメージスキャン、trivyなどはpackage　managerから脆弱性を検出しているのでこれに関してはアラートがでるかは未検証です。）

これら４種類をrenovateで更新できるかみていきます。

# baseimage
renovateのデフォルトで有効になっているdockerの設定を使うことで可能でした。

https://docs.renovatebot.com/docker/

dockerは`-`より後ろの文字列を互換性識別子として扱うので`alpine3.15`部分が変更されることはなく、`amazoncorretto:18.0.1`部分が変更されるそうです。

実際のPRです、良さそうに見えますね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392181/4417f5a2-4dce-2851-8247-ca03a56e3791.png)

# baseimageのdistributionのversion部分
これに関してはversioning-overridesの正規表現を用いて可能に見えますが後述のapkなどのpackageマネージャーによってinstallした依存関係の更新でコンフリクトしそうなため断念しました。まだ私がうまいやり方を見つけれていないのかもしれません。

https://docs.renovatebot.com/modules/versioning/#examples-of-versioning-overrides

# apkによるインストールしたcurlパッケージ部分
packageマネージャーによってインストールした依存関係の更新にはrepologyデータソースを使えば可能でした。
apk以外もrepologyで管理されていれば更新可能です。

https://docs.renovatebot.com/modules/datasource/#repology-datasource

``` json
  "regexManagers": [
    {
      "fileMatch": ["(^|/)Dockerfile[^/]*$"],
      "matchStrings": [
        "#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\nENV.*?_VERSION=\"(?<currentValue>.*)\"\\s"
      ],
      "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}"
    }
  ]
```

``` dockerfile
# renovate: datasource=repology depName=alpine_3_15/curl versioning=loose
ENV CURL_VERSION="7.79.1-r3"

RUN apk update
RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.14/main curl=${CURL_VERSION}
```
ほぼdocument通りですが上記の通りregexManagersでdockerfile内の特定の文字列を抽出しています。
先ほどの`baseimageのdistributionのversion部分`の説で言及した依存更新がコンフリクトしてしまう箇所はdepNameにあたります。repology-datasourceを使用する際のdepNameは`alpine_3_15/curl`のような形式になりdistributionのversion名を記載しなければいけません。。。

実際のPR
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392181/6f5d7a2a-06a4-99ef-e84b-24a64b4d416e.png)


# curl等でダウンロードした実行バイナリ部分
こちらも先ほどのpackageマネージャーを対象とした更新と同様にregexManagersを使い、データソースとしては今回github releasesを使いました。

https://docs.renovatebot.com/modules/datasource/#github-releases-datasource

バイナリがデータソースとして定義済みのものから取得できない場合は更新ができないですがgithub releases以外にもデータソースとしてはgithub tagsにも対応しているようなのが嬉しいですね。今回取り上げているjqはパッケージマネージャーからも取得可能ですが例としてバイナリを直接releaseアーティファクトからダウンロードしています。

```dockerfile
# renovate: datasource=github-releases depName=stedolan/jq versioning=regex:^jq-(?<major>\d+)(\.(?<minor>\d+))$
ENV JQ_VERSION="jq-1.5"

RUN curl -o /usr/local/bin/jq -L https://github.com/stedolan/jq/releases/download/${JQ_VERSION}/jq-linux64\
  && chmod +x /usr/local/bin/jq
```

jqのversion形式は"jq-1.5"の様な形式になっており定義済みversioningを利用できないため正規表現で直接抽出しています。そしてgithub-releasesをデータソースとして使用する際はdepNameはrepository名を記載するとrelease一覧を取得してくれる様です。

実際のPR
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392181/14bc60b9-58e0-19d4-ad4a-0fbc69ff1e8a.png)
release noteも取ってきてくれる様です...!

# 感想
以上がDockerfileにおいてrenovateが自動更新可能そうな依存対象でした。
distributionの更新とpackageマネージャーのコンフリクトはなかなか解決が難しそうですが、ある程度の範囲は自動で更新できそうに見えました。

renovateとdependabotに関してよく比較されることがありますがrenovateの方がdependabotよりも広範囲、柔軟に更新対象を設定できるのが良いところに思えます。設定、導入コストはdependabotの方が低いので一長一短ではありますが寿命の長いシステムであればペイできそうですね。

次回は@Rykuさんの記事になります！