Security and compliance

Built to be checked

Ingressi stands in front of everything you publish, so it has to earn trust in the open: signed images, an SBOM for every release, a public disclosure policy, and a usage ping that sends nothing unless you say yes.

Signed images and an SBOM for every release

  • Every image the release workflow pushes (ingressi-web, ingressi-caddy and ingressi-l4-port-manager on ghcr.io/ingres-si) is signed with Sigstore cosign keyless signing, tied to the repository's GitHub Actions workflow, and carries an SBOM and a build provenance attestation.
  • Every GitHub release has a CycloneDX SBOM of the source tree attached, covering the JavaScript and Go dependencies.
  • Images are built for linux/amd64 and linux/arm64.
  • Pull requests are built without the right to push images, so they never publish one.
  • Dependabot opens the dependency updates. Only patch and minor updates of the application's JavaScript packages merge automatically; major versions, GitHub Actions, Go modules (the Caddy build and its plugins) and container images wait for a maintainer's review.

Verify an image before you run it:

cosign verify ghcr.io/ingres-si/ingressi-web:latest \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github\.com/ingres-si/ingressi/\.github/workflows/docker-build-trusted\.yml@refs/(heads|tags)/'

The same works for ingressi-caddy and ingressi-l4-port-manager. Releases and their SBOMs · Security policy

Reporting a vulnerability

Report it privately through GitHub's private vulnerability reporting. Do not open a public issue. Include the type of issue, the steps to reproduce it, its impact and a fix if you have one.

  • We answer within 48 hours and keep you updated until it is fixed.
  • We follow coordinated disclosure: keep the details private until a fix is released or 90 days have passed since your report, whichever comes first. We may ask for more time when a fix needs it, and agree it with you.
  • We confirm the issue, agree its severity with you, fix it on the latest release and publish a GitHub Security Advisory, with a CVE when the issue warrants one. Reporters are credited unless they prefer not to be.
  • An actively exploited vulnerability is also reported as the EU Cyber Resilience Act requires of manufacturers.
  • Test only installations you own or are authorised to test. Do not access other people's data or degrade services.

The same contact is published in machine-readable form at /.well-known/security.txt (RFC 9116). We read reports in English and Italian. Supported versions: the latest release, and long-term-support lines within their 24 months once they exist.

Preparing for the Cyber Resilience Act

The Cyber Resilience Act (Regulation (EU) 2024/2847) sets security requirements for software sold in the EU. Its reporting obligations apply from 11 September 2026 and the rest from 11 December 2027. Where Ingressi stands:

CRA requirements and what we do
RequirementWhat we do
A vulnerability handling process and a coordinated disclosure policyPublished in SECURITY.md and in security.txt; see Reporting a vulnerability.
Reporting actively exploited vulnerabilities and severe incidentsReported as Article 14 requires of manufacturers. These obligations apply from 11 September 2026.
A software bill of materialsA CycloneDX SBOM of the source tree attached to every GitHub release, and an SBOM attestation on every image.
Integrity of updatesEvery release image is signed with Sigstore cosign and carries a build provenance attestation.
Security updates for a stated support periodFixes ship on the latest release. Long-term-support lines with 24 months of security fixes are planned for Enterprise; the first one is not announced yet.
Secure by defaultProduction installs refuse example secrets and weak admin passwords, stored secrets are encrypted at rest, and MFA is free for everyone.
Technical documentation, conformity assessment, EU declaration of conformity and CE markingNot done yet. Due by 11 December 2027, when the remaining obligations apply. We do not claim conformity before then.

NIS2: evidence for your own duties

NIS2 (Directive (EU) 2022/2555; in Italy D.Lgs. 138/2024) applies to the organisations that run essential and important services, not to the software they use. The compliance reports of the Enterprise edition turn Ingressi's configuration and audit log into evidence for the risk-management measures of Article 21(2), and prepare the notifications of Article 23. The same reports map to ISO/IEC 27001:2022 Annex A.

The Compliance page: the next scheduled report, the last evidence pack with its findings and hashes, and the status of the live controls.
Compliance: scheduled evidence packs, live control status and the incident register.
How each report supports NIS2
ReportNIS2 measureHow it supports it
Access reviewArt. 21(2)(i) Human resources security, access control policies and asset managementLists every account with its role, permissions and status for a periodic review of access rights, and flags inactive accounts and unused API tokens.
Access reviewArt. 21(2)(j) Multi-factor or continuous authenticationShows which accounts use multi-factor authentication and flags administrators without it.
Change logArt. 21(2)(b) Incident handlingA tamper-evident record of who changed what and when, as input to incident investigation.
Change logArt. 21(2)(e) Security in acquisition, development and maintenance, including vulnerability handling and disclosureRecords maintenance changes to the reverse proxy's configuration.
Certificate inventoryArt. 21(2)(h) Policies and procedures regarding the use of cryptography and encryptionInventory of TLS server, CA and client certificates with issuer, key type and expiry.
Certificate inventoryArt. 21(2)(i) Human resources security, access control policies and asset managementCertificates and the hosts that use them, as managed assets.
Protection coverageArt. 21(2)(f) Policies and procedures to assess the effectiveness of cybersecurity risk-management measuresShows coverage gaps (hosts without WAF, authentication or HSTS) as input to assessing the effectiveness of the measures.
Protection coverageArt. 21(2)(h) Policies and procedures regarding the use of cryptography and encryptionShows HTTPS redirects, HSTS and upstream TLS verification per host.
Protection coverageArt. 21(2)(i) Human resources security, access control policies and asset managementShows which hosts are protected by access lists, forward auth or mTLS.
Protection coverageArt. 21(2)(j) Multi-factor or continuous authenticationShows the MFA coverage of dashboard users.
Incident notification draftsArt. 21(2)(b) Incident handlingStructured preparation of incident notifications from collected facts.
Incident notification draftsArt. 23 Reporting obligations (early warning, incident notification, final report)Drafts of the early warning, incident notification and final report, with their deadlines.

A report is evidence that can support the controls mapped to it. It does not by itself show compliance with NIS2, its national transpositions or ISO/IEC 27001: those also depend on policies, processes and systems outside Ingressi, and on how the evidence is reviewed and acted on. Every report says so. Compliance reports

Security in the product

  • Production installs refuse to start with an example SESSION_SECRET or a weak or example admin password, and every account follows the same password policy.
  • Stored secrets (DNS provider credentials, OAuth client secrets, certificate and CA private keys, sync tokens) are encrypted at rest. On SQLite, deleted ones are also wiped from the database file; PostgreSQL keeps old rows until it reuses their space.
  • Multi-factor authentication with authenticator apps and passkeys is free, with a policy that can require it.
  • Sign-in, the forward-auth portal and password changes are rate limited.
  • The audit log is linked into a hash chain, so an event changed or removed inside it breaks the chain. Verifying the chain is part of audit streaming (Business).
  • Instance sync seals secrets to each replica's own key, pinned the first time the replica presents it.
  • License keys are verified offline, and nothing on the request path checks the license.
  • Virtual patches for new CVEs are coming soon (Enterprise). They will be accepted only from a feed signed with Ed25519 by a key built into the release, and a feed will be refused whole unless every check passes: signature, schema, expiry, sequence, size and an allowlist of WAF rule syntax. Rules will be rebuilt from their checked parts, so a feed cannot turn the WAF off, remove other rules or hide its own matches. Releases ship no feed key yet, so they accept no feed. What the signature protects

The usage ping: off until you say yes

The usage ping tells us how many installs run which versions, editions and features, so we know which releases still need fixes and what deserves more work. It is off until an administrator answers yes to the question on the overview page, where both answers are offered alike and neither is preselected, or, for installs nobody signs in to, the operator sets USAGE_PING_ENABLED=true. Settings → Usage ping shows the exact JSON before anything is sent, and turns it on or off at any time.

When it is on, one document a day, exactly this shape:

{
  "schema": 1,
  "install_id": "0b6f3c1e-2a4d-4f8e-9c3b-5d7e1f2a3b4c",
  "version": "2.0.0",
  "edition": "community",
  "role": "standalone",
  "counts": { "proxy_hosts": "6-20", "l4_hosts": "0", "users": "1-5", "replicas": "0" },
  "features": {
    "waf": true, "forward_auth": false, "clickhouse_analytics": true, "rate_limiting": false,
    "sso_saml": false, "sso_enforce": false, "custom_roles": false, "audit_streaming": false,
    "alerting": false, "config_history": false, "scheduled_backups": false, "ai_analyst": false,
    "approvals": false, "compliance_reports": false, "scim": false, "access_reviews": false,
    "ldap": false, "fleet": false, "high_availability": false, "multi_tenancy": false,
    "white_label": false, "api_monetization": false, "virtual_patching": false
  },
  "arch": "x64"
}
  • Never sent: hostnames, domains, IP addresses, e-mail addresses, user names, license ids or customer names, configuration contents, and nothing from access, WAF or audit logs. Counts are ranges only. Instance sync replicas never send.
  • What we keep: a SHA-256 of the install id (not the id), the first and last day a ping arrived, and the latest values. No IP addresses or user agents, and request logs are off. Installs not seen for 90 days are deleted. The data is stored in the EU.
  • Turning it off deletes the install id and asks the receiver to delete everything stored for it. USAGE_PING_DISABLED=true turns it off for good and hides the question; it sends no deletion request, so data received earlier is deleted after 90 days. The air-gapped bundle sets it.
  • Check it yourself: the dashboard shows the exact document before anything is sent (Settings → Usage ping), and the code that builds it is open source in the product repository (src/lib/usage-ping/payload.ts).

Every field, and the privacy notice