Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
Executable
+639
@@ -0,0 +1,639 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#########################################################################################
|
||||
#### security-group-auditor.sh — Audit AWS Security Groups for risky configs ####
|
||||
#### Finds 0.0.0.0/0 rules, dangerous ports, all-port rules, unused groups ####
|
||||
#### Requires: bash 4+, aws CLI, jq ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version 1.01 ####
|
||||
#### ####
|
||||
#### Usage: ####
|
||||
#### ./security-group-auditor.sh --full ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#########################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Colors (pre-initialized) ─────────────────────────────────────────
|
||||
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
|
||||
|
||||
setup_colors() {
|
||||
if [[ "${COLOR:-auto}" == "never" ]]; then
|
||||
return
|
||||
fi
|
||||
if [[ "${COLOR:-auto}" == "always" ]] || [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
RESET='\033[0m'
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────
|
||||
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
||||
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${DIM}[DEBUG]${RESET} $*"; fi; }
|
||||
die() { err "$*"; exit 1; }
|
||||
|
||||
# ── Severity counters ────────────────────────────────────────────────
|
||||
TOTAL_CRIT=0
|
||||
TOTAL_WARN=0
|
||||
TOTAL_INFO=0
|
||||
TOTAL_OK=0
|
||||
|
||||
flag_crit() { ((TOTAL_CRIT++)) || true; }
|
||||
flag_warn() { ((TOTAL_WARN++)) || true; }
|
||||
flag_info() { ((TOTAL_INFO++)) || true; }
|
||||
flag_ok() { ((TOTAL_OK++)) || true; }
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
RUN_MODE=""
|
||||
DANGEROUS_PORTS="${DANGEROUS_PORTS:-22,3389,3306,5432,1433,6379,27017,9200,8080,8443}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
AWS_REGION=""
|
||||
VPC_ID=""
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
START_TIME=""
|
||||
|
||||
# ── Dependency and credential checks ────────────────────────────────
|
||||
check_deps() {
|
||||
command -v aws &>/dev/null || die "aws CLI is required (install: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)"
|
||||
command -v jq &>/dev/null || die "jq is required"
|
||||
}
|
||||
|
||||
check_credentials() {
|
||||
local identity
|
||||
identity=$(aws_cmd sts get-caller-identity --output json 2>/dev/null) \
|
||||
|| die "No valid AWS credentials — run 'aws configure' or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY"
|
||||
|
||||
local account arn
|
||||
account=$(echo "$identity" | jq -r '.Account')
|
||||
arn=$(echo "$identity" | jq -r '.Arn')
|
||||
|
||||
verbose "Account: ${account}"
|
||||
verbose "ARN: ${arn}"
|
||||
|
||||
local region
|
||||
region=$(aws_cmd configure get region 2>/dev/null || true)
|
||||
if [[ -z "$AWS_REGION" && -z "$region" ]]; then
|
||||
die "No region set — use --region or 'aws configure set region REGION' or set AWS_DEFAULT_REGION"
|
||||
fi
|
||||
|
||||
log "Account: ${account}"
|
||||
log "Region: ${AWS_REGION:-$region}"
|
||||
}
|
||||
|
||||
# ── aws wrapper ──────────────────────────────────────────────────────
|
||||
aws_cmd() {
|
||||
local args=("$@")
|
||||
if [[ -n "$AWS_REGION" ]]; then
|
||||
args+=(--region "$AWS_REGION")
|
||||
fi
|
||||
verbose "aws ${args[*]}"
|
||||
aws "${args[@]}"
|
||||
}
|
||||
|
||||
# ── Port-to-service mapping ─────────────────────────────────────────
|
||||
port_to_service() {
|
||||
local port="$1"
|
||||
case "$port" in
|
||||
22) echo "SSH" ;;
|
||||
80) echo "HTTP" ;;
|
||||
443) echo "HTTPS" ;;
|
||||
3306) echo "MySQL" ;;
|
||||
5432) echo "PostgreSQL" ;;
|
||||
1433) echo "MSSQL" ;;
|
||||
3389) echo "RDP" ;;
|
||||
6379) echo "Redis" ;;
|
||||
27017) echo "MongoDB" ;;
|
||||
9200) echo "Elasticsearch" ;;
|
||||
8080) echo "HTTP-Alt" ;;
|
||||
8443) echo "HTTPS-Alt" ;;
|
||||
53) echo "DNS" ;;
|
||||
25) echo "SMTP" ;;
|
||||
5900) echo "VNC" ;;
|
||||
11211) echo "Memcached" ;;
|
||||
2379) echo "etcd" ;;
|
||||
9090) echo "Prometheus" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── Check if port is in dangerous list ───────────────────────────────
|
||||
is_dangerous_port() {
|
||||
local port="$1"
|
||||
local IFS=','
|
||||
for dp in $DANGEROUS_PORTS; do
|
||||
if [[ "$port" == "$dp" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Check if dangerous port falls in a from-to range ────────────────
|
||||
port_in_range() {
|
||||
local port="$1" from="$2" to="$3"
|
||||
[[ "$port" -ge "$from" && "$port" -le "$to" ]]
|
||||
}
|
||||
|
||||
# ── Fetch security groups ───────────────────────────────────────────
|
||||
fetch_sgs() {
|
||||
local args=(ec2 describe-security-groups --output json)
|
||||
if [[ -n "$VPC_ID" ]]; then
|
||||
args+=(--filters "Name=vpc-id,Values=${VPC_ID}")
|
||||
fi
|
||||
aws_cmd "${args[@]}" 2>/dev/null | jq '.SecurityGroups'
|
||||
}
|
||||
|
||||
# ── Fetch ENI attachments (SG IDs in use) ────────────────────────────
|
||||
fetch_attached_sg_ids() {
|
||||
aws_cmd ec2 describe-network-interfaces --output json \
|
||||
--query 'NetworkInterfaces[].Groups[].GroupId' 2>/dev/null | jq -r '.[]' | sort -u
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# OPEN PORTS AUDIT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
audit_open_ports() {
|
||||
log "Auditing security groups for dangerous open ports..."
|
||||
log "Dangerous ports: ${DANGEROUS_PORTS}"
|
||||
echo ""
|
||||
|
||||
printf " %-24s %-14s %-14s %-8s %-12s %-18s %s\n" \
|
||||
"SG_NAME" "SG_ID" "VPC" "PROTO" "PORT" "SOURCE" "SEVERITY"
|
||||
printf " %s\n" "$(printf '%.0s─' {1..105})"
|
||||
|
||||
local sgs_json
|
||||
sgs_json=$(fetch_sgs)
|
||||
|
||||
echo "$sgs_json" | jq -c '.[]' 2>/dev/null | while IFS= read -r sg; do
|
||||
local sg_name sg_id vpc_id
|
||||
sg_name=$(echo "$sg" | jq -r '.GroupName')
|
||||
sg_id=$(echo "$sg" | jq -r '.GroupId')
|
||||
vpc_id=$(echo "$sg" | jq -r '.VpcId // "none"')
|
||||
|
||||
echo "$sg" | jq -c '.IpPermissions[]? // empty' 2>/dev/null | while IFS= read -r perm; do
|
||||
local protocol from_port to_port
|
||||
protocol=$(echo "$perm" | jq -r '.IpProtocol')
|
||||
from_port=$(echo "$perm" | jq -r '.FromPort // -1')
|
||||
to_port=$(echo "$perm" | jq -r '.ToPort // -1')
|
||||
|
||||
# Check for 0.0.0.0/0 or ::/0
|
||||
local has_open="false"
|
||||
local source_cidr=""
|
||||
while IFS= read -r cidr; do
|
||||
if [[ "$cidr" == "0.0.0.0/0" || "$cidr" == "::/0" ]]; then
|
||||
has_open="true"
|
||||
source_cidr="$cidr"
|
||||
break
|
||||
fi
|
||||
done < <(echo "$perm" | jq -r '(.IpRanges[].CidrIp // empty), (.Ipv6Ranges[].CidrIpv6 // empty)' 2>/dev/null)
|
||||
|
||||
[[ "$has_open" != "true" ]] && continue
|
||||
|
||||
# Protocol -1 means all traffic
|
||||
if [[ "$protocol" == "-1" ]]; then
|
||||
printf " %-24s %-14s %-14s %-8s %-12s %-18s %b%s%b\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" "all" "all" \
|
||||
"$source_cidr" "$RED" "CRITICAL" "$RESET"
|
||||
flag_crit
|
||||
continue
|
||||
fi
|
||||
|
||||
# No port range (e.g. icmp) — skip dangerous port check
|
||||
if [[ "$from_port" == "-1" && "$to_port" == "-1" && "$protocol" != "-1" ]]; then
|
||||
verbose "SG ${sg_id}: ${protocol} rule without port range — skipping port check"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if any dangerous port is in the from-to range
|
||||
local IFS=','
|
||||
for dp in $DANGEROUS_PORTS; do
|
||||
if port_in_range "$dp" "$from_port" "$to_port"; then
|
||||
local svc severity color
|
||||
svc=$(port_to_service "$dp")
|
||||
if [[ "$dp" == "80" || "$dp" == "443" ]]; then
|
||||
severity="INFO"; color="$CYAN"; flag_info
|
||||
else
|
||||
severity="CRITICAL"; color="$RED"; flag_crit
|
||||
fi
|
||||
local port_display="$dp"
|
||||
[[ -n "$svc" ]] && port_display="${dp} (${svc})"
|
||||
printf " %-24s %-14s %-14s %-8s %-12s %-18s %b%s%b\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" "$protocol" \
|
||||
"$port_display" "$source_cidr" "$color" "$severity" "$RESET"
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# PERMISSIVE RULES AUDIT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
audit_permissive() {
|
||||
log "Auditing overly permissive security groups..."
|
||||
echo ""
|
||||
|
||||
printf " %-24s %-14s %-14s %-18s %s\n" \
|
||||
"SG_NAME" "SG_ID" "VPC" "SOURCE" "SEVERITY"
|
||||
printf " %s\n" "$(printf '%.0s─' {1..85})"
|
||||
|
||||
local sgs_json
|
||||
sgs_json=$(fetch_sgs)
|
||||
|
||||
echo "$sgs_json" | jq -c '.[]' 2>/dev/null | while IFS= read -r sg; do
|
||||
local sg_name sg_id vpc_id
|
||||
sg_name=$(echo "$sg" | jq -r '.GroupName')
|
||||
sg_id=$(echo "$sg" | jq -r '.GroupId')
|
||||
vpc_id=$(echo "$sg" | jq -r '.VpcId // "none"')
|
||||
|
||||
echo "$sg" | jq -c '.IpPermissions[]? // empty' 2>/dev/null | while IFS= read -r perm; do
|
||||
local protocol
|
||||
protocol=$(echo "$perm" | jq -r '.IpProtocol')
|
||||
|
||||
[[ "$protocol" != "-1" ]] && continue
|
||||
|
||||
local source_cidr=""
|
||||
while IFS= read -r cidr; do
|
||||
if [[ "$cidr" == "0.0.0.0/0" || "$cidr" == "::/0" ]]; then
|
||||
source_cidr="$cidr"
|
||||
break
|
||||
fi
|
||||
done < <(echo "$perm" | jq -r '(.IpRanges[].CidrIp // empty), (.Ipv6Ranges[].CidrIpv6 // empty)' 2>/dev/null)
|
||||
|
||||
[[ -z "$source_cidr" ]] && continue
|
||||
|
||||
printf " %-24s %-14s %-14s %-18s %b%s%b\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" \
|
||||
"$source_cidr" "$RED" "CRITICAL" "$RESET"
|
||||
flag_crit
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# EGRESS AUDIT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
audit_egress() {
|
||||
log "Auditing egress security group rules..."
|
||||
echo ""
|
||||
|
||||
printf " %-24s %-14s %-14s %-18s %s\n" \
|
||||
"SG_NAME" "SG_ID" "VPC" "DESTINATION" "SEVERITY"
|
||||
printf " %s\n" "$(printf '%.0s─' {1..85})"
|
||||
|
||||
local sgs_json
|
||||
sgs_json=$(fetch_sgs)
|
||||
|
||||
echo "$sgs_json" | jq -c '.[]' 2>/dev/null | while IFS= read -r sg; do
|
||||
local sg_name sg_id vpc_id
|
||||
sg_name=$(echo "$sg" | jq -r '.GroupName')
|
||||
sg_id=$(echo "$sg" | jq -r '.GroupId')
|
||||
vpc_id=$(echo "$sg" | jq -r '.VpcId // "none"')
|
||||
|
||||
echo "$sg" | jq -c '.IpPermissionsEgress[]? // empty' 2>/dev/null | while IFS= read -r perm; do
|
||||
local protocol
|
||||
protocol=$(echo "$perm" | jq -r '.IpProtocol')
|
||||
|
||||
[[ "$protocol" != "-1" ]] && continue
|
||||
|
||||
local dest_cidr=""
|
||||
while IFS= read -r cidr; do
|
||||
if [[ "$cidr" == "0.0.0.0/0" || "$cidr" == "::/0" ]]; then
|
||||
dest_cidr="$cidr"
|
||||
break
|
||||
fi
|
||||
done < <(echo "$perm" | jq -r '(.IpRanges[].CidrIp // empty), (.Ipv6Ranges[].CidrIpv6 // empty)' 2>/dev/null)
|
||||
|
||||
[[ -z "$dest_cidr" ]] && continue
|
||||
|
||||
printf " %-24s %-14s %-14s %-18s %b%s%b\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" \
|
||||
"$dest_cidr" "$YELLOW" "WARN" "$RESET"
|
||||
flag_warn
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# UNUSED GROUPS AUDIT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
audit_unused() {
|
||||
log "Checking for unused security groups..."
|
||||
echo ""
|
||||
|
||||
printf " %-24s %-14s %-14s %-14s %s\n" \
|
||||
"SG_NAME" "SG_ID" "VPC" "ATTACHED_ENIS" "SEVERITY"
|
||||
printf " %s\n" "$(printf '%.0s─' {1..85})"
|
||||
|
||||
local sgs_json attached_ids
|
||||
sgs_json=$(fetch_sgs)
|
||||
attached_ids=$(fetch_attached_sg_ids)
|
||||
|
||||
echo "$sgs_json" | jq -c '.[]' 2>/dev/null | while IFS= read -r sg; do
|
||||
local sg_name sg_id vpc_id
|
||||
sg_name=$(echo "$sg" | jq -r '.GroupName')
|
||||
sg_id=$(echo "$sg" | jq -r '.GroupId')
|
||||
vpc_id=$(echo "$sg" | jq -r '.VpcId // "none"')
|
||||
|
||||
# Skip default SG — can't be deleted
|
||||
if [[ "$sg_name" == "default" ]]; then
|
||||
verbose "Skipping default SG ${sg_id} (cannot be deleted)"
|
||||
flag_ok
|
||||
continue
|
||||
fi
|
||||
|
||||
if echo "$attached_ids" | grep -qx "$sg_id"; then
|
||||
verbose "SG ${sg_id} (${sg_name}): attached to ENI(s)"
|
||||
flag_ok
|
||||
else
|
||||
printf " %-24s %-14s %-14s %-14s %b%s%b\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" \
|
||||
"0" "$YELLOW" "WARN — unused" "$RESET"
|
||||
flag_warn
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# LIST ALL RULES
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
list_rules() {
|
||||
log "Listing all security group rules..."
|
||||
echo ""
|
||||
|
||||
printf " %-24s %-14s %-14s %-10s %-8s %-12s %-18s\n" \
|
||||
"SG_NAME" "SG_ID" "VPC" "DIR" "PROTO" "PORTS" "SOURCE/DEST"
|
||||
printf " %s\n" "$(printf '%.0s─' {1..105})"
|
||||
|
||||
local sgs_json
|
||||
sgs_json=$(fetch_sgs)
|
||||
|
||||
echo "$sgs_json" | jq -c '.[]' 2>/dev/null | while IFS= read -r sg; do
|
||||
local sg_name sg_id vpc_id
|
||||
sg_name=$(echo "$sg" | jq -r '.GroupName')
|
||||
sg_id=$(echo "$sg" | jq -r '.GroupId')
|
||||
vpc_id=$(echo "$sg" | jq -r '.VpcId // "none"')
|
||||
|
||||
# Ingress rules
|
||||
echo "$sg" | jq -c '.IpPermissions[]? // empty' 2>/dev/null | while IFS= read -r perm; do
|
||||
local protocol from_port to_port port_str cidr_list
|
||||
protocol=$(echo "$perm" | jq -r '.IpProtocol')
|
||||
from_port=$(echo "$perm" | jq -r '.FromPort // -1')
|
||||
to_port=$(echo "$perm" | jq -r '.ToPort // -1')
|
||||
|
||||
if [[ "$protocol" == "-1" ]]; then
|
||||
port_str="all"
|
||||
protocol="all"
|
||||
elif [[ "$from_port" == "$to_port" ]]; then
|
||||
port_str="$from_port"
|
||||
else
|
||||
port_str="${from_port}-${to_port}"
|
||||
fi
|
||||
|
||||
cidr_list=$(echo "$perm" | jq -r '(.IpRanges[0].CidrIp // .Ipv6Ranges[0].CidrIpv6 // .UserIdGroupPairs[0].GroupId // "any")' 2>/dev/null)
|
||||
|
||||
printf " %-24s %-14s %-14s %b%-10s%b %-8s %-12s %-18s\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" "$CYAN" "INGRESS" "$RESET" \
|
||||
"$protocol" "${port_str:0:11}" "${cidr_list:0:17}"
|
||||
done
|
||||
|
||||
# Egress rules
|
||||
echo "$sg" | jq -c '.IpPermissionsEgress[]? // empty' 2>/dev/null | while IFS= read -r perm; do
|
||||
local protocol from_port to_port port_str cidr_list
|
||||
protocol=$(echo "$perm" | jq -r '.IpProtocol')
|
||||
from_port=$(echo "$perm" | jq -r '.FromPort // -1')
|
||||
to_port=$(echo "$perm" | jq -r '.ToPort // -1')
|
||||
|
||||
if [[ "$protocol" == "-1" ]]; then
|
||||
port_str="all"
|
||||
protocol="all"
|
||||
elif [[ "$from_port" == "$to_port" ]]; then
|
||||
port_str="$from_port"
|
||||
else
|
||||
port_str="${from_port}-${to_port}"
|
||||
fi
|
||||
|
||||
cidr_list=$(echo "$perm" | jq -r '(.IpRanges[0].CidrIp // .Ipv6Ranges[0].CidrIpv6 // .UserIdGroupPairs[0].GroupId // "any")' 2>/dev/null)
|
||||
|
||||
printf " %-24s %-14s %-14s %b%-10s%b %-8s %-12s %-18s\n" \
|
||||
"${sg_name:0:23}" "$sg_id" "${vpc_id:0:13}" "$YELLOW" "EGRESS" "$RESET" \
|
||||
"$protocol" "${port_str:0:11}" "${cidr_list:0:17}"
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# SUMMARY
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
print_summary() {
|
||||
local elapsed
|
||||
elapsed=$(( $(date +%s) - START_TIME ))
|
||||
|
||||
echo ""
|
||||
echo " ══════════════════════════════════════════"
|
||||
echo " Security Group Audit Summary"
|
||||
echo " ══════════════════════════════════════════"
|
||||
printf " %-20s %b%d%b\n" "CRITICAL:" "$RED" "$TOTAL_CRIT" "$RESET"
|
||||
printf " %-20s %b%d%b\n" "WARN:" "$YELLOW" "$TOTAL_WARN" "$RESET"
|
||||
printf " %-20s %b%d%b\n" "INFO:" "$CYAN" "$TOTAL_INFO" "$RESET"
|
||||
printf " %-20s %b%d%b\n" "OK:" "$GREEN" "$TOTAL_OK" "$RESET"
|
||||
echo " ──────────────────────────────────────────"
|
||||
printf " Completed in %ds\n" "$elapsed"
|
||||
echo ""
|
||||
|
||||
if [[ "$TOTAL_CRIT" -gt 0 ]]; then
|
||||
echo -e " ${RED}${BOLD}Action required:${RESET} ${TOTAL_CRIT} critical finding(s)"
|
||||
echo ""
|
||||
echo " Top recommendations:"
|
||||
echo " • Close 0.0.0.0/0 rules on SSH (22), RDP (3389), and database ports"
|
||||
echo " • Replace all-traffic (-1) rules with specific port/protocol pairs"
|
||||
echo " • Use VPC endpoints or NAT gateways instead of public access"
|
||||
echo " • Delete unused security groups to reduce attack surface"
|
||||
echo ""
|
||||
elif [[ "$TOTAL_WARN" -gt 0 ]]; then
|
||||
echo -e " ${YELLOW}Review recommended:${RESET} ${TOTAL_WARN} warning(s)"
|
||||
echo ""
|
||||
echo " Suggestions:"
|
||||
echo " • Review unused security groups for deletion"
|
||||
echo " • Restrict overly broad egress rules"
|
||||
echo " • Use security group references instead of CIDR blocks where possible"
|
||||
echo ""
|
||||
else
|
||||
echo -e " ${GREEN}All checks passed${RESET}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# USAGE
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
show_help() {
|
||||
cat <<EOF
|
||||
${BOLD}${SCRIPT_NAME}${RESET} — AWS Security Group Auditor
|
||||
|
||||
Audit AWS EC2 Security Groups for risky configurations
|
||||
via the AWS CLI. Read-only — never modifies security groups.
|
||||
|
||||
${BOLD}MODES${RESET}
|
||||
--full Run all audits
|
||||
--open-ports Find dangerous ports open to 0.0.0.0/0
|
||||
--permissive Find all-traffic rules open to the internet
|
||||
--unused Find security groups not attached to any ENI
|
||||
--egress Audit wide egress rules
|
||||
--rules List all security group rules
|
||||
|
||||
${BOLD}OPTIONS${RESET}
|
||||
--region REGION AWS region (overrides AWS_DEFAULT_REGION)
|
||||
--vpc VPC_ID Limit to specific VPC (vpc-xxx)
|
||||
--ports PORTS Override dangerous ports (comma-separated)
|
||||
--verbose Debug output
|
||||
--no-color Disable colored output
|
||||
--help Show this help message
|
||||
|
||||
${BOLD}ENVIRONMENT VARIABLES${RESET}
|
||||
DANGEROUS_PORTS Comma-separated ports to flag (default: 22,3389,...)
|
||||
VERBOSE Enable verbose output (true/false)
|
||||
COLOR Color mode: auto, always, never
|
||||
AWS_DEFAULT_REGION Default AWS region
|
||||
|
||||
${BOLD}EXAMPLES${RESET}
|
||||
# Full audit
|
||||
${SCRIPT_NAME} --full
|
||||
|
||||
# Check open ports only
|
||||
${SCRIPT_NAME} --open-ports
|
||||
|
||||
# Audit a specific region
|
||||
${SCRIPT_NAME} --full --region us-east-1
|
||||
|
||||
# Filter by VPC
|
||||
${SCRIPT_NAME} --open-ports --vpc vpc-0abc123def456
|
||||
|
||||
# Custom dangerous ports
|
||||
${SCRIPT_NAME} --open-ports --ports "22,3389,5432,6379"
|
||||
|
||||
# List all security group rules
|
||||
${SCRIPT_NAME} --rules
|
||||
|
||||
${BOLD}EXIT CODES${RESET}
|
||||
0 All checks passed
|
||||
1 Warnings found (review recommended)
|
||||
2 Critical findings (action required)
|
||||
EOF
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# PARSE ARGS
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
parse_args() {
|
||||
local modes=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--full)
|
||||
modes=(open-ports permissive unused egress)
|
||||
shift ;;
|
||||
--open-ports)
|
||||
modes+=(open-ports); shift ;;
|
||||
--permissive)
|
||||
modes+=(permissive); shift ;;
|
||||
--unused)
|
||||
modes+=(unused); shift ;;
|
||||
--egress)
|
||||
modes+=(egress); shift ;;
|
||||
--rules)
|
||||
modes+=(rules); shift ;;
|
||||
--region)
|
||||
AWS_REGION="${2:?--region requires a value}"; shift 2 ;;
|
||||
--vpc)
|
||||
VPC_ID="${2:?--vpc requires a value}"; shift 2 ;;
|
||||
--ports)
|
||||
DANGEROUS_PORTS="${2:?--ports requires a value}"; shift 2 ;;
|
||||
--verbose)
|
||||
VERBOSE="true"; shift ;;
|
||||
--no-color)
|
||||
COLOR="never"; shift ;;
|
||||
--help|-h)
|
||||
setup_colors; show_help; exit 0 ;;
|
||||
*)
|
||||
die "Unknown option: $1 (see --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${#modes[@]} -eq 0 ]]; then
|
||||
err "No audit mode specified"
|
||||
echo "Run ${SCRIPT_NAME} --help for usage" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RUN_MODE="${modes[*]}"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
main() {
|
||||
parse_args "$@"
|
||||
setup_colors
|
||||
check_deps
|
||||
check_credentials
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}AWS Security Group Auditor${RESET}"
|
||||
echo -e "Region: ${AWS_REGION:-$(aws configure get region 2>/dev/null || echo 'default')}"
|
||||
echo -e "Mode: ${RUN_MODE}"
|
||||
if [[ -n "$VPC_ID" ]]; then
|
||||
echo -e "VPC: ${VPC_ID}"
|
||||
fi
|
||||
echo -e "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo ""
|
||||
|
||||
for mode in $RUN_MODE; do
|
||||
case "$mode" in
|
||||
open-ports) audit_open_ports ;;
|
||||
permissive) audit_permissive ;;
|
||||
unused) audit_unused ;;
|
||||
egress) audit_egress ;;
|
||||
rules) list_rules ;;
|
||||
esac
|
||||
done
|
||||
|
||||
print_summary
|
||||
|
||||
if [[ "$TOTAL_CRIT" -gt 0 ]]; then
|
||||
exit 2
|
||||
elif [[ "$TOTAL_WARN" -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user