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
AFFECTSfrom an instance to each attached security group.REACHABLEfrominternet:globalto an instance only when the instance has a public IPv4 address and at least one attached security group allows0.0.0.0/0or::/0. A public IP with a closed group, or an open group on a private instance, does not create this edge. The edge propertyvia_security_groupis the first open group id.ASSUMESfrom the instance to the role on its instance profile. The workload is the source.CAN_ACCESSfrom 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_accessis true when the bucket ACL grantsREADorFULL_CONTROLto 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_blockisenabledonly 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, isdisabled.encryptionis true when default encryption has at least one rule. A missing encryption configuration is false.versioningis true only when versioning status isEnabled. Suspended is false.sensitivityis copied from a bucket tag namedsensitivityordata-class.sensitivitywins when both are set. A blank value is omitted, and a failed tag read leaves the bucket unmarked. The same copy runs for an RDSTagList.
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.
Copyright © 2026 OpenSourceOM. Licensed under Apache-2.0.