Skip to content

Add OpenBao engine support to safe CLI and CI matrix#7

Open
itsouvalas wants to merge 11 commits into
cloudfoundry-community:developfrom
itsouvalas:feature/openbao-engine-support
Open

Add OpenBao engine support to safe CLI and CI matrix#7
itsouvalas wants to merge 11 commits into
cloudfoundry-community:developfrom
itsouvalas:feature/openbao-engine-support

Conversation

@itsouvalas

Copy link
Copy Markdown

Summary

  • safe local gains an --engine vault|bao flag with an engine-abstraction
    layer; auto-detects whichever binary is on $PATH when unset (prefers
    bao). Vault<0.8 compatibility code removed along the way.
  • ci/scripts/tests round-trip suite (~2000 assertions) parameterised on
    ENGINE. Handles the Vault↔OpenBao archive-naming divergence
    (vault_…_linux_amd64.zip vs bao_…_Linux_x86_64.tar.gz) via a small
    bash helper library with its own unit tests.
  • New test-openbao-2.5 Concourse job tracking the latest 2.5.x patch,
    sibling to the existing test-vault-1.9test-vault-1.13 jobs. Runs
    OpenBao in non-dev mode with disable_unauthed_rekey_endpoints = "false"
    so /sys/rekey/* stays reachable (bao defaults it off since v2.5.0).
  • Restores the make test-release target; the post-ed78ec7 cleanup
    redefined make test to plain go test, silently disconnecting the
    round-trip script from CI.
  • One safe bug fix in duration(): ^time.Duration(0) >> 1 yielded -1
    not MaxInt64, so the overflow guard rejected every non-zero year
    count (the default 10y CA TTL included).
  • Three test-expectation updates for safe's lowercase-first error wording
    (normalised in the earlier gosec/staticcheck cleanup wave).

Test plan

  • make test — go unit tests pass
  • make test-release-engine — 29/29 bash helper unit tests green
  • make test-release ENGINE=vault VERSIONS=1.13.13 — 1694/1694 green
  • SAFE_DISABLE_GPG_TESTS=1 make test-release ENGINE=bao VERSIONS=2.5.3 — 1694/1694 green
  • fly execute of the bao task on a linux/amd64 Concourse worker — 1724/1724 green
  • fly set-pipeline on a docker-compose Concourse resolves the
    new github-openbao-2.5 resource to v2.5.3; existing
    github-vault-1.13 continues to resolve to v1.13.13.

Adds an Engine interface with vaultEngine and baoEngine
implementations plus selectLocalEngine() which picks the preferred
available binary on PATH. Order: prefer bao (OpenBao), fall back to
vault (HashiCorp Vault). An optional preference string pins a
specific engine or reports unknown engines cleanly.

Test coverage includes auto-detect (bao-only, vault-only, both,
neither) and preference-override paths. No production code yet
consumes selectLocalEngine — subsequent commits wire it into
safe local.
safe local previously discarded the root token returned by
v.Init(1, 1) and regenerated one via /sys/generate-root/attempt.
OpenBao disables that endpoint, so the flow failed with
'unsupported operation' on every fresh bao server.

Capture Init's token directly, and only fall back to NewRootToken
when it is empty — i.e. when --file points at an existing vault
whose unseal key the user supplied but whose original root token
is unknown.

On vault this saves one round trip. On bao it unblocks --memory
and fresh --file backends. Re-targeting an existing bao file
backend still requires --engine vault because generate-root is
unavailable.
Extends the 'safe local' help block with the new --engine flag in
the Usage line and a paragraph explaining the auto-detect order
(prefer bao over vault when both are installed).

Adds a '### local' section to README covering --memory vs --file
usage, the --engine flag, and the OpenBao caveat that the --file
backend can only be used with a fresh directory — re-targeting an
existing one needs the original root token, which requires
--engine vault because OpenBao disables the legacy generate-root
API.
Adds --engine {vault,bao} to 'safe local' and routes server spawn
through Engine.Binary() instead of hard-coded 'vault'. The
selection respects the flag when set, otherwise auto-detects
per selectLocalEngine.

Also drops the pre-0.8.0 Vault storage-key version probe.
The 'backend' HCL key was renamed to 'storage' in Vault 0.8
(August 2017); any Vault recent enough to run safe uses
'storage' unconditionally, and OpenBao has never supported the
legacy key. The storage key is now hard-coded, removing one
exec.Command('vault', 'version') round trip and the
goto-based fallback.

The hard-coded 'Vault is not installed' error message is
replaced by the engine-aware messages produced inside
selectLocalEngine (names both candidate binaries when neither
is found; names the specific binary when a preference is
unreachable).
`^time.Duration(0) >> 1` on a signed int64 uses arithmetic right
shift, so `^(-1) >> 1 == -1`, not MaxInt64 as intended. With
maxDuration==-1ns, `maxDuration/time.Hour==0`, so the overflow
guard `v > 0/hoursPerYear` rejected every non-zero year count and
surfaced as `duration overflow: 10 years is too large` against
the default 10y CA TTL.

Swap in `^uint64(0) >> 1` (unsigned logical shift) to get the
intended MaxInt64. Add util_test.go covering happy paths, the
overflow detection still kicking in at the right scale, and
unrecognised duration strings.

Introduced alongside the G115 gosec overflow guards in
83f7830. Invisible until the ci/scripts/tests round-trip suite
was reconnected via make test-release.
ci/scripts/tests now selects between HashiCorp Vault and OpenBao
via the ENGINE env var (default vault). The download URL,
archive format, binary name, and OS/arch aliasing vary per
engine; the suite itself runs unchanged.

Helper functions live in ci/scripts/lib/engine.sh:
  engine_url, engine_archive_ext, engine_binary_name,
  engine_local_binary, engine_arch_for, engine_os_for,
  engine_startup_mode.

Unit tests in ci/scripts/t/engine_test.sh exercise every
supported {engine,os,arch} combination plus the error paths
(unknown engine / arch / os). Pure bash, no network, <1s.

restart_engine_server dispatches on engine_startup_mode:
  - vault -> _start_engine_dev (existing `server -dev` flow)
  - bao   -> _start_engine_config (writes HCL config with
    disable_unauthed_rekey_endpoints=false so /sys/rekey/*
    stays reachable, then init + unseal + pre-mount secret/
    via the HTTP API)

Also drops the old arch=amd64 hardcode in favour of a
uname -m case that handles x86_64/amd64/aarch64/arm64. The
vault path's behaviour is preserved identically when ENGINE is
unset or vault.
Three test expectations in ci/scripts/tests referenced the
pre-cleanup uppercase-first error strings (`!! Failed ...`,
`!! No file ...`, `!! Key submission ...`). Safe's user-facing
error output was normalised to lowercase-first during an earlier
gosec/staticcheck pass (same cleanup wave as ed78ec7/83f7830).

These assertions have been silently wrong since the round-trip
suite was disconnected from make test, and surfaced as soon as
make test-release wired them back up. Update the expected
strings to match current output; no safe code change.
The make test target was rewritten in commit ed78ec7 into a
plain `go test` invocation, which silently ignored the
TEST_PATH / SAFE_PATH / VAULT_VERSIONS variables that
ci/scripts/test-release-against-vault has been passing via CI.
The ~2000-line round-trip suite in ci/scripts/tests has not
actually run in Concourse since that commit landed in 2025.

Add a distinct test-release target that invokes the
parameterized round-trip script directly, honoring ENGINE /
VERSIONS / SAFE_PATH / TEST_PATH. `make test` (Go unit tests)
is left untouched so nothing that depends on it regresses.

Also add test-release-engine as a convenience for running the
fast bash-level helper unit tests without touching the live
engine.
Split the Concourse task entry point into two scripts:

- test-release-against-engine: canonical wrapper. Accepts
  ENGINE env (default vault), picks VAULT_VERSIONS or
  OPENBAO_VERSIONS per engine, and invokes
  `make test-release` with the right env.

- test-release-against-vault: trivial back-compat shim that
  `exec`s test-release-against-engine with ENGINE=vault.
  Preserves the existing task.yml reference until
  ci/tasks/test.yml flips in the follow-up commit.

Also fixes a pre-existing bug where the wrapper validated
VAULT_VERSION (singular) but forwarded VAULT_VERSIONS (plural);
task.yml always passes plural. New wrapper validates per
engine on the plural name.
Adds a github-release resource tracking the latest OpenBao
2.5.x patch (tag_filter v?(2.5.[^v].*), mirrors the existing
Vault resources) and a test-openbao-2.5 job that runs the
round-trip suite against it with ENGINE=bao and
OPENBAO_VERSIONS loaded from the resource's version file.

Appends test-openbao-2.5 to the build-safe-releases group via
spruce (( append )), so the job shows up alongside the five
existing test-vault-* jobs without touching pipeline/base.yml
(which is marked DO NOT MODIFY).

Flips ci/tasks/test.yml from the back-compat vault shim to the
generalized test-release-against-engine wrapper. Vault jobs
continue to work unchanged (ENGINE defaults to vault in the
wrapper); the OpenBao job passes ENGINE=bao via params.

Validated locally via spruce merge + docker-compose Concourse:
fly set-pipeline accepts the merged yaml, fly check-resource
resolves github-openbao-2.5 to v2.5.3, test-vault-1.13 to
v1.13.13. Full end-to-end task run of test-openbao-2.5
(via fly execute on a linux worker) completes clean — 1724
assertions passing, 0 failing.
Add a Testing section covering:
  - make test (Go unit tests)
  - make test-release ENGINE={vault,bao} VERSIONS=...
    with multi-version and SAFE_DISABLE_GPG_TESTS examples
  - make test-release-engine (fast helper lib unit tests)
  - Concourse matrix description and release-page links
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant