Skip to content
Open
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
2,873 changes: 2,873 additions & 0 deletions plugins/cloudtrail_rs/Cargo.lock

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions plugins/cloudtrail_rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "cloudtrail"
version = "0.16.0"
edition = "2021"
authors = ["The Falco Authors"]
license = "Apache-2.0"
description = "reads cloudtrail JSON data saved to file in the directory specified in the settings"

[lib]
crate-type = ["cdylib"]

[dependencies]
falco_event = "0.5.1"
falco_plugin = "0.5.1"
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }
serde_spanned = "1"
schemars = "1"
aws-config = "1"
aws-sdk-s3 = "1"
aws-sdk-sqs = "1"
aws-types = "1"
tokio = { version = "1", features = ["rt-multi-thread", "sync"] }
flate2 = "1"
regex = "1"
chrono = "0.4"

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
49 changes: 49 additions & 0 deletions plugins/cloudtrail_rs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2026 The Falco Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#

.PHONY: all build clean release debug test

NAME := cloudtrail
OUTPUT := lib$(NAME).so

all: release

build: release

release:
@echo "Building release version..."
cargo build --release
@cp target/release/lib$(NAME).so $(OUTPUT) || cp target/release/lib$(NAME).dylib $(OUTPUT) 2>/dev/null || true

debug:
@echo "Building debug version..."
cargo build
@cp target/debug/lib$(NAME).so $(OUTPUT) || cp target/debug/lib$(NAME).dylib $(OUTPUT) 2>/dev/null || true

clean:
cargo clean
rm -f $(OUTPUT)

test:
cargo test

check:
cargo clippy -- -D warnings
cargo fmt -- --check

fmt:
cargo fmt

readme:
@echo "Generate README with falco plugin tool if available"
246 changes: 246 additions & 0 deletions plugins/cloudtrail_rs/README.md

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions plugins/cloudtrail_rs/src/aws.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2026 The Falco Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use anyhow::Result;
use aws_types::region::Region;
use aws_types::SdkConfig;

use crate::config::AwsConfig;

/// Load the AWS SDK config from the given AwsConfig settings
pub async fn load_aws_config(aws: &AwsConfig) -> Result<SdkConfig> {
// Apply config and credentials file overrides via env vars.
// Safety: called once during initialization before any concurrent access.
if !aws.config.is_empty() {
unsafe {
std::env::set_var("AWS_CONFIG_FILE", &aws.config);
}
}
if !aws.credentials.is_empty() {
unsafe {
std::env::set_var("AWS_SHARED_CREDENTIALS_FILE", &aws.credentials);
}
}

let mut loader = aws_config::defaults(aws_config::BehaviorVersion::latest());

if !aws.profile.is_empty() {
loader = loader.profile_name(&aws.profile);
}

if !aws.region.is_empty() {
loader = loader.region(Region::new(aws.region.clone()));
}

Ok(loader.load().await)
}
135 changes: 135 additions & 0 deletions plugins/cloudtrail_rs/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2026 The Falco Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// AWS SDK configuration
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct AwsConfig {
#[serde(rename = "profile")]
#[schemars(
title = "AWS Profile",
description = "If non-empty overrides the AWS shared configuration profile (e.g. 'default') and environment variables such as AWS_PROFILE (Default: empty)"
)]
pub profile: String,

#[serde(rename = "region")]
#[schemars(
title = "AWS Region",
description = "If non-empty overrides the AWS region specified in the profile (e.g. 'us-east-1') and environment variables such as AWS_REGION (Default: empty)"
)]
pub region: String,

#[serde(rename = "config")]
#[schemars(
title = "Shared AWS Config File",
description = "If non-empty overrides the AWS shared configuration filepath (e.g. ~/.aws/config) and env variables such as AWS_CONFIG_FILE (Default: empty)"
)]
pub config: String,

#[serde(rename = "credentials")]
#[schemars(
title = "Shared AWS Credentials File",
description = "If non-empty overrides the AWS shared credentials filepath (e.g. ~/.aws/credentials) and env variables such as AWS_SHARED_CREDENTIALS_FILE (Default: empty)"
)]
pub credentials: String,
}

impl Default for AwsConfig {
fn default() -> Self {
AwsConfig {
profile: String::new(),
region: String::new(),
config: String::new(),
credentials: String::new(),
}
}
}

/// Plugin configuration for the CloudTrail plugin
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct PluginConfig {
#[serde(rename = "s3DownloadConcurrency")]
#[schemars(
title = "S3 download concurrency",
description = "Controls the number of background goroutines used to download S3 files (Default: 32)"
)]
pub s3_download_concurrency: i32,

#[serde(rename = "s3Interval")]
#[schemars(
title = "S3 log interval",
description = "Download log files over the specified interval (Default: no interval)"
)]
pub s3_interval: String,

#[serde(rename = "sqsDelete")]
#[schemars(
title = "Delete SQS messages",
description = "If true then the plugin will delete SQS messages from the queue immediately after receiving them (Default: true)"
)]
pub sqs_delete: bool,

#[serde(rename = "useAsync")]
#[schemars(
title = "Use async extraction (ignored)",
description = "Ignored. This option is present for compatibility with the original Go version of this plugin."
)]
pub use_async: bool,

#[serde(rename = "useS3SNS")]
#[schemars(
title = "Use S3 SNS",
description = "If true then the plugin will expect SNS messages to originate from S3 instead of directly from Cloudtrail (Default: false)"
)]
pub use_s3_sns: bool,

#[serde(rename = "s3AccountList")]
#[schemars(
title = "S3 account list",
description = "A comma separated list of account IDs for organizational Cloudtrails (Default: no account IDs)"
)]
pub s3_account_list: String,

#[serde(rename = "sqsOwnerAccount")]
#[schemars(
title = "SQS owner account",
description = "The AWS account ID that owns the SQS queue in case the queue is owned by a different account (Default: no account ID)"
)]
pub sqs_owner_account: String,

#[serde(rename = "aws")]
pub aws: AwsConfig,
}

impl Default for PluginConfig {
fn default() -> Self {
PluginConfig {
s3_download_concurrency: 32,
s3_interval: String::new(),
sqs_delete: true,
use_async: true,
use_s3_sns: false,
s3_account_list: String::new(),
sqs_owner_account: String::new(),
aws: AwsConfig::default(),
}
}
}
Loading
Loading