Get started

AWS security collector

om scan aws uses the AWS SDK default credential chain in AWS_REGION (default us-east-1). It calls sts:GetCallerIdentity for the account id, then inventories that one region plus account-global IAM and the bucket list. Run it once per region you care about. EC2 and security group ids include the region, so two regions do not overwrite each other. IAM and S3 ids use global instead of the scan region, so a second regional scan updates the same role, user, and bucket.

export AWS_REGION=us-east-1
./om scan aws
./om rules run

Resources

API Graph
EC2 DescribeInstances Workload. Properties include public_ip (bool), public_ip_address, imdsv2, instance_type, state, os_platform, and image (the AMI id, when set).
EC2 DescribeSecurityGroups Network. open_ingress and internet_facing are true when a permission allows 0.0.0.0/0 or ::/0.
IAM ListRoles, attached and inline role policies Identity with principal_type: role and admin_access.
IAM users, attached and inline user policies, MFA devices, access keys Identity with principal_type: user, admin_access, mfa, and unused_access_keys (keys unused for 90 days).
S3 bucket list, public access block, encryption, versioning Datastore with service: s3, public_access, public_access_block (disabled or enabled), encryption, versioning, and sensitivity when a bucket tag names it.
CloudTrail LookupEvents Up to five audit_events on the identity and the resource the event names. See below.

Edges

  • AFFECTS from an instance to each attached security group.
  • REACHABLE from internet:global to an instance only when the instance has a public IPv4 address and at least one attached security group allows 0.0.0.0/0 or ::/0. A public IP with a closed group, or an open group on a private instance, does not create this edge. The edge property via_security_group is the first open group id.
  • ASSUMES from the instance to the role on its instance profile. The workload is the source.
  • CAN_ACCESS from that role to S3 buckets allowed by the role’s identity policies.

S3 access reads the instance profile, attached and inline role policies, and the default policy version. Allow statements are interpreted. Deny statements and conditions are not, so an allow of s3:* on * still produces a bucket edge when a deny would block it in IAM. admin_access is true when an attached or inline policy allows * on *, or when the AWS-managed AdministratorAccess policy is attached. The role or user name is not used. Deny statements, conditions, permission boundaries, and group policies are not evaluated. A role can have admin_access: false and still receive CAN_ACCESS edges from a narrower policy.

How S3 properties are set

  • public_access is true when the bucket ACL grants READ or FULL_CONTROL to All Users or Authenticated Users, or when the bucket policy allows anonymous object read. A policy that cannot be parsed is ignored. A missing policy is not public.
  • public_access_block is enabled only when Block Public ACLs, Block Public Policy, Ignore Public ACLs, and Restrict Public Buckets are all true. Any other configuration, including a missing public access block, is disabled.
  • encryption is true when default encryption has at least one rule. A missing encryption configuration is false.
  • versioning is true only when versioning status is Enabled. Suspended is false.
  • sensitivity is copied from a bucket tag named sensitivity or data-class. sensitivity wins when both are set. A blank value is omitted, and a failed tag read leaves the bucket unmarked. The same copy runs for an RDS TagList.

Node ids are aws:{account}:{scope}:{kind}:{resource}. EC2 instances and security groups use the scan region as the scope. IAM roles, IAM users, and S3 buckets use global, and their node region is left empty. Instance resources use the Name tag when present, otherwise the instance id. Security groups use the group id. IAM resources use the role or user name. Buckets use the bucket name. DescribeInstances, DescribeSecurityGroups, and ListBuckets follow pagination until the API reports no further page.

The collector does not inventory Lambda, EKS, VPCs, or IAM groups. RDS and Aurora instances are datastores, and their tags supply sensitivity the same way a bucket tag does. Buckets are listed account-wide. EC2 and security groups are the one region in AWS_REGION.

CloudTrail

After inventory is built, the scan calls cloudtrail:LookupEvents for the last 24 hours in AWS_REGION. IAM and S3 management events are recorded in us-east-1, so a scan of another region also looks there. Each lookup stops after four pages. A failed lookup omits audit_events and does not fail the scan.

An event is kept when its name is on a fixed management-event list (for example AssumeRole, PutBucketPolicy, AuthorizeSecurityGroupIngress) and it names an identity already in the batch plus a resource on an exposed path. Exposed means an internet-reachable workload, a node that workload assumes or can access, a network that workload affects, a public datastore, or an identity that can access a public datastore. The same event is stored on both nodes, newest first, at most five per node. GetObject is an S3 data event. LookupEvents does not return it, so this slice does not.

GET /v1/graph/query and om paths run list those events in audits when the resource node is on the returned path. The console prints the same lines under the path. Azure Activity Log events are collected by om scan azure. GCP Admin Activity logs are collected by om scan gcp.

Read-only actions

A least-privilege policy for this collector needs read access for the calls above, including iam:GetInstanceProfile, iam:ListAttachedRolePolicies, iam:ListRolePolicies, iam:GetRolePolicy, iam:GetPolicy, iam:GetPolicyVersion, s3:GetBucketTagging, and cloudtrail:LookupEvents. The scan does not call mutating APIs.