Skip to content

Commit 4fbe9fe

Browse files
authored
Merge pull request #251 from clue-labs/configs
Update nginx + Apache configuration files to prevent access to internal files
2 parents 544ef66 + 99d43a5 commit 4fbe9fe

File tree

7 files changed

+116
-14
lines changed

7 files changed

+116
-14
lines changed

.github/workflows/ci.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,34 @@ jobs:
110110
- run: docker logs $(docker ps -qn1)
111111
if: ${{ always() }}
112112

113-
nginx-webserver:
113+
nginx-reverse-proxy:
114+
name: nginx (${{ matrix.config.name }})
115+
runs-on: ubuntu-22.04
116+
strategy:
117+
matrix:
118+
config:
119+
- name: reverse proxy with files
120+
path: nginx-reverse-proxy-public.conf
121+
- name: minimal reverse proxy
122+
path: nginx-reverse-proxy-minimal.conf
123+
steps:
124+
- uses: actions/checkout@v4
125+
- uses: shivammathur/setup-php@v2
126+
with:
127+
php-version: 8.3
128+
- run: composer install -d tests/integration/
129+
- run: docker build -f tests/integration/Dockerfile-basics tests/integration/
130+
- run: docker run -d -p 8080:8080 -v "$PWD/composer.json":/app/composer.json $(docker images -q | head -n1)
131+
- run: docker run -d --net=host -v "$PWD/tests/integration/":/home/framework-x/ -v "$PWD"/tests/integration/${{ matrix.config.path }}:/etc/nginx/conf.d/default.conf nginx:stable-alpine
132+
- run: bash tests/await.sh http://localhost
133+
- run: bash tests/integration.bash http://localhost
134+
- run: docker stop $(docker ps -qn2)
135+
- run: docker logs $(docker ps -qn1)
136+
if: ${{ always() }}
137+
- run: docker logs $(docker ps -qn2 | tail -n1)
138+
if: ${{ always() }}
139+
140+
nginx-fpm:
114141
name: nginx + PHP-FPM (PHP ${{ matrix.php }})
115142
runs-on: ubuntu-22.04
116143
strategy:

docs/best-practices/deployment.md

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ server {
7070
try_files $uri $uri/ /index.php$is_args$args;
7171
}
7272
73+
# Optional: handle Apache config with Framework X if it exists in `public/`
74+
error_page 403 = /index.php;
75+
location ~ \.htaccess$ {
76+
deny all;
77+
}
78+
7379
location ~ \.php$ {
7480
fastcgi_pass localhost:9000;
7581
fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -186,6 +192,9 @@ RewriteCond %{REQUEST_FILENAME} !-d
186192
RewriteCond %{REQUEST_FILENAME} !-f
187193
RewriteRule .* index.php
188194
195+
# Optional: handle `.htaccess` with Framework X instead of `403 Forbidden`
196+
ErrorDocument 403 /%{REQUEST_URI}/../index.php
197+
189198
# This adds support for authorization header
190199
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
191200
```
@@ -412,20 +421,44 @@ all you need to do is to point the nginx' [`root`](http://nginx.org/en/docs/http
412421
to instruct nginx to process any dynamic requests through X. This can be
413422
achieved by using an nginx configuration with the following contents:
414423

415-
```
416-
server {
417-
root /home/alice/projects/acme/public;
418-
index index.php index.html;
424+
=== "nginx.conf (reverse proxy with static files)"
419425

420-
location / {
421-
try_files $uri $uri/ @x;
426+
```
427+
server {
428+
# Serve static files from `public/`, proxy dynamic requests to Framework X
429+
location / {
430+
location ~* \.php$ {
431+
try_files /dev/null @x;
432+
}
433+
root /home/alice/projects/acme/public;
434+
try_files $uri @x;
435+
}
436+
437+
location @x {
438+
proxy_pass http://localhost:8080;
439+
proxy_set_header Host $host;
440+
proxy_set_header Connection "";
441+
}
442+
443+
# Optional: handle Apache config with Framework X if it exists in `public/`
444+
location ~ \.htaccess$ {
445+
try_files /dev/null @x;
446+
}
422447
}
448+
```
423449

424-
location @x {
425-
proxy_pass http://localhost:8080;
450+
=== "nginx.conf (minimal reverse proxy)"
451+
452+
```
453+
server {
454+
# Proxy all requests to Framework X
455+
location / {
456+
proxy_pass http://localhost:8080;
457+
proxy_set_header Host $host;
458+
proxy_set_header Connection "";
459+
}
426460
}
427-
}
428-
```
461+
```
429462

430463
> ℹ️ **New to nginx?**
431464
>

tests/integration.bash

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@ skipifnot() {
2323
}
2424

2525
out=$(curl -v $base/ 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain; charset=utf-8[\r\n]"
26-
out=$(curl -v $base/invalid 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
27-
out=$(curl -v $base// 2>&1); match "HTTP/.* 404"
28-
out=$(curl -v $base/ 2>&1 -X POST); match "HTTP/.* 405"
26+
out=$(curl -v $base/ 2>&1 -X POST); match "HTTP/.* 405" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
27+
28+
out=$(curl -v $base/unknown 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
29+
out=$(curl -v $base/index.php 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
30+
out=$(curl -v $base/.htaccess 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
31+
out=$(curl -v $base// 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
32+
2933
out=$(curl -v $base/error 2>&1); match "HTTP/.* 500" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" && match "<code>Unable to load error</code>"
3034
out=$(curl -v $base/error/null 2>&1); match "HTTP/.* 500" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]"
3135

tests/integration/nginx-fpm.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ server {
66
try_files $uri $uri/ /index.php$is_args$args;
77
}
88

9+
# Optional: handle Apache config with Framework X if it exists in `public/`
10+
error_page 403 = /index.php;
11+
location ~ \.htaccess$ {
12+
deny all;
13+
}
14+
915
location ~ \.php$ {
1016
fastcgi_pass php:9000;
1117
fastcgi_split_path_info ^(.+\.php)(/.+)$;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
server {
2+
# Proxy all requests to Framework X
3+
location / {
4+
proxy_pass http://localhost:8080;
5+
proxy_set_header Host $host;
6+
proxy_set_header Connection "";
7+
}
8+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
server {
2+
# Serve static files from `public/`, proxy dynamic requests to Framework X
3+
location / {
4+
location ~* \.php$ {
5+
try_files /dev/null @x;
6+
}
7+
root /home/framework-x/public;
8+
try_files $uri @x;
9+
}
10+
11+
location @x {
12+
proxy_pass http://localhost:8080;
13+
proxy_set_header Host $host;
14+
proxy_set_header Connection "";
15+
}
16+
17+
# Optional: handle Apache config with Framework X if it exists in `public/`
18+
location ~ \.htaccess$ {
19+
try_files /dev/null @x;
20+
}
21+
}

tests/integration/public/.htaccess

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ RewriteCond %{REQUEST_FILENAME} !-d
44
RewriteCond %{REQUEST_FILENAME} !-f
55
RewriteRule .* index.php
66

7+
# Optional: handle `.htaccess` with Framework X instead of `403 Forbidden`
8+
ErrorDocument 403 /%{REQUEST_URI}/../index.php
9+
710
# This adds support for authorization header
811
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0

0 commit comments

Comments
 (0)