一个屏蔽 IP 的脚本

在测试可用性的时候,经常需要模拟断网。这时候用 iptables 是比较方便的。但是如果想更方便一点,不用去敲那么长的命令会更好些。于是就写了个 ban_ip 脚本。

#!/bin/bash

if [[ "$EUID" != 0 ]]; then
  echo "should run as root"
  exit 1
fi

action="DROP"
comment="ban_ip"
cmd="$1"
case "$cmd" in
list)
  iptables -L -n | awk -v "cmt=$comment" '$0~cmt{print $4}'
  ;;
add)
  ip="$2"
  if [[ -z "$ip" ]]; then
    echo "missing arg ip"
    exit 1
  fi
  iptables -A INPUT -s "$ip" -j "$action" -m comment --comment "$comment"
  ;;
del)
  ip="$2"
  if [[ -z "$ip" ]]; then
    echo "missing arg ip"
    exit 1
  fi
  iptables -D INPUT -s "$ip" -j "$action" -m comment --comment "$comment"
  ;;
*)
  echo "bad command: should be list, add <ip>, del <ip>"
  exit 1
  ;;
esac

用的时候就方便了不少,也便于查看当前已经 ban 掉的 ip。

$ sudo ./ban_ip.sh list
10.10.10.1

$ sudo ./ban_ip.sh add 10.10.10.2

$ sudo ./ban_ip.sh list
10.10.10.1
10.10.10.2

$ sudo ./ban_ip.sh del 10.10.10.1

$ sudo ./ban_ip.sh list
10.10.10.2

linux capabilities

在 linux 系统中,很多操作是需要 root 用户权限才能操作的。比如 chown,改变进程 uid,使用 raw socket 等等。要不就得用 sudo 提升权限,如果想让每个用户都能用特权来执行一个程序,配置管理和权限控制就很麻烦了。还有一个办法是使用粘滞位,通过 chmod +s,可以让一个 owner 为 root 的可执行文件再运行时具有 root 权限。在一些发型版中,ping 命令就是这么干的。给 ping 命令加上粘滞位以后,普通用户也可以使用这个命令通过 raw socket 发送 icmp 包了。不过这样一来,这个程序也就无所不能了。万一程序有啥漏洞,就容易造成严重后果。有没有办法只给这个程序开所需要的权限呢?其实是可以的。linux 有一套 capabilities 机制就是用来实现这个。

事实上,linux 本身对权限的检查就是基于 capabilities 的,root 用户有全部的 capabilities,所以啥都能干。如果想给一个可执行文件加上某个 capability,可以用 setcap 命令,如

setcap cap_net_raw=+ep ping

就可以给 ping 命令加上使用 raw socket 的权限。cap_net_raw 是 capability 的名字,后面是 mode,可以有

  • e:表示是否激活该 capability
  • p:是否允许进程设置该 capability
  • i:子进程是否能继承 capabilities

+ 表示启用,- 表示禁用。
这样执行以后,普通用户执行这个 ping 命令,也可以正常玩耍了。而且这个 ping 命令只获得了 raw socket 的权限。
通过 getcap ping 可以查看这个程序所拥有的 capabilities。

实现上,是通过 setxattr 系统调用,为文件加上 security.capability 的扩展属性。

man 7 capabilities 中可以看到所有可用的 capabilities。

man 3 cap_from_text 中可以看到关于 capability mode 的表达式说明。