feat(geo): GEO_FORCE_OFFLINE flag to suppress runtime ipinfo.io egress (#382) - #474
Conversation
#382) Add a global kill-switch that suppresses the one intentional runtime egress (ipinfo.io) for strict air-gapped / egress-monitored deployments. When tracepcap.geo.force-offline=true, GeoIpService.isOnline() short-circuits to false before any network call, skipping both the connectivity probe and all ipinfo.io lookups. Geo resolves exclusively from the bundled DB-IP Lite MMDB (geo_source = mmdb). Default off — the existing online/offline auto-fallback is unchanged. - GeoIpService: force-offline gate + startup log line - application.yml: geo.force-offline (GEO_FORCE_OFFLINE, default false) - docker-compose.yml: passthrough (default false) - docker-compose.offline.yml: default true for the air-gapped stack - .env.example: documented flag + the one intentional egress - docs/offline-deployment.rst: egress-suppression section Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a ChangesGEO_FORCE_OFFLINE flag
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant Backend
participant GeoIpService
participant ipinfo_io
Backend->>GeoIpService: init()
alt forceOffline enabled
GeoIpService->>GeoIpService: log force-offline mode
GeoIpService->>GeoIpService: isOnline() returns false
GeoIpService->>GeoIpService: resolve geo from bundled MMDB
else forceOffline disabled
GeoIpService->>ipinfo_io: connectivity probe / lookup
ipinfo_io-->>GeoIpService: response or timeout
end
Related issues: Suggested labels: enhancement, backend, documentation Suggested reviewers: NotYuSheng 🐰 A hop, a flag, an offline switch, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces the GEO_FORCE_OFFLINE configuration option to suppress all outbound egress (connectivity probes and ipinfo.io lookups) for strict air-gapped or egress-monitored deployments, forcing the system to resolve geolocation exclusively from the bundled MMDB. The feedback suggests a security improvement to also short-circuit the connectivity check when GeoIP enrichment is disabled (geoEnabled is false) to prevent any accidental egress.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Short-circuit isOnline() to false when geoEnabled is false, not just when forceOffline is set — prevents the ipinfo.io connectivity probe from firing via currentSource() when GeoIP enrichment is disabled. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/com/tracepcap/analysis/service/GeoIpService.java (1)
274-297: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShort-circuit correctly suppresses all egress.
isOnline()returningfalsebefore any network call correctly ensureslookupExternal()'sonline ? lookupFromIpInfo(ip) : nullbranches always skip ipinfo.io, andcurrentSource()reports"mmdb". This matches the hybrid offline-fallback strategy required forGeoIpService. No test currently exercises this path — worth adding a unit test assertingisOnline()returnsfalseand no HTTP connection is attempted whenforce-offlineis set, given this is the crux of the air-gapped compliance guarantee.As per path instructions,
**/GeoIpService.java: "the application must function fully offline with no external API calls at runtime... hybrid strategy with ipinfo.io as primary... and DB-IP Lite MMDB... as automatic offline fallback."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/main/java/com/tracepcap/analysis/service/GeoIpService.java` around lines 274 - 297, Add a unit test around GeoIpService.isOnline() that sets forceOffline and verifies it returns false without attempting any HTTP connection, so lookupExternal() continues to skip lookupFromIpInfo(ip) and currentSource() stays on mmdb. Use the isOnline() path as the target behavior, and mock or spy the network probe so the test asserts no openConnection/getResponseCode interaction occurs when force-offline is enabled.Source: Path instructions
♻️ Duplicate comments (1)
docs/getting-started/offline-deployment.rst (1)
66-77: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winStep 2 should call out the
GEO_FORCE_OFFLINEdefault trap.Copying
.env.exampleverbatim (as instructed here) setsGEO_FORCE_OFFLINE=false, which — per Docker Compose precedence — overridesdocker-compose.offline.yml's intendedtruedefault. Given the new subsection below states this is "the recommended setting for the offline compose stack" (line 44), Step 2 should explicitly instruct settingGEO_FORCE_OFFLINE=truein the copied.envfor air-gapped use. See linked comment ondocker-compose.offline.yml.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/getting-started/offline-deployment.rst` around lines 66 - 77, The offline setup instructions currently tell users to copy .env.example verbatim, which can leave GEO_FORCE_OFFLINE set to false and override the offline compose default. Update the Step 2 guidance in offline-deployment.rst to explicitly instruct users to set GEO_FORCE_OFFLINE=true in the copied .env for air-gapped deployments, and ensure the wording aligns with the offline compose stack guidance already referenced in docker-compose.offline.yml.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.env.example:
- Around line 45-52: The offline template still hardcodes GEO_FORCE_OFFLINE to
false, which prevents docker-compose.offline.yml’s default fallback from taking
effect. Update the .env.example entry for GEO_FORCE_OFFLINE so the offline
template sets it to true by default, aligning with the intended air-gapped
behavior and the existing fallback logic.
---
Outside diff comments:
In `@backend/src/main/java/com/tracepcap/analysis/service/GeoIpService.java`:
- Around line 274-297: Add a unit test around GeoIpService.isOnline() that sets
forceOffline and verifies it returns false without attempting any HTTP
connection, so lookupExternal() continues to skip lookupFromIpInfo(ip) and
currentSource() stays on mmdb. Use the isOnline() path as the target behavior,
and mock or spy the network probe so the test asserts no
openConnection/getResponseCode interaction occurs when force-offline is enabled.
---
Duplicate comments:
In `@docs/getting-started/offline-deployment.rst`:
- Around line 66-77: The offline setup instructions currently tell users to copy
.env.example verbatim, which can leave GEO_FORCE_OFFLINE set to false and
override the offline compose default. Update the Step 2 guidance in
offline-deployment.rst to explicitly instruct users to set
GEO_FORCE_OFFLINE=true in the copied .env for air-gapped deployments, and ensure
the wording aligns with the offline compose stack guidance already referenced in
docker-compose.offline.yml.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 77a8593a-f688-4de1-8157-f270ec3b9f12
📒 Files selected for processing (6)
.env.examplebackend/src/main/java/com/tracepcap/analysis/service/GeoIpService.javabackend/src/main/resources/application.ymldocker-compose.offline.ymldocker-compose.ymldocs/getting-started/offline-deployment.rst
An explicit GEO_FORCE_OFFLINE=false in a copied .env overrides the offline
compose file's ${GEO_FORCE_OFFLINE:-true} default, silently keeping air-gapped
deployments on the ipinfo.io probe path. Comment the line out so each compose
file's own default governs (base=false, offline=true).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes #382.
What
Adds a
GEO_FORCE_OFFLINEglobal kill-switch (default off) that suppresses the one intentional runtime egress —ipinfo.iogeolocation enrichment — for strict air-gapped / egress-monitored deployments.When
tracepcap.geo.force-offline=true,GeoIpService.isOnline()short-circuits tofalsebefore any network call, so both the connectivity probe and everyipinfo.iolookup are skipped. Geo resolves exclusively from the bundled DB-IP Lite MMDB (geo_source = mmdb).currentSource()reportsmmdbtoo, since it routes throughisOnline(). Default behaviour (online probe + graceful MMDB fallback) is unchanged.Changes
GeoIpService.java—tracepcap.geo.force-offline@Value+ force-offline gate inisOnline()+ a startup log line.application.yml—geo.force-offline: ${GEO_FORCE_OFFLINE:false}.docker-compose.yml— passthrough, defaultfalse.docker-compose.offline.yml— defaults totruefor the air-gapped stack (overridable)..env.example— new "Geolocation Egress" block documenting the flag and the one intentional egress.docs/getting-started/offline-deployment.rst— "Suppressing Runtime Egress" section.Acceptance criteria
GEO_FORCE_OFFLINEflag, default off, that skips the probe + ipinfo.io lookups and resolves from the MMDB only (geo_source = mmdb).ipinfo.ioconnections attempted.Verification
Backend built cleanly. With
GEO_FORCE_OFFLINE=true, startup logs:With the flag off (default), no force-offline line and MMDB loads as fallback — both paths confirmed at runtime.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Bug Fixes