Skip to content

compliance-framework/plugin-aws-secretsmanager

Repository files navigation

AWS Secrets Manager CCF Plugin

This repository builds a CCF RunnerV2 plugin for AWS Secrets Manager. It collects read-only Secrets Manager metadata, resource policies, version stages, tags, and CloudTrail event history, then evaluates configured Rego policy bundles against one normalized record per secret ARN.

The plugin never calls GetSecretValue.

Subject

The plugin registers one subject template:

Name Type Identity labels
aws-secretsmanager-secret component account_id, region, resource_id

Every evidence record also includes labels: provider=aws, type=secretsmanager, subject=aws-secretsmanager-secret, account_id, region, resource_id, resource_arn, resource_type=secret, and account_tag_<key> for account config tags. resource_id is the ARN segment after the final : and keeps the AWS 6-character suffix.

Configuration

The CCF agent passes flat string config. Structured values are JSON strings.

Key Default Notes
accounts [] JSON array of {account_id, regions[], role_arn, external_id, session_name, tags{}}. Empty uses the ambient AWS credential chain.
default_regions [] JSON array used when an account omits regions; then falls back to the SDK default region.
lookback_days 90 Positive integer up to 90, used for CloudTrail event history.
policy_inputs {} JSON object exposed to Rego as input.policy_inputs.
policy_input {} Alias for policy_inputs.
policy_labels {} JSON string map merged into evidence labels.
max_concurrency 4 Positive worker count for account/region targets; 0 is normalized to 1.
api_timeout_seconds 120 Positive per-target budget covering all paginated Secrets Manager, CloudTrail, and STS calls for one account/region.

Runtime logs default to info. Set LOG_LEVEL to debug, info, warn, or error to override the level.

Rego Input

Each secret is marshaled with resource.type = "secret":

{
  "schema_version": "v1",
  "source": "aws-secretsmanager",
  "account": {"account_id": "123456789012", "role_arn": "", "tags": {"environment": "prod"}},
  "region": {"name": "us-east-1"},
  "resource": {
    "id": "MyApp/db/credentials-AbCdEf",
    "arn": "arn:aws:secretsmanager:us-east-1:123456789012:secret:MyApp/db/credentials-AbCdEf",
    "type": "secret"
  },
  "config": {
    "secret_arn": "arn:aws:secretsmanager:us-east-1:123456789012:secret:MyApp/db/credentials-AbCdEf",
    "name_hash": "sha256:...",
    "kms_key_id": "aws/secretsmanager",
    "rotation_enabled": true,
    "rotation_lambda_arn": "arn:aws:lambda:us-east-1:123456789012:function:rotate-db",
    "rotation_rules": {"automatically_after_days": 30, "schedule_expression": "", "duration": ""},
    "last_rotated_date": "2026-04-12T03:00:00Z",
    "last_changed_date": "2026-04-12T03:00:00Z",
    "last_accessed_date": "2026-05-26T14:22:11Z",
    "deleted_date": "",
    "recovery_window_days": 0,
    "owning_service": "",
    "replication_status": [
      {"region": "us-west-2", "status": "InSync", "last_accessed_date": "2026-05-26T14:22:11Z", "status_message": ""}
    ],
    "description_hash": "sha256:...",
    "resource_policy": {
      "hash": "sha256:...",
      "document": {"Version": "2012-10-17", "Statement": []},
      "principals": [
        {"principal": "arn:aws:iam::123456789012:role/app-reader", "action": ["secretsmanager:GetSecretValue"], "condition": null, "effect": "Allow"}
      ]
    },
    "resource_policy_present": true,
    "versions": [
      {"version_id": "abc-123", "created_date": "2026-04-12T03:00:00Z", "kms_key_ids": ["arn:aws:kms:us-east-1:123456789012:key/..."], "stages": ["AWSCURRENT"]},
      {"version_id": "def-456", "created_date": "2026-03-13T03:00:00Z", "kms_key_ids": ["arn:aws:kms:us-east-1:123456789012:key/..."], "stages": ["AWSPREVIOUS"]}
    ],
    "deprecated_version_count": 0
  },
  "dynamic": {
    "cloudtrail_events": [
      {"event_name": "RotateSecret", "event_time": "2026-04-12T03:00:00Z", "user_identity_arn": "arn:aws:iam::123456789012:role/rotation", "aws_region": "us-east-1", "event_id": "evt-1", "resources": ["MyApp/db/credentials-AbCdEf"]}
    ],
    "iam_credential_removal_events": []
  },
  "tags": {"Owner": "platform-team", "Environment": "prod", "DataClassification": "confidential"},
  "collection": {
    "collected_at": "2026-05-28T12:00:00Z",
    "collector_version": "aws-secretsmanager",
    "collection_type": "config_dynamic",
    "lookback_window": {"start": "2026-02-27T12:00:00Z", "end": "2026-05-28T12:00:00Z"},
    "raw_payload_hashes": {"describe": "sha256:...", "policy": "sha256:...", "versions": "sha256:..."},
    "errors": []
  },
  "policy_inputs": {}
}

When AWS omits KmsKeyId for the AWS-managed default key, config.kms_key_id is emitted as the sentinel string aws/secretsmanager. Date fields are always strings and are "" when absent. When no resource policy exists, resource_policy is {"hash":"","document":null,"principals":[]} and resource_policy_present is false.

recovery_window_days defaults to 0 and is populated from a matched CloudTrail DeleteSecret event's requestParameters.recoveryWindowInDays when that event is available. Secrets Manager does not expose the original recovery-window setting directly on DescribeSecret.

Coverage

CONFIG evidence supports rotation, vendor credential, confidentiality, and privacy policy bundles through DescribeSecret, GetResourcePolicy, ListSecretVersionIds, and tags from DescribeSecret. Fields include rotation state, rotation rules, KMS key ID string, owning service, replication status, version stages, resource policy principals, and hashed name/description.

DYNAMIC evidence uses a 90-day default CloudTrail lookback. Secrets Manager events are filtered to RotateSecret, PutSecretValue, UpdateSecret, UpdateSecretVersionStage, DeleteSecret, RestoreSecret, PutResourcePolicy, DeleteResourcePolicy, TagResource, UntagResource, CreateSecret, and GetSecretValue. IAM credential-removal events are filtered to DeleteUser, DeleteAccessKey, DetachUserPolicy, RemoveUserFromGroup, DeleteRole, DetachRolePolicy, and RemoveRoleFromInstanceProfile.

CloudTrail Attribution

Secrets Manager events are matched to a secret by ARN, friendly name with suffix, or friendly name without suffix using substring checks against the raw CloudTrail event during collection. This is intentional because many Secrets Manager event types put the identifier in requestParameters rather than the structured Resources list.

IAM credential-removal events are account-wide. The plugin parses affected userName, roleName, userArn, and roleArn from requestParameters and attaches the event only to secrets whose resource-policy principal strings substring-match those identifiers. Unmatched IAM events are dropped.

Only normalized event fields are emitted; raw CloudTrail payloads are not included in Rego input.

Error Scoping

Target-level failures, such as ListSecrets or CloudTrail lookup failure for an account/region, are stored once under target scope. They are not copied onto every secret.

Per-secret failures, such as a GetResourcePolicy error for one ARN, are attached only to that secret's collection.errors. ResourceNotFoundException and empty resource policies are valid no-policy states, not errors.

Out of Scope

KMS key policy, key rotation, and grants are collected by plugin-aws-kms; this plugin records only the kms_key_id string from Secrets Manager. IAM principal resolution belongs to plugin-aws-iam; this plugin records policy principal strings and CloudTrail IAM-event ARNs only. Decrypted secret material is never read.

Development

make build
make test

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors