AlinsRan opened a new pull request, #13743:
URL: https://github.com/apache/apisix/pull/13743

   ### Description
   
   An active health check runs independently of any request. There is no 
downstream client — **the gateway itself is the client** probing the upstream 
node, so the probe must address the node by the node's own identity: its 
configured **domain**.
   
   `compute_targets()` in `apisix/healthcheck_manager.lua` currently derives 
the probe Host header by reusing the request-path `pass_host` logic:
   
   ```lua
   local use_node_hdr = up_conf.pass_host == "node" or nil
   local host_hdr = up_hdr or (use_node_hdr and node.domain) or nil
   ```
   
   But `pass_host` only decides how a **client** request's Host is forwarded to 
the upstream, which is meaningless for a probe (there is no client Host). For a 
**domain-based upstream with the default `pass_host` (`pass`)**, this yields a 
`nil` Host header, and the health-check library then falls back to `hostname`, 
which is the **resolved ip** for a domain node. The result, for every domain 
upstream:
   
   - the probe sends `Host: <resolved-ip>` → hits the wrong virtual host on the 
backend → **false unhealthy**;
   - over HTTPS it sends `SNI: <resolved-ip>` → **the TLS handshake fails** 
(the certificate does not match the ip) → the node is marked unhealthy and 
never recovers.
   
   Real traffic never has this problem: for `pass_host = node`/`rewrite` the 
request Host/SNI is `node.domain`/`upstream_host`, and for `pass` it forwards 
the real client's Host. Only the client-less health checker degrades to the ip.
   
   ### Fix
   
   ```lua
   local host_hdr = up_hdr or node.domain
   ```
   
   Always carry `node.domain` as the probe Host header, except when `pass_host` 
is `rewrite` (which still pins `upstream_host` via `up_hdr`). `node.domain` is 
`nil` for ip-based nodes, so ip upstreams are unaffected. This makes the probe 
address the node by the node's identity and honors the documented default of 
`checks.active.host` being the node host.
   
   Per `pass_host` mode:
   
   | `pass_host` | before | after |
   |---|---|---|
   | `node` | `node.domain` | `node.domain` (unchanged) |
   | `rewrite` | `upstream_host` | `upstream_host` (unchanged) |
   | `pass` (default), domain node | `nil` → probe uses resolved **ip** | 
`node.domain` |
   | any, ip node | `nil` | `nil` (unchanged) |
   
   ### Tests
   
   `t/node/healthcheck-node-domain-host.t`: a domain node (`test.com:1988`, 
resolved locally to `127.0.0.1`) with the default `pass_host` and an active 
check; a probe-target server logs the received Host header. Asserts the probe 
sends `Host: test.com`, not `Host: 127.0.0.1`.
   
   ### Checklist
   
   - [x] I have explained the need for this PR and the problem it solves
   - [x] I have explained the changes or the new features added to this PR
   - [x] I have added tests corresponding to this change
   - [ ] I have updated the documentation to reflect this change (behavior now 
matches the documented default of `checks.active.host`; no user-facing config 
change)
   - [x] I have verified that the change follows the existing code style
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to