Maison  >  Article  >  développement back-end  >  Contrôle d'accès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHP

Contrôle d'accès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHP

藏色散人
藏色散人avant
2022-08-06 14:52:013832parcourir

Récemment, j'ai rencontré une exigence. Un de mes serveurs est toujours soumis à une analyse de port et à des attaques de connexion malveillantes. Que puis-je faire à ce sujet ? Il semble qu'à part l'isolation intranet, l'authentification améliorée par mot de passe, la connexion par certificat et la configuration de pare-feuiptables, il n'existe aucune autre solution disponible en ligne. D'ailleurs, j'utilise également la solution d'hôte bastion.

Aucune de ces solutions ne résout réellement mon problème. Il s'agit d'un serveur de réseau public et ne possède pas de structure de réseau compliquée. L'isolation intranet ne peut donc pas être établie. Ajuster la politique de mot de passe du compte est naturellement une solution, mais l'opération manuelle est trop gênante et je change généralement d'ordinateur si je change le mot de passe, les ordinateurs de l'entreprise et ceux de la maison doivent être mis à jour, ce qui est très gênant. La configuration d'un pare-feu est naturellement une opération de base pour l'exploitation et la maintenance, mais la configuration de iptables est trop compliquée, ufw est un meilleur outil, firewall-cmd est plus compliquée et il y a un énorme problème. Comme nous le savons tous, l'adresse IP sortante de chacun Tout change fréquemment. Je l'ai finalement configuré lettre par lettre dans la ligne de commande. Après un long sommeil, tout cela a été en vain. La machine bastion n'est pas une solution courante, c'est un peu exagéré. Si vous utilisez une machine bastion, vous ne pouvez pas utiliser le système à volonté, sans compter que je n'ai jamais entendu parler de la machine bastion gratuite. [Recommandation : Tutoriel vidéo PHP]

Que dois-je faire ? En tant que développeur

PHP senior, l'application serveur n'est pas encore facilement disponible au début, même la pénétration de l'intranet pourrait être facilement réalisée avec un seul système de filtration IP. , trivial. Je prévois donc de développer un tel projet moi-même. Tout d'abord, je peux mettre en œuvre le filtrage IP. De plus, je peux facilement ajouter IP à la liste blanche. Par exemple, lorsque je visite une page Web, elle sera automatiquement ajoutée. la liste blanche.

L'ensemble du projet a été développé en moins de quelques heures. Au moins, il répondait à mes propres besoins et implémentait plusieurs fonctionnalités :

    Processus multiples
  • Prise en charge de la concurrence
  • Le processus démon
  • peut être géré via le panneau Web
  • IP.
  • Statistiques de trafic
  • Enregistrements d'interceptions
Mettons maintenant en œuvre ce système étape par étape.

La première étape consiste simplement à filtrer

IP

Il existe de nombreux frameworks utilisant

PHPpour écouter sur le port et transférer les données. Pour cela, j'ai choisi workerman pour 3 raisons :

    Facile à utiliser. exécutez Stable
  • Interface de méthode simple
  • Démon de processus intégré
En ce qui concerne la méthode d'installation spécifique, vous pouvez vous référer à sa documentation officielle.

Déclaration de droits d'auteur : cet article est original et publié par

phpreturn.com (PHPSite officiel d'Arsenal). Tous les droits appartiennent à phpreturn (PHPArsenal). citation Article, mais la source doit être indiquée en même temps.

Contrôle daccès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHPworkerman

est très simple à utiliser. Il ne faut que

10 lignes de code pour implémenter IPforwarding+filtrage de liste blanche :

$worker = new Worker('tcp:0.0.0.0:' . Config::get('door.port_in'));
// 监听一个端口
$worker->count = 2;
// 设置多进程
$worker->onConnect = function (TcpConnection $connection) {
    // 获取IP白名单
    $list_ip = AppIp::where('status', 0)->cache(3)->column('ip');
    $remote_ip = $connection->getRemoteIp();
    // 拦截IP
    if (!in_array($remote_ip, $list_ip)) {
        $connection->close();
    }
    // 放行连接,连接内部目标端口
    $to_connection = new AsyncTcpConnection('tcp:127.0.0.1:' . Config::get('door.port_to'));
    // 互相转发流量
    $connection->pipe($to_connection);
    $to_connection->pipe($connection);
    $to_connection->connect();
}
Comme indiqué dans le code ci-dessus, avec seulement quelques simples. lignes, la surveillance et le transfert IP

sont mis en œuvre, dans lesquels la liste blanche

IP est interrogée via la base de données et mise en cache. La deuxième étape consiste à l'intégrer à la ligne de commande ThinkPHP

Pour faciliter le développement du projet, j'utiliserai le framework

ThinkPHP pour le développement. Il est assez simple et possède des fonctions relativement complètes.

Contrôle daccès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHP

最终实现的命令行效果如下:

版权声明:本文由phpreturn.comPHP武器库官网)原创和首发,所有权利归phpreturnPHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

运行命令
php think door start
php think door start --mode d  // 守护进程重启
重启
php think door restart
停止
php think door stop

workerman的命令参数与thinkphp并不兼容,但是实现这样的效果并不难,实际上很简单,代码如下:

<?php

declare(strict_types=1);

namespace app\common\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;

class Door extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName(&#39;door&#39;)
            // 设置think的命令参数
            ->addArgument(&#39;action&#39;, Argument::OPTIONAL, "start|stop|restart|reload|status|connections", &#39;start&#39;)
            ->addOption(&#39;mode&#39;, &#39;m&#39;, Option::VALUE_OPTIONAL, &#39;Run the workerman server in daemon mode.&#39;)
            ->setDescription(&#39;the door command&#39;);
    }
    protected function execute(Input $input, Output $output)
    {
        // 指令输出
        $output->writeln(&#39;door&#39;);
        $action = $input->getArgument(&#39;action&#39;);
        $mode = $input->getOption(&#39;mode&#39;);
        // 重新构造命令行参数,以便兼容workerman的命令
        global $argv;
        $argv = [];
        array_unshift($argv, &#39;think&#39;, $action);
        if ($mode == &#39;d&#39;) {
            $argv[] = &#39;-d&#39;;
        } else if ($mode == &#39;g&#39;) {
            $argv[] = &#39;-g&#39;;
        }
        // ...workerman的代码
    }
}

在上面的代码中,主要做了两件事:

  • 实现ThinkPHP的命令设置
  • 将命令参数重新构造为workerman兼容的方式

第三步,实现管理面板

使用PHP实现一个管理面板太简单了,PHP到处都是这样的后台框架,这里我选择ulthon_admin,这是我自己开发维护的,它基于ThinkPHP6,很简单,为定制而生,不搞所谓的“插件”和“市场”生态,能够自动生成CURD代码,并且内置几了几个有趣的皮肤。

最终效果如下:

Contrôle daccès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHPContrôle daccès IP : Nanny Style vous apprend à implémenter un pare-feu IP avec PHP

以上是ulthon_admin内置的两款皮肤效果,分别是:科幻、像素。

对于面板的管理,这里多做介绍,这算是PHP开发者的基本功,谁还不会个CURD啊。

版权声明:本文由phpreturn.comPHP武器库官网)原创和首发,所有权利归phpreturnPHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

第四步,进阶,更好的性能和流量统计

我们的IP拦截客户端需要运行在服务器上,并且直接连接数据库,如果每次收到请求都要查询数据库,那么很有可能导致连接不通畅,尤其是客户端和数据库本身位置较远的时候。在第一步的代码中,我们只是简单的使用了查询缓存,但是还不够,还可以优化。并且我们可以在管理面板的截图中看到,我们是可以统计流量和拦截次数的,现在我们要实现这些功能:

流量统计

首先我们将第一个步中,流量转发部分的代码改造成如下的样子:

<?php
// 向TO发起连接
$to_connection = new AsyncTcpConnection(&#39;tcp://127.0.0.1:&#39; . Config::get(&#39;door.port_to&#39;));
$to_connection->onMessage = function ($source, $data) use ($connection, $remote_ip) {
    // 接收到来自TO的数据,返回的数据
    $connection->send($data);
    // 将流量统计存储到内存里
    Cache::inc(md5($remote_ip) . &#39;-to&#39;, strlen($data));
};
// 流程和流量控制
$to_connection->onClose = function ($source) use ($connection) {
    $connection->close();
};
$connection->onBufferFull = function ($dest) use ($to_connection) {
    $to_connection->pauseRecv();
};
$connection->onBufferDrain = function ($dest) use ($to_connection) {
    $to_connection->resumeRecv();
};
$connection->onMessage = function ($source, $data) use ($to_connection, $remote_ip) {
    // 接收来自IN的数据,请求的数据
    $to_connection->send($data);
    // 将流量统计存储到内存里
    Cache::inc(md5($remote_ip) . &#39;-in&#39;, strlen($data));
};
// 流程和流量控制
$connection->onClose = function ($source) use ($to_connection) {
    $to_connection->close();
};
$to_connection->onBufferFull = function ($dest) use ($connection) {
    $connection->pauseRecv();
};
$to_connection->onBufferDrain = function ($dest) use ($connection) {
    $connection->resumeRecv();
};

在第一部的代码中,只用两行便实现了这些代码:

// 放行连接,连接内部目标端口
$to_connection = new AsyncTcpConnection(&#39;tcp:127.0.0.1:&#39; . Config::get(&#39;door.port_to&#39;));
// 互相转发流量
$connection->pipe($to_connection);
$to_connection->pipe($connection);

这里使用的是workerman内置的流量转发,它很好用,但是这里我们要统计流量,所以我们手动转发流量。

这里我们将统计的数据存储到缓存里,而不是直接连接数据库更新,这是为了更好的连接性能。我们会另外开启一个进程将这些改动更新到数据库。后面会介绍到。

拦截统计

我们将第一步中的加载IP白名单的逻辑改成下面这样:

版权声明:本文由phpreturn.comPHP武器库官网)原创和首发,所有权利归phpreturnPHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

<?php
$worker->onConnect = function (TcpConnection $connection) {
    $disable_cache_key = &#39;disable_ip_list&#39;;
    $list_ip = Cache::get($disable_cache_key);
    if (empty($list_ip)) {
        $connection->close();
    }
    $remote_ip = $connection->getRemoteIp();
    if (!in_array($remote_ip, $list_ip)) {
        AppIpReject::initRecord($remote_ip);
        $connection->close();
    }
};

在这里我们不连接数据库查询,而是直接从本地缓存读取白名单,这样会有更好的性能。我们会在另一个进程中更新这份白名单。

另外我们可以看到,拦截的IP调用了一个静态方法,这里的功能很简单,判断数据库中该IP是否存在,如果不存在则新增,如果存在,则更新拦截次数+·1。这里就不多介绍了。这里也没有必要做什么性能优化,反正本来就是拦截的IP,优化个毛。

高性能处理缓存数据

上面我们介绍,我们会另外开启一个进程,维护IP白名单,并且将流量统计提交到数据库。这就是这个进程:

<?php
$worker_ip = new Worker();
$worker_ip->name = &#39;report&#39;;
$worker_ip->onWorkerStart = function () {
    Timer::add(5, function () {
        $disable_cache_key = &#39;disable_ip_list&#39;;
        $list_ip = AppIp::where(&#39;status&#39;, 1)->column(&#39;ip&#39;);
        Cache::set($disable_cache_key, $list_ip);
        foreach ($list_ip as  $ip) {
            $ip_md5 = md5($ip);
            $in_length = Cache::pull("$ip_md5-in");
            // 请求的数据
            $to_length = Cache::pull("$ip_md5-to");
            // 返回的数据
            if (!empty($in_length) || !empty($to_length)) {
                $model_ip = AppIp::where(&#39;ip&#39;, $ip)->find();
                $model_ip->in_buffer += $in_length;
                $model_ip->to_buffer += $to_length;
                $model_ip->save();
            }
        }
    });
};

他做的事情很简单,读取缓存,更新数据到数据库,并且更新IP白名单。这里不需要考虑它和数据库之间的性能问题,这是额外的进程,不影响端口的连接和转发。

下一步,更好的性能设计

以上,只有几行代码,几个小时(如果不含设计系统的时间,代码量可能只有一两个小时。还能再怎么优化呢?实际上还是可以优化的。

更好的内存驱动

这里使用的是ThinkPHP内置的文件缓存,存储到磁盘上,以上方法,在大量连接并发时,肯定受制于磁盘的性能。所以自然而然,我们可以使用内存缓存。

版权声明:本文由phpreturn.comPHP武器库官网)原创和首发,所有权利归phpreturnPHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

但是使用内存缓存,redis可以吗?并不好。这里是客户端,它只是想简简单单实现一个拦截转发,还要再部署redis,不可取。

但实际上,workerman本身内置了数据共享组件,这是一个很好的方案。相当于一个极简的redis。完美符合我们的需求。但是我并没有实现这个功能,目前的系统已经符合我的场景。

更好的客户端

目前拦截IP客户端和管理面板集成在一起,使用相同的配置,面板基于ThinkPHP,客户端只是ThinkPHP的一个命令。我之所以这样做,是希望直接在Workerman中使用ThinkPHP的众多特性(数据库、缓存

实际上,我们可以将客户端的代码,另外开一个项目,使客户端和面板独立开。在面板上实现通用得API。客户端通过API操作数据。这样客户端就不需要连接数据库。好处多多。

Mais cela entraîne également plus de charge de travail. Dans ce cas, nous pensons naturellement que l'environnement client n'est pas sûr, nous devons donc effectuer une authentification des autorisations et une authentification de connexion. Le développement d’interfaces nécessite également d’écrire davantage de code.

Résumé

Cet article présente principalement mes idées pour implémenter le pare-feu IP. Ces technologies exigent que les développeurs aient une riche expérience en développement de sites Web. Cette exigence n'est pas élevée, mais ils doivent également avoir une expérience de base en développement de réseaux, qui a un certain seuil. Workerman est très simple, mais Workerman n'est pas HTTP Il ne s'agit pas d'un développement de site Web ordinaire et nécessite un certain apprentissage et un changement de mentalité. Mais pour moi, c’est facile de conduire en toute simplicité. Si je recherche d’autres solutions à apprendre, à déployer et à tester, cela ne sera peut-être pas plus rapide que de les développer moi-même.

Déclaration de droits d'auteur : cet article est original et publié par phpreturn.com (PHPSite officiel d'Arsenal). Tous les droits appartiennent à phpreturn (PHPArsenal). citation Article, mais la source doit être indiquée en même temps.

IP

Comment gérer la liste blanche ? Vous pouvez l'ajouter via le panneau, ou vous pouvez visiter une page du panneau pour obtenir automatiquement l'IPIP sortante et l'ajouter à la liste blanche. . En fait, il existe un meilleur moyen, c'est de créer un serveur

rss

, et d'obtenir automatiquement le réseau sortant IP des clients qui s'abonnent à rss et de les ajouter à la liste blanche. Mais je n'ai pas l'habitude d'utiliser rss, et je n'ai pas un bon lecteur rss sur mon téléphone, et je ne veux pas l'ouvrir à chaque fois que je mets à jour la IPliste blanche, donc Je n'ai pas utilisé cette solution. Je l'ai rendu open source, si vous en avez besoin, vous pouvez vous référer à : https://gitee.com/augushong/ip-door.

Plus

Par rapport à

iptables

, ce système offre simplement une expérience de gestion de liste blanche IP plus pratique, qui équivaut à une simple machine bastion. Il peut cacher certains ports pour que seul « moi » puisse se connecter. Par exemple, masquez le port de

ssh

et transférez-le via le contrôle d'accès ip. Un autre exemple consiste à masquer le port 80 et à le transférer via le contrôle d'accès ip. Actuellement, mon système n'a pas implémenté la liaison et le transfert simultanés de plusieurs ports, mais l'idée de base est la même et peut être utilisée comme référence.

Déclaration de droits d'auteur : cet article est original et publié par

phpreturn.com

(PHPSite officiel d'Arsenal). Tous les droits appartiennent à phpreturn (PHPArsenal). citation Article, mais la source doit être indiquée en même temps. Cet article est réimprimé, titre original : IP

Contrôle d'accès : Apprenez-vous étape par étape à utiliser

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer