Last active
May 6, 2025 21:30
-
-
Save wellington1993/9997dec4724048094d7daceb3ed52286 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ----------------------------------------------------------------------------- | |
# Script: report-portas-mesclado-ipv4-final-adaptado.sh | |
# Descricao: Mapeia conexoes TCP internas IPv4, usando root se disponível. | |
# Prioriza ESTAB para outros hosts. | |
# Exibe ESTABLISHED entre hosts internos diferentes E LISTEN. | |
# Oculta TODAS as conexoes ESTAB puramente locais (incluindo loopback). | |
# Oculta conexoes para/de IPs externos publicos IPv4. | |
# Exibe apenas IPv4. Remove linhas duplicadas. Ordena por Categoria, Server, Servico. | |
# ----------------------------------------------------------------------------- | |
# --- Configuracoes --- | |
INTERNAL_PREFIX="192.168" | |
LOCAL_HOSTNAME=$(hostname) | |
LOCAL_IP=$(hostname -I | awk '{print $1}') | |
# Defina larguras fixas para as colunas (ajuste conforme necessario) | |
w1=36 # Cliente | |
w2=36 # Server | |
w3=20 # Servico | |
# Abreviacao de hostnames: no maximo 13 caracteres (ex.: HS-SRV-APP-03) | |
HOST_MAX=13 | |
# --- Verificar se é root --- | |
if [ "$(id -u)" -eq 0 ]; then | |
IS_ROOT=1 | |
else | |
IS_ROOT=0 | |
echo "Aviso: Executando sem root. O relatório pode estar incompleto (apenas conexões do usuário atual)." | |
fi | |
# --- Funcoes de Pre-processamento de Conexao --- | |
if [ "$IS_ROOT" -eq 1 ]; then | |
# Coleta dados brutos de lsof para conexoes ESTABLISHED e LISTEN | |
function get_raw_connections_lsof() { | |
lsof -iTCP -P -n 2>/dev/null | awk ' | |
/TCP/ { | |
if ($9 ~ /->/ && $0 ~ /\(ESTABLISHED\)/) { | |
split($9, conn_parts, "->"); local_part_str = conn_parts[1]; remote_part_str = conn_parts[2]; | |
gsub(/\*:/, "0.0.0.0:", local_part_str); | |
if (match(local_part_str, /^(.*):([^:]+)$/, arr)) { local_ip = arr[1]; local_port = arr[2]; gsub(/\[|\]/, "", local_ip); } | |
else { local_ip="err_lsof_li"; local_port="err_lsof_lp"; } | |
gsub(/\*:/, "0.0.0.0:", remote_part_str); | |
if (match(remote_part_str, /^(.*):([^:]+)$/, arr)) { remote_ip = arr[1]; remote_port = arr[2]; gsub(/\[|\]/, "", remote_ip); } | |
else { remote_ip="err_lsof_ri"; remote_port="err_lsof_rp"; } | |
print "ESTABLISHED", local_ip, local_port, remote_ip, remote_port; | |
} else if ($0 ~ /\(LISTEN\)/) { | |
listen_addr_port = $9; | |
gsub(/\*:/, "0.0.0.0:", listen_addr_port); gsub(/\[::\]:/, ":::", listen_addr_port); | |
if (match(listen_addr_port, /^(.*):([^:]+)$/, arr)) { listen_ip = arr[1]; listen_port = arr[2]; gsub(/\[|\]/, "", listen_ip); } | |
else { listen_ip="err_lsof_lni"; listen_port="err_lsof_lnp"; } | |
print "LISTEN", listen_ip, listen_port; | |
} | |
}' | |
} | |
# Coleta dados brutos de ss para conexoes ESTAB e LISTEN | |
function get_raw_connections_ss() { | |
ss -tna 2>/dev/null | awk ' | |
NR == 1 { next } # Pula o cabecalho do ss | |
{ | |
state = $1; local_addr_port = $4; peer_addr_port = $5; | |
if (match(local_addr_port, /^(.*):([^:]+)$/, arr_local)) { local_ip = arr_local[1]; local_port = arr_local[2]; gsub(/\[|\]/, "", local_ip); } | |
else { local_ip="err_ss_li"; local_port="err_ss_lp"; } | |
if (state == "LISTEN") { | |
print "LISTEN", local_ip, local_port; | |
} else if (state == "ESTAB") { | |
if (match(peer_addr_port, /^(.*):([^:]+)$/, arr_peer)) { peer_ip = arr_peer[1]; peer_port = arr_peer[2]; gsub(/\[|\]/, "", peer_ip); } | |
else { peer_ip="err_ss_pi"; peer_port="err_ss_pp"; } | |
print "ESTABLISHED", local_ip, local_port, peer_ip, peer_port; | |
} | |
}' | |
} | |
# --- Coleta de Dados Mesclados --- | |
RAW_CONNECTIONS=$( (get_raw_connections_lsof; get_raw_connections_ss) ) | |
else | |
# Coleta dados brutos de /proc/net/tcp para conexoes ESTABLISHED e LISTEN | |
function get_raw_connections_proc() { | |
awk ' | |
NR > 1 { | |
split($2, local_addr, ":") | |
split($3, remote_addr, ":") | |
local_ip = sprintf("%d.%d.%d.%d", strtonum("0x" substr(local_addr[1],1,2)), strtonum("0x" substr(local_addr[1],3,2)), strtonum("0x" substr(local_addr[1],5,2)), strtonum("0x" substr(local_addr[1],7,2))) | |
local_port = strtonum("0x" local_addr[2]) | |
remote_ip = sprintf("%d.%d.%d.%d", strtonum("0x" substr(remote_addr[1],1,2)), strtonum("0x" substr(remote_addr[1],3,2)), strtonum("0x" substr(remote_addr[1],5,2)), strtonum("0x" substr(remote_addr[1],7,2))) | |
remote_port = strtonum("0x" remote_addr[2]) | |
state = $4 | |
if (state == "01") { # 01 = ESTABLISHED | |
print "ESTABLISHED", local_ip, local_port, remote_ip, remote_port | |
} else if (state == "0A") { # 0A = LISTEN | |
print "LISTEN", local_ip, local_port | |
} | |
}' /proc/net/tcp | |
} | |
# --- Coleta de Dados Mesclados --- | |
RAW_CONNECTIONS=$(get_raw_connections_proc) | |
fi | |
# --- Processamento AWK Principal, Filtragem, Categorizacao e Geracao de Saida --- | |
# AWK gera "Categoria|LinhaFormatada". Ordenacao no bash. | |
RESULT=$(echo "$RAW_CONNECTIONS" | awk -v prefix="$INTERNAL_PREFIX" \ | |
-v local_hostname="$LOCAL_HOSTNAME" -v local_ip_main="$LOCAL_IP" \ | |
-v w1="$w1" -v w2="$w2" -v w3="$w3" \ | |
-v HOST_MAX="$HOST_MAX" ' | |
BEGIN { | |
# Mapeamento de portas conhecidas (ajuste conforme sua necessidade) | |
svc["21"] = "ftp"; svc["22"] = "SSH"; svc["23"] = "telnet"; svc["25"] = "SMTP"; | |
svc["53"] = "domain"; svc["80"] = "HTTP"; svc["110"] = "pop3"; svc["111"] = "rpcbind"; | |
svc["137"] = "netbios-ns"; svc["138"] = "netbios-dgm"; svc["139"] = "netbios-ssn"; | |
svc["143"] = "imap"; svc["443"] = "HTTPS"; svc["445"] = "microsoft-ds"; | |
svc["514"] = "syslog"; svc["631"] = "ipp"; svc["993"] = "imaps"; svc["995"] = "pop3s"; | |
svc["1716"] = "kdeconnect"; svc["2049"] = "nfs"; svc["2947"] = "gpsd"; svc["3050"] = "gds-db"; | |
svc["3306"] = "MySQL"; svc["3389"] = "RDP"; svc["5432"] = "PostgreSQL"; | |
svc["5900"] = "VNC"; svc["6379"] = "Redis"; svc["11211"] = "Memcached"; | |
svc["5938"] = "TeamViewer"; svc["7071"] = "unknown_app"; svc["12501"]= "SSH"; | |
svc["10808"]= "unknown"; svc["199"] = "smux"; svc["39115"]= "unknown"; | |
svc["42957"]= "unknown"; svc["5201"] = "unknown"; svc["54165"]= "unknown"; | |
svc["57621"]= "unknown"; svc["9050"] = "unknown"; | |
} | |
# Funcao para encurtar APENAS NOMES DE HOST para exibicao | |
function shorten_hostname(hostname_str, max_len) { | |
max_len=HOST_MAX; | |
gsub(/\..*$/, "", hostname_str); | |
return (length(hostname_str)>max_len) ? substr(hostname_str,1,max_len)"..." : hostname_str; | |
} | |
# Funcao para verificar se um IP representa ESTA maquina especifica | |
function is_this_machine(ip_addr, main_local_ip) { | |
if (ip_addr == main_local_ip || ip_addr == "127.0.0.1" || ip_addr == "0.0.0.0") { | |
return 1; | |
} | |
return 0; | |
} | |
# Funcao para verificar se um IP esta dentro do escopo interno | |
function is_internal_scope(ip_chk, prefix, main_local_ip) { | |
if (is_this_machine(ip_chk, main_local_ip)) return 1; | |
if (ip_chk ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) { | |
return (index(ip_chk,prefix)==1); | |
} | |
return 0; | |
} | |
# Funcao para resolver IP -> "hostname_curto (IP_COMPLETO)" ou "IP_COMPLETO" | |
function resolve_ip_display(ip_addr, main_local_ip, current_local_hostname) { | |
if (is_this_machine(ip_addr, main_local_ip)) { | |
display_ip_used = ip_addr; | |
if (ip_addr == "0.0.0.0") display_ip_used = main_local_ip; | |
return shorten_hostname(current_local_hostname) " (" display_ip_used ")"; | |
} else { | |
cmd = "getent hosts " ip_addr " | awk \047{print $2; exit}\047"; | |
resolved_hostname = ""; | |
pipe_status = (cmd | getline resolved_hostname); | |
close(cmd); | |
if (pipe_status > 0 && resolved_hostname != "" && resolved_hostname != ip_addr) { | |
short_host = shorten_hostname(resolved_hostname); | |
return short_host " (" ip_addr ")"; | |
} else { | |
return ip_addr; | |
} | |
} | |
} | |
# Funcao para resolver porta -> "porta (servico)" | |
function res_svc(p_str) { | |
if (p_str in svc) { | |
return p_str " (" svc[p_str] ")"; | |
} else { | |
cmd = "getent services " p_str "/tcp 2>/dev/null"; | |
svc_l = ""; | |
if ((cmd | getline svc_l) > 0 && svc_l != "" && svc_l !~ /^#/) { | |
split(svc_l, svc_a, /[ \t]+/); | |
s_name = svc_a[1]; | |
ret_val = p_str " (" s_name ")"; | |
} else { | |
ret_val = p_str " (unknown)"; | |
} | |
close(cmd); | |
return ret_val; | |
} | |
} | |
{ | |
type=$1; lip=$2; lport=$3; | |
formatted_line = ""; | |
category_prefix = ""; | |
if (type == "ESTABLISHED") { | |
rip=$4; rport=$5; | |
if (index(lip, ":") > 0 || index(rip, ":") > 0) next; | |
is_lip_this_machine = is_this_machine(lip, local_ip_main); | |
is_rip_this_machine = is_this_machine(rip, local_ip_main); | |
if (is_lip_this_machine && is_rip_this_machine) next; | |
if (!is_internal_scope(lip, prefix, local_ip_main) || !is_internal_scope(rip, prefix, local_ip_main)) next; | |
c_ip = (lport+0 < rport+0) ? rip : lip; | |
s_ip = (lport+0 < rport+0) ? lip : rip; | |
s_pnum = (lport+0 < rport+0) ? lport : rport; | |
if (lport == rport) { | |
c_ip = (lip <= rip) ? lip : rip; | |
s_ip = (lip <= rip) ? rip : lip; | |
s_pnum = lport; | |
} | |
c_disp = resolve_ip_display(c_ip, local_ip_main, local_hostname); | |
s_disp = resolve_ip_display(s_ip, local_ip_main, local_hostname); | |
p_lab = res_svc(s_pnum); | |
formatted_line = sprintf("| %-*s | %-*s | %-*s |", w1, c_disp, w2, s_disp, w3, p_lab); | |
category_prefix = "1_ESTAB_OTHER|"; | |
} else if (type == "LISTEN") { | |
if (index(lip, ":") > 0) next; | |
if (!is_internal_scope(lip, prefix, local_ip_main)) next; | |
p_lab = res_svc(lport); | |
h_disp = resolve_ip_display(lip, local_ip_main, local_hostname); | |
formatted_line = sprintf("| %-*s | %-*s | %-*s |", w1, h_disp, w2, h_disp, w3, p_lab); | |
category_prefix = "2_LISTEN_LOCAL|"; | |
} | |
if (category_prefix != "") { | |
print category_prefix formatted_line; | |
} | |
} | |
') | |
# --- Geracao da Saida Markdown --- | |
printf "| %-*s | %-*s | %-*s |\n" "$w1" "Cliente" "$w2" "Server" "$w3" "Servico" | |
div1=$(printf '%*s' $((w1+2)) '' | tr ' ' '-') | |
div2=$(printf '%*s' $((w2+2)) '' | tr ' ' '-') | |
div3=$(printf '%*s' $((w3+2)) '' | tr ' ' '-') | |
printf "|%s|%s|%s|\n" "$div1" "$div2" "$div3" | |
if [[ -n "$RESULT" ]]; then | |
echo "$RESULT" | sort -s -t'|' -k1,1 -k3,3 -k4,4 | cut -d'|' -f2- | sort -u | |
else | |
printf "| %-*s | %-*s | %-*s |\n" "$w1" "Nenhuma conexao" "$w2" "relevante encontrada" "$w3" "." | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment