fix(oidc): fail closed when verifier is unavailable (#4013)

Signed-off-by: EndlessSeeker <1766508902@qq.com>
Co-authored-by: Jingze <52855280+Jing-ze@users.noreply.github.com>
This commit is contained in:
EndlessSeeker
2026-06-24 14:22:24 +08:00
committed by GitHub
parent e958290283
commit 10498a2c86
4 changed files with 116 additions and 5 deletions

View File

@@ -6,12 +6,19 @@ toolchain go1.24.4
require (
github.com/higress-group/oauth2-proxy v1.0.1-0.20260605094027-396b01ca2ca4
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250611100342-5654e89a7a80
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0
github.com/higress-group/wasm-go v1.0.2-0.20250911113549-cbf1cfcce774
github.com/tidwall/gjson v1.18.0
)
require github.com/tidwall/sjson v1.2.5 // indirect
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tetratelabs/wazero v1.7.2 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/benbjohnson/clock v1.3.5 // indirect

View File

@@ -16,8 +16,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/higress-group/oauth2-proxy v1.0.1-0.20260605094027-396b01ca2ca4 h1:P7p5oh95mvgi38QhIN+nHJJCJPGjWqv3UHAmKzo25ko=
github.com/higress-group/oauth2-proxy v1.0.1-0.20260605094027-396b01ca2ca4/go.mod h1:sDlsoo4dx+Cx56vI5kegeXZAoQ4nnd7xWL7Oc4/4hLI=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250611100342-5654e89a7a80 h1:xqmtTZI0JQ2O+Lg9/CE6c+Tw9KD6FnvWw8EpLVuuvfg=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250611100342-5654e89a7a80/go.mod h1:tRI2LfMudSkKHhyv1uex3BWzcice2s/l8Ah8axporfA=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0 h1:YGdj8KBzVjabU3STUfwMZghB+VlX6YLfJtLbrsWaOD0=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20250822030947-8345453fddd0/go.mod h1:tRI2LfMudSkKHhyv1uex3BWzcice2s/l8Ah8axporfA=
github.com/higress-group/wasm-go v1.0.2-0.20250911113549-cbf1cfcce774 h1:3nzDlUZ8+Nc0c2f8y0wUiw6mnyu1+ZYT0mK7x9Oitro=
github.com/higress-group/wasm-go v1.0.2-0.20250911113549-cbf1cfcce774/go.mod h1:882/J8ccU4i+LeyFKmeicbHWAYLj8y7YZr60zk0OOCI=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
@@ -36,6 +36,8 @@ github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.7.2 h1:1+z5nXJNwMLPAWaTePFi49SSTL0IMx/i3Fg8Yc25GDc=
github.com/tetratelabs/wazero v1.7.2/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -54,5 +56,7 @@ golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -71,7 +71,14 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config PluginConfig, log log.
// TODO: remove this verifier after envoy support send request during parseConfig
if err := config.oidcHandler.ValidateVerifier(); err != nil {
log.Critical(err.Error())
return types.ActionContinue
_ = proxywasm.SendHttpResponseWithDetail(
http.StatusServiceUnavailable,
"oidc.verifier_unavailable",
nil,
[]byte("OIDC verifier is unavailable"),
-1,
)
return types.ActionPause
}
config.oidcHandler.ServeHTTP(rw, req)

View File

@@ -0,0 +1,93 @@
package main
import (
"encoding/json"
"testing"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/higress-group/wasm-go/pkg/test"
)
func oidcTestConfig(matchList []map[string]interface{}) json.RawMessage {
data, _ := json.Marshal(map[string]interface{}{
"redirect_url": "http://foo.bar.com/oauth2/callback",
"oidc_issuer_url": "http://127.0.0.1:65535/realms/poc",
"client_id": "poc",
"client_secret": "poc",
"cookie_secret": "nqavJrGvRmQxWwGNptLdyUVKcBNZ2b18Guc1n_8DCfY=",
"service_name": "keycloak.static",
"service_port": 80,
"service_host": "127.0.0.1:65535",
"match_type": "whitelist",
"match_list": matchList,
"verifier_interval": "2s",
})
return data
}
func TestOnHttpRequestHeadersVerifierUnavailable(t *testing.T) {
test.RunTest(t, func(t *testing.T) {
host, status := test.NewTestHost(oidcTestConfig(nil))
defer host.Reset()
if status != types.OnPluginStartStatusOK {
t.Fatalf("plugin start status = %v, want %v", status, types.OnPluginStartStatusOK)
}
action := host.CallOnHttpRequestHeaders([][2]string{
{":scheme", "http"},
{":authority", "foo.bar.com"},
{":path", "/protected"},
{":method", "GET"},
})
if action != types.ActionPause {
t.Fatalf("request action = %v, want %v", action, types.ActionPause)
}
if streamAction := host.GetHttpStreamAction(); streamAction != types.ActionPause {
t.Fatalf("stream action = %v, want %v", streamAction, types.ActionPause)
}
localResponse := host.GetLocalResponse()
if localResponse == nil {
t.Fatal("local response is nil")
}
if localResponse.StatusCode != 503 {
t.Fatalf("local response status = %d, want 503", localResponse.StatusCode)
}
if body := string(localResponse.Data); body != "OIDC verifier is unavailable" {
t.Fatalf("local response body = %q, want %q", body, "OIDC verifier is unavailable")
}
})
}
func TestOnHttpRequestHeadersAllowlistBypassesVerifierCheck(t *testing.T) {
test.RunTest(t, func(t *testing.T) {
host, status := test.NewTestHost(oidcTestConfig([]map[string]interface{}{
{
"match_rule_domain": "foo.bar.com",
"match_rule_path": "/public",
"match_rule_type": "prefix",
},
}))
defer host.Reset()
if status != types.OnPluginStartStatusOK {
t.Fatalf("plugin start status = %v, want %v", status, types.OnPluginStartStatusOK)
}
action := host.CallOnHttpRequestHeaders([][2]string{
{":scheme", "http"},
{":authority", "foo.bar.com"},
{":path", "/public/info"},
{":method", "GET"},
})
if action != types.ActionContinue {
t.Fatalf("request action = %v, want %v", action, types.ActionContinue)
}
if streamAction := host.GetHttpStreamAction(); streamAction != types.ActionContinue {
t.Fatalf("stream action = %v, want %v", streamAction, types.ActionContinue)
}
if localResponse := host.GetLocalResponse(); localResponse != nil {
t.Fatalf("local response = %+v, want nil", localResponse)
}
})
}