From e4db128d1a49b628a36e24fbcebe6e33a58c36d5 Mon Sep 17 00:00:00 2001 From: DeWitt Gibson Date: Sat, 20 Jun 2026 15:23:45 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20supply-chain=20hardening=20=E2=80=94=20?= =?UTF-8?q?MCR=20base=20images=20+=20Azure=20Artifacts=20feeds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch all three app Dockerfiles from Docker Hub images to MCR (mcr.microsoft.com/cbl-mariner/base/nodejs:20 and python:3.12), eliminating the Docker Hub dependency. Replace apk/apt-get with tdnf and Alpine/Debian user-management syntax with standard groupadd/useradd. Route all npm and pip package installs through the project Azure Artifacts feeds (agentaflow/agentbase/_packaging/python). A committed .npmrc sets the registry for pnpm; NpmAuthenticate@0 and PipAuthenticate@1 tasks authenticate the Validate stage agent. The deploy-env.yml step 2 maps System.AccessToken → AZURE_ARTIFACTS_TOKEN and passes it as a --build-arg to all three az acr build calls so packages inside ACR Tasks builds also resolve through the feed. --- .npmrc | 2 ++ azure-pipelines/agentbase-deploy.yml | 9 +++++++++ azure-pipelines/templates/deploy-env.yml | 12 +++++++++++- packages/ai-service/Dockerfile | 15 ++++++++++----- packages/core/Dockerfile | 20 ++++++++++++-------- packages/frontend/Dockerfile | 18 ++++++++++++------ 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..961823f --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +registry=https://pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/ +always-auth=true diff --git a/azure-pipelines/agentbase-deploy.yml b/azure-pipelines/agentbase-deploy.yml index a582e7b..cb6dd02 100644 --- a/azure-pipelines/agentbase-deploy.yml +++ b/azure-pipelines/agentbase-deploy.yml @@ -85,6 +85,10 @@ stages: displayName: 'Use Node.js 20' inputs: versionSpec: '20.x' + - task: NpmAuthenticate@0 + displayName: 'Authenticate npm → Azure Artifacts' + inputs: + workingFile: .npmrc - script: | corepack enable && corepack prepare pnpm@9 --activate pnpm install --frozen-lockfile || pnpm install @@ -111,6 +115,11 @@ stages: displayName: 'Use Python 3.12' inputs: versionSpec: '3.12' + - task: PipAuthenticate@1 + displayName: 'Authenticate pip → Azure Artifacts' + inputs: + artifactsFeeds: 'agentbase/python' + onlyAddExtraIndex: false - script: | pip install -r packages/ai-service/requirements.txt pip install pip-audit pytest diff --git a/azure-pipelines/templates/deploy-env.yml b/azure-pipelines/templates/deploy-env.yml index 9a893d5..13d8584 100644 --- a/azure-pipelines/templates/deploy-env.yml +++ b/azure-pipelines/templates/deploy-env.yml @@ -67,24 +67,34 @@ stages: echo "##vso[task.setvariable variable=AI_URL]$(out aiUrl)" # 2 · Build & push the three images server-side in ACR (no agent Docker). + # AZURE_ARTIFACTS_TOKEN (System.AccessToken) is passed as a build arg so + # each Dockerfile can authenticate to the Azure Artifacts npm/PyPI feeds + # during the package install step inside the build. - task: AzureCLI@2 displayName: '2 · Build & push images (az acr build)' + env: + AZURE_ARTIFACTS_TOKEN: $(System.AccessToken) inputs: azureSubscription: ${{ parameters.serviceConnection }} scriptType: bash scriptLocation: inlineScript inlineScript: | set -euo pipefail + # Construct authenticated PyPI URL from the same token (basic-auth format) + ARTIFACTS_PIP_URL="https://build:${AZURE_ARTIFACTS_TOKEN}@pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/pypi/simple/" az acr build --registry "$(ACR_NAME)" \ + --build-arg AZURE_ARTIFACTS_TOKEN="$AZURE_ARTIFACTS_TOKEN" \ --image agentbase-core:$(imageTag) --image agentbase-core:latest \ --file packages/core/Dockerfile . az acr build --registry "$(ACR_NAME)" \ + --build-arg AZURE_ARTIFACTS_PIP_URL="$ARTIFACTS_PIP_URL" \ --image agentbase-ai-service:$(imageTag) --image agentbase-ai-service:latest \ --file packages/ai-service/Dockerfile . az acr build --registry "$(ACR_NAME)" \ - --image agentbase-frontend:$(imageTag) --image agentbase-frontend:latest \ + --build-arg AZURE_ARTIFACTS_TOKEN="$AZURE_ARTIFACTS_TOKEN" \ --build-arg NEXT_PUBLIC_API_URL="$(CORE_URL)/api" \ --build-arg NEXT_PUBLIC_AI_URL="$(AI_URL)/api" \ + --image agentbase-frontend:$(imageTag) --image agentbase-frontend:latest \ --file packages/frontend/Dockerfile . # 3 · Seed Key Vault. Secret values are mapped through env: (required for diff --git a/packages/ai-service/Dockerfile b/packages/ai-service/Dockerfile index 86002c3..5be8676 100644 --- a/packages/ai-service/Dockerfile +++ b/packages/ai-service/Dockerfile @@ -1,16 +1,21 @@ # ---- Build Stage ---- -FROM python:3.12-slim AS builder +FROM mcr.microsoft.com/cbl-mariner/base/python:3.12 AS builder WORKDIR /app COPY packages/ai-service/requirements.txt . -RUN pip install --no-cache-dir --target=/deps -r requirements.txt + +# Inject Azure Artifacts PyPI URL; falls back to public PyPI for local builds without the arg +ARG AZURE_ARTIFACTS_PIP_URL +RUN pip install --no-cache-dir \ + --index-url "${AZURE_ARTIFACTS_PIP_URL:-https://pypi.org/simple/}" \ + --target=/deps -r requirements.txt # ---- Production Stage ---- -FROM python:3.12-slim AS production +FROM mcr.microsoft.com/cbl-mariner/base/python:3.12 AS production WORKDIR /app -RUN addgroup --system agentbase && adduser --system --group agentbase -RUN apt-get update && apt-get install -y --no-install-recommends wget && rm -rf /var/lib/apt/lists/* +RUN groupadd -r agentbase && useradd -r -g agentbase -s /sbin/nologin -M agentbase +RUN tdnf install -y wget COPY --from=builder /deps /usr/local/lib/python3.12/site-packages COPY packages/ai-service/app ./app diff --git a/packages/core/Dockerfile b/packages/core/Dockerfile index 7d3c7a2..77b4858 100644 --- a/packages/core/Dockerfile +++ b/packages/core/Dockerfile @@ -1,17 +1,22 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS builder WORKDIR /app # Install pnpm RUN corepack enable && corepack prepare pnpm@9 --activate -# Copy workspace config -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/core/package.json packages/core/ COPY packages/shared/package.json packages/shared/ -# Install dependencies +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc # Copy source COPY packages/shared packages/shared @@ -23,11 +28,11 @@ WORKDIR /app/packages/core RUN npx nest build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS production WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -RUN apk add --no-cache tini +RUN tdnf install -y wget # Copy built app COPY --from=builder /app/packages/core/dist ./dist @@ -35,7 +40,7 @@ COPY --from=builder /app/packages/core/package.json ./ COPY --from=builder /app/node_modules ./node_modules # Non-root user -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -44,5 +49,4 @@ EXPOSE 3001 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3001/api/health || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "dist/main.js"] diff --git a/packages/frontend/Dockerfile b/packages/frontend/Dockerfile index 10e4b1f..25b9f07 100644 --- a/packages/frontend/Dockerfile +++ b/packages/frontend/Dockerfile @@ -1,14 +1,21 @@ # ---- Build Stage ---- -FROM node:20-alpine AS builder +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS builder WORKDIR /app RUN corepack enable && corepack prepare pnpm@9 --activate -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./ +# Copy workspace config and registry settings +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* .npmrc ./ COPY packages/frontend/package.json packages/frontend/ COPY packages/shared/package.json packages/shared/ +# Inject Azure Artifacts auth token; strip it after install so the layer has no creds +ARG AZURE_ARTIFACTS_TOKEN +RUN if [ -n "${AZURE_ARTIFACTS_TOKEN}" ]; then \ + echo "//pkgs.dev.azure.com/agentaflow/agentbase/_packaging/python/npm/registry/:_authToken=${AZURE_ARTIFACTS_TOKEN}" >> .npmrc; \ + fi RUN pnpm install --frozen-lockfile 2>/dev/null || pnpm install +RUN sed -i '/_authToken/d' .npmrc COPY packages/shared packages/shared COPY packages/frontend packages/frontend @@ -23,16 +30,16 @@ ENV NEXT_PUBLIC_AI_URL=$NEXT_PUBLIC_AI_URL RUN npx next build # ---- Production Stage ---- -FROM node:20-alpine AS production +FROM mcr.microsoft.com/cbl-mariner/base/nodejs:20 AS production WORKDIR /app -RUN apk add --no-cache tini +RUN tdnf install -y wget COPY --from=builder /app/packages/frontend/.next/standalone ./ COPY --from=builder /app/packages/frontend/.next/static ./.next/static COPY --from=builder /app/packages/frontend/public ./public -RUN addgroup -g 1001 -S agentbase && adduser -S agentbase -u 1001 +RUN groupadd -g 1001 agentbase && useradd -u 1001 -g agentbase -s /sbin/nologin -M agentbase USER agentbase ENV NODE_ENV=production @@ -41,5 +48,4 @@ EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \ CMD wget -q --spider http://localhost:3000/ || exit 1 -ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"]