この記事はFLINTERS Advent Calendar 2022の7日目です。
ECRの自動イメージスキャン、dependabotによるアラートなどコンテナに限ったことではないですが脆弱性を自動検知なんてしてくれる便利な世の中ですが日々のタスクに追われるとなかなか対処まで手を回せないこともよくあるかと思います(ブーメラン)
脆弱性として指摘されそうなDockerfile内の依存関係を自動更新したい....
そこで今回は依存関係自動更新ツールのrenovateをDockerイメージにおける利用方法にフォーカスして調べました。
脆弱性として指摘されそうな依存関係
まずrenovateの前にDockerfile内で脆弱性として指摘されそうな依存関係を整理してみます、↓のamazoncorretto:18.0.1-alpine3.15
をbaseにjqを追加した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から脆弱性を検出しているのでこれに関してはアラートがでるかは未検証です。)
これら4種類をrenovateで更新できるかみていきます。
baseimage
renovateのデフォルトで有効になっているdockerの設定を使うことで可能でした。
dockerは-
より後ろの文字列を互換性識別子として扱うのでalpine3.15
部分が変更されることはなく、amazoncorretto:18.0.1
部分が変更されるそうです。
baseimageのdistributionのversion部分
これに関してはversioning-overridesの正規表現を用いて可能に見えますが後述のapkなどのpackageマネージャーによってinstallした依存関係の更新でコンフリクトしそうなため断念しました。まだ私がうまいやり方を見つけれていないのかもしれません。
apkによるインストールしたcurlパッケージ部分
packageマネージャーによってインストールした依存関係の更新にはrepologyデータソースを使えば可能でした。
apk以外もrepologyで管理されていれば更新可能です。
"regexManagers": [
{
"fileMatch": ["(^|/)Dockerfile[^/]*$"],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\nENV.*?_VERSION=\"(?<currentValue>.*)\"\\s"
],
"versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}"
}
]
# 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名を記載しなければいけません。。。
curl等でダウンロードした実行バイナリ部分
こちらも先ほどのpackageマネージャーを対象とした更新と同様にregexManagersを使い、データソースとしては今回github releasesを使いました。
バイナリがデータソースとして定義済みのものから取得できない場合は更新ができないですがgithub releases以外にもデータソースとしてはgithub tagsにも対応しているようなのが嬉しいですね。今回取り上げているjqはパッケージマネージャーからも取得可能ですが例としてバイナリを直接releaseアーティファクトからダウンロードしています。
# 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
release noteも取ってきてくれる様です...!
感想
以上がDockerfileにおいてrenovateが自動更新可能そうな依存対象でした。
distributionの更新とpackageマネージャーのコンフリクトはなかなか解決が難しそうですが、ある程度の範囲は自動で更新できそうに見えました。
renovateとdependabotに関してよく比較されることがありますがrenovateの方がdependabotよりも広範囲、柔軟に更新対象を設定できるのが良いところに思えます。設定、導入コストはdependabotの方が低いので一長一短ではありますが寿命の長いシステムであればペイできそうですね。
次回は@Rykuさんの記事になります!