Skip to content
This repository was archived by the owner on Dec 15, 2021. It is now read-only.

877 allow controller to use token #885

Merged
Merged
3 changes: 1 addition & 2 deletions cmd/function-controller/function-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/kubeless/kubeless/pkg/version"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/rest"
)

const (
Expand All @@ -53,7 +52,7 @@ var rootCmd = &cobra.Command{
FunctionClient: kubelessClient,
}

restCfg, err := rest.InClusterConfig()
restCfg, err := utils.GetInClusterConfig()
if err != nil {
logrus.Fatalf("Cannot get REST client: %v", err)
}
Expand Down
34 changes: 32 additions & 2 deletions docs/function-controller-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ or the following information can be added to `functions.kubeless.io` `CustomReso

```yaml
apiVersion: apiextensions.k8s.io/v1beta1
description: Kubernetes Native Serverless Framework
kind: CustomResourceDefinition
metadata:
name: functions.kubeless.io
Expand Down Expand Up @@ -229,4 +228,35 @@ It is possible to configure the different images that Kubeless uses for deploy a
- (Optional) Image Pull Secrets: Secret required to pull the image in case the repository is private.
- The image used to populate the base image with the function. This is called `provision-image`. This image should have at least `unzip` and `curl`. It is also possible to specify `provision-image-secret` to specify a secret to pull that image from a private registry.
- The image used to build function images. This is called `builder-image`. This image is optional since its usage can be disabled with the property `enable-build-step`. A Dockerfile to build this image can be found [here](https://github.com/kubeless/kubeless/tree/master/docker/function-image-builder). It is also possible to specify `builder-image-secret` to specify a secret to pull that image from a private registry.


## Authenticate Kubeless Function Controller using OAuth Bearer Token

In some non-RBAC k8s deployments using webhook authorization, service accounts may have insufficient privileges to perform all k8s operations that the Kubeless Function Controller requires for interacting with the cluster. It's possible to override the default behavior of the Kubeless Function Controller using a k8s serviceaccount for authentication with the cluster and instead use a provided OAuth Bearer token for all k8s operations.

This can be done by creating a k8s secret and mounting that secret as a volume on controller pods, then setting the environmental variable `KUBELESS_TOKEN_FILE_PATH` to the filepath of that secret. Be sure to set this environmental variable on the controller template spec or to every pod created in the deployment.

For example, if the bearer token is mounted at /mnt/secrets/bearer-token, this k8s spec can use it:

```yaml
# Kubeless core controller
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubeless-controller-manager
namespace: kubeless
labels:
kubeless: controller
spec:
template:
metadata:
labels:
kubeless: controller
spec:
containers:
- env:
- name: KUBELESS_TOKEN_FILE_PATH
value: /mnt/secrets/bearer-token
... # The rest of the Deployment has been omitted
```

1 change: 0 additions & 1 deletion docs/implementing-new-trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ First step is to create a new CRD for the event source. CRD for the new triggers

```yaml
apiVersion: apiextensions.k8s.io/v1beta1
description: CRD object for Kafka trigger type
kind: CustomResourceDefinition
metadata:
name: kafkatriggers.kubeless.io
Expand Down
1 change: 0 additions & 1 deletion docs/use-existing-kafka.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ spec:
serviceAccountName: controller-acct
---
apiVersion: apiextensions.k8s.io/v1beta1
description: CRD object for Kafka trigger type
kind: CustomResourceDefinition
metadata:
name: kafkatriggers.kubeless.io
Expand Down
45 changes: 0 additions & 45 deletions glide.yaml

This file was deleted.

1 change: 0 additions & 1 deletion kafka-zookeeper.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ local crd = [
kind: "CustomResourceDefinition",
metadata: objectMeta.name("kafkatriggers.kubeless.io"),
spec: {group: "kubeless.io", version: "v1beta1", scope: "Namespaced", names: {plural: "kafkatriggers", singular: "kafkatrigger", kind: "KafkaTrigger"}},
description: "CRD object for Kafka trigger type",
},
];

Expand Down
3 changes: 0 additions & 3 deletions kubeless-non-rbac.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,18 @@ local crd = [
kind: "CustomResourceDefinition",
metadata: objectMeta.name("functions.kubeless.io"),
spec: {group: "kubeless.io", version: "v1beta1", scope: "Namespaced", names: {plural: "functions", singular: "function", kind: "Function"}},
description: "Kubernetes Native Serverless Framework",
},
{
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: objectMeta.name("httptriggers.kubeless.io"),
spec: {group: "kubeless.io", version: "v1beta1", scope: "Namespaced", names: {plural: "httptriggers", singular: "httptrigger", kind: "HTTPTrigger"}},
description: "CRD object for HTTP trigger type",
},
{
apiVersion: "apiextensions.k8s.io/v1beta1",
kind: "CustomResourceDefinition",
metadata: objectMeta.name("cronjobtriggers.kubeless.io"),
spec: {group: "kubeless.io", version: "v1beta1", scope: "Namespaced", names: {plural: "cronjobtriggers", singular: "cronjobtrigger", kind: "CronJobTrigger"}},
description: "CRD object for HTTP trigger type",
}
];

Expand Down
8 changes: 5 additions & 3 deletions pkg/utils/k8sutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const (

// GetClient returns a k8s clientset to the request from inside of cluster
func GetClient() kubernetes.Interface {
config, err := rest.InClusterConfig()
config, err := GetInClusterConfig()
if err != nil {
logrus.Fatalf("Can not get kubernetes config: %v", err)
}
Expand Down Expand Up @@ -123,7 +123,8 @@ func GetAPIExtensionsClientOutOfCluster() clientsetAPIExtensions.Interface {

// GetAPIExtensionsClientInCluster returns a k8s clientset to access APIExtensions from inside of cluster
func GetAPIExtensionsClientInCluster() clientsetAPIExtensions.Interface {
config, err := rest.InClusterConfig()
config, err := GetInClusterConfig()

if err != nil {
logrus.Fatalf("Can not get kubernetes config: %v", err)
}
Expand All @@ -136,10 +137,11 @@ func GetAPIExtensionsClientInCluster() clientsetAPIExtensions.Interface {

// GetFunctionClientInCluster returns function clientset to the request from inside of cluster
func GetFunctionClientInCluster() (versioned.Interface, error) {
config, err := rest.InClusterConfig()
config, err := GetInClusterConfig()
if err != nil {
return nil, err
}

kubelessClient, err := versioned.NewForConfig(config)
if err != nil {
return nil, err
Expand Down
19 changes: 19 additions & 0 deletions pkg/utils/kubelessutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
kubelessApi "github.com/kubeless/kubeless/pkg/apis/kubeless/v1beta1"
"github.com/kubeless/kubeless/pkg/langruntime"
"github.com/sirupsen/logrus"
"io/ioutil"
batchv1 "k8s.io/api/batch/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
Expand All @@ -40,6 +41,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

// GetFunctionPort returns the port for a function service
Expand Down Expand Up @@ -760,6 +762,23 @@ func GetOwnerReference(kind, apiVersion, name string, uid types.UID) ([]metav1.O
}, nil
}

// GetInClusterConfig returns necessary Config object to authenticate k8s clients if env variable is set
func GetInClusterConfig() (*rest.Config, error) {
config, err := rest.InClusterConfig()

tokenFile := os.Getenv("KUBELESS_TOKEN_FILE_PATH")
if len(tokenFile) == 0 {
return config, err
}
tokenBytes, err := ioutil.ReadFile(tokenFile)
if err != nil {
return nil, fmt.Errorf("unable to read file containing oauth token: %s", err)
}
config.BearerToken = string(tokenBytes)

return config, nil
}

func getConfigLocation(apiExtensionsClientset clientsetAPIExtensions.Interface) (ConfigLocation, error) {
configLocation := ConfigLocation{}
controllerNamespace := os.Getenv("KUBELESS_NAMESPACE")
Expand Down