Module: Legate::Auth::UrlGuard
- Defined in:
- lib/legate/auth/url_guard.rb
Overview
Canonical SSRF guard for outbound auth and credential-test URLs.
Resolves the host and refuses loopback, link-local, private, 0.0.0.0/8 and CGNAT (100.64.0.0/10) targets so a misconfigured or attacker-supplied URL cannot reach internal services or cloud metadata. Set LEGATE_ALLOW_PRIVATE_AUTH_URLS=1 to bypass in development.
Constant Summary collapse
- BLOCKED_RANGES =
[ IPAddr.new('0.0.0.0/8'), IPAddr.new('100.64.0.0/10') ].freeze
Class Method Summary collapse
- .parse_http_uri!(url, label) ⇒ Object
-
.resolved_ips(hostname) ⇒ Array<String>
Resolved IP strings ([] when resolution fails — the caller treats empty as a hard failure / fail-closed).
- .restricted?(ip) ⇒ Boolean
- .validate!(url, label: 'Auth URL') ⇒ Object
Class Method Details
.parse_http_uri!(url, label) ⇒ Object
54 55 56 57 58 59 |
# File 'lib/legate/auth/url_guard.rb', line 54 def parse_http_uri!(url, label) uri = URI.parse(url.to_s) return uri if uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS) raise Legate::Auth::Error, "#{label} must use http or https scheme" end |
.resolved_ips(hostname) ⇒ Array<String>
Returns resolved IP strings ([] when resolution fails —the caller treats empty as a hard failure / fail-closed).
63 64 65 66 67 68 69 70 71 |
# File 'lib/legate/auth/url_guard.rb', line 63 def resolved_ips(hostname) [IPAddr.new(hostname).to_s] rescue IPAddr::InvalidAddressError begin Resolv.getaddresses(hostname) rescue Resolv::ResolvError [] end end |
.restricted?(ip) ⇒ Boolean
73 74 75 76 77 78 |
# File 'lib/legate/auth/url_guard.rb', line 73 def restricted?(ip) # Normalize IPv4-mapped IPv6 (e.g. ::ffff:127.0.0.1) to its IPv4 form so # the loopback/private/link-local checks aren't bypassed by the mapping. ip = ip.native if ip.ipv4_mapped? ip.loopback? || ip.link_local? || ip.private? || BLOCKED_RANGES.any? { |r| r.include?(ip) } end |
.validate!(url, label: 'Auth URL') ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/legate/auth/url_guard.rb', line 28 def validate!(url, label: 'Auth URL') return if ENV['LEGATE_ALLOW_PRIVATE_AUTH_URLS'] hostname = parse_http_uri!(url, label).host ips = resolved_ips(hostname) # Fail closed: if we can't resolve the host, refuse rather than letting # the request through (an unresolvable host can't be checked, and a # resolver discrepancy could otherwise be used to slip past the guard). if ips.empty? raise Legate::Auth::Error, "#{label}: could not resolve host '#{hostname}' for SSRF validation." end ips.each do |ip_str| ip = IPAddr.new(ip_str) next unless restricted?(ip) raise Legate::Auth::Error, "#{label} resolves to restricted network address (#{hostname} -> #{ip_str}). " \ 'Set LEGATE_ALLOW_PRIVATE_AUTH_URLS=1 for development.' rescue IPAddr::InvalidAddressError next # skip unparseable IPs from the resolver end end |