Skip to content

Commit c17c8f6

Browse files
om986O Mcursoragent
authored
Inventory packages on AWS Linux root volumes. (#113)
CVE enrichment can only match packages the graph already stores, and the API scan never read the disk. This adds an opt-in worker command that fills that list from the root volume. Co-authored-by: O M <om@omLaptop.local> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b8ef230 commit c17c8f6

26 files changed

Lines changed: 2487 additions & 12 deletions

‎CONTRIBUTING.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ On a PR you can comment `@coderabbitai review` (incremental) or `@coderabbitai f
2424

2525
## Development guidelines
2626

27-
- Keep collectors **read-only** toward cloud accounts by default. The Phase 4 workload scan is the opt-in exception: it may create and delete snapshots in the account, and it stores package identifiers only
27+
- Keep collectors **read-only** toward cloud accounts by default. `om scan workload` is the opt-in exception: it may create and delete snapshots in the account, and it stores package identifiers only
2828
- Document graph schema changes in `docs/`
2929
- Prefer small, reviewable PRs
3030
- Add tests for graph queries and normalization logic

‎README.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ OpenSourceOM Core is the platform behind [opensourceom.org](https://opensourceom
2121

2222
Traditional scanners flood you with CVEs and misconfigurations. OpenSourceOM connects the dots — showing which findings sit on paths from the internet to your sensitive data and privileged identities.
2323

24-
> **Status:** Phase 3 has shipped. Phase 4 (workload package inventory) and Phase 5 (secret and malware findings on that storage) are open. Further rule packs stay open for contributors ([#10](https://github.com/OpenSourceOM/core/issues/10)). See the [roadmap](./docs/ROADMAP.md).
24+
> **Status:** Phase 3 has shipped. Phase 4 is open: `om scan workload` inventories AWS Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors ([#10](https://github.com/OpenSourceOM/core/issues/10)). See the [roadmap](./docs/ROADMAP.md).
2525
2626
## Why this exists
2727

@@ -93,6 +93,8 @@ go build -o om ./cmd/om
9393
# Or scan cloud / cluster inventory
9494
export AWS_REGION=us-east-1
9595
./om scan aws
96+
# On an EC2 instance in that account, as root, after the API scan:
97+
# sudo -E ./om scan workload
9698
./om scan k8s # requires kubeconfig
9799

98100
# Or ingest a custom collector (stdout is a graph batch)
@@ -133,6 +135,7 @@ An API token is created only when an admin asks for one, from the console or wit
133135
```bash
134136
./om scan demo # sample environment, no credentials
135137
./om scan aws
138+
./om scan workload # root on an EC2 worker; Linux root-volume packages
136139
./om scan azure # requires AZURE_SUBSCRIPTION_ID + az login
137140
./om scan gcp # requires GCP_PROJECT_ID + ADC
138141
./om scan k8s # requires kubeconfig or in-cluster credentials
@@ -162,7 +165,7 @@ Full documentation: [opensourceom.org](https://opensourceom.org) (docs at [opens
162165
| **1** | Attack path queries, CVE enrichment, web UI, Azure/GCP collectors |
163166
| **2** | CSPM rules, blast radius, K8s connector, exports |
164167
| **3** | Plugin SDK, Helm, embedded rule pack, demo graph, inventory-backed CVEs, crown-jewel datastores, attack-path findings, and cloud audit context (CloudTrail, Activity Log, Admin Activity). Shipped. |
165-
| **4** | Package inventory from persistent workload storage: root volumes, boot images, container layers, function bundles, shared filesystems. Open. |
168+
| **4** | Package inventory from persistent workload storage. AWS Linux root volumes ship with `om scan workload`. The other clouds, boot images, container layers, function bundles, and shared filesystems are open. |
166169
| **5** | Secret and malware findings on storage Phase 4 already opens. Kind and path only. Open. |
167170

168171
Details: [docs/ROADMAP.md](./docs/ROADMAP.md)

‎collectors/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Cloud and platform ingestion plugins. Each collector normalizes provider APIs in
2020

2121
The **demo** collector loads a fixed environment (internet-exposed web tier, production database, public/private buckets, admin vs app identities, public Kubernetes service). Use it to exercise CSPM packs without cloud credentials.
2222

23-
The **AWS** collector emits properties the CIS pack matches on: `open_ingress`, `imdsv2`, `public_ip`, S3 `encryption` / `versioning` / `public_access_block`, and IAM user `mfa` / `unused_access_keys`. It also records RDS and Aurora instances as datastores. S3 bucket tags and the RDS `TagList` copy `sensitivity` or `data-class` onto the datastore. `sensitivity` wins when both are set. The same scan reads CloudTrail management events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events.
23+
The **AWS** collector emits properties the CIS pack matches on: `open_ingress`, `imdsv2`, `public_ip`, S3 `encryption` / `versioning` / `public_access_block`, and IAM user `mfa` / `unused_access_keys`. It also records RDS and Aurora instances as datastores. S3 bucket tags and the RDS `TagList` copy `sensitivity` or `data-class` onto the datastore. `sensitivity` wins when both are set. The same scan reads CloudTrail management events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. `om scan aws` does not read instance disks. `om scan workload` is the separate command that inventories packages on Linux root volumes.
2424

2525
The **Azure** collector records logical SQL servers. The same scan reads administrative Activity Log events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. Entra ID sign-in logs are not part of this scan. The **GCP** collector records Cloud SQL instances. The same scan reads Admin Activity audit logs from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. Data Access logs are not part of this scan. A workload gets `CAN_ACCESS` only when a security group, firewall, or VPC path allows it. Azure copies the crown-jewel mark from storage-account and SQL-server tags. GCP copies it from a bucket label or a Cloud SQL user label. The keys are `sensitivity` and `data-class`. A plugin may set `sensitivity` on a datastore directly.
2626

‎deploy/helm/opensourceom/values.yaml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ resources:
5858
# collector is enabled and credentials are set (a static key, an existing
5959
# Secret, or serviceAccountAuth). Kubernetes is off unless enabled; without
6060
# a kubeconfig Secret the chart creates a read-only ServiceAccount for this
61-
# cluster. `om scan demo` is not scheduled.
61+
# cluster. `om scan demo` and `om scan workload` are not scheduled.
62+
# The workload scan mounts disks and runs on an EC2 instance in the account.
6263
scan:
6364
schedule: "0 */6 * * *"
6465
successfulJobsHistoryLimit: 3

‎docs/ARCHITECTURE.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0
55

66
# Architecture
77

8-
> **Status:** Phase 3 has shipped. The collector plugin SDK, Helm, the embedded rule pack, and cloud audit context from CloudTrail, Activity Log, and Admin Activity logs are in the tree, alongside Phase 2 CSPM rules, blast radius, Kubernetes ingest, and exports. Phase 4 (package inventory from persistent workload storage) and Phase 5 (secret and malware findings on that storage) are open. Further rule packs stay open for contributors.
8+
> **Status:** Phase 3 has shipped. The collector plugin SDK, Helm, the embedded rule pack, and cloud audit context from CloudTrail, Activity Log, and Admin Activity logs are in the tree, alongside Phase 2 CSPM rules, blast radius, Kubernetes ingest, and exports. Phase 4 is open: `om scan workload` inventories AWS Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors.
99
1010
## Overview
1111

@@ -32,6 +32,7 @@ SPDX-License-Identifier: Apache-2.0
3232
| Component | Location | Notes |
3333
|-----------|----------|-------|
3434
| **Collectors** | `internal/collectors/` | AWS, Azure, GCP, Kubernetes, demo; RDS, Azure SQL, and Cloud SQL are datastores; AWS emits CIS pack properties and recent CloudTrail management events; Azure emits recent Activity Log events; GCP emits recent Admin Activity audit logs |
35+
| **Workload packages** | `internal/workload/` | `om scan workload` reads AWS Linux root volumes and sets `packages` |
3536
| **Plugin SDK** | `sdk/collector`, `internal/plugins/` | External executables; `om scan plugin` |
3637
| **Graph store** | `internal/graph/`, `migrations/` | PostgreSQL `nodes` + `edges` |
3738
| **Path queries** | `internal/graph/query.go` | Named queries including `internet-to-datastore` and `internet-to-sensitive-datastore` |
@@ -51,7 +52,7 @@ SPDX-License-Identifier: Apache-2.0
5152

5253
Findings link to affected resources via `VIOLATES` edges. CSPM and CVE findings share the same `Finding` node type but use `finding_type` and `rule_id` / `cve_id` properties for provenance.
5354

54-
See [ADR 001](./adr/001-graph-schema-v0.md), [ADR 002](./adr/002-phase1-findings-ui.md), [ADR 003](./adr/003-phase2-cspm-rbac.md), [ADR 004](./adr/004-collector-plugin-sdk.md), and [ADR 005](./adr/005-console-admins.md).
55+
See [ADR 001](./adr/001-graph-schema-v0.md), [ADR 002](./adr/002-phase1-findings-ui.md), [ADR 003](./adr/003-phase2-cspm-rbac.md), [ADR 004](./adr/004-collector-plugin-sdk.md), [ADR 005](./adr/005-console-admins.md), and [ADR 006](./adr/006-aws-root-volume-packages.md).
5556

5657
## Technology choices
5758

@@ -84,7 +85,9 @@ Further community rule packs (PCI and additional CIS mappings) stay open for con
8485

8586
## Phase 4 and Phase 5
8687

87-
Planned, not in the tree. Phase 4 writes `packages` (CPE 2.3 names and package URLs) from persistent workload storage onto the workload node that enrichment already matches. The sources, in order, are AWS Linux root volumes, Azure VM and GCE root volumes, boot images, container image layers and function bundles, and shared filesystems (EFS, Azure Files, Filestore). The scan is opt-in. `om scan aws`, `om scan azure`, and `om scan gcp` stay read-only API collectors. The workload scan runs a worker in the account, and it stores package identifiers only.
88+
Phase 4 writes `packages` (CPE 2.3 names and package URLs) from persistent workload storage onto the workload node that enrichment already matches. The sources, in order, are AWS Linux root volumes, Azure VM and GCE root volumes, boot images, container image layers and function bundles, and shared filesystems (EFS, Azure Files, Filestore). The scan is opt-in. `om scan aws`, `om scan azure`, and `om scan gcp` stay read-only API collectors.
89+
90+
`om scan workload` is the AWS root-volume scan. It runs as root on an EC2 instance in the account, snapshots the root volume, mounts the copy read-only, and stores package identifiers only. A later `om scan aws` keeps `packages`. Windows, LVM, and non-root disks are skipped. The other Phase 4 sources are still open. See [ADR 006](./adr/006-aws-root-volume-packages.md).
8891

8992
Phase 5 adds a finding for a secret pattern or a malware signature on storage that Phase 4 reader already opened. The finding stores the kind and the path. Object bodies and database rows are in neither phase.
9093

‎docs/ROADMAP.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,14 @@ The scan is opt-in and separate from `om scan aws`, `om scan azure`, and `om sca
6868

6969
The path, in order:
7070

71-
- [ ] **AWS Linux root volumes** — [#102](https://github.com/OpenSourceOM/core/issues/102)
71+
- [x] **AWS Linux root volumes** — [#102](https://github.com/OpenSourceOM/core/issues/102)
7272
- [ ] **Azure VM and GCE root volumes** — the same walker, those snapshot APIs — [#103](https://github.com/OpenSourceOM/core/issues/103)
7373
- [ ] **Boot images** — an AMI and the Azure and GCE equivalents, scanned once and applied to instances launched from that image — [#104](https://github.com/OpenSourceOM/core/issues/104)
7474
- [ ] **Container image layers and function bundles** — layers for images already stored on Kubernetes workloads, plus Lambda deployment packages and the Azure and GCP equivalents. No volume attach — [#105](https://github.com/OpenSourceOM/core/issues/105)
7575
- [ ] **Shared filesystems** — EFS, Azure Files, and Filestore, when the application is installed there and a root-volume scan would miss it — [#106](https://github.com/OpenSourceOM/core/issues/106)
7676

77+
`om scan workload` reads Linux root volumes in the current AWS account. It runs as root on an EC2 instance in that account, snapshots the root volume, mounts the copy read-only, and writes CPE 2.3 names and package URLs onto `packages`. A later `om scan aws` leaves `packages` in place. Windows, LVM, and disks other than the root volume are skipped.
78+
7779
Object bodies and database rows stay out. They are customer data, not the installed software. A public bucket or database is already an exposure finding from the API scan.
7880

7981
## Phase 5 — Findings on inventoried storage
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
Copyright 2026 OpenSourceOM
3+
SPDX-License-Identifier: Apache-2.0
4+
-->
5+
6+
# ADR 006: AWS Linux root-volume package inventory
7+
8+
**Status:** Accepted
9+
**Date:** 2026-10-14
10+
**Phase:** 4
11+
12+
## Context
13+
14+
`om enrich cve` writes a finding when `packages` on a workload matches a CPE 2.3 name or a versioned package URL. `om scan aws` records the instance from `DescribeInstances` and copies the AMI id into `image`. It does not read the root volume, so a package installed on the instance never reaches `packages`.
15+
16+
Reading a volume means creating a snapshot and attaching a copy. That is a write to the account. The API collectors stay read-only.
17+
18+
## Decision
19+
20+
`om scan workload` is a separate command. It runs as root on an EC2 instance in the account and region being scanned.
21+
22+
For each Linux instance with an EBS root volume, the command:
23+
24+
1. Creates a snapshot of the root volume and a volume from that snapshot in the worker's availability zone.
25+
2. Attaches the volume to the worker and mounts it read-only.
26+
3. Reads the dpkg, apk, or rpm database and writes CPE 2.3 names and versioned package URLs onto `packages` of the existing workload node.
27+
4. Unmounts, detaches, and deletes the volume and the snapshot, including when a later step fails.
28+
29+
Windows instances, instance-store roots, LVM physical volumes, and non-root disks are skipped. The graph, logs, and exports store package identifiers. They do not store file contents.
30+
31+
An inventory upsert that omits `packages` keeps the list already stored. `om scan aws` can run again without erasing the workload scan. An upsert that sets `packages`, including an empty list, replaces it.
32+
33+
The IAM actions are `ec2:CreateSnapshot`, `ec2:AttachVolume`, `ec2:DeleteSnapshot`, `ec2:DeleteVolume`, the describe and tag calls around that lifecycle, and `kms:CreateGrant` plus `kms:Decrypt` (with `kms:DescribeKey`, `kms:GenerateDataKeyWithoutPlaintext`, and `kms:ReEncrypt*`) for an encrypted root volume. `om scan aws` does not gain these actions.
34+
35+
Azure, GCE, boot images, container layers, function bundles, and shared filesystems stay on later Phase 4 issues. Secret and malware findings stay on Phase 5, after this reader exists.
36+
37+
## Consequences
38+
39+
The command has to run on an instance in the account. A process killed with SIGKILL can leave a snapshot or volume; both are tagged `opensourceom:scan=workload`. The Helm chart does not schedule this command.
40+
41+
`om enrich cve` is unchanged. It attaches a CVE only when one of these identifiers matches.
42+
43+
## References
44+
45+
- [docs/ROADMAP.md](../ROADMAP.md)
46+
- [Issue #102](https://github.com/OpenSourceOM/core/issues/102)

‎go.mod‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ require (
1313
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v8 v8.0.0
1414
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v3 v3.0.1
1515
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage/v3 v3.0.0
16+
github.com/anchore/go-rpmdb v0.2.2
1617
github.com/aws/aws-sdk-go-v2 v1.47.3
1718
github.com/aws/aws-sdk-go-v2/config v1.33.6
19+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.1
1820
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.65.4
1921
github.com/aws/aws-sdk-go-v2/service/ec2 v1.338.1
2022
github.com/aws/aws-sdk-go-v2/service/iam v1.64.4
2123
github.com/aws/aws-sdk-go-v2/service/rds v1.130.3
2224
github.com/aws/aws-sdk-go-v2/service/s3 v1.114.3
2325
github.com/aws/aws-sdk-go-v2/service/sts v1.51.1
26+
github.com/aws/smithy-go v1.28.5
27+
github.com/glebarez/go-sqlite v1.23.0
2428
github.com/jackc/pgx/v5 v5.11.0
2529
github.com/spf13/cobra v1.10.2
2630
golang.org/x/crypto v0.57.0
@@ -47,7 +51,6 @@ require (
4751
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect
4852
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.22 // indirect
4953
github.com/aws/aws-sdk-go-v2/credentials v1.20.6 // indirect
50-
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.1 // indirect
5154
github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6 // indirect
5255
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6 // indirect
5356
github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6 // indirect
@@ -58,10 +61,10 @@ require (
5861
github.com/aws/aws-sdk-go-v2/service/signin v1.10.1 // indirect
5962
github.com/aws/aws-sdk-go-v2/service/sso v1.38.1 // indirect
6063
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.1 // indirect
61-
github.com/aws/smithy-go v1.28.5 // indirect
6264
github.com/cespare/xxhash/v2 v2.3.0 // indirect
6365
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
6466
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
67+
github.com/dustin/go-humanize v1.0.1 // indirect
6568
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
6669
github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
6770
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
@@ -96,11 +99,14 @@ require (
9699
github.com/jackc/puddle/v2 v2.2.2 // indirect
97100
github.com/json-iterator/go v1.1.12 // indirect
98101
github.com/kylelemons/godebug v1.1.0 // indirect
102+
github.com/mattn/go-isatty v0.0.20 // indirect
99103
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
100104
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
101105
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
106+
github.com/ncruces/go-strftime v1.0.0 // indirect
102107
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
103108
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
109+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
104110
github.com/spf13/pflag v1.0.10 // indirect
105111
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
106112
github.com/x448/float16 v0.8.4 // indirect
@@ -130,6 +136,10 @@ require (
130136
k8s.io/klog/v2 v2.140.0 // indirect
131137
k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect
132138
k8s.io/utils v0.0.0-20260626114624-be93311217bd // indirect
139+
modernc.org/libc v1.74.1 // indirect
140+
modernc.org/mathutil v1.7.1 // indirect
141+
modernc.org/memory v1.11.0 // indirect
142+
modernc.org/sqlite v1.55.0 // indirect
133143
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
134144
sigs.k8s.io/randfill v1.0.0 // indirect
135145
sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect

0 commit comments

Comments
 (0)