Skip to content

tiup HTTP clients ignore NO_PROXY #2710

@Jaehui-Lee

Description

@Jaehui-Lee

Bug Report

Please answer these questions before submitting your issue. Thanks!

  1. What did you do?

On a control machine behind an HTTP proxy (needed to reach the component mirror), with the cluster on an internal network excluded via NO_PROXY:

export HTTP_PROXY=http://internal-proxy:3128
export HTTPS_PROXY=http://internal-proxy:3128
export NO_PROXY=10.0.0.0/8,.internal.example.com,localhost,127.0.0.1
tiup cluster reload <cluster> -R tikv
  1. What did you expect to see?

NO_PROXY honored: internal hosts (PD, TiKV) reached directly; only the external component mirror goes through the proxy — the standard HTTP_PROXY/NO_PROXY contract.

  1. What did you see instead?

Every request is forced through HTTP_PROXY regardless of NO_PROXY, so the internal API calls fail. It surfaces in two stages of reload:

  • PD APIerror requesting http://pd-1.internal.example.com:2379/pd/api/v1/config, ... code 403 (the proxy rejects the internal request).
  • After fixing that, TiKV status scrapeError: failed to get leader count ...: metric tikv_raftstore_region_count{type="leader"} not found. tiup scrapes http://tikv-1.internal.example.com:20180/metrics to count leaders before restart; that request also goes through the proxy and returns the proxy's error page, so the metric can't be parsed.

(Conversely, without a proxy, InitConfig fails to fetch /timestamp.json from the mirror — there is no single configuration that works.)

  1. What version of TiUP are you using (tiup --version)?

v1.16.5 (also reproduced on master).


Root cause

Two HTTP clients set tr.Proxy to a fixed http.ProxyURL (applied to every destination, ignoring NO_PROXY) instead of http.ProxyFromEnvironment:

  • pkg/utils/http_client.goNewHTTPClient — used by the PD API client (pkg/cluster/api/pdapi.go).
  • pkg/cluster/spec/tikv.gomakeTransport — used by genLeaderCounter to scrape the TiKV status port.
httpProxy := os.Getenv("TIUP_INNER_HTTP_PROXY")
if len(httpProxy) == 0 {
    httpProxy = os.Getenv("HTTP_PROXY")
}
if len(httpProxy) > 0 {
    if proxyURL, err := url.Parse(httpProxy); err == nil {
        tr.Proxy = http.ProxyURL(proxyURL) // ignores NO_PROXY
    }
}

Proposed fix (backward compatible)

Resolve the proxy with golang.org/x/net/http/httpproxy so NO_PROXY is honored, keeping the TIUP_INNER_HTTP_PROXY override, in both clients:

import "golang.org/x/net/http/httpproxy"

proxyCfg := httpproxy.FromEnvironment() // HTTP(S)_PROXY + NO_PROXY
if inner := os.Getenv("TIUP_INNER_HTTP_PROXY"); len(inner) > 0 {
    proxyCfg.HTTPProxy, proxyCfg.HTTPSProxy = inner, inner
}
proxyFunc := proxyCfg.ProxyFunc()
tr.Proxy = func(req *http.Request) (*url.URL, error) {
    return proxyFunc(req.URL)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions