Check IP public, Kiểm tra xem IP đó có phải là VPN/proxy và chấm điểm rủi ro chỉ với một lệnh curl

Phần lớn các trang check ip khác được làm ra để xem bằng trình duyệt. Gọi chúng từ script thì hoặc nhận về nguyên một trang HTML, hoặc bị bắt đăng ký lấy API key chỉ để đọc đúng một dòng chữ.

Trong bài này mình dùng checkip.me, một dịch vụ tra cứu IP miễn phí, trả kết quả dạng text thuần nên gọi từ terminal rất tiện. Tất cả những gì bên dưới đều dùng được mà không cần tài khoản hay key.

Chỉ lấy IP

curl checkip.me
203.0.113.42

Kết quả trả về là text thuần, đúng thứ bạn cần khi viết script. Trên Windows 10/11 thì dùng curl.exe, vì trong PowerShell curl chỉ là alias của Invoke-WebRequest.

Nếu máy có cả IPv4 lẫn IPv6, curl có thể chọn bất kỳ cái nào. Thêm -4 hoặc -6 để chỉ định:

curl -4 checkip.me
curl -6 checkip.me

Xem chi tiết, hoặc chỉ lấy một trường

/json trả về thông tin mạng và vị trí của IP hiện tại. Muốn tra IP khác thì đặt địa chỉ đó vào path:

curl checkip.me/json
curl checkip.me/1.1.1.1
{
  "ip": "203.0.113.42",
  "asn": "AS45899",
  "isp": "VNPT Corp",
  "country": "Vietnam",
  "city": "Hanoi",
  "timezone": "Asia/Bangkok",
  "vpn": false,
  "proxy": false,
  "tor": false,
  "hosting": false,
  ...
}

Nếu chỉ cần một giá trị, thêm tên trường vào path. Kết quả trả về dạng text thuần nên không cần tới jq:

curl checkip.me/country          # Vietnam
curl checkip.me/1.1.1.1/isp      # Cloudflare, Inc.
curl checkip.me/1.1.1.1/hosting  # true
curl checkip.me/vpn              # false

curl checkip.me/help liệt kê toàn bộ các trường.

Script dynamic DNS nho nhỏ

Đây là lý do phổ biến nhất để lấy IP từ script. Script nhớ địa chỉ lần trước và chỉ làm gì đó khi IP thay đổi:

#!/usr/bin/env bash
set -euo pipefail

STATE_FILE="$HOME/.last_ip"
CURRENT=$(curl -fs -4 --max-time 10 checkip.me)
LAST=$(cat "$STATE_FILE" 2>/dev/null || true)

if [ "$CURRENT" != "$LAST" ]; then
  echo "IP changed: ${LAST:-none} -> $CURRENT"
  # call your DNS provider's API here
  echo "$CURRENT" > "$STATE_FILE"
fi

Cho chạy bằng cron vài phút một lần. -f khiến curl báo lỗi khi gặp HTTP error, thay vì lưu luôn thông báo lỗi làm “IP”; -s giữ cho thanh tiến trình không lọt vào biến; còn --max-time tránh để job bị treo khi mạng chậm.

Khoá SSH theo IP hiện tại bằng Terraform

Nếu bạn quản lý bastion host bằng Terraform, có thể truyền IP vào lúc apply:

variable "my_ip" {
  type = string
}

resource "aws_security_group_rule" "ssh_from_me" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["${var.my_ip}/32"]
  security_group_id = aws_security_group.bastion.id
}
terraform apply -var "my_ip=$(curl -s -4 checkip.me)"

Đừng bỏ -4. Nếu curl chọn IPv6, bạn sẽ có một địa chỉ IPv6 đi kèm /32, và Terraform sẽ từ chối vì CIDR block không hợp lệ.

Gọi từ code

CORS đã được bật, nên dùng được cả từ trình duyệt lẫn Node 18+. Đoạn JavaScript dùng top-level await, nên hãy chạy trong ES module hoặc console của trình duyệt.

import requests

ip = requests.get("https://checkip.me/ip", timeout=10).text.strip()
info = requests.get(f"https://checkip.me/{ip}", timeout=10).json()
print(ip, info["country"], info["isp"])
const info = await fetch("https://checkip.me/json").then(r => r.json());
console.log(info.ip, info.country, info.vpn);

Giới hạn

  • Tra IP của chính mình thì không giới hạn. Tra IP khác thì tối đa 120 lượt/giờ cho mỗi người dùng. Vượt quá sẽ nhận HTTP 429 kèm header Retry-After.
  • Kết quả tra các IP khác được cache, nên có thể cũ tới 30 ngày.
  • Vị trí lấy từ dữ liệu IP geolocation. Quốc gia thường đúng, còn thành phố thì nhiều khi chỉ gần đúng, dịch vụ nào kiểu này cũng vậy.
  • Các cờ VPN, proxy và Tor là gợi ý tốt, nhưng đừng dựa vào chúng cho những việc liên quan đến bảo mật quan trọng.

Tài liệu đầy đủ ở checkip.me/api.

And for those who want to know HOW MANY IPs (IPv4 and IPv6 – excluding 127.0.0.x) are assigned to their computer (it’s in JAVA)

  public ArrayList<String> ipList() {
    allIP = new ArrayList<>(32);
    try {
      if (System.getProperty("os.name").equals("Linux")) {
        Enumeration nic = NetworkInterface.getNetworkInterfaces();
        while(nic.hasMoreElements()) {
          Enumeration ipAdr = ((NetworkInterface) nic.nextElement()).getInetAddresses();
          while(ipAdr.hasMoreElements()) {
            String ip = ((InetAddress) ipAdr.nextElement()).getHostAddress();
            if (ip.startsWith("192.")) allIP.add(ip);
            else if (!ip.startsWith("127.")) {
              int p = ip.indexOf("%");
              if (p > 0 && ip.indexOf(":0:") < 0 && ip.indexOf("::") < 0) allIP.add(ip.substring(0, p));
            }
          }
          for (int i = 0; i < allIP.size(); ++i) if (allIP.get(i).indexOf(".") > 0) allIP.add(0, allIP.remove(i));
        }
      } else { // Windows
        InetAddress[] ias = InetAddress.getAllByName((InetAddress.getLocalHost()).getHostName());
        for (int i = 0; i < ias.length; ++i) {
          String ip = ias[i].getHostAddress();
          if (!ias[i].isLinkLocalAddress() && ip.indexOf(":0:") < 0 && 
              ip.indexOf("::") < 0 && ip.indexOf("127.") < 0) allIP.add(ip);
        }
      }
    } catch(Exception _e) { }    
    return allIP;
  }

The ipList could be as following:

192.168.0.116
2a02:8071:5301:24c0:b849:909a:9d03:58cf
2a02:8071:5301:24c0:49f5:681c:8347:699a
2a02:8071:5301:24c0:61cf:f40d:ccce:81bf

and all these IPs are valid.

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?