Pterodactyl

Mise en place d'une stack Docker pour faire tourner Pterodactyl sous une machine AMD64.
Connaissances requises pour ce tuto :
- Linux (shell, script, Bash, pid, gid, uid, systemd, ip, port)
- Docker
- Docker Compose
- Portainer
- Cloudflare (domaine & DNS)
- Caddy
- API (le principe)
- CORS (pour que ça fonctionne)
Introduction
Pterodactyl est panneau d'installation, de configuration et de gestion de jeux.
Pterodactyl® est un panneau de gestion de serveur de jeu gratuit et open-source construit avec PHP, React et Go. Conçu dans un souci de sécurité, Pterodactyl exécute tous les serveurs de jeu dans des conteneurs Docker isolés, tout en exposant une interface utilisateur belle et intuitive aux utilisateurs finaux.
Ne vous contentez pas de peu. Faites des serveurs de jeux un citoyen de première classe sur votre plateforme.
Il est disponible ici : https://pterodactyl.io/
Il repose sur 2 composants principaux :
- Le panneau (panel) qui est l'interface (frontend) du logiciel
- Le démon (wings) de gestion des jeux (backend)
Prérequis
Cet article par du principe que vous avez un nom de domaine et que ses DNS sont gérés par Cloudflare.
Si ce n'est pas le cas, il vous faudra adapter en fonction de votre hébergeur ; ou au moins votre gestionnaire de DNS.
La 2ᵉ supposition est que vous disposez d'un serveur Caddy afin de disposer d'un reverse proxy.
Et celui-ci doit être lié à Cloudflare avec une gestion de certificats wildcard.
Donc, si vous n'avez ni nom de domaine, ni une gestion des DNS chez Cloudflare, ni Caddy; je vous conseille d'aller mettre ça en place avant de continuer.
Il faudra, en suivant entièrement cet article, créer les répertoires suivant sur la machine :
- /opt/docker/pterodactyl (qui servira de répertoire de base)
Ensuite, dans ce répertoire, il faut créer les sous-répertoires suivants :
- scripts
- database
- certs
- etc
- var
- nginx
- panel
- lib
- containers
- tmp
- logs/panel
- logs/panel/logs
- logs/wings
Par défaut, l'utilisateur (uid) et le groupe d'utilisateur (gid) utilisés sont ceux du 1er compte utilisateur Linux (1000).
Libre à vous de le changer ; en pensant à mettre à jour les valeurs dans le docker-compose et dans init.sh.
Une fois toute l'arborescence créée, il vous faut créer les fichiers suivants :
scripts/init.sh
C'est le seul moyen que j'aie trouvé pour que le démon Wings arrive à gérer les conteneurs.
Sachant qu'avec la solution décrite dans cet article, nous nous retrouvons avec des conteneurs (Wings > Jeux) gérés au sein de Docker...
#!/bin/bash
if [ ! -d /var/lib/pterodactyl ]; then
mkdir -p /var/lib/pterodactyl
fi
if [ ! -L /var/lib/pterodactyl/volumes ]; then
ln -s /opt/docker/pterodactyl/lib/volumes /var/lib/pterodactyl/volumes
fi
chown -R 1000:1000 /var/lib/pterodactyl
if [ ! -L /tmp/pterodactyl ]; then
ln -s /opt/docker/pterodactyl/tmp /tmp/pterodactyl
fi
chown -R 1000:1000 /tmp/pterodactyl
chown -R 1000:1000 /opt/docker/pterodactyl
scripts/pterodactyl-init.service
Afin de s'assurer que les liens symboliques soient toujours présents pour Wings.
Il sera à copier dans /etc/systemd/system et à assigner à root.
# /opt/docker/pterodactyl/pterodactyl-init.service
[Unit]
Description=Pterodactyl Docker Init
After=default.target
[Service]
Type=oneshot
ExecStart=/opt/docker/pterodactyl/scripts/init.sh
[Install]
WantedBy=default.target
Il conviendra de l'installer et de l'activer:
sudo cp pterodactyl-init.service /etc/systemd/system/
sudo chown root:root /etc/systemd/system/pterodactyl-init.service
sudo systemctl enable pterodactyl-init.service
etc/config.yml
Le fichier est juste à initialiser à vide:
touch etc/config.yml
Attribution des droits
Maintenant, on met à jour les droits utilisateurs :)
sudo chown -R 1000:1000 opt/docker/pterodactyl/
Cloudflare
Il est temps de mettre en place la partie DNS chez Cloudflare.
Soit, vous êtes hébergés et toute la configuration est uniquement sur un serveur distant directement lié à votre nom de domaine.
Dans ce cas, vous créez 2 entrées de type CNAME.
Type | Nom | Contenu | Etat du proxy | Durée TTL |
---|---|---|---|---|
CNAME | panel | domain.com | Proxied | Automatic |
CNAME | node | domain.com | Proxied | Automatic |
Si vous êtes auto hébergés, il faut créer 2 entrées de type A.
Type | Nom | Contenu | Etat du proxy | Durée TTL |
---|---|---|---|---|
A | panel | IP publique du serveur | Proxied | Automatic |
A | node | IP publique du serveur | Proxied | Automatic |
Traefik
Je partirai du principe que Traefik est installé et fonctionnel ; et que sa configuration s'effectue par des fichiers Yaml.
Vous aurez à créer 2 routers:
panel:
entryPoints:
- "https"
rule: "Host(`panel.**domain.com**`)"
middlewares:
- pterodadctyl
tls: {}
service: panel
node:
entryPoints:
- "https"
rule: "Host(`node.**domain.com**`)"
middlewares:
- pterodadctyl
tls: {}
service: node
Ainsi que 2 services:
panel:
loadBalancer:
servers:
- url: "http://**[IP de la machine héberge le panel]**:8001"
passHostHeader: true
node:
loadBalancer:
servers:
- url: "http://**[IP de la machine héberge wings]**:8181"
passHostHeader: true
Et 4 middlewares:
pterodadctyl:
chain:
middlewares:
- default-whitelist
- security-headers
- cors-all
default-whitelist:
ipWhiteList:
sourceRange:
- 10.0.0.0/8
- 192.168.0.0/16
- 172.16.0.0/12
- **[Plage d'IP correspondant à vos machines]**
- 173.245.48.0/20
- 103.21.244.0/22
- 103.22.200.0/22
- 103.31.4.0/22
- 141.101.64.0/18
- 108.162.192.0/18
- 190.93.240.0/20
- 188.114.96.0/20
- 197.234.240.0/22
- 198.41.128.0/17
- 162.158.0.0/15
- 104.16.0.0/13
- 104.24.0.0/14
- 172.64.0.0/13
- 131.0.72.0/22
- 2400:cb00::/32
- 2606:4700::/32
- 2803:f800::/32
- 2405:b500::/32
- 2405:8100::/32
- 2a06:98c0::/29
- 2c0f:f248::/32
security-headers:
headers:
customResponseHeaders:
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
server: ""
X-Forwarded-Proto: "https"
sslProxyHeaders:
X-Forwarded-Proto: "https"
referrerPolicy: "same-origin"
hostsProxyHeaders:
- "X-Forwarded-Host"
customRequestHeaders:
X-Forwarded-Proto: "https"
contentTypeNosniff: true
cors-all:
headers:
customRequestHeaders:
Access-Control-Allow-Origin: "origin-list-or-null"
Sec-Fetch-Site: "cross-site"
X-Forwarded-Proto: "https"
Access-Control-Allow-Headers: "*, Authorization"
customResponseHeaders:
Access-Control-Allow-Origin: "*"
Sec-Fetch-Site: "cross-site"
X-Forwarded-Proto: "https"
Access-Control-Allow-Headers: "*, Authorization"
accessControlAllowMethods:
- OPTIONS
- POST
- GET
- PUT
- DELETE
- PATCH
accessControlAllowHeaders:
- "*, Authorization"
accessControlExposeHeaders:
- "*, Authorization"
accessControlMaxAge: 100
addVaryHeader: true
accessControlAllowCredentials: true
accessControlAllowOriginList:
- "*"
Tout ça est à ajouter dans votre fichier config.yml de Traefik.
C'est le seul moyen, dans cette configuration, de passer outre les blocages CORS.
Caddy
Je partirai du principe que Caddy est installé et fonctionnel ; et que sa configuration s'effectue par des fichiers.
Vous aurez besoin simplement de 2 entrées de configuration pour Caddy :
Panel
@panel host panel.domain.com
handle @panel {
import pterodadctyl
reverse_proxy [ip]:[port1] {
import reverseProxy
}
}
Wings
@node host node.domain.com
handle @node {
import pterodadctyl
reverse_proxy [ip]:[port2] {
import reverseProxy
}
}
Portainer
Maintenant que les préparatifs sont faits, on passe aux choses sérieuses :)
... La création de la stack Docker sous Portainer !
version: "4.2"
#
# 2022-12-10
# pterodactyl
#
# Before running Stack !!!
#
# # /opt/docker/pterodactyl/pterodactyl-init.service
#
# [Unit]
# Description=Pterodactyl Docker Init
# After=default.target
#
# [Service]
# Type=oneshot
# ExecStart=/opt/docker/standard/pterodactyl/scripts/init.sh
#
# [Install]
# WantedBy=default.target
#
# # /opt/docker/pterodactyl/scripts/init.sh
#
# #!/bin/bash
# if [ ! -d /var/lib/pterodactyl ]; then
# mkdir -p /var/lib/pterodactyl
# fi
# if [ ! -L /var/lib/pterodactyl/volumes ]; then
# ln -s /opt/docker/standard/pterodactyl/work/lib/volumes /var/lib/pterodactyl/volumes
# fi
# chown -R 1000:1000 /var/lib/pterodactyl
# if [ ! -L /tmp/pterodactyl ]; then
# ln -s /opt/docker/standard/pterodactyl/work/tmp /tmp/pterodactyl
# fi
# chown -R 1000:1000 /tmp/pterodactyl
# chown -R 1000:1000 /opt/docker/pterodactyl
# Initialization:
#
# After the first succeessful launch of the stack, you need to create the first user in Panel.
#
# run a command shell into "pterodactyl-panel" using '/bin/sh'
# run:
# php artisan p:user:mak
services:
panel:
container_name: panel
hostname: panel
image: ghcr.io/pterodactyl/panel:v1.11.1
restart: always
stdin_open: true
tty: true
ports:
- "8001:80"
expose:
- "80"
environment:
TZ: "Etc/UTC"
APP_TIMEZONE: "Etc/UTC"
APP_ENV: "production"
APP_ENVIRONMENT_ONLY: "false"
APP_URL: "https://panel.domain.com"
APP_SERVICE_AUTHOR: "[...]"
MAIL_FROM: "[...]"
MAIL_DRIVER: "[...]"
MAIL_HOST: "[...]"
MAIL_PORT: "[...]"
MAIL_USERNAME: "[...]"
MAIL_PASSWORD: "[...]"
MAIL_ENCRYPTION: "true"
TRUSTED_PROXIES: "*"
PTERODACTYL_TELEMETRY_ENABLED: "false"
DB_HOST: "[...]"
DB_PORT: "[...]"
DB_USERNAME: "[...]"
DB_PASSWORD: "[...]"
DB_DATABASE: "panel"
CACHE_DRIVER: "redis"
SESSION_DRIVER: "redis"
QUEUE_DRIVER: "redis"
REDIS_HOST: "[...]"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/docker/standard/ssl/:/ssl/:ro
- /opt/docker/standard/notification:/notify:ro
- /etc/ssl/certs:/etc/ssl/certs:ro
- /opt/docker/standard/pterodactyl/config/certs:/etc/letsencrypt
- /opt/docker/standard/pterodactyl/config/etc:/etc/pterodactyl
- /opt/docker/standard/pterodactyl/config/nginx:/etc/nginx/http.d
- /opt/docker/standard/pterodactyl/work/var:/app/var
- /opt/docker/standard/pterodactyl/work/logs/panel:/app/storage/logs
- /opt/docker/standard/pterodactyl/work/logs/panel:/var/log/panel/logs
- /opt/docker/standard/pterodactyl/work/panel:/var/www/html
- /opt/docker/standard/pterodactyl/work/lib:/var/lib/pterodactyl
- /opt/docker/standard/pterodactyl/work/containers:/var/lib/docker/containers
wings:
container_name: wings
hostname: wings
image: ghcr.io/pterodactyl/wings:v1.11.0
restart: always
stdin_open: true
tty: true
networks:
- wings0
ports:
- "8181:8080"
- "2022:2022"
expose:
- "8080"
- "2022"
depends_on:
- panel
environment:
TZ: "Etc/UTC"
WINGS_UID: 1000
WINGS_GID: 1000
WINGS_USERNAME: pterodactyl
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/docker/standard/ssl/:/ssl/:ro
- /opt/docker/standard/notification:/notify:ro
- /etc/ssl/certs:/etc/ssl/certs:ro
- /opt/docker/standard/pterodactyl/config/etc:/etc/pterodactyl
- /opt/docker/standard/pterodactyl/work/logs/wings:/var/log/pterodactyl
- /opt/docker/standard/pterodactyl/work/tmp:/tmp/pterodactyl
- /opt/docker/standard/pterodactyl/work/lib:/var/lib/pterodactyl
- /opt/docker/standard/pterodactyl/work/containers:/var/lib/docker/containers
- /opt/docker/vm/pterodactyl/work/lib/wings.db:/var/lib/pterodactyl/wings.db
networks:
default:
ipam:
config:
- subnet: 172.20.0.0/16
wings0:
name: wings0
driver: bridge
ipam:
config:
- subnet: "172.21.0.0/16"
driver_opts:
com.docker.network.bridge.name: wings0
Pensez à modifier les éléments qui seront propres à votre configuration :
- YOUR_PASSWORD_CHOICE
- YOUR_DOMAIN
- YOUR_SENDER_EMAIL
- SMTP_HOST
- SMTP_PORT
- SMTP_EMAIL_USERNAME
- SMTP_EMAIL_PASSWORD
Éventuellement :
- APP_TIMEZONE
- TZ
- MAIL_DRIVER
- MAIL_ENCRYPTION
- PUID
- PGID
Ajustez :p
Y compris dans la configuration de Traefik/Caddy.
Lorsque la stack démarre correctement, pensez à créer votre premier utilisateur (qui sera automatiquement administrateur du panel).
Sous Portainer, il suffit de faire lancer une invite de commande avec /bin/sh et de lancer
php artisan p:user:mak
Une fois ceci réaliser, allez faire un tour du côté de votre panel :)
Le Panel et son 1er Node
Une fois sur le panel et correctement identifié, la 1ère chose à faire est de créer un node.

Ce qui est important ici :
- FQDN: doit être le nom complet que vous avez défini sur Cloudflare et paramétré dans Traefik pour le [u]node[/u] (node.domain.com)
- Use SSL Connection: doit être coché
- Behind Proxy: doit être coché
- Daemon Port: doit être sur 443
Il s'agit des valeurs maximales disponibles pour l'ensemble des jeux qui seront installés sur ce node.
Une fois que c'est passé, vous allez sur l'onglet Configuration et vous copiez le contenu pour aller le coller dans le fichier etc/config.yml.
Vous redémarrez uniquement le conteneur pterodactyl-wings et une fois que c'est fait, vous cliquez sur Nodes dans le bandeau gauche du Panel.
Et là, tant que vous n'avez pas le petit cœur vert indiquant que le démon Wings est bien lancé et accessible, vous reprenez toutes les étapes précédentes parce que vous avez loupé un truc.

Lorsque vous avez la confirmation que Wings est opérationnel, vous avez fait le plus dur ; à savoir mettre en place un panel et son gestionnaire de jeux fonctionnels.
Félicitations !
Allocations / Serveurs
Maintenant que le Panel et Wings fonctionnent et sont capables de communiquer, il ne reste plus qu'à attribuer des ressources (allocations) pour les futurs jeux (servers).
On va partir sur un objectif simple : un serveur Minecraft Vanilla tout simple.
Sur le Node, vous allez dans l'onglet Allocations et dans la partie droite, vous saisissez l'adresse IP du node (dans IP Address) et vous mettez un numéro de port (dans Ports) et vous faites Submit.
Vous venez de définir une ressource (allocation = couple IP du node + un n° de port [ou une plage de n° de ports]).
À présent, vous passez sur l'onglet Servers et il est vide !...
Et bien, on va le remplir :)
Dans le bandeau gauche du Panel, vous cliquez sur Servers et vous faites Create New !
Vous lui donnez:
- un nom (Server Name)
- un propriétaire (Server Owner, par défaut, ce sera votre compte mail)
- un node (qui doit déjà être sélectionné par défaut)
- une allocation (qui doit par défaut être celle que vous venez de définir)
- du cpu (CPU Limit)
- de la mémoire (Memory)
- de l'espace disque (Disk Space)
Pour CPU Limit, il s'agit du nombre de threads (dépendant de la machine hôte) que vous affecterez au jeu. Donc 100% définit un jeu qui tournera sur 1 thread.
Vous allez choisir le groupe de jeux (Nest) et le type de jeux (Egg) que vous souhaitez utiliser.
Dans notre cas :
- Nest = Minecraft
- Egg = Minecraft Vanilla
Et il ne reste plus qu'à cliquer sur Create Server en bas à droite.
Dans le cas contraire, soit il n'y a pas assez de ressource allouée, soit vous avez modifié d'autres paramètres (et dans ce cas, repartez sur un cas simple), soit il faut supprimer le serveur et recommencer.
Minecraft Vanilla
Lorsque le serveur de jeux (ici Minecraft Vanilla) est installé et opérationnel, il n'y a plus qu'à le lancer :)
Vous revenez à l'accueil du Panel et vous cliquez sur votre serveur pour le sélectionner.
Là, vous cliquez sur Start et vous attendez qu'il se lance !


Lancer votre client Minecraft, passez en multijoueurs et connectez-vous en utilisant les informations indiquées dans Address.
Minecraft Fabric
Voici un petit exemple ce que ça peut donner avec un serveur Minecraft Fabric légèrement moddé qui utilise mon image GraalVM :)


Remerciements
- Pterodactyl Panel: https://pterodactyl.io/
- Marius Hosting: https://mariushosting.com/docker/
IBRACORP: https://docs.ibracorp.io/
Techno Tim: https://docs.technotim.live/
- Put Wildcard Certificates and SSL on EVERYTHING: https://docs.technotim.live/posts/traefik-portainer-ssl/
- I Built the PERFECT Game Server with Pterodactyl and Docker: https://docs.technotim.live/posts/pterodactyl-game-server/
Changelog
2022-12-12
- Ajout de la configuration avec Caddy en reverse proxy
- Déport de la base de données en externe (BDD commune pré-installée)
- Déport du cache Redis en externe (pré-installé)
- Simplification de la stack Portainer (uniquement le Panel et Wings)