diff --git a/lib/aprsme_web/plugs/ip_geolocation.ex b/lib/aprsme_web/plugs/ip_geolocation.ex index f88f790..ae5cfc1 100644 --- a/lib/aprsme_web/plugs/ip_geolocation.ex +++ b/lib/aprsme_web/plugs/ip_geolocation.ex @@ -68,12 +68,22 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do end defp get_client_ip(conn) do - # Check for forwarded IP first (when behind proxy/load balancer) + # Check for Cloudflare header first (CF-Connecting-IP) + cf_ip = get_req_header(conn, "cf-connecting-ip") + + # Then check for standard forwarded headers forwarded_for = get_req_header(conn, "x-forwarded-for") + real_ip = get_req_header(conn, "x-real-ip") ip = - case forwarded_for do - [forwarded | _] -> + case {cf_ip, forwarded_for, real_ip} do + {[cf | _], _, _} -> + # Cloudflare header takes precedence + ip_from_cf = String.trim(cf) + Logger.info("IP geolocation: Using Cloudflare CF-Connecting-IP: #{ip_from_cf}") + ip_from_cf + + {[], [forwarded | _], _} -> # Take the first IP from the X-Forwarded-For header ip_from_header = forwarded @@ -84,7 +94,13 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do Logger.info("IP geolocation: Using forwarded IP from X-Forwarded-For header: #{ip_from_header}") ip_from_header - [] -> + {[], [], [real | _]} -> + # Use X-Real-IP header + ip_from_real = String.trim(real) + Logger.info("IP geolocation: Using X-Real-IP header: #{ip_from_real}") + ip_from_real + + {[], [], []} -> # Fall back to remote_ip ip_from_remote = case conn.remote_ip do diff --git a/lib/aprsme_web/plugs/rate_limiter.ex b/lib/aprsme_web/plugs/rate_limiter.ex index 6c59035..d9aae0a 100644 --- a/lib/aprsme_web/plugs/rate_limiter.ex +++ b/lib/aprsme_web/plugs/rate_limiter.ex @@ -40,9 +40,22 @@ defmodule AprsmeWeb.Plugs.RateLimiter do end defp get_key(conn, :ip) do - case get_req_header(conn, "x-forwarded-for") do - [ip | _] -> ip - [] -> conn.remote_ip |> :inet.ntoa() |> to_string() + # Check headers in order of preference + cond do + # Cloudflare header takes precedence + cf_ip = get_req_header(conn, "cf-connecting-ip") != [] -> + hd(cf_ip) + + # Then standard forwarded headers + forwarded = get_req_header(conn, "x-forwarded-for") != [] -> + forwarded |> hd() |> String.split(",") |> List.first() |> String.trim() + + real_ip = get_req_header(conn, "x-real-ip") != [] -> + hd(real_ip) + + # Fall back to remote_ip + true -> + conn.remote_ip |> :inet.ntoa() |> to_string() end end