diff --git a/platform/control-plane/query-engines/engine-detail.md b/platform/control-plane/query-engines/engine-detail.md index 3f0a166..782bca7 100644 --- a/platform/control-plane/query-engines/engine-detail.md +++ b/platform/control-plane/query-engines/engine-detail.md @@ -14,6 +14,48 @@ The header shows the engine name, its namespace (`egret-engines`), the image, th A provisioned StarRocks engine that a policy profile names shows, above the tabs, the warning that unless the engine runs `egret-starrocks-fe` `4.1.7-egret.4` or later its StarRocks lets users past the profile: official images serve the Iceberg metadata tables without asking Ranger and apply no mask or row filter to prepared statements, and official and earlier patched images apply none through the HTTP SQL API or Arrow Flight SQL, let anyone read object storage through `FILES()` and let a masked or filtered user store a table's rows in the clear in a materialized view, and images before `4.1.7-egret.4` show a filtered user the statistics of hidden rows through `EXPLAIN COSTS` and let anyone reach object storage with the cluster's own identity through `BROKER LOAD`, `EXPORT` and `SELECT INTO OUTFILE` without keys (since platform #822, widened in #826, #827 and #832; see [External engine](#overview-1), KI-340, KI-345 to KI-348, KI-350, KI-351). It is not shown while the profile list loads, or to a viewer who cannot read it. It is not shown for a provisioned engine on Egret's patched image `ghcr.io/egret-data/egret-starrocks-fe` `4.1.7-egret.4` or later (the default for new engines since platform #832), which fixes all of them; it is shown for an engine still on `4.1.7-egret.1` or `4.1.7-egret.3`; an external engine always shows it, because Egret does not know its image. +### Security warnings on a StarRocks engine + +Two warnings can appear above the tabs of a StarRocks engine (since platform #847). + +#### Not governed + +With Ranger on, a StarRocks engine that is bound to no policy profile shows: + +*Not governed: this engine is bound to no policy profile, so egret's grants, masks and row filters do not reach it. Bind it to a StarRocks profile in its settings.* + +The engine list shows the same engine with a **Not governed** badge. To fix it, bind a StarRocks profile in the engine's settings (`authzServiceId` on `PUT /api/control-plane/engines/{id}`). With Ranger off no engine is governed, so the warning is not shown. Add engine now offers a profile for StarRocks (see [Provision an engine](./provision.md#create-an-engine)), so a new engine is bound from the start. + +#### Security upgrade needed + +A provisioned StarRocks engine that lacks egret's security fixes shows the warning *This engine needs a security upgrade* with one sentence per reason, and an **Upgrade security** button. The older warning about metadata tables is not shown while this one is. + +![The engine page of a StarRocks engine with the security upgrade warning and the Upgrade security button](/img/platform/admin/cp-engine-security-upgrade-warning.png) + +| Reason | API value | What it means | +|---|---|---| +| Image | `unpatchedImage` | The FE image is not `egret-starrocks-fe` `4.1.7-egret.4` or later (or a mirror of one). StarRocks then lets users past the engine's profile: Iceberg metadata tables, prepared statements, the HTTP SQL API, `FILES()` and more (KI-340, KI-345). | +| Root password | `noRootPassword` | The FE `root` account has no password: anyone who reaches port 9030 can log in as `root`. Engines created before the root-password change have none. | +| Not applied | `notApplied` | The last security upgrade stored its changes but did not finish applying them, for example because the apply failed. | + +The warning needs the `engine:update` permission to act on; without it the page says *Upgrading it needs the engine:update permission.* An external engine never shows it: egret does not change an external engine's image. + +**Upgrade security** opens a confirmation dialog, *Upgrade this engine's security?*, that lists what will change: + +- `FE image: → `, and *The CN image moves to the same release.* When egret has no FE image recorded for the engine, `` reads *the current image (not recorded)*; +- *root password will be set*, when the engine has none; +- *The stored changes are applied again.*, for `notApplied`. + +![The Upgrade this engine's security dialog listing the FE image change and the root password](/img/platform/admin/cp-engine-security-upgrade-dialog.png) + +It also warns that the FE and CN pods restart, and that a client outside egret that logs in as `root` with no password is refused afterwards. When a root password will be set, it adds *egret's own admin statements on this engine fail until the FE takes the new password, usually within a minute.* Press **Upgrade security** in the dialog to run it. The engine is applied on every run, so running it again is safe. + +If the FE image is of a newer release than egret's default fork image (an official 4.1.8, for example), egret does not change the image, because StarRocks FE metadata cannot be downgraded. The root password is still set, and the dialog and the result say: + +*No patched image for release* `` *yet: the image was not changed.* + +The `unpatchedImage` reason then stays. If the apply fails, the page shows the error, the engine keeps the **Security upgrade needed** badge with the reason *Not applied*, and you run **Upgrade security** again. Any later successful apply of the engine, a settings save for example, also clears that reason. + ### Overview A strip shows the **phase**, **pods** (ready/total; **containers** on the Docker provisioner), **replicas** (for example *Coord 1 · Worker 0*, the counts the engine runs with), **catalogs**, **workspaces** and **auth** (*JWT auth*, impersonation, password auth or no delegation). For StarRocks, **FE replicas** and **CN replicas** replace *replicas*. diff --git a/platform/control-plane/query-engines/index.md b/platform/control-plane/query-engines/index.md index e4bc8b4..c3b6c24 100644 --- a/platform/control-plane/query-engines/index.md +++ b/platform/control-plane/query-engines/index.md @@ -24,6 +24,13 @@ A summary strip counts **engines**, how many are **running** and **not running** A provisioned Trino engine can also carry a **Re-apply needed** badge (the API field is `securityReapplyNeeded` on the engine). Its tooltip says *This engine still accepts unauthenticated requests on port 8080. Re-apply it to turn on authentication.* The engine was applied before platform #842 and still runs the old configuration. The badge clears after the next successful apply. An engine with JWT only and no password users never shows it. See [Provision an engine](./provision.md#security--exposure). +Since platform #847 a StarRocks engine can carry two more badges beside the status: + +- **Security upgrade needed** for a provisioned engine on an image or login that lacks egret's security fixes. Open the engine and press **Upgrade security** (see [Engine detail](./engine-detail.md#security-upgrade-needed)). +- **Not governed** for a StarRocks engine with no policy profile while Ranger is on (see [Engine detail](./engine-detail.md#not-governed)). + +![The Engines list with the Security upgrade needed and Not governed badges on StarRocks engines](/img/platform/admin/cp-engines-security-upgrade-badge.png) + ## Add an engine Two buttons sit at the top right: diff --git a/platform/control-plane/query-engines/provision.md b/platform/control-plane/query-engines/provision.md index babd973..f52d579 100644 --- a/platform/control-plane/query-engines/provision.md +++ b/platform/control-plane/query-engines/provision.md @@ -18,6 +18,10 @@ You need **engine:create** and **engine:provision**. Egret runs the engine where 4. Under **Configuration**, adjust what you need. Leave a field blank to use the default. 5. Press **Create engine**. +For a StarRocks engine the form has a **Policy profile** select (since platform #847; fixes KI-336). It lists the StarRocks profiles that no engine uses yet. Its default, **Create a profile named after the engine**, makes a new StarRocks profile with the engine's name when Ranger is on; with Ranger off nothing is bound. Without a profile, egret's grants, masks and row filters do not reach the engine, and with Ranger on the engine shows **Not governed**. If a profile with the engine's name already exists and is of another type, or another engine uses it, the create is refused (409). + +![The Add engine form for StarRocks with the Policy profile select open](/img/platform/admin/cp-engine-create-starrocks-profile.png) + The engine page opens with the status **Provisioning**. It turns **Running** when the coordinator answers. Measured on the demo stack: 72 seconds for the first engine on a Docker host that already had the image, and 38 seconds for a restart. :::tip Single-node engine diff --git a/platform/deploy-operate/upgrade.md b/platform/deploy-operate/upgrade.md index 4aa89a3..f894d88 100644 --- a/platform/deploy-operate/upgrade.md +++ b/platform/deploy-operate/upgrade.md @@ -678,6 +678,14 @@ default, and mounts the OPA surface with no configuration change, so a profile c - **Tag-service warning:** a Trino profile with no usable tag service now carries `no_tag_service_warning` on the `PATCH …/mechanism` answer and on each explain profile, and the console shows it. See [Trino on OPA](../data-governance/engines-and-policy/trino-on-opa.md#through-the-api). - **Known issues** on OPA engines: KI-352, KI-353 and KI-354 in [Known issues](../known-issues.md). +### StarRocks engine posture (platform #847) + +Nothing changes on its own when you upgrade: no migration rewrites an engine's image or sets a password, because the FE restarts. Migration 0217 adds one flag, which records a security upgrade that was stored but not applied. + +- **After the upgrade, open Control Plane → Infrastructure → Engines and look for Security upgrade needed.** An engine created before platform #826 and #832 can still run an official StarRocks FE or an egret image before `4.1.7-egret.4`, or have a passwordless FE `root`. Open each one and press **Upgrade security** (or `POST /api/control-plane/engines/{id}/security-upgrade`) in a quiet window: the FE and CN pods restart. A client outside egret that logs in as `root` with no password is refused afterwards. See [Engine detail](../control-plane/query-engines/engine-detail.md#security-upgrade-needed). +- Look for **Not governed** too: with Ranger on, a StarRocks engine with no policy profile enforces none of egret's policies. Bind a profile in its settings. +- **API change:** the engine adds `notGoverned`, `securityUpgradeNeeded`, `securityUpgradeReasons`, `securityUpgradeFeImage` and `securityUpgradeBlockedRelease`, and a new route `POST /api/control-plane/engines/{id}/security-upgrade`. See [Engines API](../reference/rest-api/control-plane-engines.md). + ## Further reading - [Backup and restore](./backup-restore) diff --git a/platform/known-issues.md b/platform/known-issues.md index be9de2d..1467f07 100644 --- a/platform/known-issues.md +++ b/platform/known-issues.md @@ -386,16 +386,16 @@ Collected while writing the detailed pages for each area (October 2026), checked | KI-333 | Engines / Trino | Bug | S2 | A provisioned Trino engine whose **Per-user delegation** is **None (open engine)** gets none of the per-user baseline policies egret writes for a delegating engine (each user may become themself, run queries, and set `query_max_run_time` on their session). The Ranger plugin checks them on every session, so every query fails with *Principal X cannot become user X*. In the source, egret seeds the baseline only for an engine that delegates (JWT or impersonate). *Workaround:* use **Auto**, **JWT** or **Impersonate**. (Found in the live check of platform #818.) | | KI-334 | Governance / Data | Bug | S3 | The engines in the Governance **Data** page's object header can show a running engine as `provisioning`: in the live check of platform #818 the Trino engine `p7jwt` showed `provisioning` there, while the engine API said it was running. The header shows the `status` the governance browse API answers for each engine attached to the catalog, read from the stored engine row; why that row still said `provisioning` is not confirmed (seen with the Docker provisioner). Nothing else depends on the label. (Found in the live check of platform #818.) | | KI-335 | Engines / StarRocks | Bug | S2 | On Kubernetes, a provisioned StarRocks engine can come up with its compute node (CN) in a crash loop. The CN spec carries no root password, while the `{name}-initpwd` Job sets the FE's root password first: when the CN starts after that, for example on a node that does not have the CN image cached yet and has to pull it, it cannot log in to the FE (`ERROR 1045`), queries fail with `ERROR 5904` *Warehouse default_warehouse is not available*, the engine stays `provisioning`, and the catalog attachment still says *applied*. *Steps:* provision a StarRocks engine on a fresh node without `starrocks/cn-ubuntu:4.1.6` cached. *Workaround:* give the CN the root password from the `{name}-initpwd-secret` Secret as `MYSQL_PWD` (`cnEnvVars` of the StarRocksCluster resource, `valueFrom.secretKeyRef`, key `password`), or pull the CN image onto the node before provisioning. (Found in the Wave 4 re-check on kind, StarRocks 4.1.6.) | -| KI-336 | Engines / StarRocks | Gap | S2 | A StarRocks engine provisioned without a policy profile (no `authzServiceId`) stays without one, even when Ranger is on: egret creates a profile automatically only for a Trino engine, and **Add engine** has no profile picker for StarRocks. Such an engine is not governed by egret's policies, and in the re-check the admin saw no egret catalog on it and nothing said why. *Workaround:* create a StarRocks profile (**Engine & policy → New profile**, type **StarRocks**), then pass its id as `authzServiceId` when you create the engine through `POST /api/control-plane/engines`, or bind it afterwards with `PUT /api/control-plane/engines/{id}`. (Found in the Wave 4 re-check on kind.) | +| KI-336 | Engines / StarRocks | Gap | S2 | A StarRocks engine provisioned without a policy profile (no `authzServiceId`) stays without one, even when Ranger is on: egret creates a profile automatically only for a Trino engine, and **Add engine** has no profile picker for StarRocks. Such an engine is not governed by egret's policies, and in the re-check the admin saw no egret catalog on it and nothing said why. *Workaround:* create a StarRocks profile (**Engine & policy → New profile**, type **StarRocks**), then pass its id as `authzServiceId` when you create the engine through `POST /api/control-plane/engines`, or bind it afterwards with `PUT /api/control-plane/engines/{id}`. (Found in the Wave 4 re-check on kind.) *Status:* fixed in platform #847: with Ranger on, **Add engine** for StarRocks has a **Policy profile** select whose default creates a profile named after the engine, and an engine left without a profile shows **Not governed** (badge in the list, warning on its page). With Ranger off nothing is bound. | | KI-337 | Governance / Masks and row filters | Bug | S2 | **Fixed in Egret's patched StarRocks image `4.1.7-egret.3` (platform #827); open upstream.** On StarRocks (4.1.6 and later, an upstream StarRocks bug), when a row filter applies to a user, `SELECT *` on an Iceberg table returns two extra columns: `_file` (the object-store path of the data file) and `_pos` (the row's position). Users without a filter get the table's own columns; Trino is not affected. So the result has a different shape per user: `INSERT … SELECT *`, and datasets or charts built on `SELECT *`, can break for a filtered user, and an Iceberg view whose stored SQL is a literal `SELECT * FROM t` fails for that user with *Index N out of bounds*. With a mask on the table the query is refused instead (KI-304). The path is not a new disclosure: any reader can already get it with `SELECT _file` or from the table's `$files` metadata table (KI-340). *Workaround:* list the columns (`SELECT id, region, email …`); a view created from StarRocks stores the expanded column list and is not affected. Severity S2: wrong result shape with a workaround, no value the user could not read otherwise. (Found in the Wave 4 re-check; the cause is StarRocks' row-policy rewrite, which turns the hidden columns into visible ones.) | | KI-338 | SQL Lab / StarRocks | Bug | S2 | In SQL Lab, the schema explorer of a StarRocks connection lists StarRocks' default catalog (`_statistics_`, `information_schema`, `sys`), not the egret catalog the engine serves, so the tables you can query are not shown there. Queries work. *Workaround:* write fully qualified names (`..`). Severity S2: the explorer shows the wrong catalog, and a workaround exists. (Found in the Wave 4 re-check on kind.) | | KI-339 | Workspaces | UX | S3 | A member whose only grant in a workspace is a deny gets the 403 page there (platform #811), and on that page the workspace switcher is empty, so it offers no way to another workspace. *Workaround:* open another workspace from its address or from the home page. (Found in the Wave 4 re-check.) | -| KI-340 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image (platform #826; `4.1.7-egret.3` from platform #827 also closes KI-346 to KI-349); open upstream.** Official StarRocks images serve the Iceberg metadata tables of a table (`orders$files`, `$partitions`, `$manifests`, `$snapshots`, `$history`, `$refs`, `$metadata_log_entries`, …) to any login without asking Ranger. So a StarRocks user reads, regardless of grants, masks and row filters: per-column minimum and maximum values (for example the first and last e-mail address of a masked column), partition values, row counts and data file paths, even when the same user is refused `SELECT` on the table. No egret policy can stop it: StarRocks 4.1.x never checks a metadata table against Ranger and has no setting that turns them off. egret does not filter SQL text, since users connect to StarRocks directly. *Workaround:* do not give StarRocks logins to people who must not see the values; serve masked or filtered data to them through Trino. Since platform #822 egret says so with a warning wherever a StarRocks engine is bound to a policy profile: the Governance engine page, the Control plane engine page (provisioned, and an external engine's **Overview** and **Settings**) and **Register external engine** once a profile is picked. (Measured in the Wave 4 re-check and in the work for platform #821, StarRocks 4.1.6.) *Status:* from platform #826 new provisioned StarRocks engines run Egret's patched front end image `ghcr.io/egret-data/egret-starrocks-fe:4.1.7-egret.1` (source and patches at github.com/egret-data/egret-starrocks), on which reading a metadata table needs `SELECT` on its table and is refused to a person a mask or row filter on that table restricts. Existing engines keep their image until moved (see *Egret's patched StarRocks image* on **Engines and Ranger**); an external engine is fixed only when its operator runs the same image. The warning is no longer shown for a provisioned engine on the patched image. Reported to the StarRocks security team on 2026-11-02. | +| KI-340 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image (platform #826; `4.1.7-egret.3` from platform #827 also closes KI-346 to KI-349); open upstream.** Official StarRocks images serve the Iceberg metadata tables of a table (`orders$files`, `$partitions`, `$manifests`, `$snapshots`, `$history`, `$refs`, `$metadata_log_entries`, …) to any login without asking Ranger. So a StarRocks user reads, regardless of grants, masks and row filters: per-column minimum and maximum values (for example the first and last e-mail address of a masked column), partition values, row counts and data file paths, even when the same user is refused `SELECT` on the table. No egret policy can stop it: StarRocks 4.1.x never checks a metadata table against Ranger and has no setting that turns them off. egret does not filter SQL text, since users connect to StarRocks directly. *Workaround:* do not give StarRocks logins to people who must not see the values; serve masked or filtered data to them through Trino. Since platform #822 egret says so with a warning wherever a StarRocks engine is bound to a policy profile: the Governance engine page, the Control plane engine page (provisioned, and an external engine's **Overview** and **Settings**) and **Register external engine** once a profile is picked. (Measured in the Wave 4 re-check and in the work for platform #821, StarRocks 4.1.6.) *Status:* from platform #826 new provisioned StarRocks engines run Egret's patched front end image `ghcr.io/egret-data/egret-starrocks-fe:4.1.7-egret.1` (source and patches at github.com/egret-data/egret-starrocks), on which reading a metadata table needs `SELECT` on its table and is refused to a person a mask or row filter on that table restricts. Existing engines keep their image until moved (see *Egret's patched StarRocks image* on **Engines and Ranger**); an external engine is fixed only when its operator runs the same image. The warning is no longer shown for a provisioned engine on the patched image. Reported to the StarRocks security team on 2026-11-02. *Status (November 2026):* for a provisioned engine on an older image, **Upgrade security** on the engine page (since platform #847) moves the FE and CN to the patched image; the engine list shows **Security upgrade needed** until then. | | KI-341 | Governance / Masks and row filters | Security | S2 | On Trino, the Iceberg metadata tables of a table (`orders$files`, `$entries`, `$manifests`, `$partitions`, `$history`, …) did not follow masks, row filters or denies: the Ranger plugin checks `orders$files` as a table named `orders$files`, which a grant on `table: *` matches and a mask, row filter or deny on `table: orders` does not. A masked user read the masked column's minimum and maximum from `$files`, a row-filtered user read the bounds, partition values and counts of the rows filtered away, and a user denied `orders` read its metadata tables through a broader schema grant. Unlike StarRocks (KI-340), Trino does check `SELECT` on the metadata table. *Status:* fixed in platform #821 (November 2026): for each mask, row filter, select deny or deny-all-else of a Trino profile that egret compiles (from egret's own rules and grants, and from enabled hand-written policies in such a profile), egret also compiles a deny on that table's metadata tables (`
$*`, every one of them) for the people the policy restricts, except the people it exempts; a tag mask, tag row filter or clearance gets one per table that carries the classification. A restricted user now gets *Access Denied* on every `t$…` table of that table; users with no restriction on it read them as before. Not covered: profiles that egret's permission model does not compile. (Found in the Wave 4 work on StarRocks row filters; measured on Trino 483.) | | KI-342 | Governance / Grants | UX | S2 | A local user created in **Control plane → Users** (or through `POST /api/control-plane/users`) has no engine username (`username` is `null`) until their first query, so a grant or rule exemption that names them through the API is refused with 400: by email or id, *this is a user's email, and that user has no username yet (one is minted the first time they use an engine), so no engine can name them*; by the username they will get, *no user has this username*. So an administrator cannot prepare a new person's access through the API before that person has used an engine. The Governance grant and rule dialogs list such a user apart with **Allocate username and add** (see [Grants](./data-governance/data/grants.md)). *Workaround:* in the API, allocate the username first with `POST /api/control-plane/perm/people/users/{user_id}/username`, then name the user by the returned username; or grant a group the user belongs to. Severity S2: a task is refused for a reason the API caller does not expect, and a workaround exists. (Seen while preparing the Wave 4 checks; reproduced on platform main fb168a8f.) *Status:* fixed in platform #823 (November 2026): a user gets an engine username when they are created (**New user**, `POST /api/control-plane/users`, `egret create-admin`, first SSO sign-in), returned in `username`, so a grant can name them at once. At start egret gives a username to every existing person who has none, except one whose would-be name a stored grant principal or rule exemption already uses (left without one and logged). Service accounts still get theirs at their first token. | | KI-343 | Engines / Policy profiles | Bug | S3 | When **Add engine** fails after egret created the new Trino engine's policy profile (for example the Docker provisioner refused it because a container of that name already exists), egret deletes the tag profile it created but can leave the Trino profile behind: the delete is refused because the first compile already wrote a grant's policy into it (*profile … still holds 1 policy compiled by the SP4a permission model*; the server logs *create compensation: policy profile delete failed*). The leftover profile is listed under **Profiles without an engine** and names a tag service that no longer exists. A later create of an engine with the same name was not tried. *Workaround:* recompile the leftover profile (`POST /api/control-plane/perm/compile/{service_id}` reaps the compiled policy) and delete it. (Seen on platform main fb168a8f, Docker provisioner, while preparing the docs screenshots.) | | KI-344 | Governance / Data | UX | S3 | The Data page of a name the catalog does not have (opened by its address, for example to attach a classification before the table exists) shows **Kind unknown** and, under **Columns**, the error *The columns could not be loaded. Retry, or reload the page.*, although nothing failed: there are no columns to read. **Retry** gives the same error. The page should say that the catalog has no such table. Attaching a classification there works and warns as described in [Tagging](./data-governance/classifications/tagging.md#tag-a-table-or-a-column). (Seen while capturing the attach-notice screenshots on platform main fb168a8f.) | -| KI-345 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image (platform #826; `4.1.7-egret.3` from platform #827 also closes KI-346 to KI-349); open upstream (StarRocks #78176, fix #78368 not merged).** Official StarRocks images apply no column mask and no row filter to a statement run through `PREPARE`/`EXECUTE` or the MySQL binary protocol (`COM_STMT_PREPARE`/`COM_STMT_EXECUTE`). Egret's StarRocks connector runs queries with parameters that way, so on an official image a **Data API endpoint with parameters** on a StarRocks database returns every row and every column in the clear to a person whose masks and row filters should apply; so does any client that prepares statements on the server (for example JDBC with `useServerPrepStmts=true`). Grants are still enforced. Measured on StarRocks 4.1.6 and 4.1.7 with `access_control = ranger`: a person with a NULL mask on `phone` and the row filter `id = 1` got `[(1, NULL)]` from a plain query and all three rows with the real phone numbers through `PREPARE`, the binary protocol, and Egret's own path (the service user switching to the person with `EXECUTE AS`, then the binary protocol). *Workaround on an official image:* do not publish Data API endpoints with parameters on StarRocks databases whose readers have masks or row filters; serve them through Trino. *Status:* fixed on `ghcr.io/egret-data/egret-starrocks-fe:4.1.7-egret.1`, the default for new provisioned engines from platform #826; existing and external engines are fixed when moved to it. | +| KI-345 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image (platform #826; `4.1.7-egret.3` from platform #827 also closes KI-346 to KI-349); open upstream (StarRocks #78176, fix #78368 not merged).** Official StarRocks images apply no column mask and no row filter to a statement run through `PREPARE`/`EXECUTE` or the MySQL binary protocol (`COM_STMT_PREPARE`/`COM_STMT_EXECUTE`). Egret's StarRocks connector runs queries with parameters that way, so on an official image a **Data API endpoint with parameters** on a StarRocks database returns every row and every column in the clear to a person whose masks and row filters should apply; so does any client that prepares statements on the server (for example JDBC with `useServerPrepStmts=true`). Grants are still enforced. Measured on StarRocks 4.1.6 and 4.1.7 with `access_control = ranger`: a person with a NULL mask on `phone` and the row filter `id = 1` got `[(1, NULL)]` from a plain query and all three rows with the real phone numbers through `PREPARE`, the binary protocol, and Egret's own path (the service user switching to the person with `EXECUTE AS`, then the binary protocol). *Workaround on an official image:* do not publish Data API endpoints with parameters on StarRocks databases whose readers have masks or row filters; serve them through Trino. *Status:* fixed on `ghcr.io/egret-data/egret-starrocks-fe:4.1.7-egret.1`, the default for new provisioned engines from platform #826; existing and external engines are fixed when moved to it. *Status (November 2026):* for a provisioned engine on an older image, **Upgrade security** on the engine page (since platform #847) moves the FE and CN to the patched image; the engine list shows **Security upgrade needed** until then. | | KI-346 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image `4.1.7-egret.3` (platform #827); open upstream (StarRocks #78175, fix #78049 not merged).** StarRocks applies no column mask and no row filter to a query sent to the front end's HTTP SQL API (`POST /api/v1/catalogs//databases//sql`, port 8030) or to Arrow Flight SQL. A person under a mask and a row filter who can log in to the engine reads every row and column in the clear that way; the StarRocks Ingress that Egret can create exposes port 8030. Measured on `4.1.7-egret.2`: the masked, filtered reader got all four rows with clear emails over HTTP and over Flight, and two masked rows over MySQL. Arrow Flight SQL is off unless `arrow_flight_port` is set. *Workaround on other images:* keep port 8030 and the Flight port unreachable for people with a StarRocks login, or do not create the Ingress. | | KI-347 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image `4.1.7-egret.3` (platform #827); open upstream (StarRocks #79980, #80233).** `FILES()` has no privilege of its own on StarRocks: anyone who can log in reads and writes object storage through it, past every grant, mask and row filter, given storage keys in the statement, or with `aws.s3.use_instance_profile` through the identity of the engine's nodes. Measured: a person with no grant read every row of a table's Parquet files. On the patched image every use of `FILES()` needs the OPERATE privilege on SYSTEM (error 5211). | | KI-348 | Engines / StarRocks | Security | S1 | **Fixed in Egret's patched StarRocks image `4.1.7-egret.3` (platform #827).** A materialized view on StarRocks is filled by a refresh that reads its base tables without its creator's masks and row filters. A person under a mask and a row filter who may create a materialized view (`create materialized view` on a `default_catalog` database) builds one over the table and reads every row and column in the clear through it. Measured: such a person's view held all three rows with clear phone numbers; a plain view, `CREATE TABLE … AS SELECT` and `INSERT … SELECT` kept the policy. Egret's clearance compiler already denies `create materialized view` on profiles with clearances; path masks and row filters had no such deny. On the patched image creating a materialized view over a table where a mask or row filter applies to the creator is refused (error 5212, whichever columns the view reads), and a refresh stops once its user comes under one. | diff --git a/platform/reference/rest-api/control-plane-engines.md b/platform/reference/rest-api/control-plane-engines.md index e2930f6..dcbed2c 100644 --- a/platform/reference/rest-api/control-plane-engines.md +++ b/platform/reference/rest-api/control-plane-engines.md @@ -8,7 +8,7 @@ description: REST endpoints in the control-plane-engines group. Engine provisioning: Trino-on-Kubernetes clusters, their catalogs, native query monitor, and pod logs/exec. -This page lists 26 endpoints (OpenAPI tag `control-plane-engines`). It is generated from `openapi.json`; see the [REST API overview](../rest-api) for authentication, CSRF and error conventions. +This page lists 28 endpoints (OpenAPI tag `control-plane-engines`). It is generated from `openapi.json`; see the [REST API overview](../rest-api) for authentication, CSRF and error conventions. | Method | Path | Operation id | Notes | |---|---|---|---| @@ -25,6 +25,7 @@ This page lists 26 endpoints (OpenAPI tag `control-plane-engines`). It is genera | POST | `/api/control-plane/engines/{id}/catalogs/{cid}/test` | `test_catalog` | `POST /engines/\{id\}/catalogs/\{cid\}/test` — "test connection": runs `SHOW SCHEMAS FROM "<catalog>"` against the engine's Trino coordinator via the native-moni... | | GET | `/api/control-plane/engines/{id}/cluster` | `get_cluster` | | | PATCH | `/api/control-plane/engines/{id}/external` | `update_external_engine` | `PATCH /api/control-plane/engines/\{id\}/external`. | +| GET | `/api/control-plane/engines/{id}/opa-plugin-config` | `opa_plugin_config` | `GET /api/control-plane/engines/\{id\}/opa-plugin-config`. | | GET | `/api/control-plane/engines/{id}/pods/{pod}/exec` | `pod_exec` | `GET /engines/\{id\}/pods/\{pod\}/exec` — upgrades to a WebSocket and bridges it to a `/bin/sh` session inside the pod via `KubeClient::exec`. | | GET | `/api/control-plane/engines/{id}/pods/{pod}/logs` | `pod_logs` | `GET /engines/\{id\}/pods/\{pod\}/logs?tail=&previous=&container=` — bounded tail of a pod's logs. | | GET | `/api/control-plane/engines/{id}/pods/{pod}/logs/follow` | `pod_logs_follow` | `GET /engines/\{id\}/pods/\{pod\}/logs/follow?tail=` — upgrades to a WebSocket and streams the pod's logs as they arrive (`kubectl logs -f` equivalent), a ONE-di... | @@ -36,6 +37,7 @@ This page lists 26 endpoints (OpenAPI tag `control-plane-engines`). It is genera | POST | `/api/control-plane/engines/{id}/retire-previous-ranger-credential` | `retire_previous_ranger_credential` | `POST /engines/\{id\}/retire-previous-ranger-credential` — the operator's confirmation that an EXTERNAL engine's plugin now runs with the credential named `ins... | | POST | `/api/control-plane/engines/{id}/rotate-ranger-credential` | `rotate_ranger_credential` | `POST /engines/\{id\}/rotate-ranger-credential` — mint a new Ranger plugin credential. | | POST | `/api/control-plane/engines/{id}/rotate-secrets` | `rotate_engine_secrets` | `POST /engines/\{id\}/rotate-secrets` — mint a new Trino internal-communication secret and `egret-monitor` password and re-apply the engine (pods roll). | +| POST | `/api/control-plane/engines/{id}/security-upgrade` | `security_upgrade` | Bring a legacy StarRocks engine up to egret's security baseline | | GET | `/api/control-plane/engines/{id}/workspaces` | `list_engine_workspaces` | | | PUT | `/api/control-plane/engines/{id}/workspaces` | `set_engine_workspaces` | | @@ -130,6 +132,12 @@ Operation id: `list_engines`. Auth: Session cookie. Forbidden (403): Missing eng | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | Status | Meaning | |---|---| @@ -182,6 +190,12 @@ Operation id: `create_engine`. Auth: Session cookie and `X-Egret-CSRF` header. F | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | `catalogAttachErrors` | CatalogAttachErrorDto[] | yes | Catalogs from the body's `catalogs` that could not be attached. Empty on success. The engine EXISTS and is provisioned; these catalogs simply are not on it, and each can be retried with `POST /api/control-plane/engines/\{id\}/catalogs/\{cid\}`.... | | Status | Meaning | @@ -230,6 +244,12 @@ Operation id: `register_external_engine`. Auth: Session cookie and `X-Egret-CSRF | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | `rangerCredential` | RangerCredentialDto | yes | The credential the engine's Ranger plugin presents to egret's `/ranger` surface (HTTP Basic). **Shown once**: egret never returns this password again; a lost one is replaced with `POST... | | Status | Meaning | @@ -288,6 +308,12 @@ Operation id: `get_engine`. Auth: Session cookie. Forbidden (403): Missing engin | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | Status | Meaning | |---|---| @@ -345,6 +371,12 @@ Operation id: `update_engine`. Auth: Session cookie and `X-Egret-CSRF` header. F | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | Status | Meaning | |---|---| @@ -473,6 +505,12 @@ Operation id: `update_external_engine`. Auth: Session cookie and `X-Egret-CSRF` | `rangerCredentialId` | string (uuid), nullable | no | The id of the engine's CURRENT Ranger plugin credential: the `credentialId` registration or the last rotation returned with it. An opaque id that grants nothing (never the password); an external engine's operator sends it as `installed` to... | | `endpoint` | string | yes | `host:port` clients reach the engine on, as a workspace connection to it dials it (`Engine::client_endpoint`). External: the declared address. | | `runtime` | EngineRuntime | no | | +| `securityReapplyNeeded` | boolean | yes | Provisioned Trino only: `true` when the engine was last applied before the current security render (or never applied), so it may still accept unauthenticated requests on port 8080. Re-applying it clears this. | +| `securityUpgradeNeeded` | boolean | yes | Provisioned StarRocks only: `true` when `securityUpgradeReasons` is not empty, i.e. the engine runs an unpatched FE image and/or its FE root has no password. The security upgrade action (`POST... | +| `securityUpgradeReasons` | SecurityUpgradeReason[] | yes | Why the engine needs the security upgrade, in order: `unpatchedImage` (the stored FE image is absent or not an egret FE with every security patch), `noRootPassword` (the FE root has no password secret) and `notApplied` (the last security... | +| `securityUpgradeFeImage` | string, nullable | no | The FE image the security upgrade would switch the engine to. Set only when `unpatchedImage` is a reason and the swap is not a downgrade; absent otherwise. The security upgrade action (`POST... | +| `securityUpgradeBlockedRelease` | string, nullable | no | The release (e.g. `4.1.8`) that blocks the FE image swap: set when `unpatchedImage` is a reason but the engine already runs a release newer than the default FE, which cannot be downgraded. Absent otherwise. The security upgrade action... | +| `notGoverned` | boolean | yes | `true` when Ranger is on and this StarRocks engine (provisioned or external) is bound to no policy profile, so Ranger governs nothing on it (KI-336). A Trino engine is never flagged: it late-binds a profile on its next apply. Set by the... | | Status | Meaning | |---|---| @@ -484,6 +522,33 @@ Operation id: `update_external_engine`. Auth: Session cookie and `X-Egret-CSRF` | 409 | The engine changed concurrently (e.g. a delete); the profile is bound to another engine; or the change would create an order-ambiguous mask or row-filter pair (`code: rule_overlap`). Nothing was written | | 500 | Internal error | +## GET `/api/control-plane/engines/{id}/opa-plugin-config` + +Operation id: `opa_plugin_config`. Auth: Session cookie. Forbidden (403): Missing engine:read permission. + +`GET /api/control-plane/engines/\{id\}/opa-plugin-config`. + +| Parameter | In | Type | Required | Description | +|---|---|---|---|---| +| `id` | path | string (uuid) | yes | Engine id | + +**Response 200 (application/json)**: `OpaPluginConfigDto` + +| Field | Type | Required | Description | +|---|---|---|---| +| `files` | RangerPluginFileDto[] | yes | `access-control.properties`, then the context file; the context file carries a placeholder in place of the engine's secret. | +| `warnings` | string[] | yes | | +| `adminMustSupply` | string[] | yes | What the admin supplies that egret cannot. | + +| Status | Meaning | +|---|---| +| 200 | The Trino OPA plugin files to install on this external engine, rendered by the same code that renders the files egret mounts into its own engines. The context file carries the engine's id and a placeholder in place of its secret: the secret... | +| 401 | Not authenticated | +| 403 | Missing engine:read permission | +| 404 | No such engine | +| 409 | The engine is provisioned (egret mounts these files itself), it is bound to no policy profile (bind one first), or its profile is on Ranger (use /ranger-plugin-config) | +| 500 | Internal error | + ## GET `/api/control-plane/engines/{id}/pods/{pod}/exec` Operation id: `pod_exec`. Auth: Session cookie. Forbidden (403): Missing engine:provision permission. @@ -776,6 +841,38 @@ Operation id: `rotate_engine_secrets`. Auth: Session cookie and `X-Egret-CSRF` h | 500 | Internal error | | 502 | Kubernetes rejected the apply; the engine row now names the new secret pair (the cluster may already hold it), and the previous pair is kept — not deleted — until a later successful apply supersedes it | +## POST `/api/control-plane/engines/{id}/security-upgrade` + +Operation id: `security_upgrade`. Auth: Session cookie and `X-Egret-CSRF` header. Forbidden (403): Missing engine:update permission. + +Bring a legacy StarRocks engine up to egret's security baseline + +Mints a FE root password if the engine has none, moves FE and CN to the default patched images unless that would be a downgrade, and re-applies the engine: a Job sets the root password on the running FE and the FE and CN pods restart. The engine is re-applied on every call, so a repeat finishes a run whose apply failed. + +| Parameter | In | Type | Required | Description | +|---|---|---|---|---| +| `id` | path | string (uuid) | yes | Engine id | + +**Response 200 (application/json)**: `SecurityUpgradeDto` + +| Field | Type | Required | Description | +|---|---|---|---| +| `engine` | EngineDto | yes | The engine as it now stands. | +| `imageChanged` | boolean | yes | `true` when FE and CN were moved to the default patched images. | +| `rootPasswordSet` | boolean | yes | `true` when a FE root password was minted for the engine. | +| `imageKeptBecause` | string, nullable | no | Why the image was kept although it lacks the patches: the engine runs a newer release than the default FE, and egret does not downgrade. Absent otherwise. | + +| Status | Meaning | +|---|---| +| 200 | The engine is at the security baseline as far as egret can take it. The engine was re-applied (on every call): a Job sets the FE root password on the running FE, and the FE and CN pods restart (on the default patched images when... | +| 400 | Kubernetes not configured, or the engine's spec could not be assembled (e.g. no primary catalog with a warehouse) | +| 401 | Not authenticated | +| 403 | Missing engine:update permission | +| 404 | No such engine | +| 409 | Not a StarRocks engine; or the engine changed concurrently (e.g. a delete in progress), retry; or the engine is external (`code` = `engine_is_external`): egret does not upgrade the cluster of it | +| 500 | Internal error | +| 502 | Kubernetes rejected the apply. The stored spec and root password secret already carry the upgrade; the engine keeps the `notApplied` security upgrade reason (`securityUpgradeNeeded` stays true), and a repeat call finishes the run | + ## GET `/api/control-plane/engines/{id}/workspaces` Operation id: `list_engine_workspaces`. Auth: Session cookie. Forbidden (403): Missing engine:read permission. diff --git a/platform/reference/rest-api/index.md b/platform/reference/rest-api/index.md index 081b874..46624c4 100644 --- a/platform/reference/rest-api/index.md +++ b/platform/reference/rest-api/index.md @@ -133,7 +133,7 @@ described by their auth line here. | Data governance | [Governance data browser](rest-api/control-plane-governance) | 11 | | Data governance | [Classification tags](rest-api/control-plane-tags) | 8 | | Control plane | [Access control](rest-api/control-plane-access) | 26 | -| Control plane | [Engines](rest-api/control-plane-engines) | 26 | +| Control plane | [Engines](rest-api/control-plane-engines) | 28 | | Control plane | [Catalog registry](rest-api/control-plane-catalog-registry) | 14 | | Control plane | [Egret warehouses](rest-api/control-plane-catalog) | 30 | | Control plane | [Storage connections](rest-api/control-plane-storage) | 7 | diff --git a/static/img/platform/admin/cp-engine-create-starrocks-profile.png b/static/img/platform/admin/cp-engine-create-starrocks-profile.png new file mode 100644 index 0000000..6f79561 Binary files /dev/null and b/static/img/platform/admin/cp-engine-create-starrocks-profile.png differ diff --git a/static/img/platform/admin/cp-engine-security-upgrade-dialog.png b/static/img/platform/admin/cp-engine-security-upgrade-dialog.png new file mode 100644 index 0000000..0c3e2b8 Binary files /dev/null and b/static/img/platform/admin/cp-engine-security-upgrade-dialog.png differ diff --git a/static/img/platform/admin/cp-engine-security-upgrade-warning.png b/static/img/platform/admin/cp-engine-security-upgrade-warning.png new file mode 100644 index 0000000..e30b329 Binary files /dev/null and b/static/img/platform/admin/cp-engine-security-upgrade-warning.png differ diff --git a/static/img/platform/admin/cp-engines-security-upgrade-badge.png b/static/img/platform/admin/cp-engines-security-upgrade-badge.png new file mode 100644 index 0000000..d25fc1b Binary files /dev/null and b/static/img/platform/admin/cp-engines-security-upgrade-badge.png differ