Skip to content

Commit

Permalink
feat: add check for all vpcs need flow logs (#885)
Browse files Browse the repository at this point in the history
* feat: add check for all vpcs need flow logs

Resolves #886

Signed-off-by: Owen Rumney <[email protected]>

* docs: add the remediation documentation

Signed-off-by: Owen Rumney <[email protected]>

* remove missing property ref

* fix conflict issues

* fix schema

Signed-off-by: Owen Rumney <[email protected]>
Co-authored-by: Liam Galvin <[email protected]>
  • Loading branch information
Owen Rumney and liamg authored Oct 12, 2022
1 parent 2a9033a commit 43b72f0
Show file tree
Hide file tree
Showing 14 changed files with 293 additions and 29 deletions.
10 changes: 10 additions & 0 deletions avd_docs/aws/ec2/AVD-AWS-0164/Management_Console.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
1. Sign into the management console
2. Select Services then VPC
3. In the left navigation pane, select Your VPCs
4. Select a VPC
5. In the right pane, select the Flow Logs tab.
6. If no Flow Log exists, click Create Flow Log
7. For Filter, select Reject
8. Enter in a Role and Destination Log Group
9. Click Create Log Flow
10. Click on CloudWatch Logs Group
5 changes: 5 additions & 0 deletions avd_docs/aws/ec2/AVD-AWS-0178/CloudFormation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

Enable flow logs for VPC



5 changes: 5 additions & 0 deletions avd_docs/aws/ec2/AVD-AWS-0178/Terraform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

Enable flow logs for VPC



13 changes: 13 additions & 0 deletions avd_docs/aws/ec2/AVD-AWS-0178/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

VPC Flow Logs provide visibility into network traffic that traverses the VPC and can be used to detect anomalous traffic or insight during security workflows.

### Impact
Without VPC flow logs, you risk not having enough information about network traffic flow to investigate incidents or identify security issues.

<!-- DO NOT CHANGE -->
{{ remediationActions }}

### Links
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/RootDeviceStorage.html


2 changes: 1 addition & 1 deletion avd_docs/aws/rds/AVD-AWS-0176/docs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

Ensure IAM Database Authentication is enabled for RDS database instances to manage database acces
Ensure IAM Database Authentication is enabled for RDS database instances to manage database access

### Impact
<!-- Add Impact here -->
Expand Down
37 changes: 31 additions & 6 deletions internal/adapters/cloud/aws/ec2/vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,36 @@ func (a *adapter) adaptNetworkACL(apiNacl types.NetworkAcl) (*ec2.NetworkACL, er
}

func (a *adapter) adaptVPC(v types.Vpc) (*ec2.VPC, error) {

vpcMetadata := a.CreateMetadata("vpc/" + *v.VpcId)
return &ec2.VPC{
Metadata: vpcMetadata,
ID: defsecTypes.String(*v.VpcId, vpcMetadata),
IsDefault: defsecTypes.Bool(v.IsDefault != nil && *v.IsDefault, vpcMetadata),
SecurityGroups: nil, // we link these up afterwards
}, nil
vpc := &ec2.VPC{
Metadata: vpcMetadata,
ID: defsecTypes.String(*v.VpcId, vpcMetadata),
IsDefault: defsecTypes.BoolDefault(false, vpcMetadata),
FlowLogsEnabled: defsecTypes.BoolDefault(false, vpcMetadata),
SecurityGroups: nil, // we link these up afterwards
}

if v.IsDefault != nil {
vpc.IsDefault = defsecTypes.BoolDefault(*v.IsDefault, vpcMetadata)
}

logs, err := a.client.DescribeFlowLogs(a.Context(), &ec2api.DescribeFlowLogsInput{
Filter: []types.Filter{
{
Name: aws.String("resource-id"),
Values: []string{*v.VpcId},
},
},
})
if err != nil {
return nil, err
}

if logs != nil && len(logs.FlowLogs) > 0 {
vpc.FlowLogsEnabled = defsecTypes.BoolDefault(true, vpcMetadata)
}

return vpc, nil

}
86 changes: 82 additions & 4 deletions internal/adapters/cloud/aws/ec2/vpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

aws2 "github.com/aquasecurity/defsec/internal/adapters/cloud/aws"
"github.com/aquasecurity/defsec/internal/adapters/cloud/aws/test"
"github.com/aquasecurity/defsec/pkg/providers/aws/ec2"
"github.com/aquasecurity/defsec/pkg/state"
"github.com/aws/aws-sdk-go-v2/aws"
vpcApi "github.com/aws/aws-sdk-go-v2/service/ec2"
Expand Down Expand Up @@ -32,8 +33,9 @@ type sg struct {
}

type vpcDetails struct {
nacl *nacl
securityGroup *sg
nacl *nacl
securityGroup *sg
flowLogsEnabled bool
}

func Test_VPCNetworkACLs(t *testing.T) {
Expand Down Expand Up @@ -92,6 +94,51 @@ func Test_VPCNetworkACLs(t *testing.T) {
}
}

func Test_VPCFlowLogs(t *testing.T) {

tests := []struct {
name string
details vpcDetails
}{
{
name: "simple flow logs",
details: vpcDetails{
flowLogsEnabled: true,
},
},
}

ra, stack, err := test.CreateLocalstackAdapter(t)
defer func() { _ = stack.Stop() }()
require.NoError(t, err)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
vpcId := bootstrapVPC(t, ra, tt.details)

testState := &state.State{}
adapter := &adapter{}
err = adapter.Adapt(ra, testState)
require.NoError(t, err)

require.NotNil(t, testState.AWS.EC2)
var testVPCs []ec2.VPC
for _, v := range testState.AWS.EC2.VPCs {
if v.IsDefault.IsFalse() {
testVPCs = append(testVPCs, v)
}
}

require.Len(t, testVPCs, 1)
vpc := testVPCs[0]
require.Equal(t, tt.details.flowLogsEnabled, vpc.FlowLogsEnabled.Value())

destroyVPC(t, ra, vpcId)

})
}
}

func Test_VPCSecurityGroups(t *testing.T) {

tests := []struct {
Expand Down Expand Up @@ -124,7 +171,7 @@ func Test_VPCSecurityGroups(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bootstrapVPC(t, ra, tt.details)
vpcId := bootstrapVPC(t, ra, tt.details)

testState := &state.State{}
adapter := &adapter{}
Expand All @@ -137,17 +184,30 @@ func Test_VPCSecurityGroups(t *testing.T) {
sg := testState.AWS.EC2.SecurityGroups[0]
require.NotNil(t, sg)

destroyVPC(t, ra, vpcId)

})
}
}

func bootstrapVPC(t *testing.T, ra *aws2.RootAdapter, spec vpcDetails) {
func destroyVPC(t *testing.T, ra *aws2.RootAdapter, id *string) {
api := vpcApi.NewFromConfig(ra.SessionConfig())

_, err := api.DeleteVpc(ra.Context(), &vpcApi.DeleteVpcInput{
VpcId: id,
})

require.NoError(t, err)
}

func bootstrapVPC(t *testing.T, ra *aws2.RootAdapter, spec vpcDetails) *string {

api := vpcApi.NewFromConfig(ra.SessionConfig())

vpc, err := api.CreateVpc(ra.Context(), &vpcApi.CreateVpcInput{
CidrBlock: aws.String("10.0.0.0/16"),
})

require.NoError(t, err)

if spec.nacl != nil {
Expand All @@ -157,6 +217,24 @@ func bootstrapVPC(t *testing.T, ra *aws2.RootAdapter, spec vpcDetails) {
if spec.securityGroup != nil {
addSecurityGroup(t, ra, spec, api, vpc)
}

if spec.flowLogsEnabled {
addFlowLogs(t, ra, api, vpc)
}

return vpc.Vpc.VpcId
}

func addFlowLogs(t *testing.T, ra *aws2.RootAdapter, api *vpcApi.Client, vpc *vpcApi.CreateVpcOutput) {
logs, err := api.CreateFlowLogs(ra.Context(), &vpcApi.CreateFlowLogsInput{
ResourceIds: []string{*vpc.Vpc.VpcId},
ResourceType: vpcTypes.FlowLogsResourceTypeVpc,
LogDestinationType: vpcTypes.LogDestinationTypeS3,
LogDestination: aws.String("arn:aws:s3:::access-logs"),
})

require.NoError(t, err)
require.NotNil(t, logs)
}

func addNacl(t *testing.T, ra *aws2.RootAdapter, spec vpcDetails, api *vpcApi.Client, vpc *vpcApi.CreateVpcOutput) {
Expand Down
18 changes: 10 additions & 8 deletions internal/adapters/terraform/aws/ec2/vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@ func adaptVPCs(modules terraform.Modules) []ec2.VPC {
for _, module := range modules {
for _, resource := range module.GetResourcesByType("aws_default_vpc") {
vpcs = append(vpcs, ec2.VPC{
Metadata: resource.GetMetadata(),
ID: defsecTypes.StringUnresolvable(resource.GetMetadata()),
IsDefault: defsecTypes.Bool(true, resource.GetMetadata()),
SecurityGroups: nil,
Metadata: resource.GetMetadata(),
ID: defsecTypes.StringUnresolvable(resource.GetMetadata()),
IsDefault: defsecTypes.Bool(true, resource.GetMetadata()),
SecurityGroups: nil,
FlowLogsEnabled: defsecTypes.BoolDefault(false, resource.GetMetadata()),
})
}
for _, resource := range module.GetResourcesByType("aws_vpc") {
vpcs = append(vpcs, ec2.VPC{
Metadata: resource.GetMetadata(),
ID: defsecTypes.StringUnresolvable(resource.GetMetadata()),
IsDefault: defsecTypes.Bool(false, resource.GetMetadata()),
SecurityGroups: nil,
Metadata: resource.GetMetadata(),
ID: defsecTypes.StringUnresolvable(resource.GetMetadata()),
IsDefault: defsecTypes.Bool(false, resource.GetMetadata()),
SecurityGroups: nil,
FlowLogsEnabled: defsecTypes.BoolDefault(false, resource.GetMetadata()),
})
}
}
Expand Down
14 changes: 8 additions & 6 deletions internal/adapters/terraform/aws/ec2/vpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,16 @@ func Test_AdaptVPC(t *testing.T) {
expected: ec2.EC2{
VPCs: []ec2.VPC{
{
Metadata: defsecTypes.NewTestMetadata(),
IsDefault: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
ID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
Metadata: defsecTypes.NewTestMetadata(),
IsDefault: defsecTypes.Bool(true, defsecTypes.NewTestMetadata()),
ID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
FlowLogsEnabled: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
},
{
Metadata: defsecTypes.NewTestMetadata(),
IsDefault: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
ID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
Metadata: defsecTypes.NewTestMetadata(),
IsDefault: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
ID: defsecTypes.String("", defsecTypes.NewTestMetadata()),
FlowLogsEnabled: defsecTypes.Bool(false, defsecTypes.NewTestMetadata()),
},
},
SecurityGroups: []ec2.SecurityGroup{
Expand Down
12 changes: 12 additions & 0 deletions internal/rules/aws/ec2/no_default_vpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ func TestCheckNoDefaultVpc(t *testing.T) {
},
expected: true,
},
{
name: "vpc but not default AWS VPC",
input: ec2.EC2{
VPCs: []ec2.VPC{
{
Metadata: types.NewTestMetadata(),
IsDefault: types.Bool(false, types.NewTestMetadata()),
},
},
},
expected: false,
},
{
name: "no default AWS VPC",
input: ec2.EC2{},
Expand Down
39 changes: 39 additions & 0 deletions internal/rules/aws/ec2/require_vpc_flow_logs_for_all_vpcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ec2

import (
"github.com/aquasecurity/defsec/internal/rules"
"github.com/aquasecurity/defsec/pkg/providers"
"github.com/aquasecurity/defsec/pkg/scan"
"github.com/aquasecurity/defsec/pkg/severity"
"github.com/aquasecurity/defsec/pkg/state"
)

var CheckRequireVPCFlowLogs = rules.Register(
scan.Rule{
AVDID: "AVD-AWS-0178",
Aliases: []string{"aws-autoscaling-enable-at-rest-encryption"},
Provider: providers.AWSProvider,
Service: "ec2",
ShortCode: "require-vpc-flow-logs-for-all-vpcs",
Summary: `VPC Flow Logs is a feature that enables you to capture information about the IP traffic going to and from network interfaces in your VPC. After you've created a flow log, you can view and retrieve its data in Amazon CloudWatch Logs. It is recommended that VPC Flow Logs be enabled for packet "Rejects" for VPCs.`,
Impact: "Without VPC flow logs, you risk not having enough information about network traffic flow to investigate incidents or identify security issues.",
Resolution: "Enable flow logs for VPC",
Explanation: `VPC Flow Logs provide visibility into network traffic that traverses the VPC and can be used to detect anomalous traffic or insight during security workflows.`,
Links: []string{
"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/RootDeviceStorage.html",
},
Terraform: &scan.EngineMetadata{},
CloudFormation: &scan.EngineMetadata{},
Severity: severity.Medium,
},
func(s *state.State) (results scan.Results) {
for _, vpc := range s.AWS.EC2.VPCs {
if vpc.FlowLogsEnabled.IsFalse() {
results.Add("VPC Flow Logs is not enabled for VPC "+vpc.ID.Value(), vpc)
} else {
results.AddPassed(vpc)
}
}
return
},
)
Loading

0 comments on commit 43b72f0

Please sign in to comment.