Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tenant sidecar for ks v4.0 #231

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -2282,6 +2286,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -1413,6 +1417,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
11 changes: 7 additions & 4 deletions controllers/notificationmanager_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,17 @@ func (r *NotificationManagerReconciler) mutateDeployment(deploy *appsv1.Deployme
nm.Spec.ServiceAccountName = defaultServiceAccountName
}

deploy.ObjectMeta.Labels = *r.makeCommonLabels(nm)
deploy.Spec.Replicas = nm.Spec.Replicas
podLabels := deploy.ObjectMeta.Labels
podLabels := *r.makeCommonLabels(nm)
for k, v := range nm.Spec.Labels {
podLabels[k] = v
}
deploy.Spec.Selector = &metav1.LabelSelector{
MatchLabels: podLabels,
MatchLabels: *r.makeCommonLabels(nm),
}
deploy.Spec.Template.ObjectMeta = metav1.ObjectMeta{
Labels: podLabels,
Labels: podLabels,
Annotations: nm.Spec.Annotations,
}
deploy.Spec.Template.Spec.NodeSelector = nm.Spec.NodeSelector
deploy.Spec.Template.Spec.Affinity = nm.Spec.Affinity
Expand Down
8 changes: 8 additions & 0 deletions helm/crds/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1735,6 +1735,10 @@ spec:
type: array
type: object
type: object
annotations:
additionalProperties:
type: string
type: object
args:
description: Arguments to the entrypoint. The docker image's CMD is
used if this is not provided.
Expand Down Expand Up @@ -2282,6 +2286,10 @@ spec:
description: Image pull policy. One of Always, Never, IfNotPresent.
Defaults to IfNotPresent if not specified
type: string
labels:
additionalProperties:
type: string
type: object
nodeSelector:
additionalProperties:
type: string
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/v2beta2/notificationmanager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ type NotificationManagerSpec struct {
RoutePolicy string `json:"routePolicy,omitempty"`
// Template used to define information about templates
Template *Template `json:"template,omitempty"`

Annotations map[string]string `json:"annotations,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}

type ReceiversSpec struct {
Expand Down
15 changes: 15 additions & 0 deletions pkg/apis/v2beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions sidecar/kubesphere/4.0.0/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.

FROM golang:1.20 as tenant-sidecar

COPY / /
WORKDIR /
ENV GOPROXY=https://goproxy.io
RUN CGO_ENABLED=0 GO111MODULE=on go build -a -o tenant-sidecar main.go backend.go

FROM kubesphere/distroless-static:nonroot
WORKDIR /
COPY --from=tenant-sidecar /tenant-sidecar .
USER nonroot:nonroot

ENTRYPOINT ["/tenant-sidecar"]

24 changes: 24 additions & 0 deletions sidecar/kubesphere/4.0.0/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2018 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by a Apache license
# that can be found in the LICENSE file.

IMG ?= leiwanjun/notification-tenant-sidecar:v4.0.0
AMD64 ?= -amd64

all: docker-build

# Build tenant sidecar binary
tenant-sidecar:
go build -o tenant-sidecar main.go backend.go

# Build the docker image
docker-build:
docker buildx build --platform linux/amd64,linux/arm64 --push -f Dockerfile -t ${IMG} .

# Build the docker image for arm64
docker-build-amd64:
docker build -f Dockerfile -t ${IMG}${AMD64} .

# Push the docker image
push-amd64:
docker push ${IMG}${AMD64}
3 changes: 3 additions & 0 deletions sidecar/kubesphere/4.0.0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Notification tenant sidecar

It is a tenant sidecar for kubesphere v4.0.0.
177 changes: 177 additions & 0 deletions sidecar/kubesphere/4.0.0/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"context"
"time"

"k8s.io/api/authorization/v1beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/klog"
iamv1beta1 "kubesphere.io/api/iam/v1beta1"
"kubesphere.io/client-go/rest"
)

type Backend struct {
ksClient *rest.RESTClient
tenants map[string]map[string]string

interval time.Duration
}

func NewBackend(host, username, password string, interval time.Duration) (*Backend, error) {
var config *rest.Config
if username != "" && password != "" {
config = &rest.Config{
Host: host,
Username: username,
Password: password,
}
} else {
config = &rest.Config{
Host: host,
BearerTokenFile: "/var/run/secrets/kubesphere.io/serviceaccount/token",
}
}

c, err := rest.UnversionedRESTClientFor(config)
if err != nil {
return nil, err
}

return &Backend{
ksClient: c,
tenants: make(map[string]map[string]string),
interval: interval,
}, err
}

func (b *Backend) FromNamespace(ns string) []string {

m, ok := b.tenants[ns]
if !ok {
return nil
}

array := make([]string, 0)
for k := range m {
array = append(array, k)
}
return array
}

func (b *Backend) Run() {
b.reload()

ticker := time.NewTicker(b.interval)
go func() {
for {
select {
case <-ticker.C:
b.reload()
}
}
}()
}

func (b *Backend) reload() {
klog.Info("start reload tenant")
defer func() {
klog.Info("end reload tenant")
}()

users, err := b.listUsers()
if err != nil {
klog.Errorf("list users error, %s", err.Error())
}

namespaces, err := b.listNamespaces()
if err != nil {
klog.Errorf("list namespaces error, %s", err.Error())
}

m := make(map[string]map[string]string)
for _, namespace := range namespaces {
for _, user := range users {
allow, err := b.canAccess(user, namespace)
if err != nil {
klog.Errorf("get access view for user %s, namespace %s error, %s", err.Error())
return
}

if allow {
array, ok := m[namespace]
if !ok {
array = make(map[string]string)
}
array[user] = ""
m[namespace] = array
}
}
}

b.tenants = m
}

func (b *Backend) listUsers() ([]string, error) {
res := b.ksClient.Get().AbsPath("/kapis/iam.kubesphere.io/v1beta1/users").Do(context.Background())
if err := res.Error(); err != nil {
return nil, err
}
userList := &iamv1beta1.UserList{}
err := res.Into(userList)
if err != nil {
return nil, err
}

var users []string
for _, user := range userList.Items {
users = append(users, user.Name)
}

return users, nil
}

func (b *Backend) listNamespaces() ([]string, error) {
res := b.ksClient.Get().AbsPath("/api/v1/namespaces").Do(context.Background())
if err := res.Error(); err != nil {
return nil, err
}
namespacesList := &v1.NamespaceList{}
err := res.Into(namespacesList)
if err != nil {
return nil, err
}

var namespaces []string
for _, n := range namespacesList.Items {
namespaces = append(namespaces, n.Name)
}

return namespaces, nil
}

func (b *Backend) canAccess(user, namespace string) (bool, error) {
subjectAccessReview := &v1beta1.SubjectAccessReview{
Spec: v1beta1.SubjectAccessReviewSpec{
ResourceAttributes: &v1beta1.ResourceAttributes{
Namespace: namespace,
Verb: "get",
Group: "notification.kubesphere.io",
Version: "v2beta2",
Resource: "receivenotification",
},
NonResourceAttributes: nil,
User: user, // "X-Remote-User" request header
Groups: []string{}, // "X-Remote-Group" request header
},
}

if err := b.ksClient.Post().AbsPath("/kapis/iam.kubesphere.io/v1beta1/subjectaccessreviews").
Body(subjectAccessReview).
Do(context.Background()).
Into(subjectAccessReview); err != nil {
return false, err
}

return subjectAccessReview.Status.Allowed, nil
}
Loading
Loading