Skip to content

Proxy Configuration (Restricted / No Internet Access)

Repod is fully usable with zero internet access — nothing it does requires outbound connectivity by default. But a handful of optional features reach out to public sources, and if your server sits behind a restricted egress (a security team that only allows specific domains, or a fully air-gapped site), you need to know exactly what to allow — and how to route it through a proxy.

This page lists every outbound dependency, explains how to route them through a corporate proxy, and gives a worked example using Squid with a domain-allowlist, which is the pattern most regulated environments already use for controlled egress.


What actually needs internet access

Nothing in the core upload → scan → publish → serve pipeline requires internet access. Everything below is either optional or only triggered by a specific action.

Dependency Triggered by Can run fully offline?
ClamAV signature updates Automatic (freshclam daemon) Yes — works with whatever signatures are already cached; only updates need connectivity
Grype vulnerability database Automatic, periodic Yes — same as above, CVE matching uses the last successfully downloaded database
EPSS scores / CISA KEV catalog Every CVE scan Yes — 24h local cache; a package that was already scanned once keeps working offline
Upstream security sync (Debian/Ubuntu/RPM security advisories) Scheduled sync job (disabled by default) N/A — this feature is inherently about fetching external advisories
"Import from internet" (upstream package mirrors, Maven Central, PyPI, npmjs.org, Docker Hub) Explicit user action ("Import" tab) N/A — only used if you choose to import from a public source
Upstream cache (pull-through proxy for apt/dnf clients) Client requests to /upstream/... N/A — see Upstream cache, a separate air-gap-oriented feature
License usage reporting Opt-in only (settings["license_reporting"]["enabled"]) Yes — license validation is 100% offline; reporting is purely informational
LDAP / OIDC / SMTP / webhook notifications Whatever you configure Depends entirely on where your IdP/SMTP relay/webhook endpoint lives (often internal)

If your policy is "no internet, full stop", the pipeline still runs — CVE and antivirus scanning use whatever data was cached during your last connected window (or an air-gapped cache seeded from a relay host), and you simply never use the "Import from internet" tab.


Routing outbound calls through a proxy

Most of the table above goes over plain HTTP(S) from the backend container, using clients that respect the standard proxy environment variables. Set these in backend.env:

backend.env
HTTP_PROXY=http://proxy.internal.example.com:3128
HTTPS_PROXY=http://proxy.internal.example.com:3128
NO_PROXY=localhost,127.0.0.1,db,depot-apt,depot-rpm,YOUR_INTERNAL_LDAP_HOST

This covers: the CVE/EPSS/KEV enrichment calls, the "Import from internet" feature (upstream mirrors, Maven Central, PyPI, npmjs.org, Docker Hub search), and Grype's own database updates — all of them use HTTP clients that honor these variables automatically, no extra configuration needed.

ClamAV's freshclam is the one exception

freshclam does not read HTTP_PROXY/HTTPS_PROXY — it only understands its own config directives. If you need ClamAV updates to go through an explicit (non-transparent) proxy, mount a freshclam.conf override:

freshclam.conf
HTTPProxyServer proxy.internal.example.com
HTTPProxyPort 3128
docker-compose.override.yaml
services:
  backend:
    volumes:
      - ./freshclam.conf:/etc/clamav/freshclam.conf:ro

If your proxy is deployed in transparent/intercepting mode at the network level (see below), this override isn't needed — freshclam doesn't know it's being proxied either way.


Worked example: Squid with a domain allowlist

Rather than a general-purpose "allow everything" proxy, most regulated environments want an explicit allowlist — only the domains Repod actually needs, nothing else. Squid's dstdomain ACL is the standard tool for this.

1. Allowlist file

squid/allowed_domains.txt
# Security feeds
api.first.org
www.cisa.gov

# ClamAV / Grype update infrastructure — consult their own docs for the
# current full list, these CDN domains do change over time
database.clamav.net
.cvdupdate.clamav.net
grype.anchore.io
toolbox-data.anchore.io

# OS package mirrors — only the ones matching the distributions you serve
archive.ubuntu.com
security.ubuntu.com
deb.debian.org
security.debian.org
dl-cdn.alpinelinux.org
repo.almalinux.org
dl.rockylinux.org
mirror.stream.centos.org
download.opensuse.org

# Build-tool ecosystems — only if you use "Import from internet" for these
repo1.maven.org
search.maven.org
pypi.org
registry.npmjs.org
hub.docker.com
registry-1.docker.io
production.cloudflare.docker.com

2. squid.conf

squid/squid.conf
http_port 3128

acl allowed_domains dstdomain "/etc/squid/allowed_domains.txt"
acl repod_net src 172.20.0.0/16   # adjust to your Docker network / server subnet

http_access allow repod_net allowed_domains
http_access deny all

# Standard hardening
via off
forwarded_for delete
request_header_access X-Forwarded-For deny all

3. Run it (standalone, or as a compose overlay)

docker-compose.proxy.yaml
services:
  squid:
    image: ubuntu/squid:latest
    container_name: repod-squid
    ports:
      - "127.0.0.1:3128:3128"   # bind to loopback; only the Repod host should reach it
    volumes:
      - ./squid/squid.conf:/etc/squid/squid.conf:ro
      - ./squid/allowed_domains.txt:/etc/squid/allowed_domains.txt:ro
    restart: unless-stopped
docker compose -f docker-compose.proxy.yaml up -d

Then point backend.env at it:

backend.env
HTTP_PROXY=http://squid:3128
HTTPS_PROXY=http://squid:3128

(If squid runs on the same Docker network as backend, use the service name as the hostname; otherwise use the host's proxy port.)

4. Verify

# From inside the backend container:
docker exec backend-api curl -sI https://api.first.org
# Should succeed (allowlisted)

docker exec backend-api curl -sI https://example.com
# Should be denied by Squid (not on the allowlist)

Watch docker logs repod-squid (or Squid's access.log) while triggering a CVE scan or an "Import from internet" — any legitimately needed domain that gets denied will show up there as TCP_DENIED, letting you extend the allowlist deliberately rather than guessing upfront.


Fully air-gapped (no proxy at all)

If the server truly has no outbound path — not even through a proxy — see Upstream cache for seeding package mirrors via removable media through a relay host, and rely on the local EPSS/KEV/Grype/ClamAV caches for CVE and antivirus data (refreshed manually during a maintenance window with connectivity, if one ever exists — otherwise the pipeline keeps using whatever was cached at deployment time).