arm64 support: full dual-arch parity + native blue-team toolkit + desktop UX#11
arm64 support: full dual-arch parity + native blue-team toolkit + desktop UX#11cyberblu3s wants to merge 19 commits into
Conversation
…tials) This commit captures the in-progress work from the AWS build box as the baseline for the arm64-bringup branch. No ARM fixes yet - those follow in subsequent commits.
Upstream Arkime publishes both amd64 and arm64 .deb packages for Ubuntu 22.04 (verified against github.com/arkime/arkime/releases/tag/v5.7.0). BuildKit automatically provides TARGETARCH during `docker buildx build` and `docker compose up --build` when DOCKER_BUILDKIT=1 is set (which cyberblue_install.sh already exports at step 1.7), so no compose changes are required. Fixes ARM64 smoke-test blocker #1 from the ARM readiness audit.
FleetDM does not publish a linux/arm64 image or binary as of 2026-04 - neither
the `fleetdm/fleet:latest` Docker image nor the GitHub-release tarballs have
arm64 variants. Running on arm64 hosts therefore requires skipping the Fleet
stack entirely rather than failing or silently pulling amd64 under emulation.
Changes:
- docker-compose.yml: add `profiles: ["amd64"]` to fleet-redis, fleet-mysql,
and fleet-server so they only start when COMPOSE_PROFILES contains amd64.
- cyberblue_install.sh:
* Detect host arch via `dpkg --print-architecture` early in the script,
export as CYBERBLUE_ARCH, persist to .env, and set COMPOSE_PROFILES=amd64
only on amd64 hosts.
* Gate Step 2.12 (`fleet prepare db`) and Step 2.12a (enrollment secret)
behind `if [ "$CYBERBLUE_ARCH" = "amd64" ]`, with a clear skip message
on arm64.
Fixes ARM64 smoke-test blocker #2 from the ARM readiness audit.
The three agent-download scripts previously fetched only amd64 Linux binaries, so endpoints running on arm64 (Apple Silicon Docker hosts, Raspberry Pi, Graviton, etc.) could not be enrolled from the CyberBlueSOC portal. Upstream publishes arm64 variants for all three, verified live: - velociraptor-v0.75.2-linux-arm64 (GitHub release v0.75) - wazuh-agent_4.12.0-1_arm64.deb + .aarch64.rpm (packages.wazuh.com/4.x) - osquery_5.13.1-1.linux_arm64.deb + linux.aarch64.rpm (GitHub release 5.13.1) The default amd64 Linux filenames (velociraptor-linux, wazuh-agent-ubuntu.deb, osquery-ubuntu.deb) are kept unchanged so the existing portal code at portal/app.py lines 1983, 2034, 2148 continues to work. The arm64 variants are staged alongside under clearly-named files (-arm64.deb / -linux-arm64) and will be surfaced by a follow-up portal change. On download failure for an arm64 asset the script now warns and continues (rather than hard-exit), so the CyberBlueSOC install itself doesn't fail just because upstream temporarily moved an asset. Fixes ARM64 smoke-test blocker #3 from the ARM readiness audit.
Wazuh Docker Hub images (wazuh-manager, wazuh-indexer, wazuh-dashboard) are amd64-only as of 2026-04, but packages.wazuh.com publishes aarch64 RPMs for all three server components and the vendored Dockerfiles under wazuh/build-docker-images/ are architecture-agnostic except for one hardcoded s6-overlay asset name in the manager Dockerfile. Changes: - wazuh/build-docker-images/wazuh-manager/Dockerfile: add ARG TARGETARCH and map Docker's (amd64, arm64) naming onto s6-overlay's (amd64, aarch64) naming. Wazuh packages resolve through the yum repo at runtime so the amazonlinux:2023 base picks the arch matching the --platform flag. - wazuh/build-arm64-images.sh: new helper that uses `docker buildx` with --platform linux/arm64 to rebuild all three images under their canonical tags (e.g. wazuh/wazuh-manager:4.12.0) so docker-compose.yml references unchanged. On amd64 hosts the script is a no-op. - cyberblue_install.sh: new Step 2.9a invokes the builder on arm64 hosts before Step 2.10 (`docker compose up --build -d`). On amd64 it prints a one-liner explaining the skip and moves on. Fixes ARM64 smoke-test blocker #4 from the ARM readiness audit, completing the Phase 1 set of four ARM fixes.
On a fresh install, Step 1.2 adds the ubuntu user to the docker group,
but the new group membership isn't active in the installer's own shell
until re-login. That caused Step 2.9a's bare 'bash wazuh/build-arm64-images.sh'
invocation to fail with 'permission denied /var/run/docker.sock' on arm64
hosts - but the old '| while read line' pipe masked the exit code, so the
installer printed '✅ Wazuh arm64 images built' despite the build failing,
leading to wazuh-manager/indexer/dashboard failing to start later because
the local arm64 images never existed.
Fix:
- invoke via sudo so docker-group membership isn't required
- route through run_with_output which uses ${PIPESTATUS[0]} to expose the
real exit code
- on failure print a RED error (not YELLOW warn) and point at the manual
rerun command, because this failure WILL break three wazuh services
Caught during the first arm64 smoke test on UTM (192.168.64.2).
Made-with: Cursor
… exit code
Three issues caught during the first arm64 smoke run on UTM (aarch64/Apple VZ):
1) portal/Dockerfile - add gcc + python3-dev + libffi-dev
psutil==5.9.5 ships no matching prebuilt wheel for linux/arm64 on the
python:3.9-slim base, so pip falls back to a source build. Without a C
toolchain, that fails with 'command gcc: No such file or directory',
which in turn aborts `docker compose up --build` for the whole stack
(portal is the first build-from-source service compose reaches).
Adding the build tools is a no-op on amd64 (wheel is used) and unblocks
arm64.
2) wazuh/build-arm64-images.sh - pass dotted WAZUH_VERSION
The vendored Wazuh Dockerfiles install the RPM via
`yum install wazuh-indexer-${WAZUH_VERSION}-${WAZUH_TAG_REVISION}`.
packages.wazuh.com publishes 'wazuh-indexer-4.12.0-1', but an earlier
revision of this script stripped the dots (WAZUH_VERSION_NUM=4120),
producing a package spec yum couldn't resolve:
'Error: Unable to find a match: wazuh-indexer-4120-1'.
Fix: pass WAZUH_IMAGE_VERSION untouched.
3) cyberblue_install.sh - preserve compose up exit code
The previous `sudo docker compose up --build -d | while read` pipe
exposed only the `while` loop's exit code, so a compose build failure
(e.g. fix #1 above) was reported as '✅ deployed' and the installer
marched on to Step 2.11 with zero containers running. Route through
run_with_output, which uses ${PIPESTATUS[0]} to expose the real code,
and print a RED error + diagnostic hints on failure.
Made-with: Cursor
Caldera's upstream Dockerfile hardcodes: curl -L https://go.dev/dl/go1.25.0.linux-amd64.tar.gz On arm64 hosts, this pulls an amd64 Go binary which then fails `go version` with exit 126 (cannot execute binary file), aborting the compose-up build target for caldera and cascading to every service that depends on a successful full compose-up pass. Fix: after Step 2.6 clones caldera (which happens at install time, not at repo-clone time, so we can't vendor the patch), rewrite the Dockerfile in-place to use linux-${TARGETARCH}. docker buildx sets TARGETARCH automatically per --platform, so: amd64 hosts → go1.25.0.linux-amd64.tar.gz arm64 hosts → go1.25.0.linux-arm64.tar.gz The sed is idempotent (guarded by grep -q of the upstream literal URL) and also injects the ARG TARGETARCH line after the runtime FROM so the shell-level ${TARGETARCH} expands correctly. Verified during arm64 smoke run #2 on UTM (aarch64): caldera was the only compose build failure after the portal + wazuh + arkime fixes - every other image either has a multi-arch manifest upstream or gets built locally by our helpers. Made-with: Cursor
Previous attempt used 'ARG TARGETARCH=amd64' + ${TARGETARCH} substitution,
expecting BuildKit to override the default when the host was arm64.
Turns out 'docker compose up --build' does not reliably populate the
BuildKit TARGETARCH built-in on every host - the outcome depends on
whether compose was invoked with DOCKER_BUILDKIT=1 and whether buildx
is the active builder for that daemon. On the arm64 smoke-test VM,
the ARG stayed at its default (amd64), so curl still downloaded the
amd64 tarball and 'go version' kept failing with exit 126.
Replace the ARG-based substitution with direct shell substitution
inside the RUN step:
RUN curl ... https://go.dev/dl/go1.25.0.linux-$(dpkg --print-architecture).tar.gz ...
This runs at container-build time on whatever the debian:bookworm-slim
base actually is (which matches the build platform), so it picks the
correct tarball without any buildx or ARG plumbing. Works identically
on amd64 and arm64 hosts.
Made-with: Cursor
Three runtime-phase arm64 bugs caught when 26/28 containers started successfully after pass #3 but 5 crashed on startup with exec-format errors: 1) wazuh-manager/Dockerfile - detect arch via rpm, not ARG TARGETARCH The previous ARG TARGETARCH=amd64 default was being baked in because 'docker compose up --build' on this host's Docker CE 29.4 does not populate BuildKit's TARGETARCH built-in (same root cause as the earlier caldera go-tarball bug). Result: amd64 s6-overlay tarball installed into arm64 image layer → 'exec /init: exec format error' on startup. Fix: derive S6_ARCH from rpm --eval '%{_arch}' at RUN time. 2) velociraptor/Dockerfile - grep correct arch from release JSON The RUN step hardcoded 'grep "linux-amd64"' when selecting the Velociraptor binary from the GitHub release API. Image layer was arm64, but the embedded binary was x86_64 → 'cannot execute binary file: Exec format error' on every container start. Fix: use dpkg --print-architecture at RUN time to match 'linux-amd64' on amd64 builds and 'linux-arm64' on arm64 builds. 'file' verifies. 3) docker-compose.yml - swap stamus/suricata for jasonish/suricata stamus/suricata:latest publishes only linux/amd64 on Docker Hub. On arm64 hosts, compose silently pulls the amd64 image and the container crashes with 'exec /usr/bin/suricata: exec format error'. jasonish/suricata:latest is the de-facto community multi-arch image (same maintainer as evebox, which we already use) and ships both linux/amd64 and linux/arm64, with the same CLI and config layout - drop-in replacement, mounts and command: unchanged. Made-with: Cursor
Sourced by the wazuh-manager Dockerfile during image build - previously
hardcoded filebeat-oss-7.10.2-x86_64.rpm. On arm64 builds (amazonlinux
aarch64 base), this triggered an arch-mismatch failure silently buried
under a longer multi-line RUN, and the whole image build aborted after
wazuh-manager RPM install succeeded.
Fix: pick the arch at runtime via rpm --eval '%{_arch}' (x86_64 or aarch64)
and substitute into both the filename and the yum install target. Elastic
publishes both variants at the same URL pattern, so no version pin change.
Made-with: Cursor
Two more arm64 smoke-test issues resolved: 1. wazuh/wazuh-certs-generator:0.0.2 is amd64-only on Docker Hub. On arm64 hosts compose pulls it anyway and the container crashes with 'exec /entrypoint.sh: exec format error'. Docker then creates empty *directories* at every per-file bind-mount target (root-ca.pem, wazuh.indexer.pem, etc.), which persist even after later runs, causing wazuh-indexer to crash with 'root-ca.pem - is a directory' forever. Fix: build-arm64-images.sh now also builds the certs-generator locally from the vendored wazuh/indexer-certs-creator/ Dockerfile (ubuntu:focal, multi-arch base), tags it as wazuh/wazuh-certs-generator:0.0.2 so existing compose references resolve to the arm64 image transparently. 2. caldera:/usr/src/app/conf/local.yml was bind-mounted :ro. Caldera >= 5.x calls BaseWorld.apply_config(apply_hash=True) at startup which REWRITES that file with an integrity hash, so :ro causes an immediate 'OSError: Read-only file system' crash. Dropped :ro. Arch-agnostic bug (affects amd64 too), surfaced during arm64 smoke. Made-with: Cursor
Static arm64 audit of tools/native/install.sh, categorizing ~56 tools into green (50) / fix-url (3) / rebuild (1) / gap (2) buckets. Full inventory in tools/native/ARM64_AUDIT.md. URL fixes applied for the three fix-url tools via new variables in the top-level `case "$ARCH"` block (single source of truth, no more hardcoded amd64/x86_64/x64 in the body): - Chainsaw -> $CHAINSAW_ARCH (aarch64-unknown-linux-gnu on arm64) - Hayabusa -> $HAYABUSA_ARCH (lin-aarch64-gnu on arm64) - Stratus -> $STRATUS_ARCH (Linux_arm64 on arm64) Explicit arm64 short-circuits (with informative skip messages) for the three locked amd64-only exceptions: - bulk_extractor (prebuilt amd64-only; source build deferred) - sysmonforlinux (Microsoft apt arm64-HTTP-404; Falco+auditd mit.) - zui (Brim) (upstream publishes amd64 .deb only) Tested: bash -n passes; runtime verification happens in Phase 4 of the arm64 full-parity plan (sudo bash install.sh on the UTM VM). Made-with: Cursor
Static arm64 audit of the desktop UX layer found zero arm64 blockers
in either install-desktop.sh or iso/scripts/buildbox-bootstrap.sh
(full inventory in tools/native/desktop/ARM64_AUDIT.md). Both are
built from apt packages, static text assets, pure-Python helpers,
and POSIX shell — nothing arch-specific.
Independent fixes bundled with the audit:
1. Branded wallpaper committed to the repo
- Before: PNG only lived in workspace at iso/branding/, outside the
CyberBlue/ git tree. Standalone `git clone && sudo bash
install-desktop.sh` had no wallpaper to install.
- After: committed tools/native/desktop/branding/{cyberbluesoc-
wallpaper.png,cyberbluesoc-logo.png}. Added a new "1b. Branded
wallpaper" block to install-desktop.sh that copies the PNG to
/usr/share/backgrounds/cyberbluesoc/ and seeds xfce4-desktop.xml
ONLY if no desktop config exists yet — so the AWS/ISO path (where
buildbox-bootstrap.sh already writes the xml) stays untouched.
2. caldera-autostart.service path typo
- cyberblue_install.sh line 964: WorkingDirectory=/home/ubuntu/
CyberBlueSOCx -> /home/ubuntu/CyberBlue
- fix-docker-external-access.sh line 276: ExecStart=/home/ubuntu/
CyberBlueSOCx/... -> /home/ubuntu/CyberBlue/...
Previously the stray "x" suffix baked a broken path into systemd,
silently breaking Caldera auto-start on every reboot.
Tested: bash -n passes on all three shell scripts; runtime verification
in Phase 4 (full desktop-stack install on UTM arm64 VM).
Made-with: Cursor
Two fixes uncovered during the Phase 4 arm64 runtime smoke on the UTM
VM (2026-04-18, tools/native/ARM64_AUDIT.md).
1. Robust latest-release lookup (/releases?per_page=20 + jq filter).
Nuclei v3.8.0 was published with only nuclei_3.8.0_checksums.txt —
all per-arch binary zips were missing at the exact time our
installer hit it (upstream publish race / CI failure on amd64 and
arm64 alike). The old pattern of:
curl /releases/latest | jq -r .tag_name
happily returned v3.8.0 and the subsequent download 404'd with no
fallback, breaking step 2 of install.sh.
New helper _latest_release_with_asset(repo, asset-name-regex)
walks the 20 most recent non-draft / non-prerelease releases and
returns the newest one that ACTUALLY publishes an asset matching
our per-arch filename pattern. Applied to nuclei, chainsaw,
hayabusa, stratus, and zui — every GitHub release pull in the
script now survives a broken latest release on either arch.
2. Rust toolchain + libpcap-dev for NetExec's native wheels.
On a clean arm64 VM (Ubuntu Server 24.04), the NetExec
install failed with:
error: can't find Rust compiler
Failed building wheel for aardwolf
Failed building wheel for pcapy-ng
aardwolf (RDP lib) uses PyO3 bindings, pcapy-ng needs libpcap
headers. Both land via apt on both archs. Added:
apt-get install -y pipx rustc cargo \
libpcap-dev libssl-dev libffi-dev
before the `pipx install git+...NetExec` call. Works equally on
amd64 (idempotent if already installed).
Tested: on UTM arm64 VM with these fixes, `sudo bash install.sh`
completes end-to-end and 49/49 summary tools resolve via command -v
(100% — see tools/native/ARM64_AUDIT.md verification gate).
Made-with: Cursor
…ooting - Add cyberblue_init.sh as the post-prereqs umbrella that chains cyberblue_install.sh then tools/native/install.sh (which in turn auto-chains install-desktop.sh when a desktop session is present). Each downstream script stays independently runnable and idempotent. - cyberblue_install.sh: preventive fix for the /run/docker.sock permission-denied race seen on fresh Ubuntu Server 24.04 arm64 (UTM). After adding the user to the docker group we now force a docker.socket restart and re-apply root:docker 0660, so the socket no longer inherits a stray numeric uid from an earlier daemon start. - .env.example: drop the stale duplicate YETI_AUTH_SECRET_KEY and SURICATA_INT lines that had accumulated at EOF across installs, and add an inline comment explaining the installer's SURICATA_INT auto-detect behavior plus the common per-host interface names. - docs/TROUBLESHOOTING.md: document the docker.sock ownership race (diagnosis + safe recovery) and the stale caldera-autostart typo healing one-liner for hosts upgrading from pre-Phase-3 installs. Captured in ENHANCEMENTS.md #53 (Phase 4 hard-won lessons). Made-with: Cursor
- README: widen System Requirements to explicitly list amd64 and arm64 host environments (AWS Intel/AMD, AWS Graviton, UTM on Apple Silicon, VMware, VirtualBox, Raspberry Pi-class, bare metal). Add a "Full Platform" install section pointing at cyberblue_init.sh for operators who want the Docker stack + native blue-team toolkit + XFCE desktop UX in one command. Link the new ARM64_SUPPORT guide and the native toolkit README under Documentation. - docs/ARM64_SUPPORT.md (new): operator-facing dual-arch reference. Documents the supported host-environment matrix, per-service arm64 status across the Docker stack (with Wazuh local-rebuild, tenzir amd64-only, Fleet amd64-only), native toolkit bucket summary (green / fix-url / rebuild / gap), desktop UX layer, the four locked amd64-only exceptions (Fleet, tenzir, sysmonforlinux, zui), quick verification commands, and known arm64 build-time costs (Wazuh rebuild ~25min, NetExec compile ~3min). - QUICK_REFERENCE.md: mark FleetDM row as amd64-only so arm64 operators know upfront why port 7007 may be absent. Made-with: Cursor
grafana, portal healthcheck) All four issues were reported after Phase 4 sign-off; all are real bugs that existed pre-arm64, not arm64 regressions. 1. MITRE Navigator (both arches, broken): compose mapped "7013:4200" but the runtime stage is nginx:alpine serving the compiled Angular bundle on port 80 — nothing listens on 4200 in the prod image (4200 is the Angular dev-server port, only used by `npm run serve`). Result: connection refused on http://host:7013. Changed mapping to "7013:80" to match attack-navigator/Dockerfile EXPOSE 80. 2. EveBox (both arches, broken): evebox self-signs a cert on first boot and serves HTTPS on 5636, so http://host:7015 was rejected with "Received HTTP/0.9 when not allowed" and the portal tile silently failed to load. Added --no-tls to the evebox command to mirror the existing --no-auth pattern — this is a lab-convenience platform, every other core service is plain HTTP on its 70xx port, and the self-signed cert provides no real security while breaking every http:// link pointing at 7015. 3. Grafana (both arches, missing entirely): tools/grafana/docker- compose.yml carries `profiles: [standard]` and is aggregated via docker-compose.extras.yml, but cyberblue_install.sh only invoked `docker compose up --build -d` — no -f extras flag, no --profile, so grafana (and zeek, honeypots, crowdsec) never got a container. Added a new Step 2.10a that brings up the extras stack as a follow-on invocation, non-fatal if one image is arm64-missing so the core stack stays green. Also updated the post-Docker- restart re-bring-up in Step 2.11 so extras come back with core. 4. Portal (cosmetic: always "unhealthy"): portal/Dockerfile set HEALTHCHECK curl http://localhost:5500/health, but when ENABLE_HTTPS=true (our default) Flask only binds HTTPS 5443 and 5500 isn't served — so the healthcheck was failing on every probe for every install, marking the container unhealthy even though https://host:5443 was perfectly reachable. Several users (including in the arm64 smoke) saw the red badge and assumed the portal was broken. Updated the HEALTHCHECK to probe HTTPS first and fall back to HTTP, so it passes in either ENABLE_HTTPS mode. Also widened start-period from 5s to 15s to cover the Flask import time. Impact: fixes four pre-existing bugs that were hiding behind docker port mappings / "unhealthy" badges / missing-container confusion and visible on both amd64 (AWS reference) and arm64 (UTM) stacks. Made-with: Cursor
|
Follow-up fix pushed (3035927): four web apps that were broken on BOTH arches pre-existing.
Verified on the UTM arm64 VM: Will retest on the AWS amd64 reference next — but since none of these fixes are arch-specific, the amd64 reference should behave identically after |
|
Verified the same four fixes on the AWS amd64 reference box (18.118.5.223) — applied surgically (not via git, because the AWS working tree has AWS-specific runtime edits to
Container state on AWS after the fix: External reachability from a macOS client → AWS public IP confirms the fixes hold through the real network path (Portal 5443 and Grafana 3000 are open in the Security Group; 7013/7015 aren't in the public SG on that box, but that's orthogonal — they're reachable from the Portal and from the VNC browser inside the instance). Bottom line: these were four latent bugs on the main branch that the arm64 bring-up just happened to surface while probing the service matrix. Both architectures now green on the four endpoints. |
and opensearch-init to avoid the cyber-blue vs cyberblue_default split
Symptom: on the arm64 UTM VM, wazuh-dashboard showed
"Wazuh dashboard server is not ready yet" on port 7001. Logs showed
the dashboard trying to resolve wazuh.indexer and getting ENOTFOUND
in an infinite retry loop.
Root cause: the three wazuh services (wazuh.manager, wazuh.indexer,
wazuh.dashboard) and opensearch-init were the only four services in
docker-compose.yml without an explicit `networks:` key. They silently
fell back to the compose project's "default" network.
With only docker-compose.yml, that default is `cyberblue_default` (the
auto-created one). But docker-compose.extras.yml declares at the bottom:
networks:
default:
name: cyber-blue
external: true
which renames the default to the external `cyber-blue` network when
both files are loaded together. So whichever compose invocation last
force-recreated each container decided its network:
- Extras-aware invocations → `cyber-blue`
- Plain compose invocations → `cyberblue_default`
On the arm64 VM the extras-aware up-pass recreated wazuh-indexer and
wazuh-manager (they landed on cyber-blue), but a subsequent plain
invocation recreated wazuh-dashboard onto cyberblue_default — splitting
the stack across two non-routed networks and breaking the dashboard's
`opensearch.hosts: https://wazuh.indexer:9200` DNS lookup.
On AWS the sequence happened to land all three on `cyberblue_default`
consistently, so the bug was latent but not visible — still a real
bug because any operator running `cyberblue_init.sh` on a clean box
or running extras/core compose invocations out of order could trip it.
Fix: add `networks: [cyber-blue]` to all four previously-implicit
services so their network selection is deterministic regardless of
which compose files are passed. Matches every other service in the
file, 31 of which already declare it explicitly.
Audited docker-compose.yml after the fix — every service either has
`networks: [...]` or uses `network_mode: host` (suricata only).
Made-with: Cursor
|
Fifth web-app fix: Wazuh dashboard on port 7001. Symptom (arm64 UTM only, AWS was lucky)Dashboard stuck on "Wazuh dashboard server is not ready yet". Logs showed Root causeThe three wazuh services (
So whichever compose invocation last force-recreated each container decided its network. The bring-up sequence on the arm64 VM ended up with FixAdd Verified on both archesFull state of every endpoint touched in this PR thread (AWS)Commit |
Summary
Brings CyberBlueSOC to full dual-architecture parity. The same codebase now builds and runs on amd64 (x86_64 — AWS / VMware / VirtualBox / bare metal) and arm64 (AWS Graviton / Apple Silicon via UTM / Raspberry Pi-class hardware), validated end-to-end on Ubuntu Server 24.04 LTS aarch64 in a UTM VM on Apple Silicon.
Alongside arm64, this PR lands the native blue-team toolkit (~56 CLI + GUI tools installed on the host next to the container stack), the XFCE-based desktop UX layer (branded wallpaper, welcome dashboard, categorized app menu,
cyberblueCLI, Firefox policies, noVNC), and a newcyberblue_init.shumbrella that chains the whole thing into one command.What's in this PR
Docker container stack (arm64)
wazuh/build-arm64-images.sh) now buildswazuh-manager,wazuh-indexer,wazuh-dashboard, andwazuh-certs-generatorfrom the vendored sources, with arch-aware Filebeat RPM selection. Upstream Wazuh doesn't publish arm64 images; the installer calls the rebuild helper automatically on arm64 first install (~25 min one-time cost).conf/local.ymlbind-mount switched from:roto writable (Caldera ≥5 rewrites the file on boot — was broken on both arches).amd64Docker Compose profile (upstream Fleet images are amd64-only); arm64 installs skip Fleet and use Velociraptor for endpoint management.frikky/shuffle:tenzirintegration image is amd64-only, sotenzir-noderestarts on arm64 — documented as an integration-layer exception, not a core service.Native blue-team toolkit (
tools/native/install.sh)~56 CLI/GUI tools installed on the host. All tools classified in
tools/native/ARM64_AUDIT.md:caseblock ($CHAINSAW_ARCH,$HAYABUSA_ARCH,$STRATUS_ARCH)._latest_release_with_assethelper that pages through/releasesinstead of blindly trusting/releases/latest, mitigating the Nuclei-v3.8.0-style partial-publish failures.rustc,cargo,libpcap-dev,libssl-dev,libffi-devsoaardwolfandpcapy-ngcan compile from source (arm64 wheels don't exist upstream).Desktop UX layer (
tools/native/desktop/install-desktop.sh)tools/native/desktop/branding/) so non-ISO installs don't depend on the workspace-leveliso/branding/..desktopentries,cyberblueCLI, Firefox policies + bookmarks, noVNC/websockify/TigerVNC via apt,cbsoc-status.timer. Everything is arch-agnostic and validated on arm64.caldera-autostart.serviceanddocker-networking-fix.serviceunit templates (CyberBlueSOCx→CyberBlue).Infrastructure
cyberblue_init.sh(new) — post-prereqs umbrella: chainscyberblue_install.sh→tools/native/install.sh(which auto-chainsinstall-desktop.shwhen a desktop session is detected). Each stage stays independently runnable and idempotent.cyberblue_install.sh— preventive fix for the/run/docker.sockownership race observed on fresh Ubuntu Server 24.04 arm64 (the socket was coming up owned by1001:1001instead ofroot:docker). Forces adocker.socketrestart afterusermod -aG docker.cyberblue_install.sh— installer runs the Wazuh arm64 build undersudowith correctPIPESTATUSpropagation (previously the pipe masked non-zero exit codes and the installer reported success on failure)..env.example— cleaned up stale trailing duplicates (YETI_AUTH_SECRET_KEYx3,SURICATA_INTx2); the canonical lines live at the top with an inline comment explaining the installer's SURICATA_INT auto-detect.Documentation
README.md— widened System Requirements to explicitly list amd64 and arm64; added a "Full Platform" install section pointing atcyberblue_init.sh; linkeddocs/ARM64_SUPPORT.mdand the native toolkit README.docs/ARM64_SUPPORT.md(new) — operator-facing dual-arch reference: host-environment matrix, per-service arm64 status, native toolkit bucket summary, the four locked amd64-only exceptions (Fleet, tenzir, sysmonforlinux, zui), quick verification commands, known arm64 build-time costs.docs/TROUBLESHOOTING.md— new sections for the docker.sock ownership race and the stalecaldera-autostart.servicemigration one-liner.tools/native/ARM64_AUDIT.md(new) — per-tool arm64 audit, 56 entries across 11 sections.tools/native/desktop/ARM64_AUDIT.md(new) — audit of the desktop layer scripts.QUICK_REFERENCE.md— FleetDM row flagged as amd64-only.Bugs fixed along the way
19 distinct arm64 issues captured across two iterative smoke-test loops on the UTM VM; full forensic notes live in
ENHANCEMENTS.mdentries #50, #52, #53 in the workspace. Highlights:docker compose down -v. Hit this with Wazuh certs whenwazuh-certs-generator:0.0.2crashed withexec format erroron arm64. Documented in the multi-arch policy rule.docker compose up ... | while read line). Fixed via arun_with_outputhelper that captures${PIPESTATUS[0]}.ARG TARGETARCHin Dockerfiles fails silently when compose isn't driving buildx; switched to runtimedpkg --print-architecture/rpm --evaldetection.filebeat-*-x86_64.rpmvsfilebeat-*-aarch64.rpm);filebeat_module.shsourced inside the Wazuh Dockerfile now detects and swaps.linux-amd64; moved to runtime arch detection.Test plan
docker compose down -v, wipe phantom cert dirs, re-runcyberblue_install.shone-shot on the UTM arm64 VM. Acceptance: installer exit 0, 25+ containersUp, portal HTTP 200, wazuh-indexer HTTP 401/200.tools/native/install.sh→tools/native/ARM64_AUDIT.mdinventory of ~56 tools.tools/native/desktop/install-desktop.shandiso/scripts/buildbox-bootstrap.sh; wallpaper + logo assets committed to the repo;caldera-autostart+docker-networking-fixunit typos fixed.tools/native/install.sh(+ auto-chainedinstall-desktop.sh), debug arm64 issues until acceptance gate passes. Actual result on arm64: 27 containersUp(with expectedtenzir-noderestart), 49/49 native tools resolving and verified as ARM aarch64 ELFs, noVNC accessible, branded wallpaper applied (SHA256 matches repo), 76 app-menu entries, Firefox policies seeded,cbsoc-status.timeractive.cyberblue_init.shumbrella decision +docker.sockrace preventive fix + SURICATA_INT auto-detect validation. Confirmed auto-detect healsens5→enp0s1end-to-end on the UTM VM viaupdate-network-interface.sh.git clone+./cyberblue_init.shon a clean amd64 host to confirm no regressions against the existing amd64 reference build.cbsoc-arm64-goldsnapshot as the ISO baseline.Out of scope
Made with Cursor