diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 64162c95c46..48a7c5626e5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,6 +13,7 @@ /packages/abnormal_security @elastic/security-service-integrations /packages/activemq @elastic/obs-infraobs-integrations /packages/admin_by_request_epm @elastic/security-service-integrations +/packages/agentless_hello_world @elastic/agentless-team /packages/airflow @elastic/obs-infraobs-integrations /packages/airlock_digital @elastic/security-service-integrations /packages/akamai @elastic/security-service-integrations diff --git a/packages/agentless_hello_world/_dev/build/build.yml b/packages/agentless_hello_world/_dev/build/build.yml new file mode 100644 index 00000000000..d9a27e2caef --- /dev/null +++ b/packages/agentless_hello_world/_dev/build/build.yml @@ -0,0 +1,3 @@ +dependencies: + ecs: + reference: git@v9.1.0 diff --git a/packages/agentless_hello_world/_dev/deploy/docker/config.yml b/packages/agentless_hello_world/_dev/deploy/docker/config.yml new file mode 100644 index 00000000000..c42828457ff --- /dev/null +++ b/packages/agentless_hello_world/_dev/deploy/docker/config.yml @@ -0,0 +1,10 @@ +rules: + - path: / + methods: ["GET"] + responses: + - status_code: 418 + headers: + Content-Type: + - "application/json" + body: |- + {"this_is": "ignored"} diff --git a/packages/agentless_hello_world/_dev/deploy/docker/docker-compose.yml b/packages/agentless_hello_world/_dev/deploy/docker/docker-compose.yml new file mode 100644 index 00000000000..c8e15dfedfa --- /dev/null +++ b/packages/agentless_hello_world/_dev/deploy/docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + epr_mock: + image: docker.elastic.co/observability/stream:v0.18.0 + hostname: epr_mock + ports: + - 8080 + volumes: + - ./config.yml:/config.yml + environment: + PORT: "8080" + command: + - http-server + - --addr=:8080 + - --config=/config.yml diff --git a/packages/agentless_hello_world/changelog.yml b/packages/agentless_hello_world/changelog.yml new file mode 100644 index 00000000000..b6fa3dcc418 --- /dev/null +++ b/packages/agentless_hello_world/changelog.yml @@ -0,0 +1,5 @@ +- version: "0.1.0" + changes: + - description: Initial release. + type: enhancement + link: https://github.com/elastic/integrations/pull/15729 diff --git a/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json new file mode 100644 index 00000000000..8c7ded3486a --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json @@ -0,0 +1,7 @@ +{ + "events": [ + { + "message": "{\"status_code\":200}" + } + ] +} diff --git a/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-config.yml b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-config.yml new file mode 100644 index 00000000000..1a2c171ee45 --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-config.yml @@ -0,0 +1 @@ +fields: {} diff --git a/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-expected.json b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-expected.json new file mode 100644 index 00000000000..2eba7de4248 --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/_dev/test/pipeline/test-hello-world.json-expected.json @@ -0,0 +1,23 @@ +{ + "expected": [ + { + "agentless_hello_world": { + "generic": { + "status_code": 200 + } + }, + "ecs": { + "version": "9.1.0" + }, + "event": { + "category": [ + "web" + ], + "kind": "event", + "type": [ + "info" + ] + } + } + ] +} diff --git a/packages/agentless_hello_world/data_stream/generic/_dev/test/system/test-default-config.yml b/packages/agentless_hello_world/data_stream/generic/_dev/test/system/test-default-config.yml new file mode 100644 index 00000000000..4fe2e5d14a0 --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/_dev/test/system/test-default-config.yml @@ -0,0 +1,7 @@ +input: cel +service: epr_mock +data_stream: + vars: + url: http://{{Hostname}}:{{Port}} +assert: + hit_count: 1 diff --git a/packages/agentless_hello_world/data_stream/generic/agent/stream/cel.yml.hbs b/packages/agentless_hello_world/data_stream/generic/agent/stream/cel.yml.hbs new file mode 100644 index 00000000000..c7f7a6a7b8b --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/agent/stream/cel.yml.hbs @@ -0,0 +1,18 @@ +config_version: 2 +interval: 20s +resource.timeout: 15s +resource.url: "{{url}}" +state: + url: "{{url}}" +program: | + request("GET", state.url) + .do_request() + .as(resp, { + "events": [{ + "message": { + "status_code": resp.StatusCode + }.encode_json() + }] + }) +tags: + - agentless-hello-world diff --git a/packages/agentless_hello_world/data_stream/generic/elasticsearch/ingest_pipeline/default.yml b/packages/agentless_hello_world/data_stream/generic/elasticsearch/ingest_pipeline/default.yml new file mode 100644 index 00000000000..4b97edf83de --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/elasticsearch/ingest_pipeline/default.yml @@ -0,0 +1,88 @@ +--- +description: Pipeline for processing Agentless Hello World generic logs. +processors: + - set: + field: ecs.version + value: '9.1.0' + - remove: + field: + - organization + - division + - team + ignore_missing: true + if: ctx.organization instanceof String && ctx.division instanceof String && ctx.team instanceof String + tag: remove_agentless_tags + description: >- + Removes the fields added by Agentless as metadata, as they can collide with ECS fields. + - terminate: + tag: data_collection_error + if: ctx.error?.message != null && ctx.message == null && ctx.event?.original == null + - rename: + field: message + target_field: event.original + ignore_missing: true + if: ctx.event?.original == null + - remove: + field: message + tag: remove_message + ignore_missing: true + description: The `message` field is no longer required if the document has an `event.original` field. + if: ctx.event?.original != null + - json: + field: event.original + target_field: agentless_hello_world.generic + on_failure: + - set: + field: error.type + value: "json_parse_error" + - set: + field: error.message + value: "{{{ _ingest.on_failure_message }}}" + - set: + field: event.kind + value: event + - set: + field: event.type + value: [info] + - set: + field: event.category + value: [web] + - remove: + field: event.original + if: ctx.tags == null || !(ctx.tags.contains('preserve_original_event')) + ignore_failure: true + ignore_missing: true + - script: + lang: painless + description: This script processor iterates over the whole document to remove fields with null values. + source: | + void handleMap(Map map) { + for (def x : map.values()) { + if (x instanceof Map) { + handleMap(x); + } else if (x instanceof List) { + handleList(x); + } + } + map.values().removeIf(v -> v == null); + } + void handleList(List list) { + for (def x : list) { + if (x instanceof Map) { + handleMap(x); + } else if (x instanceof List) { + handleList(x); + } + } + } + handleMap(ctx); +on_failure: + - set: + field: event.kind + value: pipeline_error + - append: + field: error.message + value: >- + Processor '{{{ _ingest.on_failure_processor_type }}}' + {{#_ingest.on_failure_processor_tag}}with tag '{{{ _ingest.on_failure_processor_tag }}}' + {{/_ingest.on_failure_processor_tag}}failed with message '{{{ _ingest.on_failure_message }}}' diff --git a/packages/agentless_hello_world/data_stream/generic/fields/base-fields.yml b/packages/agentless_hello_world/data_stream/generic/fields/base-fields.yml new file mode 100644 index 00000000000..d40ecbe868b --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/fields/base-fields.yml @@ -0,0 +1,23 @@ +- name: input.type + type: keyword + description: Input type +- name: data_stream.type + type: constant_keyword + description: Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: Data stream namespace. +- name: event.module + type: constant_keyword + description: Event module + value: agentless_hello_world +- name: event.dataset + type: constant_keyword + description: Event dataset + value: agentless_hello_world.generic +- name: '@timestamp' + type: date + description: Event timestamp. diff --git a/packages/agentless_hello_world/data_stream/generic/fields/fields.yml b/packages/agentless_hello_world/data_stream/generic/fields/fields.yml new file mode 100644 index 00000000000..a21c3526dbe --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/fields/fields.yml @@ -0,0 +1,6 @@ +- name: agentless_hello_world.generic + type: group + fields: + - name: status_code + type: long + description: HTTP Status Code diff --git a/packages/agentless_hello_world/data_stream/generic/manifest.yml b/packages/agentless_hello_world/data_stream/generic/manifest.yml new file mode 100644 index 00000000000..81b14f4e916 --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/manifest.yml @@ -0,0 +1,15 @@ +title: Generic logs +type: logs +streams: + - input: cel + title: Generic logs + description: Collect generic logs from EPR endpoint. + template_path: cel.yml.hbs + vars: + - name: url + type: text + title: EPR URL + description: URL of the EPR endpoint (internal use only, for testing) + default: https://epr.elastic.co + required: false + show_user: false diff --git a/packages/agentless_hello_world/data_stream/generic/sample_event.json b/packages/agentless_hello_world/data_stream/generic/sample_event.json new file mode 100644 index 00000000000..a7f4fa78a4b --- /dev/null +++ b/packages/agentless_hello_world/data_stream/generic/sample_event.json @@ -0,0 +1,69 @@ +{ + "@timestamp": "2025-10-23T11:25:00.349Z", + "agent": { + "ephemeral_id": "2f0402ea-00e1-47fa-944c-1e34d91fdc2f", + "id": "0909c464-2093-4f85-8bf7-b11593587146", + "name": "elastic-agent-93305", + "type": "filebeat", + "version": "9.1.3" + }, + "agentless_hello_world": { + "generic": { + "status_code": 418 + } + }, + "data_stream": { + "dataset": "agentless_hello_world.generic", + "namespace": "88559", + "type": "logs" + }, + "ecs": { + "version": "9.1.0" + }, + "elastic_agent": { + "id": "0909c464-2093-4f85-8bf7-b11593587146", + "snapshot": false, + "version": "9.1.3" + }, + "event": { + "agent_id_status": "verified", + "category": [ + "web" + ], + "dataset": "agentless_hello_world.generic", + "ingested": "2025-10-23T11:25:03Z", + "kind": "event", + "module": "agentless_hello_world", + "type": [ + "info" + ] + }, + "host": { + "architecture": "aarch64", + "containerized": false, + "hostname": "elastic-agent-93305", + "ip": [ + "172.30.0.2", + "172.18.0.4" + ], + "mac": [ + "36-F7-E4-8A-31-61", + "B2-C0-07-A9-21-9B" + ], + "name": "elastic-agent-93305", + "os": { + "family": "", + "kernel": "6.10.14-linuxkit", + "name": "Wolfi", + "platform": "wolfi", + "type": "linux", + "version": "20230201" + } + }, + "input": { + "type": "cel" + }, + "tags": [ + "agentless-hello-world" + ] +} diff --git a/packages/agentless_hello_world/docs/README.md b/packages/agentless_hello_world/docs/README.md new file mode 100644 index 00000000000..e400b080988 --- /dev/null +++ b/packages/agentless_hello_world/docs/README.md @@ -0,0 +1,53 @@ +# Agentless Hello World + +This is a sample integration designed to exercise the Agentless infrastructure. It periodically fetches data from `https://epr.elastic.co` every minute to demonstrate basic agentless functionality. + +## Overview + +The Agentless Hello World integration is a minimal example that: +- Fetches data from the Elastic Package Registry (EPR) endpoint +- Runs every 1 minute +- Requires no user configuration + +## Configuration + +This integration requires no configuration from the user. All settings are pre-configured: +- **Endpoint**: `https://epr.elastic.co` +- **Interval**: 1 minute +- **Deployment mode**: Agentless by default + +## Data Collection + +The integration makes HTTP GET requests to `https://epr.elastic.co` and stores: +- **status_code**: HTTP Status Code for the response. + +## 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. + +## Logs + +### Generic + +The generic data stream collects responses from the EPR endpoint. + +**ECS Field Reference** + +Please refer to the following document for detailed information on ECS fields: +- [Elastic Common Schema](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html) + +**Exported fields** + +| Field | Description | Type | +|---|---|---| +| @timestamp | Event timestamp. | date | +| agentless_hello_world.generic.status_code | The HTTP Status Code of the response. | long | +| data_stream.dataset | Data stream dataset. | constant_keyword | +| data_stream.namespace | Data stream namespace. | constant_keyword | +| data_stream.type | Data stream type. | constant_keyword | +| event.dataset | Event dataset | constant_keyword | +| event.module | Event module | constant_keyword | diff --git a/packages/agentless_hello_world/img/icon.svg b/packages/agentless_hello_world/img/icon.svg new file mode 100644 index 00000000000..e8ba72b6c85 --- /dev/null +++ b/packages/agentless_hello_world/img/icon.svg @@ -0,0 +1,4 @@ + + + AH + diff --git a/packages/agentless_hello_world/manifest.yml b/packages/agentless_hello_world/manifest.yml new file mode 100644 index 00000000000..c2dfd9a8a88 --- /dev/null +++ b/packages/agentless_hello_world/manifest.yml @@ -0,0 +1,39 @@ +format_version: 3.3.2 +name: agentless_hello_world +title: Agentless Hello World +version: "0.1.0" +description: A sample integration to exercise the Agentless infrastructure by fetching https://epr.elastic.co every minute. +type: integration +categories: + - observability +conditions: + kibana: + version: "^8.18.0 || ^9.0.0" + elastic: + subscription: "basic" +icons: + - src: /img/icon.svg + title: Agentless Hello World + size: 32x32 + type: image/svg+xml +policy_templates: + - name: agentless_hello_world + title: Agentless Hello World + description: Collect data from EPR endpoint every minute. + deployment_modes: + default: + enabled: true + agentless: + enabled: true + is_default: true + organization: observability + division: engineering + team: agentless-team + inputs: + - type: cel + title: Collect data from EPR endpoint + description: Fetches https://epr.elastic.co every minute. + vars: [] +owner: + github: elastic/agentless-team + type: elastic