Skip to content

Commit

Permalink
adds attack path for node roles
Browse files Browse the repository at this point in the history
fixes #16
  • Loading branch information
Skybound1 committed Apr 25, 2024
1 parent 5a08913 commit 77d1498
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 1 deletion.
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

0 comments on commit 77d1498

Please sign in to comment.