Skip to content

Add Fortinet FortiGate active response integration#83

Open
lakecide wants to merge 3 commits into
wazuh:mainfrom
lakecide:fortigate-active-response
Open

Add Fortinet FortiGate active response integration#83
lakecide wants to merge 3 commits into
wazuh:mainfrom
lakecide:fortigate-active-response

Conversation

@lakecide

Copy link
Copy Markdown

Summary

Adds a Fortinet FortiGate active response integration for Wazuh.

The integration automatically blocks malicious source IPs on a FortiGate firewall when matching Wazuh rules trigger. The script dynamically creates firewall address objects and safely appends them to a preconfigured deny group through the FortiGate REST API.

Features

  • Dynamic IP blocking
  • Automatic unblock support using timeout/delete
  • Safe address-group append logic
  • Duplicate handling using Wazuh check_keys
  • Whitelist support
  • Debug logging
  • Manual test script included

Tested Environment

  • FortiOS 7.4.11
  • Wazuh Manager 4.14
  • Ubuntu 22.04

Notes

The integration intentionally uses the FortiGate address-group member append endpoint instead of PUT operations on the group object to avoid overwriting existing members.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new Wazuh Active Response integration that blocks/unblocks source IPv4 addresses on a Fortinet FortiGate firewall by creating a /32 address object and appending/removing it from a configured deny address group via the FortiGate REST API.

Changes:

  • Added fortigate-block.sh active response script implementing Wazuh check_keys handshake + FortiGate CMDB API calls.
  • Added an example configuration file and an integration-local .gitignore to prevent committing live secrets.
  • Added integration documentation and a manual test harness script.

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
integrations/fortinet_fortigate-active-response/active-response/fortigate-block.sh Implements FortiGate REST API block/unblock logic and Wazuh execd handshake.
integrations/fortinet_fortigate-active-response/active-response/fortigate-ar.conf.example Provides example configuration for FortiGate host/token/group/vdom and tuning settings.
integrations/fortinet_fortigate-active-response/tests/test-ar.sh Adds a manual test harness that feeds JSON to the AR script.
integrations/fortinet_fortigate-active-response/README.md Documents prerequisites, installation, configuration, testing, and security considerations.
integrations/fortinet_fortigate-active-response/.gitignore Prevents committing live config/token and other local artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

sudo chmod 750 /var/ossec/active-response/bin/fortigate-block.sh

# 2. Config
sudo cp integrations/fortinet_fortigate-active-response/active-response/fortigate-ar.conf \
Comment on lines +105 to +108
sudo cp integrations/fortinet_fortigate-active-response/active-response/fortigate-ar-whitelist.example \
/var/ossec/etc/lists/fortigate-ar-whitelist
sudo chown root:wazuh /var/ossec/etc/lists/fortigate-ar-whitelist
sudo chmod 640 /var/ossec/etc/lists/fortigate-ar-whitelist
```

All available options are documented in
[`active-response/fortigate-ar.conf`](active-response/fortigate-ar.conf).
</active-response>
```

`<location>server</location>` is required — the script runs on the Manager because it needs to reach the FortiGate API. See [`active-response/ossec-fortigate-ar.conf`](active-response/ossec-fortigate-ar.conf) for more trigger examples.
Comment on lines +165 to +170
# Dry-run — prints the JSON without calling the API
sudo bash integrations/fortinet_fortigate/tests/test-ar.sh block 198.51.100.99 dry

# Live block test
sudo bash integrations/fortinet_fortigate/tests/test-ar.sh block 198.51.100.99

Comment on lines +188 to +194
integrations/
└── fortinet_fortigate/
├── active-response/
│ ├── fortigate-block.sh - AR script - /var/ossec/active-response/bin/
│ ├── fortigate-ar.conf - config template - /var/ossec/etc/
│ ├── fortigate-ar-whitelist - whitelist template
│ └── ossec-fortigate-ar.conf - ossec.conf snippets
Comment on lines +92 to +94
printf '%s\n%s\n' "${ALERT_JSON}" "${CONTINUE_MSG}" \
| bash "${SCRIPT_PATH}" > /dev/null
EXIT_CODE=$?
Comment on lines +229 to +233
VDOM_PARAM="vdom=${FGT_VDOM}"

fgt_api() {
local method="$1" endpoint="$2" data="${3:-}"
local url="${BASE_URL}/${endpoint}?${VDOM_PARAM}"
Comment on lines +235 to +242
local cmd=(
curl --silent --max-time "${FGT_CURL_TIMEOUT}"
${CURL_SSL_FLAG}
-w "\n__STATUS__%{http_code}"
-X "${method}"
-H "Authorization: Bearer ${FGT_API_TOKEN}"
-H "Content-Type: application/json"
)
@MiguelCasaresRobles

Copy link
Copy Markdown
Member

@lakecide could you update based on copilot suggestions?

Regards,

lakecide added 2 commits June 8, 2026 17:42
- README: use fortigate-ar.conf.example in install step
- README: replace whitelist cp with install command
- README: fix test script paths to fortinet_fortigate-active-response
- README: fix structure block dir name
- test-ar.sh: wrap pipeline in if/else to capture exit code under set -e
- fortigate-block.sh: add validate_name_field() for URL-path config values
- fortigate-block.sh: add --show-error to curl for better failure diagnostics
- active-response: add ossec-fortigate-ar.conf with ossec.conf trigger examples
…ME references

- Rename ossec-fortigate-ar.conf - fortigate-ar.conf for consistency
- Update all README references accordingly
- modified .gitignore to only exclude live config, not .example file
@lakecide

lakecide commented Jun 9, 2026

Copy link
Copy Markdown
Author

@MiguelCasaresRobles All Copilot review comments have been addressed.

Thank you

@MiguelCasaresRobles

Copy link
Copy Markdown
Member

Hi @lakecide — thanks for this, it's well-structured and clearly security-aware (allowlist-validated URL params, IP strictly validated before use, no committed secrets, TLS-off documented as a warning). One blocker before it can merge:

Auto-unblock is broken (critical). In active-response/fortigate-block.sh, the check_keys/continue-abort handshake (~lines 683-702) runs unconditionally before the add/delete case switch. But Wazuh's execd only does that handshake on the initial add invocation (it binds stdin+stdout and reads the reply). The deferred delete invocation from the AR timeout is launched with stdin only — execd writes the alert and closes without ever reading/replying. So on every unblock, the script's read -r -t 30 EXECD_RESPONSE gets nothing, times out after 30s, hits the error/exit path, and never reaches group_remove_member/delete_addr_object. Net effect: blocked IPs are never cleaned up on the FortiGate — the opposite of the PR's headline "automatic unblock" feature. The fix is to run the handshake only inside the add branch (mirroring the upstream C AR scripts, which call it only on ADD_COMMAND).

Non-blocking nits:

  • README "Repository structure" lists active-response/fortigate-ar-whitelist as shipped, but it isn't in the PR (it's created during install) — the diagram is misleading.
  • Naming collision: the checked-in XML snippet fortigate-ar.conf shares the name of the runtime config path populated from .conf.example; combined with the .gitignore comment it could confuse users. Consider renaming the snippet.
  • is_whitelisted() arithmetic on a leading-zero octet (e.g. 172.08.x.x) hits bash octal parsing; use 10#$second or strip zeros.
  • fgt_api() only treats HTTP ≥500 as an error; a 4xx with a non-JSON body would log a generic failure without surfacing the code.

No CI runs on this repo, so please also paste a manual add/delete test against a real FortiGate once the handshake fix is in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants