Skip to content

Commit

Permalink
Merge pull request #1588 from snyk/fea/add_aws_s3_account_public_acce…
Browse files Browse the repository at this point in the history
…ss_block

add support for  aws s3 account public access block
  • Loading branch information
Martin authored Oct 12, 2022
2 parents 3e16c30 + c94dad7 commit a1427f4
Show file tree
Hide file tree
Showing 44 changed files with 4,620 additions and 72 deletions.
41 changes: 40 additions & 1 deletion enumeration/remote/aws/client/mock_AwsClientFactoryInterface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions enumeration/remote/aws/client/s3_client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3iface"
"github.com/aws/aws-sdk-go/service/s3control"
"github.com/aws/aws-sdk-go/service/s3control/s3controliface"
)

type AwsClientFactoryInterface interface {
GetS3Client(configs ...*aws.Config) s3iface.S3API
GetS3ControlClient(configs ...*aws.Config) s3controliface.S3ControlAPI
}

type AwsClientFactory struct {
Expand All @@ -22,3 +25,7 @@ func NewAWSClientFactory(config client.ConfigProvider) *AwsClientFactory {
func (s AwsClientFactory) GetS3Client(configs ...*aws.Config) s3iface.S3API {
return s3.New(s.config, configs...)
}

func (s AwsClientFactory) GetS3ControlClient(configs ...*aws.Config) s3controliface.S3ControlAPI {
return s3control.New(s.config, configs...)
}
4 changes: 3 additions & 1 deletion enumeration/remote/aws/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package aws
import (
"github.com/snyk/driftctl/enumeration"
"github.com/snyk/driftctl/enumeration/alerter"
"github.com/snyk/driftctl/enumeration/remote/aws/client"
client "github.com/snyk/driftctl/enumeration/remote/aws/client"
"github.com/snyk/driftctl/enumeration/remote/aws/repository"
"github.com/snyk/driftctl/enumeration/remote/cache"
"github.com/snyk/driftctl/enumeration/remote/common"
Expand Down Expand Up @@ -35,6 +35,7 @@ func Init(version string, alerter alerter.AlerterInterface, providerLibrary *ter
repositoryCache := cache.New(100)

s3Repository := repository.NewS3Repository(client.NewAWSClientFactory(provider.session), repositoryCache)
s3ControlRepository := repository.NewS3ControlRepository(client.NewAWSClientFactory(provider.session), repositoryCache)
ec2repository := repository.NewEC2Repository(provider.session, repositoryCache)
elbv2Repository := repository.NewELBV2Repository(provider.session, repositoryCache)
route53repository := repository.NewRoute53Repository(provider.session, repositoryCache)
Expand Down Expand Up @@ -71,6 +72,7 @@ func Init(version string, alerter alerter.AlerterInterface, providerLibrary *ter
remoteLibrary.AddEnumerator(NewS3BucketAnalyticEnumerator(s3Repository, factory, provider.Config, alerter))
remoteLibrary.AddDetailsFetcher(aws.AwsS3BucketAnalyticsConfigurationResourceType, common.NewGenericDetailsFetcher(aws.AwsS3BucketAnalyticsConfigurationResourceType, provider, deserializer))
remoteLibrary.AddEnumerator(NewS3BucketPublicAccessBlockEnumerator(s3Repository, factory, provider.Config, alerter))
remoteLibrary.AddEnumerator(NewS3AccountPublicAccessBlockEnumerator(s3ControlRepository, factory, provider.accountId, alerter))

remoteLibrary.AddEnumerator(NewEC2EbsVolumeEnumerator(ec2repository, factory))
remoteLibrary.AddDetailsFetcher(aws.AwsEbsVolumeResourceType, common.NewGenericDetailsFetcher(aws.AwsEbsVolumeResourceType, provider, deserializer))
Expand Down
12 changes: 8 additions & 4 deletions enumeration/remote/aws/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aws

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
Expand Down Expand Up @@ -42,9 +43,10 @@ type awsConfig struct {

type AWSTerraformProvider struct {
*terraform.TerraformProvider
session *session.Session
name string
version string
session *session.Session
name string
version string
accountId string
}

func NewAWSTerraformProvider(version string, progress enumeration.ProgressCounter, configDir string) (*AWSTerraformProvider, error) {
Expand Down Expand Up @@ -115,11 +117,13 @@ func (p *AWSTerraformProvider) CheckCredentialsExist() error {
// This call is to make sure that the credentials are valid
// A more complex logic exist in terraform provider, but it's probably not worth to implement it
// https://github.com/hashicorp/terraform-provider-aws/blob/e3959651092864925045a6044961a73137095798/aws/auth_helpers.go#L111
_, err = sts.New(p.session).GetCallerIdentity(&sts.GetCallerIdentityInput{})
identity, err := sts.New(p.session).GetCallerIdentity(&sts.GetCallerIdentityInput{})
if err != nil {
logrus.Debug(err)
return errors.New("Could not authenticate successfully on AWS with the provided credentials.\n" +
"Please refer to the AWS documentation: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html\n")
}

p.accountId = aws.StringValue(identity.Account)
return nil
}
51 changes: 51 additions & 0 deletions enumeration/remote/aws/repository/mock_S3ControlRepository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions enumeration/remote/aws/repository/s3_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package repository

import (
"fmt"

"github.com/snyk/driftctl/enumeration/remote/aws/client"
"github.com/snyk/driftctl/enumeration/remote/cache"

Expand Down
43 changes: 43 additions & 0 deletions enumeration/remote/aws/repository/s3control_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package repository

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3control"
"github.com/snyk/driftctl/enumeration/remote/aws/client"
"github.com/snyk/driftctl/enumeration/remote/cache"
)

type S3ControlRepository interface {
DescribeAccountPublicAccessBlock(accountID string) (*s3control.PublicAccessBlockConfiguration, error)
}

type s3ControlRepository struct {
clientFactory client.AwsClientFactoryInterface
cache cache.Cache
}

func NewS3ControlRepository(factory client.AwsClientFactoryInterface, c cache.Cache) *s3ControlRepository {
return &s3ControlRepository{
clientFactory: factory,
cache: c,
}
}

func (s *s3ControlRepository) DescribeAccountPublicAccessBlock(accountID string) (*s3control.PublicAccessBlockConfiguration, error) {
cacheKey := "S3DescribeAccountPublicAccessBlock"
if v := s.cache.Get(cacheKey); v != nil {
return v.(*s3control.PublicAccessBlockConfiguration), nil
}
out, err := s.clientFactory.GetS3ControlClient(nil).GetPublicAccessBlock(&s3control.GetPublicAccessBlockInput{
AccountId: aws.String(accountID),
})

if err != nil {
return nil, err
}

result := out.PublicAccessBlockConfiguration

s.cache.Put(cacheKey, result)
return result, nil
}
92 changes: 92 additions & 0 deletions enumeration/remote/aws/repository/s3control_repository_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package repository

import (
"strings"
"testing"

"github.com/aws/aws-sdk-go/service/s3control"
"github.com/snyk/driftctl/enumeration/remote/aws/client"
"github.com/snyk/driftctl/enumeration/remote/cache"
"github.com/stretchr/testify/mock"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/r3labs/diff/v2"
awstest "github.com/snyk/driftctl/test/aws"
"github.com/stretchr/testify/assert"
)

func Test_s3ControlRepository_DescribeAccountPublicAccessBlock(t *testing.T) {
accountID := "123456"

tests := []struct {
name string
mocks func(client *awstest.MockFakeS3Control)
want *s3control.PublicAccessBlockConfiguration
wantErr error
}{
{
name: "describe account public accessblock",
mocks: func(client *awstest.MockFakeS3Control) {
client.On("GetPublicAccessBlock", mock.Anything).Return(
&s3control.GetPublicAccessBlockOutput{
PublicAccessBlockConfiguration: &s3control.PublicAccessBlockConfiguration{
BlockPublicAcls: aws.Bool(false),
BlockPublicPolicy: aws.Bool(true),
IgnorePublicAcls: aws.Bool(false),
RestrictPublicBuckets: aws.Bool(true),
},
},
nil,
).Once()
},
want: &s3control.PublicAccessBlockConfiguration{
BlockPublicAcls: aws.Bool(false),
BlockPublicPolicy: aws.Bool(true),
IgnorePublicAcls: aws.Bool(false),
RestrictPublicBuckets: aws.Bool(true),
},
},
{
name: "Error detting account public accessblock",
mocks: func(client *awstest.MockFakeS3Control) {
client.On("GetPublicAccessBlock", mock.Anything).Return(
nil,
awserr.NewRequestFailure(nil, 403, ""),
).Once()
},
want: nil,
wantErr: awserr.NewRequestFailure(nil, 403, ""),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
store := cache.New(1)
mockedClient := &awstest.MockFakeS3Control{}
tt.mocks(mockedClient)
factory := client.MockAwsClientFactoryInterface{}
factory.On("GetS3ControlClient", (*aws.Config)(nil)).Return(mockedClient).Once()
r := NewS3ControlRepository(&factory, store)
got, err := r.DescribeAccountPublicAccessBlock(accountID)
factory.AssertExpectations(t)
assert.Equal(t, tt.wantErr, err)

if err == nil {
// Check that results were cached
cachedData, err := r.DescribeAccountPublicAccessBlock(accountID)
assert.NoError(t, err)
assert.Equal(t, got, cachedData)
assert.IsType(t, &s3control.PublicAccessBlockConfiguration{}, store.Get("S3DescribeAccountPublicAccessBlock"))
}

changelog, err := diff.Diff(got, tt.want)
assert.Nil(t, err)
if len(changelog) > 0 {
for _, change := range changelog {
t.Errorf("%s: %s -> %s", strings.Join(change.Path, "."), change.From, change.To)
}
t.Fail()
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package aws

import (
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/snyk/driftctl/enumeration/alerter"
"github.com/snyk/driftctl/enumeration/remote/aws/repository"
remoteerror "github.com/snyk/driftctl/enumeration/remote/error"
"github.com/snyk/driftctl/enumeration/resource"
"github.com/snyk/driftctl/enumeration/resource/aws"
)

type S3AccountPublicAccessBlockEnumerator struct {
repository repository.S3ControlRepository
factory resource.ResourceFactory
accountID string
alerter alerter.AlerterInterface
}

func NewS3AccountPublicAccessBlockEnumerator(repo repository.S3ControlRepository, factory resource.ResourceFactory, accountId string, alerter alerter.AlerterInterface) *S3AccountPublicAccessBlockEnumerator {
return &S3AccountPublicAccessBlockEnumerator{
repository: repo,
factory: factory,
accountID: accountId,
alerter: alerter,
}
}

func (e *S3AccountPublicAccessBlockEnumerator) SupportedType() resource.ResourceType {
return aws.AwsS3AccountPublicAccessBlock
}

func (e *S3AccountPublicAccessBlockEnumerator) Enumerate() ([]*resource.Resource, error) {
accountPublicAccessBlock, err := e.repository.DescribeAccountPublicAccessBlock(e.accountID)
if err != nil {
return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType()))
}

results := make([]*resource.Resource, 0, 1)

results = append(
results,
e.factory.CreateAbstractResource(
string(e.SupportedType()),
e.accountID,
map[string]interface{}{
"block_public_acls": awssdk.BoolValue(accountPublicAccessBlock.BlockPublicAcls),
"block_public_policy": awssdk.BoolValue(accountPublicAccessBlock.BlockPublicPolicy),
"ignore_public_acls": awssdk.BoolValue(accountPublicAccessBlock.IgnorePublicAcls),
"restrict_public_buckets": awssdk.BoolValue(accountPublicAccessBlock.RestrictPublicBuckets),
},
),
)

return results, err
}
Loading

0 comments on commit a1427f4

Please sign in to comment.