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

Audits in the Kube API server #48

Open
alejandrox1 opened this issue Sep 21, 2020 · 3 comments
Open

Audits in the Kube API server #48

alejandrox1 opened this issue Sep 21, 2020 · 3 comments
Assignees

Comments

@alejandrox1
Copy link
Collaborator

So it is possible to audit the Kube API server, https://kubernetes.io/docs/tasks/debug-application-cluster/audit/ .
The API server build audit events from the source code in https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/apiserver/pkg/audit

The cool thing that happens is that
https://github.com/kubernetes/kubernetes/blob/f5a0250800309017e667e82067d704b6ed28513a/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit_annotations.go#L22-L39
the api server surfaces function which decorates an http handler.

The decorated http handler injects an audit annotation slice into the incoming request's context.
The audit annotation is implemented as follows

// The key type is unexported to prevent collisions
type key int

const (
	// auditAnnotationsKey is the context key for the audit annotations.
	auditAnnotationsKey key = iota
)

// annotations = *[]annotation instead of a map to preserve order of insertions
type annotation struct {
	key, value string
}

https://github.com/kubernetes/kubernetes/blob/f5a0250800309017e667e82067d704b6ed28513a/staging/src/k8s.io/apiserver/pkg/audit/context.go#L49-L50

var annotations []annotation // avoid allocations until we actually need it
return genericapirequest.WithValue(parent, auditAnnotationsKey, &annotations)
// WithValue returns a copy of parent in which the value associated with key is val.
func WithValue(parent context.Context, key interface{}, val interface{}) context.Context {
  return context.WithValue(parent, key, val)                                    
}
@alejandrox1 alejandrox1 self-assigned this Sep 21, 2020
@alejandrox1
Copy link
Collaborator Author

Explain how to inject and extract request-scoped data with contexts
https://play.golang.org/p/Mj5puvW_zSF

package main

import (
	"context"
	"fmt"
)

type key int
type key_also int

// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// xref: https://godoc.org/context#Context

//The provided key must be comparable and should not be of type string
// or any other built-in type to avoid collisions between packages using
// context. Users of WithValue should define their own types for keys.
// To avoid allocating when assigning to an interface{}, context keys
// often have concrete type struct{}. Alternatively, exported context
// key variables' static type should be a pointer or interface.
//
// xref: https://godoc.org/context#WithValue
const auditAnnotationsKey key = iota

const namespaceKey key_also = iota

type auditAnnotation struct {
	op string
	resource string
}

func main() {
	fmt.Println("auditAnnotationsKey: ", auditAnnotationsKey)
	fmt.Println("namespaceKey: ", namespaceKey)
	
	ctx := context.Background()
	fmt.Printf("empty context: %#v\n", ctx)
	
	withAuditAnnotations := func(ictx context.Context) context.Context {
		a1 := auditAnnotation{op: "CREATE", resource: "POD"}
		return context.WithValue(ictx, auditAnnotationsKey, &a1)
	}
	
	withNamespace := func(ictx context.Context) context.Context {
		return context.WithValue(ictx, namespaceKey, "default")
	}
	
	ctx = withAuditAnnotations(ctx)
	fmt.Printf("context with audit annotation: %#v\n", ctx)
	
	ctx = withNamespace(ctx)
	fmt.Printf("context with namespace: %#v\n", ctx)
	
	namespaceFrom := func(ictx context.Context) (string, bool) {
		namespace, ok := ictx.Value(namespaceKey).(string)
		return namespace, ok
	}
	
	auditAnnotationFrom := func(ictx context.Context) (*auditAnnotation, bool) {
		annotations, ok := ictx.Value(auditAnnotationsKey).(*auditAnnotation)
		return annotations, ok
	}
	
	ns, ok1 := namespaceFrom(ctx)
	audit, ok2 := auditAnnotationFrom(ctx)
	fmt.Printf("context namespace: %#v %#v\n", ns, ok1)
	fmt.Printf("context audit annotations: %#v %#v\n", audit, ok2)
}

@alejandrox1
Copy link
Collaborator Author

The above works because

Struct are comparable, and two struct values are equal if their types match and their fields are equal.

@alejandrox1
Copy link
Collaborator Author

This may be a good place to follow up with filters and what goes on with the audit policies https://github.com/kubernetes/kubernetes/pull/94903/files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant