Skip to content

Commit

Permalink
Add batch RTR commands to high-level client, with example. (#339)
Browse files Browse the repository at this point in the history
Co-authored-by: Forrest Aldridge <forrest.aldridge@crowdstrike.com>
  • Loading branch information
faldridge and Forrest Aldridge committed May 3, 2023
1 parent 814c8a4 commit f6e2554
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Some of these examples ready to be used as stand-alone programs.
* [falcon_registry_token](falcon_registry_token) - helper to generate container registry logic information for `docker login`
* [falcon_rtr_read_only_command](falcon_rtr_read_only_command) - stand-alone example to run basic read-only RTR (Real-Time Response) command against a specific agent
* [falcon_rtr_admin_create_and_run_script](falcon_rtr_admin_create_and_run_script) - stand-alone example of running custom script on the specific agent using RTR (Real-Time Response) API
* [falcon_rtr_batch_read_only_command](falcon_rtr_batch_read_only_command) - stand-alone example to run basic read-only RTR (Real-Time Response) command against several agents at once.
* [falcon_spotlight_vulnerabilities](falcon_spotlight_vulnerabilities) - stand-alone tool that outputs inventory of vulnerabilities affecting your environment
* [falcon_supported_kernels](falcon_supported_kernels) - stand-alone tool that outputs short list recent Linux kernels supported by CrowdStrike Falcon for a given distribution
* [falcon_zta](falcon_zta) - stand-alone tool that utilises Hosts and ZTA APIs and outputs ZTA findings for your environment
Expand Down
33 changes: 33 additions & 0 deletions examples/falcon_rtr_batch_read_only_command/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
This is a working standalone example of a program to run a basic read-only RTR command against a specific agent.

## Build
```
go get github.com/crowdstrike/gofalcon/examples/falcon_rtr_batch_read_only_command
```

## Setup Environment Variables
```
# Highly recommended to set at least the client ID and secret
# as environment variables. Credentials should not be entered
# on the command line, so as not to pollute the command history.
export FALCON_CLIENT_ID="your_falcon_id"
export FALCON_CLIENT_SECRET="your_falcon_secret"
export FALCON_CLOUD="us-1, us-2, eu-1, us-gov-1, etc"
# Agent IDs and command are more likely to vary and are not sensitive,
# so are a more natural fit for command line arguments.
export FALCON_AGENT_IDS="def,xyz"
export FALCON_RTR_COMMAND="users"
# Further, while the values for timeout and host timeout are not sensitive,
# they are unlikely to change across requests, so setting them is likely helpful.
export FALCON_RTR_BATCH_TIMEOUT="5m"
export FALCON_RTR_BATCH_HOST_TIMEOUT="3m"
```

## Usage
```
$ FALCON_CLIENT_ID="abc" FALCON_CLIENT_SECRET="XYZ" FALCON_CLOUD=us-1 \
FALCON_RTR_BATCH_TIMEOUT="5m" FALCON_RTR_BATCH_HOST_TIMEOUT="3m" \
falcon_rtr_batch_read_only_command --aids "def,xyz" --cmd "users"
```
80 changes: 80 additions & 0 deletions examples/falcon_rtr_batch_read_only_command/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import (
"context"
"flag"
"fmt"
"os"
"strings"
"time"

"github.com/crowdstrike/gofalcon/falcon"
"github.com/crowdstrike/gofalcon/pkg/falcon_util"
)

func main() {
clientId := flag.String("client-id", os.Getenv("FALCON_CLIENT_ID"), "Client ID for accessing CrowdStrike Falcon Platform (default taken from FALCON_CLIENT_ID env)")
clientSecret := flag.String("client-secret", os.Getenv("FALCON_CLIENT_SECRET"), "Client Secret for accessing CrowdStrike Falcon Platform (default taken from FALCON_CLIENT_SECRET)")
clientCloud := flag.String("cloud", os.Getenv("FALCON_CLOUD"), "Falcon cloud abbreviation (us-1, us-2, eu-1, us-gov-1; default taken from FALCON_CLOUD)")
aidString := flag.String("aids", os.Getenv("FALCON_AGENT_IDS"), "Comma-delimited string of Falcon agent IDs on which to run the command (default taken from FALCON_AGENT_IDS)")
cmd := flag.String("cmd", os.Getenv("FALCON_RTR_COMMAND"), "RTR command to run on the specified agent. (default taken from FALCON_RTR_COMMAND)")
timeoutDurationString := flag.String("timeout", os.Getenv("FALCON_RTR_BATCH_TIMEOUT"), "Timeout in time.Duration format (e.g., '10s, 3m') for the entire batch of RTR commands (default taken from FALCON_RTR_BATCH_TIMEOUT)")
hostTimeoutDurationString := flag.String("host-timeout", os.Getenv("FALCON_RTR_BATCH_HOST_TIMEOUT"), "Timeout in time.Duration format (e.g., '10s, 3m') for an individual command within the batch (default taken from FALCON_RTR_BATCH_HOST_TIMEOUT)")

flag.Parse()
if *clientId == "" {
*clientId = falcon_util.PromptUser(`Missing FALCON_CLIENT_ID environment variable. Please provide your OAuth2 API Client ID for authentication with CrowdStrike Falcon platform. Establishing and retrieving OAuth2 API credentials can be performed at https://falcon.crowdstrike.com/support/api-clients-and-keys.
Falcon Client ID`)
}
if *clientSecret == "" {
*clientSecret = falcon_util.PromptUser(`Missing FALCON_CLIENT_SECRET environment variable. Please provide your OAuth2 API Client Secret for authentication with CrowdStrike Falcon platform. Establishing and retrieving OAuth2 API credentials can be performed at https://falcon.crowdstrike.com/support/api-clients-and-keys.
Falcon Client Secret`)
}
if *aidString == "" {
*aidString = falcon_util.PromptUser(`Missing FALCON_AGENT_IDS. Please provide the ID of the agent you would like to communicate with.
Falcon agent IDs`)
}
if *cmd == "" {
*cmd = falcon_util.PromptUser(`Missing FALCON_RTR_COMMAND. Please provide the RTR command you would like to run. See https://falcon.crowdstrike.com/documentation/71/real-time-response-and-network-containment#rtr_commands for a list of RTR Read Only Analyst commands.
Falcon RTR command`)
}
if *timeoutDurationString == "" {
*timeoutDurationString = falcon_util.PromptUser(`Missing FALCON_RTR_BATCH_TIMEOUT. Please provide the timeout in time.Duration format (10s, 2m) for the entire batch.
Falcon RTR batch timeout`)
}
if *hostTimeoutDurationString == "" {
*hostTimeoutDurationString = falcon_util.PromptUser(`Missing FALCON_RTR_BATCH_HOST_TIMEOUT. Please provide the timeout in time.Duration format (10s, 2m) for east host session in the batch.
Falcon RTR host timeout`)
}
aids := strings.Split(falcon_util.DerefString(aidString), ",")
timeoutDuration, err := time.ParseDuration(*timeoutDurationString)
if err != nil {
panic(err)
}
hostTimeoutDuration, err := time.ParseDuration(*hostTimeoutDurationString)
if err != nil {
panic(err)
}

client, err := falcon.NewRTR(&falcon.ApiConfig{
ClientId: *clientId,
ClientSecret: *clientSecret,
Cloud: falcon.Cloud(*clientCloud),
Context: context.Background(),
})
if err != nil {
panic(err)
}

session, err := client.NewBatchSession(context.Background(), nil, &timeoutDuration, hostTimeoutDuration, aids, nil, false)
if err != nil {
panic(falcon.ErrorExplain(err))
}
result, err := client.BatchCmd(context.Background(), nil, &timeoutDuration, hostTimeoutDuration, strings.Split(*cmd, " ")[0], *session.BatchID, *cmd, nil)
if err != nil {
panic(falcon.ErrorExplain(err))
}

json, _ := falcon_util.PrettyJson(result)
fmt.Println(json)
}
118 changes: 118 additions & 0 deletions falcon/rtr.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,124 @@ func (s *RTRSession) AdminWaitForExecution(ctx context.Context, cloudRequestId s
}
}

// NewBatchSession initiates a batch session for the given hosts. Use the returned BatchID in subsequent call via the
// command methods in this type to then execute RTR commands on them.
// timeout and timeoutDuration are pointers because only one is required and they are mutually exclusive. timeoutDuration is preferred.
func (r *RTR) NewBatchSession(ctx context.Context, timeout *int64, timeoutDuration *time.Duration, hostTimeoutDuration time.Duration, hostIDs []string, existingBatchID *string, queueOffline bool, opts ...real_time_response.ClientOption) (*models.DomainBatchInitSessionResponse, error) {
var timeoutDurationParam *string = nil
if timeoutDuration != nil {
timeoutDurationParam = falcon_util.StrPtr(timeoutDuration.String())
}
response, err := r.client.BatchInitSessions(&real_time_response.BatchInitSessionsParams{
Timeout: timeout,
TimeoutDuration: timeoutDurationParam,
HostTimeoutDuration: falcon_util.StrPtr(hostTimeoutDuration.String()),
Body: &models.DomainBatchInitSessionRequest{
ExistingBatchID: existingBatchID,
HostIds: hostIDs,
QueueOffline: &queueOffline,
},
Context: ctx,
}, opts...)
if err != nil {
return nil, err
}
if err = AssertNoError(response.Payload.Errors); err != nil {
return nil, err
}
return response.Payload, nil
}

// BatchCmd executes an RTR Read Only Analyst command against a batch of hosts.
// timeout and timeoutDuration are pointers because only one is required and they are mutually exclusive. timeoutDuration is preferred.
func (r *RTR) BatchCmd(ctx context.Context, timeout *int64, timeoutDuration *time.Duration, hostTimeoutDuration time.Duration,
baseCommand, batchID, commandString string, optionalHosts []string, opts ...real_time_response.ClientOption) (map[string]models.DomainMultiStatusSensorResponse, error) {

var timeoutDurationParam *string = nil
if timeoutDuration != nil {
timeoutDurationParam = falcon_util.StrPtr(timeoutDuration.String())
}
response, err := r.client.BatchCmd(&real_time_response.BatchCmdParams{
HostTimeoutDuration: falcon_util.StrPtr(hostTimeoutDuration.String()),
Timeout: timeout,
TimeoutDuration: timeoutDurationParam,
Body: &models.DomainBatchExecuteCommandRequest{
BaseCommand: &baseCommand,
BatchID: &batchID,
CommandString: &commandString,
OptionalHosts: optionalHosts,
},
Context: ctx,
}, opts...)
if err != nil {
return nil, err
}
if err = AssertNoError(response.Payload.Errors); err != nil {
return nil, err
}
return response.Payload.Combined.Resources, nil
}

// BatchActiveResponderCmd executes an RTR Active Responder command against a batch of hosts.
// timeout and timeoutDuration are pointers because only one is required and they are mutually exclusive. timeoutDuration is preferred.
func (r *RTR) BatchActiveResponderCmd(ctx context.Context, timeout *int64, timeoutDuration *time.Duration, hostTimeoutDuration time.Duration,
baseCommand, batchID, commandString string, optionalHosts []string, opts ...real_time_response.ClientOption) (map[string]models.DomainMultiStatusSensorResponse, error) {

var timeoutDurationParam *string = nil
if timeoutDuration != nil {
timeoutDurationParam = falcon_util.StrPtr(timeoutDuration.String())
}
response, err := r.client.BatchActiveResponderCmd(&real_time_response.BatchActiveResponderCmdParams{
HostTimeoutDuration: falcon_util.StrPtr(hostTimeoutDuration.String()),
Timeout: timeout,
TimeoutDuration: timeoutDurationParam,
Body: &models.DomainBatchExecuteCommandRequest{
BaseCommand: &baseCommand,
BatchID: &batchID,
CommandString: &commandString,
OptionalHosts: optionalHosts,
},
Context: ctx,
}, opts...)
if err != nil {
return nil, err
}
if err = AssertNoError(response.Payload.Errors); err != nil {
return nil, err
}
return response.Payload.Combined.Resources, nil
}

// BatchAdminCmd executes an RTR Admin command against a batch of hosts.
// timeout and timeoutDuration are pointers because only one is required and they are mutually exclusive. timeoutDuration is preferred.
func (r *RTR) BatchAdminCmd(ctx context.Context, timeout *int64, timeoutDuration *time.Duration, hostTimeoutDuration time.Duration,
baseCommand, batchID, commandString string, optionalHosts []string, opts ...real_time_response_admin.ClientOption) (map[string]models.DomainMultiStatusSensorResponse, error) {

var timeoutDurationParam *string = nil
if timeoutDuration != nil {
timeoutDurationParam = falcon_util.StrPtr(timeoutDuration.String())
}
response, err := r.adminClient.BatchAdminCmd(&real_time_response_admin.BatchAdminCmdParams{
HostTimeoutDuration: falcon_util.StrPtr(hostTimeoutDuration.String()),
Timeout: timeout,
TimeoutDuration: timeoutDurationParam,
Body: &models.DomainBatchExecuteCommandRequest{
BaseCommand: &baseCommand,
BatchID: &batchID,
CommandString: &commandString,
OptionalHosts: optionalHosts,
},
Context: ctx,
}, opts...)
if err != nil {
return nil, err
}
if err = AssertNoError(response.Payload.Errors); err != nil {
return nil, err
}
return response.Payload.Combined.Resources, nil
}

func (s *RTRSession) ListFiles(ctx context.Context) ([]*models.DomainFileV2, error) {
response, err := s.client.RTRListFilesV2(&real_time_response.RTRListFilesV2Params{
Context: ctx,
Expand Down

0 comments on commit f6e2554

Please sign in to comment.