#!/bin/bash
# check_docker_permissions.sh - 容器权限检测工具
# 配置参数
LOG_DIR="./docker_check" # 定义日志目录的路径
TS=$(date +%Y%m%d_%H%M%S) # 获取当前时间戳,格式为 YYYYMMDD_HHMMSS
LOG_FILE="${LOG_DIR}/check_${TS}.log" # 定义日志文件的完整路径,包含时间戳
# 颜色定义
RED='\033[31m'; GREEN='\033[32m'; YELLOW='\033[33m'; CYAN='\033[36m'; RESET='\033[0m' # 定义不同颜色的 ANSI 转义码,用于在终端输出不同颜色的文本
# 检查命令是否存在
check_command() {
# 函数功能:检查指定的命令是否存在
# 参数:$1 为要检查的命令名称
local cmd=$1
if ! command -v "$cmd" >/dev/null 2>&1; then
# 如果命令不存在,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: $cmd 命令未找到,请确保已安装。${RESET}" >&2
exit 1
fi
}
# 初始化日志
init_log() {
# 函数功能:初始化日志,包括创建日志目录和日志文件,并记录检测开始时间
check_command mkdir # 检查 mkdir 命令是否存在
check_command touch # 检查 touch 命令是否存在
check_command date # 检查 date 命令是否存在
if ! mkdir -p "$LOG_DIR"; then
# 如果无法创建日志目录,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: 无法创建日志目录 $LOG_DIR。${RESET}" >&2
exit 1
fi
if ! touch "$LOG_FILE"; then
# 如果无法创建日志文件,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: 无法创建日志文件 $LOG_FILE。${RESET}" >&2
exit 1
fi
# 输出检测开始时间,使用青色字体,并将信息同时输出到终端和日志文件
echo -e "${CYAN}检测开始时间: $(date +'%Y-%m-%d %H:%M:%S')${RESET}\n" | tee -a "$LOG_FILE"
}
# 权限检测
check_volume_permission() {
local container=$1
check_command docker
echo -e "\n${CYAN}持久化目录检测:${RESET}" | tee -a "$LOG_FILE"
local mounts=()
if ! docker inspect "$container" --format '{{range .Mounts}}{{.Source}}:{{.Destination}}{{"\n"}}{{end}}'| grep -v '^$' >/dev/null 2>&1; then
echo -e "${RED}错误: 无法获取容器 $container 的挂载信息。${RESET}" >&2
return 1
fi
# 创建一个临时文件
temp_file=$(mktemp)
# 将 docker inspect 的输出保存到临时文件中
docker inspect "$container" --format '{{range .Mounts}}{{.Source}}:{{.Destination}}{{"\n"}}{{end}}' | grep -v '^$' > "$temp_file"
# 使用 while 循环和 read 命令逐行读取临时文件内容并添加到数组中
while IFS= read -r line; do
mounts+=("$line")
done < "$temp_file"
# 删除临时文件
rm "$temp_file"
for i in "${!mounts[@]}"; do
IFS=':' read -r host_path container_path <<< "${mounts[$i]}"
echo -e " ${YELLOW}[映射$((i+1))]${RESET}" | tee -a "$LOG_FILE"
echo -e " ├─ 宿主机路径: $host_path" | tee -a "$LOG_FILE"
echo -e " ├─ 容器内路径: $container_path" | tee -a "$LOG_FILE"
# 获取目录或文件权限信息
local permissions=$(docker exec "$container" ls -lhd "$container_path" 2>/dev/null)
if [ -z "$permissions" ]; then
echo -e " ├─ 权限信息: ${RED}无法获取权限信息${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 权限信息: $permissions" | tee -a "$LOG_FILE"
fi
# 判断是文件还是目录
local is_file=$(docker exec "$container" test -f "$container_path" && echo "true" || echo "false")
if [ "$is_file" = "true" ]; then
# 文件权限检测
# 尝试读取文件以检测读取权限
if docker exec "$container" cat "$container_path" &>/dev/null; then
echo -e " ├─ 读取权限: ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 读取权限: ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
fi
# 尝试执行文件以检测执行权限
if docker exec "$container" chmod +x "$container_path" &>/dev/null && docker exec "$container" "$container_path" &>/dev/null; then
echo -e " ├─ 执行权限: ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 执行权限: ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
fi
# 尝试写入文件(追加)以检测写入权限
if docker exec -i "$container" sh -c "echo 'test' >> $container_path" &>/dev/null; then
echo -e " ├─ 写入权限: ${GREEN}✓ 写入成功${RESET}" | tee -a "$LOG_FILE"
# 恢复文件
docker exec "$container" sed -i '$d' "$container_path" &>/dev/null
else
echo -e " ├─ 写入权限: ${RED}✗ 拒绝写入${RESET}" | tee -a "$LOG_FILE"
fi
else
# 目录权限检测
local test_file="${container_path}/.perm_test_${RANDOM}"
# 尝试创建文件以检测写入权限
if docker exec "$container" touch "$test_file" &>/dev/null; then
echo -e " ├─ 写入权限: ${GREEN}✓ 写入成功${RESET}" | tee -a "$LOG_FILE"
# 尝试读取文件以检测读取权限
if docker exec "$container" cat "$test_file" &>/dev/null; then
echo -e " ├─ 读取权限: ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 读取权限: ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
fi
# 尝试执行文件以检测执行权限
if docker exec "$container" chmod +x "$test_file" &>/dev/null && docker exec "$container" "$test_file" &>/dev/null; then
echo -e " ├─ 执行权限: ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 执行权限: ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
fi
# 清理测试文件
if docker exec "$container" rm "$test_file" &>/dev/null; then
echo -e " └─ 清理测试文件: ${GREEN}✓ 成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " └─ 清理测试文件: ${RED}✗ 失败${RESET}" | tee -a "$LOG_FILE"
fi
else
echo -e " ├─ 写入权限: ${RED}✗ 拒绝写入${RESET}" | tee -a "$LOG_FILE"
# 尝试寻找已存在的文件进行读取和执行权限检测
local existing_files=$(docker exec "$container" ls "$container_path" 2>/dev/null)
if [ -z "$existing_files" ]; then
echo -e " ├─ 目录为空,无法完成读取和执行权限检测。${RED}${RESET}" | tee -a "$LOG_FILE"
else
for existing_file in $existing_files; do
# 尝试读取文件以检测读取权限
if docker exec "$container" cat "${container_path}/${existing_file}" &>/dev/null; then
echo -e " ├─ 读取权限 (${existing_file}): ${GREEN}✓ 读取成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 读取权限 (${existing_file}): ${RED}✗ 拒绝读取${RESET}" | tee -a "$LOG_FILE"
fi
# 尝试执行文件以检测执行权限
if docker exec "$container" chmod +x "${container_path}/${existing_file}" &>/dev/null && docker exec "$container" "${container_path}/${existing_file}" &>/dev/null; then
echo -e " ├─ 执行权限 (${existing_file}): ${GREEN}✓ 执行成功${RESET}" | tee -a "$LOG_FILE"
else
echo -e " ├─ 执行权限 (${existing_file}): ${RED}✗ 拒绝执行${RESET}" | tee -a "$LOG_FILE"
fi
done
fi
fi
fi
done
}
# 主流程
init_log # 初始化日志
check_command docker # 检查 docker 命令是否存在
if [ -z "$1" ]; then
# 如果没有提供参数,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: 请提供 '容器名称' 或 'all' 作为参数。${RESET}" >&2
exit 1
fi
if [ "$1" = "all" ]; then
# 如果参数为 all,获取所有运行容器的 ID和名称,以列表展示
container_names=$(docker ps|awk '{print $1,$NF}')
echo -e "\n${CYAN}=== 容器列表 ===${CYAN}" | tee -a "$LOG_FILE"
echo -e "${CYAN}${GREEN}${container_names}${CYAN}" | tee -a "$LOG_FILE"
# 如果参数为 all,获取所有运行容器的 ID
container_ids=$(docker ps -q)
if [ -z "$container_ids" ]; then
# 如果没有运行的容器,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: 没有运行的容器。${RESET}" >&2
exit 1
fi
for container_id in $container_ids; do
# 获取容器的名称
container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's|^/||')
# 输出检测容器信息,使用青色和绿色字体,并将信息同时输出到终端和日志文件
echo -e "\n${CYAN}=== 检测容器: ${GREEN}${container_name}${CYAN} (${container_id:0:12}) ===${RESET}" | tee -a "$LOG_FILE"
## 系统信息
#echo -e "${CYAN}[系统信息]" | tee -a "$LOG_FILE"
#if ! docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release" >/dev/null 2>&1; then
# # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
# echo -e "${RED}错误: 无法获取容器 $container_id 的操作系统版本信息。${RESET}" >&2
#else
# # 输出容器的操作系统信息,并将信息同时输出到终端和日志文件
# docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release"|awk -F\" '{print $2}'| tee -a "$LOG_FILE"
#fi
## 用户信息
#echo -e "${CYAN}[系统信息]" | tee -a "$LOG_FILE"
#if ! docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release" >/dev/null 2>&1; then
# # 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
# echo -e "${RED}错误: 无法获取容器 $container_id 的操作系统版本信息。${RESET}" >&2
#else
# # 输出容器的操作系统信息,并将信息同时输出到终端和日志文件
# docker exec "$container_id" grep "PRETTY_NAME" "/etc/os-release"|awk -F\" '{print $2}'| tee -a "$LOG_FILE"
#fi
# 进程检测
echo -e "${CYAN}[进程信息]" | tee -a "$LOG_FILE"
if ! docker top "$container_id" >/dev/null 2>&1; then
# 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
echo -e "${RED}错误: 无法获取容器 $container_id 的进程信息。${RESET}" >&2
else
# 格式化输出容器的进程信息,并将信息同时输出到终端和日志文件
docker top "$container_id" |awk -F" " '{print $1,$2,$3,$NF}'| tee -a "$LOG_FILE"
fi
check_volume_permission "$container_id" # 检测容器的持久化目录权限
done
else
# 如果参数不是 all,获取指定容器的 ID
container_id=$(docker ps -q -f "name=$1")
if [ -z "$container_id" ]; then
# 如果未找到指定的容器,输出错误信息并退出脚本,错误信息使用红色字体
echo -e "${RED}错误: 未找到名称为 $1 的运行容器。${RESET}" >&2
exit 1
fi
# 获取容器的名称
container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's|^/||')
# 输出检测容器信息,使用青色和绿色字体,并将信息同时输出到终端和日志文件
echo -e "\n${CYAN}=== 检测容器: ${GREEN}${container_name}${CYAN} (${container_id:0:12}) ===${RESET}" | tee -a "$LOG_FILE"
# 进程检测
echo -e "${CYAN}[进程信息]" | tee -a "$LOG_FILE"
if ! docker top "$container_id" >/dev/null 2>&1; then
# 如果无法获取容器的进程信息,输出错误信息,错误信息使用红色字体
echo -e "${RED}错误: 无法获取容器 $container_id 的进程信息。${RESET}" >&2
else
# 格式化输出容器的进程信息,并将信息同时输出到终端和日志文件
docker top "$container_id" |awk -F" " '{print $1,$2,$3,$NF}' | tee -a "$LOG_FILE"
fi
check_volume_permission "$container_id" # 检测容器的持久化目录权限
fi
# 输出检测日志的保存路径,使用青色和黄色字体,并将信息同时输出到终端和日志文件
echo -e "\n${CYAN}检测日志已保存至: ${YELLOW}${LOG_FILE}${RESET}" | tee -a "$LOG_FILE"s