Minimal CNA/GNA Publication Instance#

This guide describes how to run a lightweight Vulnerability-Lookup instance dedicated to vulnerability and advisory publication workflows. It is intended for organisations that want to use Vulnerability-Lookup primarily as:

  • a CNA publication workstation for reserving, publishing, rejecting, or updating CVE records through CVE Services; or

  • a GNA publication node for reserving and publishing GCVE identifiers from the local instance.

In this mode, the instance keeps the web application, local vulnerability source, user accounts, and publication features, but does not continuously import third-party vulnerability feeds. This keeps disk, CPU, memory, network, and API-key requirements lower than a full correlation instance.

Note

A minimal publication instance is still a production security application. Keep the host patched, protect administrator accounts with strong authentication, back up PostgreSQL and Kvrocks, and expose the service through HTTPS when it is reachable from the Internet.

What is disabled#

A full Vulnerability-Lookup deployment imports vulnerabilities from the configured feeders in config/modules.cfg. For a publication-only deployment, disable every feeder and do not start the feeders manager. This prevents imports from sources such as NVD, CVE List, CSAF providers, OSV feeds, KEV catalogs, and GCVE remote feeds.

The resulting instance is suitable for managing records produced by your organisation, but it will not provide the cross-source correlation and enrichment features expected from a full Vulnerability-Lookup instance. If you later need those features, re-enable the desired feeders and start them again.

Components to keep#

Keep the following components enabled:

  • Kvrocks: stores vulnerability records and local publication indexes.

  • Valkey: cache and service coordination.

  • PostgreSQL: required for user accounts, roles, CNA credentials, comments, bundles, and web application state.

  • Web service: provides the publication UI, API, and Vulnogram integration.

  • User accounts: required so administrators can authenticate and use publication features.

Optional components that are normally unnecessary for a minimal publication instance:

  • Meilisearch/full-text search: useful for public search portals, not required for publication.

  • CSAF downloader: only required when CSAF feeders are enabled.

  • Feeder submodules and API keys: not required when all feeders are disabled.

Installation outline#

Follow the standard installation guide for the base services and Python dependencies:

  1. Install and build Valkey and Kvrocks.

  2. Clone Vulnerability-Lookup.

  3. Run poetry install.

  4. Create .env with VULNERABILITYLOOKUP_HOME.

  5. Copy the sample configuration files.

For a minimal publication deployment, you may skip the CSAF support tools and external feeder API keys unless you plan to enable related feeders later.

Base configuration#

Copy the standard configuration files:

cp config/generic.json.sample config/generic.json
cp config/logging.json.sample config/logging.json
cp config/website.py.sample config/website.py
cp config/stream.json.sample config/stream.json
cp config/fulltextsearch.json.sample config/fulltextsearch.json
cp config/modules.cfg.sample config/modules.cfg

Edit config/generic.json with at least the following settings:

{
  "public_domain": "vuln-publication.example.org",
  "user_accounts": true,
  "fulltextsearch": false,
  "external_crontab": false,
  "local_instance_name": "YOURORG",
  "local_instance_uuid": "8627204d-fee7-4bdf-8707-5f0833b57c82",
  "local_instance_vulnid_pattern": "^YOURORG-SA-[0-9]{4}-[0-9]{4,19}$",
  "local_instance_vulnid_example": "YOURORG-SA-yyyy-nnnn"
}

Important fields:

  • public_domain: the externally visible host name used in generated links and feeds. Use the public HTTPS host name in production.

  • user_accounts: must be true for administrator accounts and publication workflows.

  • fulltextsearch: set to false unless Meilisearch is intentionally deployed.

  • external_crontab: keep false unless you manage any enabled feeders from cron.

  • local_instance_name: use your local source name. For a GNA, use the allocated GNA identifier/name expected by the GCVE ecosystem.

  • local_instance_uuid: generate a stable UUID once and keep it in backups. Do not regenerate it after publication.

  • local_instance_vulnid_pattern and local_instance_vulnid_example: define the local advisory identifier format used before or alongside CVE/GCVE IDs.

Generate a UUID with:

python -c "import uuid; print(uuid.uuid4())"

Disable all feeders#

Disable every feeder in config/modules.cfg by adding or changing enabled = false in each [feeder:*] section. The following command updates the copied configuration file in place:

python - <<'PY'
from pathlib import Path

path = Path("config/modules.cfg")
lines = path.read_text().splitlines()
out = []
in_feeder = False
seen_enabled = False

for line in lines:
    stripped = line.strip()
    if stripped.startswith("[") and stripped.endswith("]"):
        if in_feeder and not seen_enabled:
            out.append("enabled = false")
        in_feeder = stripped.startswith("[feeder:")
        seen_enabled = False
        out.append(line)
        continue
    if in_feeder and stripped.lower().startswith("enabled"):
        out.append("enabled = false")
        seen_enabled = True
    else:
        out.append(line)

if in_feeder and not seen_enabled:
    out.append("enabled = false")

path.write_text("\n".join(out) + "\n")
PY

Verify that no feeder remains enabled:

poetry run python - <<'PY'
from vulnerabilitylookup.helpers import get_feeders

feeders = get_feeders()
if feeders:
    raise SystemExit(f"Enabled feeders remain: {', '.join(feeders)}")
print("No enabled feeders")
PY

Warning

Do not use poetry run start for a publication-only instance with zero enabled feeders. The start command launches feeders_manager --start, and the feeders manager expects at least one enabled feeder. Start only the backend and website as shown below.

PostgreSQL and administrator account#

Install PostgreSQL, create the database, and configure config/website.py as described in the standard installation guide. Then initialize the database and create an administrator account:

poetry run flask --app website.app db_init
poetry run flask --app website.app db stamp head
poetry run flask --app website.app create_admin --login admin --email admin@example.org --password '<change-me>'

Optional but recommended for a web-facing publication instance:

poetry run flask --app website.app import_osi_approved_licenses
poetry run flask --app website.app import_languages

CNA publication setup#

Use this section if the instance will publish CVE records as a CNA.

  1. Confirm that your organisation is an authorized CNA and that administrators have CVE Services credentials.

  2. Enable the CNA publication service in config/generic.json:

    {
      "cna": true
    }
    
  3. Copy and configure the CNA settings:

    cp config/cna.json.sample config/cna.json
    python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
    
  4. Put your CVE Services URL, CNA short name, and generated Fernet key in config/cna.json.

  5. Start the application, log in as an administrator, and enter each administrator’s CVE Services credentials under Profile Your CNA credentials.

See the dedicated CNA publication documentation for the full publication lifecycle and error-handling behaviour.

GNA / GCVE publication setup#

Use this section if the instance will publish GCVE identifiers as a GNA.

  1. Ensure your organisation has the appropriate GNA identifier and follows the GCVE operational requirements.

  2. Set local_instance_name in config/generic.json to the GNA identifier/name expected for publication.

  3. Keep local_instance_uuid stable for the lifetime of the instance.

  4. Configure local identifier ranges and patterns so the local API and Vulnogram can reserve identifiers consistently.

  5. Initialize or update the local GCVE registry if your workflow needs local registry metadata:

    poetry run flask --app website.app update_gcve_registry
    
  6. Publish from the local source through the web UI or API.

The GCVE-BCP-02 practical guide is a useful operational reference for preparing a vulnerability handling process, defining roles, handling reports, coordinating disclosure, and publishing advisories: https://gcve.eu/bcp/gcve-bcp-02/. For publication-specific GCVE requirements, also review the current GCVE publication and BCP documentation before exposing the instance publicly.

Starting and stopping#

Start only the backend services and website:

poetry run run_backend --start
poetry run start_website

Stop the website and backend with:

poetry run stop

If you manage services with systemd, create separate units for the backend and web service, or adapt the existing service examples so they do not call poetry run start unless at least one feeder is enabled.

Smoke tests#

After startup, run these checks from the project directory:

poetry run python - <<'PY'
from vulnerabilitylookup.helpers import get_feeders
assert not get_feeders(), "feeders are still enabled"
print("No enabled feeders")
PY
curl -f http://127.0.0.1:10001/ >/dev/null

For CNA deployments, also verify that the web application starts without a CNA Fernet-key error and that an administrator can save CNA credentials. For GNA deployments, verify that local identifier reservation and local publication work in a staging environment before accepting real reports.

Maintenance notes#

  • Back up config/generic.json, config/website.py, config/cna.json if used, PostgreSQL, and Kvrocks.

  • Keep local_instance_uuid and CNA encryption keys stable and secret.

  • Do not run poetry run feeders_manager --start while every feeder is disabled.

  • Re-run the feeder verification command after upgrades or configuration changes.

  • If the instance later becomes a public correlation portal, selectively re-enable feeders in config/modules.cfg, install their prerequisites, and start the feeders manager.