Repository navigation
feat(soc): author stacks/soc — Wazuh and Velociraptor for odin, ahead of the guest (#266, #267) - #434
Merged
Merged
Conversation
… of the guest (#266, #267) ADR-0030 decided where the security half of ADR-0007 runs and how: Wazuh and Velociraptor in one stack on a second guest, odin, at 10.0.30.60. This is that stack, authored and CI-validated before the guest exists, the way stacks/lab landed before alexander and stacks/sensitive before trinity. Neither issue closes on it: an agentless Wazuh has nothing to say, and the agents are the domain's (#414). What the ADR fixed and this honours: 2 GiB indexer heap; one shard, no replicas, a 30 s refresh and a 30-day ISM delete policy on wazuh-alerts-*, as two JSON files the runbook applies; vm.max_map_count as a sysctl.d step on the guest; alerts.json never tailed into Loki; indexer health into the lab's Prometheus through Alloy's first-party elasticsearch exporter, in stacks/soc/alloy/ and not beside the shared config; six rules with firing and quiet promtool cases where the series arrive, in stacks/lab; no Grafana OpenSearch datasource; every port published or not per the ADR's table; each tool keeps its own CA; agents by GPO. Nothing reaches 10.0.99.20. Things decided on the way, each stated where it lives: - Velociraptor's registry does publish versioned tags, so the container path the ADR made conditional stands; 0.77.2 is pinned by digest. - The Wazuh service names carry a dot (wazuh.indexer) because the certs generator refuses a bare hostname and filebeat verifies in full mode. - The indexer's user database is rendered from two bcrypt hashes in SOPS, in place before the first start, so the demo passwords never exist on the segment. render-config.sh writes it and authd.pass; the deploying user must be uid 1000, and render refuses otherwise. - init.vql is a copy of the image's, mounted over it, so the metrics listener binds every interface for alexander to scrape. - The manager runs without docker-init, because s6-overlay is its PID 1. - Memory limits are vendor minimums, stated as bounds. Follows the ADR's consequence list: a soc rule in .sops.yaml above the catch-all, secrets/soc.example.yaml, a Dependabot entry grouping wazuh/*, seed values for the four new guards, an architecture.md row marked not built (so network.md gets a note, not a row — check_docs.py requires it), and the lab's commented ports blocks now name the runbook step that uncomments them. Also fixes README.md's stale "not yet deployed" against stacks/lab and observability.md's stale "built but not yet deployed". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 9, 2026
…ored-266 # Conflicts: # README.md
…ored-266 # Conflicts: # README.md # scripts/render-config.sh # scripts/seed-validation-env.sh
…ored-266 # Conflicts: # README.md # scripts/render-config.sh # scripts/seed-validation-env.sh
This was referenced Sep 19, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #266, #267, #101. Does not close either issue — the stack cannot be deployed until #414 builds the domain and
odinexists, and an agentless Wazuh has nothing to say. This is the authoring half, the waystacks/lab(#264) landed beforealexander(#262) andstacks/sensitive(#415) beforetrinity.What is here
stacks/soc/, decided by ADR-0030 and honouring everything it fixed:odinat10.0.30.60, derived from wazuh-docker's single-node reference with every departure marked. 2 GiB heap. One shard, no replicas, 30 s refresh and a 30-day ISM delete policy onwazuh-alerts-*, as two JSON files the runbook applies — Wazuh ships no retention at all.vm.max_map_countis asysctl.dstep in the runbook, not compose. Ports published exactly per the ADR's table;514/udpand55000are not.prometheus.exporter.elasticsearch, instacks/soc/alloy/opensearch.alloy— deliberately not beside the shared config, which the estate's Alloy mounts whole.alerts.jsonis never tailed into Loki. No Grafana OpenSearch datasource.stacks/lab/prometheus/, where the series arrive: indexer unreachable, red, yellow (which, with no replicas, is the alert that says the settings stopped applying), heap pressure, shard budget, and ingestion stalled — the Screenshot capture, and three faults it uncovered #62/ContainerHighMemory cannot fire — no service sets a memory limit #63 shape the crossing exists for.wazuh-certs-generatorbehind acertsprofile (somake upnever runs it, and the image is pinned wherecheck_image_pins.pycan see it). Velociraptor's generated config, which holds its CA key, is gitignored undervelociraptor/etc/.socrule in.sops.yamlabove the catch-all,secrets/soc.example.yaml(seven keys), a Dependabot entry groupingwazuh/*, seed values for the four new compose guards, anarchitecture.mdrow marked not built, andbuild-the-soc-guest.mdin the shape ofbuild-the-lab-guest.md.Decided on the way, stated where it lives
0.77.2is pinned by tag and digest and the native binary is not needed.wazuh.indexer): the certs generator refuses a bare hostname as an invalid DNS name, and filebeat verifies the indexer's certificate infullmode, so SAN and service name must match. Measured, not assumed —wazuh/certs.ymlsays so.render-config.shwritesinternal_users.ymlandauthd.passinto.rendered/the way it writessnmp.yaml, and because the file is in place before the first start, the demo passwords never exist on the segment. The indexer image runs as uid 1000 and mounts that 0600 file, so render refuses any other uid; the runbook says the first user Ubuntu creates is the one to deploy from.init.vqlis a copy of the image's, mounted over it, so Velociraptor's metrics listener binds every interface foralexanderto scrape. The file's header carries thediffto run after each Dependabot bump.init: true.stacks/lab's refusal to guess, for the reasonstacks/sensitivegave.One deviation from the task as written
The brief asked for a row for
odininnetwork.md's ImaginationLAN table.check_docs.pyforbids that: a host marked Not built yet inarchitecture.mdmust be absent fromnetwork.md's table, and the marker is what makes the row self-clearing on build day. Soodingets a planned-guest note in that section, the wayzionand the six domain machines do, and the runbook's §12 adds the row when the guest exists.The lab's ports stay commented
ADR-0030 says the lab's Prometheus and Loki "become published for the first time" because
odin's Alloy pushes. The blocks instacks/lab/compose.yamlare still commented here: a receiver published with no client yet is exactly the ADR-0012 case, on the segment where it matters most. The comments now name the runbook step (§7) that uncomments them on the day, in the same change that bringsodinup.Verified
make validatepasses with the three skips every non-deployment checkout records. That includesdocker compose configon the seeded stack,alloy fmt --teston the exporter file,check_compose_health.py --probeexec'ing each healthcheck binary inside the four pinned images (/bin/shin the three Wazuh images, busyboxwgetin Velociraptor's), promtool on all thirteen lab rules and both test files,check_sops_rules.pyresolvingsecrets/soc.sops.yamlto its own rule, andcheck_docs.pyon the new rows. The certs generator was run once into scratch to confirm its output names, modes and SANs, and the upstream image init scripts were read to confirm what each environment variable does.Not verified, and stated in the README: that it runs. Nothing here has been deployed; every check is static.
Also fixes two stale sentences found on the way:
README.md's "not yet deployed" againststacks/labandobservability.md's "built but not yet deployed".🤖 Generated with Claude Code