企業運維實踐-Nginx使用geoip2模組並利用MaxMind的GeoIP2資料庫實現處理不同國家或城市的存取最佳實踐指南

2022-08-12 12:01:47

關注「WeiyiGeek」公眾號

設為「特別關注」每天帶你玩轉網路安全運維、應用開發、物聯網IOT學習!

希望各位看友【關注、點贊、評論、收藏、投幣】,助力每一個夢想。


本章目錄


首發地址: https://mp.weixin.qq.com/s/PPUaSsBhS5Kv1SLiVjc4Gw


0x00 前言簡述

描述: 為了實現根據存取者存取我們的網站時根據其IP顯示其所屬地,也為獲取不同地區存取者的IP地址等相關資訊為目的,所以在搜尋引擎中查詢解決方案,在網路上查詢到如下幾種方案Nginx+GeoIP2、使用收費 IP 識別介面、DNS 根據地域解析,然後經過多方面考究,最終還是使用Nginx+GeoIP2解決方案。

三種解決方案優缺點


所有在本章中,我將向您展示如何搭建與設定一個有效的 Nginx 和 Max Mind GeoIP2/GeoLite2 設定。 從安裝部署包的下載、編譯安裝步驟以及命令使用、包括安裝過程中可能會遇到的坑,在最後博主也通過幾個實踐例子,展示如何使用 GeoIP2 按國家/地區限制對某些 URL 的存取。


TIPS: MaxMind GeoIP 已經被棄用了一段時間。 對於您最新的地理定位需求,請改用 MaxMind GeoIP2(或免費版本的 GeoLite2)。
TIPS: 當前網上大部分Nginx + GeoIP 的教學都是 GeoIP 老版本的已經不適用了當前最新版本的Nginx二進位制編譯安裝,你可參照本章更快的進行實踐使用。

TIPS: GeoUP 依賴 MaxMind 的 IP 資料,需要頻繁更新,所以我們在安裝設定後也實現了使用crontab服務,針對其國家城市資料庫進行自動化指令碼定時更新設定。

好了,不多說了,下面直接開始實踐吧!


0x01 安裝部署

環境說明

環境一覽:

# 宿主機系統
$ uname -a
113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/issue.net
Ubuntu 20.04.1 LTS

# 應用軟體
nginx-1.22.0.tar.gz
libmaxminddb-1.6.0.tar.gz
ngx_http_geoip2_module-3.4.tar.gz
GeoLite2-City_20220802.tar.gz
GeoLite2-Country_20220802.tar.gz

溫馨提示: 此處使用的是 Ubuntu 20.04 作業系統, 該系統已做安全加固和核心優化符合等保2.0要求【SecOpsDev/Ubuntu-InitializeSecurity.sh at master · WeiyiGeek/SecOpsDev 】, 如你的Linux未進行相應設定環境可能與讀者有些許差異, 如需要進行(windows server、Ubuntu、CentOS)安全加固請參照如下加固指令碼進行加固, 請大家瘋狂的 star 。
加固指令碼地址:【 https://github.com/WeiyiGeek/SecOpsDev/blob/master/OS-作業系統/Linux/Ubuntu/Ubuntu-InitializeSecurity.sh

為了節省大家的實踐時間,我已經把需要用到的原始碼包上傳到空間中,有需要的朋友可以看一下,下載地址: http://share.weiyigeek.top/d/36158960-50338508-7c5982?p=2088(存取密碼:2088)
溫馨提示: 如提示證書不對,請點選高階繼續存取即可.

原文地址: https://blog.weiyigeek.top


libmaxminddb 下載

描述: 首先安裝 libmaxminddb 庫,其提供了一個用於讀取MaxMind DB檔案的C庫,包括來自MaxMind的GeoIP2資料庫。這是一種自定義二進位制格式,旨在促進 IP 地址的快速查詢,同時允許在與地址關聯的資料型別方面具有極大的靈活性。

專案地址: https://github.com/maxmind/libmaxminddb
下載構建:

wget -c https://github.com/maxmind/libmaxminddb/releases/download/1.6.0/libmaxminddb-1.6.0.tar.gz
tar -zxvf libmaxminddb-1.6.0.tar.gz && cd libmaxminddb-1.6.0
./configure
make && make install
tee -a /etc/ld.so.conf.d/libc.conf <<'EOF'
# libc default configuration
/usr/local/lib
EOF
sudo ldconfig

或使用apt命令進行安裝:

$ sudo apt update
$ sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin geoipupdate

上面安裝的軟體包是:

  • libmaxminddb0 libmaxminddb-dev – MaxMind 地理定位資料庫
  • mmdb-bin – 二進位制。 從命令列呼叫的程式。 使用此命令手動定位 IP。
  • geoipupdate – 幫助設定和更新 GeoIP2 / GeoLite2 的軟體包。

ngx_http_geoip2_module 下載

描述: 下載 ngx_http_geoip2_module 使用基於使用者端 IP(預設)或特定變數(同時支援 IPv4 和 IPv6)的 maxmind geoip2 資料庫中的值建立變數,該模組現在支援nginx流,並且可以以與http模組相同的方式使用。

專案地址: https://github.com/leev/ngx_http_geoip2_module/
下載構建:

wget -c https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz -O /usr/local/src/ngx_http_geoip2_module-3.4.tar.gz
tar -zxf ngx_http_geoip2_module-3.4.tar.gz && ls ngx_http_geoip2_module-3.4/
  # config  LICENSE  ngx_http_geoip2_module.c  ngx_stream_geoip2_module.c  README.md

Geoip2 模組語法
語法範例:

# HTTP
http {
  ...
  geoip2 /etc/maxmind-country.mmdb {
    auto_reload 5m;
    $geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
 }
}

# Stream
stream {
  ...
  geoip2 /etc/maxmind-country.mmdb {
    auto_reload 5m;
    $geoip2_data_country_code default=US source=$remote_addr country iso_code;
  }
  ...
}

引數說明:

  • auto_reload <interval> : 啟用自動重新載入將使 nginx 以指定的時間間隔檢查資料庫的修改時間,如果發生更改則重新載入。

  • $variable_name [default=<value] [source=$variable_with_ip] path ...: 如果沒有指定【default】引數,則如果未找到該變數將為空,如果沒有指定【source】引數 $remote_addr 將用於執行查詢。


從上面語法格式中您是否是一片茫然,不管你是不是反正我第一次看到就茫然了,那 country iso_code 關鍵字又是從何而來?

為了解決上面這個疑問,我們在來看看如下操作。


mmdblookup 命令
描述: 在前面編譯安裝libmaxminddb庫後,我們便可以使用 mmdblookup 工具,查詢所需資料的路徑(例如:國家/地區名稱),以JSON格式返回的,其中continent(洲) 、country (國家) 、registered_country(已註冊的國家)物件包含了code/geoname_id/names鍵:

GeoLite2-Country.mmdb 庫只帶有 country 相關資料樣本輸出

$ mmdblookup --file ./GeoLite2-Country.mmdb --ip 223.6.6.6
{
  "continent":
    {
      "code":
        "AS" <utf8_string>
      "geoname_id":
        6255147 <uint32>
      "names":
        {
          "de":
            "Asien" <utf8_string>
          "en":
            "Asia" <utf8_string>
          "es":
            "Asia" <utf8_string>
          "fr":
            "Asie" <utf8_string>
          "ja":
            "アジア" <utf8_string>
          "pt-BR":
            "Ásia" <utf8_string>
          "ru":
            "Азия" <utf8_string>
          "zh-CN":
            "亞洲" <utf8_string>
        }
    }
  "country":
    {
      "geoname_id":
        1814991 <uint32>
      "iso_code":
        "CN" <utf8_string>
      "names":
        {
          "de":
            "China" <utf8_string>
          "en":
            "China" <utf8_string>
          "es":
            "China" <utf8_string>
          "fr":
            "Chine" <utf8_string>
          "ja":
            "中國" <utf8_string>
          "pt-BR":
            "China" <utf8_string>
          "ru":
            "Китай" <utf8_string>
          "zh-CN":
            "中國" <utf8_string>
        }
    }
  "registered_country":
    {
      "geoname_id":
        1814991 <uint32>
      "iso_code":
        "CN" <utf8_string>
      "names":
        {
          "de":
            "China" <utf8_string>
          "en":
            "China" <utf8_string>
          "es":
            "China" <utf8_string>
          "fr":
            "Chine" <utf8_string>
          "ja":
            "中國" <utf8_string>
          "pt-BR":
            "China" <utf8_string>
          "ru":
            "Китай" <utf8_string>
          "zh-CN":
            "中國" <utf8_string>
        }
    }
}

GeoLite2-City.mmdb 庫帶有 country City 相關資料樣本輸出 (一般推薦使用該庫)

$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6
 {
    "city":
      {
        "geoname_id":
          1808926 <uint32>
        "names":
          {
            "de":
              "Hangzhou" <utf8_string>
            "en":
              "Hangzhou" <utf8_string>
            "es":
              "Hangzhou" <utf8_string>
            "fr":
              "Hangzhou" <utf8_string>
            "ja":
              "杭州市" <utf8_string>
            "pt-BR":
              "Hangzhou" <utf8_string>
            "ru":
              "Ханчжоу" <utf8_string>
            "zh-CN":
              "杭州" <utf8_string>
          }
      }
    "continent":
      {
        "code":
          "AS" <utf8_string>
        "geoname_id":
          6255147 <uint32>
        "names":
          {
            "de":
              "Asien" <utf8_string>
            "en":
              "Asia" <utf8_string>
            "es":
              "Asia" <utf8_string>
            "fr":
              "Asie" <utf8_string>
            "ja":
              "アジア" <utf8_string>
            "pt-BR":
              "Ásia" <utf8_string>
            "ru":
              "Азия" <utf8_string>
            "zh-CN":
              "亞洲" <utf8_string>
          }
      }
    "country":
      {
        "geoname_id":
          1814991 <uint32>
        "iso_code":
          "CN" <utf8_string>
        "names":
          {
            "de":
              "China" <utf8_string>
            "en":
              "China" <utf8_string>
            "es":
              "China" <utf8_string>
            "fr":
              "Chine" <utf8_string>
            "ja":
              "中國" <utf8_string>
            "pt-BR":
              "China" <utf8_string>
            "ru":
              "Китай" <utf8_string>
            "zh-CN":
              "中國" <utf8_string>
          }
      }
    "location":
      {
        "accuracy_radius":
          1000 <uint16>
        "latitude":
          30.299400 <double>
        "longitude":
          120.161200 <double>
        "time_zone":
          "Asia/Shanghai" <utf8_string>
      }
    "registered_country":
      {
        "geoname_id":
          1814991 <uint32>
        "iso_code":
          "CN" <utf8_string>
        "names":
          {
            "de":
              "China" <utf8_string>
            "en":
              "China" <utf8_string>
            "es":
              "China" <utf8_string>
            "fr":
              "Chine" <utf8_string>
            "ja":
              "中國" <utf8_string>
            "pt-BR":
              "China" <utf8_string>
            "ru":
              "Китай" <utf8_string>
            "zh-CN":
              "中國" <utf8_string>
          }
      }
    "subdivisions":
      [
        {
          "geoname_id":
            1784764 <uint32>
          "iso_code":
            "ZJ" <utf8_string>
          "names":
            {
              "en":
                "Zhejiang" <utf8_string>
              "fr":
                "Province de Zhejiang" <utf8_string>
              "zh-CN":
                "浙江省" <utf8_string>
            }
        }
      ]
  }

GeoLite2-Country.mmdb 與 GeoLite2-Country.mmdb 對比。

# - 國家 ./GeoLite2-Country.mmdb 庫
# 如果此時我只想獲取 country 的名稱可以這樣。
$ mmdblookup --file ./GeoLite2-Country.mmdb --ip 223.6.6.6 country names zh-CN
  "中國" <utf8_string>
# 當然如果你想獲取國家的 iso_code 也是同樣的。
$ mmdblookup --file ./GeoLite2-Country.mmdb --ip 223.6.6.6 country iso_code
  "CN" <utf8_string>

# - 國家、城市庫 ./GeoLite2-Country.mmdb 庫
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 country names zh-CN
  "中國" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 country iso_code
  "CN" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 continent names zh-CN
  "亞洲" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 subdivisions 0 names zh-CN
  "浙江省" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 subdivisions 0 names iso_code
  "ZJ" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 city names zh-CN
  "杭州" <utf8_string>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 location longitude # 經度  
  120.161200 <double>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 location latitude  # 緯度
  30.299400  <double>
$ mmdblookup --file ./GeoLite2-City.mmdb --ip 223.6.6.6 location time_zone # 時區
  "Asia/Shanghai" <utf8_string>

上述的兩個範例我們可以將其轉換為 geoip2 模組定義的nginx變數。

# 如只需要國家資訊建議使用該庫
geoip2 /usr/local/GeoIP2/GeoLite2-Country.mmdb {
  $geoip2_data_country "default=China" source=$remote_addr country names en
}

# 如需要獲取國家以及省份資訊建議使用該庫,此處暫不演示使用,在後續實踐中再進行介紹和使用。
geoip2 /usr/local/GeoIP2/GeoLite2-City.mmdb {
  $geoip2_data_country "default=中國" source=$remote_addr country names zh-CN;  # 中國
  $geoip2_data_country_code country iso_code;                  # CN
  $geoip2_data_country_continent continent names zh-CN;        # 亞洲
  $geoip2_data_country_continent_code continent code;          # AS
  $geoip2_data_province_name subdivisions 0 names zh-CN;       # 浙江省
  $geoip2_data_province_isocode subdivisions 0 names iso_code; # "ZJ"
  $geoip2_data_city city names zh-CN;                         # 杭州
  $geoip2_data_city_longitude location longitude;              # 120.161200
  $geoip2_data_city_latitude location latitude;                # 30.299400
  $geoip2_data_city_time_zone location time_zone;             # "Asia/Shanghai"
}

溫馨提示: 當請求來自受信任的地址時,將使用「X-Forwarded-For」請求檔頭欄位中的地址, 並且設定 geoip2_proxy_recursive < on | off > 指令。

  • 如果遞迴搜尋被禁用,那麼將使用「X-Forwarded-For」中傳送的最後一個地址,而不是與一個受信任地址匹配的原始使用者端地址。
  • 如果啟用了遞迴搜尋,那麼將使用「X-Forwarded-For」中傳送的最後一個非信任地址,而不是與可信地址之一匹配的原始使用者端地址。

模組使用參考地址:https://github.com/leev/ngx_http_geoip2_module/#example-usage


Geoip2 模組編譯動態連結庫

描述: 有可能此時你通過原始碼編譯方式安裝 Nginx 了 ,那如何加入新的Nginx模組呢?

答: 那就是重新編譯 Nginx 即可,我們不需要執行make install重新安裝 Nginx 具體操作如下所示。

Step 1.執行nginx -v獲取原編譯構建的引數。

nginx version: nginx/1.22.0
built by gcc 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
built with OpenSSL 1.1.1q  5 Jul 2022
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-pcre=../pcre-8.45 ......  --with-ld-opt=-Wl,--as-needed,-O1,--sort-common 

Step 2.此處補充一點,你完全可按照自身需求使用--add-module進行靜態連結庫安裝,或者使用--add-dynamic-module進行動態連結庫安裝。

# 動態連結庫安裝模組 (絕對或者相對路徑)
--add-dynamic-module=../ngx_http_geoip2_module-3.4
--add-dynamic-module=/usr/local/src/ngx_http_geoip2_module-3.4

# 靜態連結庫生成模組
--add-module=../ngx_http_geoip2_module-3.4

Step 3.將步驟1獲取的引數加入到./configure, 並在末尾新增上--add-dynamic-module=/usr/local/src/ngx_http_geoip2_module-3.4, 以重新構建支援 geoip2 模組的 nginx 二進位制檔案。

cd /usr/local/src/nginx-1.22.0/

# 預編譯引數
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-pcre=../pcre-8.45 --with-zlib=../zlib-1.2.12 --with-openssl=../openssl-1.1.1q --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --with-threads --with-http_sub_module --with-http_v2_module --with-http_auth_request_module --with-http_realip_module --with-http_secure_link_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_ssl_module --with-http_slice_module --with-http_stub_status_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_geoip_module --with-mail --with-mail_ssl_module --with-http_addition_module --with-http_random_index_module --with-compat --with-file-aio --with-cc-opt='-Os -fomit-frame-pointer -g' --with-ld-opt=-Wl,-rpath,/usr/local/luajit/lib,--as-needed,-O1,--sort-common --add-module=/usr/local/src/ngx_devel_kit-0.3.1 --add-module=/usr/local/src/lua-nginx-module-0.10.21 --add-dynamic-module=/usr/local/src/echo-nginx-module-0.62 --add-dynamic-module=/usr/local/src/ngx_http_geoip2_module-3.4

# 編譯構建
make

# 編譯後將會在objs目錄生成動態連結庫,我們需複製到 /usr/local/nginx/modules
$ ls objs/*.so
  objs/ngx_http_geoip2_module.so  objs/ngx_stream_geoip2_module.so
$ cp -a objs/*.so /usr/local/nginx/modules
$ ls /usr/local/nginx/modules
ngx_http_echo_module.so  ngx_http_geoip2_module.so  ngx_stream_geoip2_module.so

# 然後使用objs目錄中生成的 nginx 二進位制檔案覆蓋 /usr/sbin/nginx
$ cp -a objs/nginx /usr/sbin/nginx
$ make upgrade 

# 最後執行此命令驗證安裝是否成功
nginx -V 

GeoLite2 資料庫下載

描述: Nginx 的 ngx_*_geoip2_module 模組依賴於 GeoLite2 資料庫, 免費的 GeoLite2 資料庫可從 Maxminds 網站獲得(需要註冊),GeoLite2 資料庫基於 IP 地址的資料庫和 Web 服務,提供有關地理位置、人口統計和使用者以及匿名者的資料。

如果你想下載與更新 GeoLite2 資料庫,您需要擁有 MaxMind 帳戶 ID 和許可證金鑰, 並且當我們在 nginx 中使用則該 GeoIP2 模組,在我們請求時Nginx時根據IP地址來識別來源國家城市,但是我們需要提前下載載該資料庫.

簡單流程: 首先存取 Maxminds 官網,然後註冊登陸到使用者後臺,建立並獲取 License Key, 最後下載 GeoLite2 資料庫該壓縮包,裡面包含的是二進位制mmdb格式的庫檔案。

Maxminds 官網地址: https://maxmind.com
GeoIP2 Web 服務演示(每天25次限額):https://www.maxmind.com/en/geoip2-precision-demo?ip_address=223.6.6.6
Locate My IP Address : https://www.maxmind.com/en/locate-my-ip-address

如果無法登入官網或者你不想註冊登陸,也可以下載博主已經從官網下載好的 GeoIP2 資料庫。

GeoLite2 資料庫下載地址: http://share.weiyigeek.top/d/36158960-50280983-746907 (存取密碼: 2088)
溫馨提示: 如提示證書不對,請點選高階繼續存取即可.


實踐流程

Step 1.存取並登陸maxmind.com官網,此處我已經註冊了賬號,就不在演示如何註冊了,如果沒有註冊的朋友可以安裝如下提示進行註冊(https://support.maxmind.com/hc/en-us/articles/4407099783707-Create-an-Account),相信大家都沒問題。


Step 2.登陸後點選 Account 使用者-> Manage License Keys 管理許可 -> 生成許可,然後將會看見 New license key successfully created 提示,其中請記錄號 Account/User ID 與 License key ,以備後續使用。


Step 3.隨後點選右邊的GeoIP2索引標籤中的 Download Files, 你可安裝需要下載 Country 國家 或者 City 城市的資料,上述下載連線中我們已經下載瞭如圖中的兩個壓縮包。

官方下載地址: https://www.maxmind.com/en/accounts/<your user account id>/geoip/downloads


Step 4.下載後上傳到Nginx伺服器並解壓到 /usr/local/GeoIP2 目錄之中。

# 若不存在該目錄請建立
mkdir -vp /usr/local/GeoIP2

# 解壓資料庫
ls ~
GeoLite2-City_20220802.tar.gz  GeoLite2-Country_20220802.tar.gz
tar -zxf GeoLite2-City_20220802.tar.gz
tar -zxf GeoLite2-Country_20220802.tar.gz

# 將 mmdb 檔案複製到 /usr/local/GeoIP2 目錄下
mv GeoLite2-City_20220802/GeoLite2-City.mmdb /usr/local/GeoIP2/
mv GeoLite2-Country_20220802/GeoLite2-Country.mmdb /usr/local/GeoIP2/
rm -rf GeoLite2-City_20220802/ GeoLite2-Country_20220802/

# 檢視解壓到 /usr/local/GeoIP2/ 後的 GeoIP2 資料庫
$ tree /usr/local/GeoIP2/
/usr/local/GeoIP2/
├── GeoLite2-City.mmdb
└── GeoLite2-Country.mmdb

Step 5.驗證GeoIP模組部署環境,我們需要針對nginx相關組態檔進行如下設定:

# 1.在 nginx.conf 中進行如下幾個關鍵部分設定。
$ vim nginx.conf
worker_processes  auto;
# -- 關鍵點: 載入 geoip2 模組動態連結庫
load_module modules/ngx_http_geoip2_module.so;
load_module modules/ngx_stream_geoip2_module.so;
....

http {
    ...
    # -- 關鍵點: 紀錄檔格式
    log_format demo '$remote_addr - [ $geoip2_country_code $geoip2_data_city_name ] - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" rt=$request_time urt=$upstream_response_time';
    
   # -- 關鍵點: geoip 模組變數繫結
    geoip2 /usr/local/GeoIP2/GeoLite2-Country.mmdb {
      $geoip2_country_code country names en;
    }
    geoip2 /usr/local/GeoIP2/GeoLite2-City.mmdb {
      $geoip2_data_country_name country names en;
      $geoip2_data_country_code default=China source=$remote_addr country iso_code;
      $geoip2_data_city_name city names en;
      $geoip2_data_province_name subdivisions 0 names en;
      $geoip2_data_province_isocode subdivisions 0 iso_code;
      $geoip2_continent_code continent code;
    }
    ...
}

# 2.在 demo.conf 設定如下 location 以驗證 GeoIP。
server {
....
  # -- 關鍵點: 存取紀錄檔設定
  access_log /var/log/nginx/demo-${logdate}.log demo;
  ...
  # -- 關鍵點: 該路徑顯示當前請求存取地址資訊
  location = /api/v1/info {
     default_type text/plain;
     return 200 "$remote_addr\n geoip2_country_code=$geoip2_country_code\n geoip2_data_country_name=$geoip2_data_country_name \n geoip2_data_country_code=$geoip2_data_country_code \n geoip2_data_city_name=$geoip2_data_city_name \n geoip2_continent_code=$geoip2_continent_code \n geoip2_data_province_name=$geoip2_data_province_name \n geoip2_data_province_isocode=$geoip2_data_province_isocode";
   }
...
}

Step 6.設定完成後檢查nginx設定並過載nginx執行如下命令即可 nginx -t && nginx -s reload, 然後使用瀏覽器進行存取 http://demo.weiyigeek.top/api/v1/info 驗證,將會輸入如下資訊。

222.177.***.***
 geoip2_country_code=China
 geoip2_data_country_name=China 
 geoip2_data_country_code=CN 
 geoip2_data_city_name= 
 geoip2_continent_code=AS 
 geoip2_data_province_name= 
 geoip2_data_province_isocode=

至此,GeoIP2 模組的所需環境的搭建就完畢了,更多模組範例請參考 ngx_http_geoip2_module 檔案 , 下一章節將進行實戰講解該模組的使用。


GeoLite2 資料庫自動更新

描述: 為了保證資料庫中國家與城市的準確性,我們需要設定cron定時任務來更新MaxMind提供的GeoLite2-Country.mmdb或者GeoLite2-City.mmdb資料庫庫, 以保證其資料庫保持最新。


操作流程

Step 1.使用apt命令幫助設定和更新 GeoIP2 / GeoLite2 的軟體包。

apt install -y geoipupdate

Step 2.使用文字編輯器開啟並編輯位於 /etc/GeoIP.confMaxMind GeoIP conf 檔案, 使用上述步驟獲取的 AccountID 和 LicenseKey 欄位資訊填入其檔案中, 之後便可執行進行地理位置資料庫,操作後如下圖所示

$ tee /etc/GeoIP.conf <<'EOF'
AccountID 696302
LicenseKey ycm3xq02oE7QXMOw
EditionIDs GeoLite2-Country GeoLite2-City
DatabaseDirectory /usr/local/GeoIP2
EOF

ls -alh /usr/local/GeoIP2  # 手動更新前
geoipupdate --stack-trace
ls -alh /usr/local/GeoIP2  # 手動更新後


  • Step 3.為了方便運維管理我們可以建立定時任務自動更新,提高工作效率。
# 查詢執行檔案絕對路徑
$ which geoipupdate
/usr/bin/geoipupdate

# 每週天的凌晨更新資料庫
$ crontab -e
0 * * * 6 /usr/local/bin/geoipupdate > /var/log/geoipupdate.log 2>&1

官網更新參考地址: https://dev.maxmind.com/geoip/updating-databases?lang=en.


0x02 實踐使用

1.使用GeoIP2模組請求使用者端的IP地址國家省份經緯度展示

描述: 本次實踐將根據請求者的國家顯示中文或者英文的IP地址位置等相關資訊在網頁上,通過前面的學習,我們知道 geoip2 在檢索有關 geoip 資料庫的後設資料時,其語法格式為 $variable_name metadata <field>,

實踐流程

  • Step 1.編輯Nginx.conf主組態檔在 http 片段中,自定義定義存取紀錄檔格式後加入如下兩個 geoip2 指令片段。
http {
  .....
  geoip2 /usr/local/GeoIP2/GeoLite2-Country.mmdb {
    # 啟用自動重新載入將使 nginx 以指定的時間間隔檢查資料庫的修改時間,如果發生更改則重新載入。
    auto_reload 7d;
    $geoip2_country_code country names en;
  }
  
  geoip2 /usr/local/GeoIP2/GeoLite2-City.mmdb {
    # 中國IP存取都顯示中文
    $geoip2_data_country "default=中國" source=$remote_addr country names zh-CN;  # 中國
    $geoip2_data_country_code country iso_code;                  # CN
    $geoip2_data_country_continent continent names zh-CN;        # 亞洲
    $geoip2_data_country_continent_code continent code;          # AS
    $geoip2_data_province_name subdivisions 0 names zh-CN;       # 浙江省
    $geoip2_data_province_isocode subdivisions 0 names iso_code; # "ZJ"
    $geoip2_data_city city names zh-CN;                          # 杭州
    $geoip2_data_city_longitude location longitude;              # 120.161200
    $geoip2_data_city_latitude location latitude;                # 30.299400
    $geoip2_data_city_time_zone location time_zone;              # "Asia/Shanghai"
  
    # 中國以外的存取都是顯示英文
    $geoip2_data_country_en "default=United States" source=$remote_addr country names en;  # United States
    $geoip2_data_country_code country iso_code;                     # US
    $geoip2_data_country_continent_en continent names en;           # North America
    $geoip2_data_country_continent_code continent code;             # NA
    $geoip2_data_province_name_en subdivisions 0 names en;          # ""
    $geoip2_data_province_isocode subdivisions 0 names iso_code;    # ""
    $geoip2_data_city city names en;                                # 杭州
    $geoip2_data_city_longitude location longitude;                 # 120.161200
    $geoip2_data_city_latitude location latitude;                   # 30.299400
    $geoip2_data_city_time_zone location time_zone;                 # "Asia/Shanghai"
  }
  ....
  
  map $geoip2_data_country_code $CN {
    CN yes;
    TW yes;
    HK yes;
    MO yes;
    default no;       
  }

}


  • Step 2.同樣編輯conf.d/demo.conf, 此處使用虛擬主機頭(demo.weiyigeek.top)做演, 加入如下指令片段,其主要作用是根據區其地區,使用中英文顯示請求者IP地理位置資訊。
$ vim conf.d/demo.conf
server {
  ...
  # 精準匹配
  location = /api/v1/ip {
    # 當存取者IP來自 `CN|TW|HK|MO` 時將會以json的形式進行返回中文的IP地址資訊。
    if ( $geoip2_data_country_code ~* (CN|TW|HK|MO) ){
      rewrite (.*)  /api/v1/ip/cn last;
    }
    rewrite (.*) /api/v1/ip/en last;
  }
  
  # 中文顯示
  location /api/v1/ip/cn {
    default_type application/json;
    return 200 '{"ip":"$remote_addr","country":{"name": "$geoip2_data_country", "iso_code":  "$geoip2_data_country_code", "continent": "$geoip2_data_country_continent","continent_code": "$geoip2_data_country_continent_code"},"province":{"name":"$geoip2_data_province_name","iso_code":"$geoip2_data_province_isocode"},"city":{"name":"$geoip2_data_city","timezone":"$geoip2_data_city_time_zone"},"location":{"longitude":"$geoip2_data_city_longitude","latitude":"$geoip2_data_city_latitude"}}';
  }
  
  # 英文顯示
  location /api/v1/ip/en {
    default_type application/json;
    return 200 '{"ip":"$remote_addr","country":{"name": "$geoip2_data_country_en", "iso_code":  "$geoip2_data_country_code", "continent": "$geoip2_data_country_continent_en","continent_code": "$geoip2_data_country_continent_code"},"province":{"name":"$geoip2_data_province_name_en","iso_code":"$geoip2_data_province_isocode"},"city":{"name":"$geoip2_data_city","timezone":"$geoip2_data_city_time_zone"},"location":{"longitude":"$geoip2_data_city_longitude","latitude":"$geoip2_data_city_latitude"}}';
  }
  ....
}

  • Step 3.設定 nginx 核驗與過載 nginx 服務, 此處使用不同的網路使用瀏覽器進行存取https://demo.weiyigeek.top/api/v1/ip驗證, 結果如下圖所示:。
nginx -t && nginx -s reload


2.使用GeoIP2模組靜止某一國家地區的IP地址存取網站

描述: 為了減少國外的攻擊,我們可以將指定的地區IP存取進行放行,除此之外的全部拒絕。

實際流程:

  • Step 1.在 nginx.conf 中新增 map 指令並進行如下設定, 預定義了可以存取網站的地區。
http {
....
  map $geoip2_data_country_code $allow_visit {
    CN yes;
    TW yes;
    HK yes;
    MO yes;
    default no;
  }
....
}

溫馨提示:

  1. map 指令是由ngx_http_map_module模組提供的,預設情況下安裝 nginx 都會安裝該模組.
  2. map 的主要作用是'建立自定義變數',通過使用 nginx 的'內建'變數,去'匹配'某些特定規則; 如果匹配成功則設定某個值給自定義變數,而這個'自定義變數'又可以'用作他用'。

  • Step 2.在demo.conf組態檔中新增一個存取驗證的範例。
# 存取該頁面如果$allow_visit變數不為yes則返回403頁面,否則返回存取者的IP地區資訊。
location /allow/html {
  default_type text/html;
  if ($allow_visit != yes ) {
    return 403 "IP [ $remote_addr ] 禁止存取! <br> $remote_addr - $geoip2_data_country - $geoip2_data_country_code - $geoip2_data_country_continent";
  } 
  return 200 "歡迎IP為 [ $remote_addr ] 使用者進行存取! <br> $remote_addr - $geoip2_data_country - $geoip2_data_country_code - $geoip2_data_country_continent";
}

  • Step 3.同樣修改完成後,我們需要針對nginx設定核驗與重新載入設定 nginx -t && nginx -s reload (PS: 後續將不再提示了,想必大家都聊熟於心了), ,之後分別使用工具進行存取驗證,結果如下所示。


3.使用GeoIP2模組實現不同國家存取進入不同目錄頁面

描述: 在某些時刻我們可能會對不同地區來源存取的客戶展示不同的頁面,例如國內我就顯示中文的頁面,而新加坡我就顯示英文的頁面,這樣一來就更加人性化一點。

範例演示
/usr/local/nginx/html目錄中建立ch/en子目錄,同時準備兩個不同地區存取的測試頁面:

$ tree /usr/local/nginx/html
├── ch
│   └── index.html
├── en
│   └── index.html


$ cat ch/index.html
<h1>中文站點</h1>
<iframe src="https://blog.weiyigeek.top" frameborder="0" width="500"></iframe>

Nginx 設定中使用GeoIP處理存取請求。

http {
....
  map $geoip2_data_country_code $lang_ch {
    CN yes;
    TW yes;
    HK yes;
    MO yes;
    default no;
  }
....
 server {
    listen       80;
    server_name  demo.weiyigeek.top;
    location / {
      set $rootpath html/ch; # 關鍵點設定一個根目錄變數。
      if ($lang_ch = no) { 
        set $rootpath html/en;
      }
      add_header program-path $rootpath; # 關鍵點寫入到響應頭中。
      add_header country-code $geoip2_data_country_code;
      root $rootpath;
      index index.html index.htm;
    }
  }
}

存取結果:


0x0n 入坑出坑

問題1.非二進位制方式安裝Nginx GeoIP過程中可能會出現的錯誤問題如下,也幫大家列舉出解決辦法。

錯誤資訊:

錯誤1:./configure: error: C compiler cc is not found。
錯誤2:./configure: error: the HTTP rewrite module requires the PCRE library.
錯誤3:./configure: error: the HTTP gzip module requires the zlib library.
錯誤4:./configure: error: the HTTP XSLT module requires the libxml2/libxslt。
錯誤5:./configure: error: the HTTP image filter module requires the GD library.
錯誤6:./configure: error: SSL modules require the OpenSSL library.

解決辦法: sudo apt install gcc libpcre3 libpcre3-dev zlib1g-dev libxslt-dev libgd-dev libssl-dev

問題2: 如果在進行Nginx編譯時出現 ./configure: error: the GeoIP module requires the GeoIP library.You can either do not enable the module or install the library. 問題提示時解決方法。

解決方式: 可以執行如下命令apt install -y libgeoip-dev命令進行解決。

原文地址: https://blog.weiyigeek.top/2022/7-3-678.html

本文至此完畢,更多技術文章,盡情期待下一章節!


WeiyiGeek Blog 個人部落格 - 為了能到遠方,腳下的每一步都不能少 】

歡迎各位志同道合的朋友一起學習交流,如文章有誤請在下方留下您寶貴的經驗知識!

作者主頁: 【 https://weiyigeek.top
部落格地址: 【 https://blog.weiyigeek.top 】

專欄書寫不易,如果您覺得這個專欄還不錯的,請給這篇專欄 【點個贊、投個幣、收個藏、關個注,轉個發,留個言】(人間六大情),這將對我的肯定,謝謝!。

  • echo "【點個贊】,動動你那粗壯的拇指或者芊芊玉手,親!"

  • printf("%s", "【投個幣】,萬水千山總是情,投個硬幣行不行,親!")

  • fmt.Printf("【收個藏】,閱後即焚不吃灰,親!")

  • console.info("【轉個發】,讓更多的志同道合的朋友一起學習交流,親!")

  • System.out.println("【關個注】,後續瀏覽檢視不迷路喲,親!")

  • cout << "【留個言】,文章寫得好不好、有沒有錯誤,一定要留言喲,親! " << endl;

更多網路安全、系統運維、應用開發、物聯網實踐、網路工程、全棧文章,盡在 https://blog.weiyigeek.top 之中,謝謝各位看又支援!