// Authorize makes a REST request to the remote service describing the attempted action as a JSON // serialized api.authorization.v1beta1.SubjectAccessReview object. An example request body is // provided bellow. // // { // "apiVersion": "authorization.k8s.io/v1beta1", // "kind": "SubjectAccessReview", // "spec": { // "resourceAttributes": { // "namespace": "kittensandponies", // "verb": "GET", // "group": "group3", // "resource": "pods" // }, // "user": "******", // "group": [ // "group1", // "group2" // ] // } // } // // The remote service is expected to fill the SubjectAccessReviewStatus field to either allow or // disallow access. A permissive response would return: // // { // "apiVersion": "authorization.k8s.io/v1beta1", // "kind": "SubjectAccessReview", // "status": { // "allowed": true // } // } // // To disallow access, the remote service would return: // // { // "apiVersion": "authorization.k8s.io/v1beta1", // "kind": "SubjectAccessReview", // "status": { // "allowed": false, // "reason": "user does not have read access to the namespace" // } // } // func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (authorized bool, reason string, err error) { r := &authorization.SubjectAccessReview{} if user := attr.GetUser(); user != nil { r.Spec = authorization.SubjectAccessReviewSpec{ User: user.GetName(), Groups: user.GetGroups(), Extra: convertToSARExtra(user.GetExtra()), } } if attr.IsResourceRequest() { r.Spec.ResourceAttributes = &authorization.ResourceAttributes{ Namespace: attr.GetNamespace(), Verb: attr.GetVerb(), Group: attr.GetAPIGroup(), Version: attr.GetAPIVersion(), Resource: attr.GetResource(), Subresource: attr.GetSubresource(), Name: attr.GetName(), } } else { r.Spec.NonResourceAttributes = &authorization.NonResourceAttributes{ Path: attr.GetPath(), Verb: attr.GetVerb(), } } key, err := json.Marshal(r.Spec) if err != nil { return false, "", err } if entry, ok := w.responseCache.Get(string(key)); ok { r.Status = entry.(authorization.SubjectAccessReviewStatus) } else { var ( result *authorization.SubjectAccessReview err error ) webhook.WithExponentialBackoff(w.initialBackoff, func() error { result, err = w.subjectAccessReview.Create(r) return err }) if err != nil { // An error here indicates bad configuration or an outage. Log for debugging. glog.Errorf("Failed to make webhook authorizer request: %v", err) return false, "", err } r.Status = result.Status if r.Status.Allowed { w.responseCache.Add(string(key), r.Status, w.authorizedTTL) } else { w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL) } } return r.Status.Allowed, r.Status.Reason, nil }
// AuthenticateToken implements the authenticator.Token interface. func (w *WebhookTokenAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) { r := &authentication.TokenReview{ Spec: authentication.TokenReviewSpec{Token: token}, } if entry, ok := w.responseCache.Get(r.Spec); ok { r.Status = entry.(authentication.TokenReviewStatus) } else { var ( result *authentication.TokenReview err error ) webhook.WithExponentialBackoff(w.initialBackoff, func() error { result, err = w.tokenReview.Create(r) return err }) if err != nil { return nil, false, err } r.Status = result.Status w.responseCache.Add(r.Spec, result.Status, w.ttl) } if !r.Status.Authenticated { return nil, false, nil } var extra map[string][]string if r.Status.User.Extra != nil { extra = map[string][]string{} for k, v := range r.Status.User.Extra { extra[k] = v } } return &user.DefaultInfo{ Name: r.Status.User.Username, UID: r.Status.User.UID, Groups: r.Status.User.Groups, Extra: extra, }, true, nil }