GUAC
Overview
GUAC (Graph for Understanding Artifact Composition) is an OpenSSF project that turns the scattered metadata about your software supply chain into a single, connected, queryable graph. It ingests the documents your teams already produce, such as SBOMs in CycloneDX or SPDX form, SLSA build attestations and vulnerability reports. It normalises them into a common model of packages, sources, builders and the dependencies between them.
Instead of grepping across a pile of SBOM files, you ask questions of the graph over GraphQL: which artifacts depend on a given package, what versions are running where, and which builds are affected by a newly disclosed vulnerability.
The SecureAnyCloud image installs the pinned upstream v1.1.0 release binaries, each verified by SHA-256. It runs the GUAC GraphQL server and the collectsub service as hardened systemd units, backed by a local PostgreSQL 16 using GUAC's ent (SQL) backend. This is the light, self-contained backend that fits a 2 vCPU / 4 GiB VM, with no Neo4j to operate.
What you get:
- GUAC v1.1.0 Go binaries, all pinned by SHA-256 (Apache-2.0):
guacgql(GraphQL server),guaccsub(collectsub) andguacone(ingest CLI) - PostgreSQL 16 from the official PGDG repository, bound to loopback, using GUAC's ent backend (auto-migrated on first start)
guacgql.serviceandguac-collectsub.servicerunning as theguacuser, gated on the first-boot marker- The GraphQL API and its interactive GraphiQL Playground on loopback (
127.0.0.1:8080), fronted by nginx Basic auth on port 80, a single routable port - A ufw host firewall that is deny-by-default and allows only SSH (22) and the web port (80), keeping the GraphQL server, the collectsub service and PostgreSQL unreachable off-box
guac-firstboot.service, which generates this VM's own PostgreSQL, database and web credentials, brings the services up, and ingests a bundled sample SBOM so the graph is populated and queryable immediately- No shared or default login in the image
- An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- 24/7 SecureAnyCloud support, 24h response SLA
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet.
The recommended VM size is Standard_B2s (2 vCPU / 4 GiB) for evaluation and single-team use. Scale up for larger graphs.
You reach the GraphQL API and Playground over HTTP on port 80, protected by per-VM Basic authentication.
Deploy from the Azure Portal
Search the Azure Marketplace for GUAC 1.1 on Ubuntu 24.04. Create the VM with a network security group (NSG) that allows:
- TCP 22 (SSH) from your management network
- TCP 80 (the GraphQL API and Playground) from your admin network
The GraphQL server (8080), the collectsub service (2782) and PostgreSQL (5432) are never exposed. The on-host ufw firewall blocks them, and nginx is the sole entry point.
Deploy from the Azure CLI
RG="guac-prod"; LOCATION="eastus"; VM_NAME="guac-01" IMAGE_URN="sac1647283583153:guac-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name guac-vnet --address-prefix 10.106.0.0/16 --subnet-name guac-subnet --subnet-prefix 10.106.1.0/24 az network nsg create -g "$RG" --name guac-nsg az network nsg rule create -g "$RG" --nsg-name guac-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name guac-nsg --name allow-web --priority 110 \
--source-address-prefixes "<your-admin-cidr>" --destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:guac-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name guac-vnet --subnet guac-subnet --nsg guac-nsg --public-ip-sku StandardConnect via SSH
ssh azureuser@<vm-ip>Verify the services
Confirm that first boot completed and that the GraphQL server, collectsub service, PostgreSQL and nginx are all running:
systemctl is-active postgresql guacgql guac-collectsub nginx test -f /var/lib/sac/data/guac-firstboot.done && echo "first boot: complete"Review the security posture
Upstream, GUAC's GraphQL server and collectsub service bind all interfaces, and the GraphQL /query endpoint is unauthenticated and mutating. The appliance is therefore locked down in two ways. A deny-by-default ufw firewall opens only SSH and the web port, and nginx Basic auth sits in front of the loopback GraphQL server.
Confirm both:
sudo ufw status | grep ALLOW ss -tln | grep -E ':22 |:80 |:8080|:2782|:5432'Only ports 22 and 80 are allowed inbound, and PostgreSQL is bound to 127.0.0.1 only.
Retrieve the per-VM credentials
Each VM creates its own PostgreSQL, database and web (Basic auth) credentials at first boot. Read them from the root-only file:
sudo stat -c '%a %U:%G' /root/guac-credentials.txt
sudo grep -E '^(web|postgres|guac_db)\.' /root/guac-credentials.txt# --- The GUAC GraphQL Playground + API (via nginx Basic auth on port 80) --- web.url=<GUAC_URL> web.user=guac web.password=<BASIC_PASSWORD>
# --- PostgreSQL superuser + guac DB role (LOOPBACK ONLY) --- postgres.password=<POSTGRES_PASSWORD> guac_db.password=<GUAC_DB_PASSWORD> guac_db.database=guacVerify authentication and the graph end to end
The routable API enforces Basic auth: an anonymous request is refused, and the per-VM credential succeeds. The bundled guac-selfcheck checks the whole appliance: the firewall, the loopback binding, authentication, and that a real query returns the ingested graph.
WEBPW=$(sudo grep '^web.password=' /root/guac-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'anonymous /query -> HTTP %{http_code}\n' http://127.0.0.1/query -X POST -H 'content-type: application/json' --data '{"query":"{__typename}"}' curl -s -o /dev/null -w 'authenticated / -> HTTP %{http_code}\n' -u "guac:$WEBPW" http://127.0.0.1/
sudo guac-selfcheck | tail -1The expected output is anonymous /query -> 401, authenticated / -> 200, and a self-check line confirming that the graph returned the ingested package nodes.
Open the GraphQL Playground
Open http://<vm-ip>/ in your browser. When prompted for the Basic credential, sign in as guac with the password from Step 6.
The interactive GraphiQL Playground opens. Use it to write and run GraphQL queries against your supply-chain graph.
Query the supply-chain graph
The image ships with a sample SBOM already ingested, so the graph is populated the moment you open it. Run this query to list every package in the graph with its namespace and name:
{ packages(pkgSpec: {}) { type namespaces { namespace names { name } } } }The result pane returns the package nodes parsed from the sample SBOM: a generic application and the deb packages openssl and curl.
Expand the query to include versions and qualifiers. This shows the full detail GUAC extracted from each package URL:
{ packages(pkgSpec: {}) { type namespaces { namespace names { name versions { version qualifiers { key value } } } } } }Filter the graph by ecosystem
Every query accepts a pkgSpec filter, so you can scope questions to a package type, namespace, name or version. Filter to just the Debian packages:
{ packages(pkgSpec: {type: "deb"}) { type namespaces { namespace names { name } } } }Ingest your own documents
Add your own SBOM, SLSA or vulnerability documents to the graph with the guacone CLI. It parses and ingests directly through the GraphQL API, so no message broker is required, and it auto-detects CycloneDX, SPDX and SLSA formats.
Re-ingest the bundled sample, or point the command at your own file or directory:
sudo -u guac env HOME=/var/lib/guac guacone collect files \
--gql-addr http://localhost:8080/query /usr/share/guac/samples/For continuous, watched collection across many sources, GUAC also ships the guaccollect and guacingest pipeline, which uses a NATS broker. That pipeline is out of scope for this single-node appliance but can be layered on a larger VM.
Manage the services
sudo systemctl status guacgql --no-pager sudo systemctl restart guacgql
sudo journalctl -u guacgql -n 50 --no-pagerThe GraphQL server reads /etc/guac/guac.yaml, which holds the per-VM database DSN. The graph lives in the local guac PostgreSQL database. Back up the database to preserve your ingested graph.
Security recommendations
- Restrict the NSG. Expose port 80 only to your admin network and keep SSH (22) limited to management ranges. The on-host
ufwfirewall already denies everything else by default. - Front the service with TLS. For production, terminate HTTPS, for example with your own certificate in nginx or with an Azure Application Gateway or Front Door. This encrypts the Basic credential and query traffic in transit.
- Rotate the web credential. Add further htpasswd users as needed (
sudo htpasswd /etc/nginx/guac.htpasswd <name>) instead of sharing theguaclogin. - Keep PostgreSQL and the GraphQL server private. They bind loopback or are firewalled, and the only off-box path to the graph is through the nginx Basic-auth proxy.
Support and licensing
GUAC is licensed under the Apache License 2.0. The complete source is available from the upstream project at github.com/guacsec/guac. The Apache-2.0 licence text ships in the image at /usr/share/doc/guac/LICENSE.
PostgreSQL (PostgreSQL License) and nginx (2-clause BSD) are likewise permissive and ship unmodified.
Need Help?
SecureAnyCloud images come with 24/7 support and a 24-hour response SLA. Contact support through the Azure Marketplace listing or at secureanycloud.com.
Deploy on Azure
Launch GUAC 1.1 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com