Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions aws-integration-setup/cloudformation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,13 @@ On an existing stack, add that line to the `update-stack` command above.

## Managed EKS scan (no in-cluster agent)

Nullify lists Kubernetes resources in your EKS cluster from Nullify's cloud-scan compute, using the read-only role this template creates. Nothing runs in the cluster. The scan only calls `list`, on 26 kinds:
Nullify lists Kubernetes resources in your EKS cluster from Nullify's cloud-scan compute, using the read-only role this template creates. Nothing runs in the cluster. The scan only calls `list`, on 27 kinds:

| API group | Resources |
|---|---|
| core | nodes, namespaces, pods, services, persistentvolumeclaims, persistentvolumes, configmaps, resourcequotas, limitranges, serviceaccounts |
| apps | deployments, daemonsets, statefulsets, replicasets |
| batch | jobs |
| batch | jobs, cronjobs |
| networking.k8s.io | ingresses, networkpolicies |
| discovery.k8s.io | endpointslices |
| rbac.authorization.k8s.io | roles, rolebindings, clusterroles, clusterrolebindings |
Expand Down Expand Up @@ -343,7 +343,7 @@ Other flags: `--role-arn` instead of `--customer-name`, `--authorization rbac` (
`setup-eks-managed-scan.sh verify` checks that:

- the authentication mode supports access entries, the access entry exists with **only** the expected group, and **no** EKS access policy is associated;
- `kubectl auth can-i list <resource> --all-namespaces --as nullify-verify --as-group nullify-readonly` answers `yes` for all 26 resources, and `create pods`, `get`/`list secrets`, `get`/`create pods/exec`, `get`/`list pods/log`, `get`/`create pods/attach`, `get`/`create pods/portforward`, `get nodes/proxy`, and `escalate` / `bind` on clusterroles do not. `impersonate` users / serviceaccounts / groups is required `no` when `kubectl auth can-i` accepts those checks; otherwise it is skipped;
- `kubectl auth can-i list <resource> --all-namespaces --as nullify-verify --as-group nullify-readonly` answers `yes` for all 27 resources, and `create pods`, `get`/`list secrets`, `get`/`create pods/exec`, `get`/`list pods/log`, `get`/`create pods/attach`, `get`/`create pods/portforward`, `get nodes/proxy`, and `escalate` / `bind` on clusterroles do not. `impersonate` users / serviceaccounts / groups is required `no` when `kubectl auth can-i` accepts those checks; otherwise it is skipped;
- `publicAccessCidrs` admits Nullify's egress IPs and does not contain `0.0.0.0/0`.

`can-i` exercises Kubernetes RBAC only. It cannot see access-policy grants or prove that Nullify can reach the endpoint. Finish by confirming the cluster connects on the Nullify configure page.
Expand Down
2 changes: 1 addition & 1 deletion aws-integration-setup/scripts/setup-eks-managed-scan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ RBAC_RESOURCES=(
nodes namespaces pods services persistentvolumeclaims persistentvolumes
configmaps resourcequotas limitranges serviceaccounts
deployments.apps daemonsets.apps statefulsets.apps replicasets.apps
jobs.batch
jobs.batch cronjobs.batch
ingresses.networking.k8s.io networkpolicies.networking.k8s.io
endpointslices.discovery.k8s.io
roles.rbac.authorization.k8s.io rolebindings.rbac.authorization.k8s.io
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ resource "kubernetes_cluster_role" "nullify_readonly_role" {
}

# Kinds the collector lists, kept identical to the managed-scan ClusterRole
# (managed_scan.tf). Secrets are omitted: list returns values. "jobs" is
# granted ahead of the monorepo's fix/k8s-deployment-matching-review stack
# (#12807 / #12830).
# (managed_scan.tf). Secrets are omitted: list returns values.
rule {
api_groups = [""]
resources = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ locals {
},
{
api_groups = ["batch"]
resources = ["jobs"]
resources = ["jobs", "cronjobs"]
},
{
api_groups = ["networking.k8s.io"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ run "managed_scan_rbac_only" {
}

assert {
condition = sum([for rule in slice(kubernetes_cluster_role_v1.nullify_readonly[0].rule, 0, 7) : length(rule.resources)]) == 26
error_message = "The managed-scan ClusterRole must list exactly the 26 config kinds; secrets are omitted because list returns values"
condition = sum([for rule in slice(kubernetes_cluster_role_v1.nullify_readonly[0].rule, 0, 7) : length(rule.resources)]) == 27
error_message = "The managed-scan ClusterRole must list exactly the 27 config kinds; secrets are omitted because list returns values"
}

# Pins the set, not just the size: swapping one kind for another keeps every
Expand All @@ -158,7 +158,7 @@ run "managed_scan_rbac_only" {
}

assert {
condition = tolist(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].api_groups) == tolist(["batch"]) && toset(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].resources) == toset(["jobs"])
condition = tolist(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].api_groups) == tolist(["batch"]) && toset(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].resources) == toset(["jobs", "cronjobs"])
error_message = "Rule 2 must grant exactly the batch API group kinds the scanner lists"
}

Expand Down
4 changes: 2 additions & 2 deletions aws-integration-setup/terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ terraform/
| Cluster endpoint | Any, including private-only | Public endpoint that admits Nullify's egress IPs |
| AWS setup | `enable_kubernetes_integration = true`, S3 bucket or access point | `eks-managed-scan-access`: one access entry per cluster |
| Kubernetes setup | `k8s-resources` (default) or the `nullify-k8s-collector` Helm chart | A list-only `nullify-readonly` ClusterRole and ClusterRoleBinding, applied by `k8s-resources` with `enable_collector = false, enable_managed_scan_rbac = true` |
| Kinds read | The managed scan's kinds, plus cronjobs, ingressclasses, horizontalpodautoscalers, poddisruptionbudgets, storageclasses, csidrivers, runtimeclasses, priorityclasses, flowschemas, certificatesigningrequests and customresourcedefinitions. `k8s-resources` and chart 0.4.0 and later grant the same set, `list` only. Charts before 0.4.0 grant an older set; see that version's `templates/clusterrole.yaml` | See [RBAC granted to Nullify](#rbac-granted-to-nullify-authorization--rbac) |
| Kinds read | The managed scan's kinds, plus ingressclasses, horizontalpodautoscalers, poddisruptionbudgets, storageclasses, csidrivers, runtimeclasses, priorityclasses, flowschemas, certificatesigningrequests and customresourcedefinitions. `k8s-resources` and chart 0.4.0 and later grant the same set, `list` only. Charts before 0.4.0 grant an older set; see that version's `templates/clusterrole.yaml` | See [RBAC granted to Nullify](#rbac-granted-to-nullify-authorization--rbac) |
| Upgrades | You update the image | None |

Both modes can run on the same role: IRSA uses `sts:AssumeRoleWithWebIdentity`, the managed scan uses `sts:AssumeRole` with the external ID.
Expand All @@ -103,7 +103,7 @@ The access entry carries the Kubernetes group `nullify-readonly`, bound to the C
|---|---|
| core | nodes, namespaces, pods, services, persistentvolumeclaims, persistentvolumes, configmaps, resourcequotas, limitranges, serviceaccounts |
| apps | deployments, daemonsets, statefulsets, replicasets |
| batch | jobs |
| batch | jobs, cronjobs |
| networking.k8s.io | ingresses, networkpolicies |
| discovery.k8s.io | endpointslices |
| rbac.authorization.k8s.io | roles, rolebindings, clusterroles, clusterrolebindings |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ resource "kubernetes_cluster_role" "nullify_readonly_role" {
}

# Kinds the collector lists, kept identical to the managed-scan ClusterRole
# (managed_scan.tf). Secrets are omitted: list returns values. "jobs" is
# granted ahead of the monorepo's fix/k8s-deployment-matching-review stack
# (#12807 / #12830).
# (managed_scan.tf). Secrets are omitted: list returns values.
rule {
api_groups = [""]
resources = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ locals {
},
{
api_groups = ["batch"]
resources = ["jobs"]
resources = ["jobs", "cronjobs"]
},
{
api_groups = ["networking.k8s.io"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ run "managed_scan_rbac_only" {
}

assert {
condition = sum([for rule in slice(kubernetes_cluster_role_v1.nullify_readonly[0].rule, 0, 7) : length(rule.resources)]) == 26
error_message = "The managed-scan ClusterRole must list exactly the 26 config kinds; secrets are omitted because list returns values"
condition = sum([for rule in slice(kubernetes_cluster_role_v1.nullify_readonly[0].rule, 0, 7) : length(rule.resources)]) == 27
error_message = "The managed-scan ClusterRole must list exactly the 27 config kinds; secrets are omitted because list returns values"
}

# Pins the set, not just the size: swapping one kind for another keeps every
Expand All @@ -158,7 +158,7 @@ run "managed_scan_rbac_only" {
}

assert {
condition = tolist(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].api_groups) == tolist(["batch"]) && toset(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].resources) == toset(["jobs"])
condition = tolist(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].api_groups) == tolist(["batch"]) && toset(kubernetes_cluster_role_v1.nullify_readonly[0].rule[2].resources) == toset(["jobs", "cronjobs"])
error_message = "Rule 2 must grant exactly the batch API group kinds the scanner lists"
}

Expand Down
Loading