Skip to content

Commit

Permalink
Feat/add default role and permission (#24)
Browse files Browse the repository at this point in the history
* feat: added default role for every cluster

* typo: change port

* doc: added comments

* update default role model

* fix: update role name

* remove debug code
  • Loading branch information
chintansakhiya authored Jan 10, 2025
1 parent e6fd332 commit 10ef2d9
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 10 deletions.
57 changes: 47 additions & 10 deletions cmd/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package app

import (
"fmt"
"os"
"strconv"

Expand All @@ -14,11 +15,10 @@ import (
"github.com/Improwised/kube-oidc-proxy/cmd/app/options"
"github.com/Improwised/kube-oidc-proxy/pkg/probe"
"github.com/Improwised/kube-oidc-proxy/pkg/proxy"
"github.com/Improwised/kube-oidc-proxy/pkg/proxy/rbac"
"github.com/Improwised/kube-oidc-proxy/pkg/proxy/subjectaccessreview"
"github.com/Improwised/kube-oidc-proxy/pkg/proxy/tokenreview"
"github.com/Improwised/kube-oidc-proxy/pkg/util"

rbacvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
)

func NewRunCommand(stopCh <-chan struct{}) *cobra.Command {
Expand Down Expand Up @@ -55,17 +55,27 @@ func buildRunCommand(stopCh <-chan struct{}, opts *options.Options) *cobra.Comma
return err
}

// check if the cluster role config file exists
if _, err := os.Stat(opts.App.Cluster.RoleConfig); err != nil {
// Validate cluster config
if err := clusterConfigValidation(clustersConfig); err != nil {
return err
}

clustersRoleConfigMap, err := util.LoadRBACConfig(opts.App.Cluster.RoleConfig)
if err != nil {
return err
var clustersRoleConfigMap map[string]util.RBAC
if opts.App.Cluster.RoleConfig != "" {
// check if the cluster role config file exists
if _, err := os.Stat(opts.App.Cluster.RoleConfig); err != nil {
return err
}

clustersRoleConfigMap, err = util.LoadRBACConfig(opts.App.Cluster.RoleConfig)
if err != nil {
return err
}
}

isRBACLoded := make(map[string]bool)
for _, cluster := range clustersConfig {
isRBACLoded[cluster.Name] = false
ConfigFlags := &genericclioptions.ConfigFlags{
KubeConfig: &cluster.Path,
}
Expand Down Expand Up @@ -118,20 +128,32 @@ func buildRunCommand(stopCh <-chan struct{}, opts *options.Options) *cobra.Comma
}

subectAccessReviewer, err := subjectaccessreview.New(kubeclient.AuthorizationV1().SubjectAccessReviews())
kubeclient.AuthorizationV1().RESTClient()
if err != nil {
return err
}

cluster.Kubeclient = kubeclient
cluster.SubjectAccessReviewer = subectAccessReviewer

}

for clusterName, RBACConfig := range clustersRoleConfigMap {
for _, cluster := range clustersConfig {
if cluster.Name == clusterName {
_, StaticRoles := rbacvalidation.NewTestRuleResolver(RBACConfig.Roles, RBACConfig.RoleBindings, RBACConfig.ClusterRoles, RBACConfig.ClusterRoleBindings)
cluster.Authorizer = util.NewAuthorizer(StaticRoles)
isRBACLoded[cluster.Name] = true
err := rbac.LoadRBAC(RBACConfig, cluster)
if err != nil {
return err
}
}
}
}

for _, cluster := range clustersConfig {
if !isRBACLoded[cluster.Name] {
err := rbac.LoadRBAC(util.RBAC{}, cluster)
if err != nil {
return err
}
}
}
Expand Down Expand Up @@ -200,3 +222,18 @@ func LoadClusterConfig(path string) ([]*proxy.ClusterConfig, error) {
}
return clusterList, nil
}

func clusterConfigValidation(clusterConfig []*proxy.ClusterConfig) error {
// check if the cluster name is not empty and unique
clusterNames := make(map[string]bool)
for _, cluster := range clusterConfig {
if cluster.Name == "" {
return fmt.Errorf("cluster name is empty")
}
if _, ok := clusterNames[cluster.Name]; ok {
return fmt.Errorf("cluster name %s is repeated", cluster.Name)
}
clusterNames[cluster.Name] = true
}
return nil
}
2 changes: 2 additions & 0 deletions pkg/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/transport"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -59,6 +60,7 @@ type ClusterConfig struct {
Name string
Path string
RestConfig *rest.Config
Kubeclient *kubernetes.Clientset
proxyHandler *httputil.ReverseProxy
TokenReviewer *tokenreview.TokenReview
SubjectAccessReviewer *subjectaccessreview.SubjectAccessReview
Expand Down
149 changes: 149 additions & 0 deletions pkg/proxy/rbac/rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package rbac

import (
"context"
"fmt"

"github.com/Improwised/kube-oidc-proxy/pkg/proxy"
"github.com/Improwised/kube-oidc-proxy/pkg/util"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/rbac/v1"
apisv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
rbacvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
)

var defalutRole = map[string]v1.PolicyRule{
"devops": {
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
"developer": {
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"pods", "pods/log", "pods/exec"},
},
"developer-portforward": {
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"pods", "pods/log", "pods/exec", "pods/portforward"},
},
"watcher": {
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
}

func LoadRBAC(RBACConfig util.RBAC, cluster *proxy.ClusterConfig) error {
// Watch for namespace
// if namespace is created then create role and rolebinding
watchNamespace, err := cluster.Kubeclient.CoreV1().Namespaces().Watch(context.Background(), apisv1.ListOptions{Watch: true})
if err != nil {
return err
}

for roleName, role := range defalutRole {
// Create ClusterRole
RBACConfig.ClusterRoles = append(RBACConfig.ClusterRoles, &v1.ClusterRole{
TypeMeta: apisv1.TypeMeta{
Kind: "ClusterRole",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: apisv1.ObjectMeta{
Name: fmt.Sprintf("%s:%s", cluster.Name, roleName),
},
Rules: []v1.PolicyRule{role},
})
// Create ClusterRoleBinding
RBACConfig.ClusterRoleBindings = append(RBACConfig.ClusterRoleBindings, &v1.ClusterRoleBinding{
TypeMeta: apisv1.TypeMeta{
Kind: "ClusterRoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: apisv1.ObjectMeta{
Name: fmt.Sprintf("%s:%s", cluster.Name, roleName),
},
Subjects: []v1.Subject{
{
Kind: "Group",
Name: fmt.Sprintf("%s:%s", cluster.Name, roleName),
APIGroup: "rbac.authorization.k8s.io",
},
},
RoleRef: v1.RoleRef{
Kind: "ClusterRole",
Name: fmt.Sprintf("%s:%s", cluster.Name, roleName),
},
})
}

go func() {
for e := range watchNamespace.ResultChan() {
switch e.Type {
// If namespace is created then create role and rolebinding
case watch.Added:
for role, policy := range defalutRole {
// Create Role
RBACConfig.Roles = append(RBACConfig.Roles, &v1.Role{
TypeMeta: apisv1.TypeMeta{
Kind: "Role",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: apisv1.ObjectMeta{
Namespace: e.Object.(*corev1.Namespace).Name,
Name: fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name),
},
Rules: []v1.PolicyRule{policy},
})
// Create RoleBinding
RBACConfig.RoleBindings = append(RBACConfig.RoleBindings, &v1.RoleBinding{
TypeMeta: apisv1.TypeMeta{
Kind: "RoleBinding",
APIVersion: "rbac.authorization.k8s.io/v1",
},
ObjectMeta: apisv1.ObjectMeta{
Namespace: e.Object.(*corev1.Namespace).Name,
Name: fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name),
},
Subjects: []v1.Subject{
{
Kind: "Group",
Name: fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name),
APIGroup: "rbac.authorization.k8s.io",
},
},
RoleRef: v1.RoleRef{
Kind: "Role",
Name: fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name),
},
})
}
// If namespace is deleted then delete role and rolebinding
case watch.Deleted:
for role := range defalutRole {
// Delete Role
for i, r := range RBACConfig.Roles {
if r.Name == fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name) {
RBACConfig.Roles = append(RBACConfig.Roles[:i], RBACConfig.Roles[i+1:]...)
}
}
// Delete RoleBinding
for i, r := range RBACConfig.RoleBindings {
if r.Name == fmt.Sprintf("%s:%s:%s", cluster.Name, role, e.Object.(*corev1.Namespace).Name) {
RBACConfig.RoleBindings = append(RBACConfig.RoleBindings[:i], RBACConfig.RoleBindings[i+1:]...)
}
}
}
case watch.Modified:
continue

}
_, StaticRoles := rbacvalidation.NewTestRuleResolver(RBACConfig.Roles, RBACConfig.RoleBindings, RBACConfig.ClusterRoles, RBACConfig.ClusterRoleBindings)
cluster.Authorizer = util.NewAuthorizer(StaticRoles)

}
}()
return nil
}

0 comments on commit 10ef2d9

Please sign in to comment.