Skip to content

Commit

Permalink
Support special rolebindings
Browse files Browse the repository at this point in the history
Without this patch, the rolebindings for appops, customerops,
... are only using the old names, not the LDAP groups.

This is a problem, as it will prevent us to clean up the rest
of the code later.

This fixes it by relying on the config of the group base to
know which group to add in the rolebinding.

In the future, it would be best to template those from a
config map: it would help to not being directly interconnected
to LDAP by having a simple config to template for the cluster.
It would also be simpler to manage with a gitops tool.
  • Loading branch information
evrardjp-cagip committed Jan 13, 2025
1 parent 9dd54a4 commit acbd644
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 2 deletions.
45 changes: 43 additions & 2 deletions internal/services/rolebindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log/slog"
"strings"

"github.com/ca-gip/kubi/internal/utils"
cagipv1 "github.com/ca-gip/kubi/pkg/apis/cagip/v1"
Expand All @@ -24,13 +25,19 @@ var RoleBindingsCreation = promauto.NewCounterVec(prometheus.CounterOpts{

// generateRoleBinding is convenience function for readability, returning a properly formatted rolebinding object.
func newRoleBinding(name string, namespace string, clusterRole string, subjects []v1.Subject) *v1.RoleBinding {
var validSubjects []v1.Subject
for _, subject := range subjects {
if subject.Name != "" {
validSubjects = append(validSubjects, subject)
}
}
return &v1.RoleBinding{
RoleRef: v1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: clusterRole,
},
Subjects: subjects,
Subjects: validSubjects,
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Expand Down Expand Up @@ -102,7 +109,22 @@ func generateRoleBindings(project *cagipv1.Project, defaultServiceAccountRole st
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Group",
Name: project.Spec.SourceEntity,
Name: project.Spec.SourceEntity, // the equivalent of $namespace-admin
},
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Group",
Name: toSubject(utils.Config.Ldap.AppMasterGroupBase), // the equivalent of application master (appops)
},
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Group",
Name: toSubject(utils.Config.Ldap.CustomerOpsGroupBase), // the equivalent of application master (customerops)
},
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Group",
Name: toSubject(utils.Config.Ldap.OpsMasterGroupBase), // the equivalent of ops master
},
},
},
Expand All @@ -115,6 +137,11 @@ func generateRoleBindings(project *cagipv1.Project, defaultServiceAccountRole st
Kind: "Group",
Name: utils.ApplicationViewer,
},
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Group",
Name: toSubject(utils.Config.Ldap.ViewerGroupBase),
},
},
},
{
Expand Down Expand Up @@ -152,3 +179,17 @@ func generateRoleBindings(project *cagipv1.Project, defaultServiceAccountRole st
}
}
}

// Quick and hacky way to parse DN from config, without having to load an ldap parser or doing any query
// if not valid, return an empty string, which does not get applied in the list of subjects, as:
// 1. it's not valid to not have a name
// 2. We check whether we have a name in the generation of the rolebinding object.
func toSubject(DN string) string {
parts := strings.Split(DN, ",")
for _, part := range parts {
if strings.HasPrefix(part, "CN=") {
return strings.TrimPrefix(part, "CN=")
}
}
return ""
}
36 changes: 36 additions & 0 deletions internal/services/rolebindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,39 @@ func TestNewRoleBinding(t *testing.T) {
})
}
}
func TestToSubject(t *testing.T) {
tests := []struct {
DN string
expected string
}{
{
DN: "CN=test-user,OU=Users,DC=example,DC=com",
expected: "test-user",
},
{
DN: "OU=Users,CN=test-user,DC=example,DC=com",
expected: "test-user",
},
{
DN: "OU=Users,DC=example,DC=com",
expected: "",
},
{
DN: "CN=test-user",
expected: "test-user",
},
{
DN: "",
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.DN, func(t *testing.T) {
result := toSubject(tt.DN)
if result != tt.expected {
t.Errorf("toSubject(%v) = %v, expected %v", tt.DN, result, tt.expected)
}
})
}
}

0 comments on commit acbd644

Please sign in to comment.