service-provider-integration-operator

module
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2022 License: Apache-2.0

README

service-provider-integration-operator

Code Coverage Report codecov

A Kubernetes controller/operator that manages service provider integration tasks.

OAuth service is now part of this repository (documentation).

Building & Testing

This project provides a Makefile to run all the usual development tasks. If you simply run make without any arguments, you'll get a list of available "targets".

To build the project one needs to invoke (builds both operator and oauth binaries into bin/ folder):

make build

To test the code (!tests requires running cluster in kubectl context):

make test

To build the docker images of the operator and oauth service one can run:

make docker-build

This will make a docker images called quay.io/redhat-appstudio/service-provider-integration-operator:next and quay.io/redhat-appstudio/service-provider-integration-oauth:next which might or might not be what you want. To override the name of the image build, specify it in the SPI_IMG_BASE and/or TAG_NAME environment variable, e.g.:

SPI_IMG_BASE=quay.io/acme TAG_NAME=bugfix make docker-build

To push the images to an image repository one can use:

make docker-push

The image being pushed can again be modified using the environment variable:

SPI_IMG_BASE=quay.io/acme TAG_NAME=bugfix make docker-push

To set precise image names, one can use SPIO_IMG for operator image and SPIS_IMG for oauth image (see Makefile for more details).

Before you push a PR to the repository, it is recommended to run an overall validity check of the codebase. This will run the formatting check, static code analysis and all the tests:

make check
KCP

We generate APIResourceSchemas and APIExport for KCP from our CRDs using ./hack/generate-kcp-api.sh script or make manifests-kcp. These have to be updated on every CRD change and committed. APIResourceSchema is immutable in KCP, so we version it by putting datetime as a name prefix.

Configuration

It is expected by the Kustomize deployment that this configuration lives in a Secret in the same namespaces as SPI. Name of the secret should be shared-configuration-file with this configuration yaml under config.yaml key.

This is basic configuration that is mandatory to run SPI Operator and OAuth services. See config.go for details (persistedConfiguration).

serviceProviders:
- type: <service_provider_type>
  clientId: <service_provider_client_id>
  clientSecret: <service_provider_secret>
  • <service_provider_type> - type of the service provider. This must be one of the supported values: GitHub, Quay
  • <service_provider_client_id> - client ID of the OAuth application
  • <service_provider_secret> - client secret of the OAuth application that the SPI uses to access the service provider

To create OAuth application at GitHub, follow GitHub - Creating an OAuth App

The rest of the configuration is applied using the environment variables or command line arguments.

In addition to the secret, there are 3 configmaps that contain the configuration for operator and oauth service.

ConfigMap Applicable to
spi-shared-environment-config operator and oauth service
spi-controller-manager-environment-config operator
spi-oauth-service-environment-config oauth service

The spi-shared-environment-config is bound to both the operator and oauth service and is therefore best used for configuration options that should have the same value in both deployments.

Common configuration parameters

The shared-environment-config config map contains configuration options that will be applied to both the operator and oauth service (if you look in the tables of the individual config options for operator and oauth service, you will see that a lot of them are common). This especially makes sense for configuration that needs to have the same value in both deployments, e.g. BASEURL and VAULTHOST are two examples of values that should be the same for both.

To change the VAULTHOST for both the oauth service and the operator, one can do:

kubectl -n spi-system patch configmap spi-shared-environment-config --type=merge -p '{"data": {"VAULTHOST": "https://vault.somewhere.else"}}'
kubectl -n spi-system rollout restart deployment/spi-controller-manager deployment/spi-oauth-service

This is the full set of parameters that are common for both the operator and the oauth service (note that while the command line arguments and the environment variable names are the same, it may not make sense for both the operator and oauth service to have the same value for them):

Command argument Environment variable Default Description
--base-url BASEURL This is the publicly accessible URL on which the SPI OAuth service is reachable. Note that this is not just a hostname, it is a full URL including a scheme, e.g. "https://acme.com/spi"
--config-file CONFIGFILE /etc/spi/config.yaml The location of the configuration file.
--metrics-bind-address METRICSADDR 127.0.0.1:8080 The address the metric endpoint binds to. Note: While this is the default from the operator binary point of view, the metrics are still available externally through the authorized endpoint provided by kube-rbac-proxy
--health-probe-bind-address HEALTH-PROBE-BIND-ADDRESS PROBEADDR :8081 The address the probe endpoint binds to.
--vault-host VAULTHOST http://spi-vault:8200 Vault host URL. Default is internal kubernetes service.
--vault-insecure-tls VAULTINSECURETLS false Whether is allowed or not insecure vault tls connection.
--vault-auth-method VAULTAUTHMETHOD approle Authentication method to Vault token storage. Options: 'kubernetes', 'approle'.
--vault-roleid-filepath VAULTAPPROLEROLEIDFILEPATH /etc/spi/role_id Used with Vault approle authentication. Filepath with role_id.
--vault-secretid-filepath VAULTAPPROLESECRETIDFILEPATH /etc/spi/secret_id Used with Vault approle authentication. Filepath with secret_id.
--vault-k8s-sa-token-filepath VAULTKUBERNETESSATOKENFILEPATH Used with Vault kubernetes authentication. Filepath to kubernetes ServiceAccount token. When empty, Vault configuration uses default k8s path. No need to set when running in k8s deployment, useful mostly for local development.
--vault-k8s-role VAULTKUBERNETESROLE Used with Vault kubernetes authentication. Vault authentication role set for k8s ServiceAccount.
--zap-devel ZAPDEVEL false Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn) Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
--zap-encoder ZAPENCODER Zap log encoding (‘json’ or ‘console’)
--zap-log-level ZAPLOGLEVEL Zap Level to configure the verbosity of logging.
--zap-stacktrace-level ZAPSTACKTRACELEVEL Zap Level at and above which stacktraces are captured.
--zap-time-encoding ZAPTIMEENCODING iso8601 Format of the time in the log. One of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano.
Operator configuration parameters

This table only contains the configuration parameters specific to the operator. All the common configuration parameters are also applicable to the operator. The configmap for operator-specific configuration is called spi-controller-manager-environment-config.

Command argument Environment variable Default Description
--leader-elect ENABLELEADERELECTION false Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
--metadata-cache-ttl TOKENMETADATACACHETTL 1h The maximum age of the token metadata cache. To reduce the load on the service providers, SPI only refreshes the metadata of the tokens when determined stale by this parameter.
--token-ttl TOKENLIFETIMEDURATION 120h Access token lifetime in hours, minutes or seconds. Examples: "3h", "5h30m40s" etc.
--binding-ttl BINDINGLIFETIMEDURATION 2h Access token binding lifetime in hours, minutes or seconds. Examples: "3h", "5h30m40s" etc.
--access-check-ttl ACCESSCHECKLIFETIMEDURATION 30m Access check lifetime in hours, minutes or seconds.
--token-match-policy TOKENMATCHPOLICY any The policy to match the token against the binding. Options: 'any', 'exact'."`
--kcp-api-export-name APIEXPORTNAME spi SPI ApiExport name used in KCP environment to configure controller with virtual workspace.
--deletion-grace-period DELETIONGRACEPERIOD 2s The grace period between a condition for deleting a binding or token is satisfied and the token or binding actually being deleted.
OAuth service configuration parameters

This table only contains the configuration parameters specific to the oauth service. All the common configuration parameters are also applicable to the oauth service. The configmap for oauth-service-specific configuration is called spi-oauth-service-environment-config.

Command argument Environment variable Default Description
--service-addr SERVICEADDR 0.0.0.0:8000 Service address to listen on.
--allowed-origins ALLOWEDORIGINS https://console.dev.redhat.com, https://prod.foo.redhat.com:1337 Comma-separated list of domains allowed for cross-domain requests.
--kubeconfig KUBECONFIG KUBE-CONFIG.
--kube-insecure-tls KUBEINSECURETLS false Whether is allowed or not insecure kubernetes tls connection.
--api-server API_SERVER Host:port of the Kubernetes API server to use when handling HTTP requests.
--ca-path API_SERVER_CA_PATH The path to the CA certificate to use when connecting to the Kubernetes API server.

Vault

Vault instance is deployed together with SPI components. make deploy or make deploy_minikube configures it automatically. For other deployments, like infra-deployments run ./hack/vault-init.sh manually.

There are couple of support scripts to work with Vault

  • ./hack/vault-init.sh - Initialize and configure Vault instance
  • ./hack/vault-generate-template.sh - generates deployment yamls from vault-helm. These should be commited in this repository.
  • injected in vault pod /vault/userconfig/scripts/poststart.sh - unseal vault storage. Runs automatically after pod startup.
  • injected in vault pod /vault/userconfig/scripts/root.sh - vault login as root with generated root token. Can be used for manual configuration.

Running

It is possible to run the operator both in and out of a Kubernetes cluster.

Out of cluster

There is a dedicated make target to run the operator locally:

make run

This will also deploy RBAC setup and the CRDs into the cluster and will run the operator locally with the permissions of the deployed service account as configure in the Kustomize files in the config directory.

To run the operator with the permissions of the currently active kubectl context, use:

make run_as_current_user
In cluster

Again, there is a dedicated make target to deploy the operator into the cluster:

For OpenShift, use:

make deploy

For Kubernetes, use:

make deploy_k8s

Once deployed, several manual modifications need to be made. See the below section about manual testing with custom images for details.

Debugging

It is possible to debug the operator using dlv or some IDE like vscode. Just point the debugger of your choice to main.go as the main program file and remember to configure the environment variables for the correct/intended function of the operator.

The launch.json file for vscode is included in the repository so you should be all set if using that IDE. Just make sure to run make prepare before debugging.

Manual testing with custom images

This assumes the current working directory is your local checkout of this repository.

First, we need to enable the ingress addon (skip this step, obviously, if you're working with OpenShift):

minikube addons enable ingress

Then we can install our CRDs:

make install

Next, we're ready to build and push the custom operator and oauth images:

make docker-build docker-push SPI_IMG_BASE=<MY-CUSTOM-IMAGE-BASE> TAG_NAME=<MY-CUSTOM-TAG-NAME>

Next step is to deploy the operator and oauth service along with all other Kubernetes objects to the cluster. This step assumes that you also want to use a custom image for both SPI OAuth service and Operator. If you want to use the default one, specify just the SPIS_IMG or SPIO_IMG env var below.

On OpenShift use:

make deploy SPI_IMG_BASE=<MY-CUSTOM-IMAGE-BASE> TAG_NAME=<MY-CUSTOM-TAG-NAME>

On Minikube use:

make deploy_minikube SPI_IMG_BASE=<MY-CUSTOM-IMAGE-BASE> TAG_NAME=<MY-CUSTOM-TAG-NAME>

Next, comes the manual part. We need to set the external domain of the ingress/route of the OAuth service and reconfigure the OAuth service and operator to know about it:

On Minikube, we can use nip.io to set the hostname like this:

SPI_HOST="spi.$(minikube ip).nip.io"
kubectl -n spi-system patch ingress spi-oauth-ingress --type=json --patch '[{"op": "replace", "path": "/spec/rules/0/host", "value": "'$SPI_HOST'"}]'

On Kubernetes, the host of the ingress needs to be set be the means appropriate to your cluster environment.

On OpenShift, you merely need to note down the hostname of your route.

In either case, store the hostname of your ingress/route in the $SPI_HOST environment variable

Also, note down the client id and client secret of the OAuth application in Github that you want SPI to act as and store the in the CLIENT_ID and CLIENT_SECRET env vars respectively.

Next, we need to reconfigure the oauth service and operator. Both are configured using a single configmap:

SPI_CONFIGMAP=$(kubectl -n spi-system get configmap -l app.kubernetes.io/part-of=service-provider-integration-operator | grep spi-oauth-config | cut -f1 -d' ')
kubectl -n spi-system patch configmap $SPI_CONFIGMAP --type=json --patch '[{"op": "replace", "path": "/data/config.yaml", "value": "'"$(kubectl -n spi-system get configmap $SPI_CONFIGMAP -o jsonpath='{.data.config\.yaml}' | yq -y 'setpath(["baseUrl"]; "https://'$SPI_HOST'")' | yq -y 'setpath(["serviceProviders", 0, "clientId"]; "'$CLIENT_ID'")' | yq -y 'setpath(["serviceProviders", 0, "clientSecret"]; "'$CLIENT_SECRET'")' | sed ':a;N;$!ba;s/\n/\\n/g')"'"}]'

All that is left for the setup is to restart the oauth service and operator to load the new configuration:

kubectl -n spi-system scale deployment spi-controller-manager spi-oauth-service --replicas=0
kubectl -n spi-system scale deployment spi-controller-manager spi-oauth-service --replicas=1
Go through the OAuth flow manually

Let's assume that a) we're using the default namespace and that b) the default service account has permissions to CRUD SPIAccessTokens and SPIAccessTokenBindings in the default namespace.

This can be ensured by running:

kubectl apply -f hack/give-default-sa-perms-for-accesstokens.yaml

Let's create the SPIAccessTokenBinding:

kubectl apply -f samples/spiaccesstokenbinding.yaml --as system:serviceaccount:default:default

Now, we need initiate the OAuth flow. We've already created the spi access token binding. Let's examine it to see what access token it bound to:

SPI_ACCESS_TOKEN=$(kubectl get spiaccesstokenbindings test-access-token-binding -o=jsonpath='{.status.linkedAccessTokenName}')

Let's see if it has OAuth URL to go through:

OAUTH_URL=$(kubectl get spiaccesstoken $SPI_ACCESS_TOKEN -o=jsonpath='{.status.oAuthUrl}') 

Now let's use the bearer token of the default service account to authenticate with the OAuth service endpoint:

curl -v -k $OAUTH_URL 2>&1 | grep 'location: ' | cut -d' ' -f3

This gave us the link to the actual service provider (github) that we can use in the browser to approve and finish the OAuth flow.

Upon returning from that OAuth flow, you should end up back on the SPI endpoint on the .../github/callback URL. This concludes the flow and you should be able to see the secret configured in the binding with the requested data:

kubectl get secret token-secret -o yaml

Supported providers and token kinds

The currently supported list of providers and token kinds is following:

Github
  • OAuth token via user authentication flow
  • Personal access token via manual upload
Quay
  • OAuth token via user authentication flow
  • Robot account credentials via manual upload
Snyk & other providers that do not support OAuth
  • Manual credentials upload only with both username and token required. Single token per namespace per host is stored. See details below.

Storing username and password credentials for any provider by it's URL

It is now possible to store username + token/password credentials for nearly any provider that may offer them (like Snyk.io etc). It is can be done in a few steps:

  • Create simple SPIAccessTokenBinging, indicating provider-s URL only:
apiVersion: appstudio.redhat.com/v1beta1
kind: SPIAccessTokenBinding
metadata:
 name: test-access-token-binding
 namespace: default
spec:
 repoUrl: https://snyk.io/
 secret:
   type: kubernetes.io/basic-auth

and push it into namespace:

kubectl apply -f samples/spiaccesstokenbinding.yaml --as system:serviceaccount:default:default
  • Determine the SPIAccessTokenName:
SPI_ACCESS_TOKEN=$(kubectl get spiaccesstokenbindings test-access-token-binding -o=jsonpath='{.status.linkedAccessTokenName}')
  • Perform manual push of the credential data:
curl -k -v -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $(hack/get-default-sa-token.sh)" -d'{"username": "userfoo", "access_token": "4R28N79MT"}' "https://<cluster-host-or-ip>/token/default/$SPI_ACCESS_TOKEN"

Note that both username and access_token fields are mandatory. 204 response indicates that credentials successfully uploaded and stored. Please note that SPI service didn't perform any specific validity checks for the user provided credentials, so it's assumed user provides a correct data.

Directories

Path Synopsis
api
v1beta1
Package v1beta1 contains API Schema definitions for the appstudio v1beta1 API group +kubebuilder:object:generate=true +groupName=appstudio.redhat.com
Package v1beta1 contains API Schema definitions for the appstudio v1beta1 API group +kubebuilder:object:generate=true +groupName=appstudio.redhat.com
cmd
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL