目录
对特定端口进行封禁:
#!/bin/bash
#
# 安全性修复
current_script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
read -p "请输入 ssh 登录密码: " -s password
echo ""
#保存iptables配置
if [ ! -f "/etc/iptables" ]; then
echo ${password} | sudo -S bash -c 'iptables-save > /etc/iptables'
echo ${password} | sudo -S bash -c 'chmod 755 /etc/iptables'
fi
# Log the given message at the given level. All logs are written to stderr with a timestamp.
function log() {
local -r level="$1"
local -r message="$2"
local -r timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local -r script_name="$(basename "$0")"
echo >&2 -e "${timestamp} [${level}] [$script_name] ${message}"
}
# Log the given message at INFO level. All logs are written to stderr with a timestamp.
function log_info() {
local -r message="$1"
log "\033[1mINFO\033[0m" "$message"
}
# Log the given message at WARN level. All logs are written to stderr with a timestamp.
function log_warn() {
local -r message="$1"
log "\033[33;1mWARN\033[0m" "$message"
}
# Log the given message at ERROR level. All logs are written to stderr with a timestamp.
function log_error() {
local -r message="$1"
log "\033[31;1mERROR\033[0m" "$message"
}
# 获取 /etc/iptabls *raw 第一行规则
function get_first_raw_rule_row_number() {
mapfile rows < /etc/iptables
local matched=0
for (( i=0; i<=${#rows[@]}; i++)); do
if [[ $matched -eq 1 ]]; then
if [[ ! ${rows[i]} =~ ^: ]] && [[ ! ${rows[i]} =~ ^# ]]; then
echo $((i+1))
return 0
fi
elif [[ "${rows[i]}" =~ ^\*raw ]]; then
matched=1
fi
done
}
# 封禁端口
function ban_port() {
port=$1
rule_line=$(echo ${password} | sudo -S iptables -t raw -nvL PREROUTING --line-number | grep "dpt:${port} " | grep "DROP" | wc -l)
if_raw=$(echo ${password} | sudo -S cat /etc/iptables | grep "*raw" | wc -l)
if [[ $if_raw == 0 ]]; then
echo ${password} | sudo -S sed -i "1i\*raw\n:PREROUTING" /etc/iptables
fi
if [[ $rule_line == "0" ]]; then
# 找到需要封禁的对外网卡名
echo ${password} | sudo -S sed -i "/dport ${port}/d" /etc/iptables
devs=$(cat /proc/net/dev | awk '{i++; if(i>2){print $1}}' | sed 's/^[\t]*//g' | sed 's/[:]*$//g' | grep -v -E '^veth|^br|^lo|hurricane|^flannel|^docker|^cni')
for dev in ${devs[@]}
do
echo ${password} | sudo -S iptables -w 10 -t raw -I PREROUTING -i ${dev} -p tcp -m tcp --dport ${port} -j DROP
local rule="-A PREROUTING -i ${dev} -p tcp -m tcp --dport ${port} -j DROP"
echo ${password} | sudo -S sed -i "`get_first_raw_rule_row_number`i\\${rule}" /etc/iptables
done
log_info "[${port}端口] 添加禁止外部地址访问规则"
else
log_warn "[${port}端口] 禁止外部地址访问规则已存在"
fi
}
# 开放端口
function open_port() {
port=$1
echo ${password} | sudo -S iptables -w 10 -t raw -F
echo ${password} | sudo -S sed -i "/dport ${port}/d" /etc/iptables
log_info "[${port}端口] 清除禁止外部访问规则"
}
# ==================================================
args=${1:-"fix"}
port_list=("80" "8080") #操作的端口号
if [[ $args == "fix" ]]
then
for port in ${port_list[@]}
do
ban_port $port
done
elif [[ $args == "reset" ]]
then
for port in ${port_list[@]}
do
open_port $port
done
else
echo "unsupport operation $args"
fi
- #登录目标机器
- vi port.sh #复制脚本代码 并修改需要操作的端口号 port_list
- bash port.sh fix #执行封禁脚本
- exit #退出远程
- nc -vz {IP} {PORT} #测试端口是否成功封禁

这里采用的drop方式封禁端口,所以nc不会返回

使用iptables禁用端口时,nc(netcat)不会返回“连接拒绝”,而是直接卡住在终端上,这与iptables的规则配置和TCP协议的工作方式有关。具体来说,当你使用iptables封禁端口时,如果是DROP操作,TCP连接的行为会有所不同。
TCP 连接的工作方式
- 端口关闭但可达: 当目标端口是关闭的且防火墙允许ICMP不可达消息时,
nc会立即收到一个TCP RST(重置)包,这会导致连接被拒绝(Connection refused)。 - 端口被防火墙DROP: 当目标端口被防火墙DROP规则封禁时,数据包被直接丢弃,不会生成任何响应。这样,
nc无法收到任何回应,表现为连接卡住等待超时。
iptables 的处理方式
iptables可以通过两种方式封禁端口:REJECT和DROP。
iptables -A INPUT -p tcp --dport 8080 -j REJECT --reject-with tcp-reset
DROP:丢弃数据包,不发送任何响应。对于TCP连接,意味着客户端会一直等待直到连接超时。
iptables -A INPUT -p tcp --dport 8080 -j DROP
为什么会卡住
当使用DROP规则时,数据包被直接丢弃,客户端(nc)没有收到任何响应,导致它会一直尝试等待连接超时。这就是为什么连接会卡住而不是立即返回“连接拒绝”。
解决方法
如果你希望nc在端口被封禁时能够立即返回“连接拒绝”而不是卡住,可以将iptables规则设置为REJECT而不是DROP:
这样,尝试连接被封禁端口时,客户端会收到TCP RST包,立即返回“连接拒绝”。
示例对比
使用 DROP 封禁端口
sudo iptables -A INPUT -p tcp --dport 8080 -j DROPnc -v localhost 8080 # 卡住,没有任何输出,直到超时使用 REJECT 封禁端口
此时,
nc会立即返回连接拒绝:nc -v localhost 8080 # 输出 nc: connect to localhost port 8080 (tcp) failed: Connection refused参考资料
drop还是reject
在决定使用DROP还是REJECT来封禁端口时,需要考虑安全需求和实际场景。这两种方法各有优缺点,具体选择取决于你对安全性、性能和用户体验的要求。
DROP 与 REJECT 的比较
DROP
- 行为:直接丢弃数据包,不发送任何响应。
- 优点:
- 隐蔽性:攻击者无法确定端口是否存在,因为没有任何响应返回。提高了系统的隐蔽性。
- 减少负载:没有响应包返回,节省了系统资源。
- 缺点:
- 用户体验:合法用户可能会感到困惑,因为连接会一直挂起直到超时。
- 诊断困难:排查网络问题时,不容易区分端口未开放还是被防火墙封禁。
REJECT
- 行为:拒绝数据包,并发送适当的响应回客户端。对于TCP连接,通常会返回TCP RST(重置)包。
- 优点:
- 用户体验:合法用户会立即知道端口被封禁,不会长时间等待。
- 便于诊断:更容易区分端口被封禁和网络其他问题。
- 缺点:
- 可识别性:攻击者可以通过收到的响应,确认端口被封禁。
- 系统负载:需要生成并发送响应包,略微增加系统负载。
选择 DROP 还是 REJECT 的建议
- 高安全性场景:
- 使用DROP:如果你希望最大程度隐藏系统的存在,避免被扫描器和潜在攻击者发现,可以选择DROP。这种方式可以使攻击者无法确定端口是否存在。
- 用户体验和诊断优先:
- 使用REJECT:如果你更关注合法用户的体验,或者需要更方便地进行网络问题诊断,可以选择REJECT。这种方式可以立即通知用户端口不可用。
实践建议
- 公共服务端口:对于公开的服务端口,例如Web服务器的80/443端口,建议使用REJECT。这有助于用户和管理员快速确定端口被封禁的原因。
- 内部或敏感端口:对于内部服务或敏感端口,例如数据库端口或SSH端口,建议使用DROP。这样可以减少被扫描和攻击的可能性。
配置示例
使用 DROP
sudo iptables -A INPUT -p tcp --dport 8080 -j DROP
使用 REJECT
sudo iptables -A INPUT -p tcp --dport 8080 -j REJECT --reject-with tcp-reset
总结
选择DROP还是REJECT取决于你的具体需求和安全策略。如果希望提高系统隐蔽性,避免暴露给攻击者,使用DROP是更好的选择。如果需要提高用户体验和诊断效率,使用REJECT会更合适。
参考资料
通过以上的分析和建议,你可以根据具体需求选择合适的策略来封禁端口。
No comments yet.