LoginSignup
3
0

More than 1 year has passed since last update.

Dotnet CoreプロジェクトでプライベートNuGetフィードを使ってセキュアなDockerイメージを作る

Last updated at Posted at 2022-06-23

はじめに

タイトルに書いた通りの記事です。パブリックなNuGetフィードだけを使った場合は特に気にすることもなくさっくりDockerイメージを作れるのですが、プライベートfeedを使おうとした途端に厄介なことがでてきます。あまりそんなことが必要な人はいないかも知れませんが、自分への備忘録として残しておきます。

TL; DR

主なハマりポイントと解決策を先に列挙しておきます。

ハマりポイント

  • Dockerfile内のdotnet restoreにプライベートフィード(Azure DevOpsなどに置いてある)へのクレデンシャル情報を渡す必要がある
  • クレデンシャルを平文でDockerfileに直接書くのはセキュリティ的によろしくない
  • --build-argsでクレデンシャルを引数として渡すのもダメ(理由は後述)

解決策

  • パーソナルアクセストークン (PAT) を発行してクレデンシャルとして利用する。手動のサインインが不要になる
  • VSS_NUGET_EXTERNAL_FEED_ENDPOINTS環境変数を使ってクレデンシャルをdotnet restoreに渡す
  • Dockerのsecret volume機能を使う

具体的な手順

前提

Azure DevOpsでプライベートfeedを使う(公式ドキュメント)ことを想定して記事を書いてます。

dotnet restoreでデフォルト以外のNuGetフィードを使う

このStackOverflow記事が参考になります。平たく言うとVSS_NUGET_EXTERNAL_FEED_ENDPOINTSという環境変数にJSONの形でフィードのURLとクレデンシャルを渡してやることで、dotnet restoreがデフォルト以外のフィード (-sオプションで指定) からパッケージを持ってきてくれます。こんな感じです。

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
ENV ASPNETCORE_URLS="https://+;http://+"

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
# Auth with private feed
WORKDIR /tool
RUN wget https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh \
   && chmod +x installcredprovider.sh \
   && ./installcredprovider.sh

WORKDIR /src
COPY ["./MyProject.csproj", "MyProject/"]

ARG FEED=https://pkgs.dev.azure.com/...../nuget/v3/index.json
RUN VSS_NUGET_EXTERNAL_FEED_ENDPOINTS='{"endpointCredentials":[{"endpoint":"'${FEED}'","username":"name","password":"パスワード"}]}' dotnet restore -s ${FEED} "MyProject/MyProject.csproj"

10行目で credential provider をインストールしたあと、最後の行でdotnet restoreを呼び出しています。

しかし、このままだとログイン名とパスワードをべたにDockerfile中に埋め込むことになってしまいます。それは避けなければならないですね。かといってdocker buildの最中に手動でログインすることは不可能です。

Azure DevOpsでパーソナルアクセストークン (PAT) を発行する

というわけでパーソナルアクセストークン(PAT)を取得しましょう。GitHubにも同様の機能があるので、ご存じの方はすぐ理解してもらえると思います。公式ドキュメントはここです。公式ドキュメントはPATの操作について一通り網羅しているので長いですが、序盤の"Create a PAT"だけ読めばPATを作れます。作ったら忘れずコピーしておきましょう。

おさらいですが、PATが必要な理由は以下の2点です。

  1. Azure DevOpsのプライベート NuGet feed からパッケージをダウンロードするためにはアクセス権が必要
  2. Dockerfile内でRUN dotnet restoreしたいので、手動でログインすることはできない

ただし公式ドキュメントにもあるようにPATはパスワードとほぼ同等なので、これも安易にそのままDockerfileに埋め込んでgit commitするわけにはいきません。このため必然的にdocker build実行時に外からPATを渡すことになります。

PATをdocker buildに渡す

docker buildに実行時パラメータを渡すための一般的な方法は--build-argsを使うことですが、パスワードを渡すのに使うと危険です。どうしてかというと、docker historyコマンドで丸わかりだからです。ここのブログでくわしく説明されています。

(余談ですが、go buildでパッケージをgitリポジトリから持ってくる場合も同様の問題が起こります。このブログで説明されています。)

secret volume機能を使う

解決策は上記のブログに書いてある通り、Dockerのsecret volume機能を使うことです。

  • PATを適当なローカルのファイルに保存する (下記のMYPERSONALSECURITYTOKENを、実際のPAT文字列に置き換えてください)。
cat MYPERSONALSECURITYTOKEN > ~/.nuget/tokens/mytoken
  • 上記のDockerfileの最終行を以下のように変更する
RUN --mount=type=secret,id=mytoken VSS_NUGET_EXTERNAL_FEED_ENDPOINTS='{"endpointCredentials":[{"endpoint":"'${FEED}'","username":"name","password":"'$(cat /run/secrets/mytoken)'"}]}' dotnet restore -s ${FEED} "MyProject/MyProject.csproj"

RUNコマンドに--mount=type=secretを指定するのがポイントです (イコールが2個使われてて変な感じがしますが)。PATを使ってNuGetフィードから読む場合はusernameは空文字列以外なら何でもいいので、そのままnameとしてあります。

  • docker build--secretオプションをつけて実行する
DOCKER_BUILDKIT=1 docker build --secret id=mytoken,src=$HOME/.nuget/tokens/mytoken -t myproj:1.0.0 .

これで/run/secrets/mytokenがローカル側の~/.nuget/tokens/mytokenに置き換えられます。docker buildの実行履歴にもクレデンシャルが表示されないこともポイントですsrc=には最初~/.nuget/tokens/mytokenと指定したのですが~をホームディレクトリに置き換えてくれずエラーになったので$HOMEとしてます。うーむ。
DOCKER_BUILDKIT=1--secretを使うために必要な環境変数です。あらかじめexport DOCKER_BUILDKIT=1しておけば不要です。まあお好みで。

最終形

というわけで私のDockerfileはこんな感じになりました (フィードのURL等は隠してます)。

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
ENV ASPNETCORE_URLS="https://+;http://+"

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
# Auth with private feed
WORKDIR /tool
RUN wget https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh \
   && chmod +x installcredprovider.sh \
   && ./installcredprovider.sh

WORKDIR /src
COPY ["./MyProject.csproj", "MyProject/"]

ARG FEED=https://pkgs.dev.azure.com/...../nuget/v3/index.json

RUN --mount=type=secret,id=mytoken VSS_NUGET_EXTERNAL_FEED_ENDPOINTS='{"endpointCredentials":[{"endpoint":"'${FEED}'","username":"name","password":"'$(cat /run/secrets/mytoken)'"}]}' dotnet restore -s ${FEED} "MyProject/MyProject.csproj"

COPY . MyProject
WORKDIR "/src/MyProject"
RUN dotnet build "MyProject.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MyProject.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyProject.dll"]

ビルドコマンド

DOCKER_BUILDKIT=1 docker build --secret id=mytoken,src=$HOME/.nuget/tokens/mytoken -t myproj:1.0.0 .

まとめ

基本的には参考にしたドキュメント群にすべて書かれていたのですが、「NuGetのプライベートfeedをDockerfileでセキュアに読む」というニッチな問題そのものズバリのドキュメントがなかったので自分用のメモとして書きました。他の方の参考にもなれば幸いです。

参考文献

  1. https://docs.microsoft.com/en-us/azure/devops/artifacts/get-started-nuget?view=azure-devops&tabs=windows
  2. https://stackoverflow.com/questions/57362453/artifacts-credprovider-and-vss-nuget-external-feed-endpoints-with-docker
  3. https://medium.com/marionete/pass-secure-information-for-building-docker-images-8adeafe08355
  4. https://www.dudley.codes/posts/2020.12.28-dockerfile-build-args-exposed/
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0