Skip to content

Commit

Permalink
feat: add kubernetes service account authentication (#187)
Browse files Browse the repository at this point in the history
This change adds support for kubernetes service account server side authentication.
  • Loading branch information
ChristopherFry authored Oct 31, 2022
1 parent 295332d commit f5d16d6
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 27 deletions.
8 changes: 0 additions & 8 deletions app-config.production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,9 @@ backend:
configAsData:
clusterLocatorMethod:
# Determines how the client will locate the Kubernetes cluster.
#
# values:
# 'current-context' uses kubeconfig current context to locate the cluster
# 'in-cluster' uses the same cluster that Backstage is running in
type: in-cluster

# Determines how the client will authenticate with the Kubernetes cluster.
#
# values:
# 'current-context' uses the same user as set by kubeconfig current context
# 'google' uses the current user's Google auth token
authProvider: google

auth:
Expand Down
8 changes: 0 additions & 8 deletions app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,7 @@ techdocs:
configAsData:
clusterLocatorMethod:
# Determines how the client will locate the Kubernetes cluster.
#
# values:
# 'current-context' uses kubeconfig current context to locate the cluster
# 'in-cluster' uses the same cluster that Backstage is running in
type: current-context

# Determines how the client will authenticate with the Kubernetes cluster.
#
# values:
# 'current-context' uses the same user as set by kubeconfig current context
# 'google' uses the current user's Google auth token
authProvider: current-context
21 changes: 13 additions & 8 deletions plugins/cad-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,13 @@ The following configuration will need to be added to `app-config.yaml`:
configAsData:
clusterLocatorMethod:
# Determines how the client will locate the Kubernetes cluster.
#
# values:
# 'current-context' uses kubeconfig current context to locate the cluster
# 'in-cluster' uses the same cluster that Backstage is running in
type: current-context

# Determines how the client will authenticate with the Kubernetes cluster.
#
# values:
# 'current-context' uses the same user as set by kubeconfig current context
# 'google' uses the current user's Google auth token
authProvider: current-context

# The service account token to be used when using the 'service-account' auth provider.
serviceAccountToken: ${CAD_SERVICE_ACCOUNT_TOKEN}
```
`clusterLocatorMethod` determines where to receive the cluster configuration
Expand All @@ -88,3 +83,13 @@ Valid values:
| ------ | ----------- |
| current-context | Authenticate to the cluster with the user in the kubeconfig current context |
| google | Authenticate to the cluster using the user's authentication token from the [Google auth plugin](https://backstage.io/docs/auth/) |
| service-account | Authenticate to the cluster using a Kubernetes service account token |
`clusterLocatorMethod.serviceAccountToken` defines the service account token to be used with the `service-account` auth provider. You can get the service account token with the following command:
```bash
kubectl -n <NAMESPACE> get secret $(kubectl -n <NAMESPACE> get sa <SERVICE_ACCOUNT_NAME> -o=json \
| jq -r '.secrets[0].name') -o=json \
| jq -r '.data["token"]' \
| base64 --decode
```
13 changes: 13 additions & 0 deletions plugins/cad-backend/src/service/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum ClusterLocatorMethodType {
export enum ClusterLocatorAuthProvider {
CURRENT_CONTEXT = 'current-context',
GOOGLE = 'google',
SERVICE_ACCOUNT = 'service-account',
}

export const getClusterLocatorMethodType = (
Expand Down Expand Up @@ -51,3 +52,15 @@ export const getClusterLocatorMethodAuthProvider = (

return authProvider as ClusterLocatorAuthProvider;
};

export const getClusterLocatorMethodServiceAccountToken = (
config: Config,
): string => {
const authProvider = getClusterLocatorMethodAuthProvider(config);

if (authProvider === ClusterLocatorAuthProvider.SERVICE_ACCOUNT) {
return config.getString('clusterLocatorMethod.serviceAccountToken');
}

return '';
};
19 changes: 16 additions & 3 deletions plugins/cad-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Logger } from 'winston';
import {
ClusterLocatorAuthProvider,
getClusterLocatorMethodAuthProvider,
getClusterLocatorMethodServiceAccountToken,
getClusterLocatorMethodType,
} from './config';
import { getKubernetesConfig } from './lib';
Expand Down Expand Up @@ -52,6 +53,9 @@ export async function createRouter({
throw new Error(`Current cluster is not set`);
}

const serviceAccountToken =
getClusterLocatorMethodServiceAccountToken(cadConfig);

const k8sApiServerUrl = currentCluster.server;

const healthCheck = (
Expand Down Expand Up @@ -110,14 +114,23 @@ export async function createRouter({

kubeConfig.applyToRequest(requestOptions);

const useEndUserAuthz =
clusterLocatorMethodAuthProvider !==
ClusterLocatorAuthProvider.CURRENT_CONTEXT;
const endUserProviders = [ClusterLocatorAuthProvider.GOOGLE];
const useEndUserAuthz = endUserProviders.includes(
clusterLocatorMethodAuthProvider,
);
if (useEndUserAuthz) {
requestOptions.headers = requestOptions.headers ?? {};
requestOptions.headers.authorization = request.headers.authorization;
}

const useServiceAccount =
clusterLocatorMethodAuthProvider ===
ClusterLocatorAuthProvider.SERVICE_ACCOUNT;
if (useServiceAccount) {
requestOptions.headers = requestOptions.headers ?? {};
requestOptions.headers.authorization = `Bearer ${serviceAccountToken}`;
}

requestLibrary(requestOptions, (k8Error, k8Response, k8Body) => {
if (k8Error) {
response.status(500);
Expand Down

0 comments on commit f5d16d6

Please sign in to comment.