Skip to content

feat: GeoIP support - #315

Merged
LucHeart merged 8 commits into
developfrom
feature/ip-enrichment-geo-vpn
Sep 30, 2026
Merged

LucHeart merged 8 commits into
developfrom
feature/ip-enrichment-geo-vpn

Conversation

@hhvrc

@hhvrc hhvrc commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Login sessions now include available network and location details: ISP organization, VPN status, country, and city.
    • Gateway assignments can use your approximate location to select a nearby gateway within the appropriate region.

@hhvrc hhvrc self-assigned this Jun 13, 2026
Copilot AI review requested due to automatic review settings June 13, 2026 18:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds GeoIP-based IP enrichment (ASN org, VPN heuristic, country/city) and stores/returns this metadata alongside login sessions.

Changes:

  • Add IpEnrichmentService (MaxMind GeoLite2 ASN/City DB support) plus related options/interfaces/data model.
  • Extend session creation/storage/response mapping to include enrichment fields (AsnOrg, IsVpn, CountryCode, City).
  • Wire up DI + configuration registration and call enrichment during session creation.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Directory.Packages.props Adds central package version for MaxMind.GeoIP2.
Common/Common.csproj Adds MaxMind.GeoIP2 package reference to Common.
Common/Services/Geo/IIpEnrichmentService.cs Introduces enrichment service abstraction.
Common/Services/Geo/IpEnrichmentService.cs Implements GeoLite2 DB loading + enrichment logic + VPN heuristic.
Common/Services/Geo/IpEnrichmentData.cs Adds enrichment DTO/record.
Common/Options/GeoOptions.cs Adds configuration options for GeoLite2 DB paths.
Common/Extensions/ConfigurationExtensions.cs Adds RegisterGeoOptions() builder extension.
Common/OpenShockServiceHelper.cs Registers IIpEnrichmentService in DI.
Common/OpenShockControllerBase.cs Enriches remote IP and passes enrichment into session creation.
Common/Services/Session/ISessionService.cs Extends CreateSessionAsync signature to accept optional enrichment.
Common/Services/Session/SessionService.cs Persists enrichment fields into LoginSession.
Common/Redis/LoginSessions.cs Adds enrichment fields to Redis session model.
API/Program.cs Registers geo options during API startup.
API/Models/Response/LoginSessionResponse.cs Exposes enrichment fields in API session response.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Common/OpenShockServiceHelper.cs
Comment thread Common/Services/Geo/IpEnrichmentService.cs
Comment thread Common/Options/GeoOptions.cs
@hhvrc
hhvrc marked this pull request as draft June 13, 2026 19:01
# Conflicts:
#	API/Program.cs
#	Common/Extensions/ConfigurationExtensions.cs
@ghost

ghost commented Jul 1, 2026 •

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 5 individual chapters for you:

Title
1 Add GeoIP dependencies and configuration options
2 Implement IP enrichment service
3 Update session models for GeoIP data
4 Integrate enrichment into session creation
5 Wire services and trigger enrichment in controllers
Open in Stage

Chapters generated by Stage for commit b24627d on Jul 1, 2026 2:34pm UTC.

hhvrc and others added 2 commits July 1, 2026 16:33
- Register a default GeoOptions via TryAddSingleton in AddOpenShockServices so
  hosts that don't call RegisterGeoOptions() (Cron, LiveControlGateway, SeedE2E)
  can still activate IIpEnrichmentService instead of failing DI at startup.
- Make IpEnrichmentData.IsVpn nullable so an unavailable/failed ASN lookup stays
  'unknown' (null) rather than being recorded as 'not VPN' (false).
- Rename GeoOptions section to 'OpenShock:Geo' to match the repo's config convention.
…ent-geo-vpn

# Conflicts:
#	Common/OpenShockControllerBase.cs
#	Common/OpenShockServiceHelper.cs
#	Common/Services/Session/ISessionService.cs
#	Common/Services/Session/SessionService.cs
@coderabbitai

coderabbitai Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a2c100be-008e-4b0a-adac-7a7b3eefec0b

📥 Commits

Reviewing files that changed from the base of the PR and between 2a359f1 and db99e8d.

📒 Files selected for processing (14)
  • API.IntegrationTests/GeoLocatedWebApplicationFactory.cs
  • API.IntegrationTests/Tests/LcgCoordinateAssignmentTests.cs
  • API/Controller/Device/AssignLCG.cs
  • API/Controller/Device/AssignLCGV2.cs
  • API/Services/LCGNodeProvisioner/ILCGNodeProvisioner.cs
  • API/Services/LCGNodeProvisioner/LCGNodeProvisioner.cs
  • Common.Tests/Services/GeoPointTests.cs
  • Common/Redis/LcgNode.cs
  • Common/Services/Geo/GeoPoint.cs
  • Common/Services/Geo/IpEnrichmentData.cs
  • Common/Services/Geo/IpEnrichmentService.cs
  • LiveControlGateway/LcgKeepAlive.cs
  • LiveControlGateway/Options/LcgOptions.cs
  • LiveControlGateway/Program.cs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The API adds GeoLite2 IP enrichment to login sessions and live control gateway assignment. It stores available ASN, VPN, country, and city data in login sessions. Gateway coordinates are published and used to narrow assignment candidates by proximity.

Changes

Geolocation enrichment and gateway assignment

Layer / File(s) Summary
Geo configuration and lookup
Common/Options/GeoOptions.cs, Common/Services/Geo/*, Common/Extensions/ConfigurationExtensions.cs, Common/OpenShockServiceHelper.cs, API/Program.cs, Common/Common.csproj, Directory.Packages.props, Common.Tests/Services/IpEnrichmentServiceTests.cs
Adds configurable ASN and city database paths and a GeoLite2-backed IP enrichment service. Startup and service registration provide the options and service. Tests cover VPN-provider matching.
Enrichment in session creation
Common/Services/Session/*, Common/Redis/LoginSessions.cs, Common/OpenShockControllerBase.cs, API/Models/Response/LoginSessionResponse.cs
Session creation accepts optional enrichment data. The API enriches the remote IP, and new sessions store available ASN organization, VPN status, country code, and city values. Login session responses map these values from the session.
Gateway coordinate publishing
LiveControlGateway/Options/LcgOptions.cs, LiveControlGateway/Program.cs, LiveControlGateway/LcgKeepAlive.cs, Common/Redis/LcgNode.cs
Gateway options add validated latitude and longitude values, and startup requires both values or neither. Each keep-alive write includes gateway coordinates in the complete node record.
Coordinate-based gateway selection
Common/Services/Geo/GeoPoint.cs, Common.Tests/Services/GeoPointTests.cs, API/Services/LCGNodeProvisioner/*, API/Controller/Device/AssignLCG*.cs, API.IntegrationTests/GeoLocatedWebApplicationFactory.cs, API.IntegrationTests/Tests/LcgCoordinateAssignmentTests.cs
The assignment endpoints pass the enriched client location to the provisioner. The provisioner can narrow a country-selected region to nearby gateways when every gateway in the region has coordinates, then applies load selection. Unit and integration tests cover coordinate validation, distance calculations, and assignment cases.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant AssignLCG
  participant IIpEnrichmentService
  participant LCGNodeProvisioner
  participant LcgNode
  AssignLCG->>IIpEnrichmentService: Enrich request remote IP
  IIpEnrichmentService-->>AssignLCG: Return client location
  AssignLCG->>LCGNodeProvisioner: Select node using country and location
  LCGNodeProvisioner->>LcgNode: Read country, coordinates, and load
  LCGNodeProvisioner-->>AssignLCG: Return selected node
Loading

Merge Risk: ⚪ Minimal · up to db99e

GeoIP enrichment remains optional, and coordinate-based gateway selection preserves country and missing-coordinate fallbacks. Gateway publication supports the intended expiry behavior; the change is mergeable subject to normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to db99e

The change adds location data to sessions and influences gateway selection while preserving the inspected authorization controls. No new security vulnerability was established, but deployment trust and data-lifecycle assumptions remain partly unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — Location can influence gateway selection within the API's environment-filtered registry. Session metadata exposure is bounded by the inspected session-cookie authorization and current-user listing filter; the self endpoint maps the caller's authenticated session rather than accepting another session identifier.

Trust Boundaries and Controls

  • observed — Enrichment reads Connection.RemoteIpAddress rather than parsing a caller-supplied IP string. Forwarded-header middleware uses CF-Connecting-IP and adds trusted networks before processing it. This establishes an application-level identity normalization control, but the deployed proxy configuration and upstream header sanitization were not verified. Country-header selection already existed before this PR.

Resilience and Maintainability Implications

  • observed — Session creation retains fresh token generation, hashed storage identity, TTL, audit-before-cookie ordering, and existing deletion paths; enrichment is attached to each new session rather than shared between sessions. Gateway publication retains readiness-gated refresh, a 35-second expiry, cancellation handling, and recovery by republishing. Unlike the base refresh, full replacement sets Load to zero on every heartbeat; no current competing production writer was established.

Hardening Proposals

  • proposed — Document the provenance and refresh process for the local Geo databases and the privacy purpose of the newly retained session fields. Treat VPN and location results as approximate descriptive data, not sufficient evidence for future authorization decisions.
  • proposed — Make full-record gateway ownership explicit before introducing a separate load reporter or deployment-specific writer. Such a writer would need coordination with the heartbeat, which now replaces Load and all advertised fields rather than preserving independently updated values.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 51 functions across 25 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: adding GeoIP support. It is concise and directly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hhvrc
hhvrc marked this pull request as ready for review August 14, 2026 10:23
hhvrc added 3 commits August 14, 2026 12:30
The keyword list carried datacenter ASNs (amazon, google, akamai, ovh), so
corporate and cloud egress was flagged as VPN, and substring matching let
"pia" match "Olympia". Matching is now whole-token over consumer VPN vendors.
# Conflicts:
#	Directory.Packages.props
@hhvrc
hhvrc requested a review from LucHeart September 30, 2026 14:11
@LucHeart

Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@LucHeart
LucHeart merged commit 380146f into develop Sep 30, 2026
25 checks passed
@LucHeart
LucHeart deleted the feature/ip-enrichment-geo-vpn branch September 30, 2026 19:38
@hhvrc
hhvrc restored the feature/ip-enrichment-geo-vpn branch September 30, 2026 19:42
@hhvrc
hhvrc deleted the feature/ip-enrichment-geo-vpn branch September 30, 2026 19:45
hhvrc added a commit that referenced this pull request Sep 30, 2026
Add an IP enrichment service backed by the MaxMind GeoLite2 ASN and City
databases, configured under OpenShock:Geo (AsnDbPath, CityDbPath). When
neither database is available the service returns null and callers fall
back to the previous behavior.

Login sessions:
- Record ASN organization, VPN flag, country code and city when a session
  is created, and expose them on LoginSessionResponse.
- IsVpn is nullable so a failed or missing ASN lookup stays unknown rather
  than being reported as "not VPN".
- VPN detection matches whole tokens against consumer VPN providers only,
  so datacenter/cloud egress is not flagged and substrings like "pia" in
  "Olympia" don't match.

Gateway assignment:
- LCGs can advertise an optional Latitude/Longitude, published on the
  LcgNode in Redis.
- Hub assignment still picks the closest region by country, then narrows
  it by great-circle distance to the hub's GeoIP location when every node
  in that region has coordinates.
- LcgKeepAlive now overwrites the whole node on every beat instead of
  diffing fields, which also backfills nodes written by older builds.

A default GeoOptions is registered in AddOpenShockServices so hosts that
don't configure Geo (Cron, LCG, SeedE2E) still resolve the service.

Co-authored-by: LucHeart <luc@luc.cat>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants