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:
Install and build Valkey and Kvrocks.
Clone Vulnerability-Lookup.
Run
poetry install.Create
.envwithVULNERABILITYLOOKUP_HOME.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 betruefor administrator accounts and publication workflows.fulltextsearch: set tofalseunless Meilisearch is intentionally deployed.external_crontab: keepfalseunless 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_patternandlocal_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.
Confirm that your organisation is an authorized CNA and that administrators have CVE Services credentials.
Enable the CNA publication service in
config/generic.json:{ "cna": true }
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())"
Put your CVE Services URL, CNA short name, and generated Fernet key in
config/cna.json.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.
Ensure your organisation has the appropriate GNA identifier and follows the GCVE operational requirements.
Set
local_instance_nameinconfig/generic.jsonto the GNA identifier/name expected for publication.Keep
local_instance_uuidstable for the lifetime of the instance.Configure local identifier ranges and patterns so the local API and Vulnogram can reserve identifiers consistently.
Initialize or update the local GCVE registry if your workflow needs local registry metadata:
poetry run flask --app website.app update_gcve_registry
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.jsonif used, PostgreSQL, and Kvrocks.Keep
local_instance_uuidand CNA encryption keys stable and secret.Do not run
poetry run feeders_manager --startwhile 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.