// OriginAuthorizerAttributes adapts Kubernetes authorization attributes to Origin authorization attributes // Note that some info (like resourceName, apiVersion, apiGroup) is not available from the Kubernetes attributes func OriginAuthorizerAttributes(kattrs kauthorizer.Attributes) (kapi.Context, oauthorizer.AuthorizationAttributes) { // Build a context to hold the namespace and user info ctx := kapi.NewContext() ctx = kapi.WithNamespace(ctx, kattrs.GetNamespace()) ctx = kapi.WithUser(ctx, &user.DefaultInfo{ Name: kattrs.GetUserName(), Groups: kattrs.GetGroups(), }) // If we recognize the type, use the embedded type. Do NOT use it directly, because not all things that quack are ducks. if castAdapterAttributes, ok := kattrs.(AdapterAttributes); ok { return ctx, castAdapterAttributes.authorizationAttributes } // Otherwise build what we can oattrs := &oauthorizer.DefaultAuthorizationAttributes{ Verb: kattrs.GetVerb(), APIGroup: kattrs.GetAPIGroup(), APIVersion: kattrs.GetAPIVersion(), Resource: kattrs.GetResource(), ResourceName: kattrs.GetName(), NonResourceURL: kattrs.IsResourceRequest() == false, URL: kattrs.GetPath(), // TODO: add to kube authorizer attributes // RequestAttributes interface{} } if len(kattrs.GetSubresource()) > 0 { oattrs.Resource = kattrs.GetResource() + "/" + kattrs.GetSubresource() } return ctx, oattrs }
// 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) (err error) { r := &v1beta1.SubjectAccessReview{ Spec: v1beta1.SubjectAccessReviewSpec{ User: attr.GetUserName(), Groups: attr.GetGroups(), }, } if attr.IsResourceRequest() { r.Spec.ResourceAttributes = &v1beta1.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 = &v1beta1.NonResourceAttributes{ Path: attr.GetPath(), Verb: attr.GetVerb(), } } key, err := json.Marshal(r.Spec) if err != nil { return err } if entry, ok := w.responseCache.Get(string(key)); ok { r.Status = entry.(v1beta1.SubjectAccessReviewStatus) } else { result := w.WithExponentialBackoff(func() restclient.Result { return w.RestClient.Post().Body(r).Do() }) if err := result.Error(); 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 err } var statusCode int if result.StatusCode(&statusCode); statusCode < 200 || statusCode >= 300 { return fmt.Errorf("Error contacting webhook: %d", statusCode) } if err := result.Into(r); err != nil { return err } if r.Status.Allowed { w.responseCache.Add(string(key), r.Status, w.authorizedTTL) } else { w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL) } } if r.Status.Allowed { return nil } if r.Status.Reason != "" { return errors.New(r.Status.Reason) } return errors.New("unauthorized") }
// 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 }
// 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) (err error) { r := &v1beta1.SubjectAccessReview{ Spec: v1beta1.SubjectAccessReviewSpec{ User: attr.GetUserName(), Groups: attr.GetGroups(), }, } if attr.IsResourceRequest() { r.Spec.ResourceAttributes = &v1beta1.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 = &v1beta1.NonResourceAttributes{ Path: attr.GetPath(), Verb: attr.GetVerb(), } } key, err := json.Marshal(r.Spec) if err != nil { return err } if entry, ok := w.responseCache.Get(string(key)); ok { r.Status = entry.(v1beta1.SubjectAccessReviewStatus) } else { result := w.RestClient.Post().Body(r).Do() if err := result.Error(); err != nil { return err } if err := result.Into(r); err != nil { return err } go func() { if r.Status.Allowed { w.responseCache.Add(string(key), r.Status, w.authorizedTTL) } else { w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL) } }() } if r.Status.Allowed { return nil } if r.Status.Reason != "" { return errors.New(r.Status.Reason) } return errors.New("unauthorized") }
// 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) (err error) { r := &v1beta1.SubjectAccessReview{ Spec: v1beta1.SubjectAccessReviewSpec{ User: attr.GetUserName(), Groups: attr.GetGroups(), }, } if attr.IsResourceRequest() { r.Spec.ResourceAttributes = &v1beta1.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 = &v1beta1.NonResourceAttributes{ Path: attr.GetPath(), Verb: attr.GetVerb(), } } result := w.RestClient.Post().Body(r).Do() if err := result.Error(); err != nil { return err } if err := result.Into(r); err != nil { return err } if r.Status.Allowed { return nil } if r.Status.Reason != "" { return errors.New(r.Status.Reason) } return errors.New("unauthorized") }