162 lines
4.5 KiB
Bash
162 lines
4.5 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -o errexit
|
||
|
|
set -o nounset
|
||
|
|
set -o pipefail
|
||
|
|
|
||
|
|
host_name=""
|
||
|
|
port=""
|
||
|
|
count=3
|
||
|
|
timeout_seconds=3
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
cat <<'USAGE'
|
||
|
|
Usage: check_dns_connectivity.sh --host HOST [--port PORT] [--count COUNT] [--timeout SECONDS] [--help]
|
||
|
|
|
||
|
|
Check DNS resolution, ping, optional TCP connectivity, and local route hints.
|
||
|
|
USAGE
|
||
|
|
}
|
||
|
|
|
||
|
|
is_number() {
|
||
|
|
[[ "$1" =~ ^[0-9]+$ ]]
|
||
|
|
}
|
||
|
|
|
||
|
|
while (($# > 0)); do
|
||
|
|
case "$1" in
|
||
|
|
--host) [[ $# -ge 2 ]] || { printf 'CRITICAL: --host requires a value\n'; exit 2; }; host_name="$2"; shift 2 ;;
|
||
|
|
--port) [[ $# -ge 2 ]] || { printf 'CRITICAL: --port requires a value\n'; exit 2; }; port="$2"; shift 2 ;;
|
||
|
|
--count) [[ $# -ge 2 ]] || { printf 'CRITICAL: --count requires a value\n'; exit 2; }; count="$2"; shift 2 ;;
|
||
|
|
--timeout) [[ $# -ge 2 ]] || { printf 'CRITICAL: --timeout requires a value\n'; exit 2; }; timeout_seconds="$2"; shift 2 ;;
|
||
|
|
--help|-h) usage; exit 0 ;;
|
||
|
|
*) printf 'CRITICAL: unknown option: %s\n' "$1"; usage; exit 2 ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
|
||
|
|
if [[ -z "$host_name" ]]; then
|
||
|
|
printf 'CRITICAL: --host is required\n'
|
||
|
|
usage
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
for value in "$count" "$timeout_seconds"; do
|
||
|
|
if ! is_number "$value"; then
|
||
|
|
printf 'CRITICAL: numeric option expected, got: %s\n' "$value"
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
if [[ -n "$port" ]] && ! is_number "$port"; then
|
||
|
|
printf 'CRITICAL: --port must be numeric\n'
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
if ! command -v getent >/dev/null 2>&1; then
|
||
|
|
printf 'CRITICAL: required command not found: getent\n'
|
||
|
|
exit 2
|
||
|
|
fi
|
||
|
|
|
||
|
|
dns_ok=0
|
||
|
|
ping_ok=0
|
||
|
|
tcp_ok=0
|
||
|
|
tcp_checked=0
|
||
|
|
tcp_note=""
|
||
|
|
ping_output="$(mktemp)"
|
||
|
|
trap 'rm -f "$ping_output"' EXIT
|
||
|
|
|
||
|
|
dns_output="$(getent hosts "$host_name" 2>/dev/null || true)"
|
||
|
|
if [[ -n "$dns_output" ]]; then
|
||
|
|
dns_ok=1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if command -v ping >/dev/null 2>&1; then
|
||
|
|
if ping -c "$count" -W "$timeout_seconds" "$host_name" > "$ping_output" 2>&1; then
|
||
|
|
ping_ok=1
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
printf 'WARNING: ping command not available; ICMP check skipped\n' > "$ping_output"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [[ -n "$port" ]]; then
|
||
|
|
tcp_checked=1
|
||
|
|
if command -v timeout >/dev/null 2>&1; then
|
||
|
|
if timeout "$timeout_seconds" bash -c ":</dev/tcp/${host_name}/${port}" >/dev/null 2>&1; then
|
||
|
|
tcp_ok=1
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
tcp_note="WARNING: timeout command not available; TCP /dev/tcp check used without external timeout"
|
||
|
|
if bash -c ":</dev/tcp/${host_name}/${port}" >/dev/null 2>&1; then
|
||
|
|
tcp_ok=1
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
status="OK"
|
||
|
|
exit_code=0
|
||
|
|
if ((dns_ok == 0)); then
|
||
|
|
status="CRITICAL"
|
||
|
|
exit_code=3
|
||
|
|
elif ((tcp_checked == 1 && tcp_ok == 0)); then
|
||
|
|
status="CRITICAL"
|
||
|
|
exit_code=3
|
||
|
|
elif command -v ping >/dev/null 2>&1 && ((ping_ok == 0)); then
|
||
|
|
status="WARNING"
|
||
|
|
exit_code=1
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf '%s: DNS=%s ping=%s' "$status" "$([[ "$dns_ok" == 1 ]] && printf OK || printf FAILED)" "$([[ "$ping_ok" == 1 ]] && printf OK || printf UNKNOWN_OR_FAILED)"
|
||
|
|
if ((tcp_checked == 1)); then
|
||
|
|
printf ' tcp_%s=%s' "$port" "$([[ "$tcp_ok" == 1 ]] && printf OK || printf FAILED)"
|
||
|
|
fi
|
||
|
|
printf '\n\n'
|
||
|
|
|
||
|
|
printf 'DNS result:\n'
|
||
|
|
if [[ -n "$dns_output" ]]; then
|
||
|
|
printf '%s\n' "$dns_output"
|
||
|
|
else
|
||
|
|
printf 'CRITICAL: getent hosts returned no records for %s\n' "$host_name"
|
||
|
|
fi
|
||
|
|
printf '\n'
|
||
|
|
|
||
|
|
printf 'Ping result:\n'
|
||
|
|
if [[ -s "$ping_output" ]]; then
|
||
|
|
cat "$ping_output"
|
||
|
|
else
|
||
|
|
printf 'WARNING: ping result unavailable or ping command missing\n'
|
||
|
|
fi
|
||
|
|
printf '\n'
|
||
|
|
|
||
|
|
if ((tcp_checked == 1)); then
|
||
|
|
printf 'TCP port result:\n'
|
||
|
|
if ((tcp_ok == 1)); then
|
||
|
|
printf 'OK: TCP connection to %s:%s succeeded\n' "$host_name" "$port"
|
||
|
|
else
|
||
|
|
printf 'CRITICAL: TCP connection to %s:%s failed or timed out\n' "$host_name" "$port"
|
||
|
|
fi
|
||
|
|
if [[ -n "$tcp_note" ]]; then
|
||
|
|
printf '%s\n' "$tcp_note"
|
||
|
|
fi
|
||
|
|
printf '\n'
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf 'Local network hints:\n'
|
||
|
|
if command -v ip >/dev/null 2>&1; then
|
||
|
|
ip route show default 2>/dev/null || printf 'WARNING: unable to read default route\n'
|
||
|
|
elif command -v ss >/dev/null 2>&1; then
|
||
|
|
ss -tuln 2>/dev/null | head -n 20 || printf 'WARNING: unable to read socket summary\n'
|
||
|
|
else
|
||
|
|
printf 'WARNING: ip and ss are unavailable; local network hints skipped\n'
|
||
|
|
fi
|
||
|
|
printf '\n'
|
||
|
|
|
||
|
|
printf 'Evidence:\n'
|
||
|
|
printf 'Host: %s count=%s timeout=%ss port=%s\n' "$host_name" "$count" "$timeout_seconds" "${port:-not checked}"
|
||
|
|
if [[ -n "$tcp_note" ]]; then
|
||
|
|
printf '%s\n' "$tcp_note"
|
||
|
|
fi
|
||
|
|
printf '\n'
|
||
|
|
|
||
|
|
printf 'Recommended next steps:\n'
|
||
|
|
printf -- '- Verify the DNS record and resolver path\n'
|
||
|
|
printf -- '- Check firewall, routing, security group, or proxy policy\n'
|
||
|
|
printf -- '- Compare results from another host or network segment\n'
|
||
|
|
printf -- '- Check application endpoint health after network reachability is confirmed\n'
|
||
|
|
printf -- '- Attach this output to incident ticket\n'
|
||
|
|
|
||
|
|
exit "$exit_code"
|