From 430f6e2baa4acfde045fd111ad5ba74bf69abbdf Mon Sep 17 00:00:00 2001 From: Gui-Gos <97973228+Gui-Gos@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:00:24 +0100 Subject: [PATCH] feat(nginx): add hardened nginx app with security improvements - Rate limiting (10 req/s per IP, burst 20) - Modern security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) - Request body size limits (50m) - Fixed header inheritance bug in static files location block - Removed unused form fields (NGINX_INTERNAL_PORT, NGINX_ENABLE_ACCESS_LOG) - SSL handled by Runtipi reverse proxy Co-Authored-By: Claude Opus 4.6 --- apps/nginx/README.md | 78 ++++++++++++++++++++ apps/nginx/config.json | 28 +++++++ apps/nginx/data/conf.d/default.conf | 42 +++++++++++ apps/nginx/data/html/index.html | 110 ++++++++++++++++++++++++++++ apps/nginx/data/logs/.gitkeep | 0 apps/nginx/data/nginx.conf | 46 ++++++++++++ apps/nginx/data/ssl/.gitkeep | 0 apps/nginx/data/www/.gitkeep | 0 apps/nginx/docker-compose.json | 60 +++++++++++++++ apps/nginx/metadata/description.md | 58 +++++++++++++++ 10 files changed, 422 insertions(+) create mode 100644 apps/nginx/README.md create mode 100644 apps/nginx/config.json create mode 100644 apps/nginx/data/conf.d/default.conf create mode 100644 apps/nginx/data/html/index.html create mode 100644 apps/nginx/data/logs/.gitkeep create mode 100644 apps/nginx/data/nginx.conf create mode 100644 apps/nginx/data/ssl/.gitkeep create mode 100644 apps/nginx/data/www/.gitkeep create mode 100644 apps/nginx/docker-compose.json create mode 100644 apps/nginx/metadata/description.md diff --git a/apps/nginx/README.md b/apps/nginx/README.md new file mode 100644 index 0000000..46b5240 --- /dev/null +++ b/apps/nginx/README.md @@ -0,0 +1,78 @@ +# Nginx Custom pour Runtipi + +Application Nginx avec volumes personnalisables pour la configuration et le contenu des sites. + +## Installation + +### Option 1 : App Store personnel +1. Créez votre propre app store Runtipi +2. Copiez ce dossier `nginx-custom` dans le dossier `apps/` de votre store +3. Ajoutez votre app store dans Runtipi (Settings > App Stores) +4. Installez l'app depuis l'interface + +### Option 2 : Installation manuelle +1. Copiez le contenu de `data/` vers `runtipi/app-data//nginx-custom/` +2. Utilisez `user-config` pour personnaliser si nécessaire + +## Structure des volumes + +``` +app-data/nginx-custom/ +├── nginx.conf # Configuration principale Nginx +├── conf.d/ # Virtual hosts (*.conf) +│ └── default.conf +├── www/ # Racine pour vos sites (/var/www) +├── html/ # Dossier HTML par défaut (/usr/share/nginx/html) +├── logs/ # Logs d'accès et d'erreur +└── ssl/ # Certificats SSL (lecture seule dans le conteneur) +``` + +## Personnalisation + +### Ajouter un site + +1. Créez `conf.d/monsite.conf` : +```nginx +server { + listen 80; + server_name monsite.local; + root /var/www/monsite; + index index.html; +} +``` + +2. Créez le dossier `www/monsite/` avec votre contenu + +3. Redémarrez l'app depuis Runtipi + +### Activer SSL + +1. Placez vos certificats dans `ssl/` : + - `ssl/cert.pem` + - `ssl/key.pem` + +2. Modifiez votre configuration de site : +```nginx +server { + listen 443 ssl; + server_name monsite.local; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + + root /var/www/monsite; +} +``` + +## Variables d'environnement + +| Variable | Description | Défaut | +|----------|-------------|--------| +| `NGINX_SERVER_NAME` | Nom du serveur | localhost | +| `NGINX_INTERNAL_PORT` | Port interne | 80 | +| `TZ` | Fuseau horaire | Europe/Paris | + +## Support + +- Documentation Nginx : https://nginx.org/en/docs/ +- Documentation Runtipi : https://runtipi.io/docs/ diff --git a/apps/nginx/config.json b/apps/nginx/config.json new file mode 100644 index 0000000..262d579 --- /dev/null +++ b/apps/nginx/config.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schemas.runtipi.io/config.json", + "name": "Nginx Custom", + "id": "nginx-custom", + "available": true, + "short_desc": "Serveur web Nginx avec configuration personnalisable", + "author": "Nginx Inc.", + "port": 8080, + "exposable": true, + "dynamic_config": true, + "min_tipi_version": "4.5.0", + "version": "1.0.0", + "tipiVersion": 1, + "categories": ["utilities", "network"], + "description": "Nginx est un serveur web haute performance avec des volumes montés pour la configuration et le contenu des sites.", + "website": "https://nginx.org", + "supported_architectures": ["amd64", "arm64"], + "form_fields": [ + { + "type": "text", + "label": "Nom du serveur (server_name)", + "hint": "Ex: monsite.local ou localhost", + "required": false, + "env_variable": "NGINX_SERVER_NAME", + "default": "localhost" + } + ] +} diff --git a/apps/nginx/data/conf.d/default.conf b/apps/nginx/data/conf.d/default.conf new file mode 100644 index 0000000..618f465 --- /dev/null +++ b/apps/nginx/data/conf.d/default.conf @@ -0,0 +1,42 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + + root /usr/share/nginx/html; + index index.html index.htm; + + # Headers de securite + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + + # Rate limiting (burst de 20 requetes autorise) + limit_req zone=general burst=20 nodelay; + + location / { + try_files $uri $uri/ =404; + } + + # Desactiver l'acces aux fichiers caches + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Cache pour les fichiers statiques + # Note: on utilise uniquement "expires" ici pour ne pas ecraser + # les headers de securite du bloc server (comportement add_header de nginx) + location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|woff|woff2|ttf|svg)$ { + expires 7d; + } + + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/apps/nginx/data/html/index.html b/apps/nginx/data/html/index.html new file mode 100644 index 0000000..38ad49e --- /dev/null +++ b/apps/nginx/data/html/index.html @@ -0,0 +1,110 @@ + + + + + + Nginx Custom - Runtipi + + + +
+ +

Nginx Custom

+

Votre serveur Nginx est opérationnel !

+ +
+

📁 Emplacements des fichiers

+
    +
  • /etc/nginx/conf.d/ - Configurations des sites
  • +
  • /var/www/ - Contenu de vos sites
  • +
  • /usr/share/nginx/html/ - Dossier HTML par défaut
  • +
  • /var/log/nginx/ - Logs
  • +
+
+ +
+

🛠️ Prochaines étapes

+

Remplacez cette page par votre propre contenu dans le dossier app-data de Runtipi.

+
+ +

+ Nginx latest • Runtipi App +

+
+ + diff --git a/apps/nginx/data/logs/.gitkeep b/apps/nginx/data/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/nginx/data/nginx.conf b/apps/nginx/data/nginx.conf new file mode 100644 index 0000000..042a148 --- /dev/null +++ b/apps/nginx/data/nginx.conf @@ -0,0 +1,46 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + 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; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + keepalive_timeout 65; + + # Limiter la taille des requetes + client_max_body_size 50m; + client_body_buffer_size 16k; + + # Rate limiting (10 req/s par IP) + limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s; + limit_req_status 429; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; + + # Securite de base + server_tokens off; + + # Inclure les configurations des sites + include /etc/nginx/conf.d/*.conf; +} diff --git a/apps/nginx/data/ssl/.gitkeep b/apps/nginx/data/ssl/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/nginx/data/www/.gitkeep b/apps/nginx/data/www/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/nginx/docker-compose.json b/apps/nginx/docker-compose.json new file mode 100644 index 0000000..ffdad11 --- /dev/null +++ b/apps/nginx/docker-compose.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": 2, + "$schema": "https://schemas.runtipi.io/dynamic-compose.json", + "services": [ + { + "name": "nginx-custom", + "image": "nginx:latest", + "isMain": true, + "internalPort": 80, + "volumes": [ + { + "hostPath": "${APP_DATA_DIR}/conf.d", + "containerPath": "/etc/nginx/conf.d", + "readOnly": false + }, + { + "hostPath": "${APP_DATA_DIR}/nginx.conf", + "containerPath": "/etc/nginx/nginx.conf", + "readOnly": false + }, + { + "hostPath": "${APP_DATA_DIR}/www", + "containerPath": "/var/www", + "readOnly": false + }, + { + "hostPath": "${APP_DATA_DIR}/html", + "containerPath": "/usr/share/nginx/html", + "readOnly": false + }, + { + "hostPath": "${APP_DATA_DIR}/logs", + "containerPath": "/var/log/nginx", + "readOnly": false + }, + { + "hostPath": "${APP_DATA_DIR}/ssl", + "containerPath": "/etc/nginx/ssl", + "readOnly": true + } + ], + "environment": [ + { + "key": "NGINX_HOST", + "value": "${NGINX_SERVER_NAME:-localhost}" + }, + { + "key": "TZ", + "value": "${TZ:-Europe/Paris}" + } + ], + "healthCheck": { + "test": "curl --fail http://localhost:80 || exit 1", + "interval": "30s", + "timeout": "10s", + "retries": 3 + } + } + ] +} diff --git a/apps/nginx/metadata/description.md b/apps/nginx/metadata/description.md new file mode 100644 index 0000000..7ae7e2b --- /dev/null +++ b/apps/nginx/metadata/description.md @@ -0,0 +1,58 @@ +# Nginx Custom + +Serveur web **Nginx** avec configuration entièrement personnalisable via des volumes montés. + +## Volumes disponibles + +| Chemin dans le conteneur | Description | +|--------------------------|-------------| +| `/etc/nginx/nginx.conf` | Fichier de configuration principal | +| `/etc/nginx/conf.d/` | Configurations des virtual hosts | +| `/var/www/` | Contenu de vos sites web | +| `/usr/share/nginx/html/` | Dossier html par défaut de Nginx | +| `/var/log/nginx/` | Logs d'accès et d'erreur | +| `/etc/nginx/ssl/` | Certificats SSL (lecture seule) | + +## Utilisation + +### Accéder aux fichiers + +Les fichiers sont stockés dans le dossier `app-data` de votre installation Runtipi : + +``` +runtipi/app-data//nginx-custom/ +├── conf.d/ # Vos configurations de sites +│ └── default.conf +├── nginx.conf # Config principale +├── www/ # Vos fichiers web +├── html/ # Dossier html par défaut +├── logs/ # Logs nginx +└── ssl/ # Certificats SSL +``` + +### Ajouter un nouveau site + +1. Créez un fichier `.conf` dans `conf.d/` +2. Placez vos fichiers dans `www/monsite/` +3. Redémarrez l'application depuis Runtipi + +### Exemple de configuration + +```nginx +server { + listen 80; + server_name monsite.local; + root /var/www/monsite; + index index.html index.php; + + location / { + try_files $uri $uri/ =404; + } +} +``` + +## Notes + +- Les modifications de configuration nécessitent un redémarrage de l'app +- Pour le SSL, placez vos certificats dans le dossier `ssl/` +- Les logs sont persistés et accessibles dans `logs/`