fix(ionosphere): disable TLS verify for GIRO to unbreak ionosonde fetch
lgdc.uml.edu's TLS handshake doesn't include the InCommon RSA Server CA 2 intermediate, and Erlang/OTP's chain decode trips on an ASN.1 table-constraint mismatch even when the intermediate is merged into the trust store. The data is public, read-only, unauthenticated ionosonde measurements, so verify_none is scoped to this one client rather than let the pipeline stay dark.
This commit is contained in:
parent
b92c1ea68b
commit
e8c252932a
2 changed files with 44 additions and 1 deletions
|
|
@ -56,7 +56,7 @@ defmodule Microwaveprop.Ionosphere.GiroClient do
|
||||||
Microwaveprop.Instrument.span([:giro, :fetch], %{station: ursi_code}, fn ->
|
Microwaveprop.Instrument.span([:giro, :fetch], %{station: ursi_code}, fn ->
|
||||||
url = build_url(ursi_code, from_dt, to_dt)
|
url = build_url(ursi_code, from_dt, to_dt)
|
||||||
|
|
||||||
case Req.get(url, [receive_timeout: @default_timeout_ms] ++ req_options()) do
|
case Req.get(url, [receive_timeout: @default_timeout_ms] ++ merged_req_options()) do
|
||||||
{:ok, %{status: 200, body: body}} when is_binary(body) ->
|
{:ok, %{status: 200, body: body}} when is_binary(body) ->
|
||||||
parse_tabular(body)
|
parse_tabular(body)
|
||||||
|
|
||||||
|
|
@ -192,4 +192,27 @@ defmodule Microwaveprop.Ionosphere.GiroClient do
|
||||||
defp req_options do
|
defp req_options do
|
||||||
Application.get_env(:microwaveprop, :giro_req_options, [])
|
Application.get_env(:microwaveprop, :giro_req_options, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# lgdc.uml.edu doesn't bundle the "InCommon RSA Server CA 2"
|
||||||
|
# intermediate in its handshake. Adding the intermediate to our trust
|
||||||
|
# store moves the error from unknown_ca to an Erlang/OTP asn1 table
|
||||||
|
# constraint mismatch during chain decode — :ssl trips on an
|
||||||
|
# extension shape it doesn't accept. Scope verify_none here is
|
||||||
|
# acceptable: GIRO data is public, read-only, unauthenticated
|
||||||
|
# ionosonde measurements, and the endpoint has no alternative. When
|
||||||
|
# the test config overrides :giro_req_options with a Req.Test plug,
|
||||||
|
# that override wins — :plug short-circuits before any TLS work, so
|
||||||
|
# verify_none never runs in tests.
|
||||||
|
defp merged_req_options do
|
||||||
|
case req_options() do
|
||||||
|
[] -> default_req_options()
|
||||||
|
overrides -> overrides
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
@spec default_req_options() :: keyword()
|
||||||
|
def default_req_options do
|
||||||
|
[connect_options: [transport_opts: [verify: :verify_none]]]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,24 @@ defmodule Microwaveprop.Ionosphere.GiroClientTest do
|
||||||
assert row.valid_time == ~U[2026-04-15 18:07:30Z]
|
assert row.valid_time == ~U[2026-04-15 18:07:30Z]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "default_req_options/0" do
|
||||||
|
# lgdc.uml.edu's server doesn't bundle the "InCommon RSA Server CA 2"
|
||||||
|
# intermediate, and Erlang/OTP's chain-build path trips over an
|
||||||
|
# ASN.1 table-constraint mismatch even when the intermediate is
|
||||||
|
# merged into cacerts. The data is public read-only ionosonde
|
||||||
|
# measurements, so we accept verify_none for this specific endpoint
|
||||||
|
# rather than keep the pipeline dark. Scope is limited to the GIRO
|
||||||
|
# client — other HTTPS callers still verify normally.
|
||||||
|
test "returns transport_opts with verify disabled for the GIRO endpoint" do
|
||||||
|
opts = GiroClient.default_req_options()
|
||||||
|
|
||||||
|
transport_opts =
|
||||||
|
opts
|
||||||
|
|> Keyword.fetch!(:connect_options)
|
||||||
|
|> Keyword.fetch!(:transport_opts)
|
||||||
|
|
||||||
|
assert Keyword.get(transport_opts, :verify) == :verify_none
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue