Skip to content

Commit b18b37a

Browse files
committed
fix(auth,platform): add authorization of rbac in inspectors
1 parent a1be94d commit b18b37a

File tree

5 files changed

+180
-7
lines changed

5 files changed

+180
-7
lines changed

cmd/tke-business-api/app/config/config.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import (
4444
"tkestack.io/tke/pkg/auth/filter"
4545
"tkestack.io/tke/pkg/business/apiserver"
4646
controllerconfig "tkestack.io/tke/pkg/controller/config"
47+
"tkestack.io/tke/pkg/util/log"
4748
)
4849

4950
const (
@@ -131,7 +132,11 @@ func CreateConfigFromOptions(serverName string, opts *options.Options) (*Config,
131132
if err != nil {
132133
return nil, err
133134
}
134-
clusterInspector := filter.NewClusterInspector(platformClient.PlatformV1(), opts.Authentication.PrivilegedUsername)
135+
clusterInspector, err := filter.NewClusterInspector(platformClient.PlatformV1(), opts.Authentication.PrivilegedUsername)
136+
if err != nil {
137+
log.Errorf("create clusterInspector failed: %+v", err)
138+
return nil, err
139+
}
135140
genericAPIServerConfig.BuildHandlerChainFunc = handler.BuildHandlerChain(nil, nil, []filter.Inspector{clusterInspector})
136141

137142
cfg := &Config{

cmd/tke-platform-api/app/config/config.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"tkestack.io/tke/pkg/apiserver/util"
4242
"tkestack.io/tke/pkg/auth/filter"
4343
"tkestack.io/tke/pkg/platform/apiserver"
44+
"tkestack.io/tke/pkg/util/log"
4445
)
4546

4647
const (
@@ -104,7 +105,11 @@ func CreateConfigFromOptions(serverName string, opts *options.Options) (*Config,
104105
if err != nil {
105106
return nil, fmt.Errorf("failed to create real external clientset: %v", err)
106107
}
107-
clusterInspector := filter.NewClusterInspector(clientgoExternalClient.PlatformV1(), opts.Authentication.PrivilegedUsername)
108+
clusterInspector, err := filter.NewClusterInspector(clientgoExternalClient.PlatformV1(), opts.Authentication.PrivilegedUsername)
109+
if err != nil {
110+
log.Errorf("create clusterInspector failed: %+v", err)
111+
return nil, err
112+
}
108113
genericAPIServerConfig.BuildHandlerChainFunc = handler.BuildHandlerChain(nil, nil, []filter.Inspector{clusterInspector})
109114
versionedInformers := versionedinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)
110115

pkg/apiserver/util/k8s.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making TKEStack available.
3+
*
4+
* Copyright (C) 2012-2021 Tencent. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
7+
* this file except in compliance with the License. You may obtain a copy of the
8+
* License at
9+
*
10+
* https://opensource.org/licenses/Apache-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations under the License.
16+
*/
17+
18+
package util
19+
20+
import (
21+
"k8s.io/client-go/kubernetes"
22+
"k8s.io/client-go/rest"
23+
)
24+
25+
func BuildKubeClient() (*kubernetes.Clientset, error) {
26+
config, err := rest.InClusterConfig()
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
clientset, err := kubernetes.NewForConfig(config)
32+
if err != nil {
33+
return nil, err
34+
}
35+
36+
return clientset, nil
37+
}

pkg/apiserver/util/signals.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making TKEStack available.
3+
*
4+
* Copyright (C) 2012-2021 Tencent. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
7+
* this file except in compliance with the License. You may obtain a copy of the
8+
* License at
9+
*
10+
* https://opensource.org/licenses/Apache-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations under the License.
16+
*/
17+
18+
package util
19+
20+
import (
21+
"os"
22+
"os/signal"
23+
"syscall"
24+
)
25+
26+
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
27+
// which is closed on one of these signals. If a second signal is caught, the program
28+
// is terminated with exit code 1.
29+
func SetupSignalHandler() (stopCh <-chan struct{}) {
30+
stop := make(chan struct{})
31+
c := make(chan os.Signal, 2)
32+
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
33+
go func() {
34+
<-c
35+
close(stop)
36+
<-c
37+
os.Exit(1) // second signal. Exit directly.
38+
}()
39+
40+
return stop
41+
}

pkg/auth/filter/inspector.go

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,125 @@ package filter
2020
import (
2121
"context"
2222
"fmt"
23-
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2423
"net/http"
24+
"strings"
25+
"time"
2526

2627
platformv1 "tkestack.io/tke/api/client/clientset/versioned/typed/platform/v1"
2728
"tkestack.io/tke/pkg/apiserver/authentication"
29+
"tkestack.io/tke/pkg/apiserver/util"
2830
"tkestack.io/tke/pkg/util/log"
2931

32+
rbacv1 "k8s.io/api/rbac/v1"
33+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
3034
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/apimachinery/pkg/labels"
3136
genericfilters "k8s.io/apiserver/pkg/endpoints/filters"
3237
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
3338
"k8s.io/apiserver/pkg/endpoints/request"
3439
genericapiserver "k8s.io/apiserver/pkg/server"
40+
"k8s.io/client-go/informers"
41+
"k8s.io/client-go/kubernetes"
42+
rbaclisters "k8s.io/client-go/listers/rbac/v1"
43+
"k8s.io/client-go/tools/cache"
3544
)
3645

3746
type Inspector interface {
3847
Inspect(handler http.Handler, c *genericapiserver.Config) http.Handler
3948
}
4049

4150
type clusterInspector struct {
51+
k8sClient kubernetes.Interface
52+
crbLister rbaclisters.ClusterRoleBindingLister
53+
crLister rbaclisters.ClusterRoleLister
4254
platformClient platformv1.PlatformV1Interface
4355
privilegedUsername string
4456
}
4557

46-
func NewClusterInspector(platformClient platformv1.PlatformV1Interface, privilegedUsername string) Inspector {
58+
func NewClusterInspector(platformClient platformv1.PlatformV1Interface, privilegedUsername string) (Inspector, error) {
59+
k8sClient, err := util.BuildKubeClient()
60+
if err != nil {
61+
return nil, err
62+
}
63+
informerFactory := informers.NewSharedInformerFactory(k8sClient, time.Minute)
64+
clusterRoleBindingInformer := informerFactory.Rbac().V1().ClusterRoleBindings()
65+
clusterRoleBindingLister := clusterRoleBindingInformer.Lister()
66+
clusterRoleInformer := informerFactory.Rbac().V1().ClusterRoles()
67+
clusterRoleLister := clusterRoleInformer.Lister()
68+
stopCh := util.SetupSignalHandler()
69+
informerFactory.Start(stopCh)
70+
if ok := cache.WaitForCacheSync(stopCh, clusterRoleBindingInformer.Informer().HasSynced,
71+
clusterRoleInformer.Informer().HasSynced)
72+
!ok {
73+
return nil, fmt.Errorf("failed to wait for namespaces caches to sync")
74+
}
4775
return &clusterInspector{
76+
k8sClient: k8sClient,
77+
crbLister: clusterRoleBindingLister,
78+
crLister: clusterRoleLister,
4879
platformClient: platformClient,
4980
privilegedUsername: privilegedUsername,
81+
}, nil
82+
}
83+
84+
func isClusterAdmin(rules []rbacv1.PolicyRule) bool {
85+
if len(rules) != 2 {
86+
return false
5087
}
88+
isAdmin := true
89+
for _, rul := range rules {
90+
if len(rul.APIGroups) == 1 && rul.APIGroups[0] == "*" &&
91+
len(rul.Resources) == 1 && rul.Resources[0] == "*" &&
92+
len(rul.Verbs) == 1 && rul.Verbs[0] == "*" {
93+
continue
94+
}
95+
if len(rul.NonResourceURLs) == 1 && rul.NonResourceURLs[0] == "*" &&
96+
len(rul.Verbs) == 1 && rul.Verbs[0] == "*" {
97+
continue
98+
}
99+
isAdmin = false
100+
break
101+
}
102+
return isAdmin
103+
}
104+
105+
func (i *clusterInspector) needInspect(ctx context.Context, privilegedUsername string) bool {
106+
username, tenantID := authentication.UsernameAndTenantID(ctx)
107+
if (username == privilegedUsername || username == "system:apiserver") && tenantID == "" {
108+
return false
109+
}
110+
111+
clusterRoleBindings, err := i.crbLister.List(labels.Everything())
112+
if err != nil {
113+
log.Errorf("query clusterRoleBindings failed: %+v", err)
114+
return true
115+
}
116+
username = strings.TrimPrefix(username, "system:serviceaccount:kube-system:")
117+
for _, crb := range clusterRoleBindings {
118+
for _, sub := range crb.Subjects {
119+
if sub.Name == username && sub.Namespace == "kube-system" {
120+
cr, err := i.crLister.Get(crb.RoleRef.Name)
121+
if err != nil {
122+
log.Errorf("query clusterRole: %+v failed: %+v", crb.RoleRef.Name, err)
123+
continue
124+
}
125+
if len(cr.Rules) != 2 {
126+
continue
127+
}
128+
log.Debugf("needInspect: username: %+v clusterRole: %+v->%v", username, cr.Name, cr.Rules)
129+
if isClusterAdmin(cr.Rules) {
130+
return false
131+
}
132+
}
133+
}
134+
}
135+
return true
51136
}
52137

53138
func (i *clusterInspector) Inspect(handler http.Handler, c *genericapiserver.Config) http.Handler {
54139
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
55140
ctx := req.Context()
56-
username, tenantID := authentication.UsernameAndTenantID(ctx)
57-
if (username == i.privilegedUsername || username == "system:apiserver") && tenantID == "" {
141+
if !i.needInspect(ctx, i.privilegedUsername) {
58142
handler.ServeHTTP(w, req)
59143
return
60144
}
@@ -77,7 +161,8 @@ func (i *clusterInspector) Inspect(handler http.Handler, c *genericapiserver.Con
77161
"invalid request: too many clusterName in request")
78162
return
79163
}
80-
log.Infof("WithTKEAuthorization clusterNames: %+v, username: %+v, tenant: %+v, "+
164+
username, tenantID := authentication.UsernameAndTenantID(ctx)
165+
log.Infof(" clusterNames: %+v, username: %+v, tenant: %+v, "+
81166
"action: %+v, resource: %+v, name: %+v",
82167
clusterNames, username, tenantID, tkeAttributes.GetVerb(),
83168
tkeAttributes.GetResource(), tkeAttributes.GetName())

0 commit comments

Comments
 (0)