Skip to content

Commit f8dae5e

Browse files
committed
UPSTREAM: 132960: Configure JSON content type for generic webhook RESTClient.
Authorization, token authentication, imagepolicy admission, and audit webhooks configure RESTClients that encode to JSON regardless of the ContentType of the provided rest.Config. Because this is opaque to the RESTClient, configuring a ContentType other than "application/json" results in requests with JSON-encoded bodies and a non-JSON media type in the Content-Type header. Webhook servers that respect the Content-Type request header will be unable to decode an object from the request body. Explicitly overriding the ContentType of the provided rest.Config fixes this issue and is consistent with how clients are constructed for conversion and admission webhooks.
1 parent e52b13b commit f8dae5e

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFact
8383
clientConfig := rest.CopyConfig(config)
8484

8585
codec := codecFactory.LegacyCodec(groupVersions...)
86+
clientConfig.ContentType = runtime.ContentTypeJSON
8687
clientConfig.ContentConfig.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
8788

8889
clientConfig.Wrap(x509metrics.NewDeprecatedCertificateRoundTripperWrapperConstructor(

staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"encoding/json"
2424
"errors"
2525
"fmt"
26+
"io"
2627
"net"
2728
"net/http"
2829
"net/http/httptest"
@@ -33,11 +34,15 @@ import (
3334
"strings"
3435
"testing"
3536
"time"
37+
"unicode/utf8"
3638

3739
apierrors "k8s.io/apimachinery/pkg/api/errors"
3840
"k8s.io/apimachinery/pkg/runtime"
3941
"k8s.io/apimachinery/pkg/runtime/schema"
42+
"k8s.io/apimachinery/pkg/runtime/serializer"
4043
"k8s.io/apimachinery/pkg/util/wait"
44+
exampleinstall "k8s.io/apiserver/pkg/apis/example/install"
45+
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
4146
"k8s.io/client-go/kubernetes/scheme"
4247
"k8s.io/client-go/rest"
4348
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
@@ -927,3 +932,57 @@ func getSingleCounterValueFromRegistry(t *testing.T, r metrics.Gatherer, name st
927932

928933
return -1
929934
}
935+
936+
func TestRESTConfigContentType(t *testing.T) {
937+
server, err := newTestServer(clientCert, clientKey, caCert, func(w http.ResponseWriter, r *http.Request) {
938+
if got := r.Header.Get("Content-Type"); got != runtime.ContentTypeJSON {
939+
t.Errorf("expected request content-type: want %q got %q", runtime.ContentTypeJSON, got)
940+
}
941+
body, err := io.ReadAll(r.Body)
942+
if err != nil {
943+
t.Errorf("failed to read request body: %v", err)
944+
return
945+
}
946+
if err := json.Unmarshal(body, new(any)); err != nil {
947+
switch {
948+
case len(body) == 0:
949+
t.Log("empty request body")
950+
case utf8.Valid(body):
951+
t.Logf("request body: %s", string(body))
952+
default:
953+
t.Logf("request body: 0x%x", body)
954+
}
955+
t.Errorf("failed to unmarshal request body as json: %v", err)
956+
}
957+
})
958+
if err != nil {
959+
t.Errorf("failed to create server: %v", err)
960+
return
961+
}
962+
defer server.Close()
963+
964+
config := &rest.Config{
965+
ContentConfig: rest.ContentConfig{
966+
ContentType: "foo/bar",
967+
},
968+
Host: server.URL,
969+
TLSClientConfig: rest.TLSClientConfig{
970+
CAData: caCert,
971+
CertData: clientCert,
972+
KeyData: clientKey,
973+
},
974+
}
975+
976+
scheme := runtime.NewScheme()
977+
exampleinstall.Install(scheme)
978+
codecs := serializer.NewCodecFactory(scheme)
979+
groupVersions := []schema.GroupVersion{examplev1.SchemeGroupVersion}
980+
wh, err := NewGenericWebhook(scheme, codecs, config, groupVersions, retryBackoff)
981+
if err != nil {
982+
t.Fatalf("failed to create the webhook: %v", err)
983+
}
984+
985+
if err := wh.RestClient.Post().Body(&examplev1.Pod{}).Do(context.TODO()).Error(); err != nil {
986+
t.Fatalf("failed to complete request: %v", err)
987+
}
988+
}

0 commit comments

Comments
 (0)