kubelatte-ce
Форк от sbertech/kubelatte-ce
296 строк · 7.7 Кб
1package permission
2
3import (
4"fmt"
5"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/observability/logger"
6"gitverse.ru/ktrntrsv/kubelatte-ce/pkg/util/env"
7"golang.org/x/net/context"
8v1 "k8s.io/api/authorization/v1"
9metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10kclientset "k8s.io/client-go/kubernetes"
11"sync"
12"time"
13)
14
15type PermissionerI interface {
16Init(client kclientset.Interface, operatorNamespace string, mutator, validator, creator, sideeffect bool) PermissionerI
17DeleteNamespaceTi(ns string)
18AddNamespaceTi(ns string)
19GetPermissions() []Permission
20StartCheck()
21}
22
23var PermissionsSet bool
24
25var instance PermissionerI
26
27type Manager struct {
28cancelCheck bool
29startOnly bool
30logOnly bool
31checkPeriod time.Duration
32ticker *time.Ticker
33permissions []Permission
34client kclientset.Interface
35rwMutex *sync.Mutex
36operatorNamespace string
37tiNamespaces map[string][]Permission
38isCreator bool
39isMutator bool
40isSideEffect bool
41isValidator bool
42}
43
44type Permission struct {
45Resources []string
46Verbs []string
47Group string
48Version string
49Namespace string
50Required bool
51}
52
53func NewManager() PermissionerI {
54instance = &Manager{
55rwMutex: &sync.Mutex{},
56tiNamespaces: make(map[string][]Permission),
57}
58return instance
59}
60
61func (m *Manager) Init(client kclientset.Interface, operatorNamespace string, mutator, validator, creator, sideeffect bool) PermissionerI {
62if !env.KbltPermissionsCheck {
63PermissionsSet = true
64m.cancelCheck = true
65return m
66}
67
68m.checkPeriod = env.KbltPermissionsCheckPeriod
69m.logOnly = env.KbltPermissionsLogOnly
70m.startOnly = env.KbltPermissionsStartOnly
71
72m.client = client
73m.isCreator = creator
74m.isMutator = mutator
75m.isValidator = validator
76m.isSideEffect = sideeffect
77m.operatorNamespace = operatorNamespace
78m.permissions = m.GetPermissions()
79return m
80}
81
82func (m *Manager) StartCheck() {
83if m == nil || m.cancelCheck {
84return
85}
86
87m.checkPermsForFirstTime(context.Background())
88if m.startOnly {
89return
90}
91
92go m.startPermissionWatcher(context.Background())
93}
94
95func (m *Manager) checkPermsForFirstTime(ctx context.Context) {
96for {
97err := m.checkPermissionsAndSetStatus(ctx)
98if err == nil {
99break
100}
101time.Sleep(m.checkPeriod)
102}
103}
104
105func (m *Manager) checkPermissionsAndSetStatus(ctx context.Context) error {
106err := m.checkPermissions(ctx)
107PermissionsSet = m.logOnly || err == nil
108if m.logOnly {
109return nil
110}
111return err
112}
113
114func (m *Manager) startPermissionWatcher(ctx context.Context) {
115m.ticker = time.NewTicker(m.checkPeriod)
116_ = m.checkPermissionsAndSetStatus(ctx)
117for {
118select {
119case <-m.ticker.C:
120_ = m.checkPermissionsAndSetStatus(ctx)
121case <-ctx.Done():
122m.stop(ctx)
123return
124}
125}
126}
127
128func (m *Manager) stop(ctx context.Context) {
129if m.ticker != nil {
130m.ticker.Stop()
131}
132ctx.Done()
133}
134
135func (m *Manager) checkPermissions(ctx context.Context) error {
136log := logger.FromContext(ctx)
137var cnt int
138for _, perm := range m.permissions {
139cnt += len(perm.Verbs) * len(perm.Resources)
140}
141errChan := make(chan error, cnt)
142wg := &sync.WaitGroup{}
143
144for _, permission := range m.permissions {
145for _, verb := range permission.Verbs {
146for _, resource := range permission.Resources {
147wg.Add(1)
148time.Sleep(time.Millisecond * 100) // do not dos kuber api
149go func(verb, resource, group, version, namespace string, required bool, waitGroup *sync.WaitGroup, errChan chan error) {
150defer waitGroup.Done()
151
152rv := &v1.SelfSubjectAccessReview{
153Spec: v1.SelfSubjectAccessReviewSpec{
154ResourceAttributes: &v1.ResourceAttributes{
155Namespace: namespace,
156Verb: verb,
157Group: group,
158Version: version,
159Resource: resource,
160},
161},
162}
163resp, err := m.client.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, rv, metav1.CreateOptions{})
164if err != nil {
165errChan <- err
166log.Errorf("cant check permissions: %s", err.Error())
167return
168}
169if !resp.Status.Allowed {
170if !required {
171log.Warnf("[Permission Manager] not allowed: %s %s %s", verb, resource, resp.Status.Reason)
172return
173}
174errChan <- fmt.Errorf("not allowed")
175log.Errorf("[Permission Manager] not allowed %s %s %s %s", verb, resource, resp.Status.Reason, resp.Status.EvaluationError)
176return
177}
178}(verb, resource, permission.Group, permission.Version, permission.Namespace, permission.Required, wg, errChan)
179}
180}
181}
182wg.Wait()
183close(errChan)
184if err := <-errChan; err != nil {
185return fmt.Errorf("[Init Manager] Permissions error")
186}
187
188return nil
189}
190
191func (m *Manager) GetPermissions() []Permission {
192var requestedPermissions []Permission
193
194var namespaces = Permission{
195Verbs: []string{"get", "list", "watch"},
196Group: "",
197Resources: []string{"namespaces"},
198Namespace: "",
199Required: true,
200}
201var leases = Permission{
202Verbs: []string{"get", "list", "watch", "create", "update", "watch", "delete", "patch"},
203Group: "coordination.k8s.io",
204Resources: []string{"leases"},
205Namespace: m.operatorNamespace,
206Required: true,
207}
208latteStatuses := make([]string, 0)
209latteRes := make([]string, 0)
210if m.isValidator || m.isMutator {
211latteStatuses = append(latteStatuses, "scopes/status", "templates/status")
212latteRes = append(latteRes, "scopes", "templates")
213}
214if m.isMutator {
215latteStatuses = append(latteStatuses, "triggers/status")
216latteRes = append(latteRes, "triggers")
217}
218
219var lateCrdPem = Permission{
220Verbs: []string{"get", "list", "watch"},
221Group: "kubelatte.synapse.sber",
222Resources: latteRes,
223Namespace: m.operatorNamespace,
224Required: true,
225}
226var statuses = Permission{
227Verbs: []string{"get", "list", "watch", "create", "update", "watch", "delete", "patch"},
228Group: "kubelatte.synapse.sber",
229Resources: latteStatuses,
230Namespace: m.operatorNamespace,
231Required: true,
232}
233
234requestedPermissions = append(requestedPermissions, statuses, leases, lateCrdPem, namespaces)
235return requestedPermissions
236}
237
238func (m *Manager) AddNamespaceTi(ns string) {
239if !m.isCreator {
240return
241}
242m.rwMutex.Lock()
243defer m.rwMutex.Unlock()
244log := logger.FromContext(context.Background())
245if _, ok := m.tiNamespaces[ns]; ok {
246return
247}
248log.Debugf("[Permission Manager] start check TI permissions in ns: %v", ns)
249perms := make([]Permission, 0, 3)
250perms = append(perms,
251Permission{
252Verbs: []string{"get", "list", "watch"},
253Group: "kubelatte.synapse.sber",
254Resources: []string{"triggerinstances"},
255Namespace: ns,
256Required: false,
257},
258Permission{
259Verbs: []string{"get", "list", "watch", "create", "update", "watch", "delete", "patch"},
260Group: "kubelatte.synapse.sber",
261Resources: []string{"triggerinstances/status"},
262Namespace: ns,
263Required: false,
264})
265if m.isSideEffect {
266perms = append(perms, Permission{
267Verbs: []string{"create"},
268Group: "kubelatte.synapse.sber",
269Resources: []string{"triggerinstances"},
270Namespace: ns,
271Required: false,
272})
273}
274m.tiNamespaces[ns] = perms
275m.permissions = append(m.permissions, perms...)
276}
277
278func (m *Manager) DeleteNamespaceTi(ns string) {
279m.rwMutex.Lock()
280defer m.rwMutex.Unlock()
281log := logger.FromContext(context.Background())
282if _, ok := m.tiNamespaces[ns]; !ok {
283return
284}
285log.Debugf("[Permission Manager] stopped check TI permissions in ns: %v ", ns)
286for _, tiPermission := range m.tiNamespaces[ns] {
287for idx, perm := range m.permissions {
288if perm.Namespace == tiPermission.Namespace && perm.Resources[0] == tiPermission.Resources[0] {
289m.permissions[idx] = m.permissions[len(m.permissions)-1]
290m.permissions = m.permissions[:len(m.permissions)-1]
291}
292}
293}
294delete(m.tiNamespaces, ns)
295
296}
297