feat(stackit): add ske_cluster_no_public_endpoint check - #11943
Conversation
Adds the STACKIT Kubernetes Engine (SKE) service and its first check,
verifying that a cluster's Kubernetes API endpoint is not reachable from
the whole internet.
The check evaluates Cluster.extensions.acl, which is the generally
available control for restricting API server access ("The ACL
functionality allows you to restrict access to the API server of your
cluster to certain IP ranges"), and whose default is unrestricted:
- control plane access scope SNA -> PASS (private control plane)
- ACL extension disabled -> FAIL (API open to any source)
- allowlist contains 0.0.0.0/0
or ::/0 -> FAIL
- otherwise -> PASS
Cluster.network.control_plane.access_scope is deliberately not the
primary signal: it is a private-preview field behind a feature flag, and
all three levels of the chain are optional, so it is absent on virtually
every cluster. It is only used to short-circuit SNA clusters to PASS,
since a private control plane is not internet-reachable regardless of
its ACL.
Nested SDK fields are read through _get_field, which handles both raw
dict and SDK model shapes, because _extract_items yields either.
Reading only attributes would silently drop the ACL of a dict-shaped
response and report an exposed cluster as PASS.
- prowler/providers/stackit/services/ske/ske_service.py: SKEService,
Cluster model, ACL/access-scope parsing
- prowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/:
check, metadata, __init__
- stackit_provider.py + stackit_regions_by_service.json: register the
ske service (eu01, eu02)
- pyproject.toml + uv.lock: pin stackit-ske==1.12.0
- docs: add SKE and Object Storage rows to the supported services table
- tests: 55 tests, 100% coverage of the new files, covering dict-shaped
and SDK-model-shaped responses
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughAdds STACKIT Kubernetes Engine discovery across supported regions. It introduces a check that reports clusters with publicly reachable Kubernetes API endpoints based on control-plane scope and ACL CIDRs. ChangesSTACKIT SKE endpoint security check
Estimated code review effort: 3 (Moderate) | ~25 minutes Merge Risk: ⚪ Minimal · up to The change is merge-ready after normal checks and review; no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant SKEService
participant StackitProvider
participant SkeDefaultApi
participant EndpointCheck
SKEService->>StackitProvider: generate regional SKE clients
StackitProvider->>SkeDefaultApi: create regional API client
SKEService->>SkeDefaultApi: list clusters by region
SkeDefaultApi-->>SKEService: return cluster data
SKEService->>SKEService: parse access scope and ACL
EndpointCheck->>SKEService: read discovered clusters
EndpointCheck-->>EndpointCheck: emit PASS or FAIL report
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ No Conflicts No conflict markers, and the branch merges cleanly into its base. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@prowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/ske_cluster_no_public_endpoint.metadata.json`:
- Around line 20-26: Populate Remediation.Code.CLI with a non-empty STACKIT CLI
remediation command or descriptive CLI guidance for enabling the SKE ACL
extension and restricting API source CIDRs; follow the project’s metadata
conventions if no direct command exists, while preserving the existing
Remediation.Code.Other instructions.
In
`@tests/providers/stackit/services/ske/ske_cluster_no_public_endpoint/ske_cluster_no_public_endpoint_test.py`:
- Around line 43-45: Replace every bare mock.MagicMock reference with a
mock.MagicMock() instance in all test methods of the affected test class/file,
including test_no_clusters, so setting clusters remains isolated to each test
and does not mutate the mock class globally.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c9350071-64d4-469c-82a3-7cd09d8657e0
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock,!**/uv.lock
📒 Files selected for processing (13)
docs/user-guide/providers/stackit/getting-started-stackit.mdxprowler/changelog.d/ske-cluster-no-public-endpoint.added.mdprowler/providers/stackit/services/ske/__init__.pyprowler/providers/stackit/services/ske/ske_client.pyprowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/__init__.pyprowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/ske_cluster_no_public_endpoint.metadata.jsonprowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/ske_cluster_no_public_endpoint.pyprowler/providers/stackit/services/ske/ske_service.pyprowler/providers/stackit/stackit_provider.pyprowler/providers/stackit/stackit_regions_by_service.jsonpyproject.tomltests/providers/stackit/services/ske/ske_cluster_no_public_endpoint/ske_cluster_no_public_endpoint_test.pytests/providers/stackit/services/ske/ske_service_test.py
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #11943 +/- ##
==========================================
- Coverage 93.92% 87.89% -6.04%
==========================================
Files 266 2110 +1844
Lines 39576 73490 +33914
==========================================
+ Hits 37173 64593 +27420
- Misses 2403 8897 +6494 Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Question for maintainers:
|
| Provider | Check-test files using = mock.MagicMock |
|---|---|
| azure | 165 |
| stackit | 9 (8 existing + this PR) |
| gcp / openstack | 0 |
So the options are:
- Apply the fix to this PR's test only. Removes a real (if latent) defect, but makes this file inconsistent with the other 8 STACKIT check tests.
- Leave this PR as-is for consistency, and track the isolation fix separately as a repo-wide test-hygiene change.
- Leave it entirely — the pollution is latent and the class form is deliberate.
I'm happy to do whichever you prefer; (2) seems the least disruptive, and I'd be glad to open the follow-up. Just let me know and I'll update the PR accordingly.
For reference, the other CodeRabbit comment on this PR asks for a non-empty Remediation.Code.CLI. I've left it empty to match convention: 525 of 1503 check metadata files have an empty CLI, including all of the most recently merged checks and the sibling iaas_server_public_ip_attached. Happy to add descriptive text if you'd rather.
|
@danibarranqueroo Any news about this? |
|
@HugoPBrito Any news about that? |
|
Thanks for the thorough investigation @johannes-engler-mw, and sorry for the delay. I believe option 2 is the way to go. Please keep the existing pattern in this PR for consistency, and track the MagicMock class pollution as a separate repository-wide test-hygiene change. Then feel free to open the follow-up issue. |
|
Thanks @HugoPBrito — going with option 2.
Nothing further pending on my side here — ready for review whenever you have a moment. |
|
Hi @johannes-engler-mw, could you please provide some evidence of the check working as expected? It must be censored. |
SKE enablement is per project and region. A region the project never enabled answers list_clusters with 403 "Service not enabled", which is byte-identical in status to a missing IAM role. handle_api_error mapped it to StackITInvalidTokenError, so a project with SKE in eu01 but not eu02 aborted the whole scan and reported zero findings behind the message "service account lacks the required permissions". _fetch_all_regions already skipped the 404 that IaaS and Object Storage return for an unprovisioned region; SKE is the only STACKIT service of the three that uses 403 for this. Measured on a project with neither service enabled: object storage answers 404 project.not_found, SKE answers 403 "Service not enabled". The body message is therefore the only discriminator against a genuine permission failure, which still aborts loudly. Found by running the check against real infrastructure -- the mocked tests passed both before and after.
Evidence:
|
| Service | Region without the service | Skipped today? |
|---|---|---|
| IaaS | 404 resource not found: project |
yes — no enablement concept at all |
| Object Storage | 404 {"key":"project.not_found"} |
yes — has enable/disable, but reports it as a project 404 |
| SKE | 403 Service not enabled |
was not — fixed here |
Measured on a throwaway project that existed but had neither service enabled, so the project-level 404 could not mask the answer. SKE is the only one of the three using 403, so this commit closes the actual exposure. What remains is hygiene: each service hand-rolls its own "which status means skip this region" logic, and these three don't agree — worth hoisting into StackitProvider in a separate PR, happy to open one.
Worth noting that this was only reachable by running against real infrastructure: the mocked tests passed both before and after. Good argument for asking for evidence.
|
Once all tests pass I'll merge this and this new check will be available in next minor. Thanks for the evidence, the nice catch and its fix and the contribution overall @johannes-engler-mw. |
Context
Adds the STACKIT Kubernetes Engine (SKE) service and its first check,
ske_cluster_no_public_endpoint. This continues the effort to build STACKIT coverage in Prowler (follows #11397 and #11549).Description
A new
SKEServicefetches clusters from all audited project regions vialist_clusters(project_id, region), and the check reports whether a cluster's Kubernetes API endpoint is reachable from the whole internet.New check:
ske_cluster_no_public_endpointskecontainerinternet-exposedThe check is built on
Cluster.extensions.acl, which is the generally available control for restricting API server access. STACKIT's security how-to describes it as "The ACL functionality allows you to restrict access to the API server of your cluster to certain IP ranges" and explicitly warns against0.0.0.0/0. The default is unrestricted.SNA0.0.0.0/0or::/0Why not
access_scopeas the primary signal?Cluster.network.control_plane.access_scope(PUBLIC/SNA) is a private-preview field — both the SDK docstring and the private clusters how-to state it is behind a feature flag and not yet GA. All three levels of the chain (network,control_plane,access_scope) are optional and default toNone, so it is absent on virtually every observable cluster. It is used only to short-circuitSNAclusters to PASS, since a private control plane is not internet-reachable regardless of its ACL.Dict- and object-shaped responses. Per the review on #11549, nested SDK fields are read through
_get_field, which handles both rawdictand SDK model shapes, because_extract_itemsyields either. Reading only attributes would silently drop the ACL of a dict-shaped response and report an exposed cluster asPASS. There is regression coverage for both shapes plus a parity test asserting they parse identically.Files changed:
prowler/providers/stackit/services/ske/ske_service.py—SKEService,Clustermodel, ACL / access-scope parsingprowler/providers/stackit/services/ske/ske_cluster_no_public_endpoint/— check, metadata,__init__prowler/providers/stackit/stackit_provider.py,stackit_regions_by_service.json— register theskeservice (eu01, eu02)pyproject.toml,uv.lock— pinstackit-ske==1.12.0docs/user-guide/providers/stackit/getting-started-stackit.mdx— add SKE to the supported services table (and the Object Storage row, which was missing)tests/providers/stackit/services/ske/— 55 testsSteps to review
ske_service.py— particularly_parse_acland_parse_access_scope— andCluster.has_public_endpoint().AdditionalURLs— all verified to return HTTP 200).Checklist
SDK/CLI
ske_cluster_no_public_endpoint. This adds a new STACKIT API dependency (stackit-ske). The service account needs read access to SKE clusters on the project (project.ownercovers it); no change to the existing IaaS/Object Storage credentials.UI
N/A
API
N/A
License
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
Summary by CodeRabbit
New Features
Documentation
Tests