Merge branch 'main' into GeraldC13-patch-1
commit
d0860d33a2
4
main.go
4
main.go
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/cloudskiff/driftctl/build"
|
||||
"github.com/cloudskiff/driftctl/logger"
|
||||
"github.com/cloudskiff/driftctl/pkg/cmd"
|
||||
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
|
||||
"github.com/cloudskiff/driftctl/pkg/config"
|
||||
"github.com/cloudskiff/driftctl/pkg/version"
|
||||
"github.com/fatih/color"
|
||||
|
@ -56,6 +57,9 @@ func run() int {
|
|||
}()
|
||||
|
||||
if _, err := driftctlCmd.ExecuteC(); err != nil {
|
||||
if _, isNotInSync := err.(cmderrors.InfrastructureNotInSync); isNotInSync {
|
||||
return 1
|
||||
}
|
||||
if cmd.IsReportingEnabled(&driftctlCmd.Command) {
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ func NewDriftctlCmd(build build.BuildInterface) *DriftctlCmd {
|
|||
},
|
||||
Long: "Detect, track and alert on infrastructure drift.",
|
||||
SilenceErrors: true,
|
||||
SilenceUsage: true,
|
||||
},
|
||||
build,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package errors
|
||||
|
||||
type InfrastructureNotInSync struct{}
|
||||
|
||||
func (i InfrastructureNotInSync) Error() string {
|
||||
return "Infrastructure is not in sync"
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/cloudskiff/driftctl/pkg"
|
||||
"github.com/cloudskiff/driftctl/pkg/alerter"
|
||||
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
|
||||
"github.com/cloudskiff/driftctl/pkg/cmd/scan/output"
|
||||
"github.com/cloudskiff/driftctl/pkg/filter"
|
||||
"github.com/cloudskiff/driftctl/pkg/iac/config"
|
||||
|
@ -157,8 +158,17 @@ func scanRun(opts *ScanOptions) error {
|
|||
if analysis == nil {
|
||||
return errors.New("unable to run driftctl")
|
||||
}
|
||||
out := output.GetOutput(opts.Output)
|
||||
return out.Write(analysis)
|
||||
|
||||
err = output.GetOutput(opts.Output).Write(analysis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !analysis.IsSync() {
|
||||
return cmderrors.InfrastructureNotInSync{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFromFlag(from []string) ([]config.SupplierConfig, error) {
|
||||
|
|
|
@ -46,6 +46,7 @@ func (d DriftCTL) Run() *analyser.Analysis {
|
|||
middlewares.NewAwsDefaultRouteTable(),
|
||||
middlewares.NewAwsDefaultRoute(),
|
||||
middlewares.NewAwsNatGatewayEipAssoc(),
|
||||
middlewares.NewAwsBucketPolicyExpander(),
|
||||
)
|
||||
|
||||
logrus.Debug("Ready to run middlewares")
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource/aws"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Explodes policy found in aws_s3_bucket.policy from state resources to dedicated resources
|
||||
type AwsBucketPolicyExpander struct{}
|
||||
|
||||
func NewAwsBucketPolicyExpander() AwsBucketPolicyExpander {
|
||||
return AwsBucketPolicyExpander{}
|
||||
}
|
||||
|
||||
func (m AwsBucketPolicyExpander) Execute(_, resourcesFromState *[]resource.Resource) error {
|
||||
newList := make([]resource.Resource, 0)
|
||||
for _, res := range *resourcesFromState {
|
||||
// Ignore all resources other than s3_bucket
|
||||
if res.TerraformType() != aws.AwsS3BucketResourceType {
|
||||
newList = append(newList, res)
|
||||
continue
|
||||
}
|
||||
|
||||
bucket, _ := res.(*aws.AwsS3Bucket)
|
||||
newList = append(newList, res)
|
||||
|
||||
if hasPolicyAttached(bucket, resourcesFromState) {
|
||||
bucket.Policy = nil
|
||||
continue
|
||||
}
|
||||
|
||||
err := m.handlePolicy(bucket, &newList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*resourcesFromState = newList
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *AwsBucketPolicyExpander) handlePolicy(bucket *aws.AwsS3Bucket, results *[]resource.Resource) error {
|
||||
if bucket.Policy == nil || *bucket.Policy == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
newPolicy := &aws.AwsS3BucketPolicy{
|
||||
Id: bucket.Id,
|
||||
Bucket: bucket.Bucket,
|
||||
Policy: bucket.Policy,
|
||||
}
|
||||
normalizedRes, err := newPolicy.NormalizeForState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*results = append(*results, normalizedRes)
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"id": newPolicy.TerraformId(),
|
||||
}).Debug("Created new policy from bucket")
|
||||
|
||||
bucket.Policy = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return true if the bucket has a aws_bucket_policy resource attached to itself.
|
||||
// It is mandatory since it's possible to have a aws_bucket with an inline policy
|
||||
// AND a aws_bucket_policy resource at the same time. At the end, on the AWS console,
|
||||
// the aws_bucket_policy will be used.
|
||||
func hasPolicyAttached(bucket *aws.AwsS3Bucket, resourcesFromState *[]resource.Resource) bool {
|
||||
for _, res := range *resourcesFromState {
|
||||
if res.TerraformType() == aws.AwsS3BucketPolicyResourceType &&
|
||||
res.TerraformId() == bucket.Id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
awssdk "github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource/aws"
|
||||
"github.com/r3labs/diff/v2"
|
||||
)
|
||||
|
||||
func TestAwsBucketPolicyExpander_Execute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
resourcesFromState []resource.Resource
|
||||
expected []resource.Resource
|
||||
}{
|
||||
{
|
||||
"Inline policy, no aws_s3_bucket_policy attached",
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: nil,
|
||||
},
|
||||
&aws.AwsS3BucketPolicy{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"No inline policy, aws_s3_bucket_policy attached",
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: nil,
|
||||
},
|
||||
&aws.AwsS3BucketPolicy{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: nil,
|
||||
},
|
||||
&aws.AwsS3BucketPolicy{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Inline policy and aws_s3_bucket_policy",
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
&aws.AwsS3BucketPolicy{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
[]resource.Resource{
|
||||
&aws.AwsS3Bucket{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: nil,
|
||||
},
|
||||
&aws.AwsS3BucketPolicy{
|
||||
Id: "foo",
|
||||
Bucket: awssdk.String("foo"),
|
||||
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := NewAwsBucketPolicyExpander()
|
||||
err := m.Execute(&[]resource.Resource{}, &tt.resourcesFromState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
changelog, err := diff.Diff(tt.expected, tt.resourcesFromState)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(changelog) > 0 {
|
||||
for _, change := range changelog {
|
||||
t.Errorf("%s got = %v, want %v", strings.Join(change.Path, "."), awsutil.Prettify(change.From), awsutil.Prettify(change.To))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -168,6 +169,10 @@ func runDriftCtlCmd(driftctlCmd *cmd.DriftctlCmd) (*cobra.Command, string, error
|
|||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
cmd, cmdErr := driftctlCmd.ExecuteC()
|
||||
// Ignore not in sync errors in acceptance test context
|
||||
if _, isNotInSyncErr := cmdErr.(cmderrors.InfrastructureNotInSync); isNotInSyncErr {
|
||||
cmdErr = nil
|
||||
}
|
||||
outC := make(chan string)
|
||||
// copy the output in a separate goroutine so printing can't block indefinitely
|
||||
go func() {
|
||||
|
|
Loading…
Reference in New Issue