fix(openapi_plugin): block Azure WireServer and IPv6-embedded private addresses in server URL validation - #14267
Conversation
|
Just verifying this is still in the review queue. All checks green, branch is mergeable. Happy to address any feedback. |
cf75934 to
6b77c57
Compare
`IPv6Address.sixtofour` and `.teredo` already do this, and they return None outside their own prefixes, so they carry the membership test with them. That drops two network constants, one offset tuple and the hand-written XOR for the Teredo client address, which is the piece most likely to be restated wrongly. Checked against the previous hand-rolled version on 40000 random addresses per form: no disagreement. The approach is taken from microsoft#14267, which covers the same ground.
|
Heads up on one thing here, because I hit exactly the same bug in #14242 and only found it by measuring.
The tempting fix, only decoding when the address really is The reason none of it works is RFC 6052 section 3.3: the prefix length is configuration held by the translator and is not carried in the address, so there is nothing to infer it from. What I ended up with, if it is useful to you: keep the decode for Separately, thank you for There is real overlap between this and #14242, which I opened on 29 July. I am not asking you to close anything, that is for the maintainers to sort out, but the NAT64 detail is worth fixing in whichever one moves. |
|
Thanks for the careful read, you're right, and I've applied exactly the approach you suggested.
Added regression tests for all of it: the local-use range blocks (including And thanks for the pointer on #14242 - happy to defer to the maintainers on how to handle the overlap; this is the behavior I'd keep in whichever PR moves forward. |
|
That is the right split, and putting the RFC reasoning in the comment rather than only in the PR body is what will stop someone "simplifying" the two branches back into one later. Measured the behaviour at Two things worth having on the record. The prefixes are disjoint, so neither check can shadow the other and the order of the two branches is not load-bearing. That is worth knowing because it is the kind of thing a later refactor might reorder without thinking, and here it is safe. More importantly, and this is the one I would put in a comment if it is not already: the decoded WireServer address is not caught by any generic rule. So the explicit WireServer entry is not redundant with the private-range check, and anyone reviewing this who assumes "the private-address branch already covers Azure metadata" would be wrong. 169.254.169.254 decodes to link-local and would be caught either way; 168.63.129.16 would sail through. That asymmetry is the whole reason the WireServer rule has to exist separately, and it is invisible from reading the diff.
No further concerns from me on the NAT64 half. |
|
Thanks for measuring it at the head commit - that table is exactly the evidence this kind of change needs. Both points are now on the record in the code: the WireServer comment block spells out that Comment-only commit: 96d71d7 (+6 lines). |
|
That comment is better than what I asked for. Naming the wrong assumption outright, "do not remove it on the assumption that Nothing further from me on the NAT64 or WireServer half. |
|
Thanks for the close read-through - glad the comment lands the way you wanted. I will leave the PR as is then, and watch for the workflow-approval gate on your side. Happy to rebase if main moves before then. |
… addresses in server URL validation The server URL validator could be bypassed to reach cloud metadata endpoints: the Azure WireServer IP 168.63.129.16 is publicly routable so it passed the private-address checks, and IPv6 addresses embedding an IPv4 (NAT64, 6to4, Teredo) were classified purely as IPv6, letting link-local and other private IPv4 addresses through. - Denylist the Azure WireServer metadata endpoint in _try_classify_ipv4 and enforce it even when allow_private_network_access is enabled. - Decode IPv4 addresses embedded in IPv6 (6to4 via sixtofour, Teredo via teredo, NAT64 64:ff9b::/96 and 64:ff9b:1::/48) before classification so embedded private addresses are blocked. - Add tests covering the WireServer endpoint, all three IPv6 embedding forms, and the private-network-access override.
96d71d7 to
eb61cee
Compare
Description
The OpenAPI plugin server URL validator could be bypassed to reach cloud metadata endpoints in two ways:
168.63.129.16) is publicly routable. Unlike the AWS (169.254.169.254) and GCP (metadata.google.internal) endpoints, the Azure WireServer IP falls in no private range, sotry_categorize_non_public_addresstreated it as public andvalidate_server_urlallowed it.64:ff9b::/96,64:ff9b:1::/48), 6to4 (2002::/16), or Teredo (2001::/32) address looked like a benign public IPv6 address and passed validation.Changes
_try_classify_ipv4, and enforce it even whenallow_private_network_access=True— being allowed to reach your own network is not the same as being allowed to reach the host agent's credential endpoint. The private-mode check only inspects literal IP hosts (private mode deliberately does not resolve hostnames).sixtofour,teredo, and the RFC 6052 NAT64 prefixes) intry_categorize_non_public_addressso the embedded address is classified with the IPv4 rules.Test plan
pytest tests/unit/connectors/openapi_plugin/test_server_url_validator.py— 62 passed (10 new tests added).Fixes #14240