diff --git a/.gitignore b/.gitignore index 50d6977..f6fbc56 100644 --- a/.gitignore +++ b/.gitignore @@ -689,3 +689,4 @@ settings.json main.tf builds/** cinqctl +.scratch \ No newline at end of file diff --git a/cloud-inquisitor/aws_cloudfront_resource.go b/cloud-inquisitor/aws_cloudfront_resource.go index 59fb227..278b277 100644 --- a/cloud-inquisitor/aws_cloudfront_resource.go +++ b/cloud-inquisitor/aws_cloudfront_resource.go @@ -181,6 +181,58 @@ func (cf *AWSCloudFrontDistributionResource) createDistributionEntries() error { return nil } +func (cf *AWSCloudFrontDistributionResource) deleteDistributionEntries() error { + db, err := database.NewDBConnection() + defer db.Close() + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // get account + account := model.Account{AccountID: cf.AccountID} + err = db.FirstOrCreate(&account, account).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + cf.logger.WithFields(cf.GetMetadata()).Debugf("account: %#v", account) + + distro := model.Distribution{DistributionID: cf.DistributionID, Domain: cf.DomainName, AccountID: account.ID} + err = db.Delete(&distro).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // delete origins + for _, cfOrigin := range cf.Origins { + origin := model.Origin{ + OriginID: cfOrigin.ID, + Domain: cfOrigin.Domain, + DistributionID: distro.ID, + } + err = db.Delete(&origin).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // delete origin groups + for _, cfGroup := range cf.OriginGroups { + group := model.OriginGroup{ + GroupID: cfGroup.ID, + DistributionID: distro.ID, + } + err = db.Delete(&group).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + return nil +} + func (cf *AWSCloudFrontDistributionResource) updateDistributionEntries() error { db, err := database.NewDBConnection() defer db.Close() @@ -496,6 +548,8 @@ func (cf *AWSCloudFrontDistributionHijackableResource) PublishState() error { switch cf.EventName { case "CreateDistribution": return cf.createDistributionEntries() + case "DeleteDistribution": + return cf.deleteDistributionEntries() case "UpdateDistribution": return cf.updateDistributionEntries() default: @@ -505,6 +559,50 @@ func (cf *AWSCloudFrontDistributionHijackableResource) PublishState() error { return nil } -func (cf *AWSCloudFrontDistributionHijackableResource) AnalyzeForHijack() (*model.HijackableResourceChain, error) { - return &model.HijackableResourceChain{}, nil +func (cf *AWSCloudFrontDistributionHijackableResource) AnalyzeForHijack() (*model.HijackableResourceRoot, error) { + switch cf.EventName { + case "CreateDistribution": + return cf.analyzeCreateDistributionEntries() + case "DeleteDistribution": + return cf.analyzeDeleteDistributionEntries() + default: + return &model.HijackableResourceRoot{}, nil + } +} + +func (cf *AWSCloudFrontDistributionHijackableResource) analyzeDeleteDistributionEntries(*model.HijackableResourceRoot, error) { + return cf.analyzeDeleteDistributionEntries() } + +func (cf *AWSCloudFrontDistributionHijackableResource) analyzeDeleteDistributionEntries(*model.HijackableResourceRoot, error) { + resolver, err := graph.NewResolver() + if err != nil { + cf.GetLogger().Errorf("error creating a new resolver to evaluate cloudfront hijacks: %v", err.Error()) + return &model.HijackableResourceRoot{}, err + } + + domains := make([]string, len(cf.Origins)) + for i, ori := range cf.Origins { + domain[i] = ori.Domain + } + + ctx := context.Background() + root, err := resolver.Query().GetHijackMapWithResourceIDAndDomainsAndTypeAndDirectionThenFlattened( + ctx, + fmt.Sprintf( + "cloudfront-%s-%s-%s", + cf.AccountID, + cf.DistributionID, + cf.DomainName, + ), + cf.DistributionID, + domains, + model.TypeDistribution, + model.DirectionDownstream, + ) + if err != nil { + eb.GetLogger().Errorf("error querying graph for cloudfront hijack analysis: %v", err.Error()) + } + + return root, err +} \ No newline at end of file diff --git a/cloud-inquisitor/graph/model/models_gen.go b/cloud-inquisitor/graph/model/models_gen.go index d810173..51341c3 100644 --- a/cloud-inquisitor/graph/model/models_gen.go +++ b/cloud-inquisitor/graph/model/models_gen.go @@ -22,10 +22,10 @@ type HijackableResourceMap struct { } type HijackableResourceRoot struct { - ID string `json:"id"` - RootResourceID string `json:"rootResourceID"` - Direction Direction `json:"direction"` - Maps []*HijackableResourceMap `json:"maps"` + ID string `json:"id"` + RootResource *HijackableResource `json:"rootResourceID"` + Direction Direction `json:"direction"` + Maps []*HijackableResourceMap `json:"maps"` } type Direction string diff --git a/cloud-inquisitor/graph/schema.graphqls b/cloud-inquisitor/graph/schema.graphqls index 6e84867..a9d9f3c 100644 --- a/cloud-inquisitor/graph/schema.graphqls +++ b/cloud-inquisitor/graph/schema.graphqls @@ -84,7 +84,7 @@ type HijackableResource { type HijackableResourceRoot { id: ID! - rootResourceID: ID! + rootResource: HijackableResource! direction: Direction! maps: [HijackableResourceMap!]! } diff --git a/cloud-inquisitor/notification/templates.go b/cloud-inquisitor/notification/templates.go index 9279038..065775b 100644 --- a/cloud-inquisitor/notification/templates.go +++ b/cloud-inquisitor/notification/templates.go @@ -15,87 +15,39 @@ func init() { } type HijackChainElement struct { - AccountId string - Resource string - ResourceType string - ResourceReferenced string - ResourceReferencedType string + AccountId string + Resource string + ResourceType string } type HijackNotificationContent struct { PrimaryResource string PrimaryResourceType string PrimaryAccountId string - HijackChain []HijackChainElement + HijackChains [][]HijackChainElement } -func GenerateContent(rawChain *model.HijackableResourceChain) HijackNotificationContent { +func GenerateContent(root *model.HijackableResourceRoot) HijackNotificationContent { content := HijackNotificationContent{ - PrimaryResource: rawChain.Resource.ID, - PrimaryAccountId: rawChain.Resource.Account, - PrimaryResourceType: rawChain.Resource.Type.String(), + PrimaryResource: root.RootResource.ID, + PrimaryAccountId: root.RootResource.Account, + PrimaryResourceType: root.RootResource.Type.String(), } - chain := []HijackChainElement{} - for idx, resource := range rawChain.Upstream { - if idx == len(rawChain.Upstream)-1 { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Resource.ID, - ResourceReferencedType: rawChain.Resource.Type.String(), - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Upstream[idx+1].ID, - ResourceReferencedType: rawChain.Upstream[idx+1].Type.String(), - }) - } - } - - if len(rawChain.Downstream) == 0 { - chain = append(chain, HijackChainElement{ - AccountId: rawChain.Resource.Account, - Resource: rawChain.Resource.ID, - ResourceType: rawChain.Resource.Type.String(), - ResourceReferenced: "not applicable", - ResourceReferencedType: "not applicable", - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: rawChain.Resource.Account, - Resource: rawChain.Resource.ID, - ResourceType: rawChain.Resource.Type.String(), - ResourceReferenced: rawChain.Downstream[0].ID, - ResourceReferencedType: rawChain.Downstream[0].Type.String(), - }) - } - - for idx, resource := range rawChain.Downstream { - if idx == len(rawChain.Downstream)-1 { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: "not applicable", - ResourceReferencedType: "not applicable", - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Downstream[idx+1].ID, - ResourceReferencedType: rawChain.Downstream[idx+1].Type.String(), + var hijackChains [][]HijackChainElement + for _, chain := range root.Maps { + hijackChain := []HijackChainElement{} + for _, element := range chain.Contains { + hijackChain = append(hijackChain, HijackChainElement{ + AccountId: element.Resource.Account, + Resource: element.Resource.ID, + ResourceType: element.Resource.Type.String(), }) } + hijackChains = append(hijackChains, hijackChain) } - content.HijackChain = chain + content.HijackChains = hijackChains return content } diff --git a/cloud-inquisitor/notification/templates/hijack_template.html b/cloud-inquisitor/notification/templates/hijack_template.html index 59ac00f..2a09b97 100644 --- a/cloud-inquisitor/notification/templates/hijack_template.html +++ b/cloud-inquisitor/notification/templates/hijack_template.html @@ -41,53 +41,46 @@
The following table includes all resources in the chain that would lead to a potential domain hijack.
+The following tables includes all resources in the chain that would lead to a potential domain hijack.
| - Account ID - | -- Resource - | -- Resource Type - | -- Resource Referenced - | -- Resource Referenced Type - | -|||
|---|---|---|---|---|---|---|---|
| - {{ $elem.AccountId }} - | -- {{ $elem.Resource }} - | -- {{ $elem.ResourceType }} - | -- {{ $elem.ResourceReferenced }} - | -- {{ $elem.ResourceReferencedType }} - | + {{- with .HijackChains }} + {{- range $j, $singlechain := . }} +
| + Account ID + | ++ Resource + | ++ Resource Type + |
|---|