From 1549b5a74f4de6453208f8a83e00a0e2e053205f Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 7 Apr 2025 16:07:40 +0200 Subject: [PATCH] Upgrade plugin to use new abstractions --- go.mod | 10 +-- go.sum | 10 +-- main.go | 195 ++++++++++++++------------------------------------------ 3 files changed, 59 insertions(+), 156 deletions(-) diff --git a/go.mod b/go.mod index 5d8c421..84c5f94 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,17 @@ module github.com/compliance-framework/plugin-aws-networking-security go 1.23.2 require ( - github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2/config v1.29.9 github.com/aws/aws-sdk-go-v2/service/ec2 v1.208.0 - github.com/compliance-framework/agent v0.1.1 - github.com/compliance-framework/configuration-service v0.1.1 - github.com/google/uuid v1.6.0 + github.com/compliance-framework/agent v0.1.7 github.com/hashicorp/go-hclog v1.5.0 github.com/hashicorp/go-plugin v1.6.2 - google.golang.org/protobuf v1.35.2 ) require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.2.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.62 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect @@ -30,6 +27,7 @@ require ( github.com/aws/smithy-go v1.22.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/compliance-framework/configuration-service v0.1.2-0.20250327060646-625c895cd99c // indirect github.com/fatih/color v1.15.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -37,6 +35,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -65,6 +64,7 @@ require ( golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 6969f52..1b9c6c0 100644 --- a/go.sum +++ b/go.sum @@ -56,10 +56,10 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/compliance-framework/agent v0.1.1 h1:uQ4idgwOMqrgM0JeYCtBv20HZoMymsH2nownrkl457w= -github.com/compliance-framework/agent v0.1.1/go.mod h1:jy/26xgTx9+at64ipTV1oo80pTVyhtlZaSMViQ3cVVQ= -github.com/compliance-framework/configuration-service v0.1.1 h1:p/r5vq1FLe0S8j/kLhth4Dvda8xajVPOBjnO9QauMjM= -github.com/compliance-framework/configuration-service v0.1.1/go.mod h1:tLKJKXbQbY9Pg/e3BJtJVkqxaejXJMHoE8Yp0NW4lDE= +github.com/compliance-framework/agent v0.1.7 h1:1UqJpNdrZrea9TCw9Z0bHN+I/L8Y67fl9teCGIqSU2g= +github.com/compliance-framework/agent v0.1.7/go.mod h1:dAjHJecMEMdKzylOatT54tXmbjgx5d2Wsq4mGnRksZk= +github.com/compliance-framework/configuration-service v0.1.2-0.20250327060646-625c895cd99c h1:7RIDZy+OcF/9O5EquFchd0W9TY+mlz2zSCVCsReG8Bw= +github.com/compliance-framework/configuration-service v0.1.2-0.20250327060646-625c895cd99c/go.mod h1:tLKJKXbQbY9Pg/e3BJtJVkqxaejXJMHoE8Yp0NW4lDE= github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -348,5 +348,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/main.go b/main.go index 2dec081..1a95c4c 100644 --- a/main.go +++ b/main.go @@ -4,20 +4,16 @@ import ( "context" "errors" "fmt" - "github.com/compliance-framework/plugin-aws-networking-security/internal" - "os" - "time" - "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ec2" policyManager "github.com/compliance-framework/agent/policy-manager" "github.com/compliance-framework/agent/runner" "github.com/compliance-framework/agent/runner/proto" - "github.com/compliance-framework/configuration-service/sdk" - "github.com/google/uuid" + "github.com/compliance-framework/plugin-aws-networking-security/internal" "github.com/hashicorp/go-hclog" goplugin "github.com/hashicorp/go-plugin" - "google.golang.org/protobuf/types/known/timestamppb" + "os" + "slices" ) type CompliancePlugin struct { @@ -37,7 +33,6 @@ func (l *CompliancePlugin) Configure(req *proto.ConfigureRequest) (*proto.Config func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.ApiHelper) (*proto.EvalResponse, error) { ctx := context.TODO() - startTime := time.Now() evalStatus := proto.ExecutionStatus_SUCCESS var accumulatedErrors error @@ -64,18 +59,22 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api findings := make([]*proto.Finding, 0) observations := make([]*proto.Observation, 0) - subjectAttributeMap := map[string]string{ - "type": "aws", - "service": "security-group", - "instance-id": *group.GroupId, - "instance-name": *group.GroupName, - "vpc-id": *group.VpcId, + labels := map[string]string{ + "type": "aws", + "service": "security-groups", + "instance-id": *group.GroupId, } subjects := []*proto.SubjectReference{ { - Type: "aws-security-group", - Attributes: subjectAttributeMap, - Title: internal.StringAddressed("AWS Security Group"), + Type: "aws-security-group", + Attributes: map[string]string{ + "type": "aws", + "service": "security-group", + "instance-id": *group.GroupId, + "instance-name": *group.GroupName, + "vpc-id": *group.VpcId, + }, + Title: internal.StringAddressed("AWS Security Group"), Props: []*proto.Property{ { Name: "security-group-id", @@ -87,6 +86,21 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api }, }, }, + { + Type: "aws-vpc", + Attributes: map[string]string{ + "type": "aws", + "service": "vpc", + "vpc-id": fmt.Sprintf("%v", *group.VpcId), + }, + Title: internal.StringAddressed("AWS VPC"), + Props: []*proto.Property{ + { + Name: "vpc-id", + Value: fmt.Sprintf("%v", *group.VpcId), + }, + }, + }, } actors := []*proto.OriginActor{ { @@ -119,138 +133,25 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api } for _, policyPath := range request.GetPolicyPaths() { - steps := make([]*proto.Step, 0) - steps = append(steps, &proto.Step{ - Title: "Compile policy bundle", - Description: "Using a locally addressable policy path, compile the policy files to an in memory executable.", - }) - steps = append(steps, &proto.Step{ - Title: "Execute policy bundle", - Description: "Using previously collected JSON-formatted Security Group configuration, execute the compiled policies", - }) - - results, err := policyManager.New(ctx, l.logger, policyPath).Execute(ctx, "compliance_plugin", map[string]interface{}{ - "SecurityGroupID": *group.GroupId, - "IpPermissions": group.IpPermissions, - "IpPermissionsEgress": group.IpPermissionsEgress, - }) + // Explicitly reset steps to make things readable + processor := policyManager.NewPolicyProcessor( + l.logger, + internal.MergeMaps( + labels, + map[string]string{ + "_policy_path": policyPath, + }, + ), + subjects, + components, + actors, + activities, + ) + obs, finds, err := processor.GenerateResults(ctx, policyPath, group) + observations = slices.Concat(observations, obs) + findings = slices.Concat(findings, finds) if err != nil { - l.logger.Error("policy evaluation failed", "error", err) - evalStatus = proto.ExecutionStatus_FAILURE accumulatedErrors = errors.Join(accumulatedErrors, err) - continue - } - - activities = append(activities, &proto.Activity{ - Title: "Execute policy", - Description: "Prepare and compile policy bundles, and execute them using the prepared Security Group data", - Steps: steps, - }) - - for _, result := range results { - // Observation UUID should differ for each individual subject, but remain consistent when validating the same policy for the same subject. - // This acts as an identifier to show the history of an observation. - observationUUIDMap := internal.MergeMaps(subjectAttributeMap, map[string]string{ - "type": "observation", - "policy": result.Policy.Package.PurePackage(), - "policy_file": result.Policy.File, - "policy_path": policyPath, - }) - observationUUID, err := sdk.SeededUUID(observationUUIDMap) - if err != nil { - accumulatedErrors = errors.Join(accumulatedErrors, err) - // We've been unable to do much here, but let's try the next one regardless. - continue - } - - // Finding UUID should differ for each individual subject, but remain consistent when validating the same policy for the same subject. - // This acts as an identifier to show the history of a finding. - findingUUIDMap := internal.MergeMaps(subjectAttributeMap, map[string]string{ - "type": "finding", - "policy": result.Policy.Package.PurePackage(), - "policy_file": result.Policy.File, - "policy_path": policyPath, - }) - findingUUID, err := sdk.SeededUUID(findingUUIDMap) - if err != nil { - accumulatedErrors = errors.Join(accumulatedErrors, err) - // We've been unable to do much here, but let's try the next one regardless. - continue - } - - observation := proto.Observation{ - ID: uuid.New().String(), - UUID: observationUUID.String(), - Collected: timestamppb.New(startTime), - Expires: timestamppb.New(startTime.Add(24 * time.Hour)), - Origins: []*proto.Origin{{Actors: actors}}, - Subjects: subjects, - Activities: activities, - Components: components, - RelevantEvidence: []*proto.RelevantEvidence{ - { - Description: fmt.Sprintf("Policy %v was executed against the AWS Security Group configuration, using the Local AWS Security Group Plugin", result.Policy.Package.PurePackage()), - }, - }, - } - - newFinding := func() *proto.Finding { - return &proto.Finding{ - ID: uuid.New().String(), - UUID: findingUUID.String(), - Collected: timestamppb.New(time.Now()), - Labels: map[string]string{ - "type": "aws", - "service": "security-groups", - "instance-id": *group.GroupId, - "instance-name": *group.GroupName, - "vpc-id": *group.VpcId, - "_policy": result.Policy.Package.PurePackage(), - "_policy_path": result.Policy.File, - }, - Origins: []*proto.Origin{{Actors: actors}}, - Subjects: subjects, - Components: components, - RelatedObservations: []*proto.RelatedObservation{{ObservationUUID: observation.ID}}, - Controls: nil, - } - } - - // There are no violations reported from the policies. - // We'll send the observation back to the agent - if len(result.Violations) == 0 { - observation.Title = internal.StringAddressed("The plugin succeeded. No compliance issues to report.") - observation.Description = "The plugin policies did not return any violations. The configuration is in compliance with policies." - observations = append(observations, &observation) - - finding := newFinding() - finding.Title = fmt.Sprintf("No violations found on %s", result.Policy.Package.PurePackage()) - finding.Description = fmt.Sprintf("No violations were found on the %s policy within the AWS Security Groups Compliance Plugin.", result.Policy.Package.PurePackage()) - finding.Status = &proto.FindingStatus{ - State: runner.FindingTargetStatusSatisfied, - } - findings = append(findings, finding) - continue - } - - // There are violations in the policy checks. - // We'll send these observations back to the agent - if len(result.Violations) > 0 { - observation.Title = internal.StringAddressed(fmt.Sprintf("Validation on %s failed.", result.Policy.Package.PurePackage())) - observation.Description = fmt.Sprintf("Observed %d violation(s) on the %s policy within the AWS Security groups Compliance Plugin.", len(result.Violations), result.Policy.Package.PurePackage()) - observations = append(observations, &observation) - - for _, violation := range result.Violations { - finding := newFinding() - finding.Title = violation.Title - finding.Description = violation.Description - finding.Remarks = internal.StringAddressed(violation.Remarks) - finding.Status = &proto.FindingStatus{ - State: runner.FindingTargetStatusNotSatisfied, - } - findings = append(findings, finding) - } - } } }