v0.34.0_fina;
This commit is contained in:
1 parent
1ca019d849
commit
c207e8a03f
69 files changed
+1496
-13838
No files matched your search
+40
-403
@@ -1,417 +1,54 @@
|
||||
# PHP-FPM / FastCGI for the Web Server
|
||||
# PHP-FPM with Web Services
|
||||
|
||||
**Separate pools for distinct Web Services:** Reusing the same PHP-FPM socket
|
||||
with different `PHP_ADMIN_VALUE` maps can leak `open_basedir` restrictions into
|
||||
a different virtual host. See the 0.29.7 guide
|
||||
[PHP_FPM_ISOLATION.md](PHP_FPM_ISOLATION.md) for remediation and tests.
|
||||
RustProxy's Web Server supports **FastCGI** forwarding to an existing **PHP-FPM** pool for configured script extensions. RustProxy does not include a PHP interpreter or run the PHP application itself.
|
||||
|
||||
RustProxy 0.25 can execute configured PHP extensions through a long-running PHP-FPM pool while preserving the reusable Web Service architecture.
|
||||
## Before enabling PHP
|
||||
|
||||
## Architecture
|
||||
Install and start PHP-FPM independently, configure the application files and create a **dedicated pool/socket for each application or security domain**. Set the correct Unix user/group, socket permissions, PHP version and pool-level configuration. Restrict a TCP FPM endpoint to a trusted private interface; never expose FastCGI directly to the Internet.
|
||||
|
||||
In **Web Server → PHP-FPM profiles** create a reusable profile, or set PHP options in one Web Service. Select a socket such as `unix:/run/php/app.sock` or a reachable TCP endpoint (use the syntax accepted by the editor); enable PHP, choose extensions (default `.php`) and set request/connect timeouts and concurrency. Attach the profile to a Web Service and publish that service using a Vhost.
|
||||
|
||||
## Important settings
|
||||
|
||||
| Setting | Effect |
|
||||
|---|---|
|
||||
| **Endpoint** | Unix socket or private TCP address of a real PHP-FPM service. |
|
||||
| **Extensions** | Which files are eligible for PHP execution. Confirm front-controller routes and direct PHP paths. |
|
||||
| **`try_files` / PATH_INFO** | Resolve PHP front-controller or `script.php/...` paths, where supported, without accidentally executing an unintended file. |
|
||||
| **Connect/request timeouts** | Bound waiting for FPM connection and application execution. Defaults in source include 1 s connect and 30 s request. |
|
||||
| **Concurrent requests** | Local RustProxy FastCGI request limit (default 64), **not** the number of FPM worker processes. |
|
||||
| **Authorization forwarding** | Optional preservation of eligible `Authorization` information (default enabled). Review application expectations. |
|
||||
| **Custom FastCGI parameters** | Static application-specific settings. Reserved transport/identity parameters cannot be overridden arbitrarily. |
|
||||
| **PHP values** | `PHP_VALUE` and `PHP_ADMIN_VALUE` options for the external FPM workers. |
|
||||
| **Body/header limits** | Bound upload size, response headers and stderr exposed in logs. |
|
||||
|
||||
## Pool isolation is mandatory for mixed applications
|
||||
|
||||
PHP-FPM workers can retain `PHP_VALUE` / `PHP_ADMIN_VALUE` behavior across requests. **Two applications with different INI/admin settings must not share one FPM worker pool**, even when they have different RustProxy Vhosts. RustProxy may warn about conflicting shared endpoints but cannot isolate PHP workers inside FPM.
|
||||
|
||||
Example separation:
|
||||
|
||||
```text
|
||||
Request
|
||||
-> existing listener / vhost / location / ACL / auth
|
||||
-> Web Server -> Web Service
|
||||
|- static file
|
||||
`- configured PHP extension -> FastCGI -> PHP-FPM
|
||||
-> existing error/header/logging/metrics/transport pipeline
|
||||
Web Service: blog -> unix:/run/php/php_blog.sock
|
||||
Web Service: webmail -> unix:/run/php/php_webmail.sock
|
||||
```
|
||||
|
||||
Vhosts and locations still reference only a Web Service. Each Web Service may
|
||||
use local PHP settings or reference a shared `php_fpm_profiles` entry from the
|
||||
normal revisioned configuration; its local settings are retained on detach.
|
||||
Give each pool access only to its own application's files; configure `open_basedir` and other hardening at FPM pool level where possible. Restart the affected FPM pools after policy changes to retire existing workers.
|
||||
|
||||
### Shared PHP-FPM profiles (0.29.4)
|
||||
## WordPress and Roundcube
|
||||
|
||||
In **Web Server → Edit Web Service → PHP-FPM**, select **Local configuration**
|
||||
or a named profile. **Manage PHP-FPM profiles** opens a modal with an expandable
|
||||
editor for every profile and an **Add profile** action. A profile contains the
|
||||
same PHP execution, FastCGI and PHP_VALUE/PHP_ADMIN_VALUE settings as local
|
||||
configuration. Changes to a shared profile affect all attached services after
|
||||
validation and apply. In-use profiles cannot be deleted.
|
||||
For **WordPress**, point the document root to the website directory, enable PHP for `.php`, use the appropriate front-controller (`index.php`) and verify that `wp-config.php` exists and is readable only where needed. A redirect to `setup-config.php` can indicate filesystem/FPM/open_basedir misconfiguration rather than proxy cache.
|
||||
|
||||
Configuration example:
|
||||
For **Roundcube**, use its published web root and a separate FPM pool with the application's expected file access. Some Roundcube static script URLs use `PATH_INFO`; enable/test handling that resolves `static.php/...` without turning arbitrary strings into executable script names.
|
||||
|
||||
```json
|
||||
{
|
||||
"php_fpm_profiles": [{
|
||||
"id": "php82", "name": "PHP 8.2 FPM",
|
||||
"php": {"enabled": true, "endpoint": "unix:/run/php/php8.2-fpm.sock"}
|
||||
}],
|
||||
"web_services": [{
|
||||
"id": "site", "name": "Site", "document_root": "/var/www/site",
|
||||
"php_profile_id": "php82"
|
||||
}]
|
||||
}
|
||||
```
|
||||
These are routing examples; presets do not install either application or choose a safe OS-specific pool automatically.
|
||||
|
||||
Unspecified PHP fields inherit the safe serde defaults. A service without
|
||||
`php_profile_id` uses its own `php` object as in earlier versions.
|
||||
## Diagnostics
|
||||
|
||||
## Where to configure each PHP setting (0.29.7)
|
||||
- `502/503/504`: check FPM health, Unix socket group permissions, endpoint path, capacity and configured timeouts.
|
||||
- PHP errors/blank page: check FPM stderr, application logs and effective INI values.
|
||||
- Incorrect site appears to inherit another site's `open_basedir`: use distinct FPM pools and restart them.
|
||||
- Compressed PHP content truncated: verify current application response/framing and try the same request without compression; review FastCGI and response logs.
|
||||
- Cache behavior differs from static files: PHP may ignore validators or send `private` / `Set-Cookie`, preventing shared cache storage.
|
||||
|
||||
Both editors expose the **same fields and validation**:
|
||||
|
||||
1. **Web Server → Web Service → PHP-FPM → Local configuration:** set values on one service (`web_services[].php`). Select **Local configuration** in the profile selector first.
|
||||
2. **Web Server → PHP-FPM profiles → Add/Edit profile:** set shared values on a profile (`php_fpm_profiles[].php`), then attach it in a Web Service's PHP-FPM tab (`php_profile_id`). Editing the profile changes every attached service. Per-service `php` values remain saved for later detach but do **not** override an attached profile.
|
||||
3. **Actual PHP-FPM pool config** (`php-fpm.d/*.conf` or distribution equivalent): the pool must exist and listen on the configured socket. Isolate restrictive `open_basedir`/`php_admin_value` settings in *separate pools and sockets*, not by alternating request-time `PHP_ADMIN_VALUE` on shared workers. RustProxy does not launch PHP-FPM.
|
||||
|
||||
| UI field (JSON property) | What to enter / sample | Notes |
|
||||
| --- | --- | --- |
|
||||
| Enable PHP-FPM / FastCGI (`enabled`) | Checked for PHP apps | Off means PHP is not executed. Never serve PHP source. |
|
||||
| PHP-FPM socket (`endpoint`) | `unix:/run/php/php_wordpress.sock`, `tcp:127.0.0.1:9000`, `tcp:[::1]:9000` | Only local Unix sockets or TCP loopback **IP**, not hostnames. Socket ownership and permissions must allow RustProxy access. |
|
||||
| PHP extensions (`extensions`) | `php, phtml` | Comma-separated, no leading dots; at most 16. Only listed extensions are executable. |
|
||||
| Enable PHP PATH_INFO splitting (`path_info`) | Checked for Roundcube `/static.php/skins/...` | Splits an existing PHP script path and its trailing URL; off for ordinary WordPress unless needed. |
|
||||
| Forward Authorization (`pass_authorization`) | Checked when API / Basic / Bearer auth is used | Sends request `Authorization` to `HTTP_AUTHORIZATION` in FastCGI. |
|
||||
| Connection timeout (`connect_timeout_ms`) | `1000` = 1 second | Integer 50–60000 milliseconds. |
|
||||
| Request timeout (`request_timeout_ms`) | `30000` = 30 seconds | Integer 100–3600000 milliseconds for the full FastCGI request. |
|
||||
| Max simultaneous PHP requests (`max_concurrent_requests`) | `64` | Integer 1–4096 per Web Service; excess is rejected with HTTP 503. |
|
||||
| Request-body limit (`max_request_body_bytes`) | `67108864` = 64 MiB; `0` = no additional limit | Integer 0–8589934592 **bytes**, independent of PHP `post_max_size`. |
|
||||
| Response-header limit (`max_response_header_bytes`) | `65536` = 64 KiB | Integer 4096–1048576 bytes. |
|
||||
| PHP-FPM STDERR log limit (`stderr_log_bytes`) | `4096`; `0` = disabled | Integer 0–65536 bytes collected per request. |
|
||||
| Custom FastCGI parameters (`custom_params`) | `APP_ENV = production` | One static `NAME = value` per line. Names A-Z/0-9/underscore, beginning with a letter. No core CGI, HTTP_* or PHP control params. |
|
||||
| PHP INI overrides (`php_values` / `PHP_VALUE`) | `memory_limit = 256M` | One php.ini `directive = value` per line. PHP decides which directives can change at this scope. |
|
||||
| Administrator PHP INI overrides (`php_admin_values` / `PHP_ADMIN_VALUE`) | `open_basedir = /var/www/webmail/:/tmp/` | Trusted operator only; for security isolation prefer dedicated PHP-FPM pool configuration. Do not define the same directive in both INI maps. |
|
||||
|
||||
**Syntax in the three multiline fields is not JSON:** use `key = value`, a separate line per entry; **do not** wrap it in `PHP_VALUE=...` or `php_admin_value[...]` inside the RustProxy editor. Those latter directives belong to the *native PHP-FPM pool configuration*, not the RustProxy FastCGI request fields. Empty values and control characters are rejected. Up to 64 entries per map, maximum 8192 bytes per value and 128 KiB combined.
|
||||
|
||||
### Ready-to-use WordPress and Roundcube examples
|
||||
|
||||
**WordPress — Web Service `/var/www/linuxiarz.pl/`, dedicated FPM socket:**
|
||||
|
||||
```text
|
||||
PHP enabled: checked
|
||||
PHP PATH_INFO: unchecked
|
||||
Endpoint: unix:/run/php/php_wordpress.sock
|
||||
PHP extensions: php
|
||||
PHP_VALUE:
|
||||
memory_limit = 256M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_execution_time = 60
|
||||
PHP_ADMIN_VALUE: leave empty (or set security restrictions in WordPress's own FPM pool)
|
||||
```
|
||||
|
||||
Use `index.php` in **Index files**, plus `try_files` lines `$uri`, `$uri/`, `/index.php?$query_string` in **Routing**. The WordPress pool must be allowed to read its `wp-config.php`, `wp-includes` and `wp-content` directories.
|
||||
|
||||
**Roundcube — Web Service `/var/www/webmail/public_html`, separate FPM socket:**
|
||||
|
||||
```text
|
||||
PHP enabled: checked
|
||||
PHP PATH_INFO: checked
|
||||
Endpoint: unix:/run/php/php_roundcube.sock
|
||||
PHP extensions: php
|
||||
PHP_VALUE:
|
||||
memory_limit = 256M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
PHP_ADMIN_VALUE: prefer the native Roundcube PHP-FPM pool instead; e.g.:
|
||||
open_basedir = /var/www/webmail/:/usr/lib/php/:/tmp/:/etc/enigma
|
||||
```
|
||||
|
||||
Add additional permitted external paths only if Roundcube's actual dependencies need them. Roundcube assets such as `/static.php/skins/elastic/...` require PATH_INFO splitting. Restart/recycle workers after separating pools; using a different RustProxy profile but the **same socket** does not isolate the worker state. See [pool isolation](PHP_FPM_ISOLATION.md).
|
||||
|
||||
### FastCGI vs php.ini examples
|
||||
|
||||
```text
|
||||
# Custom FastCGI parameters — application-specific static environment:
|
||||
APP_ENV = production
|
||||
APP_DEBUG = 0
|
||||
|
||||
# PHP_VALUE — regular PHP-level options:
|
||||
memory_limit = 256M
|
||||
max_execution_time = 60
|
||||
error_reporting = E_ALL
|
||||
log_errors = On
|
||||
display_errors = Off
|
||||
date.timezone = Europe/Warsaw
|
||||
|
||||
# PHP_ADMIN_VALUE — privileged operator-level options:
|
||||
open_basedir = /var/www/webmail/:/usr/lib/php/:/tmp/
|
||||
```
|
||||
|
||||
The `#` comments above are explanations separating fields; **do not paste them** into the corresponding form inputs. Boolean INI directives commonly accept `On`/`Off`, size directives can accept `K`/`M`/`G` and timeouts use seconds. The actual PHP/FPM version determines supported directives and permitted scopes.
|
||||
|
||||
## Runtime model
|
||||
|
||||
RustProxy implements the FastCGI protocol directly. It does not spawn `php`, `php-cgi` or shell processes for individual requests.
|
||||
|
||||
Supported endpoint syntax:
|
||||
|
||||
```text
|
||||
unix:/run/php/php-fpm.sock
|
||||
tcp:127.0.0.1:9000
|
||||
tcp:[::1]:9000
|
||||
```
|
||||
|
||||
Use the `unix:` prefix for a Unix-domain socket. Use `tcp:` followed by a literal loopback IP address and port for TCP. Hostnames and non-loopback addresses are rejected. Unix sockets are preferred for a local PHP-FPM installation; exposing FastCGI directly to an untrusted network is not supported by this configuration.
|
||||
|
||||
## Web Service configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"index": ["index.html", "index.php"],
|
||||
"try_files": ["$uri", "$uri/", "/index.php?$query_string"],
|
||||
"php": {
|
||||
"enabled": true,
|
||||
"extensions": ["php"],
|
||||
"endpoint": "unix:/run/php/php-fpm.sock",
|
||||
"connect_timeout_ms": 1000,
|
||||
"request_timeout_ms": 30000,
|
||||
"max_concurrent_requests": 64,
|
||||
"pass_authorization": true,
|
||||
"max_request_body_bytes": 0,
|
||||
"max_response_header_bytes": 65536,
|
||||
"stderr_log_bytes": 4096,
|
||||
"custom_params": {
|
||||
"APP_ENV": "production"
|
||||
},
|
||||
"php_values": {
|
||||
"memory_limit": "256M",
|
||||
"upload_max_filesize": "64M",
|
||||
"post_max_size": "64M"
|
||||
}
|
||||
},
|
||||
"symlink_policy": "deny"
|
||||
}
|
||||
```
|
||||
|
||||
The normal ordered `index` list handles `index.php`; there is no separate PHP-index option.
|
||||
|
||||
### Additional PHP/FastCGI options
|
||||
|
||||
- `path_info`: default-off secure script/path splitting. When enabled, `/index.php/foo/bar` may resolve the existing `index.php` script and pass `/foo/bar` as `PATH_INFO`.
|
||||
- `pass_authorization`: forwards the inbound `Authorization` header as `HTTP_AUTHORIZATION`. Disable it when the application does not need that header.
|
||||
- `max_request_body_bytes`: streaming request-body limit enforced before/during FastCGI forwarding. `0` means no additional RustProxy-side limit. A request exceeding the configured value receives `413 Payload Too Large`.
|
||||
- `max_response_header_bytes`: maximum bytes accepted before the CGI response-header terminator. The allowed range is 4096..1048576 bytes.
|
||||
- `stderr_log_bytes`: maximum PHP-FPM stderr bytes retained for one diagnostic request entry. `0` disables stderr capture; maximum 65536.
|
||||
- `custom_params`: trusted static FastCGI parameters supplied by the operator.
|
||||
- `php_values`: php.ini-style overrides encoded into one validated FastCGI `PHP_VALUE` parameter.
|
||||
|
||||
### Custom FastCGI parameter syntax
|
||||
|
||||
In the administration UI use one entry per line:
|
||||
|
||||
```text
|
||||
APP_ENV = production
|
||||
APP_DEBUG = 0
|
||||
TENANT = main
|
||||
```
|
||||
|
||||
Names are normalized to uppercase and are limited to letters, digits and `_` (the first character must be a letter). Values are static: there is no request-variable interpolation in this field. RustProxy rejects core CGI parameters, `HTTP_*`, `PHP_VALUE` and `PHP_ADMIN_VALUE`, so custom values cannot replace `SCRIPT_FILENAME`, routing data, HTTP-derived fields or PHP control parameters. Up to 64 custom FastCGI parameters and 64 PHP INI overrides are accepted, with at most 128 KiB of combined custom parameter/value data.
|
||||
|
||||
### PHP INI (`PHP_VALUE`) syntax
|
||||
|
||||
Use one `directive = value` entry per line:
|
||||
|
||||
```text
|
||||
memory_limit = 256M
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_execution_time = 30
|
||||
```
|
||||
|
||||
RustProxy validates the lines and combines them with newline separators into the FastCGI `PHP_VALUE` parameter. PHP-FPM/PHP still decides whether a directive is changeable at this scope. `PHP_ADMIN_VALUE` is available separately through the validated administrator INI overrides for trusted PHP-FPM endpoints. Control characters/newlines inside names or values are rejected.
|
||||
|
||||
These custom parameters are stored as normal Web Service configuration. Do not put secrets there unless normal configuration storage is an acceptable location for them.
|
||||
|
||||
## `try_files` front controller
|
||||
|
||||
A typical application can use:
|
||||
|
||||
```text
|
||||
$uri
|
||||
$uri/
|
||||
/index.php?$query_string
|
||||
```
|
||||
|
||||
Existing static files/directories are used first. The final path becomes the fallback and can therefore reach `index.php`. This resolution remains inside the already-selected Web Service and does not introduce a second vhost router.
|
||||
|
||||
## Roundcube 1.7 static.php / PATH_INFO (0.29.5)
|
||||
|
||||
Roundcube serves CSS, JavaScript and images through URLs such as
|
||||
`/static.php/skins/elastic/styles/styles.min.css?s=123`. These paths are
|
||||
**PHP-FPM requests to the existing** `public_html/static.php` **script**, not
|
||||
static filesystem lookups. Configure the selected Web Service (or attached
|
||||
shared PHP-FPM profile) with `php.enabled: true`, `php.path_info: true`, and
|
||||
`php.extensions: ["php"]`. Point its root to the Roundcube `public_html` tree.
|
||||
|
||||
You can keep the normal Web Service `try_files` (`$uri`, `$uri/`,
|
||||
`/index.php?$query_string`). RustProxy 0.29.5 resolves a valid PHP script plus
|
||||
PATH_INFO after security checks and rewrites, **before** falling back through
|
||||
`try_files`. No regex rewrite or extra Web Service is required for these URLs
|
||||
when the main vhost selects this Web Service.
|
||||
|
||||
For `/static.php/skins/elastic/images/favicon.ico?s=123`, FastCGI receives:
|
||||
|
||||
```text
|
||||
SCRIPT_NAME=/static.php
|
||||
SCRIPT_FILENAME=<document_root>/static.php
|
||||
PATH_INFO=/skins/elastic/images/favicon.ico
|
||||
REQUEST_URI=/static.php/skins/elastic/images/favicon.ico?s=123
|
||||
QUERY_STRING=s=123
|
||||
```
|
||||
|
||||
PHP-FPM still needs filesystem access to `static.php` and the Roundcube
|
||||
installation outside `public_html` that script reads. The PHP `PATH_INFO`
|
||||
option is explicit and remains off by default for unrelated services.
|
||||
|
||||
## Bounded execution
|
||||
|
||||
Each compiled Web Service owns a bounded PHP concurrency semaphore. `max_concurrent_requests` limits simultaneous FastCGI requests. RustProxy does not create an unbounded wait queue: if no execution slot is immediately available it returns `503 Service Unavailable` with `Retry-After`.
|
||||
|
||||
Connection establishment and complete FastCGI request execution have independent configured time limits. A connect failure produces a gateway error; a request timeout produces a gateway timeout. The outer normal RustProxy error-page processing remains able to replace these responses.
|
||||
|
||||
Request bodies are split into bounded FastCGI records rather than collected into one large buffer. When `max_request_body_bytes` is non-zero, the limit is checked both from `Content-Length` when available and while streaming chunked/unknown-length bodies. PHP-FPM stdout is parsed and delivered through a bounded response channel so application output can stream to the client.
|
||||
|
||||
## Secure script selection
|
||||
|
||||
Only configured extensions are executable. Before PHP is selected, RustProxy resolves the request/`try_files`/index path using the Web Server filesystem confinement rules and verifies that it is a regular file below the document root.
|
||||
|
||||
When PHP handling was selected:
|
||||
|
||||
- PHP source is never returned as a fallback;
|
||||
- an unavailable PHP-FPM endpoint produces 502/504 behavior rather than source disclosure;
|
||||
- encoded traversal/separators and `..` remain rejected;
|
||||
- the current implementation requires `symlink_policy=deny`.
|
||||
|
||||
The last restriction is deliberate: PHP-FPM opens `SCRIPT_FILENAME` independently, so RustProxy does not claim an `inside_root` symlink guarantee that the external process cannot enforce atomically.
|
||||
|
||||
## FastCGI parameters
|
||||
|
||||
RustProxy supplies a controlled CGI/FastCGI parameter set including:
|
||||
|
||||
- `REQUEST_METHOD`
|
||||
- `SCRIPT_FILENAME`
|
||||
- `SCRIPT_NAME`
|
||||
- `PATH_INFO` when `path_info` is enabled and a trailing path was securely split from an existing PHP script
|
||||
- `QUERY_STRING`
|
||||
- `REQUEST_URI`
|
||||
- `DOCUMENT_ROOT`
|
||||
- `SERVER_PROTOCOL`
|
||||
- `SERVER_NAME`
|
||||
- `SERVER_PORT`
|
||||
- `REMOTE_ADDR`
|
||||
- `REMOTE_PORT` when available
|
||||
- `HTTPS`
|
||||
- `GATEWAY_INTERFACE`
|
||||
- `SERVER_SOFTWARE`
|
||||
- `REDIRECT_STATUS`
|
||||
- `CONTENT_TYPE`
|
||||
- `CONTENT_LENGTH`
|
||||
- selected request headers as `HTTP_*`
|
||||
|
||||
Request headers cannot become FastCGI `PHP_VALUE` or `PHP_ADMIN_VALUE` parameters. Operator-defined `custom_params` are allowed only through the validated Web Service configuration and cannot override core CGI/HTTP-derived fields. Operator-defined `php_values` are the only path that creates `PHP_VALUE`; `PHP_ADMIN_VALUE` is never generated.
|
||||
|
||||
An inbound `Proxy` header is **never** translated into FastCGI `HTTP_PROXY` (httpoxy mitigation), regardless of the optional Web Service hardening switch. The **Block common attack probes** option additionally blocks high-confidence sensitive/probe paths before PHP-FPM dispatch. FastCGI output cannot control downstream `Content-Length`, transfer framing, connection headers or `Proxy-*` transport headers; RustProxy owns downstream response framing.
|
||||
|
||||
`PATH_INFO` splitting is optional and disabled by default. When enabled, RustProxy searches for an existing configured PHP script prefix inside the document root, keeps `SCRIPT_FILENAME`/`SCRIPT_NAME` on that script, and supplies only the remaining suffix as `PATH_INFO`. `REQUEST_URI` remains the original request target.
|
||||
|
||||
## Response parsing
|
||||
|
||||
The FastCGI stdout parser understands standard CGI response headers, including `Status`. Hop-by-hop transport headers are stripped before returning the response through RustProxy.
|
||||
|
||||
For `HEAD`, PHP-FPM can still execute normally but the client response body is empty. GET/POST/PUT and other methods can reach a PHP script once routing resolves to a configured executable extension.
|
||||
|
||||
PHP stderr is bounded by `stderr_log_bytes` and recorded as a RustProxy `WARN` diagnostic. It goes to the configured error file when enabled, or the console/systemd journal otherwise; it is not sent to the client as page content. CGI response headers are separately bounded by `max_response_header_bytes`.
|
||||
|
||||
## Caching
|
||||
|
||||
PHP output does not use static file validators or the static-file OS representation path. Application response headers and the normal vhost/location response-header pipeline control client/intermediary caching.
|
||||
|
||||
The Web Service static `cache_rules` apply to local static file responses, not
|
||||
to PHP stdout. When global/vhost caching is enabled, explicitly cacheable
|
||||
PHP-FPM responses may enter the shared RustProxy cache; responses with
|
||||
`Set-Cookie`, `Cache-Control: private` or `no-store` cannot be stored. In
|
||||
particular, do not enable forced caching of authenticated WordPress pages
|
||||
without auditing the origin response policy. HTTP/2 and HTTP/3 `:authority`
|
||||
requests populate the `HTTP_HOST` FastCGI parameter, retaining the original
|
||||
host/port from the incoming request when provided.
|
||||
|
||||
## PHP installation
|
||||
|
||||
RustProxy does not bundle PHP. Install and maintain PHP-FPM using the operating system/package ecosystem and enable only the extensions required by the application.
|
||||
|
||||
Operational requirements:
|
||||
|
||||
- a running PHP-FPM 8.x service supported by the operator's platform;
|
||||
- a dedicated Unix socket or loopback TCP listener reachable by the RustProxy service account;
|
||||
- document-root permissions usable by both RustProxy and the PHP-FPM worker identity as intended;
|
||||
- correct socket owner/group/mode when using Unix sockets;
|
||||
- PHP-FPM limits such as `pm.max_children`, application execution timeout and upload/body limits sized consistently with the RustProxy concurrency limit.
|
||||
|
||||
Extensions such as PDO drivers, mbstring, intl, gd, curl or opcache are application dependencies, not RustProxy dependencies.
|
||||
|
||||
## Example PHP-FPM pool concept
|
||||
|
||||
The exact paths/users depend on the operating system. For production, use a **separate PHP-FPM worker identity per site/application**. Never run PHP-FPM as `root` or as the `rustproxy` service account. A compromised CMS/plugin then must not inherit access to RustProxy's SQLite database, Secret Store, certificate private keys or backups.
|
||||
|
||||
A local pool can expose a socket such as:
|
||||
|
||||
```ini
|
||||
user = site_example
|
||||
group = site_example
|
||||
listen = /run/php/rustproxy-example.sock
|
||||
listen.owner = www-data
|
||||
listen.group = rustproxy
|
||||
listen.mode = 0660
|
||||
```
|
||||
|
||||
The exact socket owner may differ by distribution; the important properties are that the PHP worker user is **not** `rustproxy`, and only RustProxy plus the pool manager can reach the socket. Configure the Web Service endpoint as:
|
||||
|
||||
```text
|
||||
unix:/run/php/rustproxy-example.sock
|
||||
```
|
||||
|
||||
Keep `/var/lib/rustproxy` mode/private ownership inaccessible to every PHP pool. Prefer separate writable upload/data directories from executable PHP source, and prevent the application from creating executable PHP files in upload areas. A CMS-level arbitrary file upload or vulnerable plugin can otherwise become PHP code execution even when the reverse proxy itself is correct.
|
||||
|
||||
Do not expose a PHP-FPM TCP listener to the public network.
|
||||
|
||||
## Verification targets
|
||||
|
||||
The implementation includes source/unit coverage for:
|
||||
|
||||
- PHP front-controller resolution through `try_files`;
|
||||
- request body delivery to a FastCGI fixture;
|
||||
- FastCGI response parsing;
|
||||
- connection failure returning a gateway error without PHP source disclosure;
|
||||
- bounded endpoint/time/concurrency configuration validation.
|
||||
|
||||
Release-host acceptance should additionally exercise a real PHP-FPM runtime over Unix socket and loopback TCP and verify ACL/auth/custom errors/headers plus H1/H2/H3 client transports.
|
||||
|
||||
|
||||
## Per-Web-Service PHP administrative INI overrides (0.29.2)
|
||||
|
||||
`php.php_admin_values` is a validated operator-managed string map sent in a single
|
||||
`PHP_ADMIN_VALUE` FastCGI parameter. Example:
|
||||
|
||||
```json
|
||||
"php": {
|
||||
"enabled": true,
|
||||
"path_info": true,
|
||||
"endpoint": "unix:/run/php/php-fpm-mail.sock",
|
||||
"php_admin_values": {
|
||||
"open_basedir": "/var/www/webmail/:/tmp/",
|
||||
"display_errors": "Off"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use separate Web Services for distinct per-location PHP settings, then assign those
|
||||
services to the corresponding Custom Locations. Each Web Service can refer to the
|
||||
same trusted PHP-FPM socket. The setting does **not** isolate worker process
|
||||
identities or file permissions. Never expose PHP-FPM FastCGI to untrusted clients.
|
||||
HTTP headers cannot supply `PHP_ADMIN_VALUE`, and `php_values` must not repeat
|
||||
an admin directive. Control characters and excessive values are rejected.
|
||||
|
||||
Prometheus metrics use only configured Web Service IDs, never script paths,
|
||||
URLs, PHP values, or dynamic user labels. `rust_proxy_php_requests_total` and
|
||||
`rust_proxy_php_responses_total{class=...}` count responses after headers have
|
||||
been received (including proxy-generated failures); the cumulative header
|
||||
latency metric intentionally excludes streamed body time.
|
||||
|
||||
The same snapshot also exports `rust_proxy_web_server_requests_total`,
|
||||
`rust_proxy_web_server_responses_total{class=...}` and
|
||||
`rust_proxy_web_server_response_header_duration_microseconds_total` for all
|
||||
Web Server requests (PHP and non-PHP).
|
||||
Inspect **Web Server → Live metrics**, [Logs](LOGS.md) and the application's own FPM logs. The proxy's PHP request semaphore is not a replacement for monitoring external FPM worker capacity.
|
||||
Reference in new issue
Block a user