Skip to content

Commit 47b6a83

Browse files
committed
feat: add Elasticsearch transform support
Add Transform struct with transform configuration, manifest, and fields. Closes #8
1 parent ed6c737 commit 47b6a83

File tree

121 files changed

+26353
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+26353
-4
lines changed

fields_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestReadFields(t *testing.T) {
5959
t.Fatal(err)
6060
}
6161

62-
const expected = 81
62+
const expected = 154
6363
if len(allFields) != expected {
6464
t.Fatalf("got %d, want %d fields", len(allFields), expected)
6565
}

fleetpkg.go

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Integration struct {
3232
Manifest Manifest `json:"manifest,omitempty" yaml:"manifest,omitempty"`
3333
Input *DataStream `json:"input,omitempty" yaml:"input,omitempty"`
3434
DataStreams map[string]*DataStream `json:"data_streams,omitempty" yaml:"data_streams,omitempty"`
35+
Transforms map[string]*Transform `json:"transforms,omitempty" yaml:"transforms,omitempty"`
3536
Changelog Changelog `json:"changelog,omitempty" yaml:"changelog,omitempty"`
3637

3738
sourceFile string
@@ -243,6 +244,7 @@ func (c *Conditions) UnmarshalYAML(value *yaml.Node) error {
243244
if err := value.Decode(&pc); err != nil {
244245
return err
245246
}
247+
*c = Conditions(pc.conditions)
246248

247249
if pc.Kibana.Version != "" {
248250
c.Kibana.Version = pc.Kibana.Version
@@ -338,6 +340,11 @@ type PolicyTemplate struct {
338340
TemplatePath string `json:"template_path,omitempty" yaml:"template_path,omitempty"`
339341
Vars []Var `json:"vars,omitempty" yaml:"vars,omitempty"` // Policy template level variables.
340342
DeploymentModes *DeploymentModes `json:"deployment_modes,omitempty" yaml:"deployment_modes,omitempty"`
343+
344+
// Indicate if this package is capable of satisfying FIPS requirements. Set
345+
// to false if it uses any input that cannot be configured to use FIPS
346+
// cryptography. Defaults to true.
347+
FIPSCompatible *bool `json:"fips_compatible,omitempty" yaml:"fips_compatible,omitempty"`
341348
}
342349

343350
// DeploymentModes options. The deployment mode refers to the mode used to deploy the Elastic Agents running this policy.
@@ -523,9 +530,9 @@ func (p *Processor) MarshalJSON() ([]byte, error) {
523530
})
524531
}
525532

526-
// Read reads the Fleet integration at the specified path. The path should
527-
// point to the directory containing the integration's main manifest.yml.
528-
func Read(path string) (*Integration, error) {
533+
// Read an integration package from a directory.
534+
// The path must be the root of the integration package.
535+
func Read(path string, options ...Option) (*Integration, error) {
529536
integration := &Integration{
530537
DataStreams: map[string]*DataStream{},
531538
sourceFile: path,
@@ -634,6 +641,13 @@ func Read(path string) (*Integration, error) {
634641
}
635642
}
636643

644+
// Read elasticsearch transforms.
645+
var err error
646+
integration.Transforms, err = readTransforms(filepath.Join(path, "elasticsearch", "transform"))
647+
if err != nil {
648+
return nil, fmt.Errorf("failed to read transforms for '%s': %w", path, err)
649+
}
650+
637651
return integration, nil
638652
}
639653

@@ -670,3 +684,53 @@ func readJSON(path string, v any, strict bool) error {
670684
}
671685
return nil
672686
}
687+
688+
func readTransforms(path string) (map[string]*Transform, error) {
689+
dirs, err := os.ReadDir(path)
690+
if err != nil {
691+
if os.IsNotExist(err) {
692+
return nil, nil
693+
}
694+
return nil, err
695+
}
696+
697+
transforms := map[string]*Transform{}
698+
for _, de := range dirs {
699+
if !de.IsDir() {
700+
continue
701+
}
702+
703+
transformPath := filepath.Join(path, de.Name())
704+
transform, err := readTransform(transformPath)
705+
if err != nil {
706+
return nil, fmt.Errorf("failed to read transform from %s: %w", transformPath, err)
707+
}
708+
709+
transforms[de.Name()] = transform
710+
}
711+
712+
if len(transforms) == 0 {
713+
return nil, nil
714+
}
715+
return transforms, nil
716+
}
717+
718+
func readTransform(path string) (*Transform, error) {
719+
transform := Transform{sourceDir: path}
720+
721+
if err := readYAML(filepath.Join(path, "transform.yml"), &transform.Transform, false); err != nil {
722+
return nil, err
723+
}
724+
725+
if err := readYAML(filepath.Join(path, "manifest.yml"), &transform.Manifest, false); err != nil && !os.IsNotExist(err) {
726+
return nil, err
727+
}
728+
729+
var err error
730+
transform.Fields, err = ReadFields(filepath.Join(path, "fields/*.yml"))
731+
if err != nil {
732+
return nil, err
733+
}
734+
735+
return &transform, nil
736+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies:
2+
ecs:
3+
reference: "[email protected]"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# AbuseCH integration
2+
3+
This integration is for [AbuseCH](https://urlhaus.abuse.ch/) logs. It includes the following datasets for retrieving indicators from the AbuseCH API:
4+
5+
- `url` dataset: Supports URL based indicators from AbuseCH API.
6+
- `malware` dataset: Supports Malware based indicators from AbuseCH API.
7+
- `malwarebazaar` dataset: Supports indicators from the MalwareBazaar from AbuseCH.
8+
- `threatfox` dataset: Supports indicators from AbuseCH Threat Fox API.
9+
10+
## Note:
11+
12+
AbuseCH requires using an `Auth Key` (API Key) in the requests for authentication.
13+
Requests without authentication will be denied by the API.
14+
15+
More details on this topic can be found [here](https://abuse.ch/blog/community-first/).
16+
17+
## Agentless Enabled Integration
18+
19+
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).
20+
21+
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.
22+
23+
## Expiration of Indicators of Compromise (IOCs)
24+
All AbuseCH datasets now support indicator expiration. For `URL` dataset, a full list of active indicators are ingested every interval. For other datasets namely `Malware`, `MalwareBazaar`, and `ThreatFox`, the indicators are expired after duration `IOC Expiration Duration` configured in the integration setting. An [Elastic Transform](https://www.elastic.co/guide/en/elasticsearch/reference/current/transforms.html) is created for every source index to facilitate only active indicators be available to the end users. Each transform creates a destination index named `logs-ti_abusech_latest.dest_*` which only contains active and unexpired indicators. The indiator match rules and dashboards are updated to list only active indicators.
25+
Destinations indices are aliased to `logs-ti_abusech_latest.<datastream_name>`.
26+
27+
| Source Datastream | Destination Index Pattern | Destination Alias |
28+
|:-----------------------------------|:-------------------------------------------------|-----------------------------------------|
29+
| `logs-ti_abusech.url-*` | `logs-ti_abusech_latest.dest_url-*` | `logs-ti_abusech_latest.url` |
30+
| `logs-ti_abusech.malware-*` | `logs-ti_abusech_latest.dest_malware-*` | `logs-ti_abusech_latest.malware` |
31+
| `logs-ti_abusech.malwarebazaar-*` | `logs-ti_abusech_latest.dest_malwarebazaar-*` | `logs-ti_abusech_latest.malwarebazaar` |
32+
| `logs-ti_abusech.threatfox-*` | `logs-ti_abusech_latest.dest_threatfox-*` | `logs-ti_abusech_latest.threatfox` |
33+
34+
### ILM Policy
35+
To facilitate IOC expiration, source datastream-backed indices `.ds-logs-ti_abusech.<datastream_name>-*` are allowed to contain duplicates from each polling interval. ILM policy `logs-ti_abusech.<datastream_name>-default_policy` is added to these source indices so it doesn't lead to unbounded growth. This means data in these source indices will be deleted after `5 days` from ingested date.
36+
37+
## Logs
38+
39+
### URL
40+
41+
The AbuseCH URL data_stream retrieves full list of active threat intelligence indicators every interval from the Active Indicators URL database dump `https://urlhaus.abuse.ch/downloads/json/`.
42+
43+
{{fields "url"}}
44+
45+
### Malware
46+
47+
The AbuseCH malware data_stream retrieves threat intelligence indicators from the payload API endpoint `https://urlhaus-api.abuse.ch/v1/payloads/recent/`.
48+
49+
{{fields "malware"}}
50+
51+
### MalwareBazaar
52+
53+
The AbuseCH malwarebazaar data_stream retrieves threat intelligence indicators from the MalwareBazaar API endpoint `https://mb-api.abuse.ch/api/v1/`.
54+
55+
{{fields "malwarebazaar"}}
56+
57+
### Threat Fox
58+
59+
The AbuseCH threatfox data_stream retrieves threat intelligence indicators from the Threat Fox API endpoint `https://threatfox-api.abuse.ch/api/v1/`.
60+
61+
{{fields "threatfox"}}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version: "2.3"
2+
services:
3+
abusech:
4+
image: docker.elastic.co/observability/stream:v0.18.0
5+
ports:
6+
- 8080
7+
volumes:
8+
- ./files:/files:ro
9+
environment:
10+
PORT: 8080
11+
command:
12+
- http-server
13+
- --addr=:8080
14+
- --config=/files/config.yml
15+
abusech-threatfox:
16+
image: docker.elastic.co/observability/stream:v0.18.0
17+
ports:
18+
- 8081
19+
volumes:
20+
- ./files:/files:ro
21+
environment:
22+
PORT: 8081
23+
command:
24+
- http-server
25+
- --addr=:8081
26+
- --config=/files/config-threatfox.yml
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
rules:
2+
- path: /api/v1/
3+
methods: ["POST"]
4+
request_headers:
5+
Content-Type: "application/json"
6+
Auth-Key: "test_auth_key"
7+
body:
8+
query: "get_iocs"
9+
days: /[1-7]+/
10+
responses:
11+
- status_code: 200
12+
body: |-
13+
{
14+
"query_status": "ok",
15+
"data": [
16+
{"id":"841537","ioc":"wizzy.hopto.org","threat_type":"botnet_cc","threat_type_desc":"Indicator that identifies a botnet command&control server (C&C)","ioc_type":"domain","ioc_type_desc":"Domain that is used for botnet Command&control (C&C)","malware":"win.asyncrat","malware_printable":"AsyncRAT","malware_alias":null,"malware_malpedia":"https://malpedia.caad.fkie.fraunhofer.de/details/win.asyncrat","confidence_level":100,"first_seen":"2022-08-05 19:43:08 UTC","last_seen":null,"reference":"https://tria.ge/220805-w57pxsgae2","reporter":"AndreGironda","tags":["asyncrat"]},
17+
{"id":"839586","ioc":"872ff530d50579ae6bdc7cb4d658324b1d0e7a3e","threat_type":"payload","threat_type_desc":"Indicator that identifies a malware sample (payload)","ioc_type":"sha1_hash","ioc_type_desc":"SHA1 hash of a malware sample (payload)","malware":"win.vidar","malware_printable":"Vidar","malware_alias":null,"malware_malpedia":"https://malpedia.caad.fkie.fraunhofer.de/details/win.vidar","confidence_level":75,"first_seen":"2022-07-25 22:27:09 UTC","last_seen":null,"reference":"","reporter":"crep1x","tags":["Vidar"]},
18+
{"id":"839587","ioc":"a3b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0r1s2t3","threat_type":"payload","threat_type_desc":"Indicator that identifies a malware sample (payload)","ioc_type":"sha1_hash","ioc_type_desc":"SHA1 hash of a malware sample (payload)","malware":"win.redline","malware_printable":"RedLine","malware_alias":null,"malware_malpedia":"https://malpedia.caad.fkie.fraunhofer.de/details/win.redline","confidence_level":80,"first_seen":"2025-03-15 10:15:00 UTC","last_seen":null,"reference":"","reporter":"cyberhunter","tags":["RedLine"]}
19+
]
20+
}
21+
- path: /api/v1/
22+
methods: ["POST"]
23+
request_headers:
24+
Content-Type: "application/json"
25+
body:
26+
query: "get_iocs"
27+
days: /[1-7]+/
28+
responses:
29+
- status_code: 200
30+
body: |-
31+
{
32+
"query_status": "ok",
33+
"data": [
34+
{"id":"841537","ioc":"wizzy.hopto.org","threat_type":"botnet_cc","threat_type_desc":"Indicator that identifies a botnet command&control server (C&C)","ioc_type":"domain","ioc_type_desc":"Domain that is used for botnet Command&control (C&C)","malware":"win.asyncrat","malware_printable":"AsyncRAT","malware_alias":null,"malware_malpedia":"https://malpedia.caad.fkie.fraunhofer.de/details/win.asyncrat","confidence_level":100,"first_seen":"2022-08-05 19:43:08 UTC","last_seen":null,"reference":"https://tria.ge/220805-w57pxsgae2","reporter":"AndreGironda","tags":["asyncrat"]},
35+
{"id":"839586","ioc":"872ff530d50579ae6bdc7cb4d658324b1d0e7a3e","threat_type":"payload","threat_type_desc":"Indicator that identifies a malware sample (payload)","ioc_type":"sha1_hash","ioc_type_desc":"SHA1 hash of a malware sample (payload)","malware":"win.vidar","malware_printable":"Vidar","malware_alias":null,"malware_malpedia":"https://malpedia.caad.fkie.fraunhofer.de/details/win.vidar","confidence_level":75,"first_seen":"2022-07-25 22:27:09 UTC","last_seen":null,"reference":"","reporter":"crep1x","tags":["Vidar"]}
36+
]
37+
}

0 commit comments

Comments
 (0)