FRP Manager

Installation, configuration et désinstallation

Prérequis

  • Linux avec systemd (installation par script ou paquet .deb), ou Docker : en mode autonome, le conteneur n'a besoin ni de systemd ni de frp sur l'hôte
  • Rien d'autre : le panel est un seul programme, sans dépendance
  • Architectures : amd64, arm64, armv7

Inutile d'installer frp avant : le script télécharge frps/frpc et crée leurs services systemd. frp se met ensuite à jour depuis Réglages → Mises à jour, quand vous le décidez : rien n'est mis à jour automatiquement.

Installation par script

curl -LO https://github.com/Gogowwww/frp-manager/releases/latest/download/frp-manager.zip
unzip frp-manager.zip && cd frp-manager
sudo bash install.sh

Le script installe le programme frp-manager de votre architecture dans /opt/frp-manager, crée le service systemd frp-manager et le démarre. Il prépare aussi frp :

  • binaires frps / frpc dans /usr/local/bin (dernière version, somme SHA-256 vérifiée) ;
  • configurations par défaut /etc/frp/frps.toml et /etc/frp/frpc.toml, seulement si elles n'existent pas. Pour frps.toml, le script propose un port d'écoute tiré au hasard entre 10000 et 32767 (Entrée pour l'accepter, 7000 pour le port standard, ou un autre numéro) : les robots qui balaient Internet visent le port 7000. Sans terminal, le port aléatoire est pris ; FRPS_BIND_PORT=7000 sudo -E bash install.sh (ou random, ou un autre numéro) répond sans poser la question. Le port choisi est rappelé à la fin : ouvrez-le dans le pare-feu et reportez-le dans serverPort des clients ;
  • services frps.service et frpc.service, créés mais ni activés ni démarrés : vous les configurez puis les lancez depuis le panel.

Relancer install.sh depuis une archive plus récente met le panel à jour en conservant sa configuration. Le bouton Mettre à jour du panel fait la même chose sans ligne de commande.

Installation avec apt

FRP Manager a son dépôt APT (Debian, Ubuntu, Raspberry Pi OS ; amd64, arm64, armhf). On l'ajoute une fois, puis le panel s'installe et se met à jour comme n'importe quel paquet :

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://apt.frp-manager.org/repository.key -o /etc/apt/keyrings/frp-manager.asc
echo "deb [signed-by=/etc/apt/keyrings/frp-manager.asc] https://apt.frp-manager.org stable main" \
  | sudo tee /etc/apt/sources.list.d/frp-manager.list
sudo apt update
sudo apt install frp-manager

Les mises à jour arrivent ensuite avec sudo apt update && sudo apt upgrade (le bouton Mettre à jour du panel le rappelle). Pour suivre les pré-releases, remplacez stable par testing dans /etc/apt/sources.list.d/frp-manager.list (dev : un paquet par commit de développement, pour les essais seulement).

Le paquet installe /usr/bin/frp-manager et le service frp-manager, puis fait comme install.sh : services frps et frpc (créés, pas démarrés), configurations par défaut avec un port frps aléatoire (sudo FRPS_BIND_PORT=7000 apt install frp-manager pour le port standard), téléchargement de frp, démarrage du panel. Une installation faite avec install.sh passe sous le contrôle du paquet, sa configuration comprise.

sudo apt remove frp-manager arrête et retire le panel en gardant sa configuration ; sudo apt purge frp-manager l'efface aussi. frp, ses configurations et ses services restent en place. Pour retirer le dépôt : sudo rm /etc/apt/sources.list.d/frp-manager.list /etc/apt/keyrings/frp-manager.asc.

Docker et Portainer

L'image contient frps et frpc, et le panel les lance lui-même, dans le conteneur (mode autonome). Rien n'est installé sur l'hôte, pas de privileged, pas de pid: host, pas de socket Docker.

curl -O https://raw.githubusercontent.com/Gogowwww/frp-manager/main/docker-compose.yml
docker compose up -d

Portainer : Stacks → Add stack → Repository, URL https://github.com/Gogowwww/frp-manager, compose path docker-compose.yml, puis Deploy the stack.

Anciennes installations : une stack faite avant la version 0.6.0, qui pilotait le frp installé sur l'hôte, continue de fonctionner avec sa propre configuration. Ce mode n'est plus documenté ni recommandé ; la redéployer avec docker-compose.yml la fait passer en mode autonome, où frp tourne dans le conteneur. Les comptes et réglages de l'ancienne stack, stockés dans le /etc/frp-manager de l'hôte, et les configurations frps/frpc de /etc/frp ne sont pas repris dans le volume /data : le panel démarre comme une nouvelle installation (compte administrateur à recréer, frps.toml et frpc.toml créés à neuf). Les services frps/frpc de l'hôte ne sont pas arrêtés non plus ; arrêtez-les si le frp du conteneur doit prendre leur place. Une stack Portainer dont le compose path était packaging/docker/docker-compose.host.yml doit passer à docker-compose.yml : ce fichier n'existe plus dans le dépôt.

ImageContenu
ghcr.io/gogowwww/frp-manager:latestdernière release stable (recommandé)
ghcr.io/gogowwww/frp-manager:X.Y.Zune version précise, pour la figer ou revenir en arrière
ghcr.io/gogowwww/frp-manager:devdernière pré-release, pour tester avant tout le monde

Les images sont publiées pour amd64, arm64 (Raspberry Pi 4/5, serveurs ARM) et armv7 à partir de la version 0.0.54 ; Docker choisit automatiquement la bonne. Les versions antérieures n'existent qu'en amd64. Elles contiennent frps et frpc (archive officielle de fatedier/frp, somme SHA-256 vérifiée à la construction ; la version de frp est l'argument FRP_VERSION du Dockerfile).

Le panel reconnaît tout seul un conteneur qui ne voit rien de l'hôte et passe en mode autonome ; FRP_MANAGER_MODE=standalone l'impose.

Mode autonome

Ce que fait le conteneur. Le panel lance frps et frpc comme ses propres processus : démarrage, arrêt et redémarrage depuis le tableau de bord, interrupteur Démarrage automatique (le programme démarre avec le conteneur), relance automatique après un arrêt imprévu (au bout de 2 s, puis jusqu'à 60 s s'il échoue encore), arrêt propre de frps et frpc à docker stop. Au tout premier lancement, le panel crée frps.toml (port d'écoute tiré au hasard entre 10000 et 32767, ou FRPS_BIND_PORT ; jeton tiré au hasard) et frpc.toml. Une configuration supprimée depuis le panel ne revient pas.

Données. Tout ce qui doit durer est dans l'unique volume /data :

DossierVu par le panel commeContenu
/data/panel/etc/frp-managerconfiguration du panel, comptes, certificats
/data/frp/etc/frpfrps*.toml / frpc*.toml, modifiés depuis le panel
/data/log/var/log/frpjournaux de frp et sortie des programmes
/data/lib/var/lib/frp-managerétat, frp mis à jour depuis le panel (bin/)

Chaque fichier frps*.toml ou frpc*.toml de /data/frp est une instance (une carte dans le tableau de bord). Pour un dossier de l'hôte à la place du volume nommé : - ./frp-manager-data:/data.

Plusieurs instances. Le bouton Nouvelle instance du tableau de bord crée un frpc ou un frps de plus (frpc-<nom>.toml, frps-<nom>.toml) avec son propre journal et des ports d'écoute libres. Sur une installation systemd, il crée aussi l'unité frpc-<nom>.service, arrêtée et non activée. Une configuration déposée à la main dans /etc/frp sans service est signalée sur le tableau de bord avec un bouton Créer le service.

Journaux. La source Console montre ce qu'écrit le programme (sa sortie, /var/log/frp/<instance>.console.log, plafonnée à 5 Mo), la source Fichier le fichier choisi par log.to dans sa configuration.

Mises à jour de frp. Réglages → Mises à jour installe la nouvelle version dans /data/lib/bin : elle remplace celle de l'image et survit à la recréation du conteneur. Une image plus récente qui apporte un frp plus récent reprend la main ; pour revenir à la version de l'image, supprimez /data/lib/bin.

Réseau. La configuration fournie utilise le réseau de l'hôte : frps écoute directement sur les ports de la machine, et les ports des tunnels (remotePort) n'ont pas à être publiés un par un. Sans réseau de l'hôte, retirez network_mode, publiez le panel, le port de frps et ceux des tunnels, réglez FRPS_BIND_PORT sur le port publié et FRP_MANAGER_HOST=0.0.0.0 (voir les commentaires de docker-compose.yml).

Pare-feu. nftables fait partie de l'image. Le panel a besoin de la capacité NET_ADMIN (cap_add: [NET_ADMIN], déjà dans docker-compose.yml) ; sans elle, tout le reste fonctionne et la page Pare-feu dit ce qui manque. Avec le réseau de l'hôte, les règles filtrent le trafic de la machine, exactement comme l'installation classique, et restent en place si le conteneur s'arrête. ufw n'est pas dans l'image : le panel ne s'en sert que par son API (lecture et ajout de règles), et dans un conteneur sur le réseau de l'hôte il piloterait l'iptables de l'hôte à côté de l'ufw de l'hôte, avec un état des règles séparé (/etc/ufw) et environ 45 Mo de plus (Python).

Privilèges. NET_ADMIN sur le réseau de l'hôte permet au conteneur de modifier le réseau et les règles de pare-feu de la machine : qui contrôle le panel peut couper ou ouvrir des accès réseau. Sans NET_ADMIN, le conteneur ne peut rien changer au réseau de la machine.

Depuis une version Python (0.0.57 et avant)

Jusqu'à la 0.0.57, le panel était une application Python ; depuis la 0.0.58, c'est un programme Go unique. Rien à faire de particulier : le bouton Mettre à jour de l'ancien panel installe la nouvelle version comme d'habitude. Au redémarrage, celle-ci télécharge le programme Go de votre architecture (somme SHA-256 vérifiée), l'installe dans /opt/frp-manager, fait pointer le service frp-manager dessus et relance le panel.

La configuration ne change pas : comptes, mots de passe, réglages, webhooks, pare-feu et connexion unique sont gardés ; il faut seulement se reconnecter une fois. Relancer install.sh depuis la nouvelle archive fait la même chose et retire en plus l'environnement Python devenu inutile (/opt/frp-manager/venv). En Docker, docker compose pull && docker compose up -d suffit.

Première ouverture

Tant qu'aucun mot de passe n'existe, le panel n'affiche qu'une page de configuration initiale : choisissez l'identifiant administrateur et un mot de passe d'au moins 12 caractères. Aucune autre page ni aucune route de l'API ne répond avant.

Le certificat HTTPS est auto-signé : le navigateur affiche un avertissement au premier accès. Pour un certificat valide, placez le panel derrière un reverse proxy (nginx, Caddy) avec Let's Encrypt.

Accéder au panel depuis un autre poste

Une nouvelle installation n'écoute que sur 127.0.0.1 : le panel n'est pas joignable depuis le réseau tant que vous ne l'avez pas décidé.

MéthodeComment
Tunnel SSH (recommandé)ssh -L 8765:127.0.0.1:8765 utilisateur@serveur, puis https://127.0.0.1:8765
Reverse proxy localnginx ou Caddy écoute sur le réseau et relaie vers 127.0.0.1:8765 (autorisez l'upgrade WebSocket sur /ws/)
Écoute sur le réseaubind_host à 0.0.0.0 dans Réglages → Accès réseau (ou dans le fichier de config), ou FRP_MANAGER_HOST=0.0.0.0 en Docker, puis redémarrage du panel

Si vous ouvrez le panel au réseau, filtrez l'accès par IP (pare-feu, VPN). Une installation antérieure à 0.0.51 garde son adresse d'écoute actuelle.

Exemple nginx (extrait) :

location / {
    proxy_pass https://127.0.0.1:8765;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Derrière un reverse proxy TLS avec ssl_enabled: false, ajoutez "cookie_secure": true au fichier de config pour que le cookie de session reste marqué Secure.

Passkeys

Chaque compte qui se connecte par mot de passe peut ajouter des passkeys (Réglages → Mon compte → Passkeys ; le mot de passe est redemandé) : l'appareil vous connecte ensuite tout seul (empreinte, visage, code PIN, clé de sécurité), sans saisir d'identifiant ni de mot de passe. Le mot de passe reste valable.

  • Les navigateurs n'autorisent les passkeys que sur un nom de domaine avec un certificat HTTPS valide, par exemple derrière un reverse proxy avec Let's Encrypt (https://panel.exemple.fr), ou sur http://localhost. Le certificat auto-signé du panel et l'accès par adresse IP (https://192.0.2.10:8765) sont refusés : la carte Passkeys l'indique.
  • Une passkey est liée au domaine sur lequel elle a été créée : créée sur panel.exemple.fr, elle ne sert que là.
  • Derrière un reverse proxy, transmettez l'en-tête Host d'origine (proxy_set_header Host $host;, comme ci-dessus) ou X-Forwarded-Host.
  • Appareil perdu : supprimez la passkey dans Réglages → Mon compte après vous être connecté avec votre mot de passe, ou demandez à un administrateur de retirer les passkeys du compte (Utilisateurs → compte → Retirer ses passkeys). Les sessions ouvertes avec une passkey supprimée sont fermées.

Versions et pré-releases

Chaque nouvelle version sort d'abord en pré-release, pour être testée avant d'être proposée à tout le monde. Les numéros se suivent et une pré-release validée devient la release définitive avec le même numéro, sans être reconstruite.

Pré-releaseRelease
Page GitHubmarquée Pre-releaseLatest
Image Docker:X.Y.Z + :dev:X.Y.Z + :latest
Proposée aux panels stablesnonoui
Proposée aux panels en pré-release (installation par script)ouioui
  • Panel stable : il ne voit que les releases.
  • Panel en pré-release, installation par script : le bouton « Mettre à jour » propose la version publiée la plus récente, pré-release ou release.
  • Panel en pré-release sous Docker : changez le tag de l'image (:dev ou :latest).

Quand la pré-release installée devient une release définitive, le panel repasse de lui-même sur le canal stable.

Configuration du panel

Tout se règle depuis la page Réglages. Le fichier sous-jacent est /etc/frp-manager/frp-manager.json, lisible par root seulement :

CléDescriptionDéfaut
bind_hostAdresse d'écoute127.0.0.1 (nouvelle installation)
bind_portPort du panel8765
usernameIdentifiant de connexionadmin
password_hashMot de passe haché (argon2id ou scrypt, géré par l'interface)créé à la première ouverture
secret_keyClé de signature des sessions, tirée au hasard au premier démarragegénérée
session_timeoutDurée de session en secondes3600
ssl_enabledHTTPS avec certificat auto-signétrue
cookie_secureCookie Secure même sans HTTPS côté panel (reverse proxy TLS)false
download_mirrorsMiroirs tiers en repli de github.com pour télécharger frptrue
anonymous_statsStatistiques anonymes envoyées une fois par jour (voir plus bas)false
nicknamesSurnoms des instances (gérés par l'interface){}

bind_host, bind_port et ssl_enabled s'appliquent au prochain redémarrage de frp-manager.

Variables d'environnement :

VariableEffet
FRP_MANAGER_HOSTAdresse d'écoute, prioritaire sur bind_host
FRP_MANAGER_PORTPort d'une nouvelle installation (écrit dans bind_port au premier démarrage)
FRP_MANAGER_NO_MIRRORS=1Interdit les miroirs de téléchargement tiers
FRP_MANAGER_CONFIGAutre emplacement du fichier de configuration

Miroirs de téléchargement

Si github.com ne répond pas, frp peut être téléchargé via des miroirs tiers (mirror.ghproxy.com, ghfast.top, gh-proxy.com). Ces services voient passer le fichier et pourraient le modifier : c'est un risque pour la chaîne d'approvisionnement. Le panel n'accepte donc une archive venue d'un miroir que si sa somme SHA-256 correspond à celle publiée avec la release de frp, et le signale dans le journal d'installation. Si github.com vous est accessible, désactivez les miroirs (Réglages → Accès réseau).

Statistiques anonymes

Désactivées par défaut. Si vous les activez (case de la configuration initiale, ou Réglages → Accès au panel → Statistiques anonymes), le panel envoie une fois par jour, à https://frp-manager.org/api/ping, exactement ceci et rien d'autre (affiché dans Réglages avant de les activer) :

{"version": "0.4.0", "arch": "arm64", "install": "apt"}
  • version : version du panel ; arch : amd64, arm64 ou arm ; install : script, apt ou docker.
  • Aucun identifiant, aucun nom, aucune adresse, aucune configuration frp. Le site ne garde que des compteurs par jour, par version, architecture et méthode d'installation ; il n'enregistre pas les adresses IP (utilisées seulement en mémoire, sous forme d'empreinte, pendant 24 h, pour limiter les abus).
  • Cela aide à savoir quelles versions sont encore utilisées, pour décider de la compatibilité et des mises à jour. Les désactiver arrête l'envoi tout de suite.

Ports : TCP + UDP et plages de ports

Dans la page Ports, l'éditeur de port propose deux raccourcis pour les services qui ont besoin de plus d'un tunnel :

  • TCP + UDP ouvre le même port dans les deux protocoles, pour un jeu ou un DNS par exemple. Le panel écrit deux tunnels ordinaires, <nom>-tcp et <nom>-udp, avec les mêmes ports et les mêmes options. Le port public est obligatoire, sinon le serveur en choisirait un différent pour chacun. La liste affiche la paire sur une seule ligne (badges TCP et UDP), qui se modifie ou se supprime d'un bloc ; repassée en TCP ou en UDP seul, elle garde un seul tunnel, nommé <nom>. Deux tunnels ne sont regroupés que si leurs noms (X-tcp et X-udp), types, adresses, ports, port public et options correspondent tous : sinon, ils restent deux lignes.
  • Plage de ports (tcp et udp) : saisissez une plage (30000-30010) ou une liste (6000-6006,6007) dans le port local, et autant de ports dans le port public, alors obligatoire (1 000 ports au plus). La plage reste une seule ligne dans la liste, et le panel l'écrit sous forme de modèle frp :
{{- range $_, $v := parseNumberRangePair "30000-30010" "30000-30010" }}
[[proxies]]
name = "jeu-{{ $v.First }}"
type = "udp"
localIP = "127.0.0.1"
localPort = {{ $v.First }}
remotePort = {{ $v.Second }}
{{- end }}

frpc associe les ports dans l'ordre (le premier port local au premier port public, et ainsi de suite) et crée un tunnel par port, nommé jeu-30000, jeu-30001… Le panel refuse un nom que l'un de ces tunnels utiliserait déjà, car frpc ne démarre pas avec deux tunnels du même nom. parseNumberRangePair existe depuis frp 0.56 : un frpc plus ancien refuse cette configuration, mettez donc frp à jour d'abord (Réglages → Mises à jour).

Notifications par webhook

Réglages → Notifications envoie un message à l'adresse de votre choix quand quelque chose se passe sur la machine. Chaque webhook a sa destination, son format, ses événements et sa langue : les pannes vers Discord, les mises à jour vers ntfy, les événements de sécurité vers votre propre service.

FormatAdresse à saisir
Discordl'adresse du webhook d'un salon (Paramètres, Intégrations) ; envoyé sous forme de carte colorée
Slack / Mattermostl'adresse d'un Incoming Webhook
Telegramhttps://api.telegram.org/bot<jeton> et l'identifiant de conversation
ntfyl'adresse du sujet, https://ntfy.sh/mon-sujet ou votre serveur ; la priorité suit la gravité
Gotifyhttps://gotify.example.org/message?token=…
JSON (générique)n'importe quelle adresse ; corps {event, level, title, message, host, time, panel_version, instance, data}, ou votre propre modèle
Texte brutn'importe quelle adresse ; titre, message et nom de la machine en texte

Événements : instance arrêtée ou démarrée (contrôlée toutes les 15 secondes et confirmée par un second relevé : un redémarrage rapide ne déclenche pas d'alerte), adresse verrouillée après des échecs de connexion, connexion, configuration ou pare-feu modifié, mise à jour du panel ou de frp disponible (vérifiée toutes les 6 heures, annoncée une seule fois par version), frp mis à jour, panel démarré. Réglages facultatifs : limiter à certaines instances, un délai entre deux envois du même événement, la langue des messages, des en-têtes HTTP supplémentaires et un secret de signature.

Le format générique accepte un modèle JSON avec les variables {{event}}, {{title}}, {{message}}, {{level}}, {{host}}, {{time}}, {{instance}}, {{subject}} et {{version}}, échappées pour le JSON : {"content": "{{title}} : {{message}}"}. Avec un secret de signature, chaque requête porte X-FRPManager-Signature: sha256=<HMAC-SHA256 du corps> :

mac := hmac.New(sha256.New, []byte(secret)) // crypto/hmac, crypto/sha256
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
ok := hmac.Equal([]byte(expected), []byte(r.Header.Get("X-FRPManager-Signature")))

Les envois passent par une file et sont retentés trois fois en cas d'erreur réseau, de 5xx ou de 429 ; les derniers sont listés sur la page. L'adresse, le secret et les en-têtes sont stockés dans le fichier de configuration (lisible par root seulement) et ne reviennent jamais dans le navigateur : laissez un champ vide en modifiant pour garder la valeur enregistrée. Les redirections ne sont pas suivies.

Mot de passe perdu

sudo /opt/frp-manager/frp-manager --reset-password     # paquet .deb : sudo frp-manager --reset-password
sudo systemctl restart frp-manager

En Docker : docker exec -it frp-manager frp-manager --reset-password, puis docker restart frp-manager.

Les passkeys du propriétaire sont gardées : elles permettent toujours de se connecter.

Structure des fichiers

Dépôt
  panel/                   Le panel : programme Go (go build ./panel)
    *.go                   Serveur HTTP, API, pare-feu, mises à jour… ; *_test.go : tests
    netaddr/               Règle des adresses publiques (partagée avec tools/blocklists)
    install.sh             Installation par script
    templates/             Interface, intégrée au programme à la compilation
      index.html, login.html, setup.html
      partials/icons.html  Icônes SVG (embarquées, sans CDN)
      assets/
        css/app.css        Thèmes clair et sombre
        js/                Application (modules ES, sans étape de build)
        locales/fr.js, en.js  Textes de l'interface
  docs/                    Documentation, journaux des modifications, politique de
                           sécurité, guide de contribution (aussi sur le site) ;
                           img/ : captures
  tools/                   Outils du dépôt : blocklists (listes communautaires du
                           pare-feu), releasenotes (notes des releases)
  packaging/               Paquet .deb (deb/), passage depuis une version Python
                           (python-bridge/)
  Dockerfile, docker-compose.yml (Docker, autonome), go.mod

/opt/frp-manager/          Panel installé (script) ; /usr/bin/frp-manager (paquet .deb)
/etc/frp-manager/          Configuration du panel + certificats SSL
/etc/frp/                  Configurations frps/frpc (TOML)
/var/log/frp/              Journaux frp
/var/lib/frp-manager/      État (versions installées)
/etc/systemd/system/       frp-manager.service, frps.service, frpc.service

Docker, mode autonome : un seul volume /data, vu par le panel comme les
dossiers ci-dessus (/data/panel, /data/frp, /data/log, /data/lib) ;
frps et frpc de l'image dans /opt/frp-manager/bin

Le panel n'écrit une configuration frp que dans les dossiers où il en cherche : /etc/frp, /usr/local/etc/frp, /opt/frp, /root/frp.

Désinstallation

sudo systemctl disable --now frp-manager
sudo rm /etc/systemd/system/frp-manager.service
sudo rm -rf /opt/frp-manager /etc/frp-manager
sudo systemctl daemon-reload

Avec le paquet .deb : sudo apt purge frp-manager.

Les configurations frp (/etc/frp/) et les binaires (/usr/local/bin/) sont conservés.

Avec Docker : docker compose down arrête et supprime le conteneur ; en mode autonome, docker compose down -v efface aussi le volume /data (configurations frp et du panel comprises).

Modifier cette page sur GitHub