Repository navigation
fix(coroot): link the Homepage card to the Coroot UI, not /mcp #5922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+667
−1
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
71dfbb7
fix(coroot): link the Homepage card to the Coroot UI, not /mcp
devantler b3c7399
fix(policies): scope the Homepage href guard to HTTPRoutes
devantler 98efcea
fix(policies): require an href for wildcard hosts and empty rule lists
devantler 6eae3bf
fix(policies): require an href for a Homepage route without a parentRef
devantler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
...astructure/cluster-policies/best-practices/require-homepage-href-for-non-root-routes.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # Keeps every Homepage card pointing at the service's front page. | ||
| # | ||
| # Why: unless a route sets gethomepage.dev/href, Homepage builds the card link | ||
| # itself, from the route's first hostname plus the path of its FIRST rule's | ||
| # FIRST match. Rule order is routing-neutral in the Gateway API, where the most | ||
| # specific match wins whatever its position, so nothing in a route reads as | ||
| # wrong when a narrow rule is listed first. Homepage is the only consumer of | ||
| # that order, and it fails silently: the card still renders, healthy, and links | ||
| # somewhere else. | ||
| # | ||
| # That is how the Coroot card broke. platform#5881 put an `Exact /mcp` rule | ||
| # (the agents' MCP endpoint, which bypasses SSO) ahead of the catch-all, so the | ||
| # card linked to /mcp, and a browser without a bearer token got Coroot's 401 | ||
| # "unauthorized" instead of the UI. A RegularExpression first path or a route | ||
| # without hostnames is worse: Homepage builds no link at all. | ||
| # | ||
| # So a route that opts into the dashboard may leave the link to Homepage only | ||
| # when the derived link is the site root; any other route must name its link | ||
| # explicitly with gethomepage.dev/href. The API server defaults an omitted rule | ||
| # list, match list or path to `PathPrefix /`, so an omitted one reads as the | ||
| # root here as well. | ||
| # | ||
| # Scope: HTTPRoutes in every namespace, platform and tenant alike, with | ||
| # background scanning on so existing drift surfaces in the PolicyReports. | ||
| # CREATE and UPDATE only, never DELETE, so a route can always be removed | ||
| # (#4732). Ingress is deliberately not matched: neither this repository nor the | ||
| # cluster serves one, and the tenant role grants only HTTPRoutes. Reading an | ||
| # Ingress's link needs its `http` field, and `ksail workload validate` skips | ||
| # any policy whose expressions name `http` (it cannot tell the field from the | ||
| # CEL http library), so covering Ingress would cost the offline evaluation that | ||
| # lets a pull request fail on a violating route before admission does. | ||
| apiVersion: policies.kyverno.io/v1 | ||
| kind: ValidatingPolicy | ||
| metadata: | ||
| name: require-homepage-href-for-non-root-routes | ||
| annotations: | ||
| policies.kyverno.io/title: Require Homepage Href For Non-Root Routes | ||
| policies.kyverno.io/category: Best Practices | ||
| policies.kyverno.io/severity: low | ||
| policies.kyverno.io/subject: HTTPRoute | ||
| policies.kyverno.io/minversion: 1.17.0 | ||
| policies.kyverno.io/description: >- | ||
| Requires every HTTPRoute that opts into Homepage discovery | ||
| (gethomepage.dev/enabled: "true") to set gethomepage.dev/href unless the | ||
| link Homepage would derive from it is the site root, so reordering a | ||
| route's rules cannot silently move its dashboard card to a sub-path. | ||
| spec: | ||
| failurePolicy: Fail | ||
| validationActions: | ||
| - Deny | ||
| evaluation: | ||
| background: | ||
| enabled: true | ||
| matchConstraints: | ||
| resourceRules: | ||
| - apiGroups: ["gateway.networking.k8s.io"] | ||
| apiVersions: ["*"] | ||
| operations: ["CREATE", "UPDATE"] | ||
| resources: ["httproutes"] | ||
| matchConditions: | ||
| - name: opted-into-homepage | ||
| expression: >- | ||
| object.metadata.?annotations.orValue({})[?'gethomepage.dev/enabled'].orValue('') == 'true' | ||
| variables: | ||
| # Homepage treats an empty href as absent, so this does too. | ||
| - name: href | ||
| expression: >- | ||
| object.metadata.?annotations.orValue({})[?'gethomepage.dev/href'].orValue('') | ||
| # https://<hostnames[0]><rules[0].matches[0].path.value>, and no link at all | ||
| # without a hostname or for a RegularExpression path. A wildcard first | ||
| # hostname (`*.example.com`) yields a link no browser can open. The scheme | ||
| # comes from the Gateway named by parentRefs[0], and Homepage throws reading | ||
| # that entry when parentRefs is omitted or empty. The API server defaults | ||
| # only an OMITTED rule list or match list; an explicitly empty one stays | ||
| # empty, and Homepage then fails reading `[0]` of it, so omitted reads as | ||
| # the root and empty does not. | ||
| - name: linksToRoot | ||
| expression: >- | ||
| size(object.spec.?parentRefs.orValue([])) > 0 && | ||
| size(object.spec.?hostnames.orValue([])) > 0 && | ||
| !object.spec.hostnames[0].startsWith('*') && | ||
| ( | ||
| !object.spec.?rules.hasValue() || | ||
| ( | ||
| size(object.spec.rules) > 0 && | ||
| ( | ||
| !object.spec.rules[0].?matches.hasValue() || | ||
| ( | ||
| size(object.spec.rules[0].matches) > 0 && | ||
| object.spec.rules[0].matches[0].?path.?value.orValue('/') == '/' && | ||
| object.spec.rules[0].matches[0].?path.?type.orValue('PathPrefix') != 'RegularExpression' | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| validations: | ||
| - expression: >- | ||
| variables.href != '' || variables.linksToRoot | ||
| message: >- | ||
| An HTTPRoute with gethomepage.dev/enabled="true" must set | ||
| gethomepage.dev/href unless its first hostname and its first rule's | ||
| first path already form the site root. Homepage builds the card link | ||
| from exactly those two fields, so a route whose first rule matches a | ||
| sub-path (such as an /mcp or /api bypass) sends the card there, and a | ||
| missing or wildcard hostname, a missing parentRef, a RegularExpression | ||
| path, or an explicitly empty rule or match list leaves the card with no | ||
| usable link. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
tests/require-homepage-href-for-non-root-routes/kyverno-test.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| apiVersion: cli.kyverno.io/v1alpha1 | ||
| kind: Test | ||
| metadata: | ||
| name: require-homepage-href-for-non-root-routes | ||
| policies: | ||
| - >- | ||
| ../../k8s/bases/infrastructure/cluster-policies/best-practices/require-homepage-href-for-non-root-routes.yaml | ||
| resources: | ||
| - resources.yaml | ||
| # Exercise the shipped Coroot route, whose /mcp rule comes first: it passes | ||
| # only while it keeps its explicit href. | ||
| - ../../k8s/bases/infrastructure/controllers/coroot/http-route.yaml | ||
| results: | ||
| # --- derived link is the site root, or an explicit href is set --- | ||
| - policy: require-homepage-href-for-non-root-routes | ||
| resources: | ||
| - whoami/root-prefix-first | ||
| - whoami/exact-root-first | ||
| - whoami/omitted-matches | ||
| - whoami/omitted-rules | ||
| - whoami/omitted-path | ||
| - observability/subpath-first-with-href | ||
| - whoami/wildcard-hostname-with-href | ||
| - whoami/empty-matches-with-href | ||
| - whoami/no-parent-refs-with-href | ||
| - observability/coroot | ||
| kind: HTTPRoute | ||
| result: pass | ||
| # --- a sub-path, a regex, a missing or wildcard hostname, no parentRef, or an empty list, with no href --- | ||
| - policy: require-homepage-href-for-non-root-routes | ||
| resources: | ||
| - observability/subpath-first-without-href | ||
| - whoami/prefix-subpath-first | ||
| - whoami/root-is-second-match | ||
| - whoami/regex-root-first | ||
| - whoami/no-hostnames | ||
| - whoami/empty-href | ||
| - whoami/wildcard-hostname | ||
| - whoami/empty-matches | ||
| - whoami/empty-rules | ||
| - whoami/no-parent-refs | ||
| - whoami/empty-parent-refs | ||
| kind: HTTPRoute | ||
| result: fail | ||
| # --- the precondition's negative control: routes that never opted in --- | ||
| - policy: require-homepage-href-for-non-root-routes | ||
| resources: | ||
| - whoami/not-opted-in | ||
| - whoami/discovery-disabled | ||
| kind: HTTPRoute | ||
| result: skip |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.