mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-29 17:44:30 -04:00
fix(failover): do not follow redirects in remote probes
Go resends custom headers such as x-api-key when it follows a redirect, also to another host, so a redirecting upstream could receive the target's API key elsewhere. The probe client now treats a redirect as the response, which fails the probe as a non-2xx status. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
1 parent
132bb216a1
commit
27e5aba60e
2 files changed
+23
-1
No files matched your search
@@ -37,7 +37,12 @@ type DefaultProber struct {
|
||||
}
|
||||
|
||||
func NewProber(loaded LoadedFunc) *DefaultProber {
|
||||
return &DefaultProber{HTTP: &http.Client{}, Loaded: loaded}
|
||||
return &DefaultProber{HTTP: &http.Client{
|
||||
// A redirect is a failed probe, not something to follow: Go resends
|
||||
// custom headers such as x-api-key to any host, and the target's
|
||||
// API key must reach only the configured upstream.
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse },
|
||||
}, Loaded: loaded}
|
||||
}
|
||||
|
||||
func (p *DefaultProber) Liveness(ctx context.Context, cfg config.ModelConfig, kind Kind, warm bool) error {
|
||||
|
||||
@@ -125,6 +125,23 @@ var _ = Describe("DefaultProber", func() {
|
||||
Expect(up.auth).To(Equal("Bearer sekret"))
|
||||
})
|
||||
|
||||
It("does not follow a redirect, so the API key never leaves the upstream", func() {
|
||||
GinkgoT().Setenv("FAILOVER_PROBE_KEY", "sekret")
|
||||
other := newFakeUpstream()
|
||||
DeferCleanup(other.srv.Close)
|
||||
other.models = []string{"argus-llm"}
|
||||
redirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, other.srv.URL+r.URL.Path, http.StatusFound)
|
||||
}))
|
||||
DeferCleanup(redirect.Close)
|
||||
c := proxied("argus-llm", "")
|
||||
c.Proxy.UpstreamURL = redirect.URL + "/v1/chat/completions"
|
||||
c.Proxy.APIKeyEnv = "FAILOVER_PROBE_KEY"
|
||||
c.Proxy.Provider = config.ProxyProviderAnthropic
|
||||
Expect(p.Liveness(ctx, c, KindRemote, false)).To(MatchError(ContainSubstring("302")))
|
||||
Expect(other.paths).To(BeEmpty())
|
||||
})
|
||||
|
||||
DescribeTable("remote inference hits the usecase endpoint",
|
||||
func(usecase, path string) {
|
||||
Expect(p.Inference(ctx, proxied("m", "", usecase), KindRemote, false)).To(Succeed())
|
||||
|
||||
Reference in new issue
Block a user