Skip to content

Commit

Permalink
[aws][feat] CIS 1.5 report: Section 1 Access Management and Section 2…
Browse files Browse the repository at this point in the history
… Storage (#1392)
  • Loading branch information
aquamatthias committed Jan 31, 2023
1 parent f6aadab commit 6087fc7
Show file tree
Hide file tree
Showing 35 changed files with 1,420 additions and 153 deletions.
6 changes: 4 additions & 2 deletions plugins/aws/resoto_plugin_aws/aws_client.py
Expand Up @@ -120,6 +120,8 @@ def __to_json(self, node: Any, **kwargs: Any) -> JsonElement:
return {key: self.__to_json(value, **kwargs) for key, value in node.items()}
elif isinstance(node, datetime):
return utc_str(node)
elif isinstance(node, bytes):
return node.decode("utf-8")
else:
raise AttributeError(f"Unsupported type: {type(node)}")

Expand Down Expand Up @@ -223,7 +225,7 @@ def list(
self,
aws_service: str,
action: str,
result_name: Optional[str],
result_name: Optional[str] = None,
expected_errors: Optional[List[str]] = None,
**kwargs: Any,
) -> List[Any]:
Expand All @@ -239,7 +241,7 @@ def get(
self,
aws_service: str,
action: str,
result_name: Optional[str],
result_name: Optional[str] = None,
expected_errors: Optional[List[str]] = None,
**kwargs: Any,
) -> Optional[Json]:
Expand Down
3 changes: 3 additions & 0 deletions plugins/aws/resoto_plugin_aws/collector.py
Expand Up @@ -17,6 +17,7 @@
dynamodb,
ec2,
ecs,
efs,
eks,
elasticbeanstalk,
elasticache,
Expand Down Expand Up @@ -63,6 +64,7 @@
+ cognito.resources
+ dynamodb.resources
+ ec2.resources
+ efs.resources
+ ecs.resources
+ eks.resources
+ elasticbeanstalk.resources
Expand Down Expand Up @@ -231,6 +233,7 @@ def collect_resource(resource: Type[AwsResource], rb: GraphBuilder) -> None:
regional_builder.core_feedback.error(msg, log)
return None

# TODO: move into separate AwsAccountSettings
def update_account(self) -> None:
log.info(f"Collecting AWS IAM Account Summary in account {self.account.dname}")
sm = self.client.get("iam", "get-account-summary", "SummaryMap") or {}
Expand Down
187 changes: 187 additions & 0 deletions plugins/aws/resoto_plugin_aws/resource/efs.py
@@ -0,0 +1,187 @@
from typing import Optional, ClassVar, Dict, List, Type

from attr import field, define

from resoto_plugin_aws.aws_client import AwsClient
from resoto_plugin_aws.resource.base import AwsApiSpec, GraphBuilder, AwsResource
from resoto_plugin_aws.resource.kms import AwsKmsKey
from resoto_plugin_aws.utils import ToDict
from resotolib.baseresources import BaseVolume
from resotolib.json_bender import Bender, S, MapValue, F, Bend
from resotolib.types import Json


class EfsTaggable:
def update_resource_tag(self, client: AwsClient, key: str, value: str) -> bool:
client.call(
aws_service="efs",
action="tag-resource",
result_name=None,
resourceId=self.id, # type: ignore
tags={key: value},
)
return True

def delete_resource_tag(self, client: AwsClient, key: str) -> bool:
client.call(
aws_service="efs",
action="untag-resource",
result_name=None,
resourceId=self.id, # type: ignore
tagKeys=[key],
)
return True

@classmethod
def called_mutator_apis(cls) -> List[AwsApiSpec]:
return [AwsApiSpec("efs", "tag-resource"), AwsApiSpec("efs", "untag-resource")]


@define(eq=False, slots=False)
class AwsEfsMountTarget(AwsResource):
kind: ClassVar[str] = "aws_efs_mount_target"
mapping: ClassVar[Dict[str, Bender]] = {
"id": S("MountTargetId"),
"owner_id": S("OwnerId"),
"life_cycle_state": S("LifeCycleState"),
"ip_address": S("IpAddress"),
"availability_zone_name": S("AvailabilityZoneName"),
}
owner_id: Optional[str] = field(default=None)
life_cycle_state: Optional[str] = field(default=None)
ip_address: Optional[str] = field(default=None)
availability_zone_name: Optional[str] = field(default=None)

def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
if nic_id := source.get("NetworkInterfaceId"):
builder.dependant_node(self, reverse=True, kind="aws_ec2_network_interface", id=nic_id)


@define(eq=False, slots=False)
class AwsEfsFileSystem(AwsResource, BaseVolume, EfsTaggable):
kind: ClassVar[str] = "aws_efs_file_system"
api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("efs", "describe-file-systems", "FileSystems")
mapping: ClassVar[Dict[str, Bender]] = {
"id": S("FileSystemId"),
"tags": S("Tags", default=[]) >> ToDict(),
"name": S("Name"),
"ctime": S("CreationTime"),
"owner_id": S("OwnerId"),
"creation_token": S("CreationToken"),
"arn": S("FileSystemArn"),
"volume_status": S("LifeCycleState")
>> MapValue(
{
"creating": "busy",
"available": "available",
"updating": "busy",
"deleting": "busy",
"deleted": "deleted",
"error": "error",
},
default="unknown",
),
"number_of_mount_targets": S("NumberOfMountTargets"),
"volume_size": S("SizeInBytes", "Value") >> F(lambda x: x / 1024**3),
"performance_mode": S("PerformanceMode"),
"volume_encrypted": S("Encrypted"),
"throughput_mode": S("ThroughputMode"),
"provisioned_throughput_in_mibps": S("ProvisionedThroughputInMibps"),
"availability_zone_name": S("AvailabilityZoneName"),
}
owner_id: Optional[str] = field(default=None)
creation_token: Optional[str] = field(default=None)
number_of_mount_targets: Optional[int] = field(default=None)
performance_mode: Optional[str] = field(default=None)
throughput_mode: Optional[str] = field(default=None)
provisioned_throughput_in_mibps: Optional[float] = field(default=None)
availability_zone_name: Optional[str] = field(default=None)

@classmethod
def called_collect_apis(cls) -> List[AwsApiSpec]:
return [cls.api_spec, AwsApiSpec("efs", "describe-mount-targets")]

@classmethod
def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None:
def collect_mount_points(fs: AwsEfsFileSystem) -> None:
for mt_raw in builder.client.list("efs", "describe-mount-targets", "MountTargets", FileSystemId=fs.id):
mt = AwsEfsMountTarget.from_api(mt_raw)
builder.add_node(mt, mt_raw)
builder.add_edge(fs, node=mt)

for js in json:
instance = cls.from_api(js)
builder.add_node(instance, js)
builder.submit_work(collect_mount_points, instance)

def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
if kms_key_id := source.get("KmsKeyId"):
builder.dependant_node(from_node=self, clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(kms_key_id))


@define(eq=False, slots=False)
class AwsEfsPosixUser:
kind: ClassVar[str] = "aws_efs_posix_user"
mapping: ClassVar[Dict[str, Bender]] = {
"uid": S("Uid"),
"gid": S("Gid"),
"secondary_gids": S("SecondaryGids", default=[]),
}
uid: Optional[int] = field(default=None)
gid: Optional[int] = field(default=None)
secondary_gids: List[int] = field(factory=list)


@define(eq=False, slots=False)
class AwsEfsCreationInfo:
kind: ClassVar[str] = "aws_efs_creation_info"
mapping: ClassVar[Dict[str, Bender]] = {
"owner_uid": S("OwnerUid"),
"owner_gid": S("OwnerGid"),
"permissions": S("Permissions"),
}
owner_uid: Optional[int] = field(default=None)
owner_gid: Optional[int] = field(default=None)
permissions: Optional[str] = field(default=None)


@define(eq=False, slots=False)
class AwsEfsRootDirectory:
kind: ClassVar[str] = "aws_efs_root_directory"
mapping: ClassVar[Dict[str, Bender]] = {
"path": S("Path"),
"creation_info": S("CreationInfo") >> Bend(AwsEfsCreationInfo.mapping),
}
path: Optional[str] = field(default=None)
creation_info: Optional[AwsEfsCreationInfo] = field(default=None)


@define(eq=False, slots=False)
class AwsEfsAccessPoint(AwsResource, EfsTaggable):
kind: ClassVar[str] = "aws_efs_access_point"
api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("efs", "describe-access-points", "AccessPoints")
mapping: ClassVar[Dict[str, Bender]] = {
"id": S("AccessPointId"),
"tags": S("Tags", default=[]) >> ToDict(),
"name": S("Name"),
"client_token": S("ClientToken"),
"arn": S("AccessPointArn"),
"posix_user": S("PosixUser") >> Bend(AwsEfsPosixUser.mapping),
"root_directory": S("RootDirectory") >> Bend(AwsEfsRootDirectory.mapping),
"owner_id": S("OwnerId"),
"life_cycle_state": S("LifeCycleState"),
}
client_token: Optional[str] = field(default=None)
posix_user: Optional[AwsEfsPosixUser] = field(default=None)
root_directory: Optional[AwsEfsRootDirectory] = field(default=None)
owner_id: Optional[str] = field(default=None)
life_cycle_state: Optional[str] = field(default=None)

def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
if fs_id := source.get("FileSystemId"):
builder.dependant_node(
from_node=self, reverse=True, delete_same_as_default=True, clazz=AwsEfsFileSystem, id=fs_id
)


resources: List[Type[AwsResource]] = [AwsEfsFileSystem, AwsEfsAccessPoint]

0 comments on commit 6087fc7

Please sign in to comment.