Pinpointing the geographic origin of an IP address is a fundamental skill for network administrators, security analysts, and anyone managing Linux infrastructure. In real terms, while no method can guarantee a physical street address—due to the nature of ISP routing, VPNs, and proxies—Kali Linux and standard Linux distributions offer a powerful toolkit to get remarkably close. This guide walks you through the most effective command-line utilities, scripting approaches, and investigative techniques to find the exact location of an IP address on Linux And that's really what it comes down to..
Understanding the Limits of IP Geolocation
Before diving into commands, it is crucial to manage expectations. IP geolocation relies on databases that map IP ranges (netblocks) to physical locations registered by Internet Service Providers (ISPs) or Regional Internet Registries (RIRs) like ARIN, RIPE NCC, or APNIC Simple, but easy to overlook. Practical, not theoretical..
Accuracy varies significantly:
- Country/Region Level: Extremely high accuracy (99%+).
- City Level: Generally accurate (80–90%), but often defaults to the ISP’s headend or a major routing hub rather than the user’s house.
- Coordinates (Lat/Long): Usually represent the centroid of the city or postal code area, not a specific building.
- Mobile/Corporate/VPN IPs: Often show the location of the corporate gateway or VPN exit node, which could be in a different country entirely.
Keep this in mind when interpreting results. You are locating the network infrastructure, not necessarily the human behind the keyboard Small thing, real impact..
Method 1: The Classic geoiplookup (MaxMind GeoIP)
This is the standard, lightweight tool pre-installed on Kali Linux and easily available on Debian/Ubuntu (sudo apt install geoip-bin) and RHEL/Fedora (sudo dnf install geoip) Simple, but easy to overlook. No workaround needed..
Basic Usage
Run the command followed by the target IP or domain:
geoiplookup 8.8.8.8
Output:
GeoIP Country Edition: US, United States
GeoIP City Edition, Rev 1: US, CA, Mountain View, 94043, 37.4056, -122.0775, 807, 650
GeoIP ASNum Edition: AS15169 Google LLC
This instantly gives you Country, City, Postal Code, Coordinates, and the Autonomous System Number (ASN)/Organization.
Updating the Database
Default package repositories often ship outdated databases. For precision, download the latest free GeoLite2 databases from MaxMind (requires a free account license key since 2019).
- Install
geoipupdate:sudo apt install geoipupdate - Configure
/etc/GeoIP.confwith yourAccountIDandLicenseKey. - Run
sudo geoipupdateto pull fresh.mmdbfiles into/usr/share/GeoIP/.
Using the Modern mmdb Format with mmdblookup
Newer MaxMind databases use the .mmdb format. The mmdblookup tool (part of libmaxminddb) parses these efficiently Easy to understand, harder to ignore..
mmdblookup --file /usr/share/GeoIP/GeoLite2-City.mmdb --ip 8.8.8.8
This outputs a structured JSON-like hierarchy, allowing you to extract specific fields like subdivisions, city, location, and traits (e.g., is_anonymous_proxy, is_satellite_provider).
Method 2: whois – The Registry Source
While geoiplookup uses aggregated commercial databases, whois queries the authoritative RIR databases directly. This is essential for verifying ownership and registration details, which often differ from the physical server location.
Standard Query
whois 8.8.8.8
Look for these critical fields in the output:
- NetRange / CIDR: The block size.
- OrgName / OrgId: The legal entity holding the IP.
- Address / City / State / Country: The administrative address (often corporate HQ).
- RegDate: When the block was allocated.
- NetType: Direct Allocation, Reassigned, etc.
Querying Specific RIRs
If the standard whois hits a referral loop or you want raw data from a specific registry:
whois -h whois.arin.net 8.8.8.8 # North America
whois -h whois.ripe.net 8.8.8.8 # Europe/Middle East
whois -h whois.apnic.net 8.8.8.8 # Asia Pacific
whois -h whois.lacnic.net 8.8.8.8 # Latin America
whois -h whois.afrinic.net 8.8.8.8 # Africa
Pro Tip: Use grep -i to filter noise:
whois 8.8.8.8 | grep -iE "country|city|address|orgname|netname|descr"
Method 3: nmap – The Swiss Army Knife
Kali Linux ships with nmap, which includes the Nmap Scripting Engine (NSE). The ip-geolocation-* scripts are incredibly powerful because they can query multiple free APIs simultaneously and correlate results Small thing, real impact..
Single Target Scan
nmap --script ip-geolocation-* 8.8.8.8
Sample Output:
PORT STATE SERVICE
80/tcp open http
| ip-geolocation-maxmind:
| Country: United States (US)
| City: Mountain View
| Latitude: 37.4056
| Longitude: -122.0775
|_ Accuracy Radius: 1000 km
| ip-geolocation-db-ip:
| Country: United States (US)
| City: Mountain View
|_ Latitude: 37.4056, Longitude: -122.0775
| ip-geolocation-ipinfo:
| Country: US
| City: Mountain View
|_ Location: 37.4056,-122.0775
Notice the Accuracy Radius field. This is the "confidence circle." A radius of 1000km means "somewhere in this region," whereas 10km implies high precision Nothing fancy..
Scanning a Network Range
To map the geography of an entire subnet (useful for internal asset inventory):
nmap -sn --script ip-geolocation-* 192.168.1.0/24
-sn disables port scanning (ping sweep only), making it fast and stealthy for LAN mapping.
Method 4: Leveraging Public APIs with curl and jq
For automation or when you need richer data (timezone, currency, connection type, threat intelligence), public REST APIs are superior. Most offer generous free tiers. jq
… is indispensable for turning the raw JSON payloads into something human‑readable (or feed‑friendly for scripts). Below are a few of the most‑used free‑tier services, the exact curl invocations you’ll need, and handy jq filters to pull out the fields that matter most.
4.1 ipinfo.io
Free tier: 50 000 requests/month, no API key required for basic look‑ups Worth keeping that in mind..
curl -s https://ipinfo.io/8.8.8.8/json | \
jq -r '{ip, hostname, city, region, country, loc, org, postal, timezone}'
Typical output
{
"ip": "8.8.8.8",
"hostname": "dns.google",
"city": "Mountain View",
"region": "California",
"country": "US",
"loc": "37.4056,-122.0775",
"org": "AS15169 Google LLC",
"postal": "94043",
"timezone": "America/Los_Angeles"
}
If you have a token (recommended for higher limits), just add it as a query string:
curl -s "https://ipinfo.io/8.8.8.8/json?token=$IPINFO_TOKEN" | …
4.2 ip-api.com
Free tier: 45 requests/minute, 150 000/month (no key). Returns a wealth of fields including ISP, mobile/proxy flags, and even currency That's the part that actually makes a difference..
curl -s http://ip-api.com/json/8.8.8.8?fields=status,message,query,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,mobile,proxy,hosting,currency
Sample jq extraction
curl -s "http://ip-api.com/json/8.8.8.8?fields=status,query,country,city,isp,org,mobile,proxy,hosting,currency" |
jq -r '
.status,
"IP: \(.query)",
"Loc: \(.city), \(.country) (\(.isp))",
"Org: \(.org)",
"Mobile: \(.mobile) | Proxy: \(.proxy) | Hosting: \(.hosting)",
"Currency: \(.currency)"
'
4.3 ipgeolocation.io
Free tier: 1 000 requests/day (requires an API key). Offers security‑related data such as threat level and connection type Worth keeping that in mind..
API_KEY="YOUR_IPGEO_KEY"
curl -s "https://api.ipgeolocation.io/ipgeo?apiKey=$API_KEY&ip=8.8.8.8" |
jq -r '
.ip,
.country_name,
.state_prov,
.city,
.zipcode,
.latitude,
.longitude,
.time_zone.name,
.connection.isp,
.connection.connection_type,
.security.is_proxy,
.security.is_tor_exit_node
'
4.4 Bulk processing with a simple Bash loop
When you need to enrich a list of IPs (e.g., from a log file), combine curl, jq, and a while loop. Adding a short sleep keeps you within rate limits.
INPUT="ips.txt" # one IP per line
OUTPUT="geo.csv"
echo "ip,country,city,org,lat,lon" > "$OUTPUT"
while IFS= read -r ip; do
# Skip empty lines or comments
[[ -z "$ip" || "$ip" =~ ^# ]] && continue
json=$(curl -s "https://ipinfo.io/$ip/json")
# If the service returns an error, log it and move on
if echo "$json" | jq -e '.bogon' >/dev/null 2>&1; then
echo "$ip,bogon,,," >> "$OUTPUT"
continue
fi
# Extract fields; use empty string if missing
country=$(echo "$json" | jq -r '.country // ""')
city=$(echo "$json" | jq -r '.city // ""')
org=$(echo "$json" | jq -r '.
```bash
loc=$(echo "$json" | jq -r '.loc // ""')
lat=$(echo "$loc" | cut -d',' -f1)
lon=$(echo "$loc" | cut -d',' -f2)
echo "$ip,$country,$city,$org,$lat,$lon" >> "$OUTPUT"
# Be a good citizen—respect the free tier limits
sleep 1
done < "$INPUT"
The resulting geo.csv can be imported directly into spreadsheets, SIEMs, or visualization tools like Grafana and Kibana for further correlation.
5. Putting It All Together: A Mini “Threat Intel” Dashboard
With the building blocks above you can craft a lightweight, terminal‑based dashboard that shows real‑time context for every suspicious IP hitting your firewall. The following one‑liner (wrapped for readability) tails an auth.log, pulls the offending IP, enriches it via **ipinfo.
tail -Fn0 /var/log/auth.log |
grep --line-buffered "Failed password" |
awk '{for(i=1;i<=NF;i++) if($i~/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $i}' |
sort -u |
while read -r ip; do
data=$(curl -s "https://ipinfo.io/$ip/json?token=$IPINFO_TOKEN")
country=$(echo "$data" | jq -r '.country // "?"')
city=$(echo "$data" | jq -r '.city // "?"')
org=$(echo "$data" | jq -r '.org // "?"')
bogon=$(echo "$data" | jq -r '.bogon // false')
if [[ "$bogon" == "true" ]]; then
printf "\e[31m[BOGON]\e[0m %s\n" "$ip"
else
printf "\e[33m[%s/%s]\e[0m %s (%s)\n" "$country" "$city" "$ip" "$org"
fi
done
- Red
[BOGON]lines highlight reserved/internal addresses that should never appear on the public internet—often a sign of mis‑configured logging or spoofing attempts. - Yellow lines give you immediate geographic and organizational context, letting you spot brute‑force campaigns originating from unexpected regions or hosting providers.
Pipe the output to less -R or redirect to a file for later analysis; the same pattern works with journalctl -f, ufw.log, or any log source that emits IP addresses.
6. Operational Tips & Gotchas
| Concern | Mitigation |
|---|---|
| Rate limits | Cache results locally (e.Also, , sqlite or redis) keyed by IP with a TTL of 24‑48 h. In practice, |
| IPv6 support | All three APIs handle IPv6 transparently; just ensure your regex/awk captures the longer format. Here's the thing — |
| **Fail‑open vs. | |
| Token rotation | Store API tokens in a secret manager (HashiCorp Vault, AWS Secrets Manager, pass, etc.Practically speaking, fail‑closed** |
| Privacy / GDPR | Avoid storing full IP + geo pairs longer than necessary; hash IPs if you only need aggregate stats. Still, g. ) rather than hard‑coding them. |
7. Conclusion
Command‑line IP geolocation is no longer a “nice‑to‑have” curiosity—it’s a practical, zero‑dependency addition to any security‑focused workflow. By chaining curl, jq, and a few lines of Bash (or Python, Go, etc.), you gain immediate situational awareness: where an attacker appears to be, who owns the network, and whether the address is a known proxy, Tor exit, or hosting provider Simple, but easy to overlook..
The examples above demonstrate three complementary free tiers—ipinfo.io, ip-api.com, and ipgeolocation.io—each with distinct strengths. But pick one (or rotate among them) based on the data fields you need and the volume you expect. Wrap the calls in a small cache layer, respect the published rate limits, and you’ll have a production‑grade enrichment pipeline that runs entirely from the terminal, integrates with log shippers, and feeds dashboards without a single heavyweight agent.
Next time your IDS fires an alert, you’ll be able to answer “Who’s knocking?” in milliseconds—right from the same shell where you investigate the rest of the incident. Happy hunting!
8. Building a Reusable Enrichment Script
While one‑liners are great for ad‑hoc lookups, real investigations benefit from a structured script you can drop into your toolkit. Below is a minimal Bash wrapper that ties everything together—reading targets from a file, caching results, and producing both human‑readable and CSV output Easy to understand, harder to ignore..
#!/usr/bin/env bash
# enrich_ips.sh — batch IP enrichment with caching
# Usage: ./enrich_ips.sh targets.txt [csv|json|table]
set -euo pipefail
INPUT="${1:-targets.txt}"
FORMAT="${2:-table}"
CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/ip_enrich"
mkdir -p "$CACHE_DIR"
API_TOKEN="your_ipapi_co_token_here"
lookup_ip() {
local ip="$1"
local cache_file="$CACHE_DIR/$(echo -n "$ip" | sha256sum | cut -d' ' -f1).json"
if [[ -f "$cache_file" ]]; then
local age=$(( ( $(date +%s) - $(stat -c %Y "$cache_file") ) / 3600 ))
if [[ "$age" -lt 24 ]]; then
cat "$cache_file"
return
fi
fi
local response
response=$(curl -s --max-time 10 \
"https://api.ipapi.co/$ip/json/?
export -f lookup_ip
export CACHE_DIR API_TOKEN
process_ip() {
local ip="$1"
local data
data=$(lookup_ip "$ip")
local country city org asn
country=$(echo "$data" | jq -r '.city // "N/A"')
org =$(echo "$data" | jq -r '.And country // "N/A"')
city =$(echo "$data" | jq -r '. org // "N/A"')
asn =$(echo "$data" | jq -r '.
case "$FORMAT" in
csv) echo "$ip,$city,$country,$org,$asn" ;;
json) echo "$data" | jq -c '.' ;;
table) printf "%-16s %-20s %-6s %s\n" "$ip" "$city, $country" "$asn" "$org" ;;
esac
}
export -f process_ip
# Header for table / csv modes
if [[ "$FORMAT" == "csv" ]]; then
echo "IP,City,Country,Org,ASN"
elif [[ "$FORMAT" == "table" ]]; then
printf "%-16s %-25s %-6s %s\n" "IP" "Location" "ASN" "Organization"
echo "──────────────────────────────────────────────────────────────"
fi
# Main loop — supports parallel processing with xargs
cat "$INPUT" | xargs -P 4 -I {} bash -c 'process_ip "$@"' _ {}
Key design choices here:
- Parallel lookups via
xargs -P 4keep the script fast even with thousands of targets, while staying well within per‑minute rate limits (just throttle-Pdown if needed). - SHA‑256 keyed cache avoids filename collisions and makes cache invalidation trivial—just delete files older than your TTL.
- Multiple output formats let you feed results directly into
jqpipelines, spreadsheets, or SIEM ingest endpoints without post‑processing.
9. Feeding Results into a SIEM or Dashboard
Once you have enrichment data flowing in a consistent format, the natural next step is to pipe it into your monitoring stack Nothing fancy..