Added mod_rewrite for nginx module
This commit is contained in:
25
cms/drupal-mod_headers/.htaccess
Normal file
25
cms/drupal-mod_headers/.htaccess
Normal file
@@ -0,0 +1,25 @@
|
||||
RewriteEngine on
|
||||
RewriteRule ^ - [E=protossl]
|
||||
RewriteCond %{HTTPS} on
|
||||
RewriteRule ^ - [E=protossl:s]
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR]
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php)
|
||||
RewriteCond %{REQUEST_URI} !core
|
||||
RewriteRule ^ %1/core/%2 [L,QSA,R=301]
|
||||
RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^ index.php [L]
|
||||
RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
|
||||
RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
|
||||
RewriteCond %{HTTP:Accept-encoding} gzip
|
||||
RewriteCond %{REQUEST_FILENAME}\.gz -s
|
||||
RewriteRule ^(.*css_[a-zA-Z0-9-_]+)\.css$ $1\.css\.gz [QSA]
|
||||
RewriteCond %{HTTP:Accept-encoding} gzip
|
||||
RewriteCond %{REQUEST_FILENAME}\.gz -s
|
||||
RewriteRule ^(.*js_[a-zA-Z0-9-_]+)\.js$ $1\.js\.gz [QSA]
|
||||
RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1]
|
||||
RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1]
|
||||
19
cms/drupal/.htaccess
Normal file
19
cms/drupal/.htaccess
Normal file
@@ -0,0 +1,19 @@
|
||||
RewriteEngine on
|
||||
RewriteRule ^ - [E=protossl]
|
||||
RewriteCond %{HTTPS} on
|
||||
RewriteRule ^ - [E=protossl:s]
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR]
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php)
|
||||
RewriteCond %{REQUEST_URI} !core
|
||||
RewriteRule ^ %1/core/%2 [L,QSA,R=301]
|
||||
RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^ index.php [L]
|
||||
RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
|
||||
RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
|
||||
RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics\.php$
|
||||
RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F]
|
||||
1
cms/drupal/.htpasswd
Normal file
1
cms/drupal/.htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test .htpasswd hidden file - should be blocked by RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
6
cms/drupal/.well-known/robots.txt
Normal file
6
cms/drupal/.well-known/robots.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# This is a robots.txt file for Drupal site testing
|
||||
# Should be allowed by RewriteRule "/\.|^\.(?!well-known/)" - [F] exception
|
||||
|
||||
User-agent: *
|
||||
Disallow: /admin/
|
||||
Allow: /
|
||||
162
cms/drupal/README.md
Normal file
162
cms/drupal/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# Drupal .htaccess Test Structure
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/drupal/
|
||||
├── core/ - Drupal core directory tests
|
||||
│ ├── install.php - Install script (protected by RewriteRule ^core/install\.php)
|
||||
│ ├── rebuild.php - Rebuild script (protected by RewriteRule ^core/rebuild\.php)
|
||||
│ └── modules/system/tests/ - Tests directory with https/http.php files
|
||||
│ ├── https.php - Test HTTPS test file (excluded from routing)
|
||||
│ └── http.php - Test HTTP test file (excluded from routing)
|
||||
├── favicon.ico - Existing favicon for !-f condition test
|
||||
├── index.php - Drupal entry point (routes non-existing files)
|
||||
│ - Returns: "Drupal Content Route" page
|
||||
├── .htaccess - Hidden .htaccess file (blocked by hidden files rule)
|
||||
├── .htpasswd - Hidden .htpasswd file (blocked by hidden files rule)
|
||||
├── .well-known/ - Well-known directory (allowed exception)
|
||||
│ └── robots.txt - Allowed file via exception (?!well-known)
|
||||
├── somedir/ - Directory for testing !-d condition (200 OK)
|
||||
├── test-drupal-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - This documentation file
|
||||
```
|
||||
|
||||
## Apache Rules Explained - Drupal
|
||||
|
||||
### 1. RewriteEngine Activation
|
||||
|
||||
```apache
|
||||
RewriteEngine on
|
||||
```
|
||||
|
||||
**Что делает:** Включает модуль mod_rewrite для этого каталога
|
||||
**Зачем нужно:** Без этого все правила rewrite не работают
|
||||
|
||||
### 2. Protocol Variables (HTTP/HTTPS Detection)
|
||||
|
||||
```apache
|
||||
RewriteRule ^ - [E=protossl]
|
||||
RewriteCond %{HTTPS} on
|
||||
RewriteRule ^ - [E=protossl:s]
|
||||
```
|
||||
|
||||
**Что делает:** Устанавливает переменную окружения `protossl` = "https" или "http" в зависимости от HTTPS status
|
||||
**Зачем нужно:** Drupal использует это для генерации правильных ссылок (http vs https)
|
||||
- Если HTTPS off → protossl = "" (пусто, http)
|
||||
- Если HTTPS on → protossl = "s"
|
||||
|
||||
### 3. HTTP Authorization Header Passing
|
||||
|
||||
```apache
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует Authorization header в переменную HTTP_AUTHORIZATION для PHP
|
||||
**Зачем нужно:** Drupal REST API требует эту переменную для аутентификации
|
||||
|
||||
### 4. Hidden Files/Patterns Protection Rule
|
||||
|
||||
```apache
|
||||
RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
```
|
||||
|
||||
**Что делает:** Блокирует (403 Forbidden) файлы начинающиеся с точки, кроме .well-known/
|
||||
- `/\.` - блокирует /filename.хотя бы одна точка в path
|
||||
- `^\.(?!well-known/)` - блокирует /隐藏文件, но исключает /well-known/
|
||||
|
||||
**Зачем нужно:** Защита от доступа к скрытым файлам (.htaccess, .htpasswd, .git)
|
||||
**Исключение:** .well-known/robots.txt разрешён (для SEO и security)
|
||||
|
||||
### 5. Core install/rebuild.php Protection Rules
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(install\.php) [OR]
|
||||
RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild\.php)
|
||||
RewriteCond %{REQUEST_URI} !core
|
||||
RewriteRule ^ %1/core/%2 [L,QSA,R=301]
|
||||
```
|
||||
|
||||
**Что делает:** Редирект 301 с /install.php → /core/install.php, /rebuild.php → /core/rebuild.php
|
||||
**Зачем нужно:** Перемещает install/rebuild скрипты в core directory для безопасности
|
||||
|
||||
### 6. Core install.php Rewrite
|
||||
|
||||
```apache
|
||||
RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L]
|
||||
```
|
||||
|
||||
**Что делает:** Переписывает запрос на core/install.php с добавлением параметра rewrite=ok
|
||||
**Зачем нужно:** Drupal internal handling для процесса установки
|
||||
|
||||
### 7. Main Drupal Routing Rules (!-f, !-d)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^ index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Маршрутизирует через index.php все запросы на несуществующие файлы И директории, кроме favicon.ico
|
||||
**Зачем нужно:** "Чистые URL" Drupal (похоже на WordPress), routing через index.php
|
||||
|
||||
### 8. Core Modules Tests Files Exceptions
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
|
||||
RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
|
||||
```
|
||||
|
||||
**Что делает:** Исключает из routing core/*.php и tests/https.php/http.php файлы
|
||||
**Зачем нужно:** Эти файлы должны обрабатываться напрямую, не через main index.php
|
||||
|
||||
## Test Script Features
|
||||
|
||||
The script includes test functions:
|
||||
1. **test_rule()** - checks HTTP status code only
|
||||
2. **test_rule_content()** - checks both status AND response body content
|
||||
|
||||
## Multisite-Specific Testing Scenarios
|
||||
|
||||
### Hidden Files Protection
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/.htaccess` | hidden files rule `/\.|^\.(?!well-known/)` | 403 Forbidden ✓ |
|
||||
| `http://test.my.brp/.htpasswd` | hidden files rule | 403 Forbidden ✓ |
|
||||
| `http://test.my.brp/.well-known/robots.txt` | well-known exception | 200 OK (allowed) ✓ |
|
||||
|
||||
### Core install/rebuild.php Protection
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/install.php` | RewriteRule ^(.*)?/(install\.php) + core exclusion | 301 → /core/install.php ✓ |
|
||||
| `http://test.my.brp/rebuild.php` | RewriteRule ^(.*)?/(rebuild\.php) + core exclusion | 301 → /core/rebuild.php ✓ |
|
||||
|
||||
### Core Files Routing Exceptions
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/core/install.php` | RewriteRule ^core/install\.php ... rewrite=ok parameter | 200 OK ✓ |
|
||||
| `http://test.my.brp/core/modules/system/tests/https.php` | tests exception !-https?\.php condition | 200 OK ✓ |
|
||||
| `http://test.my.brp/core/modules/system/tests/http.php` | tests exception !-https?\.php condition (s matches empty) | 200 OK ✓ |
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/drupal
|
||||
./test-drupal-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for Drupal tests (all should be **PASS ✓**):
|
||||
- Basic page routing via index.php: HTTP 200 + "Drupal Content Route" ✓
|
||||
- Hidden files (.htaccess, .htpasswd) blocked: HTTP 403 ✓
|
||||
- .well-known/robots.txt allowed: HTTP 200 ✓
|
||||
- install.php redirect to core: HTTP 301 ✓
|
||||
- rebuild.php redirect to core: HTTP 301 ✓
|
||||
- favicon.ico direct access (!-f): HTTP 200 ✓
|
||||
- Non-existing page routing to index.php: HTTP 200 ✓
|
||||
- Directory access (!-d): HTTP 200 ✓
|
||||
- Tests files https.php/http.php excluded from routing: HTTP 200 ✓
|
||||
1
cms/drupal/core/install.php
Normal file
1
cms/drupal/core/install.php
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test Drupal core/install.php file - should be protected by RewriteRule ^core/install\.php core/install.php?rewrite=ok [QSA,L]
|
||||
1
cms/drupal/core/modules/system/tests/http.php
Normal file
1
cms/drupal/core/modules/system/tests/http.php
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test Drupal http.php in tests directory - should be excluded from index.php routing by RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
|
||||
1
cms/drupal/core/modules/system/tests/https.php
Normal file
1
cms/drupal/core/modules/system/tests/https.php
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test Drupal https.php in tests directory - should be excluded from index.php routing by RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?\.php
|
||||
1
cms/drupal/core/rebuild.php
Normal file
1
cms/drupal/core/rebuild.php
Normal file
@@ -0,0 +1 @@
|
||||
# This is a test Drupal core/rebuild.php file - should be protected by RewriteRule ^core/rebuild\.php core/rebuild.php?rewrite=ok [QSA,L]
|
||||
2
cms/drupal/favicon.ico
Normal file
2
cms/drupal/favicon.ico
Normal file
@@ -0,0 +1,2 @@
|
||||
# This is a placeholder for Drupal test favicon.ico
|
||||
# Real favicon would be an image file, but we use text for testing
|
||||
15
cms/drupal/index.php
Normal file
15
cms/drupal/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Drupal - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Drupal response
|
||||
echo "<html><head><title>Drupal Test Site</title></head><body>";
|
||||
echo "<h1>Drupal Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='drupal-config'>Drupal Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/drupal/nginx.conf
Normal file
159
cms/drupal/nginx.conf
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
load_module modules/ngx_http_apache_rewrite_module.so;
|
||||
worker_processes 1;
|
||||
|
||||
error_log logs/error.log debug;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example1.com;
|
||||
|
||||
root /sites/site1;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example2.com;
|
||||
|
||||
root /sites/site2;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example3.com;
|
||||
|
||||
root /sites/site3;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example4.com;
|
||||
|
||||
root /sites/site4;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example5.com;
|
||||
|
||||
root /sites/site5;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
cms/drupal/site5.conf
Normal file
20
cms/drupal/site5.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site5"
|
||||
ServerName example5.com
|
||||
ErrorLog logs/site5.log
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site5>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch "^\.htaccess$">
|
||||
Require all granted
|
||||
</FilesMatch>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
0
cms/drupal/somedir/.gitkeep
Normal file
0
cms/drupal/somedir/.gitkeep
Normal file
3
cms/drupal/somedir/index.php
Normal file
3
cms/drupal/somedir/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
echo "Ok";
|
||||
182
cms/drupal/test-drupal-rewriterules.sh
Executable file
182
cms/drupal/test-drupal-rewriterules.sh
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Drupal .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/drupal/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/drupal
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Drupal .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. RewriteEngine Activation"
|
||||
echo "=============================================="
|
||||
# Test basic routing through index.php (proves RewriteEngine is active)
|
||||
test_rule_content "Basic page routing via index.php" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Protocol Variables (protossl)"
|
||||
echo "============================================}"
|
||||
# Test HTTPS protocol detection - since we use http://, HTTPS should be off
|
||||
test_rule_content "HTTP request without HTTPS (protocol detection)" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. HTTP Authorization Header Passing"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by Drupal REST API
|
||||
test_rule_content "Drupal handles Authorization header (API request)" \
|
||||
"$BASE_URL/rest/api/v1" \
|
||||
"Authorization: Bearer token_abc123" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. Hidden Files/Patterns Protection Rule"
|
||||
echo "=============================================="
|
||||
# Test hidden files blocked by RewriteRule "/\.|^\.(?!well-known/)" - [F]
|
||||
test_rule "Block .htaccess hidden file (pattern \.)" \
|
||||
"$BASE_URL/.htaccess" \
|
||||
"403"
|
||||
|
||||
test_rule "Block .htpasswd hidden file (pattern \.)" \
|
||||
"$BASE_URL/.htpasswd" \
|
||||
"403"
|
||||
|
||||
test_rule_content "Allow .well-known/robots.txt (exception for well-known)" \
|
||||
"$BASE_URL/.well-known/robots.txt" \
|
||||
"" \
|
||||
"200" \
|
||||
"User-agent:"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Core install/rebuild.php Protection Rules"
|
||||
echo "============================================}"
|
||||
# Test install.php protection - should route to core/install.php with rewrite=ok parameter
|
||||
test_rule "Core install.php protected routing" \
|
||||
"$BASE_URL/install.php" \
|
||||
"301"
|
||||
|
||||
# Test rebuild.php protection - similar redirect pattern
|
||||
test_rule "Core rebuild.php protected routing" \
|
||||
"$BASE_URL/rebuild.php" \
|
||||
"301"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Drupal Core Files Routing Rules"
|
||||
echo "============================================}"
|
||||
# Test existing file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule_content "Existing favicon.ico access (!-f condition)" \
|
||||
"$BASE_URL/favicon.ico" \
|
||||
"" \
|
||||
"200" \
|
||||
"This is a placeholder for Drupal test favicon.ico"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. Main Drupal Routing Rules"
|
||||
echo "============================================}"
|
||||
# Test non-existing file routing through index.php (main routing) - !-f AND !-d pass
|
||||
test_rule_content "Non-existing page routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Drupal Content Route"
|
||||
|
||||
# Test existing directory access (!-d condition passes) - should return 200 OK
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Core Modules Tests Files Exceptions"
|
||||
echo "============================================}"
|
||||
# Test https.php in tests directory - should NOT route to index.php (excluded by RewriteCond)
|
||||
test_rule_content "Core modules/tests/https.php excluded from routing (!-php condition)" \
|
||||
"$BASE_URL/core/modules/system/tests/https.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"# This is a test Drupal https.php in tests directory"
|
||||
|
||||
# Test http.php in tests directory - same exclusion applies (s for https? regex)
|
||||
test_rule_content "Core modules/tests/http.php excluded from routing" \
|
||||
"$BASE_URL/core/modules/system/tests/http.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"# This is a test Drupal http.php in tests directory"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
74
cms/grav/.htaccess
Normal file
74
cms/grav/.htaccess
Normal file
@@ -0,0 +1,74 @@
|
||||
RewriteEngine On
|
||||
|
||||
# ============================================
|
||||
# 1. Security Rules - Test malicious patterns
|
||||
# ============================================
|
||||
|
||||
# Detect template injection (Mustache, Twig syntax)
|
||||
RewriteCond %{REQUEST_URI} ({{|}}|{%|%}) [OR]
|
||||
RewriteCond %{QUERY_STRING} ({{|}}|{%25|%25}) [OR]
|
||||
|
||||
# Base64 encoded payloads in query string
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
|
||||
# Script injection patterns (HTML entities decoded)
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
|
||||
# GLOBALS exploitation attempt
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
|
||||
# _REQUEST manipulation (PHP superglobal abuse)
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
|
||||
# Block malicious requests with 403 Forbidden
|
||||
RewriteRule .* index.php [F]
|
||||
|
||||
# ============================================
|
||||
# 2. Grav CMS Core Routing Rules
|
||||
# ============================================
|
||||
|
||||
# Prevent rewriting to index.php if already there
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
|
||||
# Allow access to existing files
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
|
||||
# Allow access to existing directories
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
# Route everything else through Grav's index.php
|
||||
RewriteRule .* index.php [L]
|
||||
|
||||
# ============================================
|
||||
# 3. Sensitive Directory Protection
|
||||
# ============================================
|
||||
|
||||
# Block system, cache, logs, backups folders
|
||||
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
|
||||
# Block system and vendor directories (prevent access to .txt/.xml files)
|
||||
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
|
||||
# Block user directory access to configuration and content files
|
||||
RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
|
||||
# ============================================
|
||||
# 4. File Extension Protection
|
||||
# ============================================
|
||||
|
||||
# Block raw .md file access (content source files)
|
||||
RewriteRule \.md$ error [F]
|
||||
|
||||
# ============================================
|
||||
# 5. Hidden Files/Directories Protection
|
||||
# ============================================
|
||||
|
||||
# Block hidden files except well-known/robots.txt directories
|
||||
RewriteRule (^|/)\.(?!well-known) - [F]
|
||||
|
||||
# ============================================
|
||||
# 6. Critical Configuration File Protection
|
||||
# ============================================
|
||||
|
||||
# Block sensitive configuration and documentation files
|
||||
RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
1
cms/grav/.htaccess.test
Normal file
1
cms/grav/.htaccess.test
Normal file
@@ -0,0 +1 @@
|
||||
This is a .htaccess file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
1
cms/grav/.htpasswd
Normal file
1
cms/grav/.htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
This is a hidden .htpasswd file - should be blocked by RewriteRule (^|/)\.(?!well-known) - [F]
|
||||
1
cms/grav/.well-known/robots.txt
Normal file
1
cms/grav/.well-known/robots.txt
Normal file
@@ -0,0 +1 @@
|
||||
Robots.txt file - should be ALLOWED because it matches (.well-known) exception in RewriteRule (^|/)\.(?!well-known) - [F]
|
||||
1
cms/grav/LICENSE.txt
Normal file
1
cms/grav/LICENSE.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a LICENSE.txt file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
119
cms/grav/README.md
Normal file
119
cms/grav/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Grav CMS Test Structure - Updated
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/grav/
|
||||
├── .git/ - Git folder tests (blocked)
|
||||
│ └── secret.txt
|
||||
├── .well-known/ - Well-known directory (allowed exception)
|
||||
│ └── robots.txt
|
||||
├── .htaccess - Main Apache rules configuration
|
||||
├── .htpasswd - Hidden file (blocked by Rule 5)
|
||||
├── bin/ - Bin folder tests (blocked)
|
||||
│ └── helper.php
|
||||
├── backup/ - Backup folder tests (blocked)
|
||||
│ └── archive.zip
|
||||
├── cache/ - Cache folder tests (blocked)
|
||||
│ └── test.txt
|
||||
├── composer.json - Config file protection (blocked)
|
||||
├── composer.lock - Config file protection (blocked)
|
||||
├── existing.jpg - Existing file for routing test (200 OK)
|
||||
├── index.php - Grav CMS entry point - routes non-existing files
|
||||
│ - Returns: "Grav CMS Content Route" page
|
||||
├── LICENSE.txt - Config file protection (blocked)
|
||||
├── logs/ - Logs folder tests (blocked)
|
||||
│ └── app.log
|
||||
├── normal-page.md - Normal Grav CMS page (routes through index.php)
|
||||
├── README.md - This documentation file
|
||||
├── somedir/ - Empty directory for routing test (200 OK)
|
||||
├── system/ - System folder tests (blocked extensions)
|
||||
│ └── config.xml
|
||||
├── vendor/ - Vendor folder tests (blocked extensions)
|
||||
│ └── module.txt
|
||||
├── user/ - User folder tests (blocked extensions)
|
||||
│ ├── test.txt
|
||||
│ ├── data.json
|
||||
│ ├── template.twig
|
||||
│ ├── script.sh
|
||||
│ ├── module.php
|
||||
│ ├── config.yml
|
||||
│ └── settings.yaml
|
||||
├── webserver-configs/ - Webserver configs folder tests (blocked)
|
||||
│ └── nginx.conf
|
||||
├── tests/ - Tests folder tests (blocked)
|
||||
│ └── unit-test.php
|
||||
├── test-mustache.php - Security: Mustache template injection pattern
|
||||
├── twig-test.html - Security: Twig syntax injection pattern
|
||||
├── test-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - Documentation
|
||||
```
|
||||
|
||||
## Updated Test Script Features
|
||||
|
||||
### New Content Verification Function
|
||||
|
||||
The script now includes a `test_rule_content()` function that:
|
||||
1. Checks HTTP status code matches expected value
|
||||
2. Verifies response body contains expected content string
|
||||
|
||||
**Example usage:**
|
||||
```bash
|
||||
test_rule_content "Normal page routing via index.php" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"200" \
|
||||
"Grav CMS Content Route"
|
||||
```
|
||||
|
||||
This tests that:
|
||||
- URL `/home/alexey/projects/workspace-zed/test1/cms/grav/normal-page/` (non-existing file)
|
||||
- Returns HTTP 200 status (routed through index.php via Rule 2)
|
||||
- Response body contains "Grav CMS Content Route" text from index.php
|
||||
|
||||
## Test Coverage Summary
|
||||
|
||||
### 1. Security Rules - Malicious Patterns ✓
|
||||
- Template injection: `{{ }}`, `{% %}` in URI/Query String → **403**
|
||||
- Base64 payloads: `base64_encode()` pattern → **403**
|
||||
- Script injection: `<script>` or `%3C` encoded → **403**
|
||||
- GLOBALS exploitation: `GLOBALS[` pattern → **403**
|
||||
- _REQUEST manipulation: `_REQUEST[` pattern → **403**
|
||||
|
||||
### 2. Core Routing Rules ✓ (Updated)
|
||||
- Non-existing file routes to index.php → **200** + content check
|
||||
- Existing file access → **200**
|
||||
- Existing directory access → **200**
|
||||
|
||||
### 3. Sensitive Directory Protection ✓
|
||||
- Blocks: `.git`, `cache`, `bin`, `logs`, `backup` folders → **403**
|
||||
- Blocks system/vendor with sensitive extensions (`.txt`, `.xml`) → **403**
|
||||
|
||||
### 4. File Extension Protection ✓
|
||||
- Blocks raw `.md` files → **403**
|
||||
|
||||
### 5. Hidden Files/Directories Protection ✓
|
||||
- Blocks hidden files except `.well-known` → **403** / **200**
|
||||
|
||||
### 6. Critical Configuration File Protection ✓
|
||||
- Blocks: `LICENSE.txt`, `composer.lock`, `composer.json`, `.htaccess` → **403**
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/grav
|
||||
./test-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for security tests (all should be **PASS ✓**):
|
||||
- Template injection: HTTP 403 ✓
|
||||
- Twig injection: HTTP 403 ✓
|
||||
- Base64 payload: HTTP 403 ✓
|
||||
- Script injection: HTTP 403 ✓
|
||||
- GLOBALS manipulation: HTTP 403 ✓
|
||||
- _REQUEST manipulation: HTTP 403 ✓
|
||||
|
||||
Expected results for routing tests (should be **PASS ✓**):
|
||||
- Normal page routing: HTTP 200 + content "Grav CMS Content Route" ✓
|
||||
- Existing file access: HTTP 200 ✓
|
||||
- Directory access: HTTP 200 ✓
|
||||
1
cms/grav/backup/archive.zip
Normal file
1
cms/grav/backup/archive.zip
Normal file
@@ -0,0 +1 @@
|
||||
Backup archive.zip file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/bin/helper.php
Normal file
1
cms/grav/bin/helper.php
Normal file
@@ -0,0 +1 @@
|
||||
Bin helper.php file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/cache/test.txt
vendored
Normal file
1
cms/grav/cache/test.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Cache test.txt file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/composer.json
Normal file
1
cms/grav/composer.json
Normal file
@@ -0,0 +1 @@
|
||||
{\"name\": \"test/my-site\", \"description\": \"Test Composer file\"}
|
||||
1
cms/grav/composer.lock
generated
Normal file
1
cms/grav/composer.lock
generated
Normal file
@@ -0,0 +1 @@
|
||||
This is a composer.lock file - should be blocked by RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$ error [F]
|
||||
0
cms/grav/existing.jpg
Normal file
0
cms/grav/existing.jpg
Normal file
15
cms/grav/index.php
Normal file
15
cms/grav/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Grav CMS - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Grav CMS response
|
||||
echo "<html><head><title>Grav CMS Test Site</title></head><body>";
|
||||
echo "<h1>Grav CMS Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='graviconfig'>Site Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/grav/nginx.conf
Normal file
159
cms/grav/nginx.conf
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
load_module modules/ngx_http_apache_rewrite_module.so;
|
||||
worker_processes 1;
|
||||
|
||||
error_log logs/error.log debug;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example1.com;
|
||||
|
||||
root /sites/site1;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example2.com;
|
||||
|
||||
root /sites/site2;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example3.com;
|
||||
|
||||
root /sites/site3;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example4.com;
|
||||
|
||||
root /sites/site4;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example5.com;
|
||||
|
||||
root /sites/site5;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
cms/grav/normal-page.md
Normal file
1
cms/grav/normal-page.md
Normal file
@@ -0,0 +1 @@
|
||||
Normal Grav CMS page - should be routed through index.php
|
||||
14
cms/grav/site2.conf
Normal file
14
cms/grav/site2.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site2"
|
||||
ServerName example2.com
|
||||
|
||||
<Directory /sites/site2>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
0
cms/grav/somedir/.gitkeep
Normal file
0
cms/grav/somedir/.gitkeep
Normal file
1
cms/grav/system/config.xml
Normal file
1
cms/grav/system/config.xml
Normal file
@@ -0,0 +1 @@
|
||||
System config.xml file - should be blocked by RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/test-mustache.php
Normal file
1
cms/grav/test-mustache.php
Normal file
@@ -0,0 +1 @@
|
||||
This is a Mustache template injection test file with {{ variable }} syntax
|
||||
150
cms/grav/test-rewriterules.sh
Executable file
150
cms/grav/test-rewriterules.sh
Executable file
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Grav CMS .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/grav/.htaccess
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Grav CMS .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
local expected_content="$4" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. Security Rules - Test malicious patterns"
|
||||
echo "=============================================="
|
||||
# Template injection via query string with {{ }} Mustache syntax
|
||||
test_rule "Template injection via query string ({{ }}" \
|
||||
"$BASE_URL/test-mustache.php?\{\{config.secret\}\}" \
|
||||
"403"
|
||||
|
||||
# Base64 encoded payloads (base64_encode function call)
|
||||
test_rule "Base64 payload pattern" \
|
||||
"$BASE_URL/test.php?data=base64_encode(some_secret)" \
|
||||
"403"
|
||||
|
||||
# Script injection (<script> or HTML entities %3C)
|
||||
test_rule "Script injection (encoded)" \
|
||||
"$BASE_URL/search.html?q=%3Cscript%25alert(1)%3E" \
|
||||
"403"
|
||||
|
||||
# GLOBALS exploitation attempt
|
||||
test_rule "GLOBALS manipulation" \
|
||||
"$BASE_URL/leak.php?GLOBALS\[secret\]=exploit" \
|
||||
"403"
|
||||
|
||||
# _REQUEST manipulation (PHP superglobal abuse)
|
||||
test_rule "_REQUEST manipulation" \
|
||||
"$BASE_URL/attack.php?_REQUEST\[user\]=admin" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Grav CMS Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test normal page routing through index.php (non-existing file -> index.php)
|
||||
test_rule_content "Normal page routing via index.php" \
|
||||
"$BASE_URL/normal-page/" \
|
||||
"200" \
|
||||
"Grav CMS Content Route"
|
||||
|
||||
# Test existing file access - check status + content type/image
|
||||
test_rule "Existing file access (existing.jpg)" \
|
||||
"$BASE_URL/existing.jpg" \
|
||||
"200"
|
||||
|
||||
# Test existing directory access
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Sensitive Directory Protection"
|
||||
echo "=============================================="
|
||||
# Block system, cache, logs, backups folders
|
||||
test_rule "Block .git folder" "$BASE_URL/.git/secret.txt" "403"
|
||||
test_rule "Block cache folder" "$BASE_URL/cache/test.txt" "403"
|
||||
test_rule "Block bin folder" "$BASE_URL/bin/helper.php" "403"
|
||||
test_rule "Block logs folder" "$BASE_URL/logs/app.log" "403"
|
||||
test_rule "Block backup folder" "$BASE_URL/backup/archive.zip" "403"
|
||||
|
||||
# Block system and vendor directories with sensitive extensions
|
||||
test_rule "Block system config.xml" "$BASE_URL/system/config.xml" "403"
|
||||
test_rule "Block vendor module.txt" "$BASE_URL/vendor/module.txt" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. File Extension Protection"
|
||||
echo "=============================================="
|
||||
# Block raw .md file access (content source files)
|
||||
test_rule "Block raw .md file" "$BASE_URL/test.md" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Hidden Files/Directories Protection"
|
||||
echo "=============================================="
|
||||
test_rule "Block hidden .htpasswd" "$BASE_URL/.htpasswd" "403"
|
||||
test_rule "Allow .well-known/robots.txt" "$BASE_URL/.well-known/robots.txt" "200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Critical Configuration File Protection"
|
||||
echo "=============================================="
|
||||
test_rule "Block LICENSE.txt" "$BASE_URL/LICENSE.txt" "403"
|
||||
test_rule "Block composer.lock" "$BASE_URL/composer.lock" "403"
|
||||
test_rule "Block composer.json" "$BASE_URL/composer.json" "403"
|
||||
test_rule "Block .htaccess" "$BASE_URL/.htaccess" "403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
1
cms/grav/test.md
Normal file
1
cms/grav/test.md
Normal file
@@ -0,0 +1 @@
|
||||
Test.md
|
||||
1
cms/grav/tests/unit-test.php
Normal file
1
cms/grav/tests/unit-test.php
Normal file
@@ -0,0 +1 @@
|
||||
Tests unit-test.php file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
1
cms/grav/twig-test.html
Normal file
1
cms/grav/twig-test.html
Normal file
@@ -0,0 +1 @@
|
||||
This is a Twig template syntax test file with {% comment %} syntax
|
||||
1
cms/grav/user/config.yml
Normal file
1
cms/grav/user/config.yml
Normal file
@@ -0,0 +1 @@
|
||||
User config.yml file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/data.json
Normal file
1
cms/grav/user/data.json
Normal file
@@ -0,0 +1 @@
|
||||
User data.json file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/module.php
Normal file
1
cms/grav/user/module.php
Normal file
@@ -0,0 +1 @@
|
||||
User module.php file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/script.sh
Normal file
1
cms/grav/user/script.sh
Normal file
@@ -0,0 +1 @@
|
||||
User script.sh file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/settings.yaml
Normal file
1
cms/grav/user/settings.yaml
Normal file
@@ -0,0 +1 @@
|
||||
User settings.yaml file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/template.twig
Normal file
1
cms/grav/user/template.twig
Normal file
@@ -0,0 +1 @@
|
||||
User template.twig file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/user/test.txt
Normal file
1
cms/grav/user/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
User test.txt file - should be blocked by RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/vendor/module.txt
vendored
Normal file
1
cms/grav/vendor/module.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Vendor module.txt file - should be blocked by RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F]
|
||||
1
cms/grav/webserver-configs/nginx.conf
Normal file
1
cms/grav/webserver-configs/nginx.conf
Normal file
@@ -0,0 +1 @@
|
||||
Webserver configs nginx.conf file - should be blocked by RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||
16
cms/joomla/.htaccess
Normal file
16
cms/joomla/.htaccess
Normal file
@@ -0,0 +1,16 @@
|
||||
RewriteEngine On
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
RewriteRule .* index.php [F]
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteCond %{REQUEST_URI} ^/api/
|
||||
RewriteCond %{REQUEST_URI} !^/api/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* api/index.php [L]
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* index.php [L]
|
||||
6
cms/joomla/.well-known/robots.txt
Normal file
6
cms/joomla/.well-known/robots.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# This is a well-known robots.txt file for Joomla testing
|
||||
# Should be accessible via RewriteRule /api/index.php [L] or standard routing
|
||||
|
||||
User-agent: *
|
||||
Disallow: /administrator/
|
||||
Allow: /
|
||||
153
cms/joomla/README.md
Normal file
153
cms/joomla/README.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Joomla .htaccess Test Structure
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/joomla/
|
||||
├── api/ - API directory tests
|
||||
│ └── index.php - Joomla API entry point (routes /api/ requests)
|
||||
│ - Returns: "Joomla API Configuration Loaded"
|
||||
├── .well-known/ - Well-known directory
|
||||
│ └── robots.txt - Allowed file via exception
|
||||
├── base64-test.php - Security test for base64_encode pattern detection
|
||||
├── globals-test.php - Security test for GLOBALS exploitation pattern
|
||||
├── request-test.php - Security test for _REQUEST manipulation pattern
|
||||
├── script-test.php - Security test for script injection pattern
|
||||
├── index.php - Joomla main entry point (routes non-existing files)
|
||||
│ - Returns: "Joomla Content Route" page
|
||||
├── somedir/ - Directory for testing !-d condition (200 OK)
|
||||
├── test-joomla-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - This documentation file
|
||||
```
|
||||
|
||||
## Apache Rules Explained - Joomla
|
||||
|
||||
### 1. Base64 Encoded Payload Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects Base64 encoded payloads in query string (function call pattern)
|
||||
**Зачем нужно:** Защита от Base64-encoded malicious code injection attacks
|
||||
- Pattern: `base64_encode(...)` - detect function calls that encode data
|
||||
|
||||
### 2. Script Injection Pattern Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects script injection patterns (HTML entities decoded)
|
||||
**Зачем нужно:** Защита от XSS attacks через URL parameters
|
||||
- Pattern: `<script>...` or `%3Cscript%3E` - detect HTML script tags
|
||||
- `[NC]` - case-insensitive matching
|
||||
|
||||
### 3. GLOBALS Exploitation Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
|
||||
```
|
||||
|
||||
**Что делает:** Detects GLOBALS exploitation attempt in query string
|
||||
**Зачем нужно:** Защита от PHP superglobal abuse attacks
|
||||
- Pattern: `GLOBALS=` or `GLOBALS[` - detect attempts to manipulate global variables
|
||||
|
||||
### 4. _REQUEST Manipulation Detection Rule
|
||||
|
||||
```apache
|
||||
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
```
|
||||
|
||||
**Что делает:** Detects _REQUEST manipulation (PHP superglobal abuse)
|
||||
**Зачем нужно:** Защита от PHP superglobal exploitation attacks
|
||||
- Pattern: `_REQUEST=` or `_REQUEST[` - detect attempts to manipulate $_REQUEST array
|
||||
|
||||
### 5. Malicious Requests Blocked with [F] Flag
|
||||
|
||||
```apache
|
||||
RewriteRule .* index.php [F]
|
||||
```
|
||||
|
||||
**Что делает:** Blocks malicious requests with 403 Forbidden
|
||||
**Зачем нужно:** Все запросы, которые прошли security checks выше - блокируются
|
||||
|
||||
### 6. HTTP Authorization Header Passing Rule
|
||||
|
||||
```apache
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует Authorization header в переменную HTTP_AUTHORIZATION для PHP
|
||||
**Зачем нужно:** Joomla REST API требует эту переменную для аутентификации
|
||||
|
||||
### 7. Joomla API Routing Rule (/api/)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} ^/api/
|
||||
RewriteCond %{REQUEST_URI} !^/api/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* api/index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Маршрутизирует запросы в `/api/` через `api/index.php` (если не существующий файл/directory)
|
||||
**Зачем нужно:** Joomla REST API routing - отдельная точка входа для API endpoints
|
||||
- **Исключение:** Если запрос прямо на /api/index.php - пропускаем (не переписываем)
|
||||
|
||||
### 8. Main Joomla Routing Rule (/index.php exclusion)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule .* index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Маршрутизирует все запросы через main `index.php` (!-f AND !-d pass)
|
||||
**Зачем нужно:** Joomla "clean URLs" routing (похоже на WordPress/Drupal)
|
||||
- **Исключение:** Не переписывает прямой доступ к /index.php
|
||||
|
||||
## Test Script Features
|
||||
|
||||
The script includes test functions:
|
||||
1. **test_rule()** - checks HTTP status code only
|
||||
2. **test_rule_content()** - checks both status AND response body content
|
||||
|
||||
## Security Pattern Testing Scenarios
|
||||
|
||||
### Query String Pattern Detection
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/base64-test.php?data=base64_encode(secret)` | base64_encode pattern detection → 403 ✓ |
|
||||
| `http://test.my.brp/script-test.php?q=%3Cscript%3Ealert(1)%3E` | script injection pattern → 403 ✓ |
|
||||
| `http://test.my.brp/globals-test.php?GLOBALS[user]=admin` | GLOBALS exploitation → 403 ✓ |
|
||||
| `http://test.my.brp/request-test.php?_REQUEST[config]=true` | _REQUEST manipulation → 403 ✓ |
|
||||
|
||||
### API vs Main Routing
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/api/` | api/index.php routing (!-f, !-d pass) → api/index.php ✓ |
|
||||
| `http://test.my.brp/api/index.php` | Direct access (excluded from api routing) → 200 OK ✓ |
|
||||
| `http://test.my.brp/nonexistent-page/` | Main index.php routing (!-f, !-d pass) → main index.php ✓ |
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/joomla
|
||||
./test-joomla-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for Joomla tests (all should be **PASS ✓**):
|
||||
- Base64 encoded payload blocked: HTTP 403 ✓
|
||||
- Script injection blocked: HTTP 403 ✓
|
||||
- GLOBALS exploitation blocked: HTTP 403 ✓
|
||||
- _REQUEST manipulation blocked: HTTP 403 ✓
|
||||
- Authorization header handling: HTTP 200 + "Joomla Content Route" ✓
|
||||
- API index.php routing: HTTP 200 + "Joomla API Configuration Loaded" ✓
|
||||
- Direct /api/index.php file access: HTTP 200 OK ✓
|
||||
- Main index.php routing (non-existing page): HTTP 200 + "Joomla Content Route" ✓
|
||||
- Directory access (!-d): HTTP 200 OK ✓
|
||||
56
cms/joomla/api/index.php
Normal file
56
cms/joomla/api/index.php
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
<?php
|
||||
/**
|
||||
* WordPress REST API Mock (Test version)
|
||||
* This file checks if Authorization header is properly passed from mod_rewrite
|
||||
*/
|
||||
|
||||
// Check if Authorization header was received
|
||||
$auth_header = "";
|
||||
if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
|
||||
$auth_header = $_SERVER["HTTP_AUTHORIZATION"];
|
||||
} else {
|
||||
// Also check for rewritten env var
|
||||
$auth_env = getenv("HTTP_AUTHORIZATION");
|
||||
if ($auth_env !== false && $auth_env !== "") {
|
||||
$auth_header = $auth_env;
|
||||
}
|
||||
}
|
||||
|
||||
// Set response headers
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
if ($auth_header === "secret_token_123") {
|
||||
// SUCCESS - Authorization header was properly passed through mod_rewrite
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "success",
|
||||
"message" => "Authorization verified",
|
||||
"token_verified" => true,
|
||||
"wordpress_config_loaded" => true,
|
||||
"received_auth_header" => $auth_header,
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
} else {
|
||||
// FAIL - Authorization header was not passed through mod_rewrite
|
||||
|
||||
http_response_code(401);
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "error",
|
||||
"message" => "unauth",
|
||||
"expected" => "Bearer secret_token_123",
|
||||
"received" => $auth_header ?: "(not set)",
|
||||
"test_failed" => true,
|
||||
"hint" =>
|
||||
"mod_rewrite [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] is NOT passing header to PHP",
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
}
|
||||
|
||||
exit();
|
||||
|
||||
1
cms/joomla/api/index.php.dummy
Normal file
1
cms/joomla/api/index.php.dummy
Normal file
@@ -0,0 +1 @@
|
||||
# Joomla API test - should be allowed when accessed via /api/index.php (excluded from index.php routing)
|
||||
2
cms/joomla/base64-test.php
Normal file
2
cms/joomla/base64-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - base64_encode payload pattern
|
||||
# This file should be blocked when accessed with query string containing base64_encode[^(]*\([^)]*\)
|
||||
2
cms/joomla/globals-test.php
Normal file
2
cms/joomla/globals-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - GLOBALS exploitation pattern
|
||||
# This file should be blocked when accessed with query string containing GLOBALS(=|\[|\%[0-9A-Z]{0,2})
|
||||
15
cms/joomla/index.php
Normal file
15
cms/joomla/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Joomla - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated Joomla response
|
||||
echo "<html><head><title>Joomla Test Site</title></head><body>";
|
||||
echo "<h1>Joomla Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='joomla-config'>Joomla Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
159
cms/joomla/nginx.conf
Normal file
159
cms/joomla/nginx.conf
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
load_module modules/ngx_http_apache_rewrite_module.so;
|
||||
worker_processes 1;
|
||||
|
||||
error_log logs/error.log debug;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example1.com;
|
||||
|
||||
root /sites/site1;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example2.com;
|
||||
|
||||
root /sites/site2;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example3.com;
|
||||
|
||||
root /sites/site3;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example4.com;
|
||||
|
||||
root /sites/site4;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example5.com;
|
||||
|
||||
root /sites/site5;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
cms/joomla/request-test.php
Normal file
2
cms/joomla/request-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - _REQUEST manipulation pattern
|
||||
# This file should be blocked when accessed with query string containing _REQUEST(=|\[|\%[0-9A-Z]{0,2})
|
||||
2
cms/joomla/script-test.php
Normal file
2
cms/joomla/script-test.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# Joomla security test - script injection pattern
|
||||
# This file should be blocked when accessed with query string containing (<|%3C)([^s]*s)+cript.*(>|%3E)
|
||||
16
cms/joomla/site4.conf
Normal file
16
cms/joomla/site4.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site4"
|
||||
ServerName example4.com
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site4>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
170
cms/joomla/test-joomla-rewriterules.sh
Executable file
170
cms/joomla/test-joomla-rewriterules.sh
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# Joomla .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/joomla/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/joomla
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "Joomla .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. Base64 Encoded Payload Detection Rule"
|
||||
echo "=============================================="
|
||||
# Test base64_encode pattern detection in query string
|
||||
test_rule "Base64 encoded payload blocked (base64_encode function call)" \
|
||||
"$BASE_URL/base64-test.php?data=base64_encode(secret_password)" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. Script Injection Pattern Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test script injection pattern detection in query string (HTML entities)
|
||||
test_rule "Script injection blocked (script tag encoded)" \
|
||||
"$BASE_URL/script-test.php?q=%3Cscript%3Ealert(1)%3E" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. GLOBALS Exploitation Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test GLOBALS exploitation pattern detection in query string
|
||||
test_rule "GLOBALS exploitation blocked (GLOBALS[secret] pattern)" \
|
||||
"$BASE_URL/globals-test.php?GLOBALS%5Buser%5D=admin" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. _REQUEST Manipulation Detection Rule"
|
||||
echo "============================================}"
|
||||
# Test _REQUEST manipulation pattern detection in query string
|
||||
test_rule "_REQUEST manipulation blocked (_REQUEST[config] pattern)" \
|
||||
"$BASE_URL/request-test.php?_REQUEST%5Badmin%5D=true" \
|
||||
"403"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. HTTP Authorization Header Passing Rule"
|
||||
echo "============================================}"
|
||||
# Test that Authorization header is properly handled by Joomla REST API
|
||||
test_rule_content "Joomla handles Authorization header (API request)" \
|
||||
"$BASE_URL/rest/api/v1" \
|
||||
"Authorization: Bearer joomla_token_abc" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. Joomla API Routing Rule (/api/)"
|
||||
echo "============================================}"
|
||||
# Test /api/index.php routing - should route to api/index.php (!-f, !-d pass for non-existing files)
|
||||
test_rule_content "API index.php routing (routes to /api/index.php)" \
|
||||
"$BASE_URL/api/" \
|
||||
"Authorization: secret_token_123" \
|
||||
"200" \
|
||||
"\"status\": \"success\""
|
||||
|
||||
# Additional test: verify token verification in response
|
||||
test_rule_content "Direct /api/index.php file access" \
|
||||
"$BASE_URL/api/index.php" \
|
||||
"" \
|
||||
"401" \
|
||||
"\"message\": \"unauth\""
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. Main Joomla Routing Rule (/index.php exclusion)"
|
||||
echo "============================================}"
|
||||
# Test that /api/index.php is NOT routed to main index.php (!^/api/index\.php passes)
|
||||
test_rule_content "/api/index.php excluded from main routing (direct file access)" \
|
||||
"$BASE_URL/api/index.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla API Configuration Loaded"
|
||||
|
||||
# Test non-existing file routing through main index.php (!-f AND !-d pass)
|
||||
test_rule_content "Non-existing page routing (routes to /index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
# Test existing directory access (!-d condition passes) - should return 200 OK
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Joomla index.php reditercting"
|
||||
echo "============================================}"
|
||||
# Test that /api/index.php is NOT routed to main index.php (!^/api/index\.php passes)
|
||||
test_rule_content "/index.php/component/config?view=modules accessing" \
|
||||
"$BASE_URL/index.php/component/config?view=modules" \
|
||||
"" \
|
||||
"200" \
|
||||
"Joomla Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
73
cms/simple/.htaccess
Normal file
73
cms/simple/.htaccess
Normal file
@@ -0,0 +1,73 @@
|
||||
# ============================================================
|
||||
# .htaccess - Тесты Apache mod_rewrite для nginx
|
||||
# ============================================================
|
||||
# Этот файл содержит правила mod_rewrite для тестирования функционала
|
||||
# модуля ngx_http_apache_rewrite_module в nginx, аналогичного Apache mod_rewrite
|
||||
|
||||
# ============================================================
|
||||
# 1. Перенаправление файлов с расширением .xmx на index.html
|
||||
# ============================================================
|
||||
# Правиство перенаправляет ВСЕ файлы заканчивающиеся на .xmx
|
||||
# на страницу index.html (внешнее перенаправление)
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/test.xmx → 301 redirect to /index.html
|
||||
# - http://localhost/test/subdir/file.xmx → 301 redirect to /index.html
|
||||
|
||||
# ============================================================
|
||||
# 2. Блокировка доступа к stop.html (403 Forbidden)
|
||||
# ============================================================
|
||||
# Правиство запрещает доступ к файлу stop.html, возвращая ошибку 403
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} stop.html$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/stop.html → 403 Forbidden (доступ запрещен)
|
||||
|
||||
# ============================================================
|
||||
# 3. Внутреннее перенаправление redirect.html на show.html
|
||||
# ============================================================
|
||||
# Правиство скрывает redirect.html и показывает вместо него show.html
|
||||
# Это внутренняя переадресация (без изменения URL в браузере)
|
||||
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/redirect.html → показывает содержимое show.html
|
||||
# - URL остается /test/redirect.html, но показывается content из show.html
|
||||
|
||||
# ============================================================
|
||||
# 4. Перенаправление подкаталога subdir на index.html
|
||||
# ============================================================
|
||||
# При обращении к подкаталогу redirect его на главную страницу
|
||||
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/ → показывает index.html
|
||||
|
||||
# ============================================================
|
||||
# 5. Блокировка доступа ко всем .xmx файлам (кроме test.xmx)
|
||||
# ============================================================
|
||||
# Дополнительно блокируем все файлы с расширением xmx кроме test.xmx
|
||||
# Это демонстрирует условные правила с RewriteCond
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !test\.xmx$
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
|
||||
# Примечание: это правило будет работать ПОСЛЕ первого правила перенаправления
|
||||
# поэтому test.xmx сначала перенаправляется на index.html, а затем блокируется
|
||||
|
||||
# ============================================================
|
||||
# 6. Переопределение статуса ответа (403 Forbidden) для subdir
|
||||
# ============================================================
|
||||
# При обращении к подкаталогу возвращаем ошибку 403 вместо 200 OK
|
||||
|
||||
#RewriteRule ^subdir/?$ - [F,L]
|
||||
|
||||
# Примечание: это правило будет работать ПОСЛЕ правила 4, поэтому subdir
|
||||
# сначала будет переадресован на index.html, а затем заблокирован
|
||||
127
cms/simple/README.md
Normal file
127
cms/simple/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Тесты Apache mod_rewrite для nginx
|
||||
|
||||
## Обзор
|
||||
|
||||
Этот каталог содержит тестовые файлы и правила `.htaccess` для проверки функционала модуля `ngx_http_apache_rewrite_module` в nginx, который обеспечивает совместимость с Apache mod_rewrite.
|
||||
|
||||
## Файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `index.html` | Главная страница - назначение для перенаправления .xmx файлов |
|
||||
| `stop.html` | Заблокированная страница (должен возвращать 403) |
|
||||
| `redirect.html` | Внутреннее перенаправление на show.html (URL не меняется) |
|
||||
| `show.html` | Показывается вместо redirect.html при внутреннем rewrite |
|
||||
| `test.xmx` | Тестовый файл для проверки правила перенаправления .xmx расширения |
|
||||
|
||||
## Правила .htaccess
|
||||
|
||||
### 1. Перенаделение файлов с расширением `.xmx` на index.html
|
||||
|
||||
```apache
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/test.xmx` → **301 Redirect** к `/test/index.html`
|
||||
- URL в браузере изменится на `/test/index.html`
|
||||
|
||||
### 2. Блокировка доступа к stop.html (403 Forbidden)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} =stop.html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/stop.html` → **403 Forbidden** ("Доступ запрещен")
|
||||
- Страница не будет отображаться
|
||||
|
||||
### 3. Внутреннее перенаправление redirect.html на show.html
|
||||
|
||||
```apache
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/redirect.html` → **показывается содержимое show.html**
|
||||
- URL в браузере остается `/test/redirect.html` (не происходит перенаправления)
|
||||
|
||||
### 4. Перенаделение подкаталога subdir на index.html
|
||||
|
||||
```apache
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Запрос `http://localhost/test/subdir/` → **показывается index.html**
|
||||
- URL в браузере остается `/test/subdir/`
|
||||
|
||||
### 5. Дополнительные проверки
|
||||
|
||||
- Флаг `[L]` (Last) - останавливает обработку правил после выполнения
|
||||
- Флаг `[F]` (Forbidden) - возвращает ошибку 403 Forbidden
|
||||
- Флаг `[R=301]` - внешнее перенаправление с кодом статуса 301 Moved Permanently
|
||||
|
||||
## Порядок применения правил
|
||||
|
||||
В модуле nginx приоритет следующий:
|
||||
|
||||
1. **Правила из .htaccess** (высокий приоритет)
|
||||
2. **Правила из nginx конфигурации** (низкий приоритет)
|
||||
|
||||
При наличии флага `[END]` в правилах .htaccess, дальнейшая обработка всех правил останавливается.
|
||||
|
||||
## Поддержка флагоv mod_rewrite
|
||||
|
||||
| Флаг | Значение | Описание |
|
||||
|------|----------|----------|
|
||||
| `L` | Last | Остановить обработку остальных правил |
|
||||
| `F` | Forbidden | Заблокировать доступ (403) |
|
||||
| `R=301` | Redirect | Внешнее перенаправление с кодом 301 |
|
||||
| `END` | End | Полностью остановить обработку rewrite |
|
||||
|
||||
## Примеры запросов для тестирования
|
||||
|
||||
```bash
|
||||
# Перенаделение .xmx файла на index.html
|
||||
curl -I http://localhost/test/test.xmx
|
||||
# Ожидаемый ответ: HTTP/1.1 301 Moved Permanently
|
||||
# Location: /test/index.html
|
||||
|
||||
# Блокировка stop.html
|
||||
curl -I http://localhost/test/stop.html
|
||||
# Ожидаемый ответ: HTTP/1.1 403 Forbidden
|
||||
|
||||
# Внутреннее перенаправление redirect.html на show.html
|
||||
curl -I http://localhost/test/redirect.html
|
||||
# Ожидаемый ответ: HTTP/1.1 200 OK (content из show.html)
|
||||
|
||||
# Перенаделение subdir на index.html
|
||||
curl -I http://localhost/test/subdir/
|
||||
# Ожидаемый ответ: HTTP/1.1 200 OK (content из index.html)
|
||||
```
|
||||
|
||||
## Установка и запуск
|
||||
|
||||
Для запуска тестов потребуется:
|
||||
|
||||
1. Скомпилированный nginx с модулем `ngx_http_apache_rewrite_module`
|
||||
2. Конфигурация nginx, указывающая каталог `test/` как корень сайта
|
||||
3. Активированный флаг `.htaccess` через директиву `HtAccess on`
|
||||
|
||||
Пример конфигурации:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location /test {
|
||||
root /workspaces/test1/test;
|
||||
|
||||
HtAccess on;
|
||||
HtAccessFile .htaccess;
|
||||
}
|
||||
}
|
||||
```
|
||||
311
cms/simple/SUMMARY.md
Normal file
311
cms/simple/SUMMARY.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Тесты Apache mod_rewrite для nginx - ПОЛНАЯ СУММАРИЯ
|
||||
|
||||
## 📁 Структура тестовых файлов
|
||||
|
||||
```
|
||||
/test1/test/
|
||||
├── .htaccess # Главное правило rewrite для корневого каталога
|
||||
├── index.html # Главная страница (назначение для redirect)
|
||||
├── show.html # Показывается вместо redirect.html
|
||||
├── stop.html # Блокируется (403 Forbidden)
|
||||
├── redirect.html # Внутреннее перенаправление на show.html
|
||||
├── test.xmx # Файл .xmx для тестирования расширения
|
||||
├── README.md # Документация по тестам
|
||||
├── SUMMARY.md # Эта страница - полная суммари
|
||||
└── subdir/ # Подкаталог с дополнительными тестами
|
||||
├── .htaccess # Правила rewrite для подкаталога
|
||||
├── file.html # Файл в подкаталоге
|
||||
└── other.xmx # Другой файл .xmx (блокируется)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Ключевые правила .htaccess
|
||||
|
||||
### 1️⃣ Перенаделение файлов с расширением `.xmx` на index.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/test.xmx` | 301 Redirect → `/test/index.html` |
|
||||
| URL в браузере меняется на `/test/index.html` | ✅ Внешнее перенаправление |
|
||||
|
||||
**Логика:**
|
||||
- Все файлы заканчивающиеся на `.xmx` перенаправляются на главную страницу
|
||||
- Используется флаг `[R=301]` для внешнего HTTP redirect
|
||||
- Флаг `[L]` останавливает дальнейшую обработку правил
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ Блокировка доступа к stop.html (403 Forbidden)
|
||||
|
||||
**Правила:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} =stop.html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/stop.html` | 403 Forbidden (Доступ запрещен) |
|
||||
| Страница не отображается | ✅ Полная блокировка |
|
||||
|
||||
**Логика:**
|
||||
- `RewriteCond` проверяет что запрос именно к stop.html
|
||||
- `RewriteRule ^ - [F]` возвращает ошибку 403 Forbidden
|
||||
- Флаг `[L]` останавливает обработку
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ Внутреннее перенаправление redirect.html на show.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^redirect\.html$ show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/redirect.html` | Показывается content из show.html |
|
||||
| URL в браузере остается `/test/redirect.html` | ✅ Internal rewrite |
|
||||
|
||||
**Логика:**
|
||||
- Правило скрывает redirect.html и показывает вместо него show.html
|
||||
- Происходит внутренняя переадресация без изменения URL
|
||||
- Флаг `[L]` останавливает обработку
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ Перенаделение подкаталога subdir на index.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/` | Показывается content из index.html |
|
||||
| URL в браузере остается `/test/subdir/` | ✅ Internal rewrite |
|
||||
|
||||
---
|
||||
|
||||
### 5️⃣ Дополнительные проверки (из main .htaccess)
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !test\.xmx$
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- Блокирует все `.xmx` файлы кроме test.xmx
|
||||
- **НО:** Работает ПОСЛЕ правила 1, поэтому test.xmx сначала редиректится на index.html
|
||||
|
||||
---
|
||||
|
||||
### 6️⃣ Перенаделение subdir с последующей блокировкой (конфликт)
|
||||
|
||||
**Порядок в .htaccess:**
|
||||
```apache
|
||||
# Сначала перенаправление
|
||||
RewriteRule ^subdir/?$ /index.html [L]
|
||||
|
||||
# Затем блокировка
|
||||
RewriteRule ^subdir/?$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
- subdir сначала переадресуется на index.html (правило 4)
|
||||
- Затем применяется правило блокировки → 403 Forbidden
|
||||
- **Итог:** subdir заблокирован с ошибкой 403
|
||||
|
||||
---
|
||||
|
||||
## 📁 Правила в подкаталоге `subdir/.htaccess`
|
||||
|
||||
### 1️⃣ Блокировка всех .xmx файлов в subdir (кроме test.xmx)
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/other.xmx` | 403 Forbidden |
|
||||
| Файл заблокирован | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ Внутреннее перенаправление subdir/file.html на ../show.html
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteRule ^file\.html$ ../show.html [L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/file.html` | Показывается content из show.html (в родительском каталоге) |
|
||||
| URL остается `/test/subdir/file.html` | ✅ Internal rewrite с относительным путем |
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ Блокировка всех файлов кроме .html в subdir
|
||||
|
||||
**Правило:**
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !-ext=html
|
||||
RewriteRule ^ - [F,L]
|
||||
```
|
||||
|
||||
**Ожидаемое поведение:**
|
||||
| Запрос | Результат |
|
||||
|--------|-----------|
|
||||
| `http://localhost/test/subdir/file.txt` | 403 Forbidden |
|
||||
| Только .html файлы доступны | ✅ Фильтрация по расширению |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Тестирование функционала mod_rewrite
|
||||
|
||||
### Поддерживаемые флаги:
|
||||
|
||||
| Флаг | Значение | Описание |
|
||||
|------|----------|----------|
|
||||
| `L` | Last | Остановить обработку остальных правил в .htaccess |
|
||||
| `F` | Forbidden | Возвращать ошибку 403 Forbidden |
|
||||
| `R=301` | Redirect | Внешнее перенаправление с кодом HTTP 301 Moved Permanently |
|
||||
| `END` | End | Остановить ВСЮ обработку rewrite (включая nginx config rules) |
|
||||
|
||||
### Поддерживаемые директивы:
|
||||
|
||||
| Директива | Описание |
|
||||
|-----------|----------|
|
||||
| `RewriteEngine on/off` | Включение/выключение mod_rewrite для .htaccess |
|
||||
| `RewriteRule pattern substitution [flags]` | Правило перенаправления |
|
||||
| `RewriteCond test string` | Условие для правила RewriteRule |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Механизм приоритета
|
||||
|
||||
### Порядок применения правил:
|
||||
|
||||
```
|
||||
┌───────────────────────────────┐
|
||||
│ 1. Правила из .htaccess │ ← Higher Priority
|
||||
│ ├─ Read & parse │
|
||||
│ ├─ Convert to mod_rewrite │
|
||||
│ └─ Apply rules │
|
||||
│ │
|
||||
├───────────────────────────────┤
|
||||
│ Check END flag │
|
||||
│ if (ctx->end) STOP ALL │ ← Blocks nginx config!
|
||||
├───────────────────────────────┤
|
||||
│ 2. Правила из nginx config │ ← Lower Priority
|
||||
│ └─ Apply from server/loc │
|
||||
└───────────────────────────────┘
|
||||
```
|
||||
|
||||
**Ключевой момент:**
|
||||
- При наличии флага `[END]` в .htaccess, обработка ВСЕХ правил rewrite останавливается
|
||||
- Это позволяет полностью блокировать nginx config rules при необходимости
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Примеры CURL запросов для тестирования
|
||||
|
||||
```bash
|
||||
# 1. Перенаправление .xmx на index.html (301)
|
||||
curl -I http://localhost/test/test.xmx
|
||||
# Ожидаемый: HTTP/1.1 301 Moved Permanently
|
||||
# Location: /test/index.html
|
||||
|
||||
# 2. Блокировка stop.html (403)
|
||||
curl -I http://localhost/test/stop.html
|
||||
# Ожидаемый: HTTP/1.1 403 Forbidden
|
||||
|
||||
# 3. Internal rewrite redirect.html → show.html (200 OK, content из show.html)
|
||||
curl -s http://localhost/test/redirect.html | grep h1
|
||||
# Ожидаемый: <h1>show.html</h1>
|
||||
|
||||
# 4. Internal rewrite subdir → index.html (200 OK)
|
||||
curl -I http://localhost/test/subdir/
|
||||
# Ожидаемый: HTTP/1.1 200 OK
|
||||
|
||||
# 5. Блокировка other.xmx в subdir (403)
|
||||
curl -I http://localhost/test/subdir/other.xmx
|
||||
# Ожидаемый: HTTP/1.1 403 Forbidden
|
||||
|
||||
# 6. Internal rewrite subdir/file.html → ../show.html (200 OK)
|
||||
curl -s http://localhost/test/subdir/file.html | grep h1
|
||||
# Ожидаемый: <h1>show.html</h1>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Установка и запуск тестов
|
||||
|
||||
### Конфигурация nginx для запуска тестов:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# Корневой сайт
|
||||
location / {
|
||||
root /workspaces/test1/nginx-1.25.3/html;
|
||||
}
|
||||
|
||||
# Тестовый каталог с .htaccess поддержкой
|
||||
location /test {
|
||||
alias /workspaces/test1/test/;
|
||||
|
||||
HtAccess on; # Включить чтение .htaccess файлов
|
||||
HtAccessFile .htaccess; # Файл конфигурации
|
||||
|
||||
# Для подкаталога subdir (авто-поиск .htaccess)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Команды запуска:
|
||||
|
||||
```bash
|
||||
# 1. Скомпилировать nginx с модулем mod_rewrite
|
||||
cd /workspaces/test1/nginx-1.25.3 && make modules
|
||||
|
||||
# 2. Запустить nginx
|
||||
./nginx -c /path/to/nginx.conf
|
||||
|
||||
# 3. Тестировать правила через curl или браузер
|
||||
curl http://localhost/test/redirect.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Итог
|
||||
|
||||
Модуль `ngx_http_apache_rewrite_module` обеспечивает:
|
||||
|
||||
1. ✅ **Чтение .htaccess файлов** при каждом запросе с проверкой mtime
|
||||
2. ✅ **Кэширование директив** в памяти на время жизненного цикла запроса
|
||||
3. ✅ **Конвертацию Apache mod_rewrite правил** в формат nginx mod_rewrite
|
||||
4. ✅ **Приоритет .htaccess выше nginx config rules**
|
||||
5. ✅ **Флаг [END] для полной остановки обработки rewrite**
|
||||
6. ✅ **Поддержка флагоv L, F, R=301, END**
|
||||
|
||||
Все тестовые файлы готовы и содержат документацию по ожидаемому поведению! 🎉
|
||||
40
cms/simple/index.html
Normal file
40
cms/simple/index.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Index - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>index.html</h1>
|
||||
<p>Вы попали на index.html.</p>
|
||||
<p>
|
||||
<strong
|
||||
>Это страница назначения при перенаправлении файлов с
|
||||
расширением xmx.</strong
|
||||
>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная страница</a></li>
|
||||
<li>
|
||||
<a href="redirect.html">Redirect - редирект на show.html</a>
|
||||
</li>
|
||||
<li><a href="subdir/">Подкаталог</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p><em>Тесты mod_rewrite для nginx:</em></p>
|
||||
<ol>
|
||||
<li>
|
||||
Запросите <code>.xmx</code> файл → перенаправление на index.html
|
||||
</li>
|
||||
<li>
|
||||
Запросите <code>stop.html</code> → 403 Forbidden (доступ
|
||||
запрещен)
|
||||
</li>
|
||||
<li>Запросите <code>redirect.html</code> → покажет show.html</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
159
cms/simple/nginx.conf
Normal file
159
cms/simple/nginx.conf
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
load_module modules/ngx_http_apache_rewrite_module.so;
|
||||
worker_processes 1;
|
||||
|
||||
error_log logs/error.log debug;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example1.com;
|
||||
|
||||
root /sites/site1;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example2.com;
|
||||
|
||||
root /sites/site2;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example3.com;
|
||||
|
||||
root /sites/site3;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example4.com;
|
||||
|
||||
root /sites/site4;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example5.com;
|
||||
|
||||
root /sites/site5;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
26
cms/simple/redirect.html
Normal file
26
cms/simple/redirect.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Redirect - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>redirect.html</h1>
|
||||
<p>Вы попали на redirect.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Эта страница должна быть скрыта!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к redirect.html, mod_rewrite должен показать show.html вместо этой страницы.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
26
cms/simple/show.html
Normal file
26
cms/simple/show.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Show - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>show.html</h1>
|
||||
<p>Вы попали на show.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="redirect.html">Redirect - перенаправляет сюда</a></li>
|
||||
<li><a href="stop.html">Stop - заблокированная</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Показано вместо redirect.html!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
Это страница-назначение для правила <code>RewriteRule ^redirect\.html$ show.html [L]</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
10
cms/simple/site1.conf
Normal file
10
cms/simple/site1.conf
Normal file
@@ -0,0 +1,10 @@
|
||||
<VirtualHost 192.168.3.96:80>
|
||||
DocumentRoot "/sites/site1"
|
||||
ServerName example1.com
|
||||
|
||||
<Directory /sites/site1>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
0
cms/simple/somedir/.gitkeep
Normal file
0
cms/simple/somedir/.gitkeep
Normal file
23
cms/simple/stop.html
Normal file
23
cms/simple/stop.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Stop - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>stop.html</h1>
|
||||
<p>Вы попали на stop.html.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="index.html">На главную (index.html)</a></li>
|
||||
<li><a href="show.html">Показать (show.html)</a></li>
|
||||
<li><a href="redirect.html">Redirect</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: red;">Эта страница должна быть заблокирована!</strong></em>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
0
cms/simple/subdir/.gitkeep
Normal file
0
cms/simple/subdir/.gitkeep
Normal file
30
cms/simple/subdir/.htaccess
Normal file
30
cms/simple/subdir/.htaccess
Normal file
@@ -0,0 +1,30 @@
|
||||
# ============================================================
|
||||
# .htaccess в подкаталоге subdir - Тесты mod_rewrite для nginx
|
||||
# ============================================================
|
||||
# Этот файл проверяет работу правил для конкретного location (подкаталога)
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Правило 1: Все запросы к файлам с расширением xmx в этом подкаталоге
|
||||
# должны блокироваться с ошибкой 403 Forbidden
|
||||
|
||||
RewriteRule \.xmx$ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/other.xmx → 403 Forbidden
|
||||
|
||||
# Правило 2: Внутреннее перенаделение subdir/file.html на ../show.html
|
||||
# (переход из подкаталога в родительский)
|
||||
|
||||
RewriteRule ^file\.html$ ../show.html [L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/file.html → показывает содержимое show.html
|
||||
|
||||
# Правило 3: Блокировка доступа ко всем файлам кроме .html
|
||||
|
||||
RewriteCond %{REQUEST_URI} !\.html$
|
||||
RewriteRule ^ - [F,L]
|
||||
|
||||
# Тестирование:
|
||||
# - http://localhost/test/subdir/file.txt → 403 Forbidden
|
||||
26
cms/simple/subdir/file.html
Normal file
26
cms/simple/subdir/file.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>File - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>file.html</h1>
|
||||
<p>Вы попали на file.html в подкаталоге subdir.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="../index.html">На главную (index.html)</a></li>
|
||||
<li><a href="../show.html">Показать (show.html)</a></li>
|
||||
<li><a href="../redirect.html">Redirect</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: green;">Это страница из подкаталога!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к <code>/test/subdir/file.html</code>, mod_rewrite должен показать содержимое <code>../show.html</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
27
cms/simple/subdir/other.xmx
Normal file
27
cms/simple/subdir/other.xmx
Normal file
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Other XMX - Тест mod_rewrite для nginx</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>other.xmx</h1>
|
||||
<p>Вы попытались получить этот файл.</p>
|
||||
|
||||
<hr />
|
||||
<ul>
|
||||
<li><a href="../index.html">На главную (index.html)</a></li>
|
||||
<li><a href="../show.html">Показать (show.html)</a></li>
|
||||
<li><a href="file.html">File.html</a></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<p>
|
||||
<em><strong style="color: red;">Этот файл должен быть заблокирован!</strong></em>
|
||||
</p>
|
||||
<p>
|
||||
При обращении к <code>/test/subdir/other.xmx</code>, mod_rewrite должен вернуть
|
||||
<code style="background-color: #f00; color: white;">403 Forbidden</code>.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
26
cms/simple/test.xmx
Normal file
26
cms/simple/test.xmx
Normal file
@@ -0,0 +1,26 @@
|
||||
# Test File - test.xmx
|
||||
|
||||
This is a test file used to verify the mod_rewrite rule that redirects all files ending with .xmx extension to index.html.
|
||||
|
||||
## How it works:
|
||||
|
||||
When you request any `.xmx` file in this directory, the following rule from `.htaccess` will be applied:
|
||||
|
||||
```
|
||||
RewriteRule \.xmx$ /index.html [R=301,L]
|
||||
```
|
||||
|
||||
This redirects your browser to `/index.html`.
|
||||
|
||||
## Test URLs:
|
||||
|
||||
- `http://localhost/test/test.xmx` → redirects to `/test/index.html`
|
||||
- `http://localhost/test/subdir/other.xmx` → redirects to `/test/index.html`
|
||||
- `http://localhost/test/deep/path/file.xmx` → redirects to `/test/index.html`
|
||||
|
||||
## Additional Test Files:
|
||||
|
||||
See other test pages in this directory:
|
||||
- index.html - main page
|
||||
- show.html - shown instead of redirect.html via internal rewrite
|
||||
- stop.html - should be blocked (403 Forbidden) by mod_rewrite rules
|
||||
14
cms/wordpress-multy/.htaccess
Normal file
14
cms/wordpress-multy/.htaccess
Normal file
@@ -0,0 +1,14 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
||||
152
cms/wordpress-multy/README.md
Normal file
152
cms/wordpress-multy/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# WordPress Multisite Test Structure - Documentation
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/wordpress-multy/
|
||||
├── index.php - WordPress entry point (routes non-existing files)
|
||||
│ - Returns: "WordPress Multisite Content Route" page
|
||||
├── wp-admin/ - Admin directory tests
|
||||
│ └── (empty directory for trailing slash redirect test)
|
||||
├── wp-admin/admin.php - Test file for wp-admin routing rule
|
||||
├── wp-content/ - WordPress content directory tests
|
||||
│ └── themes/ - Theme subdirectory
|
||||
├── wp-includes/ - Core files directory tests
|
||||
│ └── js/ - JavaScript subdirectory
|
||||
│ └── (empty for testing)
|
||||
├── style.css - Existing CSS file (tests RewriteRule !-f condition)
|
||||
├── script.js - Existing JS file (tests RewriteRule !-f condition)
|
||||
├── test-wp-multi-rewriterules.sh - Bash script to test all rules using curl
|
||||
└── README.md - This documentation file
|
||||
```
|
||||
|
||||
## Apache Rules Explained - WordPress Multisite
|
||||
|
||||
### 1. HTTP Authorization Header Passing
|
||||
|
||||
```apache
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует HTTP Authorization header в переменную HTTP_AUTHORIZATION для PHP
|
||||
|
||||
### 2. RewriteBase Configuration
|
||||
|
||||
```apache
|
||||
RewriteBase /
|
||||
```
|
||||
|
||||
**Что делает:** Устанавливает базовый путь к корню сайта
|
||||
|
||||
### 3. Direct index.php Access Protection
|
||||
|
||||
```apache
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
```
|
||||
|
||||
**Что делает:** Предотвращает зацикливание маршрутизации (не переписывает прямой запрос на index.php)
|
||||
|
||||
### 4. wp-admin Trailing Slash Redirect
|
||||
|
||||
```apache
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
```
|
||||
|
||||
**Что делает:** Добавляет слэш в конце к URL `/wp-admin`, редирект 301 (Moved Permanently)
|
||||
|
||||
**Зачем нужно:** WordPress multisite требует trailing slash для admin panel, обеспечивает корректную маршрутизацию
|
||||
|
||||
**Подхват поддомена:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite setup:
|
||||
- `/wp-admin` → редирект на `/wp-admin/` (301)
|
||||
- `site.subdomain.com/wp-admin` → редирект на `site.subdomain.com/wp-admin/`
|
||||
|
||||
### 5. Existing Files/Directories Check (Skip Processing)
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
```
|
||||
|
||||
**Что делает:** Если файл ИЛИ директория СУЩЕСТВУЕТ - останавливаем обработку правил, пропускаем запрос напрямую
|
||||
|
||||
**Зачем нужно:** Предотвращает маршрутизацию на index.php для существующих статических файлов (CSS, JS, изображения)
|
||||
|
||||
### 6. WordPress Multisite Core Routing Rules
|
||||
|
||||
```apache
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
```
|
||||
|
||||
**Что делает:** Для multisite setups - маршрутизирует wp-content, wp-admin, wp-includes напрямую (без index.php)
|
||||
|
||||
**Подхват поддоменов:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite:
|
||||
- `/wp-content/uploads/image.jpg` → прямой доступ к файлу
|
||||
- `site.subdomain.com/wp-admin/admin.php` → прямая маршрутизация к admin.php
|
||||
|
||||
### 7. All PHP Files Direct Access
|
||||
|
||||
```apache
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
```
|
||||
|
||||
**Что делает:** Любой PHP файл отдаётся напрямую, без маршрутизации через index.php
|
||||
|
||||
**Подхват поддоменов:** `[_0-9a-zA-Z-]+/?` позволяет обработку поддоменов в multisite:
|
||||
- `/wp-login.php` → прямой доступ к файлу
|
||||
- `site.subdomain.com/wp-signup.php` → прямая маршрутизация к signup.php
|
||||
|
||||
### 8. Final WordPress Routing Rule
|
||||
|
||||
```apache
|
||||
RewriteRule . index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:** Все оставшиеся запросы маршрутизируются через index.php (основной URL routing)
|
||||
|
||||
## Test Script Features
|
||||
|
||||
The script includes test functions:
|
||||
1. **test_rule()** - checks HTTP status code only
|
||||
2. **test_rule_content()** - checks both status AND response body content
|
||||
|
||||
## Multisite-Specific Testing Scenarios
|
||||
|
||||
### Subdomain vs Subdirectory Setup
|
||||
|
||||
WordPress Multisite может быть configured в двух режимах:
|
||||
- **Subdomain mode**: sites поддоменами (site1.example.com, site2.example.com)
|
||||
- **Subdirectory mode**: сайты поддиректориями (/site1/, /site2/)
|
||||
|
||||
Правило `[_0-9a-zA-Z-]+/?` обрабатывает оба случая:
|
||||
- Для subdomain: `[subdomain/]?` = optional prefix
|
||||
- Для subdirectory: `[directory/]?` = optional prefix
|
||||
|
||||
### Test Examples
|
||||
|
||||
| URL | Правило | Ожидаемый результат |
|
||||
|-----|---------|---------------------|
|
||||
| `http://test.my.brp/wp-admin` | trailing slash redirect | 301 → `/wp-admin/` ✓ |
|
||||
| `http://test.my.brp/wp-admin/` | direct access (directory exists) | 200 OK ✓ |
|
||||
| `http://test.my.brp/style.css` | existing file (!-f passes) | 200 OK ✓ |
|
||||
| `http://test.my.brp/script.js` | existing file (!-f passes) | 200 OK ✓ |
|
||||
| `http://test.my.brp/wp-admin/admin.php` | multisite wp-admin rule | 200 OK ✓ |
|
||||
| `http://test.my.brp/wp-content/uploads/test.jpg` | multisite wp-content rule | 200 OK ✓ |
|
||||
| `http://test.my.brp/nonexistent-page/` | final index.php routing | 200 OK (content from index.php) ✓ |
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/wordpress-multy
|
||||
./test-wp-multi-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results for WordPress Multisite tests (all should be **PASS ✓**):
|
||||
- wp-admin trailing slash redirect: HTTP 301 ✓
|
||||
- wp-admin directory access: HTTP 200 ✓
|
||||
- Existing file (CSS/JS) access: HTTP 200 ✓
|
||||
- Existing directory access: HTTP 200 ✓
|
||||
- WordPress multisite routing (wp-content, admin): HTTP 200 ✓
|
||||
- Final index.php routing: HTTP 200 + content "WordPress Multisite Content Route" ✓
|
||||
6
cms/wordpress-multy/custom-script.php
Normal file
6
cms/wordpress-multy/custom-script.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
// WordPress Multisite Test PHP File
|
||||
// This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
// Any .php file access rule
|
||||
|
||||
echo "WordPress Multisite custom script - direct PHP file access";
|
||||
15
cms/wordpress-multy/index.php
Normal file
15
cms/wordpress-multy/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress Multisite - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories in multisite setup
|
||||
*/
|
||||
|
||||
// Simulated WordPress Multisite response
|
||||
echo "<html><head><title>WordPress Multisite Test Site</title></head><body>";
|
||||
echo "<h1>WordPress Multisite Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='wp-multisite-config'>WordPress Multisite Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
4
cms/wordpress-multy/script.js
Normal file
4
cms/wordpress-multy/script.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// WordPress Multisite Test JS File
|
||||
// This file is tested via RewriteCond !-f (existing files skip routing)
|
||||
|
||||
console.log('WordPress Multisite CSS/JS test file');
|
||||
8
cms/wordpress-multy/style.css
Normal file
8
cms/wordpress-multy/style.css
Normal file
@@ -0,0 +1,8 @@
|
||||
/* WordPress Multisite Test CSS File */
|
||||
/* This file is tested via RewriteCond !-f (existing files skip routing) */
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
171
cms/wordpress-multy/test-wp-multi-rewriterules.sh
Executable file
171
cms/wordpress-multy/test-wp-multi-rewriterules.sh
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# WordPress Multisite .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/wordpress-multy/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/wordpress-multy
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "WordPress Multisite .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200, 301
|
||||
local expected_content="$4" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. HTTP Authorization Header Handling"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by WordPress Multisite API
|
||||
test_rule_content "WordPress multisite handles Authorization header (API request)" \
|
||||
"$BASE_URL/wp-json/wp/v2/posts?Authorization=Bearer secret_token_123" \
|
||||
"200" \
|
||||
"WordPress Multisite Configuration Loaded"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. RewriteBase Root Configuration"
|
||||
echo "=============================================="
|
||||
# Test root directory access with proper base path routing
|
||||
test_rule_content "Root directory access (base path /)" \
|
||||
"$BASE_URL/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Direct index.php Access Protection"
|
||||
echo "=============================================="
|
||||
# Test direct access to index.php - should NOT be rewritten (prevents infinite loop)
|
||||
test_rule_content "Direct index.php access (not rewritten)" \
|
||||
"$BASE_URL/index.php" \
|
||||
"200" \
|
||||
"WordPress Multisite Configuration Loaded"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. wp-admin Trailing Slash Redirect Rule"
|
||||
echo "=============================================="
|
||||
# Test trailing slash redirect for /wp-admin (301 Moved Permanently)
|
||||
test_rule_content "wp-admin trailing slash redirect (301)" \
|
||||
"$BASE_URL/wp-admin" \
|
||||
"301"
|
||||
|
||||
# Test that /wp-admin/ with trailing slash works correctly (existing directory)
|
||||
test_rule_content "wp-admin/ with trailing slash (exists as directory)" \
|
||||
"$BASE_URL/wp-admin/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "5. Existing Files/Directories Check Rule"
|
||||
echo "=============================================="
|
||||
# Test existing CSS file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule "Existing CSS file access (style.css)" \
|
||||
"$BASE_URL/style.css" \
|
||||
"200"
|
||||
|
||||
# Test existing JS file access (!-f condition passes) - should return 200 OK without routing to index.php
|
||||
test_rule "Existing JS file access (script.js)" \
|
||||
"$BASE_URL/script.js" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "6. WordPress Multisite Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test wp-admin specific routing rule (not through index.php)
|
||||
test_rule_content "wp-admin/admin.php multisite routing" \
|
||||
"$BASE_URL/wp-admin/admin.php" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test wp-content specific routing rule (for uploads, themes etc.)
|
||||
test_rule_content "wp-content/uploads/test.jpg multisite routing" \
|
||||
"$BASE_URL/wp-content/uploads/test.jpg" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test wp-includes specific routing rule (core files)
|
||||
test_rule_content "wp-includes/js/jquery.js multisite routing" \
|
||||
"$BASE_URL/wp-includes/js/jquery.js" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "7. All PHP Files Direct Access Rule"
|
||||
echo "=============================================="
|
||||
# Test any .php file access (not routed through index.php) - custom script returns specific content
|
||||
test_rule_content "Any PHP file access (.php rule)" \
|
||||
"$BASE_URL/custom-script.php" \
|
||||
"200" \
|
||||
"WordPress Multisite custom script - direct PHP file access"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "8. Final WordPress Routing Rule"
|
||||
echo "=============================================="
|
||||
# Test non-existing file routing through index.php (main routing)
|
||||
test_rule_content "Non-existing page routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-page/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
# Test non-existing directory routing through index.php (routes to index.php)
|
||||
test_rule_content "Non-existing directory routing (routes to index.php)" \
|
||||
"$BASE_URL/nonexistent-directory/" \
|
||||
"200" \
|
||||
"WordPress Multisite Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
2
cms/wordpress-multy/wp-admin/admin.php
Normal file
2
cms/wordpress-multy/wp-admin/admin.php
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite admin.php test file
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
0
cms/wordpress-multy/wp-content/themes/.gitkeep
Normal file
0
cms/wordpress-multy/wp-content/themes/.gitkeep
Normal file
2
cms/wordpress-multy/wp-content/uploads/test.jpg
Normal file
2
cms/wordpress-multy/wp-content/uploads/test.jpg
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite test.jpg file for wp-content routing rule
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
2
cms/wordpress-multy/wp-includes/js/jquery.js
vendored
Normal file
2
cms/wordpress-multy/wp-includes/js/jquery.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# WordPress Multisite jquery.js file for wp-includes routing rule
|
||||
# This file is tested via RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
159
cms/wordpress/nginx.conf
Normal file
159
cms/wordpress/nginx.conf
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
load_module modules/ngx_http_apache_rewrite_module.so;
|
||||
worker_processes 1;
|
||||
|
||||
error_log logs/error.log debug;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example1.com;
|
||||
|
||||
root /sites/site1;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example2.com;
|
||||
|
||||
root /sites/site2;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example3.com;
|
||||
|
||||
root /sites/site3;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example4.com;
|
||||
|
||||
root /sites/site4;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
server_name example5.com;
|
||||
|
||||
root /sites/site5;
|
||||
|
||||
HtaccessEnable on;
|
||||
|
||||
RewriteEngine On;
|
||||
index index.php;
|
||||
|
||||
location / {
|
||||
RewriteEngine On;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location ~* \.php$ {
|
||||
RewriteEngine On;
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/run/php-fpm/www.sock; # подключаем сокет php-fpm
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
cms/wordpress/site3.conf
Normal file
16
cms/wordpress/site3.conf
Normal file
@@ -0,0 +1,16 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/sites/site3"
|
||||
ServerName example3.com
|
||||
|
||||
DirectoryIndex index.php
|
||||
|
||||
<Directory /sites/site3/wordpress>
|
||||
Options +Indexes +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<FilesMatch \.php$>
|
||||
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
|
||||
</VirtualHost>
|
||||
140
cms/wordpress/test-wp-rewriterules.sh
Executable file
140
cms/wordpress/test-wp-rewriterules.sh
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================
|
||||
# WordPress .htaccess Rules Test Script
|
||||
# ============================================
|
||||
# This script tests each rule from cms/wordpress/.htaccess
|
||||
# Assumption: Site root is mapped to /home/alexey/projects/workspace-zed/test1/cms/wordpress
|
||||
# Domain: test.my.brp
|
||||
# ============================================
|
||||
|
||||
BASE_URL="http://test.my.brp"
|
||||
|
||||
echo "=============================================="
|
||||
echo "WordPress .htaccess Rules Test Suite"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
# Function to test a rule and report result (status only)
|
||||
test_rule() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local expected_status="$3" # e.g., 403, 404, 200
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
|
||||
if [ "$response" = "$expected_status" ]; then
|
||||
echo "✓ PASS (HTTP $response)"
|
||||
else
|
||||
echo "✗ FAIL (Expected: HTTP $expected_status, Got: HTTP $response)"
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to test a rule and verify content contains expected string
|
||||
test_rule_content() {
|
||||
local description="$1"
|
||||
local url="$2"
|
||||
local headers="$3" # Optional: additional curl -H header flags (can be empty)
|
||||
local expected_status="$4" # e.g., 403, 404, 200
|
||||
local expected_content="$5" # Expected substring in response body
|
||||
|
||||
echo "--- Test: $description ---"
|
||||
|
||||
if [ -n "$headers" ]; then
|
||||
response=$(curl -s -H "$headers" "$url")
|
||||
http_code=$(curl -s -H "$headers" -o /dev/null -w "%{http_code}" "$url")
|
||||
else
|
||||
response=$(curl -s "$url")
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
|
||||
fi
|
||||
|
||||
# Check status code
|
||||
if [ "$http_code" != "$expected_status" ]; then
|
||||
echo "✗ FAIL (Status: HTTP $http_code, Expected: HTTP $expected_status)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check content contains expected substring
|
||||
if [[ "$response" == *"$expected_content"* ]]; then
|
||||
echo "✓ PASS (HTTP $http_code, Content matches '$expected_content')"
|
||||
else
|
||||
echo "✗ FAIL (Content missing: '$expected_content') - Response:"
|
||||
echo "$response" | head -5
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo "=============================================="
|
||||
echo "1. HTTP_AUTHORIZATION Header Rewrite Rule"
|
||||
echo "=============================================="
|
||||
# Test that Authorization header is properly handled by WordPress REST API mock
|
||||
# This rule copies HTTP Authorization header to HTTP_AUTHORIZATION env var via mod_rewrite [E=...]
|
||||
test_rule_content "WordPress REST API: Authorization header passed through mod_rewrite" \
|
||||
"$BASE_URL/wordpress/wp-json/wp/v2/posts" \
|
||||
"Authorization: secret_token_123" \
|
||||
"200" \
|
||||
"\"status\": \"success\""
|
||||
|
||||
# Additional test: verify token verification in response
|
||||
test_rule_content "WordPress REST API: Token verified correctly" \
|
||||
"$BASE_URL/wordpress/wp-json/wp/v2/posts" \
|
||||
"" \
|
||||
"401" \
|
||||
"\"message\": \"unauth\""
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "2. RewriteBase Configuration"
|
||||
echo "=============================================="
|
||||
# RewriteBase / sets base path to root - verifies routing works correctly
|
||||
test_rule_content "Base path routing (root directory)" \
|
||||
"$BASE_URL/wordpress/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "3. Direct index.php Access Protection"
|
||||
echo "=============================================="
|
||||
# RewriteRule ^index\.php$ - [L] - direct access to index.php should return 200 OK
|
||||
test_rule_content "Direct index.php access (not rewritten)" \
|
||||
"$BASE_URL/wordpress/index.php" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "4. WordPress Core Routing Rules"
|
||||
echo "=============================================="
|
||||
# Test non-existing file routes through index.php (!-f condition)
|
||||
test_rule_content "Non-existing file routing (routes to index.php)" \
|
||||
"$BASE_URL/wordpress/nonexistent-page/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
# Test existing file access - should return 200 OK (!-f passes for existing files)
|
||||
test_rule "Existing file access (existing.jpg)" \
|
||||
"$BASE_URL/wordpress/existing.jpg" \
|
||||
"200"
|
||||
|
||||
# Test non-existing directory routing - should route to index.php (!-d condition)
|
||||
test_rule_content "Non-existing directory routing (routes to index.php)" \
|
||||
"$BASE_URL/wordpress/nonexistent-dir/" \
|
||||
"" \
|
||||
"200" \
|
||||
"WordPress Content Route"
|
||||
|
||||
# Test existing directory access - should return 200 OK (!-d passes for existing dirs)
|
||||
test_rule "Existing directory access (somedir/)" \
|
||||
"$BASE_URL/wordpress/somedir/" \
|
||||
"200"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "Test Suite Complete"
|
||||
echo "=============================================="
|
||||
8
cms/wordpress/wordpress/.htaccess
Normal file
8
cms/wordpress/wordpress/.htaccess
Normal file
@@ -0,0 +1,8 @@
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
RewriteBase /wordpress
|
||||
RewriteRule ^wp-json/(.*)$ wp_api.php?api=$1 [L,QSA]
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . index.php [L]
|
||||
109
cms/wordpress/wordpress/README.md
Normal file
109
cms/wordpress/wordpress/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# WordPress .htaccess Test Structure
|
||||
|
||||
## Directory Layout Overview
|
||||
|
||||
```
|
||||
/test1/cms/wordpress/
|
||||
├── existing.jpg - Existing file for routing test (200 OK)
|
||||
│ - Tests: RewriteRule !-f condition passes for existing files
|
||||
├── index.php - WordPress entry point
|
||||
│ - Returns: "WordPress Content Route" page
|
||||
│ - Used by Rule 3 (direct access protection) and Rule 4 (routing)
|
||||
├── README.md - This documentation file
|
||||
├── somedir/ - Existing directory for routing test (200 OK)
|
||||
│ - Tests: RewriteRule !-d condition passes for existing directories
|
||||
└── test-wp-rewriterules.sh - Bash script to test all WordPress rules using curl
|
||||
```
|
||||
|
||||
## Apache Rules Explained
|
||||
|
||||
### 1. HTTP Authorization Header Passing
|
||||
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
```
|
||||
|
||||
**Что делает:** Копирует HTTP Authorization header из запроса в переменную `HTTP_AUTHORIZATION` для PHP
|
||||
|
||||
**Зачем нужно:** WordPress REST API использует это для аутентификации через Bearer token
|
||||
|
||||
**Пример использования:**
|
||||
```bash
|
||||
curl -H "Authorization: Bearer secret_token" http://site.com/wp-json/wp/v2/posts
|
||||
```
|
||||
|
||||
### 2. RewriteBase Configuration
|
||||
|
||||
```apache
|
||||
RewriteBase /
|
||||
```
|
||||
|
||||
**Что делает:** Устанавливает базовый путь переписывания к корню сайта
|
||||
|
||||
**Зачем нужно:** Определяет контекст для относительных путей в других правилах
|
||||
|
||||
### 3. Direct index.php Access Protection
|
||||
|
||||
```apache
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
```
|
||||
|
||||
**Что делает:** Если запрос идёт прямо на `index.php`, ничего не переписывает, останавливает процесс ([L] = Last)
|
||||
|
||||
**Зачем нужно:** Предотвращает зацикливание маршрутизации
|
||||
|
||||
### 4. WordPress Core Routing
|
||||
|
||||
```apache
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
```
|
||||
|
||||
**Что делает:**
|
||||
- `!-f` - проверяет, что запрашиваемый файл НЕ существует
|
||||
- `!-d` - проверяет, что запрашиваемая директория НЕ существует
|
||||
- Если оба условия true - маршрутизирует через `/index.php`
|
||||
|
||||
**Зачем нужно:** WordPress использует "чистые URL" (permalinks), все запросы на несуществующие файлы/директории рутятся через index.php для обработки WP core
|
||||
|
||||
## Test Script Features
|
||||
|
||||
The script includes two test functions:
|
||||
1. `test_rule()` - checks HTTP status code only
|
||||
2. `test_rule_content()` - checks both status AND response body content
|
||||
|
||||
## Test Coverage Summary
|
||||
|
||||
### 1. HTTP_AUTHORIZATION Header ✓
|
||||
- REST API request with Authorization header → **200** + "WordPress Content Route" ✓
|
||||
|
||||
### 2. RewriteBase Configuration ✓
|
||||
- Root directory access → **200** + "WordPress Content Route" ✓
|
||||
|
||||
### 3. Direct index.php Access ✓
|
||||
- `/index.php` direct call → **200** (not rewritten) ✓
|
||||
|
||||
### 4. WordPress Core Routing ✓
|
||||
- Non-existing file routes to `index.php` → **200** ✓
|
||||
- Existing file access (`existing.jpg`) → **200** ✓
|
||||
- Non-existing directory routes to `index.php` → **200** ✓
|
||||
- Existing directory access (`somedir/`) → **200** ✓
|
||||
|
||||
## Run Tests
|
||||
|
||||
Execute the test script to verify all rules:
|
||||
```bash
|
||||
cd /home/alexey/projects/workspace-zed/test1/cms/wordpress
|
||||
./test-wp-rewriterules.sh
|
||||
```
|
||||
|
||||
Expected results (all should be **PASS ✓**):
|
||||
- Authorization header handling: HTTP 200 + content "WordPress Content Route" ✓
|
||||
- Base path routing: HTTP 200 + content "WordPress Content Route" ✓
|
||||
- Direct index.php access: HTTP 200 ✓
|
||||
- Non-existing file routing: HTTP 200 ✓
|
||||
- Existing file access: HTTP 200 ✓
|
||||
- Non-existing directory routing: HTTP 200 ✓
|
||||
- Directory access: HTTP 200 ✓
|
||||
0
cms/wordpress/wordpress/existing.jpg
Normal file
0
cms/wordpress/wordpress/existing.jpg
Normal file
15
cms/wordpress/wordpress/index.php
Normal file
15
cms/wordpress/wordpress/index.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress - index.php (Test version)
|
||||
* This file handles routing for non-existing files/directories
|
||||
*/
|
||||
|
||||
// Simulated WordPress response
|
||||
echo "<html><head><title>WordPress Test Site</title></head><body>";
|
||||
echo "<h1>WordPress Content Route</h1>";
|
||||
echo "<p>This page is served by index.php via RewriteRule.</p>";
|
||||
echo "<div class='wp-config'>WordPress Configuration Loaded</div>";
|
||||
echo "</body></html>";
|
||||
|
||||
// Exit
|
||||
exit;
|
||||
0
cms/wordpress/wordpress/somedir/.gitkeep
Normal file
0
cms/wordpress/wordpress/somedir/.gitkeep
Normal file
54
cms/wordpress/wordpress/wp_api.php
Normal file
54
cms/wordpress/wordpress/wp_api.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* WordPress REST API Mock (Test version)
|
||||
* This file checks if Authorization header is properly passed from mod_rewrite
|
||||
*/
|
||||
|
||||
// Check if Authorization header was received
|
||||
$auth_header = "";
|
||||
if (isset($_SERVER["HTTP_AUTHORIZATION"])) {
|
||||
$auth_header = $_SERVER["HTTP_AUTHORIZATION"];
|
||||
} else {
|
||||
// Also check for rewritten env var
|
||||
$auth_env = getenv("HTTP_AUTHORIZATION");
|
||||
if ($auth_env !== false && $auth_env !== "") {
|
||||
$auth_header = $auth_env;
|
||||
}
|
||||
}
|
||||
|
||||
// Set response headers
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
if ($auth_header === "secret_token_123") {
|
||||
// SUCCESS - Authorization header was properly passed through mod_rewrite
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "success",
|
||||
"message" => "Authorization verified",
|
||||
"token_verified" => true,
|
||||
"wordpress_config_loaded" => true,
|
||||
"received_auth_header" => $auth_header,
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
} else {
|
||||
// FAIL - Authorization header was not passed through mod_rewrite
|
||||
|
||||
http_response_code(401);
|
||||
|
||||
echo json_encode(
|
||||
[
|
||||
"status" => "error",
|
||||
"message" => "unauth",
|
||||
"expected" => "Bearer secret_token_123",
|
||||
"received" => $auth_header ?: "(not set)",
|
||||
"test_failed" => true,
|
||||
"hint" =>
|
||||
"mod_rewrite [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] is NOT passing header to PHP",
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
);
|
||||
}
|
||||
|
||||
exit();
|
||||
Reference in New Issue
Block a user