simplify web server configs (#65)

Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/65
Co-authored-by: Greg Sarjeant <greg@subcultureofone.org>
Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
Greg Sarjeant 2025-08-10 22:13:45 +00:00 committed by greg
parent 195de75b78
commit 86abf587f6
20 changed files with 734 additions and 736 deletions

View File

@ -7,13 +7,17 @@ declare(strict_types=1);
// Define all the important paths // Define all the important paths
define('APP_ROOT', dirname(dirname(__FILE__))); define('APP_ROOT', dirname(dirname(__FILE__)));
// Root-level directories
define('CONFIG_DIR', APP_ROOT . '/config'); define('CONFIG_DIR', APP_ROOT . '/config');
define('PUBLIC_DIR', APP_ROOT . '/public');
define('SRC_DIR', APP_ROOT . '/src'); define('SRC_DIR', APP_ROOT . '/src');
define('STORAGE_DIR', APP_ROOT . '/storage'); define('STORAGE_DIR', APP_ROOT . '/storage');
define('TEMPLATES_DIR', APP_ROOT . '/templates'); // Storage subdirectories
define('DATA_DIR', STORAGE_DIR . '/db');
define('DB_FILE', DATA_DIR . '/tkr.sqlite');
define('CSS_UPLOAD_DIR', STORAGE_DIR . '/upload/css'); 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 // Janky autoloader function
// This is a bit more consistent with current frameworks // This is a bit more consistent with current frameworks

View File

@ -1,49 +1,19 @@
# Example Apache VirtualHost # Basic .htaccess for tkr on shared hosting
# for serving tkr as a subdirectory path # For use with included docker-compose.yml
# 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 # Enable mod_rewrite
RewriteEngine On RewriteEngine On
# Security headers # Set directory index
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 DirectoryIndex public/index.php
# Security: Block direct access to .php files (except through rewrites) # Block access to sensitive directories
RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L]
RewriteRule ^.*$ - [R=404,L]
# Security: Block access to sensitive directories # Block access to hidden files
RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
# Security: Block access to hidden files
RewriteRule ^\..*$ - [F,L] RewriteRule ^\..*$ - [F,L]
# Cache CSS files for 1 hour # Route everything else through public/index.php
<FilesMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</FilesMatch>
# 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
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ public/index.php [L] RewriteRule ^(.*)$ public/index.php [L]

View File

@ -1,19 +1,21 @@
# Example Apache VirtualHost # Basic Apache VirtualHost for tkr
# for serving tkr as a subdomain root without SSL # For use with included docker-compose.yml
# e.g. http://tkr.my-domain.com/
# # HTTP - redirect to HTTPS
# NOTE: Do not use in production.
# This is provided for docker compose
# (The included docker-compose file will mount it in the container image)
<VirtualHost *:80> <VirtualHost *:80>
ServerName localhost ServerName localhost
DocumentRoot /var/www/tkr/public DocumentRoot /var/www/tkr/public
# Security headers # Main directory - route everything through index.php
Header always set X-Frame-Options "SAMEORIGIN" <Directory "/var/www/tkr/public">
Header always set X-XSS-Protection "1; mode=block" AllowOverride None
Header always set X-Content-Type-Options "nosniff" Require all granted
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L]
</Directory>
# Block access to sensitive directories # Block access to sensitive directories
<Directory "/var/www/tkr/storage"> <Directory "/var/www/tkr/storage">
@ -22,59 +24,13 @@
<Directory "/var/www/tkr/src"> <Directory "/var/www/tkr/src">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config"> <Directory "/var/www/tkr/config">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
# Block access to hidden files
<DirectoryMatch "^\.|/\.">
Require all denied Require all denied
</DirectoryMatch>
# Cache CSS files
<LocationMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</LocationMatch>
# 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)
<LocationMatch "\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# Enable rewrite engine
<Directory "/var/www/tkr/public">
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]
</Directory> </Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/tkr_error.log ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
</VirtualHost> </VirtualHost>

View File

@ -1,72 +1,38 @@
# Example Apache VirtualHost # Basic Apache config for tkr in subfolder
# for serving tkr as a subdirectory path without SSL # e.g. https://your-domain.com/tkr
# e.g. http://www.my-domain.com/tkr # For use with included docker-compose.yml
#
# NOTE: Do not use in production. # Alias for tkr subfolder
# This is provided for docker compose
# (The included docker-compose file will mount it in the container image)
<VirtualHost *:80> <VirtualHost *:80>
ServerName localhost 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 Alias /tkr /var/www/tkr/public
# Block access to sensitive TKR directories <Directory "/var/www/tkr/public">
AllowOverride None
Require all granted
# Front controller pattern
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /tkr/index.php [L]
</Directory>
# Block access to sensitive directories
<Directory "/var/www/tkr/storage"> <Directory "/var/www/tkr/storage">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/src"> <Directory "/var/www/tkr/src">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config"> <Directory "/var/www/tkr/config">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
# 404 all non-css static files in /tkr (images, js, fonts, etc.) Require all denied
# so those requests don't hit the PHP app
# (this is to reduce load on the PHP app from bots and scanners)
<LocationMatch "^/tkr/.*\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# tkr application directory
<Directory "/var/www/tkr/public">
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]
</Directory> </Directory>
# Error and access logs # Error and access logs
ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
</VirtualHost> </VirtualHost>

View File

@ -1,100 +1,45 @@
# Example nginx config # Basic nginx config for tkr
# for serving tkr as a subdomain without SSL # Replace "your-domain.com" with your actual domain
# e.g. http://tkr.my-domain.com/ # Replace "/var/www/tkr" with your installation path
#
# NOTE: Do not use in production. # HTTP - redirect to HTTPS
# This is provided for docker compose
# (The included docker-compose file will mount it in the container image)
server { server {
listen 80; listen 80 default_server;
server_name localhost; server_name localhost;
root /var/www/tkr/public; root /var/www/tkr/public;
index index.php; index index.php;
# Security headers # Block access to sensitive directories
# The first rule is to prevent including in a frame on a different domain. location ~ ^/(storage|src|templates|config) {
# Remove it if you want to do that. deny all;
add_header X-Frame-Options "SAMEORIGIN" always; return 404;
add_header X-XSS-Protection "1; mode=block" always; }
add_header X-Content-Type-Options "nosniff" always;
# Deny access to hidden files # Block access to hidden files
location ~ /\. { location ~ /\. {
deny all; deny all;
access_log off;
log_not_found off;
} }
# PHP routing - everything goes through index.php # Handle PHP files
location / { location ~ \.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 ~* ^/(?!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;
fastcgi_pass php:9000; fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params; 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_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri; fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string; fastcgi_param QUERY_STRING $query_string;
} }
# Deny access to sensitive directories
location ~ ^/(storage|src|templates|uploads|config) {
deny all;
return 404;
}
} }

View File

@ -1,101 +1,47 @@
# Example nginx config # Basic nginx config for tkr in subfolder
# for serving tkr as a subdfolder without SSL # e.g. https://your-domain.com/tkr
# e.g. http://my-domain.com/tkr # For use with included docker-compose.yml
#
# NOTE: Do not use in production.
# This is provided for docker compose
# (The included docker-compose file will mount it in the container image)
server { server {
listen 80 default_server; listen 80 default_server;
listen [::]:80 default_server;
# replace localhost with your subdomain
# e.g. tkr.my-domain.com
server_name localhost; server_name localhost;
root /var/www/html; root /var/www/html;
index index.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 { location /tkr {
alias /var/www/tkr/public; alias /var/www/tkr/public;
index index.php; index index.php;
# Cache static files # Block access to sensitive directories
# Note that I don't actually serve most of this (just css) location ~ ^/tkr/(storage|src|templates|config) {
# but this prevents requests for static content from getting to the PHP handler. deny all;
#
# 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$ {
return 404; return 404;
} }
# forward other requests to the fallback block, # Block access to hidden files
# which sends them to php-fpm for handling location ~ /\. {
try_files $uri $uri/ @tkr_fallback; 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 {
location @tkr_fallback {
fastcgi_pass php:9000; fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params; include fastcgi_params;
fastcgi_param REQUEST_METHOD $request_method; fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri; fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string; fastcgi_param QUERY_STRING $query_string;
} }
# Deny access to sensitive directories
location ~ ^/tkr/(storage|src|templates|uploads|config) {
deny all;
return 404;
}
} }

View File

@ -1,49 +1,20 @@
# Example Apache VirtualHost # Basic .htaccess for tkr on shared hosting
# for serving tkr as a subdirectory path # Place this file inside the tkr directory
# on shared hosting via .htaccess # e.g. if tkr is at /public_html/tkr/, this goes in /public_html/tkr/
#
# 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 # Enable mod_rewrite
RewriteEngine On RewriteEngine On
# Security headers # Set directory index
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 DirectoryIndex public/index.php
# Security: Block direct access to .php files (except through rewrites) # Block access to sensitive directories
RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC] RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L]
RewriteRule ^.*$ - [R=404,L]
# Security: Block access to sensitive directories # Block access to hidden files
RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
# Security: Block access to hidden files
RewriteRule ^\..*$ - [F,L] RewriteRule ^\..*$ - [F,L]
# Cache CSS files for 1 hour # Route everything else through the front controller
<FilesMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</FilesMatch>
# 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
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ public/index.php [L] RewriteRule ^(.*)$ public/index.php [L]

View File

@ -1,39 +1,34 @@
# Example Apache VirtualHost # Basic Apache VirtualHost for tkr
# for serving tkr as a subdomain root with SSL # Replace "your-domain.com" with your actual domain
# e.g. https://tkr.my-domain.com/ # Replace "/var/www/tkr" with your installation path
#
# Use SSL in production. # HTTP - redirect to HTTPS
# This is a minimal SSL confiuration
# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
<VirtualHost *:80> <VirtualHost *:80>
# CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com ServerName your-domain.com
ServerName localhost Redirect permanent / https://your-domain.com/
# 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/
</VirtualHost> </VirtualHost>
# HTTPS
<VirtualHost *:443> <VirtualHost *:443>
# CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com ServerName your-domain.com
ServerName localhost
# CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
DocumentRoot /var/www/tkr/public DocumentRoot /var/www/tkr/public
# SSL Configuration # SSL Configuration (using Let's Encrypt)
SSLEngine on SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
# Assumes you're using letsencrypt for cert generation SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
# Replace with the actual paths to your cert and key
SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem # Main directory - route everything through index.php
SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem <Directory "/var/www/tkr/public">
AllowOverride None
# Security headers Require all granted
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block" RewriteEngine On
Header always set X-Content-Type-Options "nosniff" RewriteCond %{REQUEST_FILENAME} !-f
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L]
</Directory>
# Block access to sensitive directories # Block access to sensitive directories
<Directory "/var/www/tkr/storage"> <Directory "/var/www/tkr/storage">
Require all denied Require all denied
@ -41,59 +36,13 @@
<Directory "/var/www/tkr/src"> <Directory "/var/www/tkr/src">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config"> <Directory "/var/www/tkr/config">
Require all denied Require all denied
</Directory> </Directory>
<Directory "/var/www/tkr/templates">
# Block access to hidden files
<DirectoryMatch "^\.|/\.">
Require all denied Require all denied
</DirectoryMatch>
# Cache CSS files
<LocationMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</LocationMatch>
# 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)
<LocationMatch "\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# Enable rewrite engine
<Directory "/var/www/tkr/public">
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]
</Directory> </Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/tkr_error.log ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
</VirtualHost> </VirtualHost>

View File

@ -1,91 +1,31 @@
# Example Apache VirtualHost # Basic Apache config for tkr in subfolder
# for serving tkr as a subdirectory path with SSL # e.g. https://your-domain.com/tkr
# e.g. https://www.my-domain.com/tkr # Add this to your existing VirtualHost configuration
#
# Use SSL in production.
# This is a minimal SSL confiuration
# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
<VirtualHost *:80>
# 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/
</VirtualHost>
<VirtualHost *:443> # Alias for tkr subfolder
# CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com Alias /tkr /var/www/tkr/public
ServerName localhost
# CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com
DocumentRoot /var/www/tkr/
# SSL Configuration <Directory "/var/www/tkr/public">
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]
</Directory>
# Assumes you're using letsencrypt for cert generation # Block access to sensitive directories
# Replace with the actual paths to your cert and key <Directory "/var/www/tkr/storage">
SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem Require all denied
SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem </Directory>
<Directory "/var/www/tkr/src">
# Security headers Require all denied
Header always set X-Frame-Options "SAMEORIGIN" </Directory>
Header always set X-XSS-Protection "1; mode=block" <Directory "/var/www/tkr/config">
Header always set X-Content-Type-Options "nosniff" Require all denied
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" </Directory>
<Directory "/var/www/tkr/templates">
# tkr Application at /tkr Require all denied
# NOTE: If you change the directory name, </Directory>
# 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
<Directory "/var/www/tkr/storage">
Require all denied
</Directory>
<Directory "/var/www/tkr/src">
Require all denied
</Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config">
Require all denied
</Directory>
# 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)
<LocationMatch "^/tkr/.*\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# tkr application directory
<Directory "/var/www/tkr/public">
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]
</Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
</VirtualHost>

View File

@ -1,117 +1,51 @@
# Example nginx config # Basic nginx config for tkr
# for serving tkr as a subdomain with SSL # Replace "your-domain.com" with your actual domain
# e.g. https://tkr.my-domain.com/ # Replace "/var/www/tkr" with your installation path
#
# Use SSL in production. # HTTP - redirect to HTTPS
# This is a minimal SSL confiuration server {
# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/ listen 80;
listen [::]:80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}
# HTTPS
server { server {
listen 443 ssl; listen 443 ssl;
listen [::]:443 ssl; listen [::]:443 ssl;
# CONFIG: replace "localhost" with your subdomain (e.g. tkr.my-domain.com) server_name your-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; root /var/www/tkr/public;
index index.php; index index.php;
# CONFIG: # SSL Configuration (using Let's Encrypt)
# Assumes you're using letsencrypt for cert generation ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
# Replace with the actual paths to your cert and key ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem; # Block access to sensitive directories
location ~ ^/(storage|src|templates|config) {
# Security headers deny all;
# The first rule is to prevent including in a frame on a different domain. return 404;
# Remove it if you want to do that. }
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always; # Block access to hidden files
add_header X-Content-Type-Options "nosniff" always;
# Deny access to hidden files
location ~ /\. { location ~ /\. {
deny all; 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 / { location / {
# Cache static files try_files $uri $uri/ @tkr;
# 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 # Handle PHP requests
location @tkr_fallback { location @tkr {
# CONFIG: fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed
# 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; include fastcgi_params;
fastcgi_param REQUEST_METHOD $request_method; fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri; fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string; 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;
} }

View File

@ -1,118 +1,38 @@
# Example nginx config # Basic nginx config for tkr in subfolder
# for serving tkr as a subdfolder with SSL # e.g. https://your-domain.com/tkr
# e.g. https://my-domain.com/tkr # Add this location block to your existing server configuration
#
# 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 location /tkr {
server_name localhost; alias /var/www/tkr/public;
index index.php;
# CONFIG: # Handle PHP files
# Assumes you're using letsencrypt for cert generation location ~ \.php$ {
# Replace with the actual paths to your cert and key fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version/socket as needed
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; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params; 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 # Block access to sensitive directories
location ~ ^/tkr/(storage|src|templates|uploads|config) { location ~ ^/tkr/(storage|src|templates|config) {
deny all; deny all;
return 404; return 404;
} }
# Block access to hidden files
location ~ /\. {
deny all;
}
# Front controller pattern
try_files $uri $uri/ @tkr;
} }
server { location @tkr {
listen 80 default_server; fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed
listen [::]:80 default_server; fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params;
return 301 https://$host$request_uri; fastcgi_param REQUEST_METHOD $request_method;
} fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
}

View File

@ -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
<FilesMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</FilesMatch>
# 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]

View File

@ -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/
<VirtualHost *:80>
# 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/
</VirtualHost>
<VirtualHost *:443>
# 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
<Directory "/var/www/tkr/storage">
Require all denied
</Directory>
<Directory "/var/www/tkr/src">
Require all denied
</Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config">
Require all denied
</Directory>
# 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)
<LocationMatch "^/tkr/.*\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# tkr application directory
<Directory "/var/www/tkr/public">
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]
</Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
</VirtualHost>

View File

@ -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/
<VirtualHost *:80>
# 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/
</VirtualHost>
<VirtualHost *:443>
# 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
<Directory "/var/www/tkr/storage">
Require all denied
</Directory>
<Directory "/var/www/tkr/src">
Require all denied
</Directory>
<Directory "/var/www/tkr/templates">
Require all denied
</Directory>
<Directory "/var/www/tkr/config">
Require all denied
</Directory>
# Block access to hidden files
<DirectoryMatch "^\.|/\.">
Require all denied
</DirectoryMatch>
# Cache CSS files
<LocationMatch "\.css$">
Header set Cache-Control "public, max-age=3600"
</LocationMatch>
# 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)
<LocationMatch "\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$">
<RequireAll>
Require all denied
</RequireAll>
</LocationMatch>
# Enable rewrite engine
<Directory "/var/www/tkr/public">
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]
</Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
</VirtualHost>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -50,7 +50,7 @@ class AdminController extends Controller {
} }
$result = $this->saveSettings($_POST, false); $result = $this->saveSettings($_POST, false);
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }
@ -58,7 +58,7 @@ class AdminController extends Controller {
// for setup, we don't care if they're logged in // for setup, we don't care if they're logged in
// (because they can't be until setup is complete) // (because they can't be until setup is complete)
$result = $this->saveSettings($_POST, true); $result = $this->saveSettings($_POST, true);
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }

View File

@ -39,7 +39,7 @@ class AuthController extends Controller {
} catch (Exception $e) { } catch (Exception $e) {
Log::error("Failed to create login session for {$username}: " . $e->getMessage()); Log::error("Failed to create login session for {$username}: " . $e->getMessage());
Session::setFlashMessage('error', 'Login failed - session error'); Session::setFlashMessage('error', 'Login failed - session error');
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }
} else { } else {
@ -47,13 +47,13 @@ class AuthController extends Controller {
// Set a flash message and reload the login page // Set a flash message and reload the login page
Session::setFlashMessage('error', 'Invalid username or password'); Session::setFlashMessage('error', 'Invalid username or password');
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }
} catch (Exception $e) { } catch (Exception $e) {
Log::error("Database error during login for {$username}: " . $e->getMessage()); Log::error("Database error during login for {$username}: " . $e->getMessage());
Session::setFlashMessage('error', 'Login temporarily unavailable'); Session::setFlashMessage('error', 'Login temporarily unavailable');
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }
} }

View File

@ -20,32 +20,54 @@ class CssController extends Controller {
global $app; global $app;
$cssModel = new CssModel($app['db']); $cssModel = new CssModel($app['db']);
$filename = "$baseFilename.css"; $filename = "$baseFilename.css";
Log::debug("Attempting to serve custom css: {$filename}");
// Make sure the file exists in the database
$cssRow = $cssModel->getByFilename($filename); $cssRow = $cssModel->getByFilename($filename);
if (!$cssRow){ if (!$cssRow){
http_response_code(404); 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"; $filePath = CSS_UPLOAD_DIR . "/$filename";
if (!file_exists($filePath) || !is_readable($filePath)) { if (!file_exists($filePath) || !is_readable($filePath)) {
http_response_code(404); 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 // Make sure the file has a .css extension
// about user input
$ext = strToLower(pathinfo($filename, PATHINFO_EXTENSION)); $ext = strToLower(pathinfo($filename, PATHINFO_EXTENSION));
if($ext != 'css'){ if($ext != 'css'){
http_response_code(400); 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('Content-type: text/css');
header('Cache-control: public, max-age=3600'); 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); readfile($filePath);
exit; exit;
} }
@ -64,7 +86,7 @@ class CssController extends Controller {
} }
// redirect after handling to avoid resubmitting form // redirect after handling to avoid resubmitting form
header('Location: ' . $_SERVER['PHP_SELF']); header('Location: ' . $_SERVER['REQUEST_URI']);
exit; exit;
} }
@ -129,7 +151,7 @@ class CssController extends Controller {
try { try {
if ($_POST['selectCssFile']){ if ($_POST['selectCssFile']){
// Set custom theme // Set custom theme
$app['settings']->cssId = $_POST['selectCssFile']; $app['settings']->cssId = (int)($_POST['selectCssFile']);
} else { } else {
// Set default theme // Set default theme
$app['settings']->cssId = null; $app['settings']->cssId = null;

View File

@ -27,6 +27,7 @@ class Router {
['tick/{id}', 'TickController'], ['tick/{id}', 'TickController'],
['tick/{id}/delete', 'TickController@handleDelete', ['POST']], ['tick/{id}/delete', 'TickController@handleDelete', ['POST']],
['css/custom/{filename}.css', 'CssController@serveCustomCss'], ['css/custom/{filename}.css', 'CssController@serveCustomCss'],
['css/default.css', 'CssController@serveDefaultCss'],
]; ];