如何在Docker Multi-Stage Builds使用Private Nuget Service還原套件

如何在Docker Multi-Stage Builds使用Private Nuget Service還原套件

在使用 multi-stage builds 進行建置 .NET / .NET Core 應用程式時發現一個有趣的事,在 docker build 內進行 nuget.org 還原時可正常還原套件。但如果是使用到 Private Nuget Service 時,例如,我們採用的是 Azure Artifacts - NuGet 服務,就會取得一堆紅色的錯誤訊息:

docker build nuget error
Errors in packages.config projects
    Unable to find version '2018.8.2.3' of package 'KingXXXX.YYYYYYYYYYor'.
      https://api.nuget.org/v3/index.json: Package 'KingXXXX.YYYYYYYYYYor.2018.8.2.3' is not found on source 'https://api.nuget.org/v3/index.json'.
    Unable to find version '2017.10.17.1' of package 'KingXXXX.YYYYYYYYYYYYYYFramework'.
      https://api.nuget.org/v3/index.json: Package 'KingXXXX.YYYYYYYYYYYYYYFramework.2017.10.17.1' is not found on source 'https://api.nuget.org/v3/index.json'.
    Unable to find version '2018.7.24.2' of package 'KingXXXX.PPPPService'.
      https://api.nuget.org/v3/index.json: Package 'KingXXXX.PPPPService.2018.7.24.2' is not found on source 'https://api.nuget.org/v3/index.json'.

NuGet Config files used:
    C:\Users\ContainerAdministrator\AppData\Roaming\NuGet\NuGet.Config

Feeds used:
    https://api.nuget.org/v3/index.json

方案一:加入 nuget.config

在一個 GitHub 討論串裡,看到第一種解決,在原專案中加去 nuget.config 組態,並指定好 Private Nuget Service 位置,修改 Dockerfile 一併複製進去 Build Context,當 nuget restore 執行時發現 nuget.config 存在時,就會讀取設定好的 nuget.config 來進行還原。

我們以範例來說明:

Dockerfile

FROM microsoft/aspnetcore-build:latest AS build
WORKDIR /src
COPY . .
COPY NuGet.Config ./
RUN dir
RUN dotnet restore --configfile NuGet.Config -nowarn:msb3202,nu1503 --verbosity diag
RUN dotnet publish --output /output --configuration Release

FROM microsoft/aspnetcore:latest
WORKDIR /app
COPY --from=build /output /app
ENTRYPOINT ["dotnet", "DockerProject.dll"]

COPY NuGet.Config ./ 把組態好的 nuget.config 複製進去 build context 裡。

nuget.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="AzurDevops" value="https://pkgs.dev.azure.com/{YourName}/_packaging/{YourFeed}/nuget/v3/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <AzurDevops>
      <add key="Username" value="Email here" />      
      <add key="ClearTextPassword" value="PAT here" />
    </AzurDevops>
  </packageSourceCredentials>
</configuration>

預設只有 nuget.org 那一條組態,我們把 Private Nuget Service 加入組態,組態內容可以 Azure DevOps - NuGet 取得。此方法可行,但有個可能資安問題,在 nuget.config 組態裡,我們必須包含 PAT 憑證。不過,這是不是一個資安問題,我個人是保留態度,原因很簡單:

  1. web.config 裡一樣包含 ConnectionStringConnectionString 裡面如果沒有認真處理過,那麼資料庫的帳號外流,我覺得比 nuget.config 更慘。
  2. 由於我們是採用 multi-stage builds,nuget.config 只存在 AS build 這一暫存層,而非最終產出的 runtime 映像檔之內,我能想到的點只有 COPY --from=build 沒處理好,把未編輯的專案給複製進最終 runtime 映像檔。
  3. 我比較偏向它是一個缺點,因為我們必須含 PAT 憑證的 nuget.config 給簽入版控:
nuget.config for multi build

當我們把 nuget.config 複製進去 build context 之後,我們的 .NET 採用 nuget.exe 來還原,nuget.exe 會發現存在其他設定,nuget.exe 會一併讀取 nuget.config 與使用。當然,如果你真的非常要求 PAT 憑證問題,那也能再多加一行 RUN del nuget.config 來處理 PAT 憑證問題。也就是說,記錄上會看到 nuget.config,但就算 build 暫存映像檔被人取走,裡面也不會有 nuget.config 的存在。

方案二:NuGet CLI

我們知道問題點在 nuget.exe 在 Windows 基礎映像檔(Windows Base Image)建置時沒有 Private Nuget Service 與 PAT 憑證相關設置,換個角度,那麼我們是否可以使用 nuget.exe 本身提供的 CLI 來處理。

RUN nuget sources Add `
-Name "AzureDevOpsNuGet" `
-Source "https://pkgs.dev.azure.com/{YourName}/_packaging/{YourFeed}/nuget/v3/index.json" `
-username {Email} `
-password {PAT} `
-StorePasswordInClearText

透過文件,只要在 RUN nuget restore 之前把 Private NuGet Service 的相關組態寫好,也能解決。缺點一樣,只是從 nuget.config 轉移至 Dockerfile 之內,但轉移至 Dockerfile 之後有了另一好處!

方案三:docker build 與 Dockerfile

當我們把 nuget 的組態從 nuget.config 拉回 Dockerfile 之後,我們就能利用 Dockerfile 本身的 ARG 變數功能,把機敏資訊可以從 Dockerfile 裡抽離,最後我們只需要在 docker build --build-arg {Key}={Value} . 在呼叫建置指時才由外部指定 ARG

docker build -t {yourTag} `
--build-arg SOURCE='https://pkgs.dev.azure.com/{YourName}/_packaging/{YourFeed}/nuget/v3/index.json' `
--build-arg USERNAME='{Email}' `
--build-arg PAT='{PAT}' `

方案四:Azure Artifacts Credential Provider

Azure Artifacts Credential Provider 主要整合 dotnet.exe、NuGet.exe、MSBuild 多個指令,提供互動式取得憑證來存取 Azure Artifacts feeds。Azure Artifacts Credential Provider 文件寫的算清楚,請自行參考。但在測試過程碰到一個問題,VSS_NUGET_EXTERNAL_FEED_ENDPOINTS 同時間只能有一組設定值,剛好我們需要二組 FEED,我實在不想再 Dockerfile 內容把問題複雜化了。

ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS {"endpointCredentials": [{"endpoint":"http://example.index.json", "username":"optional", "password":"accesstoken"}]}

不過核心問題還是我們免不了將 PAT 傳入 Dockerfile 之內。

docker history

注意:所有在 Dockerfile 跑過的指令都能完整使用 docker history --no-trunc 來完整查詢。

docker history

包含使用 ARG 也是一樣會完整呈現在 docker history

docker history build arg

解決四:docker build --secret

docker build --secret 透過 Builtkit 專案提供的功能,讓 Dockerfile 擁有類似 docker secret CLI 的能力,能從安全的地方來讀取機敏資料。但可惜,撰文當下,還不支援 Windows Container 的使用。

小結

在使用 Docker Multi-Stage Builds 過程,通常 PAT 只會存在於第一個 tag :sdk 映像檔 裡,當建置與發行完後從,我們會把可執行檔由 sdk 複製至 runtime 的映像檔中,建置出最終所需含執行檔的映像檔。這個 runtime 映像檔應該只包含發行後的 *.dll 或 *.exe,docker history 也不會含有相關執行 PAT 記錄。

如果確認 Build Agent 在 Build 過程暫存的 SDK 映像檔是不會被外人碰到的,那麼除了方案四無法在 Windows Container 使用之外,使用方案一、二、三都是可行的方式。在保護好 Build Agent (SDK 映像檔)的前提下,這個 PAT 是不是資安問題,我認為是有討論的空間。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。