Quand on déploie sa première application Django en production, il y a toujours ce moment un peu inconfortable : laisser /admin exposé sur Internet. Même avec un mot de passe solide, même avec un compte superuser bien protégé, une interface d’administration accessible publiquement reste une surface d’attaque évidente. Je suis étudiant en informatique et je monte progressivement mon propre homelab. Ce type de projet me passionne, autant côté hardware que software. Très vite, en exposant mes premiers services, je me suis rendu compte qu’un simple login/mot de passe, aussi robuste soit-il, ne suffisait pas à me rassurer. Une page d’administration ne devrait pas être visible du monde entier par défaut. C’est de cette réflexion qu’est née l’architecture que je présente ici : simple, efficace, et adaptée à un contexte auto-hébergé.

Le problème : Django Admin exposé sur Internet

Par défaut, Django expose l’interface d’administration via /admin. Les bonnes pratiques classiques consistent à :

- changer l’URL
- imposer un mot de passe fort
- activer la double authentification
- limiter les IP autorisées
- désactiver certaines fonctionnalités inutiles

Ces recommandations (quoi que courtes et basiques) sont détaillées dans un article de opensource.com publié il y a plusieurs années, mais dont les conseils restent pertinents aujourd’hui. Cependant, toutes ces mesures restent applicatives. L’interface reste accessible publiquement. Un attaquant peut la scanner, détecter qu’il s’agit d’un Django, tenter du brute force, analyser les headers, etc.

L’idée est donc de passer à un niveau supérieur : ne plus exposer l’interface d’administration sur l’Internet public.

L’architecture proposée

Le principe repose sur trois briques :

  1. Séparer le site public et l’admin via des sous-domaines distincts

  2. Utiliser un VPN pour accéder à l’admin

  3. Exploiter django-hosts pour router proprement les requêtes

L’objectif est simple :
site.fr est accessible publiquement
admin.site.fr n’est accessible que via une IP privée, donc uniquement si le VPN est activé

Configuration DNS

On ajoute deux enregistrements DNS :

site.fr → IP publique du serveur
admin.site.fr → IP privée (par exemple 10.0.0.5)

Ainsi, pour accéder à admin.site.fr, il faut être dans le réseau privé. Typiquement, via un VPN comme WireGuard ou OpenVPN.

Cela signifie que :

- L’interface d’administration n’est plus accessible depuis Internet
- Elle ne répond que sur le réseau interne
- Même si quelqu’un connaît l’URL exacte, il ne peut pas l’atteindre sans accès réseau

Oui, l’IP privée reste visible via le DNS. Ce n’est pas idéal, mais dans la pratique, elle n’est pas routable depuis Internet. Un attaquant peut faire du repérage, mais pas établir de connexion directe.

Utilisation de django-hosts

Pour gérer proprement la séparation entre site.fr et admin.site.fr, on utilise la librairie django-hosts. Elle permet d’associer des sous-domaines à des schémas d’URL différents, via des modules appelés hostconfs.

Exemple de configuration hosts.py :

from django.conf import settings
from django_hosts import host, patterns

host_patterns = patterns(
    "",
    host(r'admin', 'projet.urls_admin', name='admin'),
    host(r"www", settings.ROOT_URLCONF, name="www"),
)

Ici, deux hôtes sont définis :

admin → redirige vers projet.urls_admin
www → redirige vers les URL principales du site

On crée ensuite un fichier urls_admin.py dédié :

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("", admin.site.urls),
]

Cette séparation apporte plusieurs avantages :

- L’admin n’est plus mélangé aux routes publiques
- On peut appliquer des middlewares spécifiques à l’admin
- On peut durcir la configuration indépendamment du site principal

On obtient ainsi une séparation logique et réseau.

Certificat TLS pour une IP privée

Un point souvent négligé : même sur un réseau privé, il faut du HTTPS.

Pour obtenir un certificat valide pour admin.site.fr, on peut utiliser un challenge DNS (par exemple via Let’s Encrypt). Le challenge DNS ne nécessite pas que le serveur soit publiquement accessible, ce qui est parfait dans ce cas.

Résultat :

- HTTPS actif
- Certificat valide
- Aucun warning navigateur
- Interface sécurisée même sur Wi-Fi public via VPN

Renforcer encore la sécurité

Cette architecture constitue une base solide, mais elle peut être complétée :

- Activer la 2FA pour les comptes admin
- Restreindre l’admin à certaines IP internes
- Ajouter un reverse proxy avec authentification supplémentaire
- Mettre en place fail2ban
- Journaliser les tentatives d’accès

L’idée n’est pas d’empiler les couches inutilement, mais d’adopter une défense en profondeur. Ce qui me plaît dans cette architecture, c’est sa simplicité. On ne modifie pas Django en long, en large et en travers. On ne développe pas de solution exotique. On exploite :

- la séparation DNS
- le cloisonnement réseau
- la modularité offerte par django-hosts

On déplace le problème du niveau applicatif vers le niveau réseau, ce qui est généralement plus robuste. Dans un contexte homelab ou petite infrastructure auto-hébergée, cette approche est particulièrement adaptée. Elle oblige à réfléchir en termes d’architecture globale plutôt que de simple configuration logicielle. Et surtout, elle rappelle une règle fondamentale : la meilleure surface d’attaque est celle qui n’est pas exposée.