README du dépôt
Internet Atlas
Carte 3D interactive de l'infrastructure internet mondiale
Visualisez en temps réel les serveurs DNS, datacenters, points d'échange (IXP), câbles sous-marins, CDN et routes réseau de la planète sur un globe 3D.
</div>Sommaire
- Aperçu
- Fonctionnalités
- Architecture
- Arborescence du dépôt
- Stack technique
- Démarrage rapide
- Configuration
- API — Endpoints
- Modèles de données
- Génération des datasets
- Sécurité
- Déploiement en production
- Performances
- Roadmap
- Contribuer
- Crédits & licence
Aperçu
Internet Atlas est une plateforme web open-source qui rend visible l'invisible : la structure physique et logique d'internet. Le projet agrège plusieurs milliers de nœuds (DNS racines, résolveurs publics, CDN, clouds, IXP, datacenters, points d'atterrissage de câbles sous-marins…) et les projette sur un globe 3D interactif.
L'utilisateur peut :
- naviguer librement sur le globe (zoom, rotation, focus régional)
- filtrer par type d'infrastructure, opérateur, pays
- effectuer un lookup live d'un domaine ou d'une IP publique (géolocalisation, ASN, fournisseur, ville)
- visualiser les câbles sous-marins majeurs et leurs capacités
Démo en ligne : https://atlas.informaclique.fr
Fonctionnalités
- Globe 3D temps réel propulsé par
three.js+@react-three/fiber - 5 000+ nœuds géolocalisés à travers les 6 continents
- Câbles sous-marins majeurs (MAREA, Dunant, Curie, SEA-ME-WE, …)
- Lookup DNS/IP : résolution + GeoIP + ASN/ISP en moins d'une seconde
- Cache TTL local pour les lookups (30 min) afin de protéger les API publiques externes
- Rate limiting par IP cliente sur les endpoints sensibles
- Multi-source GeoIP : MaxMind GeoLite2 → ip-api → ipinfo (fallback automatique)
- API REST documentée (OpenAPI / Swagger UI sur
/docs) - SEO complet : meta OpenGraph, Twitter Card, JSON-LD, sitemap, robots
- Responsive & PWA-ready
Architecture
┌──────────────────────────────┐ HTTPS ┌──────────────────────────────┐
│ Navigateur (SPA React 3D) │ ───────────────────► │ Reverse proxy (Nginx) │
│ three.js / @react-three/fiber│ │ TLS + cache statique │
└──────────────────────────────┘ └──────────────┬───────────────┘
│
┌───────────────────────────────────────┴──────────┐
│ │
▼ ▼
Fichiers statiques (frontend/) API FastAPI (backend/)
┌───────────────────────┐ ┌──────────────────────────────┐
│ index.html + bundle JS│ │ Uvicorn + RateLimit MW │
│ assets/ + favicon … │ │ /api/nodes /api/cables │
└───────────────────────┘ │ /api/lookup /api/health │
└────────────┬─────────────────┘
│
┌─────────────────────────────┼──────────────────────────────┐
▼ ▼ ▼
GeoLite2-City (mmdb) ip-api.com / ipinfo.io dnspython
(local, optionnel) (fallback, HTTPS) (résolveur A)
Le frontend est livré sous forme de bundle statique (Vite build) servi
directement par Nginx. Le backend Python est un microservice
indépendant, derrière le même reverse proxy, qui ne sert que les routes
sous /api/.
Arborescence du dépôt
altas-web/
├── README.md ← ce fichier
├── LICENSE ← MIT
├── .gitignore
├── .env.example ← variables d'env documentées
│
├── frontend/ ← SPA buildée (servie en statique)
│ ├── index.html
│ ├── favicon.svg
│ ├── robots.txt
│ ├── sitemap.xml
│ └── assets/
│ ├── index-*.js ← bundle principal
│ ├── index-*.css
│ ├── three-*.js ← three.js (chunk dédié)
│ ├── r3f-*.js ← @react-three/fiber
│ └── vendor-*.js ← React + libs
│
└── backend/ ← API FastAPI
├── requirements.txt
├── app/
│ ├── main.py ← bootstrap FastAPI + CORS + middleware
│ ├── models.py ← schémas Pydantic
│ ├── routes/
│ │ ├── nodes.py ← /api/nodes, /api/nodes/stats, /api/nodes/cables
│ │ └── lookup.py ← /api/lookup (POST)
│ └── services/
│ ├── security.py ← validation IP/domaine + RateLimit middleware
│ ├── cache.py ← cache TTL persistant JSON
│ ├── dns.py ← résolveur DNS (dnspython)
│ ├── geoip.py ← MaxMind + fallback ip-api / ipinfo
│ └── asn.py ← lookup ASN/ISP
├── data/ ← datasets statiques (JSON)
│ ├── datacenters.json
│ ├── global_nodes.json ← ≈ 5 000 nœuds
│ ├── known_ips.json ← DNS / IXP / racines connus
│ ├── submarine_cables.json
│ └── submarine_cables_global.json
└── scripts/
└── generate_datasets.py ← régénération déterministe des nœuds & câbles
Stack technique
| Couche | Technologies |
|---|---|
| Frontend | React 18, Vite, three.js, @react-three/fiber, @react-three/drei, TailwindCSS |
| 3D / GPU | three.js (WebGL2), shaders custom (atmosphère, glow, arcs animés) |
| Backend | Python 3.10+, FastAPI 0.115, Uvicorn, Pydantic v2 |
| Données | MaxMind GeoLite2-City (mmdb), ip-api.com, ipinfo.io, dnspython |
| Infra | Nginx (reverse proxy + TLS Let's Encrypt), systemd, Ubuntu 22.04 LTS |
| Sécurité | Rate limiting par IP, validation stricte (regex domaines + ipaddress), CORS, cache TTL |
Démarrage rapide
Pré-requis
- Python 3.10+
- (optionnel) Node.js 20+ si vous souhaitez rebuilder le frontend
- (optionnel) Base MaxMind GeoLite2-City.mmdb — téléchargement gratuit sur https://www.maxmind.com après création de compte
Backend (API FastAPI)
# 1. Cloner le dépôt
git clone https://github.com/CedricPoint/altas-web.git
cd altas-web/backend
# 2. Créer un environnement virtuel
python3 -m venv .venv
source .venv/bin/activate
# 3. Installer les dépendances
pip install --upgrade pip
pip install -r requirements.txt
# 4. (Optionnel) Placer la base GeoLite2
# data/GeoLite2-City.mmdb ← non versionnée (licence MaxMind)
# 5. Lancer l'API en mode dev
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
L'API est alors disponible sur :
- http://127.0.0.1:8000/ → status JSON
- http://127.0.0.1:8000/docs → Swagger UI
- http://127.0.0.1:8000/redoc → ReDoc
- http://127.0.0.1:8000/api/health → healthcheck
Frontend (SPA statique)
Le dépôt contient uniquement le bundle de production dans frontend/.
Pour le servir en local :
cd altas-web/frontend
python3 -m http.server 5173
# → http://127.0.0.1:5173
En développement, configurez votre serveur pour proxifier les requêtes
/api/*vershttp://127.0.0.1:8000(voir Déploiement).
Configuration
Toutes les variables sont documentées dans .env.example.
Copiez-le en .env puis adaptez les valeurs.
| Variable | Défaut | Description |
|---|---|---|
GEOLITE2_DB | backend/data/GeoLite2-City.mmdb | Chemin du fichier MaxMind (optionnel — fallback API publique) |
CORS_ALLOW_ORIGINS | * | Origines CORS autorisées (à restreindre en prod) |
RATE_LIMIT_MAX_REQUESTS | 45 | Requêtes max par fenêtre sur /api/lookup |
RATE_LIMIT_WINDOW_SECONDS | 60 | Fenêtre glissante du rate limit (secondes) |
ATLAS_HOST | 127.0.0.1 | Bind Uvicorn |
ATLAS_PORT | 8000 | Port Uvicorn |
API — Endpoints
Base URL : https://atlas.informaclique.fr/api
GET /api/health
Healthcheck simple.
{ "status": "ok", "service": "internet-atlas-api" }
GET /api/nodes?scale={small|medium|large}
Retourne l'ensemble des nœuds et connexions. Le paramètre scale permet
d'adapter le volume aux appareils moins puissants :
scale | Nœuds renvoyés |
|---|---|
small | ≈ 900 |
medium | ≈ 2 200 |
large | tous (≈ 5 000) |
Exemple :
curl https://atlas.informaclique.fr/api/nodes?scale=medium | jq '.nodes | length'
GET /api/nodes/stats
Statistiques agrégées (nœuds, connexions, pays couverts).
GET /api/nodes/cables
Câbles sous-marins, segments inter-stations et points d'atterrissage.
POST /api/lookup
Résout un domaine ou une IP publique → géolocalisation + ASN.
curl -X POST https://atlas.informaclique.fr/api/lookup \
-H 'Content-Type: application/json' \
-d '{"query":"cloudflare.com"}'
Réponse :
{
"query": "cloudflare.com",
"ip": "104.16.132.229",
"domain": "cloudflare.com",
"country": "United States",
"city": "San Francisco",
"latitude": 37.7621,
"longitude": -122.3971,
"provider": "Cloudflare, Inc.",
"asn": "AS13335"
}
Limites :
- rate limit : 45 req / 60 s par IP cliente
- IP privées/réservées rejetées (RFC 1918, loopback, multicast, …)
- format strict : regex domaine + validation
ipaddress
Modèles de données
class Node(BaseModel):
name: str
ip: str | None
lat: float
lng: float
type: str # DNS, CDN, Cloud, IXP, Hosting, Root DNS, Cable Landing…
provider: str # Google, Cloudflare, AWS, OVHcloud, DE-CIX, …
country: str
city: str | None
asn: str | None
ports: list[int]
class LookupRequest(BaseModel):
query: str = Field(min_length=1, max_length=253)
class LookupResponse(BaseModel):
query: str
ip: str
domain: str | None
country: str | None
city: str | None
latitude: float | None
longitude: float | None
provider: str | None
asn: str | None
Génération des datasets
Le script backend/scripts/generate_datasets.py régénère, de manière
déterministe (random.seed(42)), les fichiers :
data/global_nodes.json— 5 000 nœudsdata/submarine_cables_global.json— câbles principaux
cd backend
source .venv/bin/activate
python scripts/generate_datasets.py
Pour ajouter une nouvelle infrastructure (nouveau CDN, nouveau pays, etc.),
éditez la liste _EXTRA_INFRA dans app/routes/nodes.py ou le tableau
cities/providers dans scripts/generate_datasets.py.
Sécurité
La sécurité a été intégrée par défaut, rien n'est confié au client.
| Mesure | Implémentation |
|---|---|
| Validation entrée | regex domaine RFC 1035 + ipaddress.ip_address() |
| Rejet IP privée | is_public_ip() ⇒ private/loopback/multicast/reserved exclus |
| Rate limiting | RateLimitMiddleware (sliding window par IP) |
| Cache anti-abus | TTL 30 min sur les lookups ⇒ moins de requêtes externes |
| Timeouts DNS | 3 s lifetime / 2 s timeout (pas de SSRF DNS-rebinding lent) |
| CORS | configurable, restreint en prod au domaine du site |
| Secrets | aucun secret committé — .env ignoré, .env.example documenté |
| GeoLite2 | base non versionnée (licence MaxMind) — fallback HTTPS si absente |
| Logs | uvicorn + accès Nginx ; ajoutez journald/fail2ban en prod |
Audit recommandé : exécutez
pip-audit -r backend/requirements.txtetbandit -r backend/apprégulièrement.
Déploiement en production
1. Service systemd (/etc/systemd/system/atlas-api.service)
[Unit]
Description=Internet Atlas API
After=network.target
[Service]
User=informaclique
Group=informaclique
WorkingDirectory=/srv/atlas/backend
Environment="PATH=/srv/atlas/backend/.venv/bin"
EnvironmentFile=/srv/atlas/.env
ExecStart=/srv/atlas/backend/.venv/bin/uvicorn app.main:app \
--host 127.0.0.1 --port 8000 --workers 2
Restart=always
RestartSec=5
# Hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/srv/atlas/backend/data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now atlas-api
2. Reverse proxy Nginx
server {
listen 443 ssl http2;
server_name atlas.informaclique.fr;
ssl_certificate /etc/letsencrypt/live/atlas.informaclique.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/atlas.informaclique.fr/privkey.pem;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
root /srv/atlas/frontend;
index index.html;
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 30s;
}
location / {
try_files $uri $uri/ /index.html;
}
}
3. TLS
sudo certbot --nginx -d atlas.informaclique.fr
Performances
- Bundle frontend ≈ 2,3 Mo (gzip ~700 Ko) — three.js et r3f sont splittés en chunks indépendants pour un cache long durée.
- API
/api/nodes≈ 60 ms (cached) en modelargesur 5 000 nœuds. - Lookup live ≈ 200 ms à 1 s (chemin mmdb local ⇒ rapide ; fallback API publique ⇒ plus lent).
- Mémoire backend ≈ 90 Mo par worker uvicorn.
Roadmap
- Sources sources React (Vite) à committer dans
frontend/src/ - Tests automatisés (
pytest,httpx) + CI GitHub Actions - Mode "traceroute live" depuis le navigateur du visiteur
- Filtres avancés par AS / pays / type combiné
- Couche BGP (peering routes) via RIPE RIS
- Internationalisation EN / ES
- PWA installable avec dataset offline
Contribuer
Les contributions sont les bienvenues ! Pour proposer une modification :
- Fork le dépôt
- Créez une branche :
git checkout -b feat/ma-fonctionnalite - Validez côté serveur, n'introduisez aucun secret, suivez la
structure clean architecture (
routes/→services/) - Lancez
ruff checketbandit -r backend/appavant de pousser - Ouvrez une Pull Request détaillée
Crédits & licence
- Auteur : Cédric Frank — Informaclique
- GitHub : @CedricPoint
- GeoIP : © MaxMind, Inc. — GeoLite2 EULA
- Fallback GeoIP : https://ip-api.com & https://ipinfo.io
- 3D : three.js (MIT) / react-three-fiber (MIT)
Distribué sous licence MIT — voir LICENSE.
<div align="center">
Fait avec passion à Paris · https://informaclique.fr
</div>