Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f8345ab
Minor note
alvagante Sep 15, 2026
50303e6
Rewrite README for conciseness and add Checkmk integration
alvagante Sep 15, 2026
d5b4eb0
feat(docker): publish core and batteries images
alvagante Sep 16, 2026
17a84a1
fix(puppetdb): reset circuit breaker failures on success while closed
alvagante Sep 24, 2026
44ee0ef
Make MCP service user provisioning repair partial state
alvagante Oct 21, 2026
2c23a95
Add reset-password CLI for local account recovery
alvagante Oct 21, 2026
a739cf2
Add external identity boundaries for LDAP-ready auth (migration 031)
alvagante Oct 21, 2026
2d37548
Add shared LDAP-ready federated provisioning and enforce directory owner
alvagante Oct 21, 2026
4fac169
Add read-only LDAP directory service and validated configuration
alvagante Oct 21, 2026
4a5c2b4
Add restricted LDAP group-mapping administration API
alvagante Oct 21, 2026
ac20696
Bump version to 1.6.0 (WIP)
alvagante Oct 21, 2026
7f58092
Add provider-bound session context and shared authentication service
alvagante Oct 21, 2026
3fee4b3
Bump version to 1.6.0 and finalize release metadata
alvagante Oct 21, 2026
56ea384
Add LDAP password login with provider-bound session revalidation
alvagante Oct 21, 2026
55c8107
Add LDAP login UI, group-mapping admin and operator docs
alvagante Oct 21, 2026
7389784
Replace @ldapjs/dn with an owned RFC 4514 DN parser for LDAP
alvagante Oct 21, 2026
8979651
Add live LDAP CI acceptance job and AD lab harness
alvagante Oct 21, 2026
be12ea5
Add AD lab harness and acceptance runner regression tests for LDAP
alvagante Oct 21, 2026
25adb4e
Update OpenVox 8 release package checksum in Dockerfile
alvagante Oct 21, 2026
8e4bc6b
Fix flaky LDAP readiness assertion and apply npm audit fix
alvagante Oct 22, 2026
09e2ef6
Limit CI dependency audit to production dependencies
alvagante Oct 22, 2026
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
83 changes: 75 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
- name: Check documentation contracts
run: node --test scripts/documentation/contracts.test.mjs

- name: Check Docker image contracts
run: node --test scripts/deployment/docker-images.test.mjs

- name: Run ESLint
run: npm run lint

Expand Down Expand Up @@ -106,6 +109,54 @@ jobs:
name: playwright-report
path: playwright-report/

ldap-acceptance:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
postgres: ['15', '17']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm ci --ignore-scripts
- run: npm run install:approved
- name: Verify acceptance orchestration contracts
run: node --test scripts/acceptance/*.test.mjs
- name: Typecheck opt-in AD fixtures without directory access
run: ./node_modules/.bin/tsc --project test/fixtures/ldap/tsconfig.ad.json
working-directory: ./backend
- name: Install fixture scanner
run: |
sudo apt-get update
sudo apt-get install -y clamav
sudo systemctl stop clamav-freshclam || true
sudo freshclam
- name: Install dedicated browser
run: npx playwright install --with-deps chromium
- name: Prepare PostgreSQL image
run: docker pull postgres:${{ matrix.postgres }}
- name: Run live LDAP and recovery matrix plus dedicated browser contracts
run: >-
npm run test:ldap -- --focused --browser --build-fixture
--postgres-image postgres:${{ matrix.postgres }} --postgres-major ${{ matrix.postgres }}
--output-root '${{ runner.temp }}/ldap-evidence'
- name: Retain synthetic LDAP acceptance evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: ldap-acceptance-postgres-${{ matrix.postgres }}
retention-days: 14
if-no-files-found: error
# Only the synthetic runner output, never container filesystems or AD evidence.
path: |
${{ runner.temp }}/ldap-evidence/**
!${{ runner.temp }}/ldap-evidence/**/*.key
!${{ runner.temp }}/ldap-evidence/**/fixture-downloads/**
!${{ runner.temp }}/ldap-evidence/**/ca.pem

secret-scan:
# The pre-commit hook excludes docs, tests and e2e fixtures. This job scans
# them too, so a credential committed under an excluded path is still
Expand All @@ -128,7 +179,7 @@ jobs:
with:
node-version-file: '.nvmrc'
- name: Audit the reviewed lockfile
run: npm audit --json --audit-level=high > npm-audit.json
run: npm audit --json --audit-level=high --omit=dev > npm-audit.json
- uses: actions/upload-artifact@v4
if: always()
with:
Expand All @@ -140,25 +191,41 @@ jobs:
strategy:
fail-fast: false
matrix:
variant: [bookworm, alpine, ubuntu]
include:
- variant: bookworm-core
file: Dockerfile
target: core
profile: core
- variant: bookworm-batteries
file: Dockerfile
target: batteries
profile: batteries
- variant: alpine
file: Dockerfile.alpine
target: ""
profile: bolt
- variant: ubuntu
file: Dockerfile.ubuntu
target: ""
profile: bolt
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build and verify shipped image
run: |
file=Dockerfile
if [ "${{ matrix.variant }}" != bookworm ]; then file="Dockerfile.${{ matrix.variant }}"; fi
docker build --no-cache -f "$file" -t pabawi:test .
bash scripts/supply-chain/image-smoke.sh pabawi:test
target=()
if [ -n "${{ matrix.target }}" ]; then target=(--target "${{ matrix.target }}"); fi
docker build --no-cache "${target[@]}" -f "${{ matrix.file }}" -t pabawi:test .
bash scripts/supply-chain/image-smoke.sh pabawi:test "${{ matrix.profile }}"
mkdir -p artifacts
docker run --rm -i --entrypoint node pabawi:test < scripts/supply-chain/dependency-graph.cjs > artifacts/dependencies.json
docker build --no-cache --target backend-deps -f "$file" -t pabawi:deps-repeat .
docker build --no-cache --target backend-deps -f "${{ matrix.file }}" -t pabawi:deps-repeat .
docker run --rm -i --entrypoint node pabawi:deps-repeat < scripts/supply-chain/dependency-graph.cjs > artifacts/dependencies-repeat.json
cmp artifacts/dependencies.json artifacts/dependencies-repeat.json
if [ "${{ matrix.variant }}" = ubuntu ]; then
docker run --rm --entrypoint ruby pabawi:test /opt/bolt/test-winrm-rubyzip.rb
docker run --rm -i --entrypoint ruby pabawi:test < docker/bolt/dependency-graph.rb > artifacts/ruby-dependencies.json
docker build --no-cache --target bolt-builder -f "$file" -t pabawi:ruby-repeat .
docker build --no-cache --target bolt-builder -f "${{ matrix.file }}" -t pabawi:ruby-repeat .
docker run --rm -i --entrypoint ruby pabawi:ruby-repeat < docker/bolt/dependency-graph.rb > artifacts/ruby-dependencies-repeat.json
cmp artifacts/ruby-dependencies.json artifacts/ruby-dependencies-repeat.json
fi
Expand Down
62 changes: 44 additions & 18 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
DOCKERHUB_IMAGE: example42/pabawi

concurrency:
group: publish-${{ github.ref }}
Expand All @@ -20,6 +21,7 @@ jobs:
fail-fast: false
matrix:
arch: [amd64, arm64]
profile: [core, batteries]
permissions:
contents: read
packages: write
Expand All @@ -40,40 +42,55 @@ jobs:
with:
context: .
platforms: linux/${{ matrix.arch }}
target: ${{ matrix.profile }}
load: true
push: false
tags: pabawi:candidate
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=release-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=release-${{ matrix.arch }}
cache-from: type=gha,scope=release-${{ matrix.profile }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=release-${{ matrix.profile }}-${{ matrix.arch }}
- name: Verify release candidate
env:
DOCKER_DEFAULT_PLATFORM: linux/${{ matrix.arch }}
run: |
bash scripts/supply-chain/image-smoke.sh pabawi:candidate
bash scripts/supply-chain/image-smoke.sh pabawi:candidate ${{ matrix.profile }}
mkdir -p artifacts
docker run --rm -i --entrypoint node pabawi:candidate < scripts/supply-chain/dependency-graph.cjs > artifacts/dependencies.json
- name: Scan release candidate
continue-on-error: true
run: bash scripts/supply-chain/scan-image.sh pabawi:candidate artifacts
run: |
ignore_file=()
if [[ "${{ matrix.profile }}" = batteries ]]; then
ignore_file=(scripts/supply-chain/trivy-batteries-ignore.yaml)
fi
bash scripts/supply-chain/scan-image.sh pabawi:candidate artifacts "${ignore_file[@]}"
- name: Retain release evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: release-evidence-${{ matrix.arch }}
name: release-evidence-${{ matrix.profile }}-${{ matrix.arch }}
path: artifacts/*.json
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push the verified image without rebuilding
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push the verified image to both registries without rebuilding
id: push
run: |
tag="${REGISTRY}/${IMAGE_NAME}:${GITHUB_REF_NAME}-${{ matrix.arch }}"
docker tag pabawi:candidate "$tag"
docker push "$tag"
digest=$(docker inspect --format '{{index .RepoDigests 0}}' "$tag")
suffix=""
if [[ "${{ matrix.profile }}" = batteries ]]; then suffix="-batteries"; fi
architecture_tag="${GITHUB_REF_NAME}${suffix}-${{ matrix.arch }}"
ghcr_tag="${REGISTRY}/${IMAGE_NAME}:${architecture_tag}"
dockerhub_tag="${DOCKERHUB_IMAGE}:${architecture_tag}"
docker tag pabawi:candidate "$ghcr_tag"
docker tag pabawi:candidate "$dockerhub_tag"
docker push "$ghcr_tag"
docker push "$dockerhub_tag"
digest=$(docker inspect --format '{{index .RepoDigests 0}}' "$ghcr_tag")
echo "digest=${digest#*@}" >> "$GITHUB_OUTPUT"
- name: Attest the verified image
uses: actions/attest-build-provenance@v1
Expand All @@ -94,16 +111,25 @@ jobs:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Publish verified architecture images
run: |
version="${GITHUB_REF_NAME#v}"
image="${REGISTRY}/${IMAGE_NAME}"
tags=(--tag "$image:$version" --tag "$image:sha-${GITHUB_SHA:0:7}")
if [[ "$version" != *-* ]]; then
tags+=(--tag "$image:${version%.*}" --tag "$image:${version%%.*}" --tag "$image:latest")
fi
docker buildx imagetools create "${tags[@]}" \
"$image:${GITHUB_REF_NAME}-amd64" "$image:${GITHUB_REF_NAME}-arm64"
for image in "${REGISTRY}/${IMAGE_NAME}" "${DOCKERHUB_IMAGE}"; do
core_tags=(--tag "$image:$version" --tag "$image:sha-${GITHUB_SHA:0:7}")
batteries_tags=(--tag "$image:batteries-$version" --tag "$image:batteries-sha-${GITHUB_SHA:0:7}")
if [[ "$version" != *-* ]]; then
core_tags+=(--tag "$image:${version%.*}" --tag "$image:${version%%.*}" --tag "$image:latest")
batteries_tags+=(--tag "$image:batteries-${version%.*}" --tag "$image:batteries-${version%%.*}" --tag "$image:batteries")
fi
docker buildx imagetools create "${core_tags[@]}" \
"$image:${GITHUB_REF_NAME}-amd64" "$image:${GITHUB_REF_NAME}-arm64"
docker buildx imagetools create "${batteries_tags[@]}" \
"$image:${GITHUB_REF_NAME}-batteries-amd64" "$image:${GITHUB_REF_NAME}-batteries-arm64"
done

create-release:
needs: publish-manifest
Expand Down
40 changes: 39 additions & 1 deletion .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

## [1.6.0] - 2026-10-22

### Added

- `reset-password` CLI for account recovery: `npm run reset-password --workspace=backend -- --username admin`
(or `node dist/cli/reset-password.js` in built installs and containers). It
reads the password from a hidden prompt, stdin or `--generate`, applies the
password policy, clears lockouts, revokes all existing sessions and writes a
`cli_password_reset` audit entry. See
[troubleshooting](docs/troubleshooting.md#reset-a-local-users-password-account-recovery).

### Container images

- `example42/pabawi` and `ghcr.io/example42/pabawi` now publish a minimal core
image as the default tag family. The core image contains Pabawi but excludes
Bolt, Ansible, Puppet/OpenVox, OpenSSH and the other integration CLIs.
- A batteries image adds Bolt 5.6.0, Ansible 2.14.18, Puppet/OpenVox 8.29.0,
Facter 5.6.1, OpenSSH, Git, curl, rsync and sshpass. It is published under
`batteries`, `batteries-1`, `batteries-1.5`, `batteries-1.5.1` and immutable
commit tags.
- Release CI builds and smoke-tests both profiles for amd64 and arm64, then
publishes the verified images to Docker Hub and GHCR without rebuilding.
Compose, setup and convenience scripts select the batteries image where the
integration toolchain is expected.

### Security

- Core and batteries images retain SBOM and vulnerability evidence. Fixable
high or critical findings block publishing, except for a reviewed
batteries-only exception for `CVE-2026-85396` in `rubyzip` 2.4.1. OpenBolt's
current `winrm-fs` dependency prevents a compatible upgrade. The exception
is scoped to the exact gem path and package URL and expires on 2026-12-31.

## [1.5.0] - 2026-09-14

### Security: breaking for operators
Expand Down
Loading
Loading