backstage / backstage/backstage
๐ Bug Report: ambiguously-named clusters cannot be targeted with the proxy endpoint
- Dominant language
- TypeScript
- Stars
- 34.4k
- Forks
- 7.6k
- Avg merge
- 8h 57m
- Merged PRs (30d)
- 50
Description
### ๐ Description
If two clusters located by Backstage have the same name, proxy requests targeting that name will always be routed to the first one -- there's no way to target the second one! You can test this by `curl`ing the proxy endpoint for all namespaces, specifying the ambiguous cluster name, and providing a bearer token that is only valid for the second cluster.
### ๐ Expected behavior
Well, depends on what you know. If you're only aware of the second cluster you might expect to see namespaces. If you know there are two ambiguously-named clusters, you might expect to see an error about this.
### ๐ Actual Behavior with Screenshots
console prints
```text
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}
```
This indicates that only the first cluster got queried -- since the token we used isn't valid for that cluster.
### ๐ Reproduction steps
1. Create two kind clusters: `kind create cluster --name kind-1 && kind create cluster --name kind-2`
1. Grab their server URLs:
```bash
export KIND_1_URL=$(kubectl config view -o jsonpath='{.clusters[?(@.name == "kind-kind-1")].cluster.server}')
export KIND_2_URL=$(kubectl config view -o jsonpath='{.clusters[?(@.name == "kind-kind-2")].cluster.server}')
```
1. Get a high-powered bearer token for the second cluster:
```bash
kubectl config use-context kind-kind-2
kubectl create clusterrolebinding default-sa-admin --serviceaccount default:default --clusterrole cluster-admin
export TOKEN=$(kubectl get secret -o go-template='{{(index .items 0).data.token | base64decode}}')
```
1. Set up an app-config to target these clusters, but with both having the same name:
```yaml
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: config
clusters:
- name: kind
url: ${KIND_1_URL}
skipTLSVerify: true
skipMetricsLookup: true
authProvider: serviceAccount
- name: kind
url: ${KIND_2_URL}
skipTLSVerify: true
skipMetricsLookup: true
authProvider: serviceAccount
```
1. Run the backend: `yarn start-backend`
1. In a separate process, try to fetch namespaces specifying the ambiguous cluster name, using the token for the second cluster:
```bash
curl -H "Authorization: Bearer $TOKEN" \
-H 'X-Kubernetes-Cluster: kind' \
localhost:7007/api/kubernetes/proxy/api/v1/namespaces
```
### ๐ Provide the context for the Bug.
#### Cause
We already know that this bug is caused by the `find()` call here:
https://github.com/backstage/backstage/blob/38c32326456b5dcef0c16b5ff011cf4181a94d7f/plugins/kubernetes-backend/src/service/KubernetesProxy.ts#L121-L123
According to the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find):
> The `find()` method returns the first element in the provided array that satisfies the provided testing function
and this is why it is always the _first_ cluster with the given name that gets targeted.
#### Background
In October 2022, in my big ["Let's just do single-cluster" comment](https://github.com/backstage/backstage/issues/12231#issuecomment-1290727412), I suggested
> At the moment there is no guarantee within the kubernetes subsystem that clusters have unique names (let's file an issue about this), so if this procedure results in multiple clusters, fail the response.
Unfortunately we didn't implement that requirement, and I'm just now filing part of the relevant issue! The rest of the issue should take the form of an RFC about unique identifiers for Kubernetes clusters generally.
### ๐ฅ๏ธ Your Environment
If you really wanna know, I'm running MacOS Ventura 13.1 on a 2019 16-inch Macbook Pro, and I just completed the reproduction steps with the code on master at the time of writing:
```console
% yarn backstage-cli info
OS: Darwin 22.2.0 - darwin/x64
node: v16.19.0
yarn: 3.2.3
cli: 0.22.1 (local)
backstage: N/A
Dependencies:
@backstage/app-defaults 0.0.0-use.local
@backstage/backend-app-api 0.0.0-use.local
@backstage/backend-common 0.0.0-use.local
@backstage/backend-defaults 0.0.0-use.local
@backstage/backend-plugin-api 0.0.0-use.local
@backstage/backend-tasks 0.0.0-use.local
@backstage/backend-test-utils 0.0.0-use.local
@backstage/catalog-client 0.0.0-use.local
@backstage/catalog-model 0.0.0-use.local
@backstage/cli-common 0.0.0-use.local
@backstage/cli 0.0.0-use.local
@backstage/codemods 0.0.0-use.local
@backstage/config-loader 0.0.0-use.local
@backstage/config 0.0.0-use.local
@backstage/core-app-api 0.0.0-use.local
@backstage/core-components 0.0.0-use.local
@backstage/core-plugin-api 0.0.0-use.local
@backstage/create-app 0.0.0-use.local
@backstage/dev-utils 0.0.0-use.local
@backstage/errors 0.0.0-use.local
@backstage/integration-aws-node 0.0.0-use.local
@backstage/integration-react 0.0.0-use.local
@backstage/integration 0.0.0-use.local
@backstage/plugin-adr-backend 0.0.0-use.local
@backstage/plugin-adr-common 0.0.0-use.local
@backstage/plugin-adr 0.0.0-use.local
@backstage/plugin-airbrake-backend 0.0.0-use.local
@backstage/plugin-airbrake 0.0.0-use.local
@backstage/plugin-allure 0.0.0-use.local
@backstage/plugin-analytics-module-ga 0.0.0-use.local
@backstage/plugin-apache-airflow 0.0.0-use.local
@backstage/plugin-api-docs-module-protoc-gen-doc 0.0.0-use.local
@backstage/plugin-api-docs 0.0.0-use.local
@backstage/plugin-apollo-explorer 0.0.0-use.local
@backstage/plugin-app-backend 0.0.0-use.local
@backstage/plugin-auth-backend 0.0.0-use.local
@backstage/plugin-auth-node 0.0.0-use.local
@backstage/plugin-azure-devops-backend 0.0.0-use.local
@backstage/plugin-azure-devops-common 0.0.0-use.local
@backstage/plugin-azure-devops 0.0.0-use.local
@backstage/plugin-azure-sites-backend 0.0.0-use.local
@backstage/plugin-azure-sites-common 0.0.0-use.local
@backstage/plugin-azure-sites 0.0.0-use.local
@backstage/plugin-badges-backend 0.0.0-use.local
@backstage/plugin-badges 0.0.0-use.local
@backstage/plugin-bazaar-backend 0.0.0-use.local
@backstage/plugin-bazaar 0.0.0-use.local
@backstage/plugin-bitbucket-cloud-common 0.0.0-use.local
@backstage/plugin-bitrise 0.0.0-use.local
@backstage/plugin-catalog-backend-module-aws 0.0.0-use.local
@backstage/plugin-catalog-backend-module-azure 0.0.0-use.local
@backstage/plugin-catalog-backend-module-bitbucket-cloud 0.0.0-use.local
@backstage/plugin-catalog-backend-module-bitbucket-server 0.0.0-use.local
@backstage/plugin-catalog-backend-module-bitbucket 0.0.0-use.local
@backstage/plugin-catalog-backend-module-gerrit 0.0.0-use.local
@backstage/plugin-catalog-backend-module-github 0.0.0-use.local
@backstage/plugin-catalog-backend-module-gitlab 0.0.0-use.local
@backstage/plugin-catalog-backend-module-incremental-ingestion 0.0.0-use.local
@backstage/plugin-catalog-backend-module-ldap 0.0.0-use.local
@backstage/plugin-catalog-backend-module-msgraph 0.0.0-use.local
@backstage/plugin-catalog-backend-module-openapi 0.0.0-use.local
@backstage/plugin-catalog-backend 0.0.0-use.local
@backstage/plugin-catalog-common 0.0.0-use.local
@backstage/plugin-catalog-graph 0.0.0-use.local
@backstage/plugin-catalog-graphql 0.0.0-use.local
@backstage/plugin-catalog-import 0.0.0-use.local
@backstage/plugin-catalog-node 0.0.0-use.local
@backstage/plugin-catalog-react 0.0.0-use.local
@backstage/plugin-catalog 0.0.0-use.local
@backstage/plugin-cicd-statistics-module-gitlab 0.0.0-use.local
@backstage/plugin-cicd-statistics 0.0.0-use.local
@backstage/plugin-circleci 0.0.0-use.local
@backstage/plugin-cloudbuild 0.0.0-use.local
@backstage/plugin-code-climate 0.0.0-use.local
@backstage/plugin-code-coverage-backend 0.0.0-use.local
@backstage/plugin-code-coverage 0.0.0-use.local
@backstage/plugin-codescene 0.0.0-use.local
@backstage/plugin-config-schema 0.0.0-use.local
@backstage/plugin-cost-insights-common 0.0.0-use.local
@backstage/plugin-cost-insights 0.0.0-use.local
@backstage/plugin-dynatrace 0.0.0-use.local
@backstage/plugin-events-backend-module-aws-sqs 0.0.0-use.local
@backstage/plugin-events-backend-module-azure 0.0.0-use.local
@backstage/plugin-events-backend-module-bitbucket-cloud 0.0.0-use.local
@backstage/plugin-events-backend-module-gerrit 0.0.0-use.local
@backstage/plugin-events-backend-module-github 0.0.0-use.local
@backstage/plugin-events-backend-module-gitlab 0.0.0-use.local
@backstage/plugin-events-backend-test-utils 0.0.0-use.local
@backstage/plugin-events-backend 0.0.0-use.local
@backstage/plugin-events-node 0.0.0-use.local
@backstage/plugin-explore-backend 0.0.0-use.local
@backstage/plugin-explore-common 0.0.0-use.local
@backstage/plugin-explore-react 0.0.0-use.local
@backstage/plugin-explore 0.0.0-use.local
@backstage/plugin-firehydrant 0.0.0-use.local
@backstage/plugin-fossa 0.0.0-use.local
@backstage/plugin-gcalendar 0.0.0-use.local
@backstage/plugin-gcp-projects 0.0.0-use.local
@backstage/plugin-git-release-manager 0.0.0-use.local
@backstage/plugin-github-actions 0.0.0-use.local
@backstage/plugin-github-deployments 0.0.0-use.local
@backstage/plugin-github-issues 0.0.0-use.local
@backstage/plugin-github-pull-requests-board 0.0.0-use.local
@backstage/plugin-gitops-profiles 0.0.0-use.local
@backstage/plugin-gocd 0.0.0-use.local
@backstage/plugin-graphiql 0.0.0-use.local
@backstage/plugin-graphql-backend 0.0.0-use.local
@backstage/plugin-home 0.0.0-use.local
@backstage/plugin-ilert 0.0.0-use.local
@backstage/plugin-jenkins-backend 0.0.0-use.local
@backstage/plugin-jenkins-common 0.0.0-use.local
@backstage/plugin-jenkins 0.0.0-use.local
@backstage/plugin-kafka-backend 0.0.0-use.local
@backstage/plugin-kafka 0.0.0-use.local
@backstage/plugin-kubernetes-backend 0.0.0-use.local
@backstage/plugin-kubernetes-common 0.0.0-use.local
@backstage/plugin-kubernetes 0.0.0-use.local
@backstage/plugin-lighthouse 0.0.0-use.local
@backstage/plugin-newrelic-dashboard 0.0.0-use.local
@backstage/plugin-newrelic 0.0.0-use.local
@backstage/plugin-org-react 0.0.0-use.local
@backstage/plugin-org 0.0.0-use.local
@backstage/plugin-pagerduty 0.0.0-use.local
@backstage/plugin-periskop-backend 0.0.0-use.local
@backstage/plugin-periskop 0.0.0-use.local
@backstage/plugin-permission-backend 0.0.0-use.local
@backstage/plugin-permission-common 0.0.0-use.local
@backstage/plugin-permission-node 0.0.0-use.local
@backstage/plugin-permission-react 0.0.0-use.local
@backstage/plugin-playlist-backend 0.0.0-use.local
@backstage/plugin-playlist-common 0.0.0-use.local
@backstage/plugin-playlist 0.0.0-use.local
@backstage/plugin-proxy-backend 0.0.0-use.local
@backstage/plugin-rollbar-backend 0.0.0-use.local
@backstage/plugin-rollbar 0.0.0-use.local
@backstage/plugin-scaffolder-backend-module-cookiecutter 0.0.0-use.local
@backstage/plugin-scaffolder-backend-module-rails 0.0.0-use.local
@backstage/plugin-scaffolder-backend-module-sentry 0.0.0-use.local
@backstage/plugin-scaffolder-backend-module-yeoman 0.0.0-use.local
@backstage/plugin-scaffolder-backend 0.0.0-use.local
@backstage/plugin-scaffolder-common 0.0.0-use.local
@backstage/plugin-scaffolder-node 0.0.0-use.local
@backstage/plugin-scaffolder-react 0.0.0-use.local
@backstage/plugin-scaffolder 0.0.0-use.local
@backstage/plugin-search-backend-module-elasticsearch 0.0.0-use.local
@backstage/plugin-search-backend-module-pg 0.0.0-use.local
@backstage/plugin-search-backend-node 0.0.0-use.local
@backstage/plugin-search-backend 0.0.0-use.local
@backstage/plugin-search-common 0.0.0-use.local
@backstage/plugin-search-react 0.0.0-use.local
@backstage/plugin-search 0.0.0-use.local
@backstage/plugin-sentry 0.0.0-use.local
@backstage/plugin-shortcuts 0.0.0-use.local
@backstage/plugin-sonarqube-backend 0.0.0-use.local
@backstage/plugin-sonarqube-react 0.0.0-use.local
@backstage/plugin-sonarqube 0.0.0-use.local
@backstage/plugin-splunk-on-call 0.0.0-use.local
@backstage/plugin-stack-overflow-backend 0.0.0-use.local
@backstage/plugin-stack-overflow 0.0.0-use.local
@backstage/plugin-tech-insights-backend-module-jsonfc 0.0.0-use.local
@backstage/plugin-tech-insights-backend 0.0.0-use.local
@backstage/plugin-tech-insights-common 0.0.0-use.local
@backstage/plugin-tech-insights-node 0.0.0-use.local
@backstage/plugin-tech-insights 0.0.0-use.local
@backstage/plugin-tech-radar 0.0.0-use.local
@backstage/plugin-techdocs-addons-test-utils 0.0.0-use.local
@backstage/plugin-techdocs-backend 0.0.0-use.local
@backstage/plugin-techdocs-module-addons-contrib 0.0.0-use.local
@backstage/plugin-techdocs-node 0.0.0-use.local
@backstage/plugin-techdocs-react 0.0.0-use.local
@backstage/plugin-techdocs 0.0.0-use.local
@backstage/plugin-todo-backend 0.0.0-use.local
@backstage/plugin-todo 0.0.0-use.local
@backstage/plugin-user-settings-backend 0.0.0-use.local
@backstage/plugin-user-settings 0.0.0-use.local
@backstage/plugin-vault-backend 0.0.0-use.local
@backstage/plugin-vault 0.0.0-use.local
@backstage/plugin-xcmetrics 0.0.0-use.local
@backstage/release-manifests 0.0.0-use.local
@backstage/repo-tools 0.0.0-use.local
@backstage/test-utils 0.0.0-use.local
@backstage/theme 0.0.0-use.local
@backstage/types 0.0.0-use.local
@backstage/version-bridge 0.0.0-use.local
```
### ๐ Have you spent some time to check if this bug has been raised before?
- [X] I checked and didn't find similar issue
### ๐ข Have you read the Code of Conduct?
- [X] I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)
### Are you willing to submit PR?
Yes I am willing to submit a PR!
Contributor guide
Research direction
Start in plugins/kubernetes-backend/src/service/KubernetesProxy.ts at the find() call linked in the issue, then reproduce the proxy request with two same-named kind clusters and the second cluster's bearer token. Done means the proxy no longer silently targets the first matching cluster and instead handles the ambiguous name according to the agreed behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kubernetes, typescript
- Domain
- api, backend, infrastructure
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100