Skip to content

Commit 31916b7

Browse files
authored
docs(cost-management): update documentation to reflect security PRs (#2679)
* docs(cost-management): update documentation to reflect security PRs #2616-#2620 - Remove outdated proxy config from workspace README (PR #2616 moved data fetching server-side) - Fix broken link typo in frontend plugin README - Update backend README: clarify mixed dot/slash permission format, add missing endpoints (access, apply-recommendation), add audit logging section (PR #2619) - Clarify permission name format in docs/rbac.md intro (dot for plugin-level, slash for cluster/project per PR #2620) - Update ADR 0002 status to Accepted with implementation notes documenting the backend gateway pattern (PRs #2616, #2618, #2619) Made-with: Cursor * docs(cost-management): address Qodo review findings - Fix inaccurate sanitization claim in ADR 0002: changed to "validated for presence and type" to match actual implementation - Remove non-existent `cost_access_check` audit action from backend README — the code emits `access_check` for both access endpoints Made-with: Cursor
1 parent 8365c70 commit 31916b7

5 files changed

Lines changed: 57 additions & 19 deletions

File tree

workspaces/cost-management/README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,6 @@ This method requires vanilla backstage to be used:
7272
```yaml
7373
# app-config.yaml
7474

75-
proxy:
76-
endpoints:
77-
'/cost-management/v1':
78-
target: https://console.redhat.com/api/cost-management/v1
79-
allowedHeaders: ['Authorization']
80-
# See: https://backstage.io/docs/releases/v1.28.0/#breaking-proxy-backend-plugin-protected-by-default
81-
credentials: dangerously-allow-unauthenticated
82-
8375
# Replace `${RHHCC_SA_CLIENT_ID}` and `${RHHCC_SA_CLIENT_SECRET}` with the service account credentials.
8476
costManagement:
8577
clientId: ${RHHCC_SA_CLIENT_ID}

workspaces/cost-management/docs/adrs/0002-applying-optimizations-using-the-orchestrator-plugin.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,27 @@
22

33
## Status
44

5-
Proposed
5+
Accepted
6+
7+
### Implementation Notes (PRs #2616, #2618, #2619)
8+
9+
The core decision to use the Orchestrator plugin was implemented as proposed.
10+
However, based on a security threat model review (FLPATH-3503), the architecture
11+
was hardened beyond the original design:
12+
13+
- **Backend gateway pattern**: The frontend no longer calls the Orchestrator API
14+
directly. Instead, requests route through the cost-management backend
15+
(`POST /api/cost-management/apply-recommendation`), which validates inputs,
16+
checks `ros.apply` permission, and forwards to Orchestrator using
17+
service-to-service authentication.
18+
- **Input validation**: `resourceType` is validated against a server-side
19+
allowlist; all input fields are validated for presence and type.
20+
- **Audit logging**: Every apply action is logged with user identity, cluster,
21+
namespace, workload, and outcome.
22+
- **Confirmation dialog**: The UI presents a confirmation step before applying.
23+
24+
The architecture diagram below reflects the original proposed flow.
25+
See [docs/rbac.md](../rbac.md) for current RBAC details.
626

727
## Context
828

workspaces/cost-management/docs/rbac.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ The Cost Management plugin protects its backend endpoints with the builtin permi
22

33
The Cost Management plugin consists of two main sections, each with its own set of permissions:
44

5-
- **Optimizations**: Uses permissions starting with `ros.`
6-
- **OpenShift**: Uses permissions starting with `cost.`
5+
- **Optimizations**: Uses `ros.plugin` and `ros.apply` (dot) for plugin-level access, and `ros/CLUSTER` / `ros/CLUSTER/PROJECT` (slash) for cluster/project-level access
6+
- **OpenShift**: Uses `cost.plugin` (dot) for plugin-level access, and `cost/CLUSTER` / `cost/CLUSTER/PROJECT` (slash) for cluster/project-level access
77

88
### How it works
99

1010
When a frontend request arrives at `/api/cost-management/proxy/*`, the backend:
1111

1212
1. Authenticates the user via Backstage `httpAuth`
13-
2. Evaluates the user's permissions against the `ros.*` or `cost.*` policy
13+
2. Evaluates the user's permissions against the `ros.*` / `ros/…` or `cost.*` / `cost/…` policies
1414
3. Determines the authorized list of clusters and projects
1515
4. Strips any client-supplied cluster/project filter parameters
1616
5. Injects the server-authorized filters before forwarding to the upstream API
@@ -62,7 +62,7 @@ The user is permitted to do an action if either the generic permission or the sp
6262

6363
To get started with policies, we recommend defining roles and assigning them to groups or users in a dedicated CSV file.
6464

65-
Here is an example policy file that includes permissions for both Optimizations (`ros.`) and OpenShift (`cost.`) sections of the Cost Management plugin:
65+
Here is an example policy file that includes permissions for both Optimizations (`ros.plugin`, `ros.apply`, `ros/…`) and OpenShift (`cost.plugin`, `cost/…`) sections of the Cost Management plugin:
6666

6767
```csv
6868
####

workspaces/cost-management/plugins/cost-management-backend/README.md

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ When a request arrives the handler:
1212

1313
1. **Authenticates** the caller via Backstage `httpAuth` (requires a valid user session).
1414
2. **Checks permissions** through the Backstage permission framework using the
15-
`ros.*` and `cost.*` permission sets (see [docs/rbac.md](../../docs/rbac.md)).
15+
`ros.*` / `ros/…` and `cost.*` / `cost/…` permission sets (see [docs/rbac.md](../../docs/rbac.md)).
1616
3. **Obtains an SSO token** internally via the OAuth2 `client_credentials` grant
1717
using the `costManagement.clientId` / `costManagement.clientSecret` from
1818
`app-config`.
@@ -25,10 +25,13 @@ When a request arrives the handler:
2525

2626
### Endpoints
2727

28-
| Path | Auth | Description |
29-
| ---------------------------------- | ------------- | ----------------------------------- |
30-
| `GET /api/cost-management/health` | None | Health check |
31-
| `ALL /api/cost-management/proxy/*` | `user-cookie` | Secure proxy to Cost Management API |
28+
| Path | Auth | Description |
29+
| ------------------------------------------------- | ------------- | ---------------------------------------------------------- |
30+
| `GET /api/cost-management/health` | None | Health check |
31+
| `GET /api/cost-management/proxy/*` | `user-cookie` | Secure proxy to Cost Management API |
32+
| `GET /api/cost-management/access` | `user-cookie` | Check user's Optimizations RBAC access |
33+
| `GET /api/cost-management/access/cost-management` | `user-cookie` | Check user's OpenShift cost RBAC access |
34+
| `POST /api/cost-management/apply-recommendation` | `user-cookie` | Apply optimization via Orchestrator (requires `ros.apply`) |
3235

3336
### Configuration
3437

@@ -42,6 +45,29 @@ costManagement:
4245
No `proxy` block with `dangerously-allow-unauthenticated` is needed — the
4346
backend plugin handles upstream communication directly.
4447

48+
### Audit Logging
49+
50+
All backend endpoints emit structured audit log entries with user identity via Backstage's logger. Each entry includes:
51+
52+
- **`actor`** — the authenticated user's entity ref (e.g., `user:default/admin`)
53+
- **`action`** — the operation performed (`data_access`, `apply_recommendation`, `access_check`)
54+
- **`decision`** — the RBAC outcome (`ALLOW` or `DENY`)
55+
- **`resource`** — the upstream API path accessed
56+
- **`filters`** — the server-injected cluster/project filters (for proxy requests)
57+
58+
Example log entry:
59+
60+
```json
61+
{
62+
"audit": true,
63+
"actor": "user:default/admin",
64+
"action": "data_access",
65+
"resource": "recommendations/openshift",
66+
"decision": "ALLOW",
67+
"filters": { "clusters": ["cluster73"], "projects": ["rhdh"] }
68+
}
69+
```
70+
4571
## Getting started
4672

4773
The plugin has been added to the example app in this workspace, meaning you'll be able to access it by running `yarn

workspaces/cost-management/plugins/cost-management/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,4 @@ The procedure involves the following steps:
149149

150150
- [License Apache 2.0](../../LICENSE.md)
151151
- [DCO](../../DCO.md)
152-
- Find more details in the [Resource Optimization back-end](../cost-mangament-backend/README.md) part of this plugin.
152+
- Find more details in the [Resource Optimization back-end](../cost-management-backend/README.md) part of this plugin.

0 commit comments

Comments
 (0)