Differences between revisions 3 and 18 (spanning 15 versions)
Revision 3 as of 2020-01-20 07:14:05
Size: 4139
Comment:
Revision 18 as of 2022-09-26 17:07:51
Size: 3634
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= FastCGI = = PHP-FPM =
Line 3: Line 3:
'''FastCGI''' is a PHP implementation of the Common Gateway Interface (CGI). It works especially well with [[NGINXSetup|NGINX]]. The PHP '''FastCGI Process Manager''' ('''PHP-FPM''') is an implementation of the [[Protocols/CGI|FastCGI]] specification.

<<TableOfContents>>
Line 9: Line 11:
== Setup Directory == == Installation ==
Line 11: Line 13:
The recommendation is to either serve web content from: PHP-FPM naturally depends on `php(1)`. See [[PHP#Installation|here]] for help with installation, and [[PHP/Configuration|here]] for help with configuration.
Line 13: Line 15:
 * a dedicated top-level directory (such as `/srv`) that can be ''easily'' separately-mounted with special settings (i.e. `ro`--the read-only fstab option)
 * the traditional web content directory, `/var/www`
Most Linux and BSD distributions will offer a `php-fpm` package.
Line 16: Line 17:
Note that any directory can be a mounted device, but there are additional considerations. Many package managers expect standard directories to be writable. Official container images are available from the upstream development team. They are tagged like `php:<version>-fpm`

----
Line 20: Line 23:
=== Setup Test Script === == Configuration ==
Line 22: Line 25:
Write the below to `cgi/test.php`, under whichever directory structure you prefer. `php-fpm(8)` listens on one or more ports or sockets. Each point of contact is a '''pool'''.

Each pool should have it's own configuration file under `/etc/php/php.fpm.d`. A pool's name (`$pool`) is derived from the section header.

As an example:
Line 25: Line 32:
<?php phpinfo(); ?> [www]
user = www-data
group = www-data

listen = 9000
listen.allowed_clients = 127.0.0.1, 192.168.86.1

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
}}}



=== TCP Sockets ===

The above example uses a shorthand: `listen = 9000`. This causes `php-fpm(8)` to listen on all addresses at port 9000.

However, `listen.allowed_clients` overrides this with a client whitelist. This should be comma-separated.

An IPv4 address is specified like `127.0.0.1:9000`; an IPv6 address is specified like `[::1]:9000`.



=== Unix Sockets ===

To use a [[Linux/UnixSocket|Unix socket]] to pass requests, try:

{{{
listen = /run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
}}}



=== Static Process Management ===

A pool spawns and maintains `pm.max_children` number of processes.

{{{
pm = static
pm.max_children = 5
}}}



=== Dynamic Process Management ===

A pool spawns `pm.start_servers` processes. At any given time, some number are 'idle'. The pool tries to keep this number within the range of `pm.min_spare_servers` to `pm.max_spare_servers`, never surpassing `pm.max_children`.

{{{
pm = static
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
}}}



=== On-Demand Process Management ===

A pool spawns processes on-demand up until `pm.max_children`. A process is killed after `pm.process_idle_timeout` has passed.

{{{
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
}}}



=== Process Termination ===

`php(1)` scripts are known to leak memory frequently. As such, a regular task is to terminate long-running processes.

A simple solution is to set `pm.max_requests` to any number other than 0. Processes are killed after handling this number of requests.



=== Hardening ===

As a security measure, the allowable script extensions should be set as strictly as possible.

{{{
security.limit_extensions = .php .html .htm
}}}

A pool can be hardened by isolating it to a distinct working directory, or even a chroot.

{{{
chroot = /absolute/path/to/chroot
# or
chdir = relative/or/absolute/path/to/working/directory
}}}

By default, `php-fpm(8)` clears all environment variables. This can be disabled, but a better strategy is to set specific environment variables.

{{{
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
}}}

The same is true of PHP system variables.

{{{
php_admin_value[error_log] = /var/log/php-fpm.log
Line 32: Line 149:
== Setup User ==

Linux permissions and restrictions are most easily done through users, groups, and umasks. The recommendation is to set a specific user and group for the web service. The common options are `www-data` (Apache) and `http` (PHP).

Depending on your ditro, these users and groups may already be created. See details on running `useradd` and `groupadd` in UserSetup.

The directory and files setup above should be owned by this user.

----



== Setup Software ==

At a minimum, we need: php, php-fpm, fcgi, fcgiwrap, and nginx.

Common additional tools include:
 * apache2-utils (a.k.a. apache-tools, httpd-utils, etc.) for creating .htpasswd files for basic restrictions



=== PHP ===

The primary configuration for PHP is found in `/etc/php/php.ini`. Some distributions carry two versions:

 * `php.ini-production` which is more secure
 * `php.ini-development` which is more backwards-compatible, and includes sensitive details in debugging messages

Chuck the latter straight into the bin.

Some key directives to check:

{{{
; Block calls from crafted URLs (i.e., `example.com/something-malicious.php`)
cgi.force_redirect = On

; Disable access to filesystem
file_uploads = Off

; Disable remote data retrieval
allow_url_fopen = Off
allow_url_include = Off
}}}



=== PHP-FPM ===

PHP-FPM is configured by a system configuration (`/etc/php/php-fpm.conf`) and by pool configurations (`/etc/php/php-fpm.d/*.conf`).

For the most part, the system configuration works out of the box.

{{{
; Pid file
pid = /run/php-fpm/php-fpm.pid

; Error log
error_log = /var/log/php-fpm.log
}}}

The pool configuration will need to be adjusted according to the user that was setup above.

{{{
; User/group of processes
user = www-data
group = www-data

; Socket file
listen = /run/php-fpm/php-fpm.pid

; User/group of sockets
listen.owner = www-data
listen.group = www-data

; Restrictions on file extensions
security.limit_extensions = .cgi .php

; Access log
access.log = /var/log/php-fpm/access.log
}}}



=== FastCGI and FCGIWrap ===

FastCGI takes a large number of parameters within NGINX configurations, so it is commonly 'configured' with `/etc/nginx/fastcgi_params`. This file should be created by default and should work by default.

FCGIWrap is, as the name implies, a wrapper around FastCGI. It will work without configuration.
== Usage ==
Line 125: Line 155:
For more details on NGINX configuration, see [[NGINXSetup|this walkthrough]]. A basic configuration for FastCGI would be:

{{{
user www-data www-data;
http {
  include mime.types;
  default_type application/mime.types;

  sendfile on;
  keepalive_timeout 65;
  gzip on;

  server {
    listen 80;
    server_name example.com;
    access_log /var/log/nginx/example.com/access.log;
    error_log /var/log/nginx/example.com/error.log;

    root /var/www;
    try_files $uri @cgi;

    location @cgi {
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $realpath_root/my-cgi-script.cgi;
      fastcgi_param PATH_INFO $uri;
      fastcgi_param QUERY_STRING $args;
      fastcgi_param HTTP_HOST $server_name;
      fastcgi_pass unix:/run/fcgiwrap.sock;
    }
  }
}
}}}
See [[NGINX/FastCGI#PHP_FPM|here]] for details.
Line 160: Line 159:
---- === Apache ===
Line 162: Line 161:


== Startup ==

----



== Maintenance ==
See [[Apache/FastCGI#PHP_FPM|here]] for details.

PHP-FPM

The PHP FastCGI Process Manager (PHP-FPM) is an implementation of the FastCGI specification.


Installation

PHP-FPM naturally depends on php(1). See here for help with installation, and here for help with configuration.

Most Linux and BSD distributions will offer a php-fpm package.

Official container images are available from the upstream development team. They are tagged like php:<version>-fpm


Configuration

php-fpm(8) listens on one or more ports or sockets. Each point of contact is a pool.

Each pool should have it's own configuration file under /etc/php/php.fpm.d. A pool's name ($pool) is derived from the section header.

As an example:

[www]
user = www-data
group = www-data

listen = 9000
listen.allowed_clients = 127.0.0.1, 192.168.86.1

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

TCP Sockets

The above example uses a shorthand: listen = 9000. This causes php-fpm(8) to listen on all addresses at port 9000.

However, listen.allowed_clients overrides this with a client whitelist. This should be comma-separated.

An IPv4 address is specified like 127.0.0.1:9000; an IPv6 address is specified like [::1]:9000.

Unix Sockets

To use a Unix socket to pass requests, try:

listen = /run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Static Process Management

A pool spawns and maintains pm.max_children number of processes.

pm = static
pm.max_children = 5

Dynamic Process Management

A pool spawns pm.start_servers processes. At any given time, some number are 'idle'. The pool tries to keep this number within the range of pm.min_spare_servers to pm.max_spare_servers, never surpassing pm.max_children.

pm = static
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

On-Demand Process Management

A pool spawns processes on-demand up until pm.max_children. A process is killed after pm.process_idle_timeout has passed.

pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s

Process Termination

php(1) scripts are known to leak memory frequently. As such, a regular task is to terminate long-running processes.

A simple solution is to set pm.max_requests to any number other than 0. Processes are killed after handling this number of requests.

Hardening

As a security measure, the allowable script extensions should be set as strictly as possible.

security.limit_extensions = .php .html .htm

A pool can be hardened by isolating it to a distinct working directory, or even a chroot.

chroot = /absolute/path/to/chroot
# or
chdir = relative/or/absolute/path/to/working/directory

By default, php-fpm(8) clears all environment variables. This can be disabled, but a better strategy is to set specific environment variables.

env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp

The same is true of PHP system variables.

php_admin_value[error_log] = /var/log/php-fpm.log


Usage

NGINX

See here for details.

Apache

See here for details.


CategoryRicottone

PHP/FPM (last edited 2023-05-25 17:00:50 by DominicRicottone)