Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Shield Tables #2315

Merged
merged 41 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1ad40ee
Adding `aws_shield_protection` table
dbermuehler Sep 26, 2024
43f9053
Adding basic documentation
dbermuehler Sep 26, 2024
256a805
Creating Default-Region-Client
dbermuehler Sep 26, 2024
b07cd98
Fixed empty result bug
dbermuehler Sep 26, 2024
221cae5
Adding resourceArn as key column for Get function
dbermuehler Sep 26, 2024
4e5dd69
Filtering for resource_type, resource_arn and protection name possibl…
dbermuehler Sep 26, 2024
ba6e4d6
Adding DRT-Access, Emergency-Contact and Subscription table
dbermuehler Sep 26, 2024
c64413b
Changed akas transform function
dbermuehler Sep 26, 2024
3ea0895
Adding attack statistic table
dbermuehler Sep 27, 2024
767cc7e
Adding protection group table
dbermuehler Sep 27, 2024
756bf4c
Removing log statements
dbermuehler Sep 27, 2024
b104a45
Adding tag support for the protection and protection group table
dbermuehler Sep 27, 2024
5340c5b
Typo
dbermuehler Sep 27, 2024
164fddd
Adding attack table
dbermuehler Sep 27, 2024
55ee9a6
Correcting key column
dbermuehler Sep 27, 2024
8dfefe6
Adding attack summary table
dbermuehler Sep 27, 2024
772faf4
Bug fixes
dbermuehler Sep 27, 2024
1d4eaff
Adding comments to follow coding standard
dbermuehler Sep 27, 2024
68efc36
Adding subscription state to subscription table
dbermuehler Sep 27, 2024
294aeff
Adding documentation
dbermuehler Sep 27, 2024
3e7e59c
Adding artificial priority column to emergency contact list
dbermuehler Sep 30, 2024
f10414c
If never enabled the ProactiveEngagementStatus can be empty -> mappin…
dbermuehler Oct 1, 2024
a8c2b32
Apply suggestions from code review
dbermuehler Oct 1, 2024
b2f1a5b
Sorting tables in alphabetic order
dbermuehler Oct 1, 2024
e18fabe
Merge branch 'main' of github.com:dbermuehler/steampipe-plugin-aws
dbermuehler Oct 1, 2024
cc7b3a9
Adding error handling and remove redundant transform function
dbermuehler Oct 1, 2024
0b6fbe4
Changing column to subscription_state
dbermuehler Oct 1, 2024
64a12f1
Correcting Column Type
dbermuehler Oct 1, 2024
8be7416
Merging aws_shield_attack_summary and aws_shield_attack tables into one
dbermuehler Oct 1, 2024
283141f
Fixing small bug
dbermuehler Oct 2, 2024
92852b1
Improving timerange code
dbermuehler Oct 2, 2024
2238a02
Downgraded Shield package to not upgrade aws-sdk-go-v2 version
dbermuehler Oct 8, 2024
2a29426
Addressing PR comments
dbermuehler Oct 15, 2024
792d40d
Fixed indentation
dbermuehler Oct 15, 2024
d943d28
Adding more queries to the documentation
dbermuehler Oct 15, 2024
fb973d4
Removing start_time and end_time qualifier
dbermuehler Oct 16, 2024
8bd8673
Apply suggestions from code review
dbermuehler Oct 24, 2024
7f54f08
Added LIST FUNCTIONS comment
dbermuehler Oct 24, 2024
e5394dc
Added more SQL examples that contain joins
dbermuehler Oct 24, 2024
1aa4c10
Fixing bug in documentation
dbermuehler Oct 28, 2024
bc972ce
Fixing documentation
dbermuehler Oct 28, 2024
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
7 changes: 7 additions & 0 deletions aws/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,13 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_sfn_state_machine": tableAwsStepFunctionsStateMachine(ctx),
"aws_sfn_state_machine_execution": tableAwsStepFunctionsStateMachineExecution(ctx),
"aws_sfn_state_machine_execution_history": tableAwsStepFunctionsStateMachineExecutionHistory(ctx),
"aws_shield_attack": tableAwsShieldAttack(ctx),
"aws_shield_attack_statistic": tableAwsShieldAttackStatistic(ctx),
"aws_shield_drt_access": tableAwsShieldDRTAccess(ctx),
"aws_shield_emergency_contact": tableAwsShieldEmergencyContact(ctx),
"aws_shield_protection": tableAwsShieldProtection(ctx),
"aws_shield_protection_group": tableAwsShieldProtectionGroup(ctx),
"aws_shield_subscription": tableAwsShieldSubscription(ctx),
"aws_simspaceweaver_simulation": tableAwsSimSpaceWeaverSimulation(ctx),
"aws_sns_subscription": tableAwsSnsSubscription(ctx),
"aws_sns_topic": tableAwsSnsTopic(ctx),
Expand Down
13 changes: 13 additions & 0 deletions aws/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/servicequotas"
"github.com/aws/aws-sdk-go-v2/service/ses"
"github.com/aws/aws-sdk-go-v2/service/sfn"
"github.com/aws/aws-sdk-go-v2/service/shield"
"github.com/aws/aws-sdk-go-v2/service/simspaceweaver"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/aws/aws-sdk-go-v2/service/sqs"
Expand Down Expand Up @@ -1494,6 +1495,18 @@ func StepFunctionsClient(ctx context.Context, d *plugin.QueryData) (*sfn.Client,
return sfn.NewFromConfig(*cfg), nil
}

func ShieldClient(ctx context.Context, d *plugin.QueryData) (*shield.Client, error) {
cfg, err := getClientForDefaultRegion(ctx, d)
if err != nil {
return nil, err
}
dbermuehler marked this conversation as resolved.
Show resolved Hide resolved

if cfg == nil {
return nil, nil
}
return shield.NewFromConfig(*cfg), nil
}

func SNSClient(ctx context.Context, d *plugin.QueryData) (*sns.Client, error) {
cfg, err := getClientForQueryRegion(ctx, d)
if err != nil {
Expand Down
242 changes: 242 additions & 0 deletions aws/table_aws_shield_attack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package aws

import (
"context"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/shield"
"github.com/aws/aws-sdk-go-v2/service/shield/types"

"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
)

//// TABLE DEFINITION

func tableAwsShieldAttack(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_shield_attack",
Description: "AWS Shield Attack",
Get: &plugin.GetConfig{
Hydrate: getAttack,
KeyColumns: plugin.SingleColumn("attack_id"),
Tags: map[string]string{"service": "shield", "action": "DescribeAttack"},
},
List: &plugin.ListConfig{
Hydrate: listAttacks,
KeyColumns: []*plugin.KeyColumn{
{
Name: "resource_arn",
Require: plugin.Optional,
Operators: []string{"="},
},
},
Tags: map[string]string{"service": "shield", "action": "ListAttacks"},
},
Columns: awsGlobalRegionColumns([]*plugin.Column{
{
Name: "attack_id",
Description: "The unique identifier (ID) of the attack.",
Type: proto.ColumnType_STRING,
},
{
Name: "resource_arn",
ParthaI marked this conversation as resolved.
Show resolved Hide resolved
Description: "The ARN (Amazon Resource Name) of the Amazon Web Services resource that was attacked.",
Type: proto.ColumnType_STRING,
},
{
Name: "start_time",
Description: "The start time of the attack.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "end_time",
Description: "The end time of the attack.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "attack_vectors",
Description: "The list of attacks for the time period.",
Type: proto.ColumnType_JSON,
},
{
Name: "sub_resources",
Description: "If applicable, additional detail about the resource being attacked, for example, IP address or URL.",
Hydrate: getAttack,
Type: proto.ColumnType_JSON,
},
{
Name: "attack_counters",
Description: "List of counters that describe the attack for the specified time period.",
Hydrate: getAttack,
Type: proto.ColumnType_JSON,
},
{
Name: "attack_properties",
Description: "The array of objects that provide details of the Shield event.",
Hydrate: getAttack,
Type: proto.ColumnType_JSON,
},
{
Name: "mitigations",
Description: "List of mitigation actions taken for the attack.",
Hydrate: getAttack,
Type: proto.ColumnType_JSON,
},
// Steampipe standard columns
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Transform: transform.FromField("AttackId"),
},
ParthaI marked this conversation as resolved.
Show resolved Hide resolved
}),
}
}

//// HYDRATE FUNCTIONS
dbermuehler marked this conversation as resolved.
Show resolved Hide resolved

func listAttacks(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
// Create session
svc, err := ShieldClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack.listAttacks", "connection_error", err)
return nil, err
}

if svc == nil {
// Unsupported region, return no data
return nil, nil
}

// Limiting the results
queryResultLimit := int32(1000)
if d.QueryContext.Limit != nil {
queryResultLimit = min(queryResultLimit, int32(*d.QueryContext.Limit))
}

input := &shield.ListAttacksInput{
MaxResults: aws.Int32(queryResultLimit),
}

if d.Quals["resource_arn"] != nil {
input.ResourceArns = []string{}
for _, q := range d.Quals["resource_arn"].Quals {
input.ResourceArns = append(input.ResourceArns, q.Value.GetStringValue())
}
}

paginator := shield.NewListAttacksPaginator(svc, input, func(o *shield.ListAttacksPaginatorOptions) {
o.Limit = queryResultLimit
o.StopOnDuplicateToken = true
})

// List call
for paginator.HasMorePages() {
// apply rate limiting
d.WaitForListRateLimit(ctx)

output, err := paginator.NextPage(ctx)
if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack.listAttacks", "api_error", err)
return nil, err
}

for _, items := range output.AttackSummaries {
d.StreamListItem(ctx, &items)
// Context can be cancelled due to manual cancellation or the limit has been hit
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil
}

func getAttack(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
dbermuehler marked this conversation as resolved.
Show resolved Hide resolved
var attackId string
if h.Item != nil {
attackId = *h.Item.(*types.AttackSummary).AttackId
} else {
attackId = d.EqualsQualString("attack_id")
}

if attackId == "" {
return nil, nil
}

// Create session
svc, err := ShieldClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack.getAttack", "connection_error", err)
return nil, err
}

if svc == nil {
// Unsupported region, return no data
return nil, nil
}

data, err := svc.DescribeAttack(ctx, &shield.DescribeAttackInput{
AttackId: aws.String(attackId),
})

if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack.getAttack", "api_error", err)
return nil, err
}

if data.Attack != nil {
return &AttackExtended{
AttackVectors: getAttackVectors(*data),
AttackCounters: data.Attack.AttackCounters,
AttackId: data.Attack.AttackId,
AttackProperties: data.Attack.AttackProperties,
EndTime: data.Attack.EndTime,
Mitigations: data.Attack.Mitigations,
ResourceArn: data.Attack.ResourceArn,
StartTime: data.Attack.StartTime,
SubResources: data.Attack.SubResources,
}, nil
}

return nil, nil
}

//// HELPER FUNCTIONS

type AttackExtended struct {
AttackVectors []types.AttackVectorDescription
AttackCounters []types.SummarizedCounter
AttackId *string
AttackProperties []types.AttackProperty
EndTime *time.Time
Mitigations []types.Mitigation
ResourceArn *string
StartTime *time.Time
SubResources []types.SubResourceSummary
}

func getAttackVectors(attack shield.DescribeAttackOutput) ([]types.AttackVectorDescription) {
attackVectors := []types.AttackVectorDescription{}

if attack.Attack.SubResources == nil {
return attackVectors
}

for _, subResource := range attack.Attack.SubResources {
if subResource.AttackVectors == nil {
continue
}
for _, attackVector := range subResource.AttackVectors {
attackVectors = append(attackVectors, types.AttackVectorDescription{
VectorType: attackVector.VectorType,
})
}
}

return attackVectors
}
114 changes: 114 additions & 0 deletions aws/table_aws_shield_attack_statistic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package aws

import (
"context"
"time"

"github.com/aws/aws-sdk-go-v2/service/shield"

"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
)

//// TABLE DEFINITION

func tableAwsShieldAttackStatistic(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_shield_attack_statistic",
Description: "AWS Shield Attack Statistic",
List: &plugin.ListConfig{
Hydrate: listAwsShieldAttackStatistic,
Tags: map[string]string{"service": "shield", "action": "DescribeAttackStatistics"},
},
Columns: awsGlobalRegionColumns([]*plugin.Column{
{
Name: "start_time",
Description: "The start time of observation time range (should be always one year ago).",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "end_time",
Description: "The end time of the observation time range (should be always the current date).",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "unit",
Description: "Unit of the attack statistic.",
Type: proto.ColumnType_STRING,
},
{
Name: "max",
Description: "The maximum attack volume observed in the observation time range for the given unit.",
Type: proto.ColumnType_DOUBLE,
},
{
Name: "attack_count",
Description: "The number of attacks detected during the time period. This is always present, but might be zero.",
Type: proto.ColumnType_INT,
},
}),
}
}

type attackStatistic struct {
StartTime time.Time
EndTime time.Time
Unit string
Max float64
AttackCount int64
}

//// HYDRATE FUNCTIONS

func listAwsShieldAttackStatistic(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
// Create session
svc, err := ShieldClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack_statistic.listAwsShieldAttackStatistic", "connection_error", err)
return nil, err
}

if svc == nil {
// Unsupported region, return no data
return nil, nil
}

data, err := svc.DescribeAttackStatistics(ctx, &shield.DescribeAttackStatisticsInput{})

if err != nil {
plugin.Logger(ctx).Error("aws_shield_attack_statistic.getAwsShieldAttackStatistic", "api_error", err)
return nil, err
}

for _, stat := range data.DataItems {
var unit string
var max float64

if stat.AttackCount == 0 {
continue
} else if stat.AttackVolume.BitsPerSecond != nil {
unit = "BitsPerSecond"
max = stat.AttackVolume.BitsPerSecond.Max
} else if stat.AttackVolume.PacketsPerSecond != nil {
unit = "PacketsPerSecond"
max = stat.AttackVolume.PacketsPerSecond.Max
} else if stat.AttackVolume.RequestsPerSecond != nil {
unit = "RequestsPerSecond"
max = stat.AttackVolume.RequestsPerSecond.Max
}

d.StreamListItem(ctx, &attackStatistic{
StartTime: *data.TimeRange.FromInclusive,
EndTime: *data.TimeRange.ToExclusive,
Unit: unit,
Max: max,
AttackCount: stat.AttackCount,
})

// Context can be cancelled due to manual cancellation or the limit has been hit
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
return nil, nil
}
Loading