这个是因为仿蹭的,本来被蹭也不是多大事,但因为我的NAS的共享服务没有设置密码,只要进了局域网所有的东西几乎透明的。为了安全所以设置了MAC白名单。刚开始也是手动添加,一个手机设置一下。可是后来为了信号强度,又添加了三个路由器用来信号增强。于是就开始麻烦了,来一个朋友要设置四个路由器,烦不胜烦,而且还是在手机上设置。
秉承一个国外大佬的理念,凡是超过一分钟的事情且能用脚本处理的,不去部署那么就是罪大恶极。
实现原理:

部署一个最简单的网站,前台界面上一个输入框加一个按钮。用js做好校验mac格式规则,点按钮post到后台。后台接收到mac地址后查看是否这个地址本身就存在,没有就添加进maclist文件列表里,一个一行。然后后台依次通过ssh连接到每个路由器,执行路由器里的shell脚本进行添加重启等流程。

使用Thinkphp框架

public function index(){
    if(Session::get('login')){
        if($_POST){
            $this->add($_POST['mac']);
            $this->run('192.168.1.1');
            $this->run('192.168.1.2');
            $this->run('192.168.1.3');
            $this->run('192.168.1.4');
        }
        return View::fetch();
    }else{
        if($_POST){
            if($_POST['password'] == '密码'){
                Session::set('login', true);
                echo 'success';
            }else{
                echo 'fail';
            }
        }else{
            return View::fetch('index/login');
        }
    }
}
public function add($mac){
    $file_path = 'maclist.txt';
    $mac = str_replace('-', ':', $mac);
    if (!file_exists($file_path)) {
        file_put_contents($file_path, $mac."\n");
    } else {
        $existing_mac_addresses = file($file_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $mac = array_diff(array($mac), $existing_mac_addresses);
        if (!empty($mac)) {
            file_put_contents($file_path, implode("\n", $mac)."\n", FILE_APPEND);
        }
    }
}
public function run($host){
    $port = 22;
    $username = ''; // 替换为目标主机的用户名
    $password = ''; // 替换为目标主机的密码

    $connection = ssh2_connect($host, $port);
    if (!$connection) {
        die("无法连接到目标主机");
    }
    if (!ssh2_auth_password($connection, $username, $password)) {
        die("认证失败");
    }
    $command = '/root/update_maclist.sh'; // 替换为脚本的路径
    $stream = ssh2_exec($connection, $command);
    fclose($stream);
    ssh2_disconnect($connection);
}

前台添加页面,这个是递交页面,另外还有一个验证页面的布局,很简单就没发了。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MAC白名单添加</title>
    <!-- 引入 Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-validator/0.5.3/css/bootstrapValidator.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6 col-sm-12">
                <form id="addmac" action="http://nas-ip" method="post" class="mb-4 p-4 border rounded shadow-sm">
                    <div class="mb-3">
                        <input type="text" class="form-control" id="mac" name="mac" placeholder="请输入MAC地址">
                    </div>
                    <button type="submit" class="btn btn-primary">提交</button>
                </form>
            </div>
        </div>
    </div>
    
    <!-- 引入 jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- 引入 Bootstrap JS 和依赖 -->
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
    <!-- 引入 BootstrapValidator JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-validator/0.5.3/js/bootstrapValidator.min.js"></script>

    <script>
        $(function () {
            $('#addmac').bootstrapValidator({
                message: 'This value is not valid',
                feedbackIcons: {
                    valid: 'glyphicon glyphicon-ok',
                    invalid: 'glyphicon glyphicon-remove',
                    validating: 'glyphicon glyphicon-refresh'
                },
                fields: {
                    mac: {
                        validators: {
                            notEmpty: {
                                message: '地址不能空'
                            },
                            regexp: {
                                regexp: /^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$/,
                                message: '地址字符非法'
                            }
                        }
                    },
                }
            });
        });
    </script>
</body>
</html>

路由器端更新脚本,为了好统一操作,所有的脚本都是一个文件名一个路径,我是都放在root用户文件夹下。路由系统都是OpenWrt的,其他的没有适配。

#!/bin/ash

URL="http://nas-ip/maclist.txt"
TEMP_FILE="/tmp/maclist.txt"
WIRELESS_FILE="/etc/config/wireless"

curl -s "$URL" -o "$TEMP_FILE"

if [ ! -f "$TEMP_FILE" ]; then
    exit 1
fi

has_new_mac=0

existing_macs=$(uci get wireless.@wifi-iface[0].maclist)

while IFS= read -r mac; do
    if ! echo "$existing_macs" | grep -qi "$mac"; then
        uci add_list wireless.@wifi-iface[0].maclist="$mac"
        has_new_mac=1
    fi
done < "$TEMP_FILE"

if [ "$has_new_mac" -eq 1 ]; then
    uci commit wireless
    /etc/init.d/network restart
    echo "restart"
else
    echo "no restart"
fi

rm -f "$TEMP_FILE"