Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@
/packages/proofpoint_tap @elastic/security-service-integrations
/packages/proxysg @elastic/sec-deployment-and-devices
/packages/pulse_connect_secure @elastic/security-service-integrations
/packages/qualys_gav @elastic/security-service-integrations
/packages/qualys_vmdr @elastic/security-service-integrations
/packages/qualys_was @elastic/security-service-integrations
/packages/qnap_nas @elastic/sec-deployment-and-devices
Expand Down
3 changes: 3 additions & 0 deletions packages/qualys_gav/_dev/build/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
ecs:
reference: [email protected]
58 changes: 58 additions & 0 deletions packages/qualys_gav/_dev/build/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Qualys Global AssetView (GAV)

## Overview

[Qualys GAV](https://docs.qualys.com/en/gav/latest/) helps you to accurately assess complex IT infrastructure and quickly identify and remediate risk. Using a combination of Qualys sensors — Cloud Agents, scanners and passive network sensors — GAV collects and analyzes data about assets across hybrid environments, and delivers up-to-date, comprehensive and continuous information about those assets as well as their security and compliance posture.

The Qualys GAV integration collect assets via REST API.

## Data streams

The Qualys GAV integration collects logs of the following type:

1. **Asset:** This data stream will collect details of all assets.

>**Note**: For the **Asset** Dashboard, ensure that the time range is aligned with the configured interval parameter to display accurate and consistent data.

## Requirements

### Agentless-enabled integration

Agentless integrations allow you to collect data without having to manage Elastic Agent in your cloud. They make manual agent deployment unnecessary, so you can focus on your data instead of the agent that collects it. For more information, refer to [Agentless integrations](https://www.elastic.co/guide/en/serverless/current/security-agentless-integrations.html) and the [Agentless integrations FAQ](https://www.elastic.co/guide/en/serverless/current/agentless-integration-troubleshooting.html).

Agentless deployments are only supported in Elastic Serverless and Elastic Cloud environments. This functionality is in beta and is subject to change. Beta features are not subject to the support SLA of official GA features.

### Agent-based installation

Elastic Agent must be installed. For more details, check the Elastic Agent [installation instructions](docs-content://reference/fleet/install-elastic-agents.md). You can install only one Elastic Agent per host.

## Compatibility

For Rest API, this module has been tested against the **2.0** API version.

## Setup

### Collect data from the Qualys GAV API:

- The base URL corresponds to the API Gateway URL of the respective Qualys GAV instance. For reference, see: [Qualys Platform Identification](https://www.qualys.com/platform-identification/#:~:text=apps.qualysksa.com-,API%20URLs,-Use%20API%20Gateway).
- The same username and password used for logging into the Qualys instance are required for authentication when fetching logs through the integration.

### Enable the integration in Elastic

1. In Kibana navigate to **Management** > **Integrations**.
2. In the search top bar, type **Qualys GAV**.
3. Select the **Qualys GAV** integration and add it.
4. Add all the required integration configuration parameters: URL, Username and Password.
5. Save the integration.

## Logs reference

### Asset

This is the `Asset` dataset.

#### Example

{{event "asset"}}

{{fields "asset"}}
15 changes: 15 additions & 0 deletions packages/qualys_gav/_dev/deploy/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '3.8'
services:
qualys_gav:
image: docker.elastic.co/observability/stream:v0.18.0
hostname: qualys_gav
ports:
- 8090
volumes:
- ./files:/files:ro
environment:
PORT: '8090'
command:
- http-server
- --addr=:8090
- --config=/files/config.yml
2,003 changes: 2,003 additions & 0 deletions packages/qualys_gav/_dev/deploy/docker/files/config.yml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions packages/qualys_gav/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# newer versions go on top
- version: 0.1.0
changes:
- description: Initial release.
type: enhancement
link: https://github.com/elastic/integrations/pull/14644

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fields:
tags:
- preserve_original_event
- preserve_duplicate_custom_fields
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
input: cel
service: qualys_gav
vars:
url: http://{{Hostname}}:{{Port}}
username: xxxx
password: xxxx
data_stream:
vars:
preserve_original_event: true
preserve_duplicate_custom_fields: true
batch_size: 2
assert:
hit_count: 5
131 changes: 131 additions & 0 deletions packages/qualys_gav/data_stream/asset/agent/stream/cel.yml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
config_version: 2
interval: {{interval}}
resource.tracer:
enabled: {{enable_request_tracer}}
filename: "../../logs/cel/http-request-trace-*.ndjson"
maxbackups: 5
{{#if proxy_url}}
resource.proxy_url: {{proxy_url}}
{{/if}}
{{#if ssl}}
resource.ssl: {{ssl}}
{{/if}}
{{#if http_client_timeout}}
resource.timeout: {{http_client_timeout}}
{{/if}}
resource.url: {{url}}

state:
batch_size: {{batch_size}}
username: {{username}}
password: {{password}}
asset_id: 0
redact:
fields:
- password
program: |
state.url.trim_right("/").as(base_url, state.with(
(has(state.expiry) && timestamp(state.expiry) > now ?
{
"access_token": state.access_token,
"expiry": state.expiry
}
:
post_request(
base_url + "/auth",
"application/x-www-form-urlencoded",
{"username":[state.username],"password":[state.password]}.format_query()
).do_request().as(resp, resp.StatusCode == 201 ?
{
"access_token": string(resp.Body),
// Include 30s grace period to manage session expiry.
"expiry": (now() + duration("4h") - duration("30s")).format(time_layout.RFC3339),
}
:
{
"events": {
"error": {
"code": string(resp.StatusCode),
"id": string(resp.Status),
"message": "POST: "+ base_url + "/auth" + (
size(resp.Body) != 0 ?
string(resp.Body)
:
string(resp.Status) + ' (' + string(resp.StatusCode) + ')'
),
},
},
"want_more": false,
}
)
).as(token,
has(token.events) ? token : // Exit early due to failure.
request(
"POST",
base_url + "/rest/2.0/search/am/asset?" + {
"pageSize": [string(state.batch_size)],
"lastSeenAssetId": [string(int(state.asset_id))]
}.format_query()
).with({
"Header":{
"Authorization": ["Bearer " + token.access_token],
}
}).do_request().as(resp, resp.StatusCode == 200 ?
resp.Body.decode_json().as(body, {
"events": body.assetListData.asset.map(e,{
"message": e.encode_json(),
}),
"want_more": body.hasMore != 0,
"access_token": token.access_token,
"expiry": token.expiry,
"asset_id": body.hasMore != 0 ? body.assetListData.asset.map(e, e.assetId).max() : 0,
})
:
(resp.StatusCode == 204) ?
// 204 No Content - Terminate Pagination and Publish Empty Event.
{
"events": [],
"want_more": false,
"access_token": token.access_token,
"expiry": token.expiry,
"asset_id": 0,
}
:
{
"events": {
"error": {
"code": string(resp.StatusCode),
"id": string(resp.Status),
"message": "POST: "+ base_url + "/rest/2.0/search/am/asset" + (
size(resp.Body) != 0 ?
string(resp.Body)
:
string(resp.Status) + ' (' + string(resp.StatusCode) + ')'
),
},
},
"want_more": false,
}
)
)
))
tags:
{{#if preserve_original_event}}
- preserve_original_event
{{/if}}
{{#if preserve_duplicate_custom_fields}}
- preserve_duplicate_custom_fields
{{/if}}
{{#if hide_sensitive}}
- hide_sensitive
{{/if}}
{{#each tags as |tag|}}
- {{tag}}
{{/each}}
{{#contains "forwarded" tags}}
publisher_pipeline.disable_host: true
{{/contains}}
{{#if processors}}
processors:
{{processors}}
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_age": "30d",
"max_primary_shard_size": "50gb"
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
Loading