How I Built a Home SOC Using Wazuh, MISP, Cortex & Shuffle – Open Source
These days, security becomes more critical by the hour. While there’s no shortage of security products out there, many come with a hefty price tag.
At the same time, open source is becoming increasingly important—especially when it comes to digital sovereignty. So, I decided to put this to the test and build a simple SOC/SIEM stack at home using only open-source tools.
Here’s what the stack consists of:
For this setup, I wanted to analyze outbound traffic from my Fortigate firewall. The data flow looks like this:
Here’s a quick diagram of the setup:

Curious how I put it all together? Let’s dive in.
In MISP, I enabled several feeds including CIRCL, Botvrij, and Abuse.ch. These feeds pull in events with IOCs:

To keep things performant, I decided to periodically import IOCs from MISP events into a Wazuh CDB list. I wrote a small Python script to extract IPs from MISP and write them to /var/ossec/etc/lists.
#!/usr/bin/python3from pymisp import ExpandedPyMISPimport reimport osMISP_URL = 'https://misp'MISP_KEY = 'xxxx'VERIFY_CERT = FalseOUTPUT_FILE = '/var/ossec/etc/lists/misp_ips'print("Starting MISP IP import...")misp = ExpandedPyMISP(MISP_URL, MISP_KEY, VERIFY_CERT)result = misp.search(controller='attributes', type_attribute='ip-dst')print( f"Found {len(result['Attribute'])} attributes in MISP, writing to {OUTPUT_FILE}...")with open(OUTPUT_FILE, 'w') as f: pattern = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$" f.seek(0) f.truncate() for attr in result['Attribute']: if re.match(pattern, attr['value']): value = attr['value'] f.write(f'{value}:misp\n')print("Restarting Wazuh manager...")os.system("systemctl restart wazuh-manager")print("MISP IP import completed successfully.")Once the list is updated, restarting the Wazuh manager triggers the creation of a new lookup database.

To get syslog data from Fortigate into Wazuh, I added the following configuration in the Wazuh manager:

<remote> <connection>syslog</connection> <port>514</port> <protocol>udp</protocol> <allowed-ips>10.59.0.0/16</allowed-ips></remote>Then, I configured my Fortigate to send logs to the Wazuh manager:

Wazuh comes with built-in decoders for Fortigate logs, which worked well out of the box. For traffic events, the rule with ID 81618 was triggered.
I added a custom rule to match outbound traffic where the destination IP appears in the CDB list. If there’s a hit, Wazuh triggers a new alert with ID 100010:

<rule id="100010" level="12"> <if_sid>81618</if_sid> <list field="dstip" lookup="address_match_key">etc/lists/misp_ips</list> <description>Outgoing traffic seen on Fortigate to known bad IP</description></rule>
In Cortex, I enabled the AbuseIPDB analyzer. Of course, Cortex is most useful when combining multiple analyzers, but for this proof-of-concept, one is enough to demonstrate the workflow.

I created a Shuffle workflow triggered by a webhook. The steps look like this:

To trigger the Shuffle workflow when a Wazuh rule hits, I added the following integration config to Wazuh:

<integration> <name>shuffle</name> <hook_url>https://shuffle.internal.remcokersten.nl/api/v1/hooks/webhook_9195d1d8-43d5-4090-b080-4c3f57a01242</hook_url> <rule_id>100010</rule_id> <alert_format>json</alert_format></integration>
With just a few open-source tools, it’s entirely possible to build a functional SOC/SIEM environment at home. While this setup is still a work in progress, it already provides valuable insights and hands-on experience in threat detection and response.
This kind of stack is ideal for learning, experimenting, and developing a deeper understanding of security operations—all without breaking the bank.