Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Community Developed/OVH API
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Workflow name="Graylog_OVH" version="1.0" xmlns="http://qradar.ibm.com/UniversalCloudRESTAPI/Workflow/V1">
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.

Update this to V2_1 so you can omit the source tag in postEvents, V2_1 is the latest schema.

Suggested change
<Workflow name="Graylog_OVH" version="1.0" xmlns="http://qradar.ibm.com/UniversalCloudRESTAPI/Workflow/V1">
<Workflow name="Graylog_OVH" version="1.0" xmlns="http://qradar.ibm.com/UniversalCloudRESTAPI/Workflow/V2_1">


<Parameters>
<Parameter name="serverurl" label="Server URL" required="true" />
<Parameter name="username" label="Username" required="true" />
<Parameter name="password" label="Password" required="true" secret="true" />
<Parameter name="streamId" label="Stream ID" required="false" />
</Parameters>

<Actions>
<!-- Authenticate Using Basic Authentication -->
<CallEndpoint url="${/serverurl}/api/streams" method="GET" savePath="/get_streams">
<BasicAuthentication username="${/username}" password="${/password}" />
</CallEndpoint>

<!-- Handle Errors -->
<If condition="/get_streams/status_code != 200">
<Log type="error" message="Auth Error: ${/get_streams/status_code} - ${/get_streams/body}" />
<Abort reason="Error: ${/get_streams/status_code} - ${/get_streams/body}" />
</If>

<!-- Extract Stream ID -->
<Set path="/streamId" value="${/get_streams/body/streams[0]/id}" />
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.

You're accessing streams[0] here, do you possible need to iterate over multiple in the array?


<!-- Get Messages - View Search -->
<CallEndpoint url="${/serverurl}/api/views/search/messages" method="POST" savePath="/search_messages">
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.

Does this endpoint require authentication? You're using BasicAuthentication in the streams call, but none here.

<RequestHeader name="X-Requested-By" value="PostMan" />
</CallEndpoint>

<!-- Handle Errors -->
<If condition="/search_messages/status_code != 200">
<Log type="error" message="Search Error: ${/search_messages/status_code} - ${/search_messages/body}" />
<Abort reason="Error: ${/search_messages/status_code} - ${/search_messages/body}" />
</If>

<!-- Get Universal Search Messages -->
<CallEndpoint url="${/serverurl}/api/search/universal/absolute?query=*&amp;from=2025-01-23T15:34:49.000Z&amp;to=2025-01-22T15:34:49.000Z&amp;decorate=true&amp;filter=streams:${/streamId}" method="GET" savePath="/search_universal">
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.

Same authorization question here, does this endpoint need auth?

Also, is a blank query= a required parameter in this API? & separates URL parameters so query here is empty.

I see you have hardcoded from/to dates here, I assume that's just for testing functionality so you'll eventually need to those to be dynamic so on each run you can save a new time window. It's usually best to take the timestamp of the last event you received as the next "from" value as relying on fixed windows is susceptible to time sync issues (always asking for a 1 minute window for example). It's best to ask for the timestamp of the last event (+1 millisecond possibly) to "now".

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.

Also, if you will be iterating over streams, you'll need to persist a unique from timestamp for each stream to ensure you don't duplicate or miss data.

<RequestHeader name="X-Requested-By" value="PostMan" />
<RequestHeader name="Accept" value="application/json" />
</CallEndpoint>

<!-- Handle Errors -->
<If condition="/search_universal/status_code != 200">
<Log type="error" message="Universal Search Error: ${/search_universal/status_code} - ${/search_universal/body}" />
<Abort reason="Error: ${/search_universal/status_code} - ${/search_universal/body}" />
</If>

<!-- Post the Logs -->
<PostEvents path="/search_universal/body" source="${/serverurl}" />
</Actions>

<Tests>
<DNSResolutionTest host="${/serverurl}" />
<TCPConnectionTest host="${/serverurl}" />
<HTTPConnectionThroughProxyTest url="${/serverurl}" />
</Tests>

</Workflow>