Skip to content

Commit

Permalink
Merge pull request #4 from dramich/rbac
Browse files Browse the repository at this point in the history
Add validation for rancher rbac objects
  • Loading branch information
ibuildthecloud authored Sep 25, 2020
2 parents da71a36 + 3d719a0 commit 1a8e7a0
Show file tree
Hide file tree
Showing 17 changed files with 1,222 additions and 125 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/dist
*.swp
.idea
/webhook
34 changes: 31 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,42 @@ module github.com/rancher/webhook

go 1.14

replace k8s.io/client-go => k8s.io/client-go v0.18.0
replace (
k8s.io/api => k8s.io/api v0.19.0
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.19.0
k8s.io/apimachinery => github.com/rancher/apimachinery v0.19.0-rancher1
k8s.io/apiserver => k8s.io/apiserver v0.19.0
k8s.io/cli-runtime => k8s.io/cli-runtime v0.19.0
k8s.io/client-go => github.com/rancher/client-go v1.19.0-rancher.1
k8s.io/cloud-provider => k8s.io/cloud-provider v0.19.0
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.19.0
k8s.io/code-generator => k8s.io/code-generator v0.19.0
k8s.io/component-base => k8s.io/component-base v0.19.0
k8s.io/cri-api => k8s.io/cri-api v0.19.0
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.19.0
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.19.0
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.19.0
k8s.io/kube-proxy => k8s.io/kube-proxy v0.19.0
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.19.0
k8s.io/kubectl => k8s.io/kubectl v0.19.0
k8s.io/kubelet => k8s.io/kubelet v0.19.0
k8s.io/kubernetes => k8s.io/kubernetes v1.19.0
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.19.0
k8s.io/metrics => k8s.io/metrics v0.19.0
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.19.0
)

require (
github.com/rancher/dynamiclistener v0.2.1-0.20200910203214-85f32491cb09
github.com/rancher/lasso v0.0.0-20200820172840-0e4cc0ef5cb0
github.com/rancher/rancher/pkg/apis v0.0.0-20200910005616-198ec5bdf52d
github.com/rancher/wrangler v0.6.2-0.20200909050541-7465f10bdac7
github.com/rancher/wrangler-api v0.6.1-0.20200427172631-a7c2f09b783e
github.com/sirupsen/logrus v1.6.0
k8s.io/api v0.18.8
k8s.io/apimachinery v0.18.8
k8s.io/api v0.19.0
k8s.io/apimachinery v0.19.0
k8s.io/apiserver v0.19.0
k8s.io/client-go v12.0.0+incompatible
k8s.io/kubernetes v1.19.0
k8s.io/utils v0.0.0-20200729134348-d5654de09c73
)
436 changes: 334 additions & 102 deletions go.sum

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:generate go run pkg/codegen/cleanup/main.go
//go:generate go run pkg/codegen/main.go
package main

import (
Expand Down
83 changes: 69 additions & 14 deletions pkg/admission/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,33 @@ import (
v1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

var (
namespace = "cattle-system"
tlsName = "rancher-webhook.cattle-system.svc"
certName = "cattle-webhook-tls"
caName = "cattle-webhook-ca"
port = int32(443)
path = "/v1/webhook/validation"
clusterScope = v1.ClusterScope
failPolicy = v1.Ignore
sideEffect = v1.SideEffectClassNone
namespace = "cattle-system"
tlsName = "rancher-webhook.cattle-system.svc"
certName = "cattle-webhook-tls"
caName = "cattle-webhook-ca"
port = int32(443)
path = "/v1/webhook/validation"
clusterScope = v1.ClusterScope
namespaceScope = v1.NamespacedScope
failPolicyFail = v1.Fail
failPolicyIgnore = v1.Ignore
sideEffect = v1.SideEffectClassNone
)

func ListenAndServe(ctx context.Context, cfg *rest.Config) error {
if err := schemes.Register(v1.AddToScheme); err != nil {
return err
}

k8s, err := kubernetes.NewForConfig(cfg)
handler, err := Validation(cfg)
if err != nil {
return err
}

handler := Validation(k8s.AuthorizationV1().SubjectAccessReviews())

return listenAndServe(ctx, cfg, handler)
}

Expand Down Expand Up @@ -91,7 +90,63 @@ func listenAndServe(ctx context.Context, cfg *rest.Config, handler http.Handler)
},
},
},
FailurePolicy: &failPolicy,
FailurePolicy: &failPolicyIgnore,
SideEffects: &sideEffect,
AdmissionReviewVersions: []string{"v1"},
},
{
Name: "rancherauth.cattle.io",
ClientConfig: v1.WebhookClientConfig{
Service: &v1.ServiceReference{
Namespace: namespace,
Name: "rancher-webhook",
Path: &path,
Port: &port,
},
CABundle: secret.Data[corev1.TLSCertKey],
},
Rules: []v1.RuleWithOperations{
{
Operations: []v1.OperationType{
v1.Create,
v1.Update,
v1.Delete,
},
Rule: v1.Rule{
APIGroups: []string{"management.cattle.io"},
APIVersions: []string{"v3"},
Resources: []string{"globalrolebindings"},
Scope: &clusterScope,
},
},
{
Operations: []v1.OperationType{
v1.Create,
v1.Update,
v1.Delete,
},
Rule: v1.Rule{
APIGroups: []string{"management.cattle.io"},
APIVersions: []string{"v3"},
Resources: []string{"projectroletemplatebindings"},
Scope: &namespaceScope,
},
},
{
Operations: []v1.OperationType{
v1.Create,
v1.Update,
v1.Delete,
},
Rule: v1.Rule{
APIGroups: []string{"management.cattle.io"},
APIVersions: []string{"v3"},
Resources: []string{"clusterroletemplatebindings"},
Scope: &namespaceScope,
},
},
},
FailurePolicy: &failPolicyFail,
SideEffects: &sideEffect,
AdmissionReviewVersions: []string{"v1"},
},
Expand Down
40 changes: 36 additions & 4 deletions pkg/admission/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,47 @@ import (

"github.com/rancher/rancher/pkg/apis/management.cattle.io"
v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
"github.com/rancher/webhook/pkg/auth"
"github.com/rancher/webhook/pkg/cluster"
mgmtcontrollers "github.com/rancher/webhook/pkg/generated/controllers/management.cattle.io"
"github.com/rancher/wrangler-api/pkg/generated/controllers/rbac"
"github.com/rancher/wrangler/pkg/webhook"
authorizationv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

func Validation(sar authorizationv1.SubjectAccessReviewInterface) http.Handler {
clusters := newClusterValidator(sar)
func Validation(cfg *rest.Config) (http.Handler, error) {
grb, err := mgmtcontrollers.NewFactoryFromConfig(cfg)
if err != nil {
return nil, err
}

r, err := rbac.NewFactoryFromConfig(cfg)
if err != nil {
return nil, err
}

globalRoleBindings, err := auth.NewGRBValidator(grb.Management().V3().GlobalRole(), r.Rbac())
if err != nil {
return nil, err
}

k8s, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, err
}

sar := k8s.AuthorizationV1().SubjectAccessReviews()

clusters := cluster.NewClusterValidator(sar)
prtbs := auth.NewPRTBalidator(sar)
crtbs := auth.NewCRTBalidator(sar)

router := webhook.NewRouter()
router.Kind("Cluster").Group(management.GroupName).Type(&v3.Cluster{}).Handle(clusters)
router.Kind("GlobalRoleBinding").Group(management.GroupName).Type(&v3.GlobalRoleBinding{}).Handle(globalRoleBindings)
router.Kind("ProjectRoleTemplateBinding").Group(management.GroupName).Type(&v3.ProjectRoleTemplateBinding{}).Handle(prtbs)
router.Kind("ClusterRoleTemplateBinding").Group(management.GroupName).Type(&v3.ClusterRoleTemplateBinding{}).Handle(crtbs)

return router
return router, nil
}
96 changes: 96 additions & 0 deletions pkg/auth/clusterrtb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package auth

import (
"net/http"
"time"

rancherv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
"github.com/rancher/wrangler/pkg/webhook"
admissionv1 "k8s.io/api/admission/v1"
authenticationv1 "k8s.io/api/authentication/v1"
v1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
authorizationv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/utils/trace"
)

const adminRole = "admin"

func NewCRTBalidator(sar authorizationv1.SubjectAccessReviewInterface) webhook.Handler {
return &clusterRoleTemplateBindingValidator{
sar: sar,
}
}

type clusterRoleTemplateBindingValidator struct {
sar authorizationv1.SubjectAccessReviewInterface
}

func (c *clusterRoleTemplateBindingValidator) Admit(response *webhook.Response, request *webhook.Request) error {
listTrace := trace.New("clusterRoleTemplateBindingValidator Admit", trace.Field{Key: "user", Value: request.UserInfo.Username})
defer listTrace.LogIfLong(1 * time.Second)

crtb, err := crtbObject(request)
if err != nil {
return err
}

if crtb.ClusterName != "local" {
response.Allowed = true
return nil
}

return adminAccessCheck(c.sar, response, request)
}

func crtbObject(request *webhook.Request) (*rancherv3.ClusterRoleTemplateBinding, error) {
var crtb runtime.Object
var err error
if request.Operation == admissionv1.Delete {
crtb, err = request.DecodeOldObject()
} else {
crtb, err = request.DecodeObject()
}
return crtb.(*rancherv3.ClusterRoleTemplateBinding), err
}

func toExtra(extra map[string]authenticationv1.ExtraValue) map[string]v1.ExtraValue {
result := map[string]v1.ExtraValue{}
for k, v := range extra {
result[k] = v1.ExtraValue(v)
}
return result
}

// adminAccessCheck checks that the user submitting the request has ** access in the local cluster
func adminAccessCheck(sar authorizationv1.SubjectAccessReviewInterface, response *webhook.Response, request *webhook.Request) error {
resp, err := sar.Create(request.Context, &v1.SubjectAccessReview{
Spec: v1.SubjectAccessReviewSpec{
ResourceAttributes: &v1.ResourceAttributes{
Group: "*",
Verb: "*",
Resource: "*",
},
User: request.UserInfo.Username,
Groups: request.UserInfo.Groups,
Extra: toExtra(request.UserInfo.Extra),
UID: request.UserInfo.UID,
},
}, metav1.CreateOptions{})
if err != nil {
return err
}

if resp.Status.Allowed {
response.Allowed = true
} else {
response.Result = &metav1.Status{
Status: "Failure",
Message: resp.Status.Reason,
Reason: metav1.StatusReasonUnauthorized,
Code: http.StatusUnauthorized,
}
}
return nil
}
Loading

0 comments on commit 1a8e7a0

Please sign in to comment.