Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .github/workflows/mainline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Mainline

# Tracks every push to the default branch: builds and pushes a bleeding-edge
# multi-arch image tagged mitre/vulcan:<git-sha> and :latest, then opens an Iron
# Bank merge request against the vulcan-mainline repo1 project. Mirrors
# release.yml, which publishes the stable mitre/vulcan:<version> + :release-latest.

on:
push:
branches: [master]
paths-ignore:
- 'docs/**'
- '*.md'
- 'LICENSE'
- '.github/workflows/ci.yml'
- '.github/workflows/docs.yml'
- '.github/workflows/release.yml'
- '.github/workflows/dependabot.yml'
workflow_dispatch:
inputs:
use_build_cloud:
description: 'Use Docker Build Cloud (fast, but consumes paid build minutes)'
required: false
type: boolean
default: false

concurrency:
group: mainline-${{ github.ref }}
cancel-in-progress: true

jobs:
# ─── DOCKER MAINLINE (multi-arch; QEMU by default, Build Cloud opt-in) ───
docker-mainline:
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Login to DockerHub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PAT }}

# Default: free QEMU-emulated multi-arch build on the runner.
# Opt into Docker Build Cloud (faster, consumes minutes) via the
# workflow_dispatch `use_build_cloud` input.
- name: Set up QEMU (for emulated multi-arch builds)
if: github.event.inputs.use_build_cloud != 'true'
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0

- name: Set up Docker Buildx (local + QEMU emulation)
if: github.event.inputs.use_build_cloud != 'true'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Set up Docker Buildx (Docker Build Cloud)
if: github.event.inputs.use_build_cloud == 'true'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
with:
driver: cloud
endpoint: "mitre/mitre-builder"

- name: Build and push multi-arch image (tags <git-sha> + latest)
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
env:
TAG_SUFFIXES: "${{ github.sha }},latest"
with:
files: docker-bake.hcl
targets: registry
push: true

# ─── IRON BANK MAINLINE STEPS ───

# Vulcan on IB has both arm and amd builds; we need hashes of both to provide to the hardening manifest in repo1
- name: Resolve per-architecture image digests for the Iron Bank manifest
id: digests
shell: bash
run: |
set -euo pipefail
tag="mitre/vulcan:${{ github.sha }}"
amd64="$(docker buildx imagetools inspect "$tag" --format '{{range .Manifest.Manifests}}{{if and (eq .Platform.OS "linux") (eq .Platform.Architecture "amd64")}}{{.Digest}}{{end}}{{end}}')"
arm64="$(docker buildx imagetools inspect "$tag" --format '{{range .Manifest.Manifests}}{{if and (eq .Platform.OS "linux") (eq .Platform.Architecture "arm64")}}{{.Digest}}{{end}}{{end}}')"
[[ "$amd64" == sha256:* ]] || { echo "Failed to resolve amd64 digest for $tag"; exit 1; }
[[ "$arm64" == sha256:* ]] || { echo "Failed to resolve arm64 digest for $tag"; exit 1; }
echo "amd64=$amd64" >> "$GITHUB_OUTPUT"
echo "arm64=$arm64" >> "$GITHUB_OUTPUT"
echo "Resolved digests: amd64=$amd64 arm64=$arm64"

# Opens a MR on Vulcan's vulcan-mainline repo1 repository with the latest main build
- name: Open Iron Bank mainline merge request
uses: mitre/ironbank_release_action@1c14c27db1c37e894f8042ac2edcde08d445882d # v1
with:
name: Vulcan
version: ${{ github.sha }}
ironbank_pat: ${{ secrets.SAF_IRONBANK_REPO1_PAT }}
ironbank_username: ${{ secrets.SAF_IRONBANK_REPO1_USERNAME }}
ironbank_project_id: 19019
ironbank_project_clone_url: repo1.dso.mil/dsop/mitre/security-automation-framework/vulcan-mainline.git
git_commit_author_name: "Automated Vulcan Mainline"
git_commit_author_email: "saf@mitre.org"
update_commands: |
yq e -i '.tags[0]=\"${{ github.sha }}\" | .labels.\"org.opencontainers.image.version\"=\"${{ github.sha }}\" | .resources[0].tag=\"mitre/vulcan:${{ github.sha }}\" | .resources[0].url=\"docker://docker.io/mitre/vulcan@${{ steps.digests.outputs.amd64 }}\" | .resources[1].tag=\"mitre/vulcan:${{ github.sha }}.arm64\" | .resources[1].url=\"docker://docker.io/mitre/vulcan@${{ steps.digests.outputs.arm64 }}\"' hardening_manifest.yaml
81 changes: 65 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ name: Release
on:
release:
types: [published]
workflow_dispatch:
inputs:
use_build_cloud:
description: 'Use Docker Build Cloud (fast, but consumes paid build minutes)'
required: false
type: boolean
default: false

jobs:
# ─── DOCKER RELEASE (multi-arch via Build Cloud) ───
Expand All @@ -21,33 +28,75 @@ jobs:
username: ${{ vars.DOCKER_USER }}
password: ${{ secrets.DOCKER_PAT }}

- name: Set up Docker Buildx (Build Cloud)
# Default: free QEMU-emulated multi-arch build on the runner.
# Opt into Docker Build Cloud (faster, consumes minutes) via the
# workflow_dispatch `use_build_cloud` input.
- name: Set up QEMU (for emulated multi-arch builds)
if: github.event.inputs.use_build_cloud != 'true'
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0

- name: Set up Docker Buildx (local + QEMU emulation)
if: github.event.inputs.use_build_cloud != 'true'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Set up Docker Buildx (Docker Build Cloud)
if: github.event.inputs.use_build_cloud == 'true'
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
with:
driver: cloud
endpoint: "mitre/mitre-builder"

- name: Docker metadata
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: mitre/vulcan
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
- name: Resolve release version
id: version
shell: bash
run: |
raw="${{ github.event.release.tag_name || github.ref_name }}"
echo "version=${raw#v}" >> "$GITHUB_OUTPUT"

- name: Build and push multi-arch image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
- name: Build and push multi-arch image (tags <version> + release-latest)
uses: docker/bake-action@6614cfa25eff9a0b2b2697efb0b6159e7680d584 # v7.2.0
env:
TAG_SUFFIXES: "${{ steps.version.outputs.version }},release-latest"
with:
context: .
platforms: linux/amd64,linux/arm64
files: docker-bake.hcl
targets: registry
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: SBOM scan and dependency submission
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
image: mitre/vulcan:${{ steps.meta.outputs.version }}
image: mitre/vulcan:${{ steps.version.outputs.version }}
artifact-name: image.spdx.json
dependency-snapshot: true

# ─── IRON BANK RELEASE STEPS ───

# Vulcan on IB has both arm and amd builds; we need hashes of both to provide to the hardening manifest in repo1
- name: Resolve per-architecture image digests for the Iron Bank manifest
id: digests
shell: bash
run: |
set -euo pipefail
tag="mitre/vulcan:${{ steps.version.outputs.version }}"
amd64="$(docker buildx imagetools inspect "$tag" --format '{{range .Manifest.Manifests}}{{if and (eq .Platform.OS "linux") (eq .Platform.Architecture "amd64")}}{{.Digest}}{{end}}{{end}}')"
arm64="$(docker buildx imagetools inspect "$tag" --format '{{range .Manifest.Manifests}}{{if and (eq .Platform.OS "linux") (eq .Platform.Architecture "arm64")}}{{.Digest}}{{end}}{{end}}')"
[[ "$amd64" == sha256:* ]] || { echo "Failed to resolve amd64 digest for $tag"; exit 1; }
[[ "$arm64" == sha256:* ]] || { echo "Failed to resolve arm64 digest for $tag"; exit 1; }
echo "amd64=$amd64" >> "$GITHUB_OUTPUT"
echo "arm64=$arm64" >> "$GITHUB_OUTPUT"
echo "Resolved digests: amd64=$amd64 arm64=$arm64"

# Opens a MR on Vulcan's repo1 repository with the new release
- name: Open Iron Bank merge request
uses: mitre/ironbank_release_action@1c14c27db1c37e894f8042ac2edcde08d445882d # v1
with:
name: Vulcan
version: ${{ steps.version.outputs.version }}
ironbank_pat: ${{ secrets.SAF_IRONBANK_REPO1_PAT }}
ironbank_username: ${{ secrets.SAF_IRONBANK_REPO1_USERNAME }}
ironbank_project_id: 17073
ironbank_project_clone_url: repo1.dso.mil/dsop/mitre/security-automation-framework/vulcan.git
git_commit_author_name: "Automated Vulcan Release"
git_commit_author_email: "saf@mitre.org"
update_commands: |
yq e -i '.tags[0]=\"${{ steps.version.outputs.version }}\" | .labels.\"org.opencontainers.image.version\"=\"${{ steps.version.outputs.version }}\" | .resources[0].tag=\"mitre/vulcan:${{ steps.version.outputs.version }}\" | .resources[0].url=\"docker://docker.io/mitre/vulcan@${{ steps.digests.outputs.amd64 }}\" | .resources[1].tag=\"mitre/vulcan:${{ steps.version.outputs.version }}.arm64\" | .resources[1].url=\"docker://docker.io/mitre/vulcan@${{ steps.digests.outputs.arm64 }}\"' hardening_manifest.yaml
38 changes: 22 additions & 16 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ variable "VERSION" {
default = "latest"
}

variable "TAG_SUFFIXES" {
default = "latest"
}

variable "VULCAN_BUNDLER_VERSION" {
default = "2.7.2"
}
Expand Down Expand Up @@ -105,6 +109,24 @@ target "production-multiarch" {
]
}

// ============================================================================
// Registry Push - For the release + mainline CI workflows
// ============================================================================
// The tag list is supplied by the workflow as a comma-separated TAG_SUFFIXES:
// release: TAG_SUFFIXES="<x.y.z>,release-latest"
// mainline: TAG_SUFFIXES="<git sha>,latest"
// The first suffix is the immutable version and is surfaced as the image label.

target "registry" {
inherits = ["production-multiarch"]

tags = [for s in split(",", TAG_SUFFIXES) : "${REGISTRY}/${IMAGE_NAME}:${trimspace(s)}"]

labels = {
"org.opencontainers.image.version" = trimspace(split(",", TAG_SUFFIXES)[0])
}
}

// ============================================================================
// Development Target - Full dev environment with all dependencies
// ============================================================================
Expand Down Expand Up @@ -152,19 +174,3 @@ target "ci-multiarch" {
"linux/arm64"
]
}

// ============================================================================
// Release Build - For tagged releases
// ============================================================================

target "release" {
inherits = ["production-multiarch"]

// Override tags for release
tags = [
"${REGISTRY}/${IMAGE_NAME}:${VERSION}",
"${REGISTRY}/${IMAGE_NAME}:latest",
"ghcr.io/${REGISTRY}/${IMAGE_NAME}:${VERSION}",
"ghcr.io/${REGISTRY}/${IMAGE_NAME}:latest"
]
}
6 changes: 3 additions & 3 deletions docs/deployment/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ Vulcan provides three ways to create the initial admin:
### Using Docker Run

```bash
# Pull the latest image
docker pull mitre/vulcan:latest
# Pull the latest released image
docker pull mitre/vulcan:release-latest

# Run with PostgreSQL
docker run -d \
--name vulcan \
-p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host/vulcan" \
-e SECRET_KEY_BASE="your-secret-key" \
mitre/vulcan:latest
mitre/vulcan:release-latest
```

## Image Details
Expand Down
9 changes: 7 additions & 2 deletions docs/development/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,19 @@ That push triggers everything. No further manual steps are required.
- Logs in to DockerHub
- Uses Docker Build Cloud (`mitre/mitre-builder`) for native multi-arch builds
- Builds `linux/amd64` and `linux/arm64` images
- Pushes `mitre/vulcan:v2.3.2` and `mitre/vulcan:latest` to DockerHub
- Pushes `mitre/vulcan:v2.3.2` and `mitre/vulcan:release-latest` to DockerHub
- Generates SBOM (SPDX format) and submits to GitHub dependency graph

> **Tag convention:** `release-latest` is the moving pointer to the most recent
> stable release; `latest` is the bleeding-edge build of `master` published by
> `mainline.yml` on every push. Use `release-latest` (or a pinned `vX.Y.Z`) for
> production; `latest` only for trying the newest unreleased code.

### 5. Verify the release

1. Check [Actions](https://github.com/mitre/vulcan/actions) — `release.yml` and `ci.yml` runs should both be green
2. Check [Releases](https://github.com/mitre/vulcan/releases) — new release should exist with changelog populated
3. Check [DockerHub](https://hub.docker.com/r/mitre/vulcan/tags) — new version tag and `latest` should be present
3. Check [DockerHub](https://hub.docker.com/r/mitre/vulcan/tags) — new version tag and `release-latest` should be present
4. Pull and smoke-test the image:

```bash
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Before installing, you can try Vulcan directly:
### 1. Pull and Run with Docker

```bash
# Pull the latest Docker image
docker pull mitre/vulcan:latest
# Pull the latest released image
docker pull mitre/vulcan:release-latest

# Or use docker compose for a complete setup
wget https://raw.githubusercontent.com/mitre/vulcan/master/docker-compose.yml
Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ const involvement = [
### Quick Test with Docker

```bash
docker pull mitre/vulcan:latest
docker run -p 3000:3000 mitre/vulcan:latest
docker pull mitre/vulcan:release-latest
docker run -p 3000:3000 mitre/vulcan:release-latest
```

### Full Setup with Docker Compose
Expand Down
2 changes: 1 addition & 1 deletion docs/security/compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ spec:

containers:
- name: vulcan
image: mitre/vulcan:latest
image: mitre/vulcan:release-latest

# Security Settings
securityContext:
Expand Down
Loading