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

add support for aws s3 account public access block #1588

Merged
merged 4 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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

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