Backstage-Kubernetes-Authorization-X-X headers are provided when it gets invoke kubernetes endpoints on backstage
Signed-off-by: Andres Mauricio Gomez P <andmagom@outlook.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
---
|
||||
|
||||
The kubernetes APIs invokes Authentication Strategies when Backstage-Kubernetes-Authorization-X-X headers are provided, this enable the possibility to invoke strategies that executes additional steps to get a kubernetes token like on pinniped or custom strategies
|
||||
@@ -398,9 +398,26 @@ describe('KubernetesBackendClient', () => {
|
||||
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
|
||||
});
|
||||
|
||||
it('hits the /proxy API', async () => {
|
||||
it('hits the /proxy API with oidc as protocol and okta as auth provider', async () => {
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:1234/api/kubernetes/clusters',
|
||||
(_, res, ctx) =>
|
||||
res(
|
||||
ctx.json({
|
||||
items: [
|
||||
{
|
||||
name: 'cluster-a',
|
||||
authProvider: 'oidc',
|
||||
oidcTokenProvider: 'okta',
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({
|
||||
token: 'k8-token',
|
||||
token: 'k8-token3',
|
||||
});
|
||||
const nsResponse = {
|
||||
kind: 'Namespace',
|
||||
@@ -414,8 +431,56 @@ describe('KubernetesBackendClient', () => {
|
||||
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
|
||||
(req, res, ctx) =>
|
||||
res(
|
||||
req.headers.get('Backstage-Kubernetes-Authorization') ===
|
||||
'Bearer k8-token'
|
||||
req.headers.get(
|
||||
'Backstage-Kubernetes-Authorization-oidc-okta',
|
||||
) === 'k8-token3'
|
||||
? ctx.json(nsResponse)
|
||||
: ctx.status(403),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const request = {
|
||||
clusterName: 'cluster-a',
|
||||
path: '/api/v1/namespaces',
|
||||
};
|
||||
|
||||
const response = await backendClient.proxy(request);
|
||||
|
||||
await expect(response.json()).resolves.toStrictEqual(nsResponse);
|
||||
});
|
||||
|
||||
it('hits the /proxy API with serviceAccount as auth provider', async () => {
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:1234/api/kubernetes/clusters',
|
||||
(_, res, ctx) =>
|
||||
res(
|
||||
ctx.json({
|
||||
items: [
|
||||
{
|
||||
name: 'cluster-a',
|
||||
authProvider: 'serviceAccount',
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const nsResponse = {
|
||||
kind: 'Namespace',
|
||||
apiVersion: 'v1',
|
||||
metadata: {
|
||||
name: 'new-ns',
|
||||
},
|
||||
};
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
|
||||
(req, res, ctx) =>
|
||||
res(
|
||||
req.headers.get('Authorization') === 'Bearer idToken'
|
||||
? ctx.json(nsResponse)
|
||||
: ctx.status(403),
|
||||
),
|
||||
@@ -450,8 +515,8 @@ describe('KubernetesBackendClient', () => {
|
||||
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
|
||||
(req, res, ctx) =>
|
||||
res(
|
||||
req.headers.get('Backstage-Kubernetes-Authorization') ===
|
||||
'Bearer k8-token'
|
||||
req.headers.get('Backstage-Kubernetes-Authorization-aws') ===
|
||||
'k8-token'
|
||||
? ctx.json(nsResponse)
|
||||
: ctx.status(403),
|
||||
),
|
||||
|
||||
@@ -75,9 +75,11 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
return this.handleResponse(response);
|
||||
}
|
||||
|
||||
private async getCluster(
|
||||
clusterName: string,
|
||||
): Promise<{ name: string; authProvider: string }> {
|
||||
private async getCluster(clusterName: string): Promise<{
|
||||
name: string;
|
||||
authProvider: string;
|
||||
oidcTokenProvider?: string;
|
||||
}> {
|
||||
const cluster = await this.getClusters().then(clusters =>
|
||||
clusters.find(c => c.name === clusterName),
|
||||
);
|
||||
@@ -140,23 +142,62 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
path: string;
|
||||
init?: RequestInit;
|
||||
}): Promise<Response> {
|
||||
const { authProvider } = await this.getCluster(options.clusterName);
|
||||
const { token: k8sToken } = await this.getCredentials(authProvider);
|
||||
const { authProvider, oidcTokenProvider } = await this.getCluster(
|
||||
options.clusterName,
|
||||
);
|
||||
const kubernetesCredentials = await this.getCredentials(authProvider);
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/proxy${
|
||||
options.path
|
||||
}`;
|
||||
const identityResponse = await this.identityApi.getCredentials();
|
||||
const headers = {
|
||||
const headers = KubernetesBackendClient.getKubernetesHeaders(
|
||||
options,
|
||||
kubernetesCredentials?.token,
|
||||
identityResponse,
|
||||
authProvider,
|
||||
oidcTokenProvider,
|
||||
);
|
||||
return await fetch(url, { ...options.init, headers });
|
||||
}
|
||||
|
||||
private static getKubernetesHeaders(
|
||||
options: {
|
||||
clusterName: string;
|
||||
path: string;
|
||||
init?: RequestInit;
|
||||
},
|
||||
k8sToken: string | undefined,
|
||||
identityResponse: { token?: string },
|
||||
authProvider: string,
|
||||
oidcTokenProvider: string | undefined,
|
||||
) {
|
||||
const kubernetesAuthHeader =
|
||||
KubernetesBackendClient.getKubernetesAuthHeaderByAuthProvider(
|
||||
authProvider,
|
||||
oidcTokenProvider,
|
||||
);
|
||||
return {
|
||||
...options.init?.headers,
|
||||
[`Backstage-Kubernetes-Cluster`]: options.clusterName,
|
||||
...(k8sToken && {
|
||||
[`Backstage-Kubernetes-Authorization`]: `Bearer ${k8sToken}`,
|
||||
[kubernetesAuthHeader]: k8sToken,
|
||||
}),
|
||||
...(identityResponse.token && {
|
||||
Authorization: `Bearer ${identityResponse.token}`,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return await fetch(url, { ...options.init, headers });
|
||||
private static getKubernetesAuthHeaderByAuthProvider(
|
||||
authProvider: string,
|
||||
oidcTokenProvider: string | undefined,
|
||||
): string {
|
||||
let header: string = 'Backstage-Kubernetes-Authorization';
|
||||
|
||||
header = header.concat('-', authProvider);
|
||||
|
||||
if (oidcTokenProvider) header = header.concat('-', oidcTokenProvider);
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user