亚洲最大看欧美片,亚洲图揄拍自拍另类图片,欧美精品v国产精品v呦,日本在线精品视频免费

  • 站長(zhǎng)資訊網(wǎng)
    最全最豐富的資訊網(wǎng)站

    IP門(mén)禁:保姆式教你用PHP實(shí)現(xiàn)一個(gè)IP防火墻

    最近我遇到一個(gè)需求,我的一臺(tái)服務(wù)器總是遭到端口掃描和惡意登錄攻擊,對(duì)此可以怎么辦呢?似乎除了內(nèi)網(wǎng)隔離、增強(qiáng)密碼認(rèn)證、證書(shū)登錄、設(shè)置防火墻iptables,網(wǎng)上找不到什么別的方案,對(duì)了,還用堡壘機(jī)的方案。

    這些方案實(shí)際上都無(wú)法解決我的問(wèn)題。這是一臺(tái)公網(wǎng)服務(wù)器,并沒(méi)有什么復(fù)雜的網(wǎng)絡(luò)結(jié)構(gòu),所以不能建立內(nèi)網(wǎng)隔離。調(diào)整賬號(hào)的密碼策略,自然是一個(gè)方案,但是人工操作太麻煩,而且我一般經(jīng)常換電腦使用,如果修改密碼,公司的和家里的電腦都要更新,很麻煩。設(shè)置防火墻自然是運(yùn)維的基本操作,但是iptables的配置太麻煩,ufw工具還好些,firewall-cmd就麻煩些,而且有一個(gè)巨大的痛點(diǎn),眾所周知,大家的出網(wǎng)IP都會(huì)經(jīng)常變,好不容易在命令行里一個(gè)字母一個(gè)字母的配置好了,睡了一覺(jué),白費(fèi)了。堡壘機(jī)更不是一個(gè)主流的方案,有點(diǎn)大材小用,用了堡壘機(jī),反而不能隨意使用系統(tǒng),更何況還沒(méi)聽(tīng)說(shuō)過(guò)那個(gè)免費(fèi)的堡壘機(jī)呢。【推薦:PHP視頻教程】

    那怎么辦呢,作為一個(gè)資深的PHP開(kāi)發(fā)者,服務(wù)器這塊的應(yīng)用還不是手到擒來(lái),當(dāng)初連內(nèi)網(wǎng)穿透都能輕松實(shí)現(xiàn),一個(gè)IP過(guò)濾系統(tǒng),小意思。所以我打算自己開(kāi)發(fā)這樣一個(gè)項(xiàng)目,首先能夠?qū)崿F(xiàn)IP過(guò)濾,另外,可以輕松地將IP加入到白名單里,比如訪問(wèn)一個(gè)網(wǎng)頁(yè),就自動(dòng)加入到白名單。

    整個(gè)項(xiàng)目不到幾個(gè)小時(shí)就研發(fā)完了,起碼滿足了我自己的需求,并且實(shí)現(xiàn)了這樣幾個(gè)特性:

    • 多進(jìn)程
    • 支持并發(fā)
    • 守護(hù)進(jìn)程
    • 可以通過(guò)網(wǎng)頁(yè)面板管理IP
    • 流量統(tǒng)計(jì)
    • 攔截記錄

    現(xiàn)在我們來(lái)一步一步的實(shí)現(xiàn)這個(gè)系統(tǒng)。

    第一步,首先能夠簡(jiǎn)簡(jiǎn)單單的過(guò)濾IP

    使用PHP監(jiān)聽(tīng)端口并且轉(zhuǎn)發(fā)數(shù)據(jù)的框架很多,對(duì)此我選擇workerman,原因有3

    • 運(yùn)行簡(jiǎn)單穩(wěn)定
    • 方法接口簡(jiǎn)單
    • 內(nèi)置進(jìn)程守護(hù)

    至于具體的安裝方法,可以參考他的官方文檔。

    版權(quán)聲明:本文由phpreturn.com(PHP武器庫(kù)官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturnPHP武器庫(kù))所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時(shí)注明出處。

    IP門(mén)禁:保姆式教你用PHP實(shí)現(xiàn)一個(gè)IP防火墻

    workerman的使用方法非常簡(jiǎn)單,只要10行代碼,就實(shí)現(xiàn)了IP轉(zhuǎn)發(fā)+白名單過(guò)濾:

    $worker = new Worker('tcp:0.0.0.0:' . Config::get('door.port_in')); // 監(jiān)聽(tīng)一個(gè)端口 $worker->count = 2; // 設(shè)置多進(jìn)程 $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();     }     // 放行連接,連接內(nèi)部目標(biāo)端口     $to_connection = new AsyncTcpConnection('tcp:127.0.0.1:' . Config::get('door.port_to'));     // 互相轉(zhuǎn)發(fā)流量     $connection->pipe($to_connection);     $to_connection->pipe($connection);     $to_connection->connect(); }

    正如上面代碼所示,只有簡(jiǎn)單幾行,便實(shí)現(xiàn)了IP監(jiān)聽(tīng)和轉(zhuǎn)發(fā),其中IP白名單通過(guò)數(shù)據(jù)庫(kù)查詢,并且緩存。

    第二步,與ThinkPHP命令行整合在一起

    為了項(xiàng)目開(kāi)發(fā)方便,我都會(huì)使用ThinkPHP框架進(jìn)行開(kāi)發(fā),它夠簡(jiǎn)單,功能也比較齊全。

    IP門(mén)禁:保姆式教你用PHP實(shí)現(xiàn)一個(gè)IP防火墻

    最終實(shí)現(xiàn)的命令行效果如下:

    版權(quán)聲明:本文由phpreturn.com(PHP武器庫(kù)官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturnPHP武器庫(kù))所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時(shí)注明出處。

    運(yùn)行命令 php think door start php think door start --mode d  // 守護(hù)進(jìn)程重啟 重啟 php think door restart 停止 php think door stop

    workerman的命令參數(shù)與thinkphp并不兼容,但是實(shí)現(xiàn)這樣的效果并不難,實(shí)際上很簡(jiǎn)單,代碼如下:

    <?php  declare(strict_types=1);  namespace appcommoncommand;  use thinkconsoleCommand; use thinkconsoleInput; use thinkconsoleinputArgument; use thinkconsoleinputOption; use thinkconsoleOutput;  class Door extends Command {     protected function configure()     {         // 指令配置         $this->setName('door')             // 設(shè)置think的命令參數(shù)             ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')             ->addOption('mode', 'm', Option::VALUE_OPTIONAL, 'Run the workerman server in daemon mode.')             ->setDescription('the door command');     }     protected function execute(Input $input, Output $output)     {         // 指令輸出         $output->writeln('door');         $action = $input->getArgument('action');         $mode = $input->getOption('mode');         // 重新構(gòu)造命令行參數(shù),以便兼容workerman的命令         global $argv;         $argv = [];         array_unshift($argv, 'think', $action);         if ($mode == 'd') {             $argv[] = '-d';         } else if ($mode == 'g') {             $argv[] = '-g';         }         // ...workerman的代碼     } }

    在上面的代碼中,主要做了兩件事:

    • 實(shí)現(xiàn)ThinkPHP的命令設(shè)置
    • 將命令參數(shù)重新構(gòu)造為workerman兼容的方式

    第三步,實(shí)現(xiàn)管理面板

    使用PHP實(shí)現(xiàn)一個(gè)管理面板太簡(jiǎn)單了,PHP到處都是這樣的后臺(tái)框架,這里我選擇ulthon_admin,這是我自己開(kāi)發(fā)維護(hù)的,它基于ThinkPHP6,很簡(jiǎn)單,為定制而生,不搞所謂的“插件”和“市場(chǎng)”生態(tài),能夠自動(dòng)生成CURD代碼,并且內(nèi)置幾了幾個(gè)有趣的皮膚。

    最終效果如下:

    IP門(mén)禁:保姆式教你用PHP實(shí)現(xiàn)一個(gè)IP防火墻IP門(mén)禁:保姆式教你用PHP實(shí)現(xiàn)一個(gè)IP防火墻

    以上是ulthon_admin內(nèi)置的兩款皮膚效果,分別是:科幻、像素。

    對(duì)于面板的管理,這里多做介紹,這算是PHP開(kāi)發(fā)者的基本功,誰(shuí)還不會(huì)個(gè)CURD啊。

    版權(quán)聲明:本文由phpreturn.com(PHP武器庫(kù)官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturnPHP武器庫(kù))所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時(shí)注明出處。

    第四步,進(jìn)階,更好的性能和流量統(tǒng)計(jì)

    我們的IP攔截客戶端需要運(yùn)行在服務(wù)器上,并且直接連接數(shù)據(jù)庫(kù),如果每次收到請(qǐng)求都要查詢數(shù)據(jù)庫(kù),那么很有可能導(dǎo)致連接不通暢,尤其是客戶端和數(shù)據(jù)庫(kù)本身位置較遠(yuǎn)的時(shí)候。在第一步的代碼中,我們只是簡(jiǎn)單的使用了查詢緩存,但是還不夠,還可以優(yōu)化。并且我們可以在管理面板的截圖中看到,我們是可以統(tǒng)計(jì)流量和攔截次數(shù)的,現(xiàn)在我們要實(shí)現(xiàn)這些功能:

    流量統(tǒng)計(jì)

    首先我們將第一個(gè)步中,流量轉(zhuǎn)發(fā)部分的代碼改造成如下的樣子:

    <?php // 向TO發(fā)起連接 $to_connection = new AsyncTcpConnection('tcp://127.0.0.1:' . Config::get('door.port_to')); $to_connection->onMessage = function ($source, $data) use ($connection, $remote_ip) {     // 接收到來(lái)自TO的數(shù)據(jù),返回的數(shù)據(jù)     $connection->send($data);     // 將流量統(tǒng)計(jì)存儲(chǔ)到內(nèi)存里     Cache::inc(md5($remote_ip) . '-to', 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) {     // 接收來(lái)自IN的數(shù)據(jù),請(qǐng)求的數(shù)據(jù)     $to_connection->send($data);     // 將流量統(tǒng)計(jì)存儲(chǔ)到內(nèi)存里     Cache::inc(md5($remote_ip) . '-in', 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(); };

    在第一部的代碼中,只用兩行便實(shí)現(xiàn)了這些代碼:

    // 放行連接,連接內(nèi)部目標(biāo)端口 $to_connection = new AsyncTcpConnection('tcp:127.0.0.1:' . Config::get('door.port_to')); // 互相轉(zhuǎn)發(fā)流量 $connection->pipe($to_connection); $to_connection->pipe($connection);

    這里使用的是workerman內(nèi)置的流量轉(zhuǎn)發(fā),它很好用,但是這里我們要統(tǒng)計(jì)流量,所以我們手動(dòng)轉(zhuǎn)發(fā)流量。

    這里我們將統(tǒng)計(jì)的數(shù)據(jù)存儲(chǔ)到緩存里,而不是直接連接數(shù)據(jù)庫(kù)更新,這是為了更好的連接性能。我們會(huì)另外開(kāi)啟一個(gè)進(jìn)程將這些改動(dòng)更新到數(shù)據(jù)庫(kù)。后面會(huì)介紹到。

    攔截統(tǒng)計(jì)

    我們將第一步中的加載IP白名單的邏輯改成下面這樣:

    版權(quán)聲明:本文由phpreturn.com(PHP武器庫(kù)官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturnPHP武器庫(kù))所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時(shí)注明出處。

    <?php $worker->onConnect = function (TcpConnection $connection) {     $disable_cache_key = 'disable_ip_list';     $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();     } };

    在這里我們不連接數(shù)據(jù)庫(kù)查詢,而是直接從本地緩存讀取白名單,這樣會(huì)有更好的性能。我們會(huì)在另一個(gè)進(jìn)程中更新這份白名單。

    另外我們可以看到,攔截的IP調(diào)用了一個(gè)靜態(tài)方法,這里的功能很簡(jiǎn)單,判斷數(shù)據(jù)庫(kù)中該IP是否存在,如果不存在則新增,如果存在,則更新攔截次數(shù)+·1。這里就不多介紹了。這里也沒(méi)有必要做什么性能優(yōu)化,反正本來(lái)就是攔截的IP,優(yōu)化個(gè)毛。

    高性能處理緩存數(shù)據(jù)

    上面我們介紹,我們會(huì)另外開(kāi)啟一個(gè)進(jìn)程,維護(hù)IP白名單,并且將流量統(tǒng)計(jì)提交到數(shù)據(jù)庫(kù)。這就是這個(gè)進(jìn)程:

    <?php $worker_ip = new Worker(); $worker_ip->name = 'report'; $worker_ip->onWorkerStart = function () {     Timer::add(5, function () {         $disable_cache_key = 'disable_ip_list';         $list_ip = AppIp::where('status', 1)->column('ip');         Cache::set($disable_cache_key, $list_ip);         foreach ($list_ip as  $ip) {             $ip_md5 = md5($ip);             $in_length = Cache::pull("$ip_md5-in");             // 請(qǐng)求的數(shù)據(jù)             $to_length = Cache::pull("$ip_md5-to");             // 返回的數(shù)據(jù)             if (!empty($in_length) || !empty($to_length)) {                 $model_ip = AppIp::where('ip', $ip)->find();                 $model_ip->in_buffer += $in_length;                 $model_ip->to_buffer += $to_length;                 $model_ip->save();             }         }     }); };

    他做的事情很簡(jiǎn)單,讀取緩存,更新數(shù)據(jù)到數(shù)據(jù)庫(kù),并且更新IP白名單。這里不需要考慮它和數(shù)據(jù)庫(kù)之間的性能問(wèn)題,這是額外的進(jìn)程,不影響端口的連接和轉(zhuǎn)發(fā)。

    下一步,更好的性能設(shè)計(jì)

    以上,只有幾行代碼,幾個(gè)小時(shí)(如果不含設(shè)計(jì)系統(tǒng)的時(shí)間,代碼量可能只有一兩個(gè)小時(shí)。還能再怎么優(yōu)化呢?實(shí)際上還是可以優(yōu)化的。

    更好的內(nèi)存驅(qū)動(dòng)

    這里使用的是ThinkPHP內(nèi)置的文件緩存,存儲(chǔ)到磁盤(pán)上,以上方法,在大量連接并發(fā)時(shí),肯定受制于磁盤(pán)的性能。所以自然而然,我們可以使用內(nèi)存緩存。

    版權(quán)聲明:本文由phpreturn.com(PHP武器庫(kù)官網(wǎng))原創(chuàng)和首發(fā),所有權(quán)利歸phpreturnPHP武器庫(kù))所有,本站允許任何形式的轉(zhuǎn)載/引用文章,但必須同時(shí)注明出處。

    但是使用內(nèi)存緩存,redis可以嗎?并不好。這里是客戶端,它只是想簡(jiǎn)簡(jiǎn)單單實(shí)現(xiàn)一個(gè)攔截轉(zhuǎn)發(fā),還要再部署redis,不可取。

    但實(shí)際上,workerman本身內(nèi)置了數(shù)據(jù)共享組件,這是一個(gè)很好的方案。相當(dāng)于一個(gè)極簡(jiǎn)的redis。完美符合我們的需求。但是我并沒(méi)有實(shí)現(xiàn)這個(gè)功能,目前的系統(tǒng)已經(jīng)符合我的場(chǎng)景。

    更好的客戶端

    目前攔截IP客戶端和管理面板集成在一起,使用相同的配置,面板基于ThinkPHP,客戶端只是ThinkPHP的一個(gè)命令。我之所以這樣做,是希望直接在Workerman中使用ThinkPHP的眾多特性(數(shù)據(jù)庫(kù)、緩存。

    實(shí)際上,我們可以將客戶端的代碼,另外開(kāi)一個(gè)項(xiàng)目,使客戶端和面板獨(dú)立開(kāi)。在面板上實(shí)現(xiàn)通用得API。客戶端通過(guò)API操作數(shù)據(jù)。這樣客戶端就不需要連接數(shù)據(jù)庫(kù)。好處多多。

    但是這樣也帶來(lái)的

    贊(0)
    分享到: 更多 (0)
    網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)