防火墙跨平台管理shell脚本(麒麟V10/CentOS/Ubuntu/macOS)快速指南

一套脚本,一键执行常见防火墙操作:查询状态、开/关、防火墙自启动、开放/关闭端口。适用于麒麟V10、CentOS、Ubuntu 和 macOS

一、脚本核心功能

运维环境中往往混合使用多个发行版,防火墙命令( firewalld / ufw / pf )差异较大。

  • 核心功能
    1. 查询防火墙状态(已开启/已关闭/不支持/未知)
    2. 查询开机自启动状态(已开启/已关闭/不支持/未知)
    3. 开启/关闭 防火墙
    4. 设置/取消 开机自动启动
    5. 永久放行端口(TCP/UDP)
    6. 列出当前已放行端口(含协议/服务)
    7. 关闭指定端口(自动删除相关规则,无需再次选择协议)

二、环境准备

1. 脚本执行环境

  • 必须用 Bash 运行(脚本首行 #!/usr/bin/env bash),不能用 sh
  • 需要 sudo 或 root 权限。

2. 各系统依赖

  • 麒麟V10 / CentOSfirewalldfirewall-cmd
sudo yum install -y firewalld
sudo systemctl start firewalld
sudo systemctl enable firewalld
  • Ubuntuufw
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.shbash firewall_manager.sh),不能用 sh
  • 对可能挂起的命令统一用 timeout 限时,避免卡死。
  • Ubuntu 下关闭端口用 yes | ufw delete … 自动确认。
  • macOS 下编辑 /etc/pf.conf 追加/删除规则后调用 pfctl -f 重载。

按此脚本即可跨麒麟 V10 / CentOS / Ubuntu / macOS 一键管理防火墙,极大简化运维复杂度。祝使用顺利!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安全日记pro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值