Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add several request/response headers in audit log entries #2121

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cmd/kube-controller-manager/app/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/metadata"
"k8s.io/client-go/rest"
restclient "k8s.io/client-go/rest"
cloudnodelifecyclecontroller "k8s.io/cloud-provider/controllers/nodelifecycle"
routecontroller "k8s.io/cloud-provider/controllers/route"
Expand Down Expand Up @@ -589,7 +590,12 @@ func startNamespaceController(ctx context.Context, controllerContext ControllerC
nsKubeconfig := controllerContext.ClientBuilder.ConfigOrDie("namespace-controller")
nsKubeconfig.QPS *= 20
nsKubeconfig.Burst *= 100
namespaceKubeClient := clientset.NewForConfigOrDie(nsKubeconfig)

// Use protobuf
config := rest.CopyConfig(nsKubeconfig)
config.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
config.ContentType = "application/vnd.kubernetes.protobuf"
namespaceKubeClient := clientset.NewForConfigOrDie(config)
return startModifiedNamespaceController(ctx, controllerContext, namespaceKubeClient, nsKubeconfig)
}

Expand Down
5 changes: 4 additions & 1 deletion cmd/kube-controller-manager/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,10 @@ func (s KubeControllerManagerOptions) Config(allControllers []string, disabledBy
kubeconfig.Wrap(customOpenShiftRoundTripper)
}

client, err := clientset.NewForConfig(restclient.AddUserAgent(kubeconfig, KubeControllerManagerUserAgent))
protobufConfig := restclient.CopyConfig(kubeconfig)
protobufConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
protobufConfig.ContentType = "application/vnd.kubernetes.protobuf"
client, err := clientset.NewForConfig(restclient.AddUserAgent(protobufConfig, KubeControllerManagerUserAgent))
if err != nil {
return nil, err
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/kube-controller-manager/app/patch_informers_openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ type combinedInformers struct {
}

func newInformerFactory(clientConfig *rest.Config) (informers.SharedInformerFactory, error) {
kubeClient, err := kubernetes.NewForConfig(clientConfig)
protobufConfig := rest.CopyConfig(clientConfig)
protobufConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
protobufConfig.ContentType = "application/vnd.kubernetes.protobuf"

kubeClient, err := kubernetes.NewForConfig(protobufConfig)
if err != nil {
return nil, err
}
Expand Down
7 changes: 5 additions & 2 deletions cmd/kube-scheduler/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,15 @@ func createKubeConfig(config componentbaseconfig.ClientConnectionConfiguration,

// createClients creates a kube client and an event client from the given kubeConfig
func createClients(kubeConfig *restclient.Config) (clientset.Interface, clientset.Interface, error) {
client, err := clientset.NewForConfig(restclient.AddUserAgent(kubeConfig, "scheduler"))
protobufConfig := restclient.CopyConfig(kubeConfig)
protobufConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
protobufConfig.ContentType = "application/vnd.kubernetes.protobuf"
client, err := clientset.NewForConfig(restclient.AddUserAgent(protobufConfig, "scheduler"))
if err != nil {
return nil, nil, err
}

eventClient, err := clientset.NewForConfig(kubeConfig)
eventClient, err := clientset.NewForConfig(protobufConfig)
if err != nil {
return nil, nil, err
}
Expand Down
18 changes: 18 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"net"
"net/http"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -242,9 +243,26 @@ func (a *auditResponseWriter) processCode(code int) {
})
}

func (a *auditResponseWriter) Header() http.Header {
for name, value := range a.ResponseWriter.Header() {
switch {
case name == "Content-Type":
a.event.Annotations["openshift.io/response-header-content-type"] = strings.Join(value, ",")
case name == "Content-Encoding":
a.event.Annotations["openshift.io/response-header-content-encoding"] = strings.Join(value, ",")
case name == "Content-Length":
a.event.Annotations["openshift.io/response-header-content-length"] = strings.Join(value, ",")
}
}
return a.ResponseWriter.Header()
}

func (a *auditResponseWriter) Write(bs []byte) (int, error) {
// the Go library calls WriteHeader internally if no code was written yet. But this will go unnoticed for us
a.processCode(http.StatusOK)
if _, ok := a.event.Annotations["openshift.io/response-header-content-length"]; !ok {
a.event.Annotations["openshift.io/response-header-content-length"] = fmt.Sprintf("%d", len(bs))
}
return a.ResponseWriter.Write(bs)
}

Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/apiserver/pkg/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.NonLongRunningRequestWaitGroup)
handler = WithNonReadyRequestLogging(handler, c.lifecycleSignals.HasBeenReady)
handler = WithLateConnectionFilter(handler)
handler = WithRequestHeaders(handler)
if c.ShutdownWatchTerminationGracePeriod > 0 {
handler = genericfilters.WithWatchTerminationDuringShutdown(handler, c.lifecycleSignals, c.WatchRequestWaitGroup)
}
Expand Down
22 changes: 20 additions & 2 deletions staging/src/k8s.io/apiserver/pkg/server/patch_genericapiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,10 @@ func WithLateConnectionFilter(handler http.Handler) http.Handler {
if late {
if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
if isLocal(r) {
audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=true,%v,readyz=false", r.URL.Host))
audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=true,%v,readyz=false", r.Host))
klog.V(4).Infof("Loopback request to %q (user agent %q) through connection created very late in the graceful termination process (more than 80%% has passed). This client probably does not watch /readyz and might get failures when termination is over.", r.URL.Path, r.UserAgent())
} else {
audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=false,%v,readyz=false", r.URL.Host))
audit.AddAuditAnnotation(r.Context(), "openshift.io/during-graceful", fmt.Sprintf("loopback=false,%v,readyz=false", r.Host))
klog.Warningf("Request to %q (source IP %s, user agent %q) through a connection created very late in the graceful termination process (more than 80%% has passed), possibly a sign for a broken load balancer setup.", r.URL.Path, r.RemoteAddr, r.UserAgent())

// create only one event to avoid event spam.
Expand All @@ -217,6 +217,24 @@ func WithLateConnectionFilter(handler http.Handler) http.Handler {
})
}

// WithRequestHeaders logs every non-probe request and logs interesting request headers.
func WithRequestHeaders(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if pth := "/" + strings.TrimLeft(r.URL.Path, "/"); pth != "/readyz" && pth != "/healthz" && pth != "/livez" {
if accept, ok := r.Header["Accept"]; ok {
audit.AddAuditAnnotation(r.Context(), "openshift.io/request-header-accept", strings.Join(accept, ","))
}
if accept_encoding, ok := r.Header["Accept-Encoding"]; ok {
audit.AddAuditAnnotation(r.Context(), "openshift.io/request-header-accept-encoding", strings.Join(accept_encoding, ","))
}
if content_length, ok := r.Header["Content-Length"]; ok {
audit.AddAuditAnnotation(r.Context(), "openshift.io/request-header-content-length", strings.Join(content_length, ","))
}
}
handler.ServeHTTP(w, r)
})
}

// WithNonReadyRequestLogging rejects the request until the process has been ready once.
func WithNonReadyRequestLogging(handler http.Handler, hasBeenReadySignal lifecycleSignal) http.Handler {
if hasBeenReadySignal == nil {
Expand Down