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

Generate kube config based on plugin RBAC #1020

Merged
merged 15 commits into from
Mar 23, 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
3 changes: 2 additions & 1 deletion cmd/botkube/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ func run(ctx context.Context) error {
CommandGuard: cmdGuard,
PluginManager: pluginManager,
BotKubeVersion: botkubeVersion,
RestCfg: kubeConfig,
AuditReporter: auditReporter,
},
)
Expand Down Expand Up @@ -380,7 +381,7 @@ func run(ctx context.Context) error {

actionProvider := action.NewProvider(logger.WithField(componentLogFieldKey, "Action Provider"), conf.Actions, executorFactory)

sourcePluginDispatcher := source.NewDispatcher(logger, bots, sinkNotifiers, pluginManager, actionProvider, reporter, auditReporter)
sourcePluginDispatcher := source.NewDispatcher(logger, bots, sinkNotifiers, pluginManager, actionProvider, reporter, auditReporter, kubeConfig)
scheduler := source.NewScheduler(logger, conf, sourcePluginDispatcher)
err = scheduler.Start(ctx)
if err != nil {
Expand Down
7 changes: 3 additions & 4 deletions cmd/source/cm-watcher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"os"

"github.com/MakeNowJust/heredoc"
"github.com/hashicorp/go-plugin"
Expand Down Expand Up @@ -76,13 +75,13 @@ func (CMWatcher) Stream(ctx context.Context, in source.StreamInput) (source.Stre
Output: make(chan []byte),
}

go listenEvents(ctx, cfg.ConfigMap, out.Output)
go listenEvents(ctx, in.Context.KubeConfig, cfg.ConfigMap, out.Output)

return out, nil
}

func listenEvents(ctx context.Context, obj Object, sink chan<- []byte) {
config, err := clientcmd.BuildConfigFromFlags("", os.Getenv("KUBECONFIG"))
func listenEvents(ctx context.Context, kubeConfig []byte, obj Object, sink chan<- []byte) {
config, err := clientcmd.RESTConfigFromKubeConfig(kubeConfig)
exitOnError(err)
clientset, err := kubernetes.NewForConfig(config)
exitOnError(err)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ require (
k8s.io/kubectl v0.25.4
k8s.io/utils v0.0.0-20221108210102-8e77b1f39fe2
sigs.k8s.io/controller-runtime v0.13.1
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down Expand Up @@ -204,7 +205,6 @@ require (
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

go 1.19
374 changes: 202 additions & 172 deletions helm/botkube/README.md

Large diffs are not rendered by default.

47 changes: 36 additions & 11 deletions helm/botkube/e2e-test-values.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
analytics:
disable: true

rbac:
create: true
rules:
- apiGroups: [ "*" ]
resources: [ "*" ]
verbs: [ "get", "watch", "list" ] # defaults
- apiGroups: [ "" ]
resources: [ "services" ]
verbs: [ "patch" ] # needed for label action
staticGroupName: &static-group-name "botkube-plugins-default"

communications:
'default-group':
slack: # Configuration for the Slack app with RTM support
Expand Down Expand Up @@ -65,6 +76,25 @@ sources:
'k8s-events':
displayName: "K8s recommendations"
'botkube/kubernetes':
context: &defaultPluginContext
defaultNamespace: "default"
# -- RBAC configuration for this plugin.
rbac:
# -- Static impersonation for a given username and groups.
group:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of group.rbac.authorization.k8s.io the plugin will be bound to.
values: [*static-group-name] # "botkube-plugins-read-only" is the default
user:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of user.rbac.authorization.k8s.io the plugin will be bound to.
value: *static-group-name
enabled: true
config:
log:
Expand All @@ -88,6 +118,7 @@ sources:
'k8s-annotated-cm-delete':
displayName: "K8s ConfigMap delete events"
'botkube/kubernetes':
context: *defaultPluginContext
enabled: true
config:
log:
Expand All @@ -105,6 +136,7 @@ sources:

'k8s-pod-create-events':
'botkube/kubernetes':
context: *defaultPluginContext
enabled: true
config:
log:
Expand All @@ -121,6 +153,7 @@ sources:
'k8s-service-create-event-for-action-only':
displayName: "K8s Service creation, used only by action"
'botkube/kubernetes':
context: *defaultPluginContext
enabled: true
config:
namespaces:
Expand All @@ -135,6 +168,7 @@ sources:
'k8s-updates':
displayName: "K8s ConfigMaps updates"
'botkube/kubernetes':
context: *defaultPluginContext
enabled: true
config:
log:
Expand All @@ -159,6 +193,7 @@ sources:
'plugin-based':
displayName: "K8s ConfigMaps changes"
botkube/cm-watcher:
context: *defaultPluginContext
enabled: true
config:
configMap:
Expand Down Expand Up @@ -229,6 +264,7 @@ executors:
changeResponseToUpperCase: true

botkube/helm:
context: *defaultPluginContext
enabled: true

plugins:
Expand Down Expand Up @@ -288,17 +324,6 @@ settings:
extraAnnotations:
botkube.io/disable: "true"

rbac:
create: true
rules:
- apiGroups: [ "*" ]
resources: [ "*" ]
verbs: [ "get", "watch", "list" ] # defaults
- apiGroups: [ "" ]
resources: [ "services" ]
verbs: [ "patch" ] # needed for label action


extraEnv:
- name: LOG_LEVEL_SOURCE_BOTKUBE_KUBERNETES
value: debug
8 changes: 8 additions & 0 deletions helm/botkube/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get"]
- apiGroups:
- ""
resources:
- users
- groups
- serviceaccounts
verbs:
- impersonate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
Expand Down
44 changes: 25 additions & 19 deletions helm/botkube/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ sources:
# -- Describes Kubernetes source configuration.
# @default -- See the `values.yaml` file for full object.
botkube/kubernetes:
context: &defaultPluginContext
defaultNamespace: "default"
# -- RBAC configuration for this plugin.
rbac:
# -- Static impersonation for a given username and groups.
group:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of group.rbac.authorization.k8s.io the plugin will be bound to.
values: [*static-group-name] # "botkube-plugins-read-only" is the default
user:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of user.rbac.authorization.k8s.io the plugin will be bound to.
value: *static-group-name
enabled: true
config:
namespaces:
Expand All @@ -141,6 +160,7 @@ sources:
# -- Describes Kubernetes source configuration.
# @default -- See the `values.yaml` file for full object.
botkube/kubernetes:
context: *defaultPluginContext
enabled: true
config:
# -- Filter settings for various sources.
Expand Down Expand Up @@ -313,6 +333,7 @@ sources:
# -- Describes Kubernetes source configuration.
# @default -- See the `values.yaml` file for full object.
botkube/kubernetes:
context: *defaultPluginContext
enabled: true
config:
# -- Describes namespaces for every Kubernetes resources you want to watch or exclude.
Expand Down Expand Up @@ -352,6 +373,7 @@ sources:
# -- Describes Kubernetes source configuration.
# @default -- See the `values.yaml` file for full object.
botkube/kubernetes:
context: *defaultPluginContext
enabled: true
config:
# -- Describes namespaces for every Kubernetes resources you want to watch or exclude.
Expand Down Expand Up @@ -382,6 +404,7 @@ sources:
# -- Describes Kubernetes source configuration.
# @default -- See the `values.yaml` file for full object.
botkube/kubernetes:
context: *defaultPluginContext
enabled: true
config:
# -- Describes namespaces for every Kubernetes resources you want to watch or exclude.
Expand Down Expand Up @@ -476,24 +499,7 @@ executors:
helmConfigDir: "/tmp/helm/"
# -- Location for storing cached files. Must be under the Helm config directory.
helmCacheDir: "/tmp/helm/.cache"
context: &defaultExecutorContext
# -- RBAC configuration for this plugin.
rbac:
# -- Static impersonation for a given username and groups.
group:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of group.rbac.authorization.k8s.io the plugin will be bound to.
values: [*static-group-name] # "botkube-plugins-read-only" is the default
user:
type: Static
# -- Prefix that will be applied to .static.value[*].
prefix: ""
static:
# -- Name of user.rbac.authorization.k8s.io the plugin will be bound to.
value: "default"
context: *defaultPluginContext

## Kubectl executor configuration
## Plugin name syntax: <repo>/<plugin>[@<version>]. If version is not provided, the latest version from repository is used.
Expand All @@ -518,7 +524,7 @@ executors:
# verbs: [ "api-resources", "api-versions", "cluster-info", "describe", "explain", "get", "logs", "top" ]
# # Configures which K8s resource are displayed in resources dropdown.
# resources: [ "deployments", "pods", "namespaces", "daemonsets", "statefulsets", "storageclasses", "nodes", "configmaps", "services", "ingresses", "replicasets", "secrets", "cronjobs", "jobs" ]
context: *defaultExecutorContext
context: *defaultPluginContext

# -- Custom aliases for given commands.
# The aliases are replaced with the underlying command before executing it.
Expand Down
42 changes: 27 additions & 15 deletions internal/executor/helm/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package helm
import (
"context"
"fmt"
"os"

"github.com/MakeNowJust/heredoc"
"github.com/alexflint/go-arg"
Expand Down Expand Up @@ -104,37 +105,47 @@ func (e *Executor) Execute(ctx context.Context, in executor.ExecuteInput) (execu
in.Command = fmt.Sprintf("%s -n %s", in.Command, cfg.DefaultNamespace)
}

kubeConfigPath, deleteFn, err := pluginx.PersistKubeConfig(ctx, in.Context.KubeConfig)
if err != nil {
return executor.ExecuteOutput{}, fmt.Errorf("while writing kubeConfig file: %w", err)
}
defer func() {
if deleteErr := deleteFn(ctx); deleteErr != nil {
fmt.Fprintf(os.Stderr, "failed to delete cube config file %s: %v", kubeConfigPath, deleteErr)
}
}()

switch {
case helmCmd.Install != nil:
return e.handleHelmCommand(ctx, helmCmd.Install, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Install, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.UninstallCommandAliases.Get() != nil:
return e.handleHelmCommand(ctx, helmCmd.UninstallCommandAliases.Get(), cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.UninstallCommandAliases.Get(), cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.ListCommandAliases.Get() != nil:
return e.handleHelmCommand(ctx, helmCmd.ListCommandAliases.Get(), cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.ListCommandAliases.Get(), cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Version != nil:
return e.handleHelmCommand(ctx, helmCmd.Version, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Version, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Status != nil:
return e.handleHelmCommand(ctx, helmCmd.Status, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Status, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Test != nil:
return e.handleHelmCommand(ctx, helmCmd.Test, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Test, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Rollback != nil:
return e.handleHelmCommand(ctx, helmCmd.Rollback, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Rollback, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Upgrade != nil:
return e.handleHelmCommand(ctx, helmCmd.Upgrade, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Upgrade, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.HistoryCommandAliases.Get() != nil:
return e.handleHelmCommand(ctx, helmCmd.HistoryCommandAliases.Get(), cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.HistoryCommandAliases.Get(), cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Get != nil:
switch {
case helmCmd.Get.All != nil:
return e.handleHelmCommand(ctx, helmCmd.Get.All, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Get.All, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Get.Hooks != nil:
return e.handleHelmCommand(ctx, helmCmd.Get.Hooks, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Get.Hooks, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Get.Manifest != nil:
return e.handleHelmCommand(ctx, helmCmd.Get.Manifest, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Get.Manifest, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Get.Notes != nil:
return e.handleHelmCommand(ctx, helmCmd.Get.Notes, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Get.Notes, cfg, wasHelpRequested, in.Command, kubeConfigPath)
case helmCmd.Get.Values != nil:
return e.handleHelmCommand(ctx, helmCmd.Get.Values, cfg, wasHelpRequested, in.Command)
return e.handleHelmCommand(ctx, helmCmd.Get.Values, cfg, wasHelpRequested, in.Command, kubeConfigPath)
default:
return executor.ExecuteOutput{
Data: helmCmd.Get.Help(),
Expand All @@ -153,7 +164,7 @@ func (*Executor) Help(context.Context) (api.Message, error) {
}

// handleHelmList construct a Helm CLI command and run it.
func (e *Executor) handleHelmCommand(ctx context.Context, cmd command, cfg Config, wasHelpRequested bool, rawCmd string) (executor.ExecuteOutput, error) {
func (e *Executor) handleHelmCommand(ctx context.Context, cmd command, cfg Config, wasHelpRequested bool, rawCmd, kubeConfig string) (executor.ExecuteOutput, error) {
if wasHelpRequested {
return executor.ExecuteOutput{
Data: cmd.Help(),
Expand All @@ -169,6 +180,7 @@ func (e *Executor) handleHelmCommand(ctx context.Context, cmd command, cfg Confi
"HELM_DRIVER": cfg.HelmDriver,
"HELM_CACHE_HOME": cfg.HelmCacheDir,
"HELM_CONFIG_HOME": cfg.HelmConfigDir,
"KUBECONFIG": kubeConfig,
}

out, err := e.executeCommandWithEnvs(ctx, rawCmd, envs)
Expand Down
Loading