[Laravel8.x] リリース前に特定のipのみログインできる様にする

リリース前にテストを行う場合に、特定のipからのアクセスのみログインできる様にしたい

$request->ip();でipを取得できる

    public function test(Request $request){
        $ip = [
            ['name'=>'a社 事業部', 'ip' => '127.0.0.1'],
            ['name'=>'a社 開発部', 'ip' => '127.0.0.2'],
            ['name'=>'b社', 'ip' => '127.0.0.3'],
            ['name'=>'myIP', 'ip' => '192.168.33.1'],
        ];
        
        $user_ip = $request->ip();
        $detect = collect($ip)->contains('ip', $request->ip());
        dd($detect);
        
        return view('admin.test');
    }

-> true

これをmiddlewareのrole_idの制御しているところで実装する。

    public function handle(Request $request, Closure $next)
    {
        
        
        $user = Auth::user();
        if(!$user->isAdmin()){

            $ip = [
                ['name'=>'a社 事業部', 'ip' => '127.0.0.1'],
                ['name'=>'a社 開発部', 'ip' => '127.0.0.2'],
                ['name'=>'b社', 'ip' => '127.0.0.3'],
                ['name'=>'myIP', 'ip' => '192.168.33.1'],
            ];
            
            $user_ip = $request->ip();
            $detect = collect($ip)->contains('ip', $request->ip());

            if(!$detect){
                Auth::logout();
                return redirect('/login')->withErrors(array('name' => 'ただ今ログインできません。'));    
            }
            return redirect()->intended('/hoge');
        }
        return $next($request);
    }

-> ただ今ログインできません。

なかなかテクニカルになってきた。

Betternet Unlimited Free VPN Proxy

Chromeのブラウザアクセス時に、ipアドレスを変更することができるChrome Extension.
ユーザー数が100万人って、思ったより少ない気がしますが、まず入れてみましょう。

Chromeに追加したら、接続します。

うーん、なんか上手くいかないな。

EIPとは?

– 企業内情報ポータルという意味でのEIP(Enterprise Information Portal)
– IPアドレスの一種であるEIP

Elastic IPという、Amazon Web Services(AWS)の文脈で用いられるEIP

Elastic IPアドレスは、AWSに登録したアカウントに紐つけされるIPアドレスです。IPアドレスは基本的にパブリックIPアドレスとプライベートIPアドレスの2つに分けることができ、パブリックIPアドレスはインターネットを通じて機器を利用する際に割り当てられるアドレス
一方のプライベートIPアドレスはインターネットではなくローカルのネットワークでのみ割り当てられるIPアドレスで、インターネットからは遮断されたIP

EC2ダッシュボードで、パブリックとプライベートのIPがありますね。
IPv4 パブリック IP
プライベート IP

Elastic IP、EIPはパブリックIPの方。なるほど。

ipアドレスに対応するホスト名を取得する

google.comのhost名をgethostbyaddrを使うと、

$url = "www.google.com";
$ip = "172.217.161.196";
$host = gethostbyaddr($ip);
echo $host;

きたー、ラリー・ページの10^100
なんか知らんが嬉しいね

www.nttdocomo.co.jp
ip:163.49.61.185
gethostbyaddr:185.61.49.163.static.iijgio.jp
え、iij使ってるの?

www.fsa.go.jp
ip:151.101.101.14
gethostbyaddr:151.101.101.14
金融庁、財務省、などはgethostbyaddrでipしか表示されない。

rakuten->deploy.static.akamaitechnologies.com
nttdata(184.27.113.234)->a184-27-113-234.deploy.static.akamaitechnologies.com
ん?どういうこと、これ?akamaitechnologiesってそんなにいいの?

gaだと、service providerは細かく表示されているので、恐らくプロバイダーのデータベースを持ってると思うんだが、、

あれ?
GeoIP ISP Edition
https://www.maxmind.com/ja/geoip2-isp-database
やっぱり。。しかしip周りの領域は、なにかとmaxmindと被るな。

service providerは、DBないとかなりキツイな~、というか関数自作は無理がある。。
ので、

$ip = "192.168.33.1";
$host = gethostbyaddr($ip);
if($host == $ip){
    $gethost = "(not set)";
} else {
    $gethost = $host;
}
echo $gethost;

よしよしよし

次は画面サイズ(screen resolution)
これはjsでいけると思います。

ipの地域をtableで表示する

getパラメーターの値で表示を切り替えます。

<table>
      <?php if($_GET&#91;"selectedDimension"&#93; == 'continent'): ?>
      <th>Continent</th><th>Users</th>
      <?php
        $i = 0;
        foreach($c1 as $key => $value){
          if($i < 10){
            echo "<tr>";
            echo "<td>".$key."</td><td>".$value."</td>";
            echo "</tr>";
          }
          $i++;    
        }
      ?>
      </table>
      <?php elseif($_GET&#91;"selectedDimension"&#93; == 'country'): ?>
      <th>Country</th><th>Users</th>
      <?php
        $i = 0;
        foreach($c2 as $key => $value){
          if($i < 10){
            echo "<tr>";
            echo "<td>".$key."</td><td>".$value."</td>";
            echo "</tr>";
          }
          $i++;    
        }
      ?>
      </table>
      <?php elseif($_GET&#91;"selectedDimension"&#93; == 'city'): ?>
      <th>City</th><th>Users</th>
      <?php
        $i = 0;
        foreach($c3 as $key => $value){
          if($i < 10){
            echo "<tr>";
            echo "<td>".$key."</td><td>".$value."</td>";
            echo "</tr>";
          }
          $i++;    
        }
      ?>
      </table>
      <?php else: ?>
      <th>Page</th><th>PageView</th>
      <?php
        $i = 0;
        foreach($data as $key => $value){
          if($i < 10){
            echo "<tr>";
            echo "<td>".$key."</td><td>".$value."</td>";
            echo "</tr>";
          }
          $i++;    
        }
      ?>
      </table>
      <?php endif; ?>

continent

country

city

一人デバックしてるなー
というか、これ、ページビュー単位ではなくて、cookie単位しないと駄目だ
うおおおおおおお、結構複雑だ

cookieに重複がない場合だけ、ipの地域を取得するに変更
foreachの前で$cookie=array();と宣言します。

$cookie = array();
foreach ($cursor as $document) {
  $pv[] = $document->date;
  if(!in_array($document->cookie, $cookie)){
    if(!is_null($document->continent)){
    $c1[] = $document->continent;
    }
    if(!is_null($document->contry)){
    $c2[] = $document->contry;
    }
    if(!is_null($document->city)){
    $c3[] = $document->city;
    }
  }
  $cookie[] = $document->cookie;
  $session[] = $document->session;
  if(!is_null($document->referrer)){
  $avgtime[] = $document->avgtime;
  }
  if(!is_null($document->referrer)){
  $referrer[] = $document->referrer;
  }
  $visit[] = $document->visit;
  $access[] = substr($document->date, 11, 2);
  $page[] = $document->page;    
}

continent, country, cityがuniqueになりました。
よっしゃ、ip周りはOKそう。

次は、browser, os, service provider
これ、js、phpでとれるんか?
取れなければ、スキップとしたい。が、GAでは必ず見る項目ではある。。

ipアドレスの地理情報を取得してmongoDBに挿入する

192.168.33.1だとgeoliteがエラーになるので、198.7.31.23にします。

$data = array("date"=>date("Y-m-d H:i:s"));
$audience = $_POST['userdata']; 
$mongo = "app.na".$audience[0][1];
$audience = array_splice($audience, 1);

$ip_addr = $audience[0][1];
if($ip_addr == "192.168.33.1"){
	$ip_addr = "198.7.31.23";
}

foreach($audience as $value){
	$data += array($value[0]=>$value[1]); 
}

require_once '../geolite/vendor/autoload.php';
use GeoIp2\Database\Reader;

$reader = new Reader('../geolite/GeoLite2-City.mmdb');
$record = json_decode(json_encode($reader->city($ip_addr)));

$continent = $record->continent->names->en;
$contry = $record->country->names->en;
$city = $record->city->names->en;

$data = $data + array('continent'=>$continent, 'contry'=>$contry, 'city'=>$city);

var_dump($data);

$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");

$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert($data);
$mng->executeBulkWrite($mongo, $bulk);

echo "finished!!!";

取得できています。

mongoDBにもはいってます。

-geoliteがエラーの時の為に、try catch (Exception $e) に変えると、以下の分岐条件は削除できます。

if($ip_addr == "192.168.33.1"){
	$ip_addr = "198.7.31.23";
}

ipの地理情報はmongoDBには入りません。

因みに、Chrom extensionでBetternet Unlimited Free VPN ProxyやHotspot Shieldなどを使うとIPアドレスを偽造できるようです。

次は、表示側を作ります。
flexを入れ子にしたレイアウトにします。

ipの大陸、国、地域を配列にpush

$audience = array("date"=>"2018-04-18 08:05:50", "ip"=>"198.7.31.23", "visit"=>"Returned User");

$ip_addr = $audience["ip"];

require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;

$reader = new Reader('GeoLite2-City.mmdb');
$record = json_decode(json_encode($reader->city($ip_addr)));

$continent = $record->continent->names->en;
$contry = $record->country->names->en;
$city = $record->city->names->en;

$audience = $audience + array('continent'=>$continent, 'contry'=>$contry, 'city'=>$city);

print_r("<pre>");
var_dump($audience);
print_r("</pre>");

きた!

ipから、continent, country, cityを取得

encodeしてdecodeします。

$reader = new Reader('GeoLite2-City.mmdb');
$ip_addr = '182.22.59.229';
$record = json_decode(json_encode($reader->city($ip_addr)));



echo $record->continent->names->en ."<br>";
echo $record->country->names->en ."<br>";
echo $record->city->names->en ."<br>";

首相官邸
https://www.kantei.go.jp/
202.214.216.10
Asia
Japan
Komagatani
大阪府羽曳野市駒ケ谷?

white house
https://www.whitehouse.gov/
North America
United States
Cambridge

おもろい!続けて..

ペニンシュラ
peninsula.com
North America
United States
cityがnullの場合もあるようです。
ってか、アメリカかい!

GeoIP2でipアドレスの住所判定

GeoLite2 CountryがIPアドレスから国を取得するデータベース、GeoLite2 CityがIPアドレスから地域を取得するデータベース

# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
# gunzip GeoLite2-Country.mmdb.gz
# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz
# gunzip GeoLite2-City.mmdb.gz

mmdbはインメモリデータベース
gunzip:圧縮された圧縮ファイルを解凍する

composerを入れてGeoIP2 PHP APIをインストール

$ curl -sS https://getcomposer.org/installer | php
composer require geoip2/geoip2:~2.0

<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;

$reader = new Reader('GeoLite2-City.mmdb');
$ip_addr = '182.22.59.229';
$record = $reader->city($ip_addr);

print_r("<pre>");
var_dump($record);
print_r("</pre>");
?>

ドイツ語、英語、スペイン語、フランス語、日本語、ポルトガル語、ロシア語、中国語で出力されます。
[“record”:”GeoIp2\Record\AbstractRecord”:private]=>
array(2) {
[“geoname_id”]=>
int(1850147)
[“names”]=>
array(8) {
[“de”]=>
string(5) “Tokio”
[“en”]=>
string(5) “Tokyo”
[“es”]=>
string(5) “Tokio”
[“fr”]=>
string(5) “Tokyo”
[“ja”]=>
string(6) “東京”
[“pt-BR”]=>
string(7) “Tóquio”
[“ru”]=>
string(10) “Токио”
[“zh-CN”]=>
string(6) “东京”
}
}

1.エリア、国、都市を取得
2.wget・gunzipをcronで実行できるか確認が必要
3.クライアントリクエストからの負担を軽くするため、都市の抽出はmongoDBに入れる前にphp側で実行したい