diff --git a/Makefile b/Makefile index b2b573bea..cd32782f7 100644 --- a/Makefile +++ b/Makefile @@ -185,7 +185,7 @@ $(KUBE_APPLYCONFIGURATION_GEN): GOBIN=$(GOBIN_DIR) $(GO_INSTALL) k8s.io/code-generator/cmd/$(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_VER) -codegen: WHAT ?= ./sdk/client +codegen: WHAT ?= ./sdk/client codegen: $(CONTROLLER_GEN) $(YAML_PATCH) $(CODE_GENERATOR) $(KUBE_CLIENT_GEN) $(KUBE_LISTER_GEN) $(KUBE_INFORMER_GEN) $(KUBE_APPLYCONFIGURATION_GEN) go mod download ./hack/update-codegen.sh @@ -245,8 +245,7 @@ E2E_PARALLELISM ?= 1 dex: git clone https://github.com/dexidp/dex.git dex/bin/dex: dex - cd dex - make + (cd dex; make) $(DEX): mkdir -p $(TOOLS_DIR) diff --git a/backend/cookie/session.go b/backend/cookie/session.go deleted file mode 100644 index 4f6efa13a..000000000 --- a/backend/cookie/session.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2022 The Kube Bind Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cookie - -import ( - "bytes" - "encoding/base64" - "fmt" - "io" - "time" - - "github.com/pierrec/lz4" - "github.com/vmihailenco/msgpack/v4" -) - -type SessionState struct { - CreatedAt time.Time `msgpack:"ca,omitempty"` - ExpiresOn time.Time `msgpack:"eo,omitempty"` - - AccessToken string `msgpack:"at,omitempty"` - IDToken string `msgpack:"it,omitempty"` - RefreshToken string `msgpack:"rt,omitempty"` - - RedirectURL string `msgpack:"ru,omitempty"` - SessionID string `msgpack:"si,omitempty"` - ClusterID string `msgpack:"ci,omitempty"` -} - -func (s *SessionState) Encode() ([]byte, error) { - return msgpack.Marshal(s) -} - -func Decode(data string) (*SessionState, error) { - decoded, err := base64.RawURLEncoding.DecodeString(data) - if err != nil { - return nil, err - } - - var ss SessionState - err = msgpack.Unmarshal(decoded, &ss) - if err != nil { - return nil, fmt.Errorf("error unmarshalling data to session state: %w", err) - } - - return &ss, nil -} - -// lz4Compress compresses with LZ4 -// -// The Compress:Decompress ratio is 1:Many. LZ4 gives fastest decompress speeds -// at the expense of greater compression compared to other compression -// algorithms. -// -//nolint:unused -func lz4Compress(payload []byte) ([]byte, error) { - buf := new(bytes.Buffer) - zw := lz4.NewWriter(nil) - zw.Header = lz4.Header{ - BlockMaxSize: 65536, - CompressionLevel: 0, - } - zw.Reset(buf) - - reader := bytes.NewReader(payload) - _, err := io.Copy(zw, reader) - if err != nil { - return nil, fmt.Errorf("error copying lz4 stream to buffer: %w", err) - } - err = zw.Close() - if err != nil { - return nil, fmt.Errorf("error closing lz4 writer: %w", err) - } - - compressed, err := io.ReadAll(buf) - if err != nil { - return nil, fmt.Errorf("error reading lz4 buffer: %w", err) - } - - return compressed, nil -} - -// lz4Decompress decompresses with LZ4 -// -//nolint:unused -func lz4Decompress(compressed []byte) ([]byte, error) { - reader := bytes.NewReader(compressed) - buf := new(bytes.Buffer) - zr := lz4.NewReader(nil) - zr.Reset(reader) - _, err := io.Copy(buf, zr) - if err != nil { - return nil, fmt.Errorf("error copying lz4 stream to buffer: %w", err) - } - - payload, err := io.ReadAll(buf) - if err != nil { - return nil, fmt.Errorf("error reading lz4 buffer: %w", err) - } - - return payload, nil -} diff --git a/backend/http/handler.go b/backend/http/handler.go index 07836b856..a1dbf192d 100644 --- a/backend/http/handler.go +++ b/backend/http/handler.go @@ -31,6 +31,7 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/securecookie" + "golang.org/x/oauth2" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -38,9 +39,9 @@ import ( componentbaseversion "k8s.io/component-base/version" "k8s.io/klog/v2" - "github.com/kube-bind/kube-bind/backend/cookie" "github.com/kube-bind/kube-bind/backend/kubernetes" "github.com/kube-bind/kube-bind/backend/kubernetes/resources" + "github.com/kube-bind/kube-bind/backend/session" "github.com/kube-bind/kube-bind/backend/template" bindversion "github.com/kube-bind/kube-bind/pkg/version" kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2" @@ -168,8 +169,20 @@ func prepareNoCache(w http.ResponseWriter) { } } +func getLogger(r *http.Request) klog.Logger { + return klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String()) +} + +func generateCookieName(clusterID string) string { + if clusterID == "" { + return "kube-bind" + } + + return fmt.Sprintf("kube-bind-%s", clusterID) +} + func (h *handler) handleAuthorize(w http.ResponseWriter, r *http.Request) { - logger := klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String()) + logger := getLogger(r) cluster := mux.Vars(r)["cluster"] @@ -200,33 +213,22 @@ func (h *handler) handleAuthorize(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, authURL, http.StatusFound) } -func parseJWT(p string) ([]byte, error) { - parts := strings.Split(p, ".") - if len(parts) < 2 { - return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) - } - payload, err := base64.RawURLEncoding.DecodeString(parts[1]) - if err != nil { - return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) - } - return payload, nil -} - // handleCallback handle the authorization redirect callback from OAuth2 auth flow. func (h *handler) handleCallback(w http.ResponseWriter, r *http.Request) { - logger := klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String()) + logger := getLogger(r) if errMsg := r.Form.Get("error"); errMsg != "" { - logger.Info("failed to authorize", "error", errMsg) + logger.Error(errors.New(errMsg), "failed to authorize") http.Error(w, errMsg+": "+r.Form.Get("error_description"), http.StatusBadRequest) return } + code := r.Form.Get("code") if code == "" { code = r.URL.Query().Get("code") } if code == "" { - logger.Info("no code in request", "error", "missing code") + logger.Error(errors.New("missing code"), "no code in request") http.Error(w, fmt.Sprintf("no code in request: %q", r.Form), http.StatusBadRequest) return } @@ -237,13 +239,13 @@ func (h *handler) handleCallback(w http.ResponseWriter, r *http.Request) { } decoded, err := base64.StdEncoding.DecodeString(state) if err != nil { - logger.Info("failed to decode state", "error", err) + logger.Error(err, "failed to decode state") http.Error(w, err.Error(), http.StatusBadRequest) return } authCode := &AuthCode{} if err := json.Unmarshal(decoded, authCode); err != nil { - logger.Info("faile to unmarshal authCode", "error", err) + logger.Error(err, "failed to unmarshal authCode") http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -252,54 +254,82 @@ func (h *handler) handleCallback(w http.ResponseWriter, r *http.Request) { token, err := h.oidc.OIDCProviderConfig(nil).Exchange(r.Context(), code) if err != nil { - logger.Info("failed to exchange token", "error", err) - http.Error(w, "internal error", http.StatusInternalServerError) - return - } - jwtStr, ok := token.Extra("id_token").(string) - if !ok { - logger.Info("failed to get id_token from token", "error", err) + logger.Error(err, "failed to exchange token") http.Error(w, "internal error", http.StatusInternalServerError) return } - jwt, err := parseJWT(jwtStr) + cookieName := generateCookieName(authCode.ClusterID) + sessionState, err := createSessionState(authCode, token) if err != nil { - logger.Info("failed to parse jwt", "error", err) + logger.Error(err, "failed to create session sessionState") http.Error(w, "internal error", http.StatusInternalServerError) return } - sessionCookie := cookie.SessionState{ - CreatedAt: time.Now(), - ExpiresOn: token.Expiry, - AccessToken: token.AccessToken, - IDToken: string(jwt), - RefreshToken: token.RefreshToken, - RedirectURL: authCode.RedirectURL, - SessionID: authCode.SessionID, - ClusterID: authCode.ClusterID, - } - - cookieName := "kube-bind-" + authCode.SessionID s := securecookie.New(h.cookieSigningKey, h.cookieEncryptionKey) - encoded, err := s.Encode(cookieName, sessionCookie) + encoded, err := s.Encode(cookieName, sessionState) if err != nil { - logger.Info("failed to encode secure session cookie", "error", err) + logger.Error(err, "failed to encode secure session cookie") http.Error(w, "internal error", http.StatusInternalServerError) return } - http.SetCookie(w, cookie.MakeCookie(r, cookieName, encoded, time.Duration(1)*time.Hour)) + // setting to false so it works over http://localhost + secure := false + + http.SetCookie(w, session.MakeCookie(r, cookieName, encoded, secure, 1*time.Hour)) if authCode.ClusterID == "" { - http.Redirect(w, r, "/resources?s="+authCode.SessionID, http.StatusFound) + http.Redirect(w, r, "/resources", http.StatusFound) } else { - http.Redirect(w, r, "/clusters/"+authCode.ClusterID+"/resources?s="+authCode.SessionID, http.StatusFound) + http.Redirect(w, r, "/clusters/"+authCode.ClusterID+"/resources", http.StatusFound) + } +} + +func unwrapJWT(p string) ([]byte, error) { + parts := strings.Split(p, ".") + if len(parts) < 2 { + return nil, fmt.Errorf("OIDC: malformed JWT, expected 3 parts, got %d", len(parts)) } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("OIDC: malformed JWT payload: %w", err) + } + return payload, nil +} + +func createSessionState(authCode *AuthCode, token *oauth2.Token) (*session.State, error) { + jwtStr, ok := token.Extra("id_token").(string) + if !ok { + return nil, errors.New("no id_token value found in token") + } + + jwt, err := unwrapJWT(jwtStr) + if err != nil { + return nil, fmt.Errorf("failed to unpack ID token: %w", err) + } + + var idToken struct { + Subject string `json:"sub"` + Issuer string `json:"iss"` + } + if err := json.Unmarshal(jwt, &idToken); err != nil { + return nil, fmt.Errorf("failed to parse ID token: %w", err) + } + + return &session.State{ + Token: session.TokenInfo{ + Subject: idToken.Subject, + Issuer: idToken.Issuer, + }, + SessionID: authCode.SessionID, + ClusterID: authCode.ClusterID, + RedirectURL: authCode.RedirectURL, + }, nil } func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) { - logger := klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String()) + logger := getLogger(r) prepareNoCache(w) @@ -344,12 +374,10 @@ func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) { } bs := bytes.Buffer{} if err := resourcesTemplate.Execute(&bs, struct { - SessionID string Cluster string CRDs []*apiextensionsv1.CustomResourceDefinition APIResourceSchemas []kubebindv1alpha2.APIResourceSchema }{ - SessionID: r.URL.Query().Get("s"), Cluster: cluster, CRDs: rightScopedCRDs, APIResourceSchemas: apiResourceSchemas.Items, @@ -359,24 +387,27 @@ func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) { return } - w.Header().Set("Content-Type", "text/html") + w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write(bs.Bytes()) //nolint:errcheck } func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { - logger := klog.FromContext(r.Context()).WithValues("method", r.Method, "url", r.URL.String()) + logger := getLogger(r) + group := r.URL.Query().Get("group") + resource := r.URL.Query().Get("resource") + cluster := mux.Vars(r)["cluster"] prepareNoCache(w) - cookieName := "kube-bind-" + r.URL.Query().Get("s") + cookieName := generateCookieName(cluster) ck, err := r.Cookie(cookieName) if err != nil { logger.Error(err, "failed to get session cookie") - http.Error(w, "internal error", http.StatusInternalServerError) + http.Error(w, "no session cookie found", http.StatusBadRequest) return } - state := cookie.SessionState{} + state := session.State{} s := securecookie.New(h.cookieSigningKey, h.cookieEncryptionKey) if err := s.Decode(cookieName, ck.Value, &state); err != nil { logger.Error(err, "failed to decode session cookie") @@ -384,21 +415,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { return } - var idToken struct { - Subject string `json:"sub"` - Issuer string `json:"iss"` - } - if err := json.Unmarshal([]byte(state.IDToken), &idToken); err != nil { - logger.Error(err, "failed to unmarshal id token") - http.Error(w, "internal error", http.StatusInternalServerError) - return - } - - group := r.URL.Query().Get("group") - resource := r.URL.Query().Get("resource") - cluster := mux.Vars(r)["cluster"] - - kfg, err := h.kubeManager.HandleResources(r.Context(), idToken.Subject+"#"+state.ClusterID, cluster, resource, group) + kfg, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject+"#"+state.ClusterID, cluster, resource, group) if err != nil { logger.Error(err, "failed to handle resources") http.Error(w, "internal error", http.StatusInternalServerError) @@ -430,6 +447,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { http.Error(w, "internal error", http.StatusInternalServerError) return } + response := kubebindv1alpha2.BindingResponse{ TypeMeta: metav1.TypeMeta{ APIVersion: kubebindv1alpha2.SchemeGroupVersion.String(), @@ -438,7 +456,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { Authentication: kubebindv1alpha2.BindingResponseAuthentication{ OAuth2CodeGrant: &kubebindv1alpha2.BindingResponseAuthenticationOAuth2CodeGrant{ SessionID: state.SessionID, - ID: idToken.Issuer + "/" + idToken.Subject, + ID: state.Token.Issuer + "/" + state.Token.Subject, }, }, Kubeconfig: kfg, @@ -447,7 +465,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { payload, err := json.Marshal(&response) if err != nil { - logger.Error(err, "failed to marshal auth response") + logger.Error(err, "failed to marshal binding response") http.Error(w, "internal error", http.StatusInternalServerError) return } @@ -456,7 +474,7 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { parsedAuthURL, err := url.Parse(state.RedirectURL) if err != nil { - logger.Error(err, "failed to parse redirect url") + logger.Error(err, "failed to parse redirect URL") http.Error(w, "internal error", http.StatusInternalServerError) return } diff --git a/backend/cookie/cookie.go b/backend/session/cookie.go similarity index 55% rename from backend/cookie/cookie.go rename to backend/session/cookie.go index ebcee017e..4fb905ea2 100644 --- a/backend/cookie/cookie.go +++ b/backend/session/cookie.go @@ -14,39 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cookie +package session import ( - "fmt" "net/http" "time" ) -func MakeCookie(req *http.Request, name string, value string, expiration time.Duration) *http.Cookie { +func MakeCookie(req *http.Request, name string, value string, secure bool, expiration time.Duration) *http.Cookie { return &http.Cookie{ Name: name, Value: value, Path: "/", // TODO: make configurable Domain: "", // TODO: add domain support Expires: time.Now().Add(expiration), - HttpOnly: true, // TODO: make configurable - // setting to false so it works over http://localhost - Secure: false, // TODO: make configurable - SameSite: ParseSameSite(""), // TODO: make configurable - } -} - -func ParseSameSite(v string) http.SameSite { - switch v { - case "lax": - return http.SameSiteLaxMode - case "strict": - return http.SameSiteStrictMode - case "none": - return http.SameSiteNoneMode - case "": - return 0 - default: - panic(fmt.Sprintf("Invalid value for SameSite: %s", v)) + HttpOnly: true, + Secure: secure, + SameSite: http.SameSiteLaxMode, } } diff --git a/backend/session/session.go b/backend/session/session.go new file mode 100644 index 000000000..387f73d93 --- /dev/null +++ b/backend/session/session.go @@ -0,0 +1,43 @@ +/* +Copyright 2022 The Kube Bind Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package session + +import ( + "github.com/vmihailenco/msgpack/v4" +) + +// State is the data stored on the clientside as a browser cookie. +// To be independent of the OIDC token sizes, it only stores the absolute minimum +// information required for this particular backend implementation. Especially +// when lots of groups and claims are involved, the tokens can grow to a size +// larger than allowed by browsers for cookies, and even compression would only +// be a small adhesive strip, not a solution. +type State struct { + Token TokenInfo `msgpack:"tok,omitempty"` + SessionID string `msgpack:"sid,omitempty"` + ClusterID string `msgpack:"cl,omitempty"` + RedirectURL string `msgpack:"red,omitempty"` +} + +type TokenInfo struct { + Subject string `msgpack:"sub,omitempty"` + Issuer string `msgpack:"iss,omitempty"` +} + +func (s *State) Encode() ([]byte, error) { + return msgpack.Marshal(s) +} diff --git a/backend/template/resources.gohtml b/backend/template/resources.gohtml index 4d395ee80..5197f4c07 100644 --- a/backend/template/resources.gohtml +++ b/backend/template/resources.gohtml @@ -13,7 +13,7 @@

CRD

- {{$sid := .SessionID}}{{range .CRDs}} + {{range .CRDs}}

{{.Spec.Names.Singular}}

- Bind + Bind
{{end}}

APIResourceSchema

- {{$sid := .SessionID}}{{range .APIResourceSchemas}} + {{range .APIResourceSchemas}}

{{.Spec.Names.Singular}}

- Bind + Bind
{{end}} @@ -45,4 +45,4 @@ - \ No newline at end of file + diff --git a/contrib/example-backend/session/session.go b/contrib/example-backend/session/session.go new file mode 100644 index 000000000..387f73d93 --- /dev/null +++ b/contrib/example-backend/session/session.go @@ -0,0 +1,43 @@ +/* +Copyright 2022 The Kube Bind Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package session + +import ( + "github.com/vmihailenco/msgpack/v4" +) + +// State is the data stored on the clientside as a browser cookie. +// To be independent of the OIDC token sizes, it only stores the absolute minimum +// information required for this particular backend implementation. Especially +// when lots of groups and claims are involved, the tokens can grow to a size +// larger than allowed by browsers for cookies, and even compression would only +// be a small adhesive strip, not a solution. +type State struct { + Token TokenInfo `msgpack:"tok,omitempty"` + SessionID string `msgpack:"sid,omitempty"` + ClusterID string `msgpack:"cl,omitempty"` + RedirectURL string `msgpack:"red,omitempty"` +} + +type TokenInfo struct { + Subject string `msgpack:"sub,omitempty"` + Issuer string `msgpack:"iss,omitempty"` +} + +func (s *State) Encode() ([]byte, error) { + return msgpack.Marshal(s) +} diff --git a/go.mod b/go.mod index e213780db..b91fd117b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/kube-bind/kube-bind/sdk/apis v0.4.8 github.com/kube-bind/kube-bind/sdk/client v0.0.0-20250515145715-d9f20e7c840d github.com/martinlindhe/base36 v1.1.1 - github.com/pierrec/lz4 v2.6.1+incompatible github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 @@ -71,7 +70,6 @@ require ( github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/frankban/quicktest v1.14.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect diff --git a/go.sum b/go.sum index 20cd8aca5..458ec2d25 100644 --- a/go.sum +++ b/go.sum @@ -27,7 +27,6 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -48,8 +47,6 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -88,7 +85,6 @@ github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -144,7 +140,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -191,8 +186,6 @@ github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -208,7 +201,6 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -373,7 +365,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=