Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
plugin_cloud_custodian
policies
62 changes: 46 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# Compliance Framework - Cloud Custodian Plugin

The **Cloud Custodian Plugin** runs Cloud Custodian policies in dry-run mode,
converts each policy execution into a standardized per-check payload, and then
executes CCF OPA bundles against that payload to generate evidence.
builds a full inventory baseline for each configured resource type, converts
each resource/policy pair into a standardized per-resource payload, and then
executes CCF OPA bundles against those payloads to generate evidence.

## Behavior Overview

1. Load Cloud Custodian policy YAML from config.
2. Parse top-level `policies` and iterate one policy entry per check.
3. Run each check with:
3. Register runtime-derived subject templates for each configured resource type during runner-v2 `Init`.
4. Run one unfiltered inventory collection for each unique resource type.
5. Run each configured check with:

```bash
custodian run --dryrun -s <output-dir> <single-policy-file>
```

4. Build a standardized payload from execution output and `resources.json`.
5. Evaluate each OPA policy bundle path from agent `EvalRequest.policyPaths`.
6. Send evidence via the plugin gRPC helper (`CreateEvidence`).
6. Compare each check's matched resources with the inventory baseline.
7. Build one standardized payload per resource per check. Matched resources are marked `non_compliant`; baseline resources not matched by that check are marked `compliant`.
8. Evaluate each OPA policy bundle path from agent `EvalRequest.policyPaths`.
9. Send evidence via the plugin gRPC helper (`CreateEvidence`).

## Safety Model

Expand All @@ -37,14 +41,16 @@ All plugin config fields are strings (agent gRPC `map<string,string>` contract).
| `custodian_binary` | No | Path/name of Cloud Custodian executable. Default: `custodian`. |
| `check_timeout_seconds` | No | Per-check timeout in seconds. Default: `300`. |
| `policy_labels` | No | JSON map of labels merged into generated evidence labels. |
| `debug_dump_payloads` | No | Boolean (`true`/`false`) toggle to write standardized check payload JSON files for troubleshooting. Default: `false`. |
| `resource_identity_fields` | No | JSON object mapping Cloud Custodian resource types to ordered identity field paths. Built-in defaults are used after configured fields. Example: `{"aws.ec2":["InstanceId","Arn"]}`. |
| `debug_dump_payloads` | No | Boolean (`true`/`false`) toggle to write standardized resource payload JSON files for troubleshooting. Default: `false`. |
| `debug_payload_output_dir` | No | Directory where debug payload JSON files are written. If set, debug dumping is auto-enabled. Default when enabled without explicit path: `debug-standardized-payloads`. |

Validation rules:

- At least one of `policies_yaml` or `policies_path` must be provided.
- `custodian_binary` must resolve on PATH (or as explicit executable path).
- `check_timeout_seconds` must be a positive integer.
- `resource_identity_fields`, when set, must be valid JSON and each resource type must include at least one field path.
- Policy YAML must include top-level `policies` array.

## Example Agent Config (Inline YAML)
Expand Down Expand Up @@ -82,13 +88,13 @@ plugins:
custodian_binary: /usr/local/bin/custodian
```

## Standardized Per-Check OPA Input
## Standardized Per-Resource OPA Input

Each policy/check iteration produces one payload with this shape:
Each resource/check iteration produces one payload with this shape:

```json
{
"schema_version": "v1",
"schema_version": "v2",
"source": "cloud-custodian",
"check": {
"name": "ec2-public-ip-check",
Expand All @@ -97,6 +103,25 @@ Each policy/check iteration produces one payload with this shape:
"index": 0,
"metadata": {}
},
"resource": {
"id": "i-1234567890abcdef0",
"type": "aws.ec2",
"provider": "aws",
"account_id": "123456789012",
"region": "us-east-1",
"identity_fields": {
"InstanceId": "i-1234567890abcdef0"
},
"data": {"...": "..."}
},
"assessment": {
"status": "non_compliant",
"matched": true,
"inventory_status": "baseline",
"matched_resource_count": 3,
"artifact_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check",
"resources_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check/ec2-public-ip-check/resources.json"
},
"execution": {
"status": "success",
"dry_run": true,
Expand All @@ -109,12 +134,6 @@ Each policy/check iteration produces one payload with this shape:
"error": "",
"errors": []
},
"result": {
"matched_resource_count": 3,
"resources": [{"...": "..."}],
"artifact_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check",
"resources_path": "/tmp/ccf-cloud-custodian-123/001-ec2-public-ip-check/ec2-public-ip-check/resources.json"
},
"raw_policy": {
"name": "ec2-public-ip-check",
"resource": "aws.ec2",
Expand All @@ -123,6 +142,17 @@ Each policy/check iteration produces one payload with this shape:
}
```

Generated evidence labels include `resource_id`, `resource_type`, `provider`,
and any available `account_id`/`region`. The resource subject also includes the
resource identifier as a link and a `resource_id` property. AWS Route53 hosted
zone identifiers such as `/hostedzone/Z123` are normalized to full ARNs such as
`arn:aws:route53:::hostedzone/Z123`.

`assessment.inventory_status` is `baseline` for resources found in the unfiltered
inventory run. If a policy returns a resource that is not present in the baseline,
the plugin still evaluates it as `non_compliant` and sets
`inventory_status` to `missing_from_baseline`.

`provider` extraction rule:

- `aws.s3` -> `aws`
Expand Down
61 changes: 33 additions & 28 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module github.com/compliance-framework/plugin-cloud-custodian

go 1.25.7
go 1.26.1
Comment thread
gusfcarvalho marked this conversation as resolved.

require (
github.com/compliance-framework/agent v0.2.1
github.com/compliance-framework/agent v0.4.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-plugin v1.7.0
github.com/mitchellh/mapstructure v1.5.0
Expand All @@ -12,46 +12,51 @@ require (

require (
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/compliance-framework/api v0.4.4 // indirect
github.com/defenseunicorns/go-oscal v0.6.3 // indirect
github.com/compliance-framework/api v0.15.0-rc1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/defenseunicorns/go-oscal v0.7.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.5 // 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.2 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/dsig v1.0.0 // indirect
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc/v3 v3.0.4 // indirect
github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/run v1.2.0 // indirect
github.com/open-policy-agent/opa v1.4.0 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
github.com/open-policy-agent/opa v1.14.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/valyala/fastjson v1.6.10 // indirect
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
golang.org/x/net v0.48.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.10 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
Loading
Loading