diff --git a/README.md b/README.md index a6a79eb..66e11cd 100644 --- a/README.md +++ b/README.md @@ -108,11 +108,14 @@ The managed agent for a container in your Task has stopped for some reasons. If 14. **_🟡 Init Process Enabled | Disabled_** This check item won't block you to use ECS Exec, but we recommend you to add the `initProcessEnabled` flag to your ECS task definition for each container to avoid having orphaned and zombie processes. See the "Considerations for using ECS Exec" in [the ECS official documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html#ecs-exec-considerations) for more details. -15. **_🔴 EC2 or Task Role | Not Configured"_ or _{serviceName}:{ActionName}: implicitDeny_** +15. **_🔴 Read-Only Root Filesystem | ReadOnly_** +ECS Exec uses the SSM agent as its managed agent, and the agents requires that the container file system is able to be written in order to create the required directories and files. Therefore, you need to set the `readonlyRootFilesystem` flag as `false` in your task definition to exec into the container using ECS Exec. See the "Considerations for using ECS Exec" in [the ECS official documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html#ecs-exec-considerations) for more details. + +16. **_🔴 EC2 or Task Role | Not Configured"_ or _{serviceName}:{ActionName}: implicitDeny_** Your ECS task needs a task role or an instance role of the underlying EC2 instance with some permissions for using SSM Session Manager at least. See the [IAM permissions required for ECS Exec](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html#ecs-exec-enabling-and-using) section and the [Enabling logging and auditing in your tasks and services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html#ecs-exec-logging) section in the official documentation for the details. Note that the `Condition` element of the IAM policy is not currently supported to evaluate by `check-ecs-exec.sh`. -16. **_🟡 SSM PrivateLink "com.amazonaws.(region).ssmmessages" not found_** +17. **_🟡 SSM PrivateLink "com.amazonaws.(region).ssmmessages" not found_** The `check-ecs-exec.sh` found one or more VPC endpoints configured in the VPC for your task, so you **may** want to add an additional SSM PrivateLink for your VPC. Make sure your ECS task has proper outbound internet connectivity, and if it doesn't, then you **need** to configure an additional SSM PrivateLink for your VPC. ## Security diff --git a/check-ecs-exec.sh b/check-ecs-exec.sh index 4885a9f..21e593b 100755 --- a/check-ecs-exec.sh +++ b/check-ecs-exec.sh @@ -356,11 +356,15 @@ fi printf "${COLOR_DEFAULT}\n" # 6. Check the managed agents' status -printf "${COLOR_DEFAULT} Managed Agent Status | " +printf "${COLOR_DEFAULT} Container-Level Checks | \n" +printf "${COLOR_DEFAULT} ----------\n" +printf "${COLOR_DEFAULT} Managed Agent Status" if [[ "x${executeCommandEnabled}" = "xfalse" ]]; then - printf "${COLOR_YELLOW}SKIPPED\n" + printf " - ${COLOR_YELLOW}SKIPPED\n" + printf "${COLOR_DEFAULT} ----------\n" else printf "\n" + printf "${COLOR_DEFAULT} ----------\n" agentsStatus=$(echo "${describedTaskJson}" | jq -r ".tasks[0].containers[].managedAgents[].lastStatus") idx=0 for _ in $agentsStatus; do @@ -368,13 +372,17 @@ else status=$(echo "${describedTaskJson}" | jq -r ".tasks[0].containers[${idx}].managedAgents[0].lastStatus") reason=$(echo "${describedTaskJson}" | jq -r ".tasks[0].containers[${idx}].managedAgents[0].reason") lastStartedAt=$(echo "${describedTaskJson}" | jq -r ".tasks[0].containers[${idx}].managedAgents[0].lastStartedAt") - printf " $((idx+1)). " + printf " $((idx+1)). " case "${status}" in *STOPPED* ) printf "${COLOR_RED}STOPPED (Reason: ${reason})";; *PENDING* ) printf "${COLOR_YELLOW}PENDING";; * ) printf "${COLOR_GREEN}RUNNING";; esac - printf "${COLOR_DEFAULT} for \"${containerName}\" container - LastStartedAt: ${lastStartedAt}\n" + printf "${COLOR_DEFAULT} for \"${containerName}\"" + if [[ "x${status}" = "xSTOPPED" ]]; then + printf " - LastStartedAt: ${lastStartedAt}" + fi + printf "\n" idx=$((idx+1)) done fi @@ -384,22 +392,44 @@ taskDefArn=$(echo "${describedTaskJson}" | jq -r ".tasks[0].taskDefinitionArn") taskDefJson=$(${AWS_CLI_BIN} ecs describe-task-definition \ --task-definition "${taskDefArn}" \ --output json) +taskDefFamily=$(echo "${taskDefJson}" | jq -r ".taskDefinition.family") +taskDefRevision=$(echo "${taskDefJson}" | jq -r ".taskDefinition.revision") initEnabledList=$(echo "${taskDefJson}" | jq -r ".taskDefinition.containerDefinitions[].linuxParameters.initProcessEnabled") idx=0 -printf "${COLOR_DEFAULT} Init Process Enabled | ${taskDefArn}\n" +printf "${COLOR_DEFAULT} ----------\n" +printf "${COLOR_DEFAULT} Init Process Enabled (${taskDefFamily}:${taskDefRevision})\n" +printf "${COLOR_DEFAULT} ----------\n" for enabled in $initEnabledList; do containerName=$(echo "${taskDefJson}" | jq -r ".taskDefinition.containerDefinitions[${idx}].name") - printf " $((idx+1)). " + printf " $((idx+1)). " case "${enabled}" in *true* ) printf "${COLOR_GREEN}Enabled";; *false* ) printf "${COLOR_YELLOW}Disabled";; * ) printf "${COLOR_YELLOW}Disabled";; esac - printf "${COLOR_DEFAULT} - \"${containerName}\" container\n" + printf "${COLOR_DEFAULT} - \"${containerName}\"\n" + idx=$((idx+1)) +done + +# 8. Check the "readonlyRootFilesystem" flag added in the task definition (red) +readonlyRootFsList=$(echo "${taskDefJson}" | jq -r ".taskDefinition.containerDefinitions[].readonlyRootFilesystem") +idx=0 +printf "${COLOR_DEFAULT} ----------\n" +printf "${COLOR_DEFAULT} Read-Only Root Filesystem (${taskDefFamily}:${taskDefRevision})\n" +printf "${COLOR_DEFAULT} ----------\n" +for enabled in $readonlyRootFsList; do + containerName=$(echo "${taskDefJson}" | jq -r ".taskDefinition.containerDefinitions[${idx}].name") + printf " $((idx+1)). " + case "${enabled}" in + *false* ) printf "${COLOR_GREEN}Disabled";; + *true* ) printf "${COLOR_RED}ReadOnly";; + * ) printf "${COLOR_GREEN}Disabled";; + esac + printf "${COLOR_DEFAULT} - \"${containerName}\"\n" idx=$((idx+1)) done -# 8. Check the task role permissions +# 9. Check the task role permissions overriddenTaskRole=true taskRoleArn=$(echo "${describedTaskJson}" | jq -r ".tasks[0].overrides.taskRoleArn") if [[ "x${taskRoleArn}" = "xnull" ]]; then @@ -538,7 +568,7 @@ else fi fi -# 9. Check existing VPC Endpoints (PrivateLinks) in the task VPC. +# 10. Check existing VPC Endpoints (PrivateLinks) in the task VPC. # If there is any VPC Endpoints configured for the task VPC, we assume you would need an additional SSM PrivateLink to be configured. (yellow) # TODO: In the ideal world, the script should simply check if the task can reach to the internet or not :) taskNetworkingAttachment=$(echo "${describedTaskJson}" | jq -r ".tasks[0].attachments[0]")