CBOM Documentation

CBOM Secure

  • Administrative Guide
  • Login and Authentication
    • Overview
    • Dashboard Interface
    • Dashboard Operations
    • Data Visualization
    • Overview
    • Host
    • Template
      • Networked Deployments
      • Cloud Deployments
      • Off-Grid Deployments
      • Tags
      • Integrations
      • Cloud Servers
      • CycloneDX Export
    • Sensors Reference
    • Overview
    • Summary Report
    • PQC Report
    • AI Services
    • Source Code Visualizer
    • Key
    • Certificate
    • CipherSuites
    • Analysis
    • Policy Management
    • User Policy
    • Standard Policy
    • User Management
    • Roles & Permissions
    • System Configuration
    • Introduction
      • Components
      • Data Flows
      • Execution Models
      • Incremental Scanning
      • Security Model
      • Quick Start
      • Topologies
      • Sizing Guide
      • HA and Scaling
      • HA Runbooks
      • Ports and Firewall
      • Reverse Proxy
      • Prerequisites
      • Docker Compose
      • Native on Linux
      • Deployment Server
      • Install Troubleshooting
      • Validation Checklist
      • Managing Services
      • Logging
      • Certificates
      • Configuration
      • Database Administration
      • Backup and Recovery
      • Upgrade and Migration
      • Ops Troubleshooting
      • Methods
      • SAML 2.0 SSO
      • OIDC and Entra ID
      • Token Authentication
      • Password Policy
      • Sensor vs Connector
      • Sensor Guide Structure
      • Capability Matrix
        • AWS
        • Azure
        • Google Cloud
        • Active Directory
        • LDAP Directories
        • AD CS
        • EJBCA
        • CertSecure Manager
        • Thales Luna HSM
        • Entrust nShield HSM
        • SoftHSM2
        • HashiCorp Vault KMIP
        • MSSQL TDE
        • Oracle TDE
        • MySQL TDE
        • MariaDB TDE
        • HTTPS and TLS
        • SSH Servers
        • Windows Certificate Store
        • Linux Trust Store
        • Java Keystores
        • PKCS#12 Keystores
        • GnuPG Keyrings
        • Binaries and Libraries
        • Source Code Scanning
        • GitHub
        • GitLab
        • Bitbucket
        • Local Git
      • Roadmap
      • Integration Model
      • CrowdStrike
      • ServiceNow
      • ServiceNow and Jira Setup
      • Notification Engine
    • Glossary
    • Configuration Reference
    • Data Model Reference
    • API Reference
    • API Authentication
    • Running a Scan via API
    • Searching via API
    • CycloneDX Export Specification
    • Risk and PQC Scoring
    • Built-in Standards
    • Widget Catalogue
    • AI Services Data Handling

Reverse Proxy Configuration

CBOM Secure is reached through a single reverse proxy. It is the only component exposed outside the platform segment, it terminates TLS, and it routes every path to the service behind it. This page is what it routes, what each route needs, and how to replace or re-front it.

The routing table

Path prefixGoes toNotes
/ConsoleThe catch-all. Everything not matched below.
/cbomsrv/Backend APILarge request bodies — see below.
/collector/CollectorResult submission over HTTPS. Unlimited body size.
/auth/Identity providerSign-in and single sign-on.
/aisrv/AI service
/grafana/Telemetry dashboards
/grafana/api/live/Telemetry dashboardsWebSocket upgrade. Needs its own block.
/rabbitmq/Broker management portal
/ui/srcvisualizer/Backend APIServer-rendered, so it cannot fall through to the console.
/helpdocs/ConsoleStatic documentation, shipped with the console.

Only 443 is published. Port 80 exists to redirect to it and carries no application traffic.

What each route needs

Body size is not uniform, and the defaults will break things. The two upload paths need explicit limits:

RouteLimitWhy
/cbomsrv/512 MBHost CSV import and off-grid result bundles
/collector/unlimitedA scan of a large host can submit a very large batch

A proxy left on a default of 1 MB accepts small scans and rejects large ones, which reads as "discovery is unreliable" rather than as a proxy setting.

Three routes must not have their prefix stripped. The identity provider, the telemetry dashboards and the broker portal each serve their own asset URLs under the prefix they are reached at. Stripping it gives a page that loads and whose stylesheets and scripts all 404 — the page renders unstyled and nothing works, with no error anywhere that names the proxy.

The collector is the opposite: its prefix is stripped, because it serves its paths at the root.

One route is a WebSocket. The telemetry dashboards' live endpoint needs Upgrade and Connection headers passed through, in a block of its own ahead of the general dashboard block.

Two upstreams must be resolved per request, not at startup. The AI service and the broker portal are optional — a deployment can run without either. A proxy that resolves every upstream name at startup refuses to start when an optional service is absent, which turns an optional component into a required one. Resolve those two per request instead.

Headers

Every route needs the client's real identity forwarded, or the backend sees the proxy as the client:

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

X-Forwarded-Proto matters more than it looks: without it the backend builds redirect URLs as http://, and a sign-in redirect downgrades to plaintext.

The security headers applied at the server level are HSTS, X-Content-Type-Options, X-Frame-Options, a referrer policy and a permissions policy.

A header set at server level is replaced, not merged, by a location that sets any header of its own. This is an nginx rule people lose hours to: a location block that adds one header silently drops every server-level header for that route. Repeat the full set in each location that declares one. The shipped configuration does exactly that, which is why those blocks look repetitive.

TLS

The shipped configuration offers TLS 1.2 and 1.3 only, and reads its certificate and key from a directory mounted read-only into the proxy. For a product whose purpose is cryptographic inventory, a weak configuration here is worth avoiding on principle as well as in fact — the platform's own endpoint is the first thing a customer will scan.

To replace the certificate:

  1. Put the new certificate and key in the certificate directory, keeping the same file names.

  2. Reload rather than restart: docker compose exec nginx nginx -s reload. A reload picks up the certificate without dropping connections.

  3. Confirm: openssl s_client -connect cbom.example.com:443 -servername cbom.example.com.

The certificate must carry the name in PUBLIC_BASE_URL. Sign-in redirects are checked against that name, so a mismatch fails authentication rather than merely warning.

Full certificate guidance, including bringing your own chain, is on the Certificates page.

Putting your own proxy in front

Many organisations front the platform with an existing load balancer, WAF or API gateway. That works, and the platform's own proxy stays in place behind it — it is doing path routing the outer device would otherwise have to reproduce.

The outer device must:

  • Terminate or pass through TLS, with a certificate carrying the public name.

  • Forward the four headers above. An outer device that sets its own X-Forwarded-For should append rather than replace.

  • Allow large bodies on /cbomsrv/ and /collector/, at least as generously as the inner proxy.

  • Pass WebSocket upgrades through on /grafana/api/live/.

  • Use session affinity if it balances across more than one platform node. Sessions are held in memory by the backend instance that created them — see High Availability Runbooks.

  • Not rewrite paths. The inner proxy's routing table assumes the paths above arrive intact.

Then set PUBLIC_BASE_URL and the other public addresses in .env to the outer device's name, and rebuild the console so its compiled backend URL matches.

Running the platform under a sub-path

Not supported. The routing table above is rooted, and the console's compiled asset paths, the identity provider's redirect URIs and the documentation's own links all assume it. Give the platform its own hostname rather than a path on a shared one.

Verifying a proxy change

# Config is valid before you reload
docker compose exec nginx nginx -t

# Each route answers
curl -sk -o /dev/null -w '%{http_code} /\n'          https://cbom.example.com/
curl -sk -o /dev/null -w '%{http_code} /cbomsrv\n'   https://cbom.example.com/cbomsrv/api/v1/
curl -sk -o /dev/null -w '%{http_code} /auth\n'      https://cbom.example.com/auth/
curl -sk -o /dev/null -w '%{http_code} /helpdocs\n'  https://cbom.example.com/helpdocs/index.html

A 401 from the API route is a correct answer — it means the route reached the backend and the backend asked for authentication. A 502 means it did not reach it at all.

Then sign in through a browser. The round trip through the identity provider exercises the redirect handling, which is the part a curl check does not.

Troubleshooting

502 Bad Gateway on one route. The service behind it is not running, or its name does not resolve. docker compose ps first, then the proxy's error log — it names the upstream it could not reach.

The proxy will not start after a change. Usually an upstream name that cannot be resolved at startup. Optional services must be resolved per request instead of in an upstream block.

A page loads unstyled and nothing works. The prefix was stripped from a route that needs it kept. Check the identity provider, dashboard and broker portal routes.

Sign-in loops back to the sign-in page. PUBLIC_BASE_URL or the identity provider's public URL does not match the name being used, or X-Forwarded-Proto is missing so a redirect downgraded to http://.

Large uploads fail at a consistent size. The body size limit. Raise it on the outer device as well as the inner proxy — the smaller of the two wins.

The telemetry dashboards load but show no live data. The WebSocket upgrade is not passing through. Check that the live endpoint has its own block, placed before the general one.

Security headers are missing on one route only. That location sets a header of its own and so replaced the server-level set. Repeat the full set there.

On this Page:
Support Privacy Policy Terms of Service

Copyright 2026 Encryption Consulting LLC. All rights reserved.