Restrict enrichment enqueue to T-Mobile CGNAT subnet (172.56.0.0/13)

Store remote_ip in session, check subnet in LiveView mount before
enqueuing weather/HRRR/terrain/solar enrichment jobs.
This commit is contained in:
Graham McIntire 2026-04-04 18:55:42 -05:00
parent dc66252fdc
commit a209fc65ae
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 38 additions and 5 deletions

View file

@ -18,10 +18,14 @@ defmodule MicrowavepropWeb.ContactLive.Show do
require Logger
@earth_radius_m 6_371_000.0
# T-Mobile CGNAT range — only enqueue enrichment jobs from this subnet
@enqueue_subnet {172, 56, 0, 0}
@enqueue_mask 13
@impl true
def mount(%{"id" => id}, _session, socket) do
def mount(%{"id" => id}, session, socket) do
contact = id |> Radio.get_contact!() |> Radio.ensure_positions!()
can_enqueue = internal_network?(session)
if connected?(socket) do
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, "contact_enrichment:#{contact.id}")
@ -31,13 +35,16 @@ defmodule MicrowavepropWeb.ContactLive.Show do
end
weather = load_weather(contact)
solar = maybe_enqueue_solar(contact)
solar = if can_enqueue, do: maybe_enqueue_solar(contact), else: load_solar(contact)
hrrr_path = Weather.hrrr_profiles_for_path(contact)
hrrr = List.first(hrrr_path)
{hrrr, contact} = maybe_enqueue_hrrr(hrrr, contact)
{hrrr, contact} =
if can_enqueue, do: maybe_enqueue_hrrr(hrrr, contact), else: {hrrr, contact}
terrain = Terrain.get_terrain_profile(contact.id)
contact = maybe_enqueue_terrain(terrain, contact)
contact = maybe_enqueue_weather(weather, contact)
contact = if can_enqueue, do: maybe_enqueue_terrain(terrain, contact), else: contact
contact = if can_enqueue, do: maybe_enqueue_weather(weather, contact), else: contact
iemre = load_iemre(contact)
elevation_profile = compute_elevation_profile(contact, hrrr_path, weather.soundings)
propagation_analysis = build_propagation_analysis(contact, hrrr, terrain, elevation_profile, weather.soundings)
@ -389,6 +396,26 @@ defmodule MicrowavepropWeb.ContactLive.Show do
|> Map.new()
end
defp internal_network?(session) do
case session["remote_ip"] do
nil ->
false
ip_str ->
case :inet.parse_address(String.to_charlist(ip_str)) do
{:ok, {a, b, c, d}} ->
ip_int = Bitwise.bsl(a, 24) + Bitwise.bsl(b, 16) + Bitwise.bsl(c, 8) + d
{sa, sb, sc, sd} = @enqueue_subnet
subnet_int = Bitwise.bsl(sa, 24) + Bitwise.bsl(sb, 16) + Bitwise.bsl(sc, 8) + sd
mask = Bitwise.bsl(0xFFFFFFFF, 32 - @enqueue_mask)
Bitwise.band(ip_int, mask) == Bitwise.band(subnet_int, mask)
_ ->
false
end
end
end
defp load_solar(contact) do
contact.qso_timestamp
|> DateTime.to_date()

View file

@ -4,12 +4,18 @@ defmodule MicrowavepropWeb.Router do
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :store_remote_ip
plug :fetch_live_flash
plug :put_root_layout, html: {MicrowavepropWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
defp store_remote_ip(conn, _opts) do
ip_str = conn.remote_ip |> :inet.ntoa() |> to_string()
Plug.Conn.put_session(conn, :remote_ip, ip_str)
end
pipeline :api do
plug :accepts, ["json"]
end