-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/risk and subject templates #13
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,79 @@ | ||
| # Plugin AWS EC2 | ||
| # AWS EC2 CCF Plugin | ||
|
|
||
| ## Assumptions | ||
| This plugin collects read-only AWS EC2, EBS, snapshot, and recovery metadata, evaluates CCF Rego policy bundles, and emits evidence back through the CCF agent. | ||
|
|
||
| - That the agent running this plugin has access to the classic AWS env vars in order to use SDK methods | ||
| - There is a set of policies specfically scoped to EC2 that the config in the agent is pointing to | ||
| ## Supported resource families | ||
|
|
||
| The collector can evaluate policies for: | ||
|
|
||
| - EC2 instances | ||
| - attached security groups | ||
| - attached EBS volumes | ||
| - account-owned EBS snapshots for attached volumes | ||
| - snapshot restore permissions | ||
| - account-owned AMIs related to the instance or collected snapshots | ||
| - Fast Snapshot Restore state for collected snapshots | ||
|
|
||
| ## How it fits in CCF | ||
|
|
||
| The CCF agent starts this binary through HashiCorp `go-plugin`, passes configuration and policy paths over gRPC, and receives generated evidence through the runner callback. This repository does not call the CCF API directly. | ||
|
|
||
| During `Init`, the plugin also registers EC2 subject templates and risk templates discovered from the supplied policy bundles. | ||
|
|
||
| ## Default policy bundle mapping | ||
|
|
||
| | Repository | Behavior | Primary input | | ||
| | --- | --- | --- | | ||
| | `plugin-aws-ec2-policies` | `ec2` | `input.instance` plus related EC2, EBS, and recovery fields | | ||
|
|
||
| ## Configuration | ||
|
|
||
| The plugin expects: | ||
|
|
||
| - AWS credentials through the default AWS SDK credential chain | ||
| - target regions from `config.regions` | ||
| - `AWS_REGION` env as a fallback when plugin config does not provide a region | ||
|
|
||
| `config.regions` can contain a comma-separated list. Duplicate and empty region values are ignored. | ||
|
|
||
| Any agent-supplied `policy_data` is passed through to Rego as `data.*`. | ||
|
|
||
| ## Data collected | ||
|
|
||
| For each running, stopped, stopping, or starting EC2 instance in each configured region, the plugin can collect and correlate: | ||
|
|
||
| - instance details and metadata options | ||
| - VPC and subnet identifiers | ||
| - attached security groups | ||
| - attached EBS volumes | ||
| - account-owned snapshots for attached volumes | ||
| - derived snapshot inventory, including encryption and public restore flags | ||
| - snapshot create-volume permissions | ||
| - account-owned AMIs that match the instance image or collected snapshots | ||
| - Fast Snapshot Restore entries for collected snapshots | ||
|
|
||
| ## Development | ||
|
|
||
| Run the local test suite with: | ||
|
|
||
| ```shell | ||
| go test ./... | ||
| ``` | ||
|
|
||
| Or use the Makefile wrapper: | ||
|
|
||
| ```shell | ||
| make test | ||
| ``` | ||
|
|
||
| Build the plugin binary with: | ||
|
|
||
| ```shell | ||
| make build | ||
| ``` | ||
|
|
||
| This writes the compiled plugin to `dist/plugin`. | ||
|
|
||
| ## Related repositories | ||
|
|
||
| - [plugin-aws-ec2-policies](https://github.com/compliance-framework/plugin-aws-ec2-policies) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package main | ||
|
|
||
| import "github.com/compliance-framework/agent/runner/proto" | ||
|
|
||
| func buildSubjectTemplates() []*proto.SubjectTemplate { | ||
| return []*proto.SubjectTemplate{ | ||
| { | ||
| Name: "aws-ec2-instance", | ||
| Type: proto.SubjectType_SUBJECT_TYPE_COMPONENT, | ||
| TitleTemplate: `AWS EC2 instance {{ index . "instance-id" }} in {{ .region }}`, | ||
| DescriptionTemplate: `Amazon EC2 instance {{ index . "instance-id" }} in subnet {{ index . "_subnet-id" }} and VPC {{ index . "_vpc-id" }} within AWS region {{ .region }}.`, | ||
| PurposeTemplate: "Represents an AWS EC2 instance evaluated for compute, network exposure, and recovery posture.", | ||
| IdentityLabelKeys: []string{"provider", "region", "instance-id"}, | ||
| SelectorLabels: selectorLabelsForType("ec2"), | ||
| LabelSchema: labelSchema( | ||
| label("provider", "Cloud provider for the evaluated resource"), | ||
| label("type", "EC2 plugin resource type"), | ||
| label("instance-id", "AWS EC2 instance identifier"), | ||
| label("_vpc-id", "AWS VPC identifier containing the instance"), | ||
| label("_subnet-id", "AWS subnet identifier containing the instance"), | ||
| label("region", "AWS region containing the instance"), | ||
| ), | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func selectorLabelsForType(resourceType string) []*proto.SubjectLabelSelector { | ||
| return []*proto.SubjectLabelSelector{ | ||
| { | ||
| Key: "type", | ||
| Value: resourceType, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func label(key string, description string) *proto.SubjectLabelSchema { | ||
| return &proto.SubjectLabelSchema{ | ||
| Key: key, | ||
| Description: description, | ||
| } | ||
| } | ||
|
|
||
| func labelSchema(labels ...*proto.SubjectLabelSchema) []*proto.SubjectLabelSchema { | ||
| return labels | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "testing" | ||
| "text/template" | ||
|
|
||
| "github.com/compliance-framework/agent/runner/proto" | ||
| ) | ||
|
|
||
| func TestBuildSubjectTemplates(t *testing.T) { | ||
| templates := buildSubjectTemplates() | ||
| if len(templates) != 1 { | ||
| t.Fatalf("expected 1 subject template, got %d", len(templates)) | ||
| } | ||
|
|
||
| tpl := templates[0] | ||
| if tpl.Name != "aws-ec2-instance" { | ||
| t.Fatalf("unexpected template name: %s", tpl.Name) | ||
| } | ||
| if tpl.Type != proto.SubjectType_SUBJECT_TYPE_COMPONENT { | ||
| t.Fatalf("unexpected template type: %v", tpl.Type) | ||
| } | ||
|
|
||
| expectedIdentityKeys := []string{"provider", "region", "instance-id"} | ||
| if len(tpl.IdentityLabelKeys) != len(expectedIdentityKeys) { | ||
| t.Fatalf("unexpected identity key count: %d", len(tpl.IdentityLabelKeys)) | ||
| } | ||
| for i, key := range expectedIdentityKeys { | ||
| if tpl.IdentityLabelKeys[i] != key { | ||
| t.Fatalf("unexpected identity key at %d: %s", i, tpl.IdentityLabelKeys[i]) | ||
| } | ||
| } | ||
|
|
||
| if len(tpl.SelectorLabels) != 1 { | ||
| t.Fatalf("unexpected selector count: %d", len(tpl.SelectorLabels)) | ||
| } | ||
| if tpl.SelectorLabels[0].Key != "type" || tpl.SelectorLabels[0].Value != "ec2" { | ||
| t.Fatalf("unexpected selector label: %s=%s", tpl.SelectorLabels[0].Key, tpl.SelectorLabels[0].Value) | ||
| } | ||
|
|
||
| expectedSchemaKeys := []string{"provider", "type", "instance-id", "_vpc-id", "_subnet-id", "region"} | ||
| if len(tpl.LabelSchema) != len(expectedSchemaKeys) { | ||
| t.Fatalf("unexpected label schema count: %d", len(tpl.LabelSchema)) | ||
| } | ||
| for i, key := range expectedSchemaKeys { | ||
| if tpl.LabelSchema[i].Key != key { | ||
| t.Fatalf("unexpected label schema key at %d: %s", i, tpl.LabelSchema[i].Key) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestBuildSubjectTemplatesRenderCurrentLabels(t *testing.T) { | ||
| tpl := buildSubjectTemplates()[0] | ||
| labels := map[string]string{ | ||
| "provider": "aws", | ||
| "type": "ec2", | ||
| "region": "eu-west-2", | ||
| "instance-id": "i-0123456789abcdef0", | ||
| "_vpc-id": "vpc-0123456789abcdef0", | ||
| "_subnet-id": "subnet-0123456789abcdef0", | ||
| } | ||
|
|
||
| title := executeTemplate(t, tpl.TitleTemplate, labels) | ||
| if title != "AWS EC2 instance i-0123456789abcdef0 in eu-west-2" { | ||
| t.Fatalf("unexpected title: %s", title) | ||
| } | ||
|
|
||
| description := executeTemplate(t, tpl.DescriptionTemplate, labels) | ||
| expectedDescription := "Amazon EC2 instance i-0123456789abcdef0 in subnet subnet-0123456789abcdef0 and VPC vpc-0123456789abcdef0 within AWS region eu-west-2." | ||
| if description != expectedDescription { | ||
| t.Fatalf("unexpected description: %s", description) | ||
| } | ||
| } | ||
|
|
||
| func executeTemplate(t *testing.T, source string, data map[string]string) string { | ||
| t.Helper() | ||
|
|
||
| tpl, err := template.New("subject").Parse(source) | ||
| if err != nil { | ||
| t.Fatalf("parse template: %v", err) | ||
| } | ||
|
|
||
| var buf bytes.Buffer | ||
| if err := tpl.Execute(&buf, data); err != nil { | ||
| t.Fatalf("execute template: %v", err) | ||
| } | ||
|
|
||
| return buf.String() | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: compliance-framework/plugin-aws-ec2
Length of output: 1114
Fix protogetter lint failures by using protobuf getters in subject_templates_test.go
Direct proto field access (
tpl.Name,tpl.Type,tpl.IdentityLabelKeys,tpl.SelectorLabels,tpl.LabelSchema, and their nested.Key/.Value) still exists in this test (lines 18-48) and will trip protogetter-style lint. Replace withGetX()accessors.Proposed fix
🧰 Tools
🪛 golangci-lint (2.12.2)
[error] 18-18: avoid direct access to proto field tpl.Name, use tpl.GetName() instead
(protogetter)
[error] 19-19: avoid direct access to proto field tpl.Name, use tpl.GetName() instead
(protogetter)
[error] 21-21: avoid direct access to proto field tpl.Type, use tpl.GetType() instead
(protogetter)
[error] 22-22: avoid direct access to proto field tpl.Type, use tpl.GetType() instead
(protogetter)
[error] 26-26: avoid direct access to proto field tpl.IdentityLabelKeys, use tpl.GetIdentityLabelKeys() instead
(protogetter)
[error] 27-27: avoid direct access to proto field tpl.IdentityLabelKeys, use tpl.GetIdentityLabelKeys() instead
(protogetter)
[error] 30-30: avoid direct access to proto field tpl.IdentityLabelKeys, use tpl.GetIdentityLabelKeys() instead
(protogetter)
[error] 35-35: avoid direct access to proto field tpl.SelectorLabels, use tpl.GetSelectorLabels() instead
(protogetter)
[error] 36-36: avoid direct access to proto field tpl.SelectorLabels, use tpl.GetSelectorLabels() instead
(protogetter)
🤖 Prompt for AI Agents