一套脚本,一键执行常见防火墙操作:查询状态、开/关、防火墙自启动、开放/关闭端口。适用于麒麟
V10、CentOS、Ubuntu 和 macOS
。
一、脚本核心功能
运维环境中往往混合使用多个发行版,防火墙命令( firewalld / ufw / pf )
差异较大。
- 核心功能:
- 查询防火墙状态(已开启/已关闭/不支持/未知)
- 查询开机自启动状态(已开启/已关闭/不支持/未知)
- 开启/关闭 防火墙
- 设置/取消 开机自动启动
- 永久放行端口(TCP/UDP)
- 列出当前已放行端口(含协议/服务)
- 关闭指定端口(自动删除相关规则,无需再次选择协议)
二、环境准备
1. 脚本执行环境
- 必须用 Bash 运行(脚本首行
#!/usr/bin/env bash
),不能用sh
。 - 需要
sudo
或 root 权限。
2. 各系统依赖
- 麒麟V10 / CentOS:
firewalld
(firewall-cmd
)
sudo yum install -y firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
- Ubuntu:
ufw
sudo apt update
sudo apt install -y ufw
sudo ufw enable
- macOS:自带应用防火墙(
socketfilterfw
)和 Packet Filter(pf
,配置文件/etc/pf.conf
)
3. 工具支持
timeout
(大多数 Linux 已预装;如无则安装coreutils
)。- 如脚本从 Windows 编辑后出现奇怪卡顿,执行:
dos2unix firewall_manager.sh
三、脚本核心思路
1. 跨平台检测
if [ "$(uname)" = "Darwin" ]; then
OS_TYPE="macos"
elif [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
kylin|kylin10|kylinv10) OS_TYPE="kylin" ;;
centos) OS_TYPE="centos" ;;
ubuntu) OS_TYPE="ubuntu" ;;
*) OS_TYPE="unknown" ;;
esac
else
OS_TYPE="unknown"
fi
2. 状态获取统一用 timeout
防卡死
firewalld
:
STATE="$(timeout 3s firewall-cmd --state 2>/dev/null || echo "timeout")"
ufw
:
STATUS_UFW="$(timeout 3s ufw status 2>/dev/null | head -n1 || echo "timeout")"
systemctl is-enabled
:
EN="$(timeout 2s systemctl is-enabled ufw 2>/dev/null || echo "timeout")"
- macOS 直接用
socketfilterfw --getglobalstate
查询 “State = 1/0”。
3. 端口关闭自动化(Ubuntu 下避免交互确认卡住):
while true; do
NUM="$(ufw status numbered | grep "${PORT}/" | head -n1 | sed -n 's/^\[ *\([0-9]\+\)\].*/\1/p')"
[ -z "$NUM" ] && break
yes | ufw delete "$NUM" >/dev/null
done
4. macOS pf 规则操作
- 备份
/etc/pf.conf
:
cp /etc/pf.conf /etc/pf.conf.bak_$(date +%F_%T)
- 新增:
echo "pass in proto tcp from any to any port 8080" >> /etc/pf.conf
pfctl -f /etc/pf.conf
- 删除:
sed -i.bak "/port[[:space:]]*8080/d" /etc/pf.conf
pfctl -f /etc/pf.conf
四、使用步骤
1. 保存脚本并赋可执行权限
vim firewall_manager.sh
# 粘贴下方“完整脚本”内容并保存
chmod +x firewall_manager.sh
2. (如有 Windows 换行)转换格式
dos2unix firewall_manager.sh
3. 启动脚本(务必用 Bash)
sudo bash firewall_manager.sh
或者
./firewall_manager.sh
4. 交互示例
- 运行后菜单显示:
五、完整脚本(复制即用)
#!/usr/bin/env bash
#
# 文件名:firewall_manager.sh
# 说明:跨平台(麒麟V10、CentOS、Ubuntu、macOS)防火墙管理脚本
#
# 功能:
# 1. 显示防火墙状态(已开启/已关闭/不支持/未知)
# 2. 显示开机自启动状态(已开启/已关闭/不支持/未知)
# 3. 开启/关闭 防火墙
# 4. 设置/取消 开机自启动
# 5. 永久放行端口(TCP/UDP)
# 6. 查看已开放端口(数字+协议/服务)
# 7. 关闭指定端口(自动删除,非交互式)
#
# 注意:
# - 必须用 Bash 运行,不要用 sh:
# chmod +x firewall_manager.sh
# sudo bash firewall_manager.sh
# - 需 root 或 sudo 权限
# - 使用 timeout 防止命令卡住
# - Ubuntu 下 ufw delete 由 yes 自动确认
# --------------------- 判断操作系统 ---------------------
OS_TYPE=""
if [ "$(uname)" = "Darwin" ]; then
OS_TYPE="macos"
elif [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
kylin|kylin10|kylinv10) OS_TYPE="kylin" ;;
centos) OS_TYPE="centos" ;;
ubuntu) OS_TYPE="ubuntu" ;;
*) OS_TYPE="unknown" ;;
esac
else
OS_TYPE="unknown"
fi
# --------------------- 辅助函数 ---------------------
print_line() {
echo "---------------------------------------------"
}
command_exists() {
command -v "$1" >/dev/null 2>&1
}
die() {
echo "Error: $1" 1>&2
exit 1
}
# --------------------- 获取防火墙状态 ---------------------
get_firewall_status() {
case "$OS_TYPE" in
centos|kylin)
if command_exists firewall-cmd; then
STATE="$(timeout 3s firewall-cmd --state 2>/dev/null || echo "timeout")"
if [ "$STATE" = "running" ]; then
echo "enabled"
elif [ "$STATE" = "timeout" ]; then
echo "unknown"
else
echo "disabled"
fi
else
echo "unsupported"
fi
;;
ubuntu)
if command_exists ufw; then
STATUS_UFW="$(timeout 3s ufw status 2>/dev/null | head -n1 || echo "timeout")"
case "$STATUS_UFW" in
*"Status: active"*) echo "enabled" ;;
*"timeout"*) echo "unknown" ;;
*) echo "disabled" ;;
esac
else
echo "unsupported"
fi
;;
macos)
if command_exists /usr/libexec/ApplicationFirewall/socketfilterfw; then
GSTATE="$(/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2>&1)"
case "$GSTATE" in
*"State = 1"*) echo "enabled" ;;
*) echo "disabled" ;;
esac
else
echo "unsupported"
fi
;;
*)
echo "unknown"
;;
esac
}
# --------------------- 获取开机自启动状态 ---------------------
get_autostart_status() {
case "$OS_TYPE" in
centos|kylin)
if command_exists systemctl; then
EN="$(timeout 2s systemctl is-enabled firewalld 2>/dev/null || echo "timeout")"
if [ "$EN" = "enabled" ]; then
echo "enabled"
elif [ "$EN" = "timeout" ]; then
echo "unknown"
else
echo "disabled"
fi
else
echo "unsupported"
fi
;;
ubuntu)
if command_exists systemctl; then
EN="$(timeout 2s systemctl is-enabled ufw 2>/dev/null || echo "timeout")"
if [ "$EN" = "enabled" ]; then
echo "enabled"
elif [ "$EN" = "timeout" ]; then
echo "unknown"
else
echo "disabled"
fi
else
echo "unsupported"
fi
;;
macos)
ST="$(get_firewall_status)"
if [ "$ST" = "enabled" ]; then
echo "enabled"
else
echo "disabled"
fi
;;
*)
echo "unknown"
;;
esac
}
# --------------------- 各操作实现 ---------------------
# 1. 开启防火墙
enable_firewall() {
case "$OS_TYPE" in
centos|kylin)
systemctl start firewalld && systemctl enable firewalld
;;
ubuntu)
ufw --force enable
;;
macos)
/usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
;;
*)
die "无法在此系统上开启防火墙"
;;
esac
}
# 2. 关闭防火墙
disable_firewall() {
case "$OS_TYPE" in
centos|kylin)
systemctl stop firewalld && systemctl disable firewalld
;;
ubuntu)
ufw disable
;;
macos)
/usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
;;
*)
die "无法在此系统上关闭防火墙"
;;
esac
}
# 3. 设置开机自启动
enable_autostart() {
case "$OS_TYPE" in
centos|kylin)
systemctl enable firewalld
;;
ubuntu)
systemctl enable ufw
;;
macos)
echo "macOS 上只要防火墙被启用,就会开机自启动。"
;;
*)
die "无法在此系统上设置开机自启动"
;;
esac
}
# 4. 取消开机自启动
disable_autostart() {
case "$OS_TYPE" in
centos|kylin)
systemctl disable firewalld
;;
ubuntu)
systemctl disable ufw
;;
macos)
echo "macOS 上关闭防火墙即可取消开机自启动。"
;;
*)
die "无法在此系统上取消开机自启动"
;;
esac
}
# 5. 永久开放端口
open_port() {
read -p "请输入要开放的协议 (tcp/udp): " PROTO
PROTO="$(echo "$PROTO" | tr '[:upper:]' '[:lower:]')"
if [ "$PROTO" != "tcp" ] && [ "$PROTO" != "udp" ]; then
echo "协议只能是 tcp 或 udp"
return
fi
read -p "请输入要开放的端口号 (1-65535): " PORT
echo "$PORT" | grep -E '^[0-9]+$' >/dev/null 2>&1 || { echo "端口号不合法。"; return; }
if [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then
echo "端口号不合法。"
return
fi
case "$OS_TYPE" in
centos|kylin)
firewall-cmd --permanent --add-port=${PORT}/${PROTO} && firewall-cmd --reload
;;
ubuntu)
ufw allow ${PORT}/${PROTO}
;;
macos)
PF_CONF="/etc/pf.conf"
BACKUP_PATH="/etc/pf.conf.bak_$(date +%F_%T)"
echo "正在备份 /etc/pf.conf 到 $BACKUP_PATH"
cp "$PF_CONF" "$BACKUP_PATH" || { echo "备份失败,请检查权限。"; return; }
RULE="pass in proto $PROTO from any to any port $PORT"
grep -Fxq "$RULE" "$PF_CONF" && { echo "规则已存在: $RULE"; return; }
echo "" >> "$PF_CONF"
echo "# 由 firewall_manager.sh 添加 $(date '+%F %T')" >> "$PF_CONF"
echo "$RULE" >> "$PF_CONF"
pfctl -f "$PF_CONF" && pfctl -e 2>/dev/null && echo "端口 $PORT/$PROTO 已添加并生效。" \
|| echo "pf 规则加载失败,请手动检查 /etc/pf.conf。"
;;
*)
echo "该系统不支持自动开放端口。"
;;
esac
}
# 6. 查看已开启的端口
list_ports() {
echo "当前已开放的端口:"
case "$OS_TYPE" in
centos|kylin)
if command_exists firewall-cmd; then
echo "-- 通过 firewall-cmd 列出端口 --"
firewall-cmd --list-ports
echo
echo "-- 通过 firewall-cmd 列出服务 --"
firewall-cmd --list-services
else
echo "firewall-cmd 未安装,无法列出端口。"
fi
;;
ubuntu)
if command_exists ufw; then
echo "-- ufw 状态信息 --"
ufw status verbose
else
echo "ufw 未安装,无法列出端口。"
fi
;;
macos)
echo "-- pf 规则列表 (包含端口规则) --"
pfctl -sr | grep 'port'
;;
*)
echo "该系统不支持查看已开放端口。"
;;
esac
}
# 7. 关闭指定端口(无需选择协议,也不会卡在确认提示)
close_port() {
read -p "请输入要关闭的端口号 (1-65535): " PORT
echo "$PORT" | grep -E '^[0-9]+$' >/dev/null 2>&1 || { echo "端口号不合法。"; return; }
if [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then
echo "端口号不合法。"
return
fi
case "$OS_TYPE" in
centos|kylin)
firewall-cmd --permanent --remove-port=${PORT}/tcp
firewall-cmd --permanent --remove-port=${PORT}/udp
firewall-cmd --reload && echo "端口 $PORT 已关闭(TCP/UDP)。" || echo "关闭端口失败,请检查。"
;;
ubuntu)
if command_exists ufw; then
while true; do
NUM="$(ufw status numbered | grep "${PORT}/" | head -n1 | sed -n 's/^\[ *\([0-9]\+\)\].*/\1/p')"
[ -z "$NUM" ] && break
yes | ufw delete "$NUM" >/dev/null
done
echo "端口 $PORT 已关闭(UDP/TCP)。"
else
echo "ufw 未安装,无法关闭端口。"
fi
;;
macos)
PF_CONF="/etc/pf.conf"
BACKUP_PATH="/etc/pf.conf.bak_$(date +%F_%T)"
echo "正在备份 /etc/pf.conf 到 $BACKUP_PATH"
cp "$PF_CONF" "$BACKUP_PATH" || { echo "备份失败,请检查权限。"; return; }
echo "从 /etc/pf.conf 删除与端口 $PORT 相关的行..."
sed -i.bak "/port[[:space:]]*$PORT/d" "$PF_CONF" && pfctl -f "$PF_CONF" && echo "端口 $PORT 相关规则已删除并生效。" || echo "删除规则失败,请手动检查 /etc/pf.conf。"
;;
*)
echo "该系统不支持关闭端口。"
;;
esac
}
# --------------------- 主菜单 ---------------------
while true; do
clear
echo "======== 防火墙管理脚本 ========"
print_line
FW_STATUS="$(get_firewall_status)"
AUTO_STATUS="$(get_autostart_status)"
echo "当前操作系统: $OS_TYPE"
echo -n "防火墙状态 : "
if [ "$FW_STATUS" = "enabled" ]; then
echo "已开启"
elif [ "$FW_STATUS" = "disabled" ]; then
echo "已关闭"
else
echo "未知/不支持"
fi
echo -n "开机自启状态 : "
if [ "$AUTO_STATUS" = "enabled" ]; then
echo "已开启"
elif [ "$AUTO_STATUS" = "disabled" ]; then
echo "已关闭"
else
echo "未知/不支持"
fi
print_line
echo "请选择操作:"
echo " 1) 开启防火墙"
echo " 2) 关闭防火墙"
echo " 3) 设置开机自启动"
echo " 4) 取消开机自启动"
echo " 5) 永久开放指定端口"
echo " 6) 查看已开启的端口"
echo " 7) 关闭指定端口"
echo " 0) 退出脚本"
print_line
read -p "输入选项 [0-7]: " CHOICE
case "$CHOICE" in
1)
enable_firewall && echo "操作完成:防火墙已开启。"
read -p "按回车继续..."
;;
2)
disable_firewall && echo "操作完成:防火墙已关闭。"
read -p "按回车继续..."
;;
3)
enable_autostart && echo "操作完成:已设置开机自启动。"
read -p "按回车继续..."
;;
4)
disable_autostart && echo "操作完成:已取消开机自启动。"
read -p "按回车继续..."
;;
5)
open_port
read -p "按回车继续..."
;;
6)
list_ports
read -p "按回车继续..."
;;
7)
close_port
read -p "按回车继续..."
;;
0)
echo "退出脚本。"
exit 0
;;
*)
echo "无效选项,请重新输入。"
sleep 1
;;
esac
done
千万要听话哦~
- 必须用 Bash 运行(
./firewall_manager.sh
或bash firewall_manager.sh
),不能用sh
。 - 对可能挂起的命令统一用
timeout
限时,避免卡死。 - Ubuntu 下关闭端口用
yes | ufw delete …
自动确认。 - macOS 下编辑
/etc/pf.conf
追加/删除规则后调用pfctl -f
重载。
按此脚本即可跨麒麟 V10 / CentOS / Ubuntu / macOS 一键管理防火墙,极大简化运维复杂度。祝使用顺利!