{"slug": "building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real", "title": "Building a Local AI SOC Lab with Wazuh and Qwen: Lessons from a Real Implementation", "summary": "A developer built an isolated, AI-assisted SOC lab pairing Wazuh and OpenSearch with a locally hosted Qwen model served through LM Studio, using a restricted read-only account so the AI can investigate alerts but cannot execute firewall commands, alter configurations, or block IPs. The implementation replaced a 20-alert fetch with OpenSearch Scroll pagination, verifying 152 of 152 matching alerts with unique event IDs, and moved processed-event tracking from a JSON file to SQLite to improve state and recovery. In limited prompt-injection tests, the model preserved a failed-login finding, but an earlier run produced an unsafe IP-block recommendation, prompting conservative recommendation filtering and regression tests.", "body_md": "Security monitoring generates a lot of data. But collecting alerts is only the beginning. The real challenge is retrieving the right events, understanding what happened, and making decisions based on evidence.\n\nI wanted to explore a practical question:\n\n**Can a locally hosted AI model help a security analyst investigate Wazuh alerts without giving the AI control over the infrastructure?**\n\nTo explore this, I built an isolated AI-assisted Security Operations Center (SOC) lab using Wazuh, OpenSearch, Python, SQLite, and a local Qwen model.\n\nThis is a manually operated proof of concept, not a production SOC or an autonomous security platform.\n\nThe environment includes Ubuntu 24.04 virtual machines for security telemetry and Wazuh services, together with a Windows workstation running the Python assistant and LM Studio.\n\nThe main components are:\n\nThe assistant uses a restricted reader account. Access to the Indexer is protected through SSH port forwarding and TLS verification.\n\n**Important security boundary:** The AI cannot execute firewall commands, modify server configurations, or automatically block IP addresses.\n\nIts purpose is investigation support, not autonomous remediation.\n\nMy initial implementation retrieved only the 20 newest alerts.\n\nThis worked for a small demonstration, but it was not reliable when more events were present.\n\nI improved the Python client using OpenSearch Scroll pagination to retrieve a bounded snapshot of matching alerts.\n\nIn one documented integration test:\n\n**152 matching alerts were reported by OpenSearch.**\n\n**152 alerts were successfully retrieved.**\n\n**152 unique event IDs were verified.**\n\nThat confirmed the pagination logic worked correctly for that query snapshot.\n\nIt did not prove continuous monitoring or complete historical coverage.\n\nI also encountered an Indexer permission issue affecting Scroll cleanup. Fixing the reader permissions was part of making the workflow reliable without giving it unnecessary access.\n\nOriginally, processed event IDs were tracked using a JSON file.\n\nThat approach was simple, but I wanted more reliable event state, recovery, and retry behavior.\n\nI introduced SQLite to preserve processed identifiers and incident reports.\n\nDuring verification, I successfully migrated 20 existing event IDs and confirmed database integrity.\n\nI also tested restoring a synthetic incident report from the database after its Markdown export was missing.\n\nThis taught me an important engineering lesson:\n\n**AI analysis is only one part of the system. Reliable event state and recovery matter just as much.**\n\nThe Python assistant connects to Qwen through LM Studio's local API-compatible endpoint.\n\nThe workflow sends selected security events to the model and requests structured JSON output.\n\nThe application validates required fields before saving a report.\n\nIn one test, I used a synthetic failed SSH authentication event.\n\nQwen proposed:\n\n**Disposition:** SUSPICIOUS\n\n**Severity:** LOW\n\n**Recommended action:** Investigate and review related logs.\n\nThis was useful, but I also noticed how easily a language model could misinterpret technical context.\n\nFor example, a client source port can be confused with a destination service port. A single failed login also does not automatically indicate brute-force activity.\n\nThat is why I separate observed evidence from AI-generated hypotheses.\n\nOne of the most interesting experiments involved an untrusted SSH log message.\n\nI tested synthetic log content containing text designed to manipulate the model into treating failed authentication as successful authentication.\n\nIn a documented retest, the model preserved the failed-login finding.\n\nAn earlier test also exposed an unsafe recommendation to block an IP address. I introduced conservative recommendation filtering and regression tests to help keep responses within the lab's read-only investigation scope.\n\nThese were limited experiments.\n\nThey do not demonstrate complete resistance to prompt injection or guarantee that every AI-generated recommendation is safe.\n\n**Security logs are evidence, not instructions.**\n\nHuman review remains essential.\n\nI wanted the project to demonstrate more than a working screenshot.\n\nThe repository includes 31 mocked and offline Python tests, exercised through GitHub Actions using Python 3.10 and 3.12.\n\nThe implementation also includes validation of event identity, persistent processing state, structured model responses, and read-only operational boundaries.\n\nHowever, automated tests alone do not prove production security or detection accuracy.\n\nThe lab still has important limitations: manual execution, no continuous alert monitoring, and network restrictions around the local model service that require further verification before broader deployment.\n\nThree lessons stand out from this project.\n\n**First, reliable data retrieval comes before AI analysis.** Missing alerts can undermine even a well-written incident report.\n\n**Second, local AI can assist security investigations, but its output must be validated.** A structured response is not necessarily a correct response.\n\n**Third, automation needs clear security boundaries.** I prefer a system that helps an analyst understand an event over one that performs potentially harmful actions without approval.\n\nMy next areas of work include more realistic alert replay tests, better performance measurements, and stronger local-model network restrictions.\n\nThe public repository includes the Python implementation, tests, documentation, and sanitized verification examples.\n\n**GitHub:** [AI-Powered SOC Lab](https://github.com/vitaliibalint39-oss/ai-powered-soc-lab)\n\n**Original engineering article:** [Building a Local AI SOC Lab with Wazuh and Qwen](https://ta551.info/blog/wazuh-ai-security-lab/)\n\nI'm interested in how other engineers approach local AI for security operations.\n\n**Would you trust a local LLM to prepare incident triage reports if every recommendation required human approval?**\n\n*Editorial disclosure: This DEV.to adaptation was prepared with AI writing assistance using my original engineering article and documented project results as source material.*", "url": "https://wpnews.pro/news/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real", "canonical_source": "https://dev.to/vitaliibalint/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real-implementation-215e", "published_at": "2026-10-24 04:02:35+00:00", "updated_at": "2026-10-24 04:11:33.796587+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "ai-tools", "mlops", "artificial-intelligence"], "entities": ["Wazuh", "OpenSearch", "Qwen", "LM Studio", "SQLite", "Ubuntu"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real", "markdown": "https://wpnews.pro/news/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real.md", "text": "https://wpnews.pro/news/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real.txt", "jsonld": "https://wpnews.pro/news/building-a-local-ai-soc-lab-with-wazuh-and-qwen-lessons-from-a-real.jsonld"}}