Skip to content

Commit 615b042

Browse files
committed
Auto-grant cluster-admin permission for GKE clusters
1 parent a70d7e2 commit 615b042

File tree

1 file changed

+127
-4
lines changed

1 file changed

+127
-4
lines changed

cloud-control-manager/cloud-driver/drivers/gcp/resources/ClusterHandler.go

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ import (
1515
call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log"
1616
idrv "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces"
1717
irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources"
18+
o2 "golang.org/x/oauth2"
19+
goo "golang.org/x/oauth2/google"
1820
compute "google.golang.org/api/compute/v1"
1921
container "google.golang.org/api/container/v1"
20-
goo "golang.org/x/oauth2/google"
21-
o2 "golang.org/x/oauth2"
22+
rbacv1 "k8s.io/api/rbac/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/client-go/kubernetes"
25+
"k8s.io/client-go/tools/clientcmd"
2226
)
2327

2428
const (
@@ -278,6 +282,11 @@ func (ClusterHandler *GCPClusterHandler) CreateCluster(clusterReqInfo irs.Cluste
278282
return irs.ClusterInfo{}, err
279283
}
280284

285+
err = ClusterHandler.grantClusterAdminPermission(clusterInfo)
286+
if err != nil {
287+
cblogger.Warn(fmt.Sprintf("Failed to grant cluster-admin permission (non-critical): %v", err))
288+
}
289+
281290
return clusterInfo, nil
282291
}
283292

@@ -1587,7 +1596,7 @@ func (ClusterHandler *GCPClusterHandler) hasActiveOperations(projectID, zone, cl
15871596
// GCP uses OAuth2 access token for GKE cluster access
15881597
func (ClusterHandler *GCPClusterHandler) GenerateClusterToken(clusterIID irs.IID) (string, error) {
15891598
cblogger.Info("call GenerateClusterToken()")
1590-
1599+
15911600
// Create JWT config from credential info
15921601
gcpType := "service_account"
15931602
data := make(map[string]string)
@@ -1616,6 +1625,120 @@ func (ClusterHandler *GCPClusterHandler) GenerateClusterToken(clusterIID irs.IID
16161625
cblogger.Errorf("Failed to get access token: %v", err)
16171626
return "", fmt.Errorf("failed to get access token: %w", err)
16181627
}
1619-
1628+
16201629
return token.AccessToken, nil
16211630
}
1631+
1632+
func (ClusterHandler *GCPClusterHandler) createK8sClientFromKubeconfig(kubeconfigContent string) (*kubernetes.Clientset, error) {
1633+
config, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfigContent))
1634+
if err != nil {
1635+
return nil, fmt.Errorf("failed to create REST config from kubeconfig: %w", err)
1636+
}
1637+
1638+
token, err := ClusterHandler.GenerateClusterToken(irs.IID{})
1639+
if err != nil {
1640+
return nil, fmt.Errorf("failed to generate cluster token: %w", err)
1641+
}
1642+
config.BearerToken = token
1643+
1644+
clientset, err := kubernetes.NewForConfig(config)
1645+
if err != nil {
1646+
return nil, fmt.Errorf("failed to create Kubernetes clientset: %w", err)
1647+
}
1648+
1649+
return clientset, nil
1650+
}
1651+
1652+
func (ClusterHandler *GCPClusterHandler) getServiceAccountUserID() (string, error) {
1653+
gcpType := "service_account"
1654+
data := make(map[string]string)
1655+
data["type"] = gcpType
1656+
data["private_key"] = ClusterHandler.Credential.PrivateKey
1657+
data["client_email"] = ClusterHandler.Credential.ClientEmail
1658+
1659+
res, err := json.Marshal(data)
1660+
if err != nil {
1661+
return "", fmt.Errorf("failed to marshal credential data: %w", err)
1662+
}
1663+
1664+
authURL := "https://www.googleapis.com/auth/cloud-platform"
1665+
conf, err := goo.JWTConfigFromJSON(res, authURL)
1666+
if err != nil {
1667+
return "", fmt.Errorf("failed to create JWT config: %w", err)
1668+
}
1669+
1670+
tokenSource := conf.TokenSource(o2.NoContext)
1671+
token, err := tokenSource.Token()
1672+
if err != nil {
1673+
return "", fmt.Errorf("failed to get access token: %w", err)
1674+
}
1675+
1676+
if token.Extra("id_token") != nil {
1677+
if idToken, ok := token.Extra("id_token").(string); ok {
1678+
parts := strings.Split(idToken, ".")
1679+
if len(parts) > 1 {
1680+
return parts[1], nil
1681+
}
1682+
}
1683+
}
1684+
1685+
return ClusterHandler.Credential.ClientEmail, nil
1686+
}
1687+
1688+
func (ClusterHandler *GCPClusterHandler) grantClusterAdminPermission(clusterInfo irs.ClusterInfo) error {
1689+
kubeconfigContent := clusterInfo.AccessInfo.Kubeconfig
1690+
if kubeconfigContent == "" {
1691+
return fmt.Errorf("kubeconfig is empty")
1692+
}
1693+
1694+
clientset, err := ClusterHandler.createK8sClientFromKubeconfig(kubeconfigContent)
1695+
if err != nil {
1696+
return fmt.Errorf("failed to create Kubernetes client: %w", err)
1697+
}
1698+
1699+
userName, err := ClusterHandler.getServiceAccountUserID()
1700+
if err != nil {
1701+
return fmt.Errorf("failed to get service account user ID: %w", err)
1702+
}
1703+
1704+
crbName := "cb-spider-admin"
1705+
1706+
existingCRB, err := clientset.RbacV1().ClusterRoleBindings().Get(
1707+
context.TODO(),
1708+
crbName,
1709+
metav1.GetOptions{},
1710+
)
1711+
if err == nil && existingCRB != nil {
1712+
cblogger.Info(fmt.Sprintf("ClusterRoleBinding '%s' already exists, skipping creation", crbName))
1713+
return nil
1714+
}
1715+
1716+
crb := &rbacv1.ClusterRoleBinding{
1717+
ObjectMeta: metav1.ObjectMeta{
1718+
Name: crbName,
1719+
},
1720+
Subjects: []rbacv1.Subject{
1721+
{
1722+
Kind: "User",
1723+
Name: userName,
1724+
},
1725+
},
1726+
RoleRef: rbacv1.RoleRef{
1727+
Kind: "ClusterRole",
1728+
Name: "cluster-admin",
1729+
APIGroup: "rbac.authorization.k8s.io",
1730+
},
1731+
}
1732+
1733+
_, err = clientset.RbacV1().ClusterRoleBindings().Create(
1734+
context.TODO(),
1735+
crb,
1736+
metav1.CreateOptions{},
1737+
)
1738+
if err != nil {
1739+
return fmt.Errorf("failed to create ClusterRoleBinding: %w", err)
1740+
}
1741+
1742+
cblogger.Info(fmt.Sprintf("Successfully created ClusterRoleBinding '%s' for user '%s'", crbName, userName))
1743+
return nil
1744+
}

0 commit comments

Comments
 (0)