From bde7e8eca78347bc136ab378caf0da2d8802d650 Mon Sep 17 00:00:00 2001 From: Mindfang Date: Wed, 10 Jan 2024 17:15:41 -0600 Subject: [PATCH] Cleanup and initial commit --- .dockerignore | 2 + Dockerfile | 100 ++++++++++++++++++++++++++++ README.md | 58 +++++++++++++++- root/defaults/nginx/http.conf | 49 ++++++++++++++ root/defaults/nginx/https.conf | 69 +++++++++++++++++++ root/etc/cont-init.d/10-initializer | 69 +++++++++++++++++++ root/etc/crontabs/nginx | 1 + root/etc/nginx/nginx.conf | 44 ++++++++++++ root/etc/php81/php-fpm.conf | 5 ++ root/etc/php81/php-fpm.d/nginx.conf | 17 +++++ root/etc/services.d/crond/run | 2 + root/etc/services.d/nginx/run | 2 + root/etc/services.d/php-fpm/run | 2 + root/etc/services.d/pteroq/run | 3 + 14 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 root/defaults/nginx/http.conf create mode 100644 root/defaults/nginx/https.conf create mode 100644 root/etc/cont-init.d/10-initializer create mode 100644 root/etc/crontabs/nginx create mode 100644 root/etc/nginx/nginx.conf create mode 100644 root/etc/php81/php-fpm.conf create mode 100644 root/etc/php81/php-fpm.d/nginx.conf create mode 100644 root/etc/services.d/crond/run create mode 100644 root/etc/services.d/nginx/run create mode 100644 root/etc/services.d/php-fpm/run create mode 100644 root/etc/services.d/pteroq/run diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4f20c0c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +/resources +README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a1b998b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,100 @@ +FROM alpine AS base + +ENV PHP_VER="php81" \ + PHPFPM_VER="php-fpm81" \ + NODE_OPTIONS=--openssl-legacy-provider + +RUN apk --no-cache add \ + bash \ + curl \ + nginx \ + ${PHP_VER} \ + ${PHP_VER}-bcmath \ + ${PHP_VER}-common \ + ${PHP_VER}-ctype \ + ${PHP_VER}-dom \ + ${PHP_VER}-fileinfo \ + ${PHP_VER}-fpm \ + ${PHP_VER}-gd \ + ${PHP_VER}-mbstring \ + ${PHP_VER}-pecl-memcached \ + ${PHP_VER}-openssl \ + ${PHP_VER}-pdo \ + ${PHP_VER}-pdo_mysql \ + ${PHP_VER}-phar \ + ${PHP_VER}-posix \ + ${PHP_VER}-json \ + ${PHP_VER}-session \ + ${PHP_VER}-simplexml \ + ${PHP_VER}-sodium \ + ${PHP_VER}-tokenizer \ + ${PHP_VER}-xmlwriter \ + ${PHP_VER}-zip \ + ${PHP_VER}-zlib && \ + mkdir -p \ + /var/www/pterodactyl \ + /run/nginx \ + /run/php-fpm && \ + ln -s /etc/${PHP_VER} /etc/php && \ + ln -s /usr/bin/${PHP_VER} /usr/bin/php && \ + ln -s /usr/sbin/${PHPFPM_VER} /usr/sbin/php-fpm && \ + ln -s /var/log/${PHP_VER} /var/log/php + +FROM base AS build +WORKDIR /var/www/pterodactyl + +# Download latest Panel build from project repository: https://github.com/pterodactyl/panel +ADD https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz panel.tar.gz + +# Install dependencies, perform Panel installation process +RUN apk --no-cache add yarn && \ + tar -xf panel.tar.gz && \ + rm panel.tar.gz && \ + chmod -R 755 storage/* bootstrap/cache && \ + find storage -type d > .storage.tmpl && \ + curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \ + cp .env.example .env && \ + composer install --ansi --no-dev --optimize-autoloader && \ + chown -R nginx:nginx * && \ + yarn install --production && \ + yarn add cross-env && \ + yarn run build:production && \ + rm -rf node_modules .env ./storage + +FROM base AS release +WORKDIR /var/www/pterodactyl +ENV S6_BEHAVIOUR_IF_STAGE2_FAILS="2" + +# Copy built Panel from Build stage +COPY --from=build --chown=nginx:nginx /var/www /var/www +COPY root/ / + +# Download latest S6-Overlay build from project repository: https://github.com/just-containers/s6-overlay +ADD https://github.com/just-containers/s6-overlay/releases/download/v2.2.0.3/s6-overlay-amd64-installer /tmp/s6-overlay + +# Download latest Wait-For-It script from project repository: https://github.com/vishnubob/wait-for-it +ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /usr/local/bin/wait-for-it + +# Download common tools +ADD https://bitbucket.org/Gethec/projecttools/raw/master/DockerUtilities/ContainerTools /usr/bin/ContainerTools + +# Install S6-Overlay and Wait-For-It +RUN chmod u+x /tmp/s6-overlay /usr/local/bin/wait-for-it && \ + /tmp/s6-overlay / && \ + # Remove preinstalled conf files + rm -rf \ + /tmp/* \ + /etc/nginx/http.d/default.conf \ + /etc/${PHP_VER}/php-fpm.d/www.conf && \ + # Symlink storage and conf file + ln -s /config/storage storage && \ + ln -s /config/pterodactyl.conf .env + +# Expose HTTP port +EXPOSE 80 + +# Persistent storage directory +VOLUME [ "/config" ] + +# Set entrypoint to S6-Overlay +ENTRYPOINT [ "/init" ] \ No newline at end of file diff --git a/README.md b/README.md index 2ae1f5b..19386fa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ -# Pterodactyl-Panel +# Pterodactyl-Panel # +## **NOTICE:** + +S6-Overlay released V3 at the start of the year, and with it came fundamental changes to how containers are built and services are handled. I will, in the near future, be moving this container to the new system, which will require some changes to how the container is configured and run. To allow users who do not wish to make these changes to continue to use the container, I have created the `s6-v2` branch and tag that will continue to be built with the final release of S6 before the version jump. For those who want to jump straight into the new, untested, but probably working S6 V3 version, check out the `s6-v3` tag. If you run into any questions or problems, please open an issue! + +## Disclaimer ## +As with anything else, exposing your system to the Internet incurs risks! This container does its best to be as secure as possible, but makes no guarantees to being completely impenetrable. Use at your own risk, and feel free to suggest changes that can further increase security. + +## About ## +The Pterodactyl project is an impressive one to me, but I wanted a way to make use of it in Unraid without installing it to the system. Thus, this set of containers was born. Panel uses uses Alpine's official image to keep the footprint small. + +## Configuration ## +The configuration of the Panel container is much less complicated than the Wings container. Most of the configuration occurs through environment variables. Simply define them to enable that component. + +### Variables ### +This container uses [vishnubob's wait-for-it](https://github.com/vishnubob/wait-for-it) script to allow you to have startup wait for the SQL and Redis servers to become available before continuing. Simply set the HOST values to the correct hostname or IP address, and specific a port number, if you are not using the services' default option. You can also set `TESTTIME` to change the wait period from the default 30 seconds to whatever you desire. + +It's also worth noting that, since this container is developed for use in Unraid, which has several excelent reverse proxy options, not much attention has been given to making the HTTPS option work. It should work in theory, but please submit a bug report if you encounter any issues. To enable, set `HTTPS` to "true", then map the location of your `fullchain.pem` and `privkey.pem` files to `/le-ssl`. + +| Variable | Default | Example | +|----------|---------|---------| +| DBHOST | `NULL` | `-e DBHOST="MariaDB"` | +| DBPORT | `3306` | `-e DBPORT=3306` | +| REDISHOST | `NULL` | `-e REDISHOST="Redis"` | +| REDISPORT | `6379` | `-e REDISPORT=6379` | +| TESTTIME | `30` | `-e TESTTIME=30` | +| HTTPS | `NULL` | `-e HTTPS="true"` | + +### Volumes ### +| Volume | Note | Example | +|--------|------|---------| +| /config | Required for persistence | `-v "/mnt/user/appdata/panel":"/config"` | +| /le-ssl | Expected location for SSL certs, if HTTPS is enabled | `-v "/letsencrypt/cert/directory":"/le-ssl":ro` | + +### Ports ### +| Port | Note | Example | +|------|------|---------| +| 80 | Default port if HTTPS is disabled. Redirects to 443 otherwise | `-p 80:80` | +| 443 | Web interface if HTTPS is enabled | `- p 443:443` | + +## Setup ## +**IMPORTANT:** While this container automates as much as possible, manual setup of the environment is still required to finish installation. For instructions, complete the Environmental Configuration, Database Setup and Add The First User sections in [Pterodactyl's "Getting Started" guide](https://pterodactyl.io/panel/1.0/getting_started.html#environment-configuration). + +Example run command: + + docker run \ + --name="Panel" \ + -e DBHOST="MariaDB" \ + -e DBPORT=3306 \ + -e REDISHOST="Redis" \ + -e REDISPORT=6379 \ + -e TESTTIME=30 \ + -e HTTPS=false \ + -v "/mnt/user/appdata/panel":"/config" \ + -v "/mnt/user/appdata/swag/etc/letsencrypt/live/":"/le-ssl":ro \ + -p 80:80 \ + gethec/pterodactyl-panel \ No newline at end of file diff --git a/root/defaults/nginx/http.conf b/root/defaults/nginx/http.conf new file mode 100644 index 0000000..fdabe7f --- /dev/null +++ b/root/defaults/nginx/http.conf @@ -0,0 +1,49 @@ +# HTTP config file, copied from https://pterodactyl.io/panel/1.0/webserver_configuration.html#nginx-without-ssl +# Last updated: 01/10/2024 + +server { + # Replace the example with your domain name or IP address + listen 80 default; + server_name _; + + + root /var/www/pterodactyl/public; + index index.php; + charset utf-8; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + access_log off; + error_log /var/log/nginx/pterodactyl.app-error.log error; + + # allow larger file uploads and longer script runtimes + client_max_body_size 100m; + client_body_timeout 120s; + + sendfile off; + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/php-fpm.sock; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTP_PROXY ""; + fastcgi_intercept_errors off; + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + location ~ /\.ht { + deny all; + } +} \ No newline at end of file diff --git a/root/defaults/nginx/https.conf b/root/defaults/nginx/https.conf new file mode 100644 index 0000000..86929ab --- /dev/null +++ b/root/defaults/nginx/https.conf @@ -0,0 +1,69 @@ +# HTTP config file, copied from https://pterodactyl.io/panel/1.0/webserver_configuration.html#nginx-with-ssl +# Last updated: 01/10/2024 + +server_tokens off; + +server { + listen 80; + server_name _; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name _; + + root /var/www/pterodactyl/public; + index index.php; + + access_log /var/log/nginx/pterodactyl.app-access.log; + error_log /var/log/nginx/pterodactyl.app-error.log error; + + # allow larger file uploads and longer script runtimes + client_max_body_size 100m; + client_body_timeout 120s; + + sendfile off; + + # SSL Configuration - Replace the example with your domain + ssl_certificate /etc/letsencrypt/live//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//privkey.pem; + ssl_session_cache shared:SSL:10m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; + ssl_prefer_server_ciphers on; + + # See https://hstspreload.org/ before uncommenting the line below. + # add_header Strict-Transport-Security "max-age=15768000; preload;"; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag none; + add_header Content-Security-Policy "frame-ancestors 'self'"; + add_header X-Frame-Options DENY; + add_header Referrer-Policy same-origin; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/run/php-fpm/php-fpm.sock; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M"; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTP_PROXY ""; + fastcgi_intercept_errors off; + fastcgi_buffer_size 16k; + fastcgi_buffers 4 16k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + include /etc/nginx/fastcgi_params; + } + + location ~ /\.ht { + deny all; + } +} \ No newline at end of file diff --git a/root/etc/cont-init.d/10-initializer b/root/etc/cont-init.d/10-initializer new file mode 100644 index 0000000..da51fe3 --- /dev/null +++ b/root/etc/cont-init.d/10-initializer @@ -0,0 +1,69 @@ +#!/usr/bin/with-contenv bash +source ContainerTools +SNAME=${BASH_SOURCE##*/} + +# If DBHOST value is present, pause boot until target container is up. +if [ -n "$DBHOST" ]; then + log "Waiting for SQL at $DBHOST:${DBPORT:=3306}" + if wait-for-it $DBHOST:$DBPORT -q -t ${TESTTIME:=30}; then + log "SQL found, continuing" + else + log "SQL could not be reached! Exiting..." + exit 1 + fi +fi + +# If REDISHOST value is present, pause boot until target container is up. +if [ -n "$REDISHOST" ]; then + log "Waiting for Redis at $REDISHOST:${REDISPORT:=6379}" + if wait-for-it $REDISHOST:$REDISPORT -q -t ${TESTTIME:=30}; then + log "Redis found, continuing" + else + log "Redis could not be reached! Exiting..." + exit 1 + fi +fi + +# Check for persistent storage directory, create file structure if not present. +if [ ! -d "/config/storage" ]; then + log "Creating storage directory" + cat .storage.tmpl | while read line; do + mkdir -p "/config/${line}" + done +fi + +# Check for persistent logging directory, create file path if not present. +if [ ! -d "/config/log/nginx" ]; then + log "Creating log directory." + mkdir -p "/config/log/nginx" +fi + +# Check for config file, create template if not present. +if [ ! -e /config/pterodactyl.conf ]; then + log "Config file does not exist, creating template" + log "[WARN] Connect to container and finish setup process" + cp .env.example /config/pterodactyl.conf + log "Generating unique Pterodactyl key" + log "$(php artisan key:generate --force --no-interaction)" +fi + +# Clear views and autogenerated configs on launch. This is necessary for updates, and doesn't affect non-update launches. +log "$(php artisan view:clear)" +log "$(php artisan config:clear)" +log "Checking for database updates, preparing cache" +log "[NOTE] This will fail if the database connection has not yet been configured" +log "$(php artisan migrate --seed --force)" +chown -R nginx:nginx /config/ + +# Load selected Nginx conf. +if [ "$HTTPS" == "true" ]; then + if [ ! -e "/etc/nginx/http.d/pterodactyl.conf" ]; then + log "Symlinking Nginx config file for HTTPS" + ln -s /defaults/nginx/https.conf /etc/nginx/http.d/pterodactyl.conf + fi +else + if [ ! -e "/etc/nginx/http.d/pterodactyl.conf" ]; then + log "Symlinking Nginx config file for HTTP" + ln -s /defaults/nginx/http.conf /etc/nginx/http.d/pterodactyl.conf + fi +fi \ No newline at end of file diff --git a/root/etc/crontabs/nginx b/root/etc/crontabs/nginx new file mode 100644 index 0000000..5989cc8 --- /dev/null +++ b/root/etc/crontabs/nginx @@ -0,0 +1 @@ +* * * * * php /var/www/pterodactyl/artisan schedule:run >> /dev/null 2>&1 \ No newline at end of file diff --git a/root/etc/nginx/nginx.conf b/root/etc/nginx/nginx.conf new file mode 100644 index 0000000..b02bb21 --- /dev/null +++ b/root/etc/nginx/nginx.conf @@ -0,0 +1,44 @@ +user nginx; +worker_processes auto; +pcre_jit on; +error_log /var/log/nginx/error.log warn; +include /etc/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server_tokens off; + client_max_body_size 1m; + sendfile on; + tcp_nopush on; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:2m; + ssl_session_timeout 5M; + ssl_session_tickets off; + + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml; + gzip_disable "MSIE [1-6]\."; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + include /etc/nginx/http.d/*.conf; +} \ No newline at end of file diff --git a/root/etc/php81/php-fpm.conf b/root/etc/php81/php-fpm.conf new file mode 100644 index 0000000..33f30cb --- /dev/null +++ b/root/etc/php81/php-fpm.conf @@ -0,0 +1,5 @@ +[global] +pid = /run/php-fpm/php-fpm.pid +error_log = /var/log/php/error.log +log_level = warning +include=/etc/php/php-fpm.d/*.conf \ No newline at end of file diff --git a/root/etc/php81/php-fpm.d/nginx.conf b/root/etc/php81/php-fpm.d/nginx.conf new file mode 100644 index 0000000..41f0646 --- /dev/null +++ b/root/etc/php81/php-fpm.d/nginx.conf @@ -0,0 +1,17 @@ +[nginx] +user = nginx +group = nginx + +listen = /run/php-fpm/php-fpm.sock +listen.owner = nginx +listen.group = nginx +listen.mode = 0750 + +pm = ondemand +pm.max_children = 9 +pm.process_idle_timeout = 10s +pm.max_requests = 200 + +slowlog = /dev/stdout +request_slowlog_timeout = 60s +catch_workers_output = yes \ No newline at end of file diff --git a/root/etc/services.d/crond/run b/root/etc/services.d/crond/run new file mode 100644 index 0000000..b2ef9e6 --- /dev/null +++ b/root/etc/services.d/crond/run @@ -0,0 +1,2 @@ +#!/usr/bin/execlineb -P +/usr/sbin/crond -fL /dev/null \ No newline at end of file diff --git a/root/etc/services.d/nginx/run b/root/etc/services.d/nginx/run new file mode 100644 index 0000000..59eb260 --- /dev/null +++ b/root/etc/services.d/nginx/run @@ -0,0 +1,2 @@ +#!/usr/bin/execlineb -P +/usr/sbin/nginx -g "daemon off;" \ No newline at end of file diff --git a/root/etc/services.d/php-fpm/run b/root/etc/services.d/php-fpm/run new file mode 100644 index 0000000..1c021f8 --- /dev/null +++ b/root/etc/services.d/php-fpm/run @@ -0,0 +1,2 @@ +#!/usr/bin/execlineb -P +/usr/sbin/php-fpm -Fc /etc/php \ No newline at end of file diff --git a/root/etc/services.d/pteroq/run b/root/etc/services.d/pteroq/run new file mode 100644 index 0000000..9fe98bc --- /dev/null +++ b/root/etc/services.d/pteroq/run @@ -0,0 +1,3 @@ +#!/usr/bin/execlineb -P +s6-setuidgid nginx +/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3 \ No newline at end of file