Skip to content

Commit

Permalink
Added support for @backstage/integration-aws-node for request authent…
Browse files Browse the repository at this point in the history
…ication

- Implement `@backstage/integration-aws-node` for aws-sdk clients authentication
- Added API report `yarn build:api-reports:only`
- Applied Prettier code formatting `yarn prettier write`
  • Loading branch information
Florian JUDITH committed Oct 29, 2024
1 parent f6da47f commit 67c2f51
Show file tree
Hide file tree
Showing 128 changed files with 5,218 additions and 3,931 deletions.
29 changes: 15 additions & 14 deletions backstage-plugins/plugins/aws-apps-backend/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<!--
<!--
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
SPDX-License-Identifier: Apache-2.0
-->

# OPA on AWS Backend

This is the backend part of the OPA on AWS plugin. Its key responsibilities:
This is the backend part of the OPA on AWS plugin. Its key responsibilities:

1. **Catalog contributions** - the plugin provides the AWSEnvironment and AWSEnvironmentProvider entity Kinds, including processing and validation of the entities.
2. **Authentication / Authorization** - the plugin assumes defined roles with permisisons for provisioning infrastructure resources for a target environment account.
3. **Audit** - the plugin provides services to record requested actions, user id and IAM role, timestamps, success/failure results, and additional information for the purpose of capturing audit-level information about the actions performed by the AWS Apps Backstage plugin against AWS.
4. **Proxying AWS requests** - the plugin provides API endpoints for specific AWS service actions. It receives requests on these endpoints, validates the request, and proxies the request and response between Backstage and a specified AWS account and region.
4. **Proxying AWS requests** - the plugin provides API endpoints for specific AWS service actions. It receives requests on these endpoints, validates the request, and proxies the request and response between Backstage and a specified AWS account and region.

## Installation

```sh
# From your Backstage root directory
yarn add --cwd packages/backend @aws/plugin-aws-apps-backend-for-backstage@0.2.0
yarn add --cwd packages/backend @alithya-oss/plugin-aws-apps-backend@0.2.0
```

## Configuration
Expand All @@ -24,15 +25,15 @@ Setup for the AWS Apps backend requires a router for Backstage, making the catal

### Configure a router

Create a `awsApps.ts` file in the `packages/backend/src/plugins/`directory. This file creates a router for the OPA on AWS backend.
Create a `awsApps.ts` file in the `packages/backend/src/plugins/`directory. This file creates a router for the OPA on AWS backend.

```ts
// packages/backend/src/plugins/awsApps.ts

import {createRouter} from '@aws/plugin-aws-apps-backend-for-backstage'
import { createRouter } from '@alithya-oss/plugin-aws-apps-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import {DefaultIdentityClient } from '@backstage/plugin-auth-node';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';

export default async function createPlugin({
logger,
Expand Down Expand Up @@ -92,13 +93,13 @@ import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
+ import { AWSEnvironmentEntitiesProcessor, AWSEnvironmentProviderEntitiesProcessor} from '@aws/plugin-aws-apps-backend-for-backstage';
+ import { AWSEnvironmentEntitiesProcessor, AWSEnvironmentProviderEntitiesProcessor} from '@alithya-oss/plugin-aws-apps-backend';

export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);

builder.addProcessor(new ScaffolderEntitiesProcessor());

+ // Custom processors
Expand All @@ -114,7 +115,7 @@ export default async function createPlugin(

### Permission Framework Policy

The OPA on AWS backend plugin leverages the [Backstage permissions framework](https://backstage.io/docs/permissions/overview) to contribute a permission decision for access to audit entries. If you would like to implement a policy for your Backstage instance to control access to audit entries you will start with the [Permission framework getting started documentation](https://backstage.io/docs/permissions/getting-started) to set up the base framework.
The OPA on AWS backend plugin leverages the [Backstage permissions framework](https://backstage.io/docs/permissions/overview) to contribute a permission decision for access to audit entries. If you would like to implement a policy for your Backstage instance to control access to audit entries you will start with the [Permission framework getting started documentation](https://backstage.io/docs/permissions/getting-started) to set up the base framework.
With the framework in place, you can leverage the `readOpaAppAuditPermission` permission in your policy definition to restrict access to audit entries.

```ts
Expand All @@ -133,8 +134,8 @@ export class permissionPolicy implements PermissionPolicy {
const VILLIANS_GROUP = stringifyEntityRef({ kind: 'Group', namespace: DEFAULT_NAMESPACE, name: "villians" });
const ownershipGroups = user?.identity.ownershipEntityRefs || [];
if (
isPermission(request.permission, readOpaAppAuditPermission) &&
ownershipGroups.length === 1 &&
isPermission(request.permission, readOpaAppAuditPermission) &&
ownershipGroups.length === 1 &&
ownershipGroups.includes(VILLIANS_GROUP)
) {
return { result: AuthorizationResult.DENY };
Expand All @@ -146,4 +147,4 @@ export class permissionPolicy implements PermissionPolicy {

```

Additional permission decisions and resources are planned for future releases.
Additional permission decisions and resources are planned for future releases.
286 changes: 286 additions & 0 deletions backstage-plugins/plugins/aws-apps-backend/api-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
## API Report File for "@aws/plugin-aws-apps-backend-for-backstage"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AuthService } from '@backstage/backend-plugin-api';
import { AwsCredentialIdentity } from '@aws-sdk/types';
import { AWSServiceResources } from '@aws/plugin-aws-apps-common-for-backstage';
import { BackendFeatureCompat } from '@backstage/backend-plugin-api';
import { BackstageUserInfo } from '@backstage/backend-plugin-api';
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { CreateBucketCommandOutput } from '@aws-sdk/client-s3';
import { CreateSecretCommandOutput } from '@aws-sdk/client-secrets-manager';
import { CreateStackCommandOutput } from '@aws-sdk/client-cloudformation';
import { DeleteStackCommandOutput } from '@aws-sdk/client-cloudformation';
import { DescribeClusterCommandOutput } from '@aws-sdk/client-eks';
import { DescribeLogGroupsCommandOutput } from '@aws-sdk/client-cloudwatch-logs';
import { DescribeLogStreamsCommandOutput } from '@aws-sdk/client-cloudwatch-logs';
import { DescribeStackEventsCommandOutput } from '@aws-sdk/client-cloudformation';
import { DescribeStacksCommandOutput } from '@aws-sdk/client-cloudformation';
import { DescribeTaskDefinitionCommandOutput } from '@aws-sdk/client-ecs';
import { DescribeTasksCommandOutput } from '@aws-sdk/client-ecs';
import express from 'express';
import { GetLogEventsCommandOutput } from '@aws-sdk/client-cloudwatch-logs';
import { GetLogRecordCommandOutput } from '@aws-sdk/client-cloudwatch-logs';
import { GetParameterCommandOutput } from '@aws-sdk/client-ssm';
import { GetSecretValueCommandOutput } from '@aws-sdk/client-secrets-manager';
import { HeadObjectCommandOutput } from '@aws-sdk/client-s3';
import { HttpAuthService } from '@backstage/backend-plugin-api';
import { InvokeCommandOutput } from '@aws-sdk/client-lambda';
import { ListGroupResourcesCommandOutput } from '@aws-sdk/client-resource-groups';
import { ListTasksCommandOutput } from '@aws-sdk/client-ecs';
import { LoggerService } from '@backstage/backend-plugin-api';
import { Parameter } from '@aws-sdk/client-cloudformation';
import { PermissionsService } from '@backstage/backend-plugin-api';
import { PutItemCommandOutput } from '@aws-sdk/client-dynamodb';
import { PutSecretValueCommandOutput } from '@aws-sdk/client-secrets-manager';
import { RegisterTaskDefinitionCommandOutput } from '@aws-sdk/client-ecs';
import { ScanCommandOutput } from '@aws-sdk/client-dynamodb';
import { TaskDefinition } from '@aws-sdk/client-ecs';
import { UpdateServiceCommandOutput } from '@aws-sdk/client-ecs';
import { UpdateStackCommandOutput } from '@aws-sdk/client-cloudformation';
import { UserEntity } from '@backstage/catalog-model';
import { UserInfoService } from '@backstage/backend-plugin-api';

// @public (undocumented)
export class AwsAppsApi {
constructor(
config: Config,
logger: LoggerService,
awsRegion: string,
awsAccount: string,
);
// (undocumented)
callLambda(functionName: string, body: string): Promise<InvokeCommandOutput>;
// (undocumented)
createS3Bucket(
bucketName: string,
tags?: {
Key: string;
Value: string | number | boolean;
}[],
): Promise<CreateBucketCommandOutput>;
// (undocumented)
createSecret(
secretName: string,
description: string,
tags?: {
Key: string;
Value: string | number | boolean;
}[],
): Promise<CreateSecretCommandOutput>;
createStack(
componentName: string,
stackName: string,
s3BucketName: string,
cfFileName: string,
providerName: string,
parameters?: Parameter[],
): Promise<CreateStackCommandOutput>;
deleteStack(stackName: string): Promise<DeleteStackCommandOutput>;
describeClusterTasks(
clusterName: string,
taskArns: string[],
): Promise<DescribeTasksCommandOutput>;
describeStack(stackName: string): Promise<DescribeStacksCommandOutput>;
describeStackEvents(
stackName: string,
): Promise<DescribeStackEventsCommandOutput>;
describeTaskDefinition(
taskDefinitionArn: string,
): Promise<DescribeTaskDefinitionCommandOutput>;
// (undocumented)
doesS3FileExist(
bucketName: string,
fileName: string,
): Promise<HeadObjectCommandOutput>;
getCategorizedResources(resourceGroup: string): Promise<AWSServiceResources>;
// (undocumented)
getDynamodbTable(
tableName: string,
appName: string,
timeFrame: number,
): Promise<ScanCommandOutput>;
getEcsServiceTask(
clusterName: string,
serviceName: string,
): Promise<ListTasksCommandOutput>;
getEksCluster(clusterName: string): Promise<DescribeClusterCommandOutput>;
getLogGroupEvents(
logGroupName: string,
logStreamName: string,
startFromHead?: boolean,
): Promise<GetLogEventsCommandOutput>;
getLogGroups(logPrefix: string): Promise<DescribeLogGroupsCommandOutput>;
// (undocumented)
getLogRecord(logRecordPointer: string): Promise<GetLogRecordCommandOutput>;
getLogStreams(logGroupName: string): Promise<DescribeLogStreamsCommandOutput>;
getResourceGroupResources(
resourceGroupName: string,
): Promise<ListGroupResourcesCommandOutput>;
getSecretValue(secretArn: string): Promise<GetSecretValueCommandOutput>;
getSSMParameter(ssmParamName: string): Promise<GetParameterCommandOutput>;
// (undocumented)
putDynamodbTableData(data: DynamoDBTableData): Promise<PutItemCommandOutput>;
// (undocumented)
putSecretValue(
secretArn: string,
secretValue: string,
): Promise<PutSecretValueCommandOutput>;
registerTaskDefinition(
taskDefinition: TaskDefinition,
): Promise<RegisterTaskDefinitionCommandOutput>;
updateServiceTask(
clusterName: string,
serviceName: string,
taskDefinition: string,
restart: boolean,
numberOfTasks?: number | undefined,
): Promise<UpdateServiceCommandOutput>;
updateStack(
componentName: string,
stackName: string,
s3BucketName: string,
cfFileName: string,
providerName: string,
parameters?: Parameter[],
): Promise<UpdateStackCommandOutput>;
}

// @public
const awsAppsPlugin: BackendFeatureCompat;
export default awsAppsPlugin;

// @public (undocumented)
export interface AwsAuditRequest {
// (undocumented)
actionName: string;
// (undocumented)
actionType: string;
// (undocumented)
apiClient: AwsAppsApi;
// (undocumented)
appName: string;
// (undocumented)
awsAccount: string;
// (undocumented)
awsRegion: string;
// (undocumented)
envProviderName: string;
// (undocumented)
envProviderPrefix: string;
// (undocumented)
logger: LoggerService;
// (undocumented)
message?: string;
// (undocumented)
owner: string;
// (undocumented)
requestArgs?: string;
// (undocumented)
requester: string;
// (undocumented)
roleArn: string;
// (undocumented)
status: string;
}

// @public (undocumented)
export interface AwsAuditResponse {
// (undocumented)
message: string;
// (undocumented)
status: string;
}

// @public (undocumented)
export interface AwsAuthResponse {
// (undocumented)
account: string;
// (undocumented)
credentials: AwsCredentialIdentity;
// (undocumented)
owner?: string;
// (undocumented)
region: string;
// (undocumented)
requester: string;
// (undocumented)
roleArn: string;
}

// @public (undocumented)
export function createAuditRecord({
envProviderPrefix,
envProviderName,
appName,
apiClient,
roleArn,
awsRegion,
awsAccount,
requester,
owner,
actionType,
actionName,
requestArgs,
status,
message,
}: AwsAuditRequest): Promise<AwsAuditResponse>;

// @public (undocumented)
export function createRouter(options: RouterOptions): Promise<express.Router>;

// @public (undocumented)
export type DynamoDBTableData = {
tableName: string;
recordId: string;
origin: string;
prefix: string;
appName: string;
environmentProviderName: string;
actionType: string;
name: string;
initiatedBy: string;
owner: string;
assumedRole: string;
targetAccount: string;
targetRegion: string;
request: string;
status: string;
message: string;
};

// @public (undocumented)
export function getAWScreds(
config: Config,
logger: LoggerService,
accountId: string,
region: string,
prefix: string,
providerName: string,
user?: UserEntity,
userIdentity?: BackstageUserInfo,
): Promise<AwsAuthResponse>;

// @public (undocumented)
export interface RouterOptions {
// (undocumented)
auth: AuthService;
// (undocumented)
catalogApi: CatalogApi;
// (undocumented)
config: Config;
// (undocumented)
httpAuth: HttpAuthService;
// (undocumented)
logger: LoggerService;
// (undocumented)
permissions: PermissionsService;
// (undocumented)
userInfo: UserInfoService;
}

// (No @packageDocumentation comment for this package)
```
4 changes: 3 additions & 1 deletion backstage-plugins/plugins/aws-apps-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"backstage": {
"role": "backend-plugin",
"pluginId": "aws-apps-backend",
"pluginPackages": ["@backstage/plugin-catalog"]
"pluginPackages": [
"@aws/plugin-aws-apps-backend-for-backstage"
]
},
"scripts": {
"start": "backstage-cli package start",
Expand Down
Loading

0 comments on commit 67c2f51

Please sign in to comment.