Skip to content

Commit

Permalink
rbac: support cleanup one cluster bindings for all deleted users
Browse files Browse the repository at this point in the history
  • Loading branch information
mozillazg committed Nov 28, 2023
1 parent 1eaf0b9 commit 599de60
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 48 deletions.
12 changes: 12 additions & 0 deletions pkg/ctl/common/cli.go
@@ -1,7 +1,10 @@
package common

import (
"context"
"os"
"os/signal"
"syscall"

"github.com/AlecAivazis/survey/v2"
"github.com/AliyunContainerService/ack-ram-tool/pkg/ctl"
Expand Down Expand Up @@ -33,3 +36,12 @@ func SetupClusterIdFlag(cmd *cobra.Command) {
&ctl.GlobalOption.FinalAssumeRoleAnotherRoleArn, "role-arn", "",
"Assume an RAM Role ARN when send request or sign token")
}

func SetupSignalHandler(parentCtx context.Context) context.Context {
ctx, cancel := signal.NotifyContext(parentCtx, os.Interrupt, syscall.SIGTERM)
go func() {
<-ctx.Done()
cancel()
}()
return ctx
}
83 changes: 66 additions & 17 deletions pkg/ctl/rbac/README.zh-cn.md
Expand Up @@ -20,7 +20,12 @@
"Statement": [
{
"Effect": "Allow",
"Action": "cs:DescribeClusterUserKubeconfig",
"Action": [
"cs:DescribeClusterUserKubeconfig",
"cs:DescribeClustersV1",
"cs:GetClusters",
"cs:DescribeClusterDetail"
],
"Resource": "*"
},
{
Expand All @@ -44,18 +49,42 @@

## scan-user-permissions

扫描指定集群中存在的 RAM 用户和角色的 RBAC bindings.
* 扫描指定集群中存在的 RAM 用户和角色的 RBAC bindings.

```shell

# 默认只输出已删除 RAM 用户和角色
$ ack-ram-tool rbac scan-user-permissions -c <集群ID>

UID UserType UserName Binding
2432******** (deleted) RamUser ClusterRoleBinding/-/24*****-clusterrolebinding


# 可以通过 -A 显示所有用户和角色
$ ack-ram-tool rbac scan-user-permissions -A -c <集群ID>

UID UserType UserName Binding
2432******** (deleted) RamUser ClusterRoleBinding/-/24*****-clusterrolebinding
2342******** RamUser foobar ClusterRoleBinding/-/23*****-clusterrolebinding

```

* 扫描所有集群中存在的 RAM 用户和角色的 RBAC bindings.

```shell

# 默认只输出已删除 RAM 用户和角色
$ ack-ram-tool rbac scan-user-permissions -c all

ClusterId: cbbXXX
UID UserType UserName Binding
2432******** (deleted) RamUser ClusterRoleBinding/-/24*****-clusterrolebinding


# 可以通过 -A 显示所有用户和角色
$ ack-ram-tool rbac scan-user-permissions -A -c all

ClusterId: cbbXXX
UID UserType UserName Binding
2432******** (deleted) RamUser ClusterRoleBinding/-/24*****-clusterrolebinding
2342******** RamUser foobar ClusterRoleBinding/-/23*****-clusterrolebinding
Expand All @@ -64,28 +93,48 @@ UID UserType UserName Binding

## cleanup-user-permissions

清理指定集群中指定的 RAM 用户和角色的 RBAC bindings.
* 清理指定集群中指定的 RAM 用户和角色的 RBAC bindings:

```shell

# <用户ID> 可以从上面的 scan 命令的结果中获取
$ ack-ram-tool rbac cleanup-user-permissions -c <集群ID> -u <用户ID>

Start to scan users and bindings
Will cleanup RBAC bindings as blow:
UID UserType UserName Binding
300******************** (deleted) RamRole RoleBinding/kube-system/300********************-heapster-rolebinding
300******************** (deleted) RamRole RoleBinding/arms-prom/300********************-arms-prom-rolebinding
300******************** (deleted) RamRole RoleBinding/default/300********************-default-rolebinding
300******************** (deleted) RamRole ClusterRoleBinding/-/300********************-clusterrolebinding
Will cleanup RBAC bindings as below:
UID UserType UserName Binding
300*** (deleted) RamRole RoleBinding/kube-system/300****-heapster-rolebinding
300*** (deleted) RamRole RoleBinding/arms-prom/300****-arms-prom-rolebinding
300*** (deleted) RamRole RoleBinding/default/300****-default-rolebinding
300*** (deleted) RamRole ClusterRoleBinding/-/300***-clusterrolebinding
? Are you sure you want to cleanup these bindings? Yes
start to cleanup binding: RoleBinding/kube-system/300********************-heapster-rolebinding
finished cleanup binding: RoleBinding/kube-system/300********************-heapster-rolebinding
start to cleanup binding: RoleBinding/arms-prom/300********************-arms-prom-rolebinding
finished cleanup binding: RoleBinding/arms-prom/300********************-arms-prom-rolebinding
start to cleanup binding: RoleBinding/default/300********************-default-rolebinding
finished cleanup binding: RoleBinding/default/300********************-default-rolebinding
start to cleanup binding: ClusterRoleBinding/-/300********************-clusterrolebinding
finished cleanup binding: ClusterRoleBinding/-/300********************-clusterrolebinding
start to cleanup binding: RoleBinding/kube-system/300***-heapster-rolebinding
finished cleanup binding: RoleBinding/kube-system/300***-heapster-rolebinding
start to cleanup binding: RoleBinding/arms-prom/300***-arms-prom-rolebinding
finished cleanup binding: RoleBinding/arms-prom/300***-arms-prom-rolebinding
start to cleanup binding: RoleBinding/default/300***-default-rolebinding
finished cleanup binding: RoleBinding/default/300***-default-rolebinding
start to cleanup binding: ClusterRoleBinding/-/300***-clusterrolebinding
finished cleanup binding: ClusterRoleBinding/-/300***-clusterrolebinding
all bindings have been cleanup

```


* 清理指定集群中所有已删除的 RAM 用户和角色的 RBAC bindings:

```shell

$ ack-ram-tool rbac cleanup-user-permissions -c <集群ID> --all-deleted-users

Start to scan users and bindings
Will cleanup RBAC bindings as below:
UID UserType UserName Binding
300*** (deleted) RamRole ClusterRoleBinding/-/300***-clusterrolebinding
? Are you sure you want to cleanup these bindings? Yes
start to backup binding: ClusterRoleBinding/-/300***-clusterrolebinding
the origin binding ClusterRoleBinding/-/300***-clusterrolebinding have been backed up to file cbbXXX/ClusterRoleBinding--300***-clusterrolebinding.json
start to delete binding: ClusterRoleBinding/-/300***-clusterrolebinding
deleted binding: ClusterRoleBinding/-/300***-clusterrolebinding

all bindings have been cleanup
10 changes: 10 additions & 0 deletions pkg/ctl/rbac/binding/binding.go
Expand Up @@ -256,11 +256,21 @@ func RemoveBinding(ctx context.Context, b Binding, client kubernetes.Interface)

func getClusterRoleBinding(ctx context.Context, b Binding, client kubernetes.Interface) (*rbacv1.ClusterRoleBinding, error) {
obj, err := client.RbacV1().ClusterRoleBindings().Get(ctx, b.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
obj.APIVersion = "rbac.authorization.k8s.io/v1"
obj.Kind = "ClusterRoleBinding"
return obj, err
}

func getRoleBinding(ctx context.Context, b Binding, client kubernetes.Interface) (*rbacv1.RoleBinding, error) {
obj, err := client.RbacV1().RoleBindings(b.Namespace).Get(ctx, b.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
obj.APIVersion = "rbac.authorization.k8s.io/v1"
obj.Kind = "RoleBinding"
return obj, err
}

Expand Down
97 changes: 68 additions & 29 deletions pkg/ctl/rbac/cleanupuserpermissions/cmd.go
Expand Up @@ -2,6 +2,7 @@ package cleanupuserpermissions

import (
"context"
"fmt"
"github.com/AliyunContainerService/ack-ram-tool/pkg/ctl/rbac/scanuserpermissions"
"github.com/AliyunContainerService/ack-ram-tool/pkg/log"
"github.com/briandowns/spinner"
Expand All @@ -22,7 +23,7 @@ type Option struct {
privateIpAddress bool
temporaryDuration time.Duration
//outputFormat OutputFormat
allUsers bool
allDeletedUsers bool
}

var opts = Option{
Expand All @@ -31,38 +32,70 @@ var opts = Option{

var cmd = &cobra.Command{
Use: "cleanup-user-permissions",
Short: "cleanup RBAC permissions for one user",
Long: ``,
Short: "cleanup RBAC permissions for one user or all deleted users",
Long: `cleanup RBAC permissions for one user or all deleted users
Examples:
# cleanup RBAC permissions for one cluster and one user
ack-ram-tool rbac cleanup-user-permissions -c <clusterId> -u <uid>
# cleanup RBAC permissions for one cluster and all deleted users
ack-ram-tool rbac cleanup-user-permissions -c <clusterId> --all-deleted-users
`,
Run: func(cmd *cobra.Command, args []string) {
run()
if !opts.allDeletedUsers && opts.userId == 0 {
cmd.Help()
log.Logger.Error("flag -u/--user-id/--all-deleted-users not set")
return
}

ctx := ctlcommon.SetupSignalHandler(context.Background())
run(ctx)
},
}

func run() {
ctx := context.Background()
func run(ctx context.Context) {
openAPIClient := ctlcommon.GetClientOrDie()

oneCluster(ctx, openAPIClient, opts.clusterId)
if err := cleanOneCluster(ctx, openAPIClient, opts.clusterId); err != nil {
ctlcommon.ExitIfError(err)
}
}

func oneCluster(ctx context.Context, openAPIClient openapi.ClientInterface, clusterId string) {
func cleanOneCluster(ctx context.Context, openAPIClient openapi.ClientInterface, clusterId string) error {
log.Logger.Info("Start to scan users and bindings")
spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
spin.Start()

kubeClient := getKubeClient(ctx, openAPIClient, clusterId)
rawBindings, err := binding.ListBindings(ctx, kubeClient)
ctlcommon.ExitIfError(err)
accounts, err := binding.ListAccounts(ctx, openAPIClient)
ctlcommon.ExitIfError(err)
spin.Stop()
var kubeClient kubernetes.Interface
var accounts map[int64]types.Account
var bindings []binding.Binding
var err error

func() {
defer spin.Stop()
kubeClient, err = getKubeClient(ctx, openAPIClient, clusterId)
if err != nil {
return
}
accounts, err = binding.ListAccounts(ctx, openAPIClient)
if err != nil {
return
}
bindings, err = scanuserpermissions.GetClusterBindings(ctx, kubeClient)
if err != nil {
return
}
}()
if err != nil {
return err
}

bindings := rawBindings.SortByUid()
cleanup(ctx, bindings, accounts, kubeClient, clusterId)
return cleanupOneCluster(ctx, bindings, accounts, kubeClient, clusterId)
}

func cleanup(ctx context.Context, bindings []binding.Binding,
accounts map[int64]types.Account, kube kubernetes.Interface, clusterId string) {
func cleanupOneCluster(ctx context.Context, bindings []binding.Binding,
accounts map[int64]types.Account, kube kubernetes.Interface, clusterId string) error {
var newBindings []binding.Binding
for _, b := range bindings {
if b.AliUid == 0 {
Expand All @@ -77,44 +110,50 @@ func cleanup(ctx context.Context, bindings []binding.Binding,
acc.MarkDeleted()
accounts[b.AliUid] = acc
}
if opts.allDeletedUsers && !acc.Deleted() {
continue
}
newBindings = append(newBindings, b)
}

log.Logger.Info("will cleanup RBAC bindings as blow:")
log.Logger.Info("will cleanup RBAC bindings as below:")
scanuserpermissions.OutputBindingsTable(newBindings, accounts, false)

ctlcommon.YesOrExit("Are you sure you want to cleanup these bindings?")

for _, b := range newBindings {
log.Logger.Infof("start to backup binding: %s", b.String())
if p, err := binding.SaveBindingToFile(ctx, clusterId, b, kube); err != nil {
ctlcommon.ExitIfError(err)
return fmt.Errorf("backup binding %s: %w", b.String(), err)
} else {
log.Logger.Infof("the origin binding %s have been backed up to file %s", b.String(), p)
}
log.Logger.Infof("start to cleanup binding: %s", b.String())
log.Logger.Infof("start to delete binding: %s", b.String())
if err := binding.RemoveBinding(ctx, b, kube); err != nil {
ctlcommon.ExitIfError(err)
return fmt.Errorf("delete binding %s: %w", b.String(), err)
}
log.Logger.Infof("finished cleanup binding: %s", b.String())
log.Logger.Infof("deleted binding: %s", b.String())
}
log.Logger.Info("all bindings have been cleanup")
return nil
}

func getKubeClient(ctx context.Context, openAPIClient openapi.ClientInterface, clusterId string) kubernetes.Interface {
func getKubeClient(ctx context.Context, openAPIClient openapi.ClientInterface,
clusterId string) (kubernetes.Interface, error) {
kubeconfig, err := openAPIClient.GetUserKubeConfig(ctx, clusterId,
opts.privateIpAddress, opts.temporaryDuration)
ctlcommon.ExitIfError(err)
if err != nil {
return nil, fmt.Errorf("get kubeconfig: %w", err)
}

client, err := ctlcommon.NewKubeClient(kubeconfig.RawData)
ctlcommon.ExitIfError(err)
return client
return client, err
}

func SetupCmd(rootCmd *cobra.Command) {
rootCmd.AddCommand(cmd)
cmd.Flags().Uint64VarP(&opts.userId, "user-id", "u", 0, "limit user id")
cmd.Flags().StringVarP(&opts.clusterId, "cluster-id", "c", "", "cluster id")
//cmd.Flags().BoolVarP(&opts.allUsers, "all-users", "A", false, "list all users")
cmd.Flags().BoolVar(&opts.allDeletedUsers, "all-deleted-users", false, "cleanup all deleted users")
ctlcommon.ExitIfError(cmd.MarkFlagRequired("cluster-id"))
ctlcommon.ExitIfError(cmd.MarkFlagRequired("user-id"))
}
3 changes: 1 addition & 2 deletions pkg/ctl/rbac/scanuserpermissions/cmd.go
Expand Up @@ -38,8 +38,7 @@ var opts = Option{
var cmd = &cobra.Command{
Use: "scan-user-permissions",
Short: "scan RBAC permissions for one or all users",
Long: `
scan RBAC permissions for one or all users
Long: `scan RBAC permissions for one or all users
Examples:
# list all deleted users/roles for one cluster
Expand Down

0 comments on commit 599de60

Please sign in to comment.