Skip to content

Commit

Permalink
Use http/2 for localhost webhook
Browse files Browse the repository at this point in the history
Signed-off-by: Eric Lin <exlin@google.com>
  • Loading branch information
linxiulei committed Jan 3, 2024
1 parent c686334 commit 08a7b2b
Show file tree
Hide file tree
Showing 308 changed files with 31,854 additions and 9 deletions.
25 changes: 25 additions & 0 deletions LICENSES/vendor/github.com/foxcpp/go-mockdns/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions LICENSES/vendor/github.com/miekg/dns/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
github.com/docker/go-units v0.5.0
github.com/emicklei/go-restful/v3 v3.11.0
github.com/evanphx/json-patch v4.12.0+incompatible
github.com/foxcpp/go-mockdns v1.0.0
github.com/fsnotify/fsnotify v1.7.0
github.com/go-logr/logr v1.3.0
github.com/godbus/dbus/v5 v5.1.0
Expand Down Expand Up @@ -194,6 +195,7 @@ require (
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.25 // indirect
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI=
github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
Expand Down Expand Up @@ -611,6 +613,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
Expand Down Expand Up @@ -858,6 +862,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
Expand Down Expand Up @@ -922,6 +927,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand Down Expand Up @@ -1000,6 +1006,8 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -1089,6 +1097,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
Expand Down
4 changes: 4 additions & 0 deletions hack/unwanted-dependencies.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
"sigs.k8s.io/kustomize/kustomize/v5",
"sigs.k8s.io/kustomize/kyaml"
],
"github.com/miekg/dns": [
"github.com/foxcpp/go-mockdns",
],
"github.com/pkg/errors": [
"github.com/Microsoft/hcsshim",
"github.com/containerd/continuity",
Expand Down Expand Up @@ -248,6 +251,7 @@
"github.com/grpc-ecosystem/grpc-gateway",
"github.com/json-iterator/go",
"github.com/mailru/easyjson",
"github.com/miekg/dns",
"github.com/pkg/errors",
"github.com/rubiojr/go-vhd",
"go.opencensus.io",
Expand Down
46 changes: 38 additions & 8 deletions staging/src/k8s.io/apiserver/pkg/util/webhook/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"net"
"net/url"
"strconv"
"strings"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -32,6 +33,7 @@ import (
"k8s.io/apiserver/pkg/util/x509metrics"
"k8s.io/client-go/rest"
"k8s.io/utils/lru"
netutils "k8s.io/utils/net"
)

const (
Expand Down Expand Up @@ -128,7 +130,21 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
return client.(*rest.RESTClient), nil
}

complete := func(cfg *rest.Config) (*rest.RESTClient, error) {
cfg, err := cm.hookClientConfig(cc)
if err != nil {
return nil, err
}

client, err := rest.UnversionedRESTClientFor(cfg)
if err == nil {
cm.cache.Add(string(cacheKey), client)
}
return client, err
}

func (cm *ClientManager) hookClientConfig(cc ClientConfig) (*rest.Config, error) {
forceHTTP1 := true
complete := func(cfg *rest.Config) (*rest.Config, error) {
// Avoid client-side rate limiting talking to the webhook backend.
// Rate limiting should happen when deciding how many requests to serve.
cfg.QPS = -1
Expand All @@ -142,7 +158,9 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
// Use http/1.1 instead of http/2.
// This is a workaround for http/2-enabled clients not load-balancing concurrent requests to multiple backends.
// See https://issue.k8s.io/75791 for details.
cfg.NextProtos = []string{"http/1.1"}
if forceHTTP1 {
cfg.NextProtos = []string{"http/1.1"}
}

cfg.ContentConfig.NegotiatedSerializer = cm.negotiatedSerializer
cfg.ContentConfig.ContentType = runtime.ContentTypeJSON
Expand All @@ -153,12 +171,7 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
x509MissingSANCounter,
x509InsecureSHA1Counter,
))

client, err := rest.UnversionedRESTClientFor(cfg)
if err == nil {
cm.cache.Add(string(cacheKey), client)
}
return client, err
return cfg, nil
}

if cc.Service != nil {
Expand Down Expand Up @@ -211,6 +224,10 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)}
}

if isLocalHost(u) {
forceHTTP1 = false
}

hostPort := u.Host
if len(u.Port()) == 0 {
// Default to port 443 if no port is specified
Expand All @@ -228,3 +245,16 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {

return complete(cfg)
}

func isLocalHost(u *url.URL) bool {
host := u.Hostname()
if strings.EqualFold(host, "localhost") {
return true
}

netIP := netutils.ParseIPSloppy(host)
if netIP != nil {
return netIP.IsLoopback()
}
return false
}
75 changes: 75 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/util/webhook/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Copyright 2024 The Kubernetes 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 webhook

import (
"testing"

"k8s.io/apimachinery/pkg/runtime/schema"
)

func TestWebhookClientConfig(t *testing.T) {
cm, _ := NewClientManager([]schema.GroupVersion{})
authInfoResolver, err := NewDefaultAuthenticationInfoResolver("")
if err != nil {
t.Fatal(err)
}
cm.SetAuthenticationInfoResolver(authInfoResolver)
cm.SetServiceResolver(NewDefaultServiceResolver())

tests := []struct {
name string
url string
expectAllowHTTP2 bool
}{
{
name: "force http1",
url: "https://webhook.example.com",
expectAllowHTTP2: false,
},
{
name: "allow http2 for localhost",
url: "https://localhost",
expectAllowHTTP2: true,
},
{
name: "allow http2 for 127.0.0.1",
url: "https://127.0.0.1",
expectAllowHTTP2: true,
},
{
name: "allow http2 for [::1]:0",
url: "https://[::1]",
expectAllowHTTP2: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {

cc := ClientConfig{
URL: tc.url,
}
cfg, err := cm.hookClientConfig(cc)
if err != nil {
t.Fatal(err)
}
if tc.expectAllowHTTP2 && len(cfg.NextProtos) != 0 {
t.Errorf("expected allow http/2, got: %v", cfg.NextProtos)
}
})
}
}
20 changes: 19 additions & 1 deletion test/integration/apiserver/admissionwebhook/load_balance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import (
"k8s.io/client-go/rest"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/test/integration/framework"

"github.com/foxcpp/go-mockdns"
)

const (
Expand All @@ -48,6 +50,21 @@ const (

// TestWebhookLoadBalance ensures that the admission webhook opens multiple connections to backends to satisfy concurrent requests
func TestWebhookLoadBalance(t *testing.T) {
srv, err := mockdns.NewServer(map[string]mockdns.Zone{
"example.com.": {
A: []string{"127.0.0.1"},
AAAA: []string{"::1"},
},
}, false)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = srv.Close()
}()

srv.PatchNet(net.DefaultResolver)
defer mockdns.UnpatchNet(net.DefaultResolver)

roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(localhostCert) {
Expand Down Expand Up @@ -80,7 +97,8 @@ func TestWebhookLoadBalance(t *testing.T) {
}()
defer httpServer.Close()

webhookURL := "https://" + localListener.Addr().String()
port := localListener.Addr().(*net.TCPAddr).Port
webhookURL := fmt.Sprintf("https://example.com:%d", port)

s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{
"--disable-admission-plugins=ServiceAccount",
Expand Down
1 change: 1 addition & 0 deletions vendor/github.com/foxcpp/go-mockdns/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions vendor/github.com/foxcpp/go-mockdns/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 08a7b2b

Please sign in to comment.