# Accès à distance

Par défaut, SkyView n'est accessible que sur le réseau local — une fois que vous quittez votre domicile, l'application mobile ne peut plus joindre votre serveur. Ce chapitre présente trois façons d'exposer SkyView sur Internet de manière sécurisée : **VPN maillé (recommandé)**, reverse proxy, et redirection de ports, et explique les ports nécessaires pour chacune.

> **🛑 À lire en premier**
>
> SkyView écoute par défaut sur `0.0.0.0` sans aucune règle de pare-feu intégrée. Exposer des ports nus sur Internet revient à laisser n'importe quel scanner de la planète atteindre vos caméras. Tout schéma d'accès à distance doit être associé à **HTTPS + un mot de passe fort**, et éviter autant que possible une exposition publique nue.

## Choisir parmi les trois méthodes

| Méthode | Sécurité | Difficulté | Exposition publique | Idéal pour |
| --- | --- | --- | --- | --- |
| **VPN maillé** | La plus élevée | Faible | Non | La grande majorité des utilisateurs domestiques (**recommandé**) |
| Reverse proxy + domaine | Moyenne | Moyenne | Oui (443 uniquement) | Vous voulez un nom de domaine, ou envoyer un lien à des proches qui ne peuvent pas facilement installer l'application |
| Redirection de ports | Faible | Faible | Oui (exposition nue) | Non recommandé ; uniquement temporaire / si vous comprenez parfaitement le risque |

En cas de doute, utilisez le **VPN maillé** : n'ouvrez aucun port sur le routeur, n'achetez aucun nom de domaine, ne configurez aucun certificat — sécurité maximale et administration quasi nulle. Les trois sections ci-dessous détaillent chaque méthode.

## Méthode 1 : VPN maillé (recommandé)

Les outils de maillage (Tailscale / WireGuard / ZeroTier, etc.) créent un tunnel chiffré entre votre téléphone et votre serveur domestique, de sorte que le téléphone accède à SkyView via l'adresse interne exactement comme à la maison — **aucun port à ouvrir sur le routeur, et le serveur n'est absolument pas exposé sur Internet**.

1. **Installer le client de maillage sur le serveur**

   Installez Tailscale sur la machine exécutant SkyView (ou un routeur logiciel sur le même réseau local), puis exécutez `tailscale up` pour vous connecter. WireGuard / ZeroTier fonctionnent de façon similaire.

   ```bash
   curl -fsSL https://tailscale.com/install.sh | sh
   sudo tailscale up
   ```

2. **Installer la même application sur votre téléphone et vous connecter au même compte**

   Installez l'application Tailscale / ZeroTier correspondante sur le téléphone et connectez-vous avec le même compte que le serveur ; les deux extrémités se trouvent désormais sur le même réseau local virtuel.

3. **Saisir l'adresse de maillage dans l'application**

   Définissez l'adresse du serveur dans l'application SkyView sur l'IP attribuée par le maillage — Tailscale utilise `100.x.x.x`, ZeroTier utilise `10.x.x.x` — le port reste `:23406`, par ex. `http://100.x.x.x:23406`.

4. **Terminé**

   Une fois à l'extérieur, le téléphone peut y accéder même en 4G/5G, exactement comme à la maison. Aucune IP publique, aucun nom de domaine, aucun certificat HTTPS n'est nécessaire.

> **💡 Pourquoi le VPN maillé est la recommandation numéro un**
>
> Ne pas ouvrir de ports sur le routeur = surface d'attaque nulle depuis Internet ; les scanners ne peuvent tout simplement pas vous trouver. Cela ne dépend pas non plus d'une IP publique — de nombreuses connexions haut débit sont en CGNAT sans aucune IP publique, et le maillage traverse quand même. Un avantage facilement négligé : **sur un maillage, le téléphone est traité comme étant sur le réseau local, donc la vue en direct utilise par défaut le canal WebRTC à faible latence (latence inférieure à la seconde, démarrage instantané), alors que le reverse proxy / la redirection de ports ne peuvent utiliser que HLS (~3 secondes de latence)** — le maillage n'est donc pas seulement le plus sûr, il est aussi le plus réactif pour la vue en direct. Le seul coût est d'installer l'application de maillage une fois sur chaque appareil que vous voulez utiliser.

## Méthode 2 : reverse proxy + nom de domaine

Si vous disposez d'une IP publique + d'un nom de domaine et souhaitez y accéder via une adresse du type `https://cam.example.com` (pratique pour l'envoyer à des proches qui ne peuvent pas facilement installer l'application de maillage), vous pouvez placer une couche nginx / Caddy devant SkyView pour la terminaison HTTPS. Le conteneur SkyView inclut déjà une couche nginx en interne, le proxy externe n'a donc qu'à rediriger tout le trafic vers `:23406`.

> **ℹ️ Un seul port à rediriger**
>
> Le conteneur regroupe le Web Admin, l'API et les flux en direct (HLS + signalisation WebRTC) sur le seul port `:23406`. **Le proxy externe ne redirige que ce seul port** — n'exposez pas séparément les ports `24214` / `24215` de mediamtx. La vidéo WebRTC en direct utilise l'UDP `:23515`, qui ne passe pas par le proxy, mais sur Internet ce chemin UDP ne peut généralement pas être établi — la vue en direct sur Internet bascule automatiquement sur HLS, donc ne pas rediriger `23515` ne pose pas de problème ; voir la remarque à la fin de la « Méthode 2 » ci-dessous.

### Configuration nginx

Enregistrez le contenu ci-dessous **intégralement** sous `/etc/nginx/sites-available/skyview.conf`, remplacez `<your-domain>`, puis créez un lien symbolique (`ln -s`) vers `sites-enabled/` — un seul fichier suffit, aucun extrait séparé n'est nécessaire. Les blocs `proxy_set_header` des deux `location /` sont identiques : le `proxy_set_header` de nginx écrase au lieu d'hériter, donc chaque `location` doit porter sa propre copie — copiez-le simplement tel quel.

```nginx
# Transmission de l'en-tête WebSocket Upgrade — doit être dans le contexte http {}
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name <your-domain>;
    # certbot --nginx réécrira ce bloc en redirection 301 vers https
    location / {
        proxy_pass http://127.0.0.1:23406;
        proxy_http_version 1.1;
        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-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;        # ★ Si omis, les déploiements HTTPS tombent dans une boucle de connexion
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_buffering       off;
        proxy_connect_timeout 60s;
        proxy_send_timeout    1d;
        proxy_read_timeout    1d;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name <your-domain>;

    ssl_certificate     /etc/letsencrypt/live/<your-domain>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<your-domain>/privkey.pem;

    # Les exports d'enregistrements et les imports groupés de la bibliothèque de visages peuvent faire plusieurs centaines de Mo ; les cookies JWT longs sont volumineux, évite les erreurs 414
    client_max_body_size        200m;
    client_header_buffer_size   4k;
    large_client_header_buffers 8 16k;

    location / {
        proxy_pass http://127.0.0.1:23406;
        proxy_http_version 1.1;
        # ↓ Identique à la location :80 ci-dessus, copiez ligne par ligne (proxy_set_header n'hérite pas)
        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-Host  $host;
        proxy_set_header X-Forwarded-Port  $server_port;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_buffering       off;
        proxy_connect_timeout 60s;
        proxy_send_timeout    1d;
        proxy_read_timeout    1d;
    }
}
```

*/etc/nginx/sites-available/skyview.conf*

> **⚠️ X-Forwarded-Proto / X-Forwarded-Host doivent être transmis et inclus dans chaque location**
>
> Le nginx du conteneur écoute uniquement en HTTP ; il s'appuie sur `X-Forwarded-Proto` pour connaître le schéma réel du client et sur `X-Forwarded-Host` pour connaître le domaine externe. Omettre `X-Forwarded-Proto` → sur les déploiements HTTPS, vous êtes renvoyé à la page de connexion juste après vous être connecté, et la vue en direct est bloquée par le navigateur en tant que contenu mixte (Mixed Content) ; omettre `X-Forwarded-Host` → les liens de lecture / téléchargement / export d'enregistrements sont construits avec de mauvaises adresses et le client ne peut pas les ouvrir. Le modèle nginx ci-dessus et Caddy (qui transmet ces deux en-têtes par défaut) couvrent tous les deux ce cas — il suffit de le copier. Vous n'avez pas besoin de définir `X-Forwarded-Port` manuellement ; le nginx du conteneur gère automatiquement les scénarios « reverse proxy externe / accès direct par IP nue ». De plus, le `proxy_set_header` de nginx **écrase au lieu d'ajouter** : si une `location` en écrit ne serait-ce qu'un seul, tous les set_header du niveau supérieur cessent de s'appliquer — chaque `location` doit donc `include` l'extrait complet.

Obtenez un certificat (la machine doit être joignable depuis Internet sur le port `:80`) :

```bash
certbot --nginx -d <your-domain> -m <your-email> --agree-tos --no-eff-email --redirect
```

### Configuration Caddy (plus simple)

Caddy émet / renouvelle automatiquement les certificats Let's Encrypt sans certbot, et la configuration est bien plus courte. `/etc/caddy/Caddyfile` :

```caddyfile
<your-domain> {
    reverse_proxy 127.0.0.1:23406 {
        # Caddy transmet X-Forwarded-{For,Proto,Host} par défaut
        # Pour les connexions longue durée (WS de conversation / SSE d'événements / flux en direct), augmentez flush + les délais d'expiration
        flush_interval -1
        transport http {
            read_timeout  24h
            write_timeout 24h
        }
    }
    request_body {
        max_size 200MB
    }
}
```

*/etc/caddy/Caddyfile*

> **ℹ️ La vue en direct sur Internet utilise HLS — ne vous souciez pas de WebRTC**
>
> Avec le schéma de reverse proxy, le routeur n'a besoin de rediriger que le port public `443/tcp` vers le `443` de la machine proxy. **`23515/udp` n'a pas besoin d'être redirigé** : le direct WebRTC à faible latence repose sur le fait que mediamtx annonce des adresses de candidats ICE « joignables », mais SkyView n'annonce par défaut que des adresses du réseau local (`webrtcAdditionalHosts` et le STUN nécessaires à la traversée NAT publique ne sont pas configurés), donc sous un NAT domestique, le client externe ne peut pas obtenir de canal WebRTC utilisable, et rediriger `23515` ne sert à rien. La vue en direct sur Internet se dégrade automatiquement vers HLS (~3 secondes de latence, pleinement fonctionnel) ; pour la faible latence à démarrage instantané de WebRTC, utilisez la **Méthode 1, VPN maillé** — sur un maillage, le téléphone est traité comme étant sur le réseau local et WebRTC fonctionne normalement.

## Méthode 3 : redirection de ports (non recommandée)

> **🛑 Comprendre le risque avant utilisation**
>
> La redirection de ports fait correspondre directement le port du serveur à Internet, où n'importe qui peut y accéder. Rediriger `23406` nu, c'est du **HTTP en clair** — les mots de passe et le flux ne sont pas chiffrés de bout en bout, et les navigateurs ainsi qu'Android 9+ refusent les requêtes HTTP en clair pour les permissions caméra. Ne l'envisagez que si vous comprenez parfaitement le risque et que c'est temporaire ; pour un usage à long terme, passez au VPN maillé ou à un reverse proxy.

Si vous tenez absolument à la redirection de ports, nous **recommandons vivement d'exécuter aussi une couche de reverse proxy sur l'hôte SkyView pour le HTTPS** (voir Méthode 2), en redirigeant le port public `443` vers le proxy plutôt que d'exposer `23406` nu. Les ports à rediriger :

| Port public | → interne | Protocole | Remarques |
| --- | --- | --- | --- |
| 443 | Hôte proxy 443 | TCP | Faites le HTTPS via le proxy, puis redirigez vers `23406` (approche recommandée) |
| 23406 | Hôte SkyView 23406 | TCP | Le port redirigé nu sans proxy, **HTTP en clair, non recommandé** |

> **⚠️ Ne jamais rediriger 24214 / 24215**
>
> Les flux en direct passent déjà par le proxy sur `23406` avec un jeton live-grant. Exposer nus sur Internet les ports `24214` (HLS) / `24215` (signalisation WebRTC) de mediamtx contourne l'authentification par jeton et constitue une faille de sécurité. Le fichier `mediamtx.yml` du conteneur restreint déjà ces deux ports à `127.0.0.1` uniquement.

## Aide-mémoire des ports

| Port | Protocole | Usage | Ouvert sur Internet ? |
| --- | --- | --- | --- |
| 23406 | TCP | Web Admin + API + proxy à jeton pour le flux en direct (seul point d'entrée HTTP) | **Requis** (avec un proxy, vous redirigez le 443) |
| 23515 | UDP | Flux média WebRTC de la vue en direct (fonctionne sur le réseau local / maillage) | Inutile (le direct sur Internet utilise automatiquement HLS) |
| 23880 | TCP | RTSP de mediamtx, pour que les caméras / lecteurs externes se connectent directement | Inutile |
| 24214 | TCP | HLS de mediamtx (`127.0.0.1` uniquement) | Interdit |
| 24215 | TCP | Signalisation WebRTC de mediamtx (`127.0.0.1` uniquement) | Interdit |

## Comment vérifier après la configuration

```bash
# 1. Redirection HTTP→HTTPS (scénario reverse proxy)
curl -sSI http://<your-domain>/healthz | head -1      # attendu : 301

# 2. Vérification de santé
curl -sS https://<your-domain>/healthz                 # attendu : {"code":0,...}

# 3. Accéder à un point de terminaison protégé sans être connecté
curl -sS -o /dev/null -w '%{http_code}\n' \
     https://<your-domain>/api/cameras                 # attendu : 401
```

Enfin, parcourez tout le flux dans un navigateur : connexion → voir le flux en direct → aucune erreur de contenu mixte (Mixed Content) dans la console du navigateur. Si vous êtes renvoyé à la page de connexion juste après vous être connecté, ou si la vue en direct signale du Mixed Content, c'est dans 99 % des cas que `X-Forwarded-Proto` n'a pas été transmis correctement. Pour plus de dépannage, consultez [Dépannage](/docs/troubleshooting).

---

来源:https://yun-kan.com/fr/docs/remote-access
