Skip to content

Commit a1427f4

Browse files
author
Martin
authored
Merge pull request #1588 from snyk/fea/add_aws_s3_account_public_access_block
add support for aws s3 account public access block
2 parents 3e16c30 + c94dad7 commit a1427f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+4620
-72
lines changed

enumeration/remote/aws/client/mock_AwsClientFactoryInterface.go

Lines changed: 40 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enumeration/remote/aws/client/s3_client_factory.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import (
55
"github.com/aws/aws-sdk-go/aws/client"
66
"github.com/aws/aws-sdk-go/service/s3"
77
"github.com/aws/aws-sdk-go/service/s3/s3iface"
8+
"github.com/aws/aws-sdk-go/service/s3control"
9+
"github.com/aws/aws-sdk-go/service/s3control/s3controliface"
810
)
911

1012
type AwsClientFactoryInterface interface {
1113
GetS3Client(configs ...*aws.Config) s3iface.S3API
14+
GetS3ControlClient(configs ...*aws.Config) s3controliface.S3ControlAPI
1215
}
1316

1417
type AwsClientFactory struct {
@@ -22,3 +25,7 @@ func NewAWSClientFactory(config client.ConfigProvider) *AwsClientFactory {
2225
func (s AwsClientFactory) GetS3Client(configs ...*aws.Config) s3iface.S3API {
2326
return s3.New(s.config, configs...)
2427
}
28+
29+
func (s AwsClientFactory) GetS3ControlClient(configs ...*aws.Config) s3controliface.S3ControlAPI {
30+
return s3control.New(s.config, configs...)
31+
}

enumeration/remote/aws/init.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package aws
33
import (
44
"github.com/snyk/driftctl/enumeration"
55
"github.com/snyk/driftctl/enumeration/alerter"
6-
"github.com/snyk/driftctl/enumeration/remote/aws/client"
6+
client "github.com/snyk/driftctl/enumeration/remote/aws/client"
77
"github.com/snyk/driftctl/enumeration/remote/aws/repository"
88
"github.com/snyk/driftctl/enumeration/remote/cache"
99
"github.com/snyk/driftctl/enumeration/remote/common"
@@ -35,6 +35,7 @@ func Init(version string, alerter alerter.AlerterInterface, providerLibrary *ter
3535
repositoryCache := cache.New(100)
3636

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

7577
remoteLibrary.AddEnumerator(NewEC2EbsVolumeEnumerator(ec2repository, factory))
7678
remoteLibrary.AddDetailsFetcher(aws.AwsEbsVolumeResourceType, common.NewGenericDetailsFetcher(aws.AwsEbsVolumeResourceType, provider, deserializer))

enumeration/remote/aws/provider.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package aws
22

33
import (
4+
"github.com/aws/aws-sdk-go/aws"
45
"github.com/aws/aws-sdk-go/aws/credentials"
56
"github.com/aws/aws-sdk-go/aws/session"
67
"github.com/aws/aws-sdk-go/service/sts"
@@ -42,9 +43,10 @@ type awsConfig struct {
4243

4344
type AWSTerraformProvider struct {
4445
*terraform.TerraformProvider
45-
session *session.Session
46-
name string
47-
version string
46+
session *session.Session
47+
name string
48+
version string
49+
accountId string
4850
}
4951

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

enumeration/remote/aws/repository/mock_S3ControlRepository.go

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enumeration/remote/aws/repository/s3_repository.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package repository
22

33
import (
44
"fmt"
5+
56
"github.com/snyk/driftctl/enumeration/remote/aws/client"
67
"github.com/snyk/driftctl/enumeration/remote/cache"
78

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package repository
2+
3+
import (
4+
"github.com/aws/aws-sdk-go/aws"
5+
"github.com/aws/aws-sdk-go/service/s3control"
6+
"github.com/snyk/driftctl/enumeration/remote/aws/client"
7+
"github.com/snyk/driftctl/enumeration/remote/cache"
8+
)
9+
10+
type S3ControlRepository interface {
11+
DescribeAccountPublicAccessBlock(accountID string) (*s3control.PublicAccessBlockConfiguration, error)
12+
}
13+
14+
type s3ControlRepository struct {
15+
clientFactory client.AwsClientFactoryInterface
16+
cache cache.Cache
17+
}
18+
19+
func NewS3ControlRepository(factory client.AwsClientFactoryInterface, c cache.Cache) *s3ControlRepository {
20+
return &s3ControlRepository{
21+
clientFactory: factory,
22+
cache: c,
23+
}
24+
}
25+
26+
func (s *s3ControlRepository) DescribeAccountPublicAccessBlock(accountID string) (*s3control.PublicAccessBlockConfiguration, error) {
27+
cacheKey := "S3DescribeAccountPublicAccessBlock"
28+
if v := s.cache.Get(cacheKey); v != nil {
29+
return v.(*s3control.PublicAccessBlockConfiguration), nil
30+
}
31+
out, err := s.clientFactory.GetS3ControlClient(nil).GetPublicAccessBlock(&s3control.GetPublicAccessBlockInput{
32+
AccountId: aws.String(accountID),
33+
})
34+
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
result := out.PublicAccessBlockConfiguration
40+
41+
s.cache.Put(cacheKey, result)
42+
return result, nil
43+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package repository
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/aws/aws-sdk-go/service/s3control"
8+
"github.com/snyk/driftctl/enumeration/remote/aws/client"
9+
"github.com/snyk/driftctl/enumeration/remote/cache"
10+
"github.com/stretchr/testify/mock"
11+
12+
"github.com/aws/aws-sdk-go/aws"
13+
"github.com/aws/aws-sdk-go/aws/awserr"
14+
"github.com/r3labs/diff/v2"
15+
awstest "github.com/snyk/driftctl/test/aws"
16+
"github.com/stretchr/testify/assert"
17+
)
18+
19+
func Test_s3ControlRepository_DescribeAccountPublicAccessBlock(t *testing.T) {
20+
accountID := "123456"
21+
22+
tests := []struct {
23+
name string
24+
mocks func(client *awstest.MockFakeS3Control)
25+
want *s3control.PublicAccessBlockConfiguration
26+
wantErr error
27+
}{
28+
{
29+
name: "describe account public accessblock",
30+
mocks: func(client *awstest.MockFakeS3Control) {
31+
client.On("GetPublicAccessBlock", mock.Anything).Return(
32+
&s3control.GetPublicAccessBlockOutput{
33+
PublicAccessBlockConfiguration: &s3control.PublicAccessBlockConfiguration{
34+
BlockPublicAcls: aws.Bool(false),
35+
BlockPublicPolicy: aws.Bool(true),
36+
IgnorePublicAcls: aws.Bool(false),
37+
RestrictPublicBuckets: aws.Bool(true),
38+
},
39+
},
40+
nil,
41+
).Once()
42+
},
43+
want: &s3control.PublicAccessBlockConfiguration{
44+
BlockPublicAcls: aws.Bool(false),
45+
BlockPublicPolicy: aws.Bool(true),
46+
IgnorePublicAcls: aws.Bool(false),
47+
RestrictPublicBuckets: aws.Bool(true),
48+
},
49+
},
50+
{
51+
name: "Error detting account public accessblock",
52+
mocks: func(client *awstest.MockFakeS3Control) {
53+
client.On("GetPublicAccessBlock", mock.Anything).Return(
54+
nil,
55+
awserr.NewRequestFailure(nil, 403, ""),
56+
).Once()
57+
},
58+
want: nil,
59+
wantErr: awserr.NewRequestFailure(nil, 403, ""),
60+
},
61+
}
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
store := cache.New(1)
65+
mockedClient := &awstest.MockFakeS3Control{}
66+
tt.mocks(mockedClient)
67+
factory := client.MockAwsClientFactoryInterface{}
68+
factory.On("GetS3ControlClient", (*aws.Config)(nil)).Return(mockedClient).Once()
69+
r := NewS3ControlRepository(&factory, store)
70+
got, err := r.DescribeAccountPublicAccessBlock(accountID)
71+
factory.AssertExpectations(t)
72+
assert.Equal(t, tt.wantErr, err)
73+
74+
if err == nil {
75+
// Check that results were cached
76+
cachedData, err := r.DescribeAccountPublicAccessBlock(accountID)
77+
assert.NoError(t, err)
78+
assert.Equal(t, got, cachedData)
79+
assert.IsType(t, &s3control.PublicAccessBlockConfiguration{}, store.Get("S3DescribeAccountPublicAccessBlock"))
80+
}
81+
82+
changelog, err := diff.Diff(got, tt.want)
83+
assert.Nil(t, err)
84+
if len(changelog) > 0 {
85+
for _, change := range changelog {
86+
t.Errorf("%s: %s -> %s", strings.Join(change.Path, "."), change.From, change.To)
87+
}
88+
t.Fail()
89+
}
90+
})
91+
}
92+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package aws
2+
3+
import (
4+
awssdk "github.com/aws/aws-sdk-go/aws"
5+
"github.com/snyk/driftctl/enumeration/alerter"
6+
"github.com/snyk/driftctl/enumeration/remote/aws/repository"
7+
remoteerror "github.com/snyk/driftctl/enumeration/remote/error"
8+
"github.com/snyk/driftctl/enumeration/resource"
9+
"github.com/snyk/driftctl/enumeration/resource/aws"
10+
)
11+
12+
type S3AccountPublicAccessBlockEnumerator struct {
13+
repository repository.S3ControlRepository
14+
factory resource.ResourceFactory
15+
accountID string
16+
alerter alerter.AlerterInterface
17+
}
18+
19+
func NewS3AccountPublicAccessBlockEnumerator(repo repository.S3ControlRepository, factory resource.ResourceFactory, accountId string, alerter alerter.AlerterInterface) *S3AccountPublicAccessBlockEnumerator {
20+
return &S3AccountPublicAccessBlockEnumerator{
21+
repository: repo,
22+
factory: factory,
23+
accountID: accountId,
24+
alerter: alerter,
25+
}
26+
}
27+
28+
func (e *S3AccountPublicAccessBlockEnumerator) SupportedType() resource.ResourceType {
29+
return aws.AwsS3AccountPublicAccessBlock
30+
}
31+
32+
func (e *S3AccountPublicAccessBlockEnumerator) Enumerate() ([]*resource.Resource, error) {
33+
accountPublicAccessBlock, err := e.repository.DescribeAccountPublicAccessBlock(e.accountID)
34+
if err != nil {
35+
return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType()))
36+
}
37+
38+
results := make([]*resource.Resource, 0, 1)
39+
40+
results = append(
41+
results,
42+
e.factory.CreateAbstractResource(
43+
string(e.SupportedType()),
44+
e.accountID,
45+
map[string]interface{}{
46+
"block_public_acls": awssdk.BoolValue(accountPublicAccessBlock.BlockPublicAcls),
47+
"block_public_policy": awssdk.BoolValue(accountPublicAccessBlock.BlockPublicPolicy),
48+
"ignore_public_acls": awssdk.BoolValue(accountPublicAccessBlock.IgnorePublicAcls),
49+
"restrict_public_buckets": awssdk.BoolValue(accountPublicAccessBlock.RestrictPublicBuckets),
50+
},
51+
),
52+
)
53+
54+
return results, err
55+
}

0 commit comments

Comments
 (0)