Every time a rule was updated, the process was: SSH into EC2, open nano, edit the XML, reload Wazuh. No version history. No rollback capability. No way to share cleanly with a team. The fix: treat rules like code. Store them in Git. Deploy automatically when changes are pushed.
Repository structure
wazuh-detection-rules/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── rules/
│ ├── windows_authentication.xml
│ ├── windows_privilege.xml
│ ├── windows_attack_chain.xml
│ └── sysmon_detections.xml
└── README.md
One file per logical category. Each file is a complete XML group with all related rules inside it.
The GitHub Actions workflow
name: Deploy Wazuh Rules
on:
push:
branches:
- main
paths:
- 'rules/**.xml'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Copy rules to Wazuh manager
uses: appleboy/scp-action@v0.1.4
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
source: "rules/*.xml"
target: "/tmp/wazuh-rules/"
strip_components: 1
- name: Deploy and reload
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
sudo cp /tmp/wazuh-rules/*.xml /var/ossec/etc/rules/
sudo chown root:wazuh /var/ossec/etc/rules/*.xml
sudo chmod 660 /var/ossec/etc/rules/*.xml
sudo systemctl reload wazuh-manager
GitHub secrets
Three secrets stored in GitHub — never in code:
| Secret | Value |
|---|---|
EC2_HOST | EC2 public IP — update this when EC2 restarts |
EC2_USER | ubuntu |
EC2_SSH_KEY | Contents of the .pem key file |
The local_rules.xml placeholder problem
After migrating all rules to separate files, local_rules.xml was left empty. Wazuh requires at least one valid rule in every file it reads. An empty group fails validation.
<group name="local,">
<rule id="199999" level="0">
<match>placeholder</match>
<description>Placeholder — all active rules are in separate files</description>
</rule>
</group>
Verification after deployment
sudo grep -r "rule id" /var/ossec/etc/rules/ | grep "100"
This shows all custom rules (100000+) and which file they loaded from.
The result
Edit a rule in VS Code → commit → push → GitHub Actions deploys in under 60 seconds → Wazuh reloads → new rules are live. No SSH required. Full version history. The same workflow mature SOC teams use to manage detection rules at scale.