CD/CD consolidation
This commit is contained in:
150
devops/docker/Dockerfile.ci
Normal file
150
devops/docker/Dockerfile.ci
Normal file
@@ -0,0 +1,150 @@
|
||||
# Dockerfile.ci - Local CI testing container matching Gitea runner environment
|
||||
# Sprint: SPRINT_20251226_006_CICD
|
||||
#
|
||||
# Usage:
|
||||
# docker build -t stellaops-ci:local -f devops/docker/Dockerfile.ci .
|
||||
# docker run --rm -v $(pwd):/src stellaops-ci:local ./devops/scripts/test-local.sh
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
LABEL org.opencontainers.image.title="StellaOps CI"
|
||||
LABEL org.opencontainers.image.description="Local CI testing environment matching Gitea runner"
|
||||
LABEL org.opencontainers.image.source="https://git.stella-ops.org/stella-ops.org/git.stella-ops.org"
|
||||
|
||||
# Environment variables
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV DOTNET_VERSION=10.0.100
|
||||
ENV NODE_VERSION=20
|
||||
ENV HELM_VERSION=3.16.0
|
||||
ENV COSIGN_VERSION=2.2.4
|
||||
ENV TZ=UTC
|
||||
|
||||
# Disable .NET telemetry
|
||||
ENV DOTNET_NOLOGO=1
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
|
||||
# .NET paths
|
||||
ENV DOTNET_ROOT=/usr/share/dotnet
|
||||
ENV PATH="/usr/share/dotnet:/root/.dotnet/tools:${PATH}"
|
||||
|
||||
# ===========================================================================
|
||||
# BASE DEPENDENCIES
|
||||
# ===========================================================================
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
# Core utilities
|
||||
curl \
|
||||
wget \
|
||||
gnupg2 \
|
||||
ca-certificates \
|
||||
git \
|
||||
unzip \
|
||||
jq \
|
||||
# Build tools
|
||||
build-essential \
|
||||
# Docker CLI (for DinD scenarios)
|
||||
docker.io \
|
||||
docker-compose-plugin \
|
||||
# Cross-compilation
|
||||
binutils-aarch64-linux-gnu \
|
||||
# Python (for scripts)
|
||||
python3 \
|
||||
python3-pip \
|
||||
# Locales
|
||||
locales \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set locale
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LANGUAGE=en_US:en
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
|
||||
# ===========================================================================
|
||||
# POSTGRESQL CLIENT 16
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends postgresql-client-16 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# .NET 10 SDK
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
|
||||
&& chmod +x /tmp/dotnet-install.sh \
|
||||
&& /tmp/dotnet-install.sh --version ${DOTNET_VERSION} --install-dir /usr/share/dotnet \
|
||||
&& rm /tmp/dotnet-install.sh \
|
||||
&& dotnet --version
|
||||
|
||||
# Install common .NET tools
|
||||
RUN dotnet tool install -g trx2junit \
|
||||
&& dotnet tool install -g dotnet-reportgenerator-globaltool
|
||||
|
||||
# ===========================================================================
|
||||
# NODE.JS 20
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& node --version \
|
||||
&& npm --version
|
||||
|
||||
# ===========================================================================
|
||||
# HELM 3.16.0
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz | \
|
||||
tar -xzf - -C /tmp \
|
||||
&& mv /tmp/linux-amd64/helm /usr/local/bin/helm \
|
||||
&& rm -rf /tmp/linux-amd64 \
|
||||
&& helm version
|
||||
|
||||
# ===========================================================================
|
||||
# COSIGN
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-amd64 \
|
||||
-o /usr/local/bin/cosign \
|
||||
&& chmod +x /usr/local/bin/cosign \
|
||||
&& cosign version
|
||||
|
||||
# ===========================================================================
|
||||
# SYFT (SBOM generation)
|
||||
# ===========================================================================
|
||||
|
||||
RUN curl -fsSL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
|
||||
|
||||
# ===========================================================================
|
||||
# SETUP
|
||||
# ===========================================================================
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Create non-root user for safer execution (optional)
|
||||
RUN useradd -m -s /bin/bash ciuser \
|
||||
&& mkdir -p /home/ciuser/.dotnet/tools \
|
||||
&& chown -R ciuser:ciuser /home/ciuser
|
||||
|
||||
# Health check script
|
||||
COPY --chmod=755 <<'EOF' /usr/local/bin/ci-health-check
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "=== CI Environment Health Check ==="
|
||||
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"
|
||||
echo ".NET: $(dotnet --version)"
|
||||
echo "Node: $(node --version)"
|
||||
echo "npm: $(npm --version)"
|
||||
echo "Helm: $(helm version --short)"
|
||||
echo "Cosign: $(cosign version 2>&1 | head -1)"
|
||||
echo "Docker: $(docker --version 2>/dev/null || echo 'Not available')"
|
||||
echo "PostgreSQL client: $(psql --version)"
|
||||
echo "=== All checks passed ==="
|
||||
EOF
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
40
devops/docker/Dockerfile.console
Normal file
40
devops/docker/Dockerfile.console
Normal file
@@ -0,0 +1,40 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Multi-stage Angular console image with non-root runtime (DOCKER-44-001)
|
||||
ARG NODE_IMAGE=node:20-bullseye-slim
|
||||
ARG NGINX_IMAGE=nginxinc/nginx-unprivileged:1.27-alpine
|
||||
ARG APP_DIR=src/Web/StellaOps.Web
|
||||
ARG DIST_DIR=dist
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${NODE_IMAGE} AS build
|
||||
ENV npm_config_fund=false npm_config_audit=false SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /app
|
||||
COPY ${APP_DIR}/package*.json ./
|
||||
RUN npm ci --prefer-offline --no-progress --cache .npm
|
||||
COPY ${APP_DIR}/ ./
|
||||
RUN npm run build -- --configuration=production --output-path=${DIST_DIR}
|
||||
|
||||
FROM ${NGINX_IMAGE} AS runtime
|
||||
ARG APP_PORT
|
||||
ENV APP_PORT=${APP_PORT}
|
||||
USER 101
|
||||
WORKDIR /
|
||||
COPY --from=build /app/${DIST_DIR}/ /usr/share/nginx/html/
|
||||
COPY ops/devops/docker/healthcheck-frontend.sh /usr/local/bin/healthcheck-frontend.sh
|
||||
RUN rm -f /etc/nginx/conf.d/default.conf && \
|
||||
cat > /etc/nginx/conf.d/default.conf <<CONF
|
||||
server {
|
||||
listen ${APP_PORT};
|
||||
listen [::]:${APP_PORT};
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
location / {
|
||||
try_files $$uri $$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
CONF
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck-frontend.sh
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
172
devops/docker/Dockerfile.crypto-profile
Normal file
172
devops/docker/Dockerfile.crypto-profile
Normal file
@@ -0,0 +1,172 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Regional Crypto Profile
|
||||
# Selects regional cryptographic configuration at build time
|
||||
|
||||
# ============================================================================
|
||||
# Build Arguments
|
||||
# ============================================================================
|
||||
ARG CRYPTO_PROFILE=international
|
||||
ARG BASE_IMAGE=stellaops/platform:latest
|
||||
ARG SERVICE_NAME=authority
|
||||
|
||||
# ============================================================================
|
||||
# Regional Crypto Profile Layer
|
||||
# ============================================================================
|
||||
FROM ${BASE_IMAGE} AS regional-profile
|
||||
|
||||
# Copy regional cryptographic configuration
|
||||
ARG CRYPTO_PROFILE
|
||||
COPY etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml /app/etc/appsettings.crypto.yaml
|
||||
COPY etc/crypto-plugins-manifest.json /app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Set environment variable for runtime verification
|
||||
ENV STELLAOPS_CRYPTO_PROFILE=${CRYPTO_PROFILE}
|
||||
ENV STELLAOPS_CRYPTO_CONFIG_PATH=/app/etc/appsettings.crypto.yaml
|
||||
ENV STELLAOPS_CRYPTO_MANIFEST_PATH=/app/etc/crypto-plugins-manifest.json
|
||||
|
||||
# Add labels for metadata
|
||||
LABEL com.stellaops.crypto.profile="${CRYPTO_PROFILE}"
|
||||
LABEL com.stellaops.crypto.config="/app/etc/appsettings.crypto.${CRYPTO_PROFILE}.yaml"
|
||||
LABEL com.stellaops.crypto.runtime-selection="true"
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Regional Images
|
||||
# ============================================================================
|
||||
|
||||
# Authority with Regional Crypto
|
||||
FROM regional-profile AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer with Regional Crypto
|
||||
FROM regional-profile AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor with Regional Crypto
|
||||
FROM regional-profile AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier with Regional Crypto
|
||||
FROM regional-profile AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner with Regional Crypto
|
||||
FROM regional-profile AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor with Regional Crypto
|
||||
FROM regional-profile AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy with Regional Crypto
|
||||
FROM regional-profile AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler with Regional Crypto
|
||||
FROM regional-profile AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify with Regional Crypto
|
||||
FROM regional-profile AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava with Regional Crypto
|
||||
FROM regional-profile AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway with Regional Crypto
|
||||
FROM regional-profile AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer with Regional Crypto
|
||||
FROM regional-profile AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter with Regional Crypto
|
||||
FROM regional-profile AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI with Regional Crypto
|
||||
FROM regional-profile AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build international profile (default):
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=international \
|
||||
# --target authority \
|
||||
# -t stellaops/authority:international .
|
||||
#
|
||||
# Build Russia (GOST) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=russia \
|
||||
# --target scanner \
|
||||
# -t stellaops/scanner:russia .
|
||||
#
|
||||
# Build EU (eIDAS) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=eu \
|
||||
# --target signer \
|
||||
# -t stellaops/signer:eu .
|
||||
#
|
||||
# Build China (SM) profile:
|
||||
# docker build -f deploy/docker/Dockerfile.crypto-profile \
|
||||
# --build-arg CRYPTO_PROFILE=china \
|
||||
# --target attestor \
|
||||
# -t stellaops/attestor:china .
|
||||
#
|
||||
# ============================================================================
|
||||
# Regional Profile Descriptions
|
||||
# ============================================================================
|
||||
# international: Default NIST algorithms (ES256, RS256, SHA-256)
|
||||
# Uses offline-verification plugin
|
||||
# Jurisdiction: world
|
||||
#
|
||||
# russia: GOST R 34.10-2012, GOST R 34.11-2012
|
||||
# Uses CryptoPro CSP plugin
|
||||
# Jurisdiction: russia
|
||||
# Requires: CryptoPro CSP SDK
|
||||
#
|
||||
# eu: eIDAS-compliant qualified trust services
|
||||
# Uses eIDAS plugin with qualified certificates
|
||||
# Jurisdiction: eu
|
||||
# Requires: eIDAS trust service provider integration
|
||||
#
|
||||
# china: SM2, SM3, SM4 algorithms
|
||||
# Uses SM crypto plugin
|
||||
# Jurisdiction: china
|
||||
# Requires: GmSSL or BouncyCastle SM extensions
|
||||
#
|
||||
# ============================================================================
|
||||
# Runtime Configuration
|
||||
# ============================================================================
|
||||
# The crypto provider is selected at runtime based on:
|
||||
# 1. STELLAOPS_CRYPTO_PROFILE environment variable
|
||||
# 2. /app/etc/appsettings.crypto.yaml configuration file
|
||||
# 3. /app/etc/crypto-plugins-manifest.json plugin metadata
|
||||
#
|
||||
# Plugin loading sequence:
|
||||
# 1. Application starts
|
||||
# 2. CryptoPluginLoader reads /app/etc/appsettings.crypto.yaml
|
||||
# 3. Loads enabled plugins from manifest
|
||||
# 4. Validates platform compatibility
|
||||
# 5. Validates jurisdiction compliance
|
||||
# 6. Registers providers with DI container
|
||||
# 7. Application uses ICryptoProvider abstraction
|
||||
#
|
||||
# No cryptographic code is executed until runtime plugin selection completes.
|
||||
56
devops/docker/Dockerfile.hardened.template
Normal file
56
devops/docker/Dockerfile.hardened.template
Normal file
@@ -0,0 +1,56 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
# Hardened multi-stage template for StellaOps services
|
||||
# Parameters are build-time ARGs so this file can be re-used across services.
|
||||
|
||||
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim
|
||||
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim
|
||||
ARG APP_PROJECT=src/Service/Service.csproj
|
||||
ARG CONFIGURATION=Release
|
||||
ARG PUBLISH_DIR=/app/publish
|
||||
ARG APP_BINARY=StellaOps.Service
|
||||
ARG APP_USER=stella
|
||||
ARG APP_UID=10001
|
||||
ARG APP_GID=10001
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${SDK_IMAGE} AS build
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
||||
DOTNET_NOLOGO=1 \
|
||||
SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /src
|
||||
# Expect restore sources to be available offline via local-nugets/
|
||||
COPY . .
|
||||
RUN dotnet restore ${APP_PROJECT} --packages /src/local-nugets && \
|
||||
dotnet publish ${APP_PROJECT} -c ${CONFIGURATION} -o ${PUBLISH_DIR} \
|
||||
/p:UseAppHost=true /p:PublishTrimmed=false
|
||||
|
||||
FROM ${RUNTIME_IMAGE} AS runtime
|
||||
# Create non-root user/group with stable ids for auditability
|
||||
RUN groupadd -r -g ${APP_GID} ${APP_USER} && \
|
||||
useradd -r -u ${APP_UID} -g ${APP_GID} -d /var/lib/${APP_USER} ${APP_USER} && \
|
||||
mkdir -p /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp && \
|
||||
chown -R ${APP_UID}:${APP_GID} /app /var/lib/${APP_USER} /var/run/${APP_USER} /tmp
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=build --chown=${APP_UID}:${APP_GID} ${PUBLISH_DIR}/ ./
|
||||
# Ship healthcheck helper; callers may override with their own script
|
||||
COPY --chown=${APP_UID}:${APP_GID} ops/devops/docker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
|
||||
ENV ASPNETCORE_URLS=http://+:${APP_PORT} \
|
||||
DOTNET_EnableDiagnostics=0 \
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
|
||||
COMPlus_EnableDiagnostics=0 \
|
||||
APP_BINARY=${APP_BINARY}
|
||||
|
||||
USER ${APP_UID}:${APP_GID}
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Harden filesystem; deploys should also set readOnlyRootFilesystem true
|
||||
RUN chmod 500 /app && \
|
||||
find /app -maxdepth 1 -type f -exec chmod 400 {} \; && \
|
||||
find /app -maxdepth 1 -type d -exec chmod 500 {} \;
|
||||
|
||||
# Use shell form so APP_BINARY env can be expanded without duplicating the template per service
|
||||
ENTRYPOINT ["sh","-c","exec ./\"$APP_BINARY\""]
|
||||
212
devops/docker/Dockerfile.platform
Normal file
212
devops/docker/Dockerfile.platform
Normal file
@@ -0,0 +1,212 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
# StellaOps Platform Image - Build Once, Deploy Everywhere
|
||||
# Builds ALL crypto plugins unconditionally for runtime selection
|
||||
|
||||
# ============================================================================
|
||||
# Stage 1: SDK Build - Build ALL Projects and Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build
|
||||
WORKDIR /src
|
||||
|
||||
# Copy solution and project files for dependency restore
|
||||
COPY Directory.Build.props Directory.Build.targets nuget.config ./
|
||||
COPY src/StellaOps.sln ./src/
|
||||
|
||||
# Copy all crypto plugin projects
|
||||
COPY src/__Libraries/StellaOps.Cryptography/ ./src/__Libraries/StellaOps.Cryptography/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.DependencyInjection/ ./src/__Libraries/StellaOps.Cryptography.DependencyInjection/
|
||||
COPY src/__Libraries/StellaOps.Cryptography.PluginLoader/ ./src/__Libraries/StellaOps.Cryptography.PluginLoader/
|
||||
|
||||
# Crypto plugins - ALL built unconditionally
|
||||
COPY src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/ ./src/__Libraries/StellaOps.Cryptography.Plugin.OfflineVerification/
|
||||
# Note: Additional crypto plugins can be added here when available:
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/ ./src/__Libraries/StellaOps.Cryptography.Plugin.eIDAS/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/ ./src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/
|
||||
# COPY src/__Libraries/StellaOps.Cryptography.Plugin.SM/ ./src/__Libraries/StellaOps.Cryptography.Plugin.SM/
|
||||
|
||||
# Copy all module projects
|
||||
COPY src/Authority/ ./src/Authority/
|
||||
COPY src/Signer/ ./src/Signer/
|
||||
COPY src/Attestor/ ./src/Attestor/
|
||||
COPY src/Concelier/ ./src/Concelier/
|
||||
COPY src/Scanner/ ./src/Scanner/
|
||||
COPY src/AirGap/ ./src/AirGap/
|
||||
COPY src/Excititor/ ./src/Excititor/
|
||||
COPY src/Policy/ ./src/Policy/
|
||||
COPY src/Scheduler/ ./src/Scheduler/
|
||||
COPY src/Notify/ ./src/Notify/
|
||||
COPY src/Zastava/ ./src/Zastava/
|
||||
COPY src/Gateway/ ./src/Gateway/
|
||||
COPY src/Cli/ ./src/Cli/
|
||||
|
||||
# Copy shared libraries
|
||||
COPY src/__Libraries/ ./src/__Libraries/
|
||||
|
||||
# Restore dependencies
|
||||
RUN dotnet restore src/StellaOps.sln
|
||||
|
||||
# Build entire solution (Release configuration)
|
||||
RUN dotnet build src/StellaOps.sln --configuration Release --no-restore
|
||||
|
||||
# Publish all web services and libraries
|
||||
# This creates /app/publish with all assemblies including crypto plugins
|
||||
RUN dotnet publish src/Authority/StellaOps.Authority.WebService/StellaOps.Authority.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/authority
|
||||
|
||||
RUN dotnet publish src/Signer/StellaOps.Signer.WebService/StellaOps.Signer.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/signer
|
||||
|
||||
RUN dotnet publish src/Attestor/StellaOps.Attestor.WebService/StellaOps.Attestor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/attestor
|
||||
|
||||
RUN dotnet publish src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/concelier
|
||||
|
||||
RUN dotnet publish src/Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scanner
|
||||
|
||||
RUN dotnet publish src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/excititor
|
||||
|
||||
RUN dotnet publish src/Policy/StellaOps.Policy.WebService/StellaOps.Policy.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/policy
|
||||
|
||||
RUN dotnet publish src/Scheduler/StellaOps.Scheduler.WebService/StellaOps.Scheduler.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/scheduler
|
||||
|
||||
RUN dotnet publish src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/notify
|
||||
|
||||
RUN dotnet publish src/Zastava/StellaOps.Zastava.WebService/StellaOps.Zastava.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/zastava
|
||||
|
||||
RUN dotnet publish src/Gateway/StellaOps.Gateway.WebService/StellaOps.Gateway.WebService.csproj \
|
||||
--configuration Release --no-build --output /app/publish/gateway
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Importer/StellaOps.AirGap.Importer.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-importer
|
||||
|
||||
RUN dotnet publish src/AirGap/StellaOps.AirGap.Exporter/StellaOps.AirGap.Exporter.csproj \
|
||||
--configuration Release --no-build --output /app/publish/airgap-exporter
|
||||
|
||||
RUN dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj \
|
||||
--configuration Release --no-build --output /app/publish/cli
|
||||
|
||||
# Copy crypto plugin manifest
|
||||
COPY etc/crypto-plugins-manifest.json /app/publish/etc/
|
||||
|
||||
# ============================================================================
|
||||
# Stage 2: Runtime Base - Contains ALL Crypto Plugins
|
||||
# ============================================================================
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-preview AS runtime-base
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies for crypto providers
|
||||
# PostgreSQL client for Authority/Concelier/etc
|
||||
RUN apt-get update && apt-get install -y \
|
||||
postgresql-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy all published assemblies (includes all crypto plugins)
|
||||
COPY --from=build /app/publish /app/
|
||||
|
||||
# Expose common ports (these can be overridden by docker-compose)
|
||||
EXPOSE 8080 8443
|
||||
|
||||
# Labels
|
||||
LABEL com.stellaops.image.type="platform"
|
||||
LABEL com.stellaops.image.variant="all-plugins"
|
||||
LABEL com.stellaops.crypto.plugins="offline-verification"
|
||||
# Additional plugins will be added as they become available:
|
||||
# LABEL com.stellaops.crypto.plugins="offline-verification,eidas,cryptopro,sm"
|
||||
|
||||
# Health check placeholder (can be overridden per service)
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/health || exit 1
|
||||
|
||||
# ============================================================================
|
||||
# Service-Specific Final Stages
|
||||
# ============================================================================
|
||||
|
||||
# Authority Service
|
||||
FROM runtime-base AS authority
|
||||
WORKDIR /app/authority
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Authority.WebService.dll"]
|
||||
|
||||
# Signer Service
|
||||
FROM runtime-base AS signer
|
||||
WORKDIR /app/signer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Signer.WebService.dll"]
|
||||
|
||||
# Attestor Service
|
||||
FROM runtime-base AS attestor
|
||||
WORKDIR /app/attestor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Attestor.WebService.dll"]
|
||||
|
||||
# Concelier Service
|
||||
FROM runtime-base AS concelier
|
||||
WORKDIR /app/concelier
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Concelier.WebService.dll"]
|
||||
|
||||
# Scanner Service
|
||||
FROM runtime-base AS scanner
|
||||
WORKDIR /app/scanner
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scanner.WebService.dll"]
|
||||
|
||||
# Excititor Service
|
||||
FROM runtime-base AS excititor
|
||||
WORKDIR /app/excititor
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Excititor.WebService.dll"]
|
||||
|
||||
# Policy Service
|
||||
FROM runtime-base AS policy
|
||||
WORKDIR /app/policy
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Policy.WebService.dll"]
|
||||
|
||||
# Scheduler Service
|
||||
FROM runtime-base AS scheduler
|
||||
WORKDIR /app/scheduler
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Scheduler.WebService.dll"]
|
||||
|
||||
# Notify Service
|
||||
FROM runtime-base AS notify
|
||||
WORKDIR /app/notify
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Notify.WebService.dll"]
|
||||
|
||||
# Zastava Service
|
||||
FROM runtime-base AS zastava
|
||||
WORKDIR /app/zastava
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Zastava.WebService.dll"]
|
||||
|
||||
# Gateway Service
|
||||
FROM runtime-base AS gateway
|
||||
WORKDIR /app/gateway
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Gateway.WebService.dll"]
|
||||
|
||||
# AirGap Importer (CLI tool)
|
||||
FROM runtime-base AS airgap-importer
|
||||
WORKDIR /app/airgap-importer
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Importer.dll"]
|
||||
|
||||
# AirGap Exporter (CLI tool)
|
||||
FROM runtime-base AS airgap-exporter
|
||||
WORKDIR /app/airgap-exporter
|
||||
ENTRYPOINT ["dotnet", "StellaOps.AirGap.Exporter.dll"]
|
||||
|
||||
# CLI Tool
|
||||
FROM runtime-base AS cli
|
||||
WORKDIR /app/cli
|
||||
ENTRYPOINT ["dotnet", "StellaOps.Cli.dll"]
|
||||
|
||||
# ============================================================================
|
||||
# Build Instructions
|
||||
# ============================================================================
|
||||
# Build platform image:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target runtime-base -t stellaops/platform:latest .
|
||||
#
|
||||
# Build specific service:
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target authority -t stellaops/authority:latest .
|
||||
# docker build -f deploy/docker/Dockerfile.platform --target scanner -t stellaops/scanner:latest .
|
||||
#
|
||||
# The platform image contains ALL crypto plugins.
|
||||
# Regional selection happens at runtime via configuration (see Dockerfile.crypto-profile).
|
||||
76
devops/docker/base-image-guidelines.md
Normal file
76
devops/docker/base-image-guidelines.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Docker hardening blueprint (DOCKER-44-001)
|
||||
|
||||
Use this template for core services (API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI).
|
||||
|
||||
The reusable multi-stage scaffold lives at `ops/devops/docker/Dockerfile.hardened.template` and expects:
|
||||
- .NET 10 SDK/runtime images provided via offline mirror (`SDK_IMAGE` / `RUNTIME_IMAGE`).
|
||||
- `APP_PROJECT` path to the service csproj.
|
||||
- `healthcheck.sh` copied from `ops/devops/docker/` (already referenced by the template).
|
||||
- Optional: `APP_BINARY` (assembly name, defaults to `StellaOps.Service`) and `APP_PORT`.
|
||||
|
||||
Copy the template next to the service and set build args in CI (per-service matrix) to avoid maintaining divergent Dockerfiles.
|
||||
|
||||
```Dockerfile
|
||||
# syntax=docker/dockerfile:1.7
|
||||
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim
|
||||
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim
|
||||
ARG APP_PROJECT=src/Service/Service.csproj
|
||||
ARG CONFIGURATION=Release
|
||||
ARG APP_USER=stella
|
||||
ARG APP_UID=10001
|
||||
ARG APP_GID=10001
|
||||
ARG APP_PORT=8080
|
||||
|
||||
FROM ${SDK_IMAGE} AS build
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1 SOURCE_DATE_EPOCH=1704067200
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN dotnet restore ${APP_PROJECT} --packages /src/local-nugets && \
|
||||
dotnet publish ${APP_PROJECT} -c ${CONFIGURATION} -o /app/publish /p:UseAppHost=true /p:PublishTrimmed=false
|
||||
|
||||
FROM ${RUNTIME_IMAGE} AS runtime
|
||||
RUN groupadd -r -g ${APP_GID} ${APP_USER} && \
|
||||
useradd -r -u ${APP_UID} -g ${APP_GID} -d /var/lib/${APP_USER} ${APP_USER}
|
||||
WORKDIR /app
|
||||
COPY --from=build --chown=${APP_UID}:${APP_GID} /app/publish/ ./
|
||||
COPY --chown=${APP_UID}:${APP_GID} ops/devops/docker/healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||
ENV ASPNETCORE_URLS=http://+:${APP_PORT} \
|
||||
DOTNET_EnableDiagnostics=0 \
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
|
||||
COMPlus_EnableDiagnostics=0
|
||||
USER ${APP_UID}:${APP_GID}
|
||||
EXPOSE ${APP_PORT}
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 CMD /usr/local/bin/healthcheck.sh
|
||||
RUN chmod 500 /app && find /app -maxdepth 1 -type f -exec chmod 400 {} \; && find /app -maxdepth 1 -type d -exec chmod 500 {} \;
|
||||
ENTRYPOINT ["sh","-c","exec ./\"$APP_BINARY\""]
|
||||
```
|
||||
|
||||
Build stage (per service) should:
|
||||
- Use `mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim` (or mirror) with `DOTNET_CLI_TELEMETRY_OPTOUT=1`.
|
||||
- Restore from `local-nugets/` (offline) and run `dotnet publish -c Release -o /app/out`.
|
||||
- Set `SOURCE_DATE_EPOCH` to freeze timestamps.
|
||||
|
||||
Required checks:
|
||||
- No `root` user in final image.
|
||||
- `CAP_NET_RAW` dropped (default with non-root).
|
||||
- Read-only rootfs enforced at deploy time (`securityContext.readOnlyRootFilesystem: true` in Helm/Compose).
|
||||
- Health endpoints exposed: `/health/liveness`, `/health/readiness`, `/version`, `/metrics`.
|
||||
- Image SBOM generated (syft) in pipeline; attach cosign attestations (see DOCKER-44-002).
|
||||
|
||||
Service matrix & helper:
|
||||
- Build args for the core services are enumerated in `ops/devops/docker/services-matrix.env` (API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI).
|
||||
- `ops/devops/docker/build-all.sh` reads the matrix and builds/tag images from the shared template with consistent non-root/health defaults. Override `REGISTRY` and `TAG_SUFFIX` to publish.
|
||||
|
||||
Console (Angular) image:
|
||||
- Use `ops/devops/docker/Dockerfile.console` for the UI (Angular v17). It builds with `node:20-bullseye-slim`, serves via `nginxinc/nginx-unprivileged`, includes `healthcheck-frontend.sh`, and runs as non-root UID 101. Build with `docker build -f ops/devops/docker/Dockerfile.console --build-arg APP_DIR=src/Web/StellaOps.Web .`.
|
||||
|
||||
SBOM & attestation helper (DOCKER-44-002):
|
||||
- Script: `ops/devops/docker/sbom_attest.sh <image> [out-dir] [cosign-key]`
|
||||
- Emits SPDX (`*.spdx.json`) and CycloneDX (`*.cdx.json`) with `SOURCE_DATE_EPOCH` pinned for reproducibility.
|
||||
- Attaches both as cosign attestations (`--type spdx` / `--type cyclonedx`); supports keyless when `COSIGN_EXPERIMENTAL=1` or explicit PEM key.
|
||||
- Integrate in CI after image build/push; keep registry creds offline-friendly (use local registry mirror during air-gapped builds).
|
||||
|
||||
Health endpoint verification (DOCKER-44-003):
|
||||
- Script: `ops/devops/docker/verify_health_endpoints.sh <image> [port]` spins container, checks `/health/liveness`, `/health/readiness`, `/version`, `/metrics`, and warns if `/capabilities.merge` is not `false` (for Concelier/Excititor).
|
||||
- Run in CI after publishing the image; requires `docker` and `curl` (or `wget`).
|
||||
- Endpoint contract and ASP.NET wiring examples live in `ops/devops/docker/health-endpoints.md`; service owners should copy the snippet and ensure readiness checks cover DB/cache/bus.
|
||||
50
devops/docker/build-all.sh
Normal file
50
devops/docker/build-all.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build hardened images for the core services using the shared template/matrix (DOCKER-44-001)
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=${ROOT:-"$(git rev-parse --show-toplevel)"}
|
||||
MATRIX=${MATRIX:-"${ROOT}/ops/devops/docker/services-matrix.env"}
|
||||
REGISTRY=${REGISTRY:-"stellaops"}
|
||||
TAG_SUFFIX=${TAG_SUFFIX:-"dev"}
|
||||
SDK_IMAGE=${SDK_IMAGE:-"mcr.microsoft.com/dotnet/sdk:10.0-bookworm-slim"}
|
||||
RUNTIME_IMAGE=${RUNTIME_IMAGE:-"mcr.microsoft.com/dotnet/aspnet:10.0-bookworm-slim"}
|
||||
|
||||
if [[ ! -f "${MATRIX}" ]]; then
|
||||
echo "matrix file not found: ${MATRIX}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building services from ${MATRIX} -> ${REGISTRY}/<service>:${TAG_SUFFIX}" >&2
|
||||
|
||||
while IFS='|' read -r service dockerfile project binary port; do
|
||||
[[ -z "${service}" || "${service}" =~ ^# ]] && continue
|
||||
image="${REGISTRY}/${service}:${TAG_SUFFIX}"
|
||||
df_path="${ROOT}/${dockerfile}"
|
||||
if [[ ! -f "${df_path}" ]]; then
|
||||
echo "skipping ${service}: dockerfile missing (${df_path})" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "${dockerfile}" == *"Dockerfile.console"* ]]; then
|
||||
# Angular console build uses its dedicated Dockerfile
|
||||
echo "[console] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg APP_DIR="${project}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
else
|
||||
echo "[service] ${service} -> ${image}" >&2
|
||||
docker build \
|
||||
-f "${df_path}" "${ROOT}" \
|
||||
--build-arg SDK_IMAGE="${SDK_IMAGE}" \
|
||||
--build-arg RUNTIME_IMAGE="${RUNTIME_IMAGE}" \
|
||||
--build-arg APP_PROJECT="${project}" \
|
||||
--build-arg APP_BINARY="${binary}" \
|
||||
--build-arg APP_PORT="${port}" \
|
||||
-t "${image}"
|
||||
fi
|
||||
|
||||
done < "${MATRIX}"
|
||||
|
||||
echo "Build complete. Remember to enforce readOnlyRootFilesystem at deploy time and run sbom_attest.sh (DOCKER-44-002)." >&2
|
||||
44
devops/docker/health-endpoints.md
Normal file
44
devops/docker/health-endpoints.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Health & capability endpoint contract (DOCKER-44-003)
|
||||
|
||||
Target services: API, Console, Orchestrator, Task Runner, Concelier, Excititor, Policy, Notify, Export, AdvisoryAI.
|
||||
|
||||
## HTTP paths
|
||||
- `GET /health/liveness` — fast, dependency-free check; returns `200` and minimal body.
|
||||
- `GET /health/readiness` — may hit critical deps (DB, bus, cache); returns `503` when not ready.
|
||||
- `GET /version` — static payload with `service`, `version`, `commit`, `buildTimestamp` (ISO-8601 UTC), `source` (channel).
|
||||
- `GET /metrics` — Prometheus text exposition; reuse existing instrumentation.
|
||||
- `GET /capabilities` — if present for Concelier/Excititor, must include `"merge": false`.
|
||||
|
||||
## Minimal ASP.NET 10 wiring (per service)
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
// health checks; add real checks as needed
|
||||
builder.Services.AddHealthChecks();
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapHealthChecks("/health/liveness", new() { Predicate = _ => false });
|
||||
app.MapHealthChecks("/health/readiness");
|
||||
|
||||
app.MapGet("/version", () => Results.Json(new {
|
||||
service = "StellaOps.Policy", // override per service
|
||||
version = ThisAssembly.AssemblyInformationalVersion,
|
||||
commit = ThisAssembly.Git.Commit,
|
||||
buildTimestamp = ThisAssembly.Git.CommitDate.UtcDateTime,
|
||||
source = Environment.GetEnvironmentVariable("STELLA_CHANNEL") ?? "edge"
|
||||
}));
|
||||
|
||||
app.UseHttpMetrics();
|
||||
app.MapMetrics();
|
||||
|
||||
app.Run();
|
||||
```
|
||||
- Ensure `ThisAssembly.*` source generators are enabled or substitute build vars.
|
||||
- Keep `/health/liveness` lightweight; `/health/readiness` should test critical dependencies (Mongo, Redis, message bus) with timeouts.
|
||||
- When adding `/capabilities`, explicitly emit `merge = false` for Concelier/Excititor.
|
||||
|
||||
## CI verification
|
||||
- After publishing an image, run `ops/devops/docker/verify_health_endpoints.sh <image> [port]`.
|
||||
- CI should fail if any required endpoint is missing or non-200.
|
||||
|
||||
## Deployment
|
||||
- Helm/Compose should set `readOnlyRootFilesystem: true` and wire readiness/liveness probes to these paths/port.
|
||||
10
devops/docker/healthcheck-frontend.sh
Normal file
10
devops/docker/healthcheck-frontend.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
PATH_CHECK="${HEALTH_PATH:-/}"
|
||||
USER_AGENT="stellaops-frontend-healthcheck"
|
||||
|
||||
wget -qO- "http://${HOST}:${PORT}${PATH_CHECK}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
24
devops/docker/healthcheck.sh
Normal file
24
devops/docker/healthcheck.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
HOST="${HEALTH_HOST:-127.0.0.1}"
|
||||
PORT="${HEALTH_PORT:-8080}"
|
||||
LIVENESS_PATH="${LIVENESS_PATH:-/health/liveness}"
|
||||
READINESS_PATH="${READINESS_PATH:-/health/readiness}"
|
||||
USER_AGENT="stellaops-healthcheck"
|
||||
|
||||
fetch() {
|
||||
target_path="$1"
|
||||
# BusyBox wget is available in Alpine; curl not assumed.
|
||||
wget -qO- "http://${HOST}:${PORT}${target_path}" \
|
||||
--header="User-Agent: ${USER_AGENT}" \
|
||||
--timeout="${HEALTH_TIMEOUT:-4}" >/dev/null
|
||||
}
|
||||
|
||||
fail=0
|
||||
if ! fetch "$LIVENESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
if ! fetch "$READINESS_PATH"; then
|
||||
fail=1
|
||||
fi
|
||||
exit "$fail"
|
||||
48
devops/docker/sbom_attest.sh
Normal file
48
devops/docker/sbom_attest.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deterministic SBOM + attestation helper for DOCKER-44-002
|
||||
# Usage: ./sbom_attest.sh <image-ref> [output-dir] [cosign-key]
|
||||
# - image-ref: fully qualified image (e.g., ghcr.io/stellaops/policy:1.2.3)
|
||||
# - output-dir: defaults to ./sbom
|
||||
# - cosign-key: path to cosign key (PEM). If omitted, uses keyless if allowed (COSIGN_EXPERIMENTAL=1)
|
||||
|
||||
set -euo pipefail
|
||||
IMAGE_REF=${1:?"image ref required"}
|
||||
OUT_DIR=${2:-sbom}
|
||||
COSIGN_KEY=${3:-}
|
||||
|
||||
mkdir -p "${OUT_DIR}"
|
||||
|
||||
# Normalize filename (replace / and : with _)
|
||||
name_safe() {
|
||||
echo "$1" | tr '/:' '__'
|
||||
}
|
||||
|
||||
BASENAME=$(name_safe "${IMAGE_REF}")
|
||||
SPDX_JSON="${OUT_DIR}/${BASENAME}.spdx.json"
|
||||
CDX_JSON="${OUT_DIR}/${BASENAME}.cdx.json"
|
||||
ATTESTATION="${OUT_DIR}/${BASENAME}.sbom.att"
|
||||
|
||||
# Freeze timestamps for reproducibility
|
||||
export SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-1704067200}
|
||||
|
||||
# Generate SPDX 3.0-ish JSON (syft formats are stable and offline-friendly)
|
||||
syft "${IMAGE_REF}" -o spdx-json > "${SPDX_JSON}"
|
||||
# Generate CycloneDX 1.6 JSON
|
||||
syft "${IMAGE_REF}" -o cyclonedx-json > "${CDX_JSON}"
|
||||
|
||||
# Attach SBOMs as cosign attestations (one per format)
|
||||
export COSIGN_EXPERIMENTAL=${COSIGN_EXPERIMENTAL:-1}
|
||||
COSIGN_ARGS=("attest" "--predicate" "${SPDX_JSON}" "--type" "spdx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
COSIGN_ARGS=("attest" "--predicate" "${CDX_JSON}" "--type" "cyclonedx" "${IMAGE_REF}")
|
||||
if [[ -n "${COSIGN_KEY}" ]]; then
|
||||
COSIGN_ARGS+=("--key" "${COSIGN_KEY}")
|
||||
fi
|
||||
cosign "${COSIGN_ARGS[@]}"
|
||||
|
||||
echo "SBOMs written to ${SPDX_JSON} and ${CDX_JSON}" >&2
|
||||
echo "Attestations pushed for ${IMAGE_REF}" >&2
|
||||
12
devops/docker/services-matrix.env
Normal file
12
devops/docker/services-matrix.env
Normal file
@@ -0,0 +1,12 @@
|
||||
# service|dockerfile|project|binary|port
|
||||
# Paths are relative to repo root; dockerfile is usually the shared hardened template.
|
||||
api|ops/devops/docker/Dockerfile.hardened.template|src/VulnExplorer/StellaOps.VulnExplorer.Api/StellaOps.VulnExplorer.Api.csproj|StellaOps.VulnExplorer.Api|8080
|
||||
orchestrator|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.WebService/StellaOps.Orchestrator.WebService.csproj|StellaOps.Orchestrator.WebService|8080
|
||||
task-runner|ops/devops/docker/Dockerfile.hardened.template|src/Orchestrator/StellaOps.Orchestrator.Worker/StellaOps.Orchestrator.Worker.csproj|StellaOps.Orchestrator.Worker|8081
|
||||
concelier|ops/devops/docker/Dockerfile.hardened.template|src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj|StellaOps.Concelier.WebService|8080
|
||||
excititor|ops/devops/docker/Dockerfile.hardened.template|src/Excititor/StellaOps.Excititor.WebService/StellaOps.Excititor.WebService.csproj|StellaOps.Excititor.WebService|8080
|
||||
policy|ops/devops/docker/Dockerfile.hardened.template|src/Policy/StellaOps.Policy.Gateway/StellaOps.Policy.Gateway.csproj|StellaOps.Policy.Gateway|8084
|
||||
notify|ops/devops/docker/Dockerfile.hardened.template|src/Notify/StellaOps.Notify.WebService/StellaOps.Notify.WebService.csproj|StellaOps.Notify.WebService|8080
|
||||
export|ops/devops/docker/Dockerfile.hardened.template|src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj|StellaOps.ExportCenter.WebService|8080
|
||||
advisoryai|ops/devops/docker/Dockerfile.hardened.template|src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj|StellaOps.AdvisoryAI.WebService|8080
|
||||
console|ops/devops/docker/Dockerfile.console|src/Web/StellaOps.Web|StellaOps.Web|8080
|
||||
70
devops/docker/verify_health_endpoints.sh
Normal file
70
devops/docker/verify_health_endpoints.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Smoke-check /health and capability endpoints for a built image (DOCKER-44-003)
|
||||
# Usage: ./verify_health_endpoints.sh <image-ref> [port]
|
||||
# Requires: docker, curl or wget
|
||||
set -euo pipefail
|
||||
IMAGE=${1:?"image ref required"}
|
||||
PORT=${2:-8080}
|
||||
CONTAINER_NAME="healthcheck-$$"
|
||||
TIMEOUT=30
|
||||
SLEEP=1
|
||||
|
||||
have_curl=1
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
have_curl=0
|
||||
fi
|
||||
|
||||
req() {
|
||||
local path=$1
|
||||
local url="http://127.0.0.1:${PORT}${path}"
|
||||
if [[ $have_curl -eq 1 ]]; then
|
||||
curl -fsS --max-time 3 "$url" >/dev/null
|
||||
else
|
||||
wget -qO- --timeout=3 "$url" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "[info] starting container ${IMAGE} on port ${PORT}" >&2
|
||||
cleanup
|
||||
if ! docker run -d --rm --name "$CONTAINER_NAME" -p "${PORT}:${PORT}" "$IMAGE" >/dev/null; then
|
||||
echo "[error] failed to start image ${IMAGE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# wait for readiness
|
||||
start=$(date +%s)
|
||||
while true; do
|
||||
if req /health/liveness 2>/dev/null; then break; fi
|
||||
now=$(date +%s)
|
||||
if (( now - start > TIMEOUT )); then
|
||||
echo "[error] liveness endpoint did not come up in ${TIMEOUT}s" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep $SLEEP
|
||||
done
|
||||
|
||||
# verify endpoints
|
||||
fail=0
|
||||
for path in /health/liveness /health/readiness /version /metrics; do
|
||||
if ! req "$path"; then
|
||||
echo "[error] missing or failing ${path}" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
# capability endpoint optional; if present ensure merge=false for Concelier/Excititor
|
||||
if req /capabilities 2>/dev/null; then
|
||||
body="$(curl -fsS "http://127.0.0.1:${PORT}/capabilities" 2>/dev/null || true)"
|
||||
if echo "$body" | grep -q '"merge"[[:space:]]*:[[:space:]]*false'; then
|
||||
:
|
||||
else
|
||||
echo "[warn] /capabilities present but merge flag not false" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $fail
|
||||
Reference in New Issue
Block a user