Get started

Install a self-hosted security graph

Core runs as two processes that share one Postgres database: the om CLI (scans, rules, queries) and the API (console and HTTP). Docker Compose starts Postgres and the API. You build or download the CLI and point it at the published database port.

Prerequisites

  • Docker with Compose
  • Go 1.27.1 if you build om from source (or a release archive from GitHub Releases)
  • Cloud credentials only when you scan a real account. The demo graph needs none.

Start Postgres and the API

git clone https://github.com/OpenSourceOM/core.git
cd core
cp .env.example .env
docker compose up -d

Compose publishes Postgres on localhost:5432 and the API on http://localhost:8080. The API container does not apply schema migrations. The CLI does.

Compose reads .env for the API container. The CLI does not. It defaults to POSTGRES_HOST=localhost, which matches the published port. Leave POSTGRES_HOST=postgres inside .env for the container, and do not export that value into the shell you use for om.

Build the CLI and migrate

go build -o om ./cmd/om
./om migrate

om migrate applies the SQL files in migrations/. Run it from the repository root so the CLI can find that directory.

Load the sample graph

./om scan demo
./om paths list
./om paths run internet-to-datastore
./om graph stats

om scan demo replaces the previous demo account, upserts a small AWS-and-Kubernetes sample, runs every CSPM rule, and prints one attack path.

What the demo shows

The command replaces account ids 111122223333 and cluster-demo, then prints an attack path and up to eight findings from those accounts. The path is Internet → sg-web → web-1 → AdminRole → prod-db. worker-1 has no public IP and only reaches the private bucket acme-assets through AppRole. acme-logs-public is a public bucket that is not on the web-1 path. The Kubernetes sample is a workload named frontend with k8s_service_type: LoadBalancer and image nginx:1.25.3, reachable from the internet and not connected to prod-db. web-1 lists package cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*. worker-1 lists the same product at 2.17.1. om enrich cve attaches CVE-2021-44228 to web-1 because that package matches.

Open http://localhost:8080. The console opens on internet-to-datastore, which should draw that path. Stats should be non-zero, and the findings list should include attack-path rows for web-1 to prod-db, the public datastore, and the internet-exposed workload. web-1 matches the internet-exposed workload rule, the public-IP rule, and the IMDSv1 rule, so each of those becomes its own attack-path finding. The card lists the ids internet:global, aws:sg:sg-web, aws:ec2:i-web-1, aws:iam:role/AdminRole, and aws:rds:prod-db. If the page is empty, the CLI and the API are pointed at different databases. See Docker Compose.

Scan a cloud account

Collectors use each cloud’s default credential chain. They are read-only toward the account. Export variables in the shell, then scan. Details and IAM scope are on the collector pages.

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

export AZURE_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000000
./om scan azure

export GCP_PROJECT_ID=my-project
./om scan gcp

./om scan k8s

After a scan, evaluate rules and attach CVEs that match workload inventory:

./om rules run
./om enrich cve
./om identity blast-radius --name AdminRole

Next

Questions

How do I install OpenSourceOM?

Clone the OpenSourceOM/core repository, start Postgres and the API with Docker Compose, build the om CLI, and run om migrate. Then om scan demo loads a sample graph at http://localhost:8080.

Can I try OpenSourceOM without cloud credentials?

Yes. om scan demo loads a sample environment and runs the CSPM rules. AWS, Azure, GCP, and Kubernetes scans are separate commands and use each cloud’s default credential chain.

Related reading