diff --git a/config/bootstrap.php b/config/bootstrap.php index 0adc2b8..363f9c4 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -7,13 +7,17 @@ declare(strict_types=1); // Define all the important paths define('APP_ROOT', dirname(dirname(__FILE__))); +// Root-level directories define('CONFIG_DIR', APP_ROOT . '/config'); +define('PUBLIC_DIR', APP_ROOT . '/public'); define('SRC_DIR', APP_ROOT . '/src'); define('STORAGE_DIR', APP_ROOT . '/storage'); -define('TEMPLATES_DIR', APP_ROOT . '/templates'); -define('DATA_DIR', STORAGE_DIR . '/db'); -define('DB_FILE', DATA_DIR . '/tkr.sqlite'); +// Storage subdirectories define('CSS_UPLOAD_DIR', STORAGE_DIR . '/upload/css'); +define('DATA_DIR', STORAGE_DIR . '/db'); +define('TEMPLATES_DIR', APP_ROOT . '/templates'); +// Database file +define('DB_FILE', DATA_DIR . '/tkr.sqlite'); // Janky autoloader function // This is a bit more consistent with current frameworks diff --git a/docker/apache/shared-hosting/.htaccess b/docker/apache/shared-hosting/.htaccess index 9fd4083..c2bf33a 100644 --- a/docker/apache/shared-hosting/.htaccess +++ b/docker/apache/shared-hosting/.htaccess @@ -1,49 +1,19 @@ -# Example Apache VirtualHost -# for serving tkr as a subdirectory path -# on shared hosting via .htaccess -# -# e.g. http://www.my-domain.com/tkr -# -# This should work without modification if you extract the app -# to /tkr from your web document root +# Basic .htaccess for tkr on shared hosting +# For use with included docker-compose.yml # Enable mod_rewrite RewriteEngine On -# Security headers -Header always set X-Frame-Options "SAMEORIGIN" -Header always set X-XSS-Protection "1; mode=block" -Header always set X-Content-Type-Options "nosniff" - -# Directory index +# Set directory index DirectoryIndex public/index.php -# Security: Block direct access to .php files (except through rewrites) -RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] -RewriteRule ^.*$ - [R=404,L] +# Block access to sensitive directories +RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L] -# Security: Block access to sensitive directories -RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L] - -# Security: Block access to hidden files +# Block access to hidden files RewriteRule ^\..*$ - [F,L] -# Cache CSS files for 1 hour - - Header set Cache-Control "public, max-age=3600" - - -# Serve the one static file that exists: css/tkr.css -# (Pass requests to css/custom/ through to the PHP app) -RewriteCond %{REQUEST_URI} !^/css/custom/ -RewriteRule ^css/tkr\.css$ public/css/tkr.css [L] - -# 404 all other static files (images, js, fonts, etc.) -# so those requests don't hit the PHP app -# (this is to reduce load on the PHP app from bots and scanners) -RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L] - -# Everything else goes to the front controller +# Route everything else through public/index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ public/index.php [L] diff --git a/docker/apache/vps/root/tkr.my-domain.com.conf b/docker/apache/vps/root/tkr.my-domain.com.conf index e699adf..99b7290 100644 --- a/docker/apache/vps/root/tkr.my-domain.com.conf +++ b/docker/apache/vps/root/tkr.my-domain.com.conf @@ -1,19 +1,21 @@ -# Example Apache VirtualHost -# for serving tkr as a subdomain root without SSL -# e.g. http://tkr.my-domain.com/ -# -# NOTE: Do not use in production. -# This is provided for docker compose -# (The included docker-compose file will mount it in the container image) +# Basic Apache VirtualHost for tkr +# For use with included docker-compose.yml + +# HTTP - redirect to HTTPS ServerName localhost DocumentRoot /var/www/tkr/public - # Security headers - Header always set X-Frame-Options "SAMEORIGIN" - Header always set X-XSS-Protection "1; mode=block" - Header always set X-Content-Type-Options "nosniff" - Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + # Main directory - route everything through index.php + + AllowOverride None + Require all granted + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [L] + # Block access to sensitive directories @@ -22,59 +24,13 @@ Require all denied - - Require all denied - Require all denied - - # Block access to hidden files - + Require all denied - - - # Cache CSS files - - Header set Cache-Control "public, max-age=3600" - - - # Serve static CSS file - Alias /css/tkr.css /var/www/tkr/public/css/tkr.css - - # 404 all non-css static files (images, js, fonts, etc.) - # so those requests don't hit the PHP app - # (this is to reduce load on the PHP app from bots and scanners) - - - Require all denied - - - - # Enable rewrite engine - - Options -Indexes - AllowOverride None - Require all granted - - RewriteEngine On - - # Block direct PHP access - RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] - RewriteRule ^.*$ - [R=404,L] - - # Serve the one static file that exists: css/tkr.css - # (Pass requests to css/custom/ through to the PHP app) - RewriteCond %{REQUEST_URI} !^/css/custom/ - RewriteRule ^css/tkr\.css$ css/tkr.css [L] - - # Everything else to front controller - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ index.php [L] - # Error and access logs ErrorLog ${APACHE_LOG_DIR}/tkr_error.log CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined diff --git a/docker/apache/vps/subfolder/my-domain.com.conf b/docker/apache/vps/subfolder/my-domain.com.conf index b1b40d7..856d8ec 100644 --- a/docker/apache/vps/subfolder/my-domain.com.conf +++ b/docker/apache/vps/subfolder/my-domain.com.conf @@ -1,72 +1,38 @@ -# Example Apache VirtualHost -# for serving tkr as a subdirectory path without SSL -# e.g. http://www.my-domain.com/tkr -# -# NOTE: Do not use in production. -# This is provided for docker compose -# (The included docker-compose file will mount it in the container image) +# Basic Apache config for tkr in subfolder +# e.g. https://your-domain.com/tkr +# For use with included docker-compose.yml + +# Alias for tkr subfolder ServerName localhost - DocumentRoot /var/www/html - - # Security headers - Header always set X-Frame-Options "SAMEORIGIN" - Header always set X-XSS-Protection "1; mode=block" - Header always set X-Content-Type-Options "nosniff" - Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" - - # tkr Application at /tkr - # NOTE: If you change the directory name, - # remember to update all instances of /var/www/tkr in this file to match Alias /tkr /var/www/tkr/public - # Block access to sensitive TKR directories + + AllowOverride None + Require all granted + + # Front controller pattern + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ /tkr/index.php [L] + + + # Block access to sensitive directories Require all denied Require all denied - - Require all denied - Require all denied - - # 404 all non-css static files in /tkr (images, js, fonts, etc.) - # so those requests don't hit the PHP app - # (this is to reduce load on the PHP app from bots and scanners) - - - Require all denied - - - - # tkr application directory - - Options -Indexes - AllowOverride None - Require all granted - - RewriteEngine On - - # Block direct PHP access - RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] - RewriteRule ^.*$ - [R=404,L] - - # Serve the one static file that exists: css/tkr.css - # (Pass requests to css/custom/ through to the PHP app) - RewriteCond %{REQUEST_URI} !^/tkr/css/custom/ - RewriteRule ^css/tkr\.css$ css/tkr.css [L] - - # Send everything else to the front controller - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ index.php [L] + + Require all denied # Error and access logs - ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log - CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined - + ErrorLog ${APACHE_LOG_DIR}/tkr_error.log + CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined + \ No newline at end of file diff --git a/docker/nginx/root/nginx.conf b/docker/nginx/root/nginx.conf index 8d01eef..f40851f 100644 --- a/docker/nginx/root/nginx.conf +++ b/docker/nginx/root/nginx.conf @@ -1,100 +1,45 @@ -# Example nginx config -# for serving tkr as a subdomain without SSL -# e.g. http://tkr.my-domain.com/ -# -# NOTE: Do not use in production. -# This is provided for docker compose -# (The included docker-compose file will mount it in the container image) +# Basic nginx config for tkr +# Replace "your-domain.com" with your actual domain +# Replace "/var/www/tkr" with your installation path + +# HTTP - redirect to HTTPS server { - listen 80; + listen 80 default_server; server_name localhost; root /var/www/tkr/public; index index.php; - # Security headers - # The first rule is to prevent including in a frame on a different domain. - # Remove it if you want to do that. - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; + # Block access to sensitive directories + location ~ ^/(storage|src|templates|config) { + deny all; + return 404; + } - # Deny access to hidden files + # Block access to hidden files location ~ /\. { deny all; - access_log off; - log_not_found off; } - # PHP routing - everything goes through index.php - location / { - # Cache static files - # Note that I don't actually serve most of this (just css) - # but this prevents requests for static content from getting to the PHP handler. - # - # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. - # That lets me store uploaded content outside of the document root, - # so it isn't served directly. - - # CSS files - 1 hour cache - location ~* ^/(?!css/custom/).+\.css$ { - expires 1h; - add_header Cache-Control "public"; - try_files $uri =404; - } - - # Other static assets - 1 year cache - location ~* ^/.+\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # index.php is the entry point - # It needs to be sent to php-fpm - # But if someone tries to directly access index.php, that file will throw a 404 - # so bots and scanners can't tell this is a php app - location = /index.php { - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; - fastcgi_pass php:9000; - fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; - include fastcgi_params; - - fastcgi_param REQUEST_METHOD $request_method; - fastcgi_param REQUEST_URI $request_uri; - fastcgi_param QUERY_STRING $query_string; - } - - # Block attempts to access all other .php files directly - # (these are bots and scanners) - location ~ ^/.+\.php$ { - return 404; - } - - # forward other requests to the fallback block, - # which sends them to php-fpm for handling - try_files $uri $uri/ @tkr_fallback; - } - - # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php - location @tkr_fallback { - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; + # Handle PHP files + location ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; include fastcgi_params; + } + # Front controller pattern - route everything else through index.php + location / { + try_files $uri $uri/ @tkr; + } + + # Handle PHP requests + location @tkr { + fastcgi_pass php:9000; + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param REQUEST_URI $request_uri; fastcgi_param QUERY_STRING $query_string; } - - # Deny access to sensitive directories - location ~ ^/(storage|src|templates|uploads|config) { - deny all; - return 404; - } } diff --git a/docker/nginx/subfolder/nginx.conf b/docker/nginx/subfolder/nginx.conf index b6f8d84..b381910 100644 --- a/docker/nginx/subfolder/nginx.conf +++ b/docker/nginx/subfolder/nginx.conf @@ -1,101 +1,47 @@ -# Example nginx config -# for serving tkr as a subdfolder without SSL -# e.g. http://my-domain.com/tkr -# -# NOTE: Do not use in production. -# This is provided for docker compose -# (The included docker-compose file will mount it in the container image) +# Basic nginx config for tkr in subfolder +# e.g. https://your-domain.com/tkr +# For use with included docker-compose.yml + server { listen 80 default_server; - listen [::]:80 default_server; - - # replace localhost with your subdomain - # e.g. tkr.my-domain.com server_name localhost; root /var/www/html; index index.html; - # Security headers - # The first rule is to prevent including in a frame on a different domain. - # Remove it if you want to do that. - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - - # Deny access to hidden files - location ~ /\. { - deny all; - access_log off; - log_not_found off; - } - - # PHP routing - everything under /tkr goes through index.php location /tkr { alias /var/www/tkr/public; index index.php; - # Cache static files - # Note that I don't actually serve most of this (just css) - # but this prevents requests for static content from getting to the PHP handler. - # - # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. - # That lets me store uploaded content outside of the document root, - # so it isn't served directly. - - # CSS files - 1 hour cache - location ~* ^/tkr/(?!css/custom/).+\.css$ { - expires 1h; - add_header Cache-Control "public"; - try_files $uri =404; - } - - # Other static assets - 1 year cache - location ~* ^/tkr/.+\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # index.php is the entry point - # It needs to be sent to php-fpm - # But if someone tries to directly access index.php, that file will throw a 404 - # so bots and scanners can't tell this is a php app - location = /tkr/index.php { - fastcgi_pass php:9000; - fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; - include fastcgi_params; - - fastcgi_param REQUEST_METHOD $request_method; - fastcgi_param REQUEST_URI $request_uri; - fastcgi_param QUERY_STRING $query_string; - } - - # Block attempts to access all other .php files directly - # (these are bots and scanners) - location ~ ^/tkr/.+\.php$ { + # Block access to sensitive directories + location ~ ^/tkr/(storage|src|templates|config) { + deny all; return 404; } - # forward other requests to the fallback block, - # which sends them to php-fpm for handling - try_files $uri $uri/ @tkr_fallback; + # Block access to hidden files + location ~ /\. { + deny all; + } + + # Handle PHP files + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + } + + # Front controller pattern + # Send everything else to index.php + try_files $uri $uri/ @tkr; } - # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php - location @tkr_fallback { + location @tkr { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; include fastcgi_params; - fastcgi_param REQUEST_METHOD $request_method; fastcgi_param REQUEST_URI $request_uri; fastcgi_param QUERY_STRING $query_string; } - - # Deny access to sensitive directories - location ~ ^/tkr/(storage|src|templates|uploads|config) { - deny all; - return 404; - } } \ No newline at end of file diff --git a/examples/apache/shared-hosting/.htaccess b/examples/apache/shared-hosting/.htaccess index 9fd4083..65e699e 100644 --- a/examples/apache/shared-hosting/.htaccess +++ b/examples/apache/shared-hosting/.htaccess @@ -1,49 +1,20 @@ -# Example Apache VirtualHost -# for serving tkr as a subdirectory path -# on shared hosting via .htaccess -# -# e.g. http://www.my-domain.com/tkr -# -# This should work without modification if you extract the app -# to /tkr from your web document root +# Basic .htaccess for tkr on shared hosting +# Place this file inside the tkr directory +# e.g. if tkr is at /public_html/tkr/, this goes in /public_html/tkr/ # Enable mod_rewrite RewriteEngine On -# Security headers -Header always set X-Frame-Options "SAMEORIGIN" -Header always set X-XSS-Protection "1; mode=block" -Header always set X-Content-Type-Options "nosniff" - -# Directory index +# Set directory index DirectoryIndex public/index.php -# Security: Block direct access to .php files (except through rewrites) -RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] -RewriteRule ^.*$ - [R=404,L] +# Block access to sensitive directories +RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L] -# Security: Block access to sensitive directories -RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L] - -# Security: Block access to hidden files +# Block access to hidden files RewriteRule ^\..*$ - [F,L] -# Cache CSS files for 1 hour - - Header set Cache-Control "public, max-age=3600" - - -# Serve the one static file that exists: css/tkr.css -# (Pass requests to css/custom/ through to the PHP app) -RewriteCond %{REQUEST_URI} !^/css/custom/ -RewriteRule ^css/tkr\.css$ public/css/tkr.css [L] - -# 404 all other static files (images, js, fonts, etc.) -# so those requests don't hit the PHP app -# (this is to reduce load on the PHP app from bots and scanners) -RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L] - -# Everything else goes to the front controller +# Route everything else through the front controller RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ public/index.php [L] diff --git a/examples/apache/vps/root/tkr.my-domain.com.conf b/examples/apache/vps/root/tkr.my-domain.com.conf index 753a7af..9da6844 100644 --- a/examples/apache/vps/root/tkr.my-domain.com.conf +++ b/examples/apache/vps/root/tkr.my-domain.com.conf @@ -1,39 +1,34 @@ -# Example Apache VirtualHost -# for serving tkr as a subdomain root with SSL -# e.g. https://tkr.my-domain.com/ -# -# Use SSL in production. -# This is a minimal SSL confiuration -# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ +# Basic Apache VirtualHost for tkr +# Replace "your-domain.com" with your actual domain +# Replace "/var/www/tkr" with your installation path + +# HTTP - redirect to HTTPS - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com - ServerName localhost - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com - DocumentRoot /var/www/tkr/public - # Redirect HTTP to HTTPS - Redirect permanent / https://tkr.my-domain.com/ + ServerName your-domain.com + Redirect permanent / https://your-domain.com/ +# HTTPS - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com - ServerName localhost - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + ServerName your-domain.com DocumentRoot /var/www/tkr/public - - # SSL Configuration + + # SSL Configuration (using Let's Encrypt) SSLEngine on - - # Assumes you're using letsencrypt for cert generation - # Replace with the actual paths to your cert and key - SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem - SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem - - # Security headers - Header always set X-Frame-Options "SAMEORIGIN" - Header always set X-XSS-Protection "1; mode=block" - Header always set X-Content-Type-Options "nosniff" - Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" - + SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem + + # Main directory - route everything through index.php + + AllowOverride None + Require all granted + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [L] + + # Block access to sensitive directories Require all denied @@ -41,59 +36,13 @@ Require all denied - - Require all denied - Require all denied - - # Block access to hidden files - + Require all denied - - - # Cache CSS files - - Header set Cache-Control "public, max-age=3600" - - - # Serve static CSS file - Alias /css/tkr.css /var/www/tkr/public/css/tkr.css - - # 404 all non-css static files (images, js, fonts, etc.) - # so those requests don't hit the PHP app - # (this is to reduce load on the PHP app from bots and scanners) - - - Require all denied - - - - # Enable rewrite engine - - Options -Indexes - AllowOverride None - Require all granted - - RewriteEngine On - - # Block direct PHP access - RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] - RewriteRule ^.*$ - [R=404,L] - - # Serve the one static file that exists: css/tkr.css - # (Pass requests to css/custom/ through to the PHP app) - RewriteCond %{REQUEST_URI} !^/css/custom/ - RewriteRule ^css/tkr\.css$ css/tkr.css [L] - - # Everything else to front controller - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ index.php [L] - - # Error and access logs + ErrorLog ${APACHE_LOG_DIR}/tkr_error.log CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined diff --git a/examples/apache/vps/subfolder/my-domain.com.conf b/examples/apache/vps/subfolder/my-domain.com.conf index b2d42ea..8d14e94 100644 --- a/examples/apache/vps/subfolder/my-domain.com.conf +++ b/examples/apache/vps/subfolder/my-domain.com.conf @@ -1,91 +1,31 @@ -# Example Apache VirtualHost -# for serving tkr as a subdirectory path with SSL -# e.g. https://www.my-domain.com/tkr -# -# Use SSL in production. -# This is a minimal SSL confiuration -# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ - - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com - ServerName localhost - # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com - DocumentRoot /var/www/tkr - # Redirect HTTP to HTTPS - Redirect permanent / https://my-domain.com/ - +# Basic Apache config for tkr in subfolder +# e.g. https://your-domain.com/tkr +# Add this to your existing VirtualHost configuration - - # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com - ServerName localhost - # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com - DocumentRoot /var/www/tkr/ +# Alias for tkr subfolder +Alias /tkr /var/www/tkr/public - # SSL Configuration - SSLEngine on + + AllowOverride None + Require all granted + + # Front controller pattern + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ /tkr/index.php [L] + - # Assumes you're using letsencrypt for cert generation - # Replace with the actual paths to your cert and key - SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem - SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem - - # Security headers - Header always set X-Frame-Options "SAMEORIGIN" - Header always set X-XSS-Protection "1; mode=block" - Header always set X-Content-Type-Options "nosniff" - Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" - - # tkr Application at /tkr - # NOTE: If you change the directory name, - # remember to update all instances of /var/www/tkr in this file to match - Alias /tkr /var/www/tkr/public - - # Block access to sensitive TKR directories - - Require all denied - - - Require all denied - - - Require all denied - - - Require all denied - - - # 404 all non-css static files in /tkr (images, js, fonts, etc.) - # so those requests don't hit the PHP app - # (this is to reduce load on the PHP app from bots and scanners) - - - Require all denied - - - - # tkr application directory - - Options -Indexes - AllowOverride None - Require all granted - - RewriteEngine On - - # Block direct PHP access - RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] - RewriteRule ^.*$ - [R=404,L] - - # Serve the one static file that exists: css/tkr.css - # (Pass requests to css/custom/ through to the PHP app) - RewriteCond %{REQUEST_URI} !^/tkr/css/custom/ - RewriteRule ^css/tkr\.css$ css/tkr.css [L] - - # Send everything else to the front controller - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ index.php [L] - - - # Error and access logs - ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log - CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined - +# Block access to sensitive directories + + Require all denied + + + Require all denied + + + Require all denied + + + Require all denied + \ No newline at end of file diff --git a/examples/nginx/root/nginx.conf b/examples/nginx/root/nginx.conf index c7855a1..717efa1 100644 --- a/examples/nginx/root/nginx.conf +++ b/examples/nginx/root/nginx.conf @@ -1,117 +1,51 @@ -# Example nginx config -# for serving tkr as a subdomain with SSL -# e.g. https://tkr.my-domain.com/ -# -# Use SSL in production. -# This is a minimal SSL confiuration -# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ +# Basic nginx config for tkr +# Replace "your-domain.com" with your actual domain +# Replace "/var/www/tkr" with your installation path + +# HTTP - redirect to HTTPS +server { + listen 80; + listen [::]:80; + server_name your-domain.com; + return 301 https://$host$request_uri; +} + +# HTTPS server { listen 443 ssl; listen [::]:443 ssl; - - # CONFIG: replace "localhost" with your subdomain (e.g. tkr.my-domain.com) - server_name localhost; - - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + + server_name your-domain.com; root /var/www/tkr/public; index index.php; - - # CONFIG: - # Assumes you're using letsencrypt for cert generation - # Replace with the actual paths to your cert and key - ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem; - - # Security headers - # The first rule is to prevent including in a frame on a different domain. - # Remove it if you want to do that. - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - - # Deny access to hidden files + + # SSL Configuration (using Let's Encrypt) + ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; + + # Block access to sensitive directories + location ~ ^/(storage|src|templates|config) { + deny all; + return 404; + } + + # Block access to hidden files location ~ /\. { deny all; - access_log off; - log_not_found off; } - - # PHP routing - everything goes through index.php + + # Front controller pattern - route everything through index.php location / { - # Cache static files - # Note that I don't actually serve most of this (just css) - # but this prevents requests for static content from getting to the PHP handler. - # - # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. - # That lets me store uploaded content outside of the document root, - # so it isn't served directly. - location ~* ^/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # index.php is the entry point - # It needs to be sent to php-fpm - # But if someone tries to directly access index.php, that file will throw a 404 - # so bots and scanners can't tell this is a php app - location = /index.php { - # CONFIG: - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; - fastcgi_pass php:9000; - - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) - fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; - include fastcgi_params; - - fastcgi_param REQUEST_METHOD $request_method; - fastcgi_param REQUEST_URI $request_uri; - fastcgi_param QUERY_STRING $query_string; - } - - # Block attempts to access all other .php files directly - # (these are bots and scanners) - location ~ ^/.+\.php$ { - return 404; - } - - # forward other requests to the fallback block, - # which sends them to php-fpm for handling - try_files $uri $uri/ @tkr_fallback; + try_files $uri $uri/ @tkr; } - - # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php - location @tkr_fallback { - # CONFIG: - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; - fastcgi_pass php:9000; - - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + + # Handle PHP requests + location @tkr { + fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; include fastcgi_params; - fastcgi_param REQUEST_METHOD $request_method; fastcgi_param REQUEST_URI $request_uri; fastcgi_param QUERY_STRING $query_string; } - - # Deny access to sensitive directories - location ~ ^/(storage|src|templates|uploads|config) { - deny all; - return 404; - } -} - -server { - listen 80 default_server; - listen [::]:80 default_server; - - return 301 https://$host$request_uri; } diff --git a/examples/nginx/subfolder/nginx.conf b/examples/nginx/subfolder/nginx.conf index 3095e2b..1fb18b8 100644 --- a/examples/nginx/subfolder/nginx.conf +++ b/examples/nginx/subfolder/nginx.conf @@ -1,118 +1,38 @@ -# Example nginx config -# for serving tkr as a subdfolder with SSL -# e.g. https://my-domain.com/tkr -# -# Use SSL in production. -# This is a minimal SSL confiuration -# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ -server { - listen 443 ssl; - listen [::]:443 ssl; +# Basic nginx config for tkr in subfolder +# e.g. https://your-domain.com/tkr +# Add this location block to your existing server configuration - # CONFIG: Replace localhost with your domain e.g. my-domain.com - server_name localhost; +location /tkr { + alias /var/www/tkr/public; + index index.php; - # CONFIG: - # Assumes you're using letsencrypt for cert generation - # Replace with the actual paths to your cert and key - ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem; - - # Security headers - # The first rule is to prevent including in a frame on a different domain. - # Remove it if you want to do that. - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - - # Deny access to hidden files - location ~ /\. { - deny all; - access_log off; - log_not_found off; - } - - # PHP routing - everything under /tkr goes through index.php - location /tkr { - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) - alias /var/www/tkr/public; - index index.php; - - # Cache static files - # Note that I don't actually serve most of this (just css) - # but this prevents requests for static content from getting to the PHP handler. - # - # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. - # That lets me store uploaded content outside of the document root, - # so it isn't served directly. - location ~* ^/tkr/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # index.php is the entry point - # It needs to be sent to php-fpm - # But if someone tries to directly access index.php, that file will throw a 404 - # so bots and scanners can't tell this is a php app - location = /tkr/index.php { - # CONFIG: - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; - fastcgi_pass php:9000; - - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) - fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; - include fastcgi_params; - - fastcgi_param REQUEST_METHOD $request_method; - fastcgi_param REQUEST_URI $request_uri; - fastcgi_param QUERY_STRING $query_string; - } - - # Block attempts to access all other .php files directly - # (these are bots and scanners) - location ~ ^/tkr/.+\.php$ { - return 404; - } - - # forward other requests to the fallback block, - # which sends them to php-fpm for handling - try_files $uri $uri/ @tkr_fallback; - } - - # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php - location @tkr_fallback { - # CONFIG: - # If you're running php-fpm on the same server as nginx, - # then change this to the local php-fpm socket - # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; - fastcgi_pass php:9000; - - # CONFIG: - # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) - fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + # Handle PHP files + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version/socket as needed fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; include fastcgi_params; - - fastcgi_param REQUEST_METHOD $request_method; - fastcgi_param REQUEST_URI $request_uri; - fastcgi_param QUERY_STRING $query_string; } - # Deny access to sensitive directories - location ~ ^/tkr/(storage|src|templates|uploads|config) { + # Block access to sensitive directories + location ~ ^/tkr/(storage|src|templates|config) { deny all; return 404; } + + # Block access to hidden files + location ~ /\. { + deny all; + } + + # Front controller pattern + try_files $uri $uri/ @tkr; } -server { - listen 80 default_server; - listen [::]:80 default_server; - - return 301 https://$host$request_uri; -} +location @tkr { + fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param QUERY_STRING $query_string; +} \ No newline at end of file diff --git a/examples/production/apache/.htaccess b/examples/production/apache/.htaccess new file mode 100644 index 0000000..c1637c0 --- /dev/null +++ b/examples/production/apache/.htaccess @@ -0,0 +1,49 @@ +# Example Apache VirtualHost +# for serving tkr as a subdirectory path +# on shared hosting via .htaccess +# +# e.g. http://www.my-domain.com/tkr +# +# This should work without modification if you extract the app +# to /tkr from your web document root + +# Enable mod_rewrite +RewriteEngine On + +# Security headers +Header always set X-Frame-Options "SAMEORIGIN" +Header always set X-XSS-Protection "1; mode=block" +Header always set X-Content-Type-Options "nosniff" + +# Directory index +DirectoryIndex public/index.php + +# Security: Block direct access to .php files (except through rewrites) +RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] +RewriteRule ^.*$ - [R=404,L] + +# Security: Block access to sensitive directories +RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L] + +# Security: Block access to hidden files +RewriteRule ^\..*$ - [F,L] + +# Cache CSS files for 1 hour + + Header set Cache-Control "public, max-age=3600" + + +# Serve the one static file that exists: css/default.css +# (Pass requests to css/custom/ through to the PHP app) +RewriteCond %{REQUEST_URI} !^/css/custom/ +RewriteRule ^css/default\.css$ public/css/default.css [L] + +# 404 all other static files (images, js, fonts, etc.) +# so those requests don't hit the PHP app +# (this is to reduce load on the PHP app from bots and scanners) +RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L] + +# Everything else goes to the front controller +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ public/index.php [L] diff --git a/examples/production/apache/subfolder.conf b/examples/production/apache/subfolder.conf new file mode 100644 index 0000000..e3fb161 --- /dev/null +++ b/examples/production/apache/subfolder.conf @@ -0,0 +1,91 @@ +# Example Apache VirtualHost +# for serving tkr as a subdirectory path with SSL +# e.g. https://www.my-domain.com/tkr +# +# Use SSL in production. +# This is a minimal SSL confiuration +# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ + + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + ServerName localhost + # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com + DocumentRoot /var/www/tkr + # Redirect HTTP to HTTPS + Redirect permanent / https://my-domain.com/ + + + + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + ServerName localhost + # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com + DocumentRoot /var/www/tkr/ + + # SSL Configuration + SSLEngine on + + # Assumes you're using letsencrypt for cert generation + # Replace with the actual paths to your cert and key + SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem + + # Security headers + Header always set X-Frame-Options "SAMEORIGIN" + Header always set X-XSS-Protection "1; mode=block" + Header always set X-Content-Type-Options "nosniff" + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + # tkr Application at /tkr + # NOTE: If you change the directory name, + # remember to update all instances of /var/www/tkr in this file to match + Alias /tkr /var/www/tkr/public + + # Block access to sensitive TKR directories + + Require all denied + + + Require all denied + + + Require all denied + + + Require all denied + + + # 404 all non-css static files in /tkr (images, js, fonts, etc.) + # so those requests don't hit the PHP app + # (this is to reduce load on the PHP app from bots and scanners) + + + Require all denied + + + + # tkr application directory + + Options -Indexes + AllowOverride None + Require all granted + + RewriteEngine On + + # Block direct PHP access + RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] + RewriteRule ^.*$ - [R=404,L] + + # Serve the one static file that exists: css/default.css + # (Pass requests to css/custom/ through to the PHP app) + RewriteCond %{REQUEST_URI} !^/tkr/css/custom/ + RewriteRule ^css/default\.css$ css/tkr.css [L] + + # Send everything else to the front controller + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [L] + + + # Error and access logs + ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log + CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined + diff --git a/examples/production/apache/tkr.my-domain.com.conf b/examples/production/apache/tkr.my-domain.com.conf new file mode 100644 index 0000000..3f64ec6 --- /dev/null +++ b/examples/production/apache/tkr.my-domain.com.conf @@ -0,0 +1,99 @@ +# Example Apache VirtualHost +# for serving tkr as a subdomain root with SSL +# e.g. https://tkr.my-domain.com/ +# +# Use SSL in production. +# This is a minimal SSL confiuration +# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ + + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + ServerName localhost + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + DocumentRoot /var/www/tkr/public + # Redirect HTTP to HTTPS + Redirect permanent / https://tkr.my-domain.com/ + + + + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + ServerName localhost + # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com + DocumentRoot /var/www/tkr/public + + # SSL Configuration + SSLEngine on + + # Assumes you're using letsencrypt for cert generation + # Replace with the actual paths to your cert and key + SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem + + # Security headers + Header always set X-Frame-Options "SAMEORIGIN" + Header always set X-XSS-Protection "1; mode=block" + Header always set X-Content-Type-Options "nosniff" + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + # Block access to sensitive directories + + Require all denied + + + Require all denied + + + Require all denied + + + Require all denied + + + # Block access to hidden files + + Require all denied + + + # Cache CSS files + + Header set Cache-Control "public, max-age=3600" + + + # Serve static CSS file + Alias /css/tkr.css /var/www/tkr/public/css/tkr.css + + # 404 all non-css static files (images, js, fonts, etc.) + # so those requests don't hit the PHP app + # (this is to reduce load on the PHP app from bots and scanners) + + + Require all denied + + + + # Enable rewrite engine + + Options -Indexes + AllowOverride None + Require all granted + + RewriteEngine On + + # Block direct PHP access + RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] + RewriteRule ^.*$ - [R=404,L] + + # Serve the one static file that exists: css/default.css + # (Pass requests to css/custom/ through to the PHP app) + RewriteCond %{REQUEST_URI} !^/css/custom/ + RewriteRule ^css/default\.css$ css/default.css [L] + + # Everything else to front controller + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php [L] + + + # Error and access logs + ErrorLog ${APACHE_LOG_DIR}/tkr_error.log + CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined + diff --git a/examples/production/nginx/nginx.conf b/examples/production/nginx/nginx.conf new file mode 100644 index 0000000..c7855a1 --- /dev/null +++ b/examples/production/nginx/nginx.conf @@ -0,0 +1,117 @@ +# Example nginx config +# for serving tkr as a subdomain with SSL +# e.g. https://tkr.my-domain.com/ +# +# Use SSL in production. +# This is a minimal SSL confiuration +# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ +server { + listen 443 ssl; + listen [::]:443 ssl; + + # CONFIG: replace "localhost" with your subdomain (e.g. tkr.my-domain.com) + server_name localhost; + + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + root /var/www/tkr/public; + index index.php; + + # CONFIG: + # Assumes you're using letsencrypt for cert generation + # Replace with the actual paths to your cert and key + ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem; + + # Security headers + # The first rule is to prevent including in a frame on a different domain. + # Remove it if you want to do that. + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + + # Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # PHP routing - everything goes through index.php + location / { + # Cache static files + # Note that I don't actually serve most of this (just css) + # but this prevents requests for static content from getting to the PHP handler. + # + # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. + # That lets me store uploaded content outside of the document root, + # so it isn't served directly. + location ~* ^/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # index.php is the entry point + # It needs to be sent to php-fpm + # But if someone tries to directly access index.php, that file will throw a 404 + # so bots and scanners can't tell this is a php app + location = /index.php { + # CONFIG: + # If you're running php-fpm on the same server as nginx, + # then change this to the local php-fpm socket + # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; + fastcgi_pass php:9000; + + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param QUERY_STRING $query_string; + } + + # Block attempts to access all other .php files directly + # (these are bots and scanners) + location ~ ^/.+\.php$ { + return 404; + } + + # forward other requests to the fallback block, + # which sends them to php-fpm for handling + try_files $uri $uri/ @tkr_fallback; + } + + # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php + location @tkr_fallback { + # CONFIG: + # If you're running php-fpm on the same server as nginx, + # then change this to the local php-fpm socket + # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; + fastcgi_pass php:9000; + + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param QUERY_STRING $query_string; + } + + # Deny access to sensitive directories + location ~ ^/(storage|src|templates|uploads|config) { + deny all; + return 404; + } +} + +server { + listen 80 default_server; + listen [::]:80 default_server; + + return 301 https://$host$request_uri; +} diff --git a/examples/production/nginx/subfolder.conf b/examples/production/nginx/subfolder.conf new file mode 100644 index 0000000..3095e2b --- /dev/null +++ b/examples/production/nginx/subfolder.conf @@ -0,0 +1,118 @@ +# Example nginx config +# for serving tkr as a subdfolder with SSL +# e.g. https://my-domain.com/tkr +# +# Use SSL in production. +# This is a minimal SSL confiuration +# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ +server { + listen 443 ssl; + listen [::]:443 ssl; + + # CONFIG: Replace localhost with your domain e.g. my-domain.com + server_name localhost; + + # CONFIG: + # Assumes you're using letsencrypt for cert generation + # Replace with the actual paths to your cert and key + ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem; + + # Security headers + # The first rule is to prevent including in a frame on a different domain. + # Remove it if you want to do that. + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + + # Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # PHP routing - everything under /tkr goes through index.php + location /tkr { + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + alias /var/www/tkr/public; + index index.php; + + # Cache static files + # Note that I don't actually serve most of this (just css) + # but this prevents requests for static content from getting to the PHP handler. + # + # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app. + # That lets me store uploaded content outside of the document root, + # so it isn't served directly. + location ~* ^/tkr/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # index.php is the entry point + # It needs to be sent to php-fpm + # But if someone tries to directly access index.php, that file will throw a 404 + # so bots and scanners can't tell this is a php app + location = /tkr/index.php { + # CONFIG: + # If you're running php-fpm on the same server as nginx, + # then change this to the local php-fpm socket + # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; + fastcgi_pass php:9000; + + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param QUERY_STRING $query_string; + } + + # Block attempts to access all other .php files directly + # (these are bots and scanners) + location ~ ^/tkr/.+\.php$ { + return 404; + } + + # forward other requests to the fallback block, + # which sends them to php-fpm for handling + try_files $uri $uri/ @tkr_fallback; + } + + # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php + location @tkr_fallback { + # CONFIG: + # If you're running php-fpm on the same server as nginx, + # then change this to the local php-fpm socket + # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock; + fastcgi_pass php:9000; + + # CONFIG: + # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different) + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; + include fastcgi_params; + + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param QUERY_STRING $query_string; + } + + # Deny access to sensitive directories + location ~ ^/tkr/(storage|src|templates|uploads|config) { + deny all; + return 404; + } +} + +server { + listen 80 default_server; + listen [::]:80 default_server; + + return 301 https://$host$request_uri; +} diff --git a/src/Controller/AdminController/AdminController.php b/src/Controller/AdminController/AdminController.php index 72ada00..91d0fad 100644 --- a/src/Controller/AdminController/AdminController.php +++ b/src/Controller/AdminController/AdminController.php @@ -50,7 +50,7 @@ class AdminController extends Controller { } $result = $this->saveSettings($_POST, false); - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } @@ -58,7 +58,7 @@ class AdminController extends Controller { // for setup, we don't care if they're logged in // (because they can't be until setup is complete) $result = $this->saveSettings($_POST, true); - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } diff --git a/src/Controller/AuthController/AuthController.php b/src/Controller/AuthController/AuthController.php index 83644ce..58942cb 100644 --- a/src/Controller/AuthController/AuthController.php +++ b/src/Controller/AuthController/AuthController.php @@ -39,7 +39,7 @@ class AuthController extends Controller { } catch (Exception $e) { Log::error("Failed to create login session for {$username}: " . $e->getMessage()); Session::setFlashMessage('error', 'Login failed - session error'); - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } } else { @@ -47,13 +47,13 @@ class AuthController extends Controller { // Set a flash message and reload the login page Session::setFlashMessage('error', 'Invalid username or password'); - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } } catch (Exception $e) { Log::error("Database error during login for {$username}: " . $e->getMessage()); Session::setFlashMessage('error', 'Login temporarily unavailable'); - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } } diff --git a/src/Controller/CssController/CssController.php b/src/Controller/CssController/CssController.php index 090ffee..cbe8483 100644 --- a/src/Controller/CssController/CssController.php +++ b/src/Controller/CssController/CssController.php @@ -20,32 +20,54 @@ class CssController extends Controller { global $app; $cssModel = new CssModel($app['db']); $filename = "$baseFilename.css"; + Log::debug("Attempting to serve custom css: {$filename}"); + // Make sure the file exists in the database $cssRow = $cssModel->getByFilename($filename); - if (!$cssRow){ http_response_code(404); - exit("CSS file not found: $filename"); + Log::error("Custom css file not in database: {$filename}"); + exit; } + // Make sure the file exists on the filesystem and is readable $filePath = CSS_UPLOAD_DIR . "/$filename"; - if (!file_exists($filePath) || !is_readable($filePath)) { http_response_code(404); - exit("CSS file not found: $filePath"); + Log::error("Custom css file not found or not readable: $filePath"); + exit; } - // This shouldn't be possible, but I'm being extra paranoid - // about user input + // Make sure the file has a .css extension $ext = strToLower(pathinfo($filename, PATHINFO_EXTENSION)); if($ext != 'css'){ http_response_code(400); - exit("Invalid file type requested: $ext"); + Log::error("Invalid file type requested: $ext"); + exit; } + // If we get here, serve the file + Log::debug("Serving custom css: {$filename}"); header('Content-type: text/css'); header('Cache-control: public, max-age=3600'); + readfile($filePath); + exit; + } + public function serveDefaultCss(){ + $filePath = PUBLIC_DIR . '/css/default.css'; + Log::debug("Serving default css: {$filePath}"); + + // Make sure the default CSS file exists and is readable + if (!file_exists($filePath) || !is_readable($filePath)) { + http_response_code(404); + Log::error("Default CSS file not found"); + exit; + } + + // Serve the file + header('Content-type: text/css'); + header('Cache-control: public, max-age=3600'); readfile($filePath); exit; } @@ -64,7 +86,7 @@ class CssController extends Controller { } // redirect after handling to avoid resubmitting form - header('Location: ' . $_SERVER['PHP_SELF']); + header('Location: ' . $_SERVER['REQUEST_URI']); exit; } @@ -129,7 +151,7 @@ class CssController extends Controller { try { if ($_POST['selectCssFile']){ // Set custom theme - $app['settings']->cssId = $_POST['selectCssFile']; + $app['settings']->cssId = (int)($_POST['selectCssFile']); } else { // Set default theme $app['settings']->cssId = null; diff --git a/src/Framework/Router/Router.php b/src/Framework/Router/Router.php index 66f5f29..a5d37ce 100644 --- a/src/Framework/Router/Router.php +++ b/src/Framework/Router/Router.php @@ -27,6 +27,7 @@ class Router { ['tick/{id}', 'TickController'], ['tick/{id}/delete', 'TickController@handleDelete', ['POST']], ['css/custom/{filename}.css', 'CssController@serveCustomCss'], + ['css/default.css', 'CssController@serveDefaultCss'], ];