Skip to content
Open
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
24 changes: 21 additions & 3 deletions .github/workflows/openbao-jwt-plugin.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: Copyright (c) NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Build and test the OpenBao JWT secrets plugin.
# Build and test the OpenBao server and JWT secrets plugin.
#
# This module is deliberately NOT in go.work.bazel and has no BUILD.bazel, so
# the Bazel matrix in bazel.yml never sees it. That is the point: its graph is
Expand All @@ -20,12 +20,12 @@ on:
push:
branches: [main]
paths:
- 'infra/openbao/plugins/vault-plugin-secrets-jwt/**'
- 'infra/openbao/**'
- '.github/workflows/openbao-jwt-plugin.yml'
pull_request:
branches: [main]
paths:
- 'infra/openbao/plugins/vault-plugin-secrets-jwt/**'
- 'infra/openbao/**'
- '.github/workflows/openbao-jwt-plugin.yml'
merge_group:
types: [checks_requested]
Expand All @@ -43,6 +43,24 @@ defaults:
shell: bash

jobs:
server-build:
name: build and verify server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: 1.27.0

- name: Build and verify both server architectures
working-directory: infra/openbao
run: scripts/build-openbao.sh

- name: Test semantic version comparisons
working-directory: infra/openbao
run: scripts/test-semver.sh

build-test:
name: build and test
runs-on: ubuntu-latest
Expand Down
1 change: 0 additions & 1 deletion dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,6 @@ Generated by `go run -C ./tools/collect-dependencies .`. Refresh: `go run -C ./t
- `Go`: `google.golang.org/grpc/cmd/protoc-gen-go-grpc`
- `Go`: `gopkg.in/go-jose/go-jose.v2`
- `Go`: `gopkg.in/ini.v1`
- `Go`: `gopkg.in/square/go-jose.v2`
- `Go`: `gopkg.in/yaml.v2`
- `Go`: `gopkg.in/yaml.v3`
- `Go`: `gotest.tools/v3`
Expand Down
39 changes: 34 additions & 5 deletions infra/openbao/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,30 @@
# context, so the stage below compiles it from the same commit that produces
# the image. Nothing is vendored and nothing is injected, so `docker build .`
# here produces the same image as the release pipeline.
ARG BAO_VERSION=2.5.5
ARG GO_VERSION=1.25
ARG BAO_VERSION=2.6.2
ARG GO_VERSION=1.27.0
ARG GO_IMAGE=golang:1.27.0-alpine@sha256:4c9fe60190a2a3350ddc51de80d0224b8a6698d12bdfc999fee45ea9d6c46dbc
ARG BAO_SOURCE_COMMIT=dd9c19c37a878cf4a81b18efb8d6f0599c7da923
ARG BAO_SOURCE_SHA256=a7784550a9db16f24e99d65a18c9b12a433707c79ef4c1f34262d3f48171c7a9
ARG BAO_RUNTIME_IMAGE=openbao/openbao:2.6.2@sha256:11fd73a2102cda9c55d5d881a8c3210303146a7ec1e8ac76f526e175c6d24641

FROM golang:${GO_VERSION}-alpine AS plugin-build
FROM ${GO_IMAGE} AS bao-build
ARG BAO_VERSION
ARG BAO_SOURCE_COMMIT
ARG BAO_SOURCE_SHA256
ARG GO_VERSION
ARG TARGETARCH
RUN apk add --no-cache curl xz
COPY scripts/build-openbao.sh scripts/verify-openbao.sh scripts/semver.sh /usr/local/bin/
RUN BAO_VERSION="${BAO_VERSION}" \
BAO_SOURCE_COMMIT="${BAO_SOURCE_COMMIT}" \
BAO_SOURCE_SHA256="${BAO_SOURCE_SHA256}" \
GO_TOOLCHAIN="go${GO_VERSION}" \
TARGETARCH="${TARGETARCH}" \
OUTPUT_DIR=/out \
/usr/local/bin/build-openbao.sh

FROM ${GO_IMAGE} AS plugin-build
ARG TARGETARCH
WORKDIR /src
# Module files first so dependency download caches independently of source edits.
Expand All @@ -26,10 +46,19 @@ COPY plugins/vault-plugin-secrets-jwt/ ./
RUN GOOS=linux GOARCH="${TARGETARCH}" CGO_ENABLED=0 \
go build -trimpath -o /out/vault-plugin-secrets-jwt ./cmd/vault-plugin-secrets-jwt

FROM openbao/openbao:${BAO_VERSION}
FROM ${BAO_RUNTIME_IMAGE}
ARG TARGETARCH

# Upgrade packages inherited from the upstream image before installing the
# runtime tools. This ensures security fixes from the pinned Alpine branch are
# applied even when the upstream image predates them.
USER root
# hadolint ignore=DL3018
RUN apk add --no-cache curl jq bash && \
RUN apk upgrade --no-cache && \
apk add --no-cache curl jq bash && \
mkdir -p /openbao/plugins

COPY --from=bao-build --chmod=0555 /out/bao-linux-${TARGETARCH} /usr/bin/bao
COPY --from=plugin-build --chmod=0555 /out/vault-plugin-secrets-jwt /openbao/plugins/vault-plugin-secrets-jwt

USER openbao
39 changes: 39 additions & 0 deletions infra/openbao/OPENBAO_PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# OpenBao server provenance

The image replaces the server binary from the upstream runtime image with a
locally compiled OpenBao binary. The runtime filesystem, entrypoint, default
configuration, user, and command remain from `openbao/openbao:2.6.2`.
The runtime image is pinned to multi-architecture manifest digest
`sha256:11fd73a2102cda9c55d5d881a8c3210303146a7ec1e8ac76f526e175c6d24641`.
The Go 1.27.0 Alpine builder is pinned to multi-architecture manifest digest
`sha256:4c9fe60190a2a3350ddc51de80d0224b8a6698d12bdfc999fee45ea9d6c46dbc`.

## Source

The build uses the official `openbao-dist-v2.6.2.tar.xz` release asset. That
asset includes the generated web UI used by upstream release binaries.

- Version: `v2.6.2`
- Source commit: `dd9c19c37a878cf4a81b18efb8d6f0599c7da923`
- Source SHA-256: `a7784550a9db16f24e99d65a18c9b12a433707c79ef4c1f34262d3f48171c7a9`
- License: MPL-2.0

`scripts/build-openbao.sh` verifies the source checksum before extracting it.
The MPL-2.0 license remains in the upstream runtime image at
`/licenses/mozilla.txt`.

## Dependency floors

The source build updates these modules before compiling the server:

- Go 1.27.0
- `golang.org/x/crypto v0.56.0`
- `google.golang.org/grpc v1.83.1`
- `github.com/moby/go-archive v0.3.0`

Go minimal version selection also updates the transitive modules required by
those versions. The build runs `go mod tidy` and `go mod verify`, then compiles
with the upstream `ui` build tag and release version metadata.

`scripts/verify-openbao.sh` asserts both target architectures and the three
dependency floors against the module metadata embedded in each binary.
38 changes: 31 additions & 7 deletions infra/openbao/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ Container image used by NVCF deployments to run [OpenBao](https://openbao.org/),
This repository ships:

- A multi-arch container image definition (`Dockerfile`) layered on top of `openbao/openbao`
- A directory (`files/plugins/`) where the user supplies the vault plugin binary at build time
- A checksum-pinned OpenBao source build with reviewed dependency floors
- The additional JWT secrets plugin NVCF expects at runtime

The final image keeps the upstream runtime filesystem, entrypoint, default
configuration, and `openbao` user. It replaces `/usr/bin/bao` with a binary
built from the matching official distribution source. See
`OPENBAO_PROVENANCE.md` for the source identity and dependency floors.

## Plugin binaries

Expand Down Expand Up @@ -38,19 +44,37 @@ scripts/verify-jwt-plugin.sh # asserts module path, target, toolchain, deps
`files/plugins/` is gitignored apart from `.gitkeep`; see
`files/plugins/PROVENANCE.md` for the dependency floors the verifier enforces.

## OpenBao server binary

The server build downloads the official OpenBao 2.6.2 distribution source,
checks its SHA-256 digest, applies the reviewed Go module floors, and builds
the same UI-enabled command for Linux amd64 and arm64:

```bash
scripts/build-openbao.sh # writes both arch binaries to files/openbao/
scripts/verify-openbao.sh # asserts target metadata and dependency floors
```

The distribution source is used because it contains the generated web UI that
is embedded in upstream release binaries. The auto-generated GitHub source
archive does not contain those assets.

## Prerequisites

- Docker or another OCI-compatible builder (with `buildx` for multi-arch)
- A built copy of `vault-plugin-secrets-jwt` for each platform you target, placed in `files/plugins/`
- Go 1.27.0 or newer when building the server or plugin outside the container
- `curl`, `tar` with xz support, and `sha256sum` or `shasum` for a local server build

## Building the container

The `Dockerfile` defaults to the `openbao/openbao:2.5.5` base image. Override the `BAO_VERSION` build-arg to track a different upstream tag.
The `Dockerfile` defaults to the `openbao/openbao:2.6.2` base image and the
matching checksum-pinned distribution source. A version update must also pin
the corresponding source commit, checksum, and commit date.

```bash
docker build \
--build-arg TARGETARCH=amd64 \
--build-arg BAO_VERSION=2.5.5 \
--build-arg BAO_VERSION=2.6.2 \
-t <your-registry>/<your-org>/nvcf-openbao:<version> .
```

Expand All @@ -59,7 +83,7 @@ For multi-arch builds:
```bash
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg BAO_VERSION=2.5.5 \
--build-arg BAO_VERSION=2.6.2 \
-t <your-registry>/<your-org>/nvcf-openbao:<version> \
--push .
```
Expand All @@ -68,6 +92,6 @@ docker buildx build \

At runtime the image provides:

- The upstream OpenBao server (`/usr/local/bin/bao`)
- The OpenBao server at `/usr/bin/bao`, built from the upstream 2.6.2 source
- Alpine packages `curl`, `jq`, and `bash` (used by entrypoint scripts in consumers such as the migrations Job)
- `/openbao/plugins/vault-plugin-secrets-jwt` - the JWT secrets plugin built from `outfoxx/vault-plugin-secrets-jwt`
- `/openbao/plugins/vault-plugin-secrets-jwt` - the JWT secrets plugin built from `github.com/NVIDIA/nvcf/infra/openbao/plugins/vault-plugin-secrets-jwt`
2 changes: 2 additions & 0 deletions infra/openbao/files/openbao/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
18 changes: 11 additions & 7 deletions infra/openbao/files/plugins/PROVENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ modified copy of the Apache-2.0 project `outfoxx/vault-plugin-secrets-jwt`;
that directory's `NOTICE` enumerates every NVIDIA change.

The image build compiles the plugin from that source in a Dockerfile build
stage, so the binary and the image come from the same commit. Nothing is
fetched from outside this repository at build time.
stage, so the binary and the image come from the same commit. Go module inputs
are verified through the committed `go.sum` checksums.

## Dependency floors

Held deliberately, not incidental to a `go mod tidy`:

- `golang.org/x/net v0.55.0` - security floor
- Go 1.27.0 - security floor for the standard library
- `golang.org/x/crypto v0.56.0` - security floor
- `golang.org/x/net v0.57.0` - selected by `golang.org/x/crypto v0.56.0`
- `golang.org/x/text v0.41.0` - selected by `golang.org/x/crypto v0.56.0`
- `google.golang.org/grpc v1.83.1` - security floor
- `github.com/go-jose/go-jose/v4 v4.1.4` - direct JWT/JWS implementation and security floor
- `github.com/hashicorp/vault/api v1.15.0`
- `github.com/hashicorp/vault/sdk v0.15.2`
- `google.golang.org/grpc v1.69.4`
- `github.com/go-jose/go-jose/v4 v4.0.4`

The vault and grpc pins keep compatibility with the previously shipped plugin
binary. `scripts/verify-jwt-plugin.sh` asserts them against the built artifact.
The Vault pins preserve the previously shipped plugin compatibility contract;
the remaining pins are security floors. `scripts/verify-jwt-plugin.sh` asserts
them against the built artifact.

## Local build

Expand Down
7 changes: 7 additions & 0 deletions infra/openbao/plugins/vault-plugin-secrets-jwt/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ changes were made to the original work:
install script, Makefile and linter configuration. Source, tests, and
license material were retained in full.

11. plugin/backend.go, plugin/config.go, plugin/path_config.go,
plugin/path_config_test.go, plugin/path_jwks.go, plugin/path_jwks_test.go,
plugin/path_sign.go, plugin/path_sign_test.go, plugin/policy_signer.go, and
test/jwtverify/jwtverify.go: JSON Web Signature and JWT handling was
migrated from square/go-jose v2 to go-jose v4. Parser calls now explicitly
restrict accepted signature algorithms to the configured supported set.

Files carrying an NVIDIA copyright header are NVIDIA-authored. Every
upstream-originated file retains the upstream Outfox, Inc. header. AGENTS.md
and CLAUDE.md carry neither, being repository guidance rather than distributed
Expand Down
36 changes: 17 additions & 19 deletions infra/openbao/plugins/vault-plugin-secrets-jwt/go.mod
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
module github.com/NVIDIA/nvcf/infra/openbao/plugins/vault-plugin-secrets-jwt

go 1.23.3

toolchain go1.23.8
go 1.27.0

require (
github.com/go-jose/go-jose/v4 v4.1.4
github.com/go-test/deep v1.1.1
github.com/google/uuid v1.6.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/vault/api v1.15.0
github.com/hashicorp/vault/sdk v0.15.2
gopkg.in/square/go-jose.v2 v2.6.0
)

require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.2.1+incompatible // indirect
Expand All @@ -26,8 +25,7 @@ require (
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
Expand Down Expand Up @@ -56,7 +54,6 @@ require (
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
Expand All @@ -75,20 +72,21 @@ require (
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sasha-s/go-deadlock v0.3.5 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tink-crypto/tink-go/v2 v2.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.69.4 // indirect
google.golang.org/protobuf v1.36.3 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.83.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading