Skip to content
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

adds attack path for node roles #23

Merged
merged 1 commit into from
Apr 29, 2024
Merged
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
23 changes: 23 additions & 0 deletions docs/IS_CLUSTER_ADMIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# IS_CLUSTER_ADMIN

### Overview

This attack path aims to provide a route for nodes that are expected to grant cluster administrator access just due to the nature of the resource.

### Description

Compromise of certain resources within a cluster can be considered to grant cluster administrator due to the nature of the resource compromised.

For example, compromise of a control plane node within a Kubernetes cluster that runs services such as the API server or etcd effectively grants cluster administrator access.

### Defense

Security of resources that are effectively cluster administrator should be reviewed and hardened.

### Cypher Deep-Dive

```cypher
MATCH (src:Node), (dest:ClusterRoleBinding)-[:GRANTS_PERMISSION]->(:ClusterRole {name: "cluster-admin"}) WHERE any(x in ["master", "control-plane"] WHERE x in src.node_roles)
```

The above query finds nodes `src` that have the `master` or `control-plane` role. The destination is set to a cluster role binding that binds to `cluster-admin`.
4 changes: 4 additions & 0 deletions icekube/attack_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,8 @@ def workload_query(
})
""",
],
Relationship.IS_CLUSTER_ADMIN: """
MATCH (src:Node), (dest:ClusterRoleBinding)-[:GRANTS_PERMISSION]->(:ClusterRole {name: "cluster-admin"})
WHERE any(x in ["master", "control-plane"] WHERE x in src.node_roles)
""",
}
3 changes: 3 additions & 0 deletions icekube/icekube.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ def relationship_generator(
with driver.session() as session:
logger.info(f"Generating relationships for {resource}")
for source, relationship, target in resource.relationships(initial):
logger.debug(
f"Creating relationship: {source} -> {relationship} -> {target}"
)
if isinstance(source, Resource):
src_cmd, src_kwargs = get(source, prefix="src")
else:
Expand Down
19 changes: 18 additions & 1 deletion icekube/models/node.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
from __future__ import annotations

from typing import List
from typing import Any, Dict, List

from icekube.models.base import Resource
from pydantic import computed_field


class Node(Resource):
supported_api_groups: List[str] = [""]

@computed_field # type: ignore
@property
def node_roles(self) -> List[str]:
return [
x.split("/", 1)[1]
for x in self.labels.keys()
if x.startswith("node-role.kubernetes.io/")
]

@property
def db_labels(self) -> Dict[str, Any]:
return {
**super().db_labels,
"node_roles": self.node_roles,
}
2 changes: 2 additions & 0 deletions icekube/relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Relationship:
in this direction in neo4j: (ObjectOne)-[:RELATIONSHIP]->(ObjectTwo)
"""

IS_CLUSTER_ADMIN: ClassVar[str] = "IS_CLUSTER_ADMIN"

HOSTS_POD: ClassVar[str] = "HOSTS_POD"

AUTHENTICATION_TOKEN_FOR: ClassVar[str] = "AUTHENTICATION_TOKEN_FOR"
Expand Down
Loading