1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
---
title: Apache and PHP-FPM
published: 2015-11-01
modified: 2015-11-02
---
There is lots of crappy information out there about deploying PHP with Apache,
or nginx. It is really hard to distill what is really a safe configuration and
what works. Combining this with a safe TLS configuration nears the
impossible.
### PHP-FPM
Configuring PHP-FPM is not that difficult, actually, one could keep the
defaults and that will work pretty well.
```
$ sudo dnf -y install php-fpm
```
I only change the configuration not to use a socket, but listen on TCP
instead. There are some more tweaks you can perform, but to get it working
reasonably well that is not needed yet.
```
$ sudo sed -i "s|listen = /run/php-fpm/www.sock|listen = [::]:9000|" /etc/php-fpm.d/www.conf
$ sudo sed -i "s/listen.allowed_clients = 127.0.0.1/listen.allowed_clients = 127.0.0.1,::1/" /etc/php-fpm.d/www.conf
```
You possibly have to update the `listen.allowed_clients` if you use a
separate VM or container for the web server.
Do not forget to enable and start PHP-FPM.
```
$ sudo systemctl enable php-fpm
$ sudo systemctl start php-fpm
```
That should be all for PHP-FPM.
### Apache
We start simple, with a HTTP server serving a PHP application using PHP-FPM.
```
<VirtualHost www.example.org:80>
ServerName www.example.org
ErrorLog logs/www.example.org_error_log
TransferLog logs/www.example.org_access_log
CustomLog logs/www.example.org_combined_log combined
LogLevel warn
DocumentRoot /usr/share/my-php-app/web
<Directory "/usr/share/my-php-app/web">
Options -MultiViews
#Require local
Require all granted
AllowOverride none
</Directory>
# Pass through the "Authorization" header
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
# Some request are handled by Apache directly
ProxyPass "/css/" !
ProxyPass "/img/" !
ProxyPass "/js/" !
ProxyPassMatch "^/robots.txt$" !
ProxyPassMatch "^/favicon.ico$" !
# The rest goes to PHP-FPM...
ProxyPass "/" fcgi://[::1]:9000/usr/share/php-my-app/web/index.php/
</VirtualHost>
```
### Apache TLS
Basically, this means that we remove the current contents of the
`VirtualHost` block and use it to rewrite to HTTPS instead and move
the PHP-FPM stuff to the new TLS `VirtualHost`.
```
<VirtualHost www.example.org:80>
ServerName www.example.org
ErrorLog logs/www.example.org_error_log
TransferLog logs/www.example.org_access_log
CustomLog logs/www.example.org_combined_log combined
LogLevel warn
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{ENV:HTTPS} !=on
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</VirtualHost>
```
Now, we create a new TLS `VirtualHost` that contains the stuff from
the previous section and some extra TLS configuration options.
```
<VirtualHost www.example.org:443>
ServerName www.example.org
ErrorLog logs/www.example.org_ssl_error_log
TransferLog logs/www.example.org_ssl_access_log
CustomLog logs/www.example.org_ssl_combined_log combined
LogLevel warn
DocumentRoot /usr/share/php-my-app/web
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/www.example.org.crt
#SSLCertificateChainFile /etc/pki/tls/certs/www.example.org-chain.crt
SSLCertificateKeyFile /etc/pki/tls/private/www.example.org.key
SSLProtocol all -SSLv3 -TLSv1
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
SSLHonorCipherOrder on
SSLCompression off
# OCSP Stapling, only in httpd 2.3.3 and later
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
<Directory "/usr/share/php-my-app/web">
Options -MultiViews
Require all granted
AllowOverride none
</Directory>
# Pass through the "Authorization" header
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
# Some request are handled by Apache directly
ProxyPass "/css/" !
ProxyPass "/img/" !
ProxyPass "/js/" !
ProxyPassMatch "^/robots.txt$" !
ProxyPassMatch "^/favicon.ico$" !
# The rest goes to PHP-FPM...
ProxyPass "/" fcgi://[::1]:9000/usr/share/php-my-app/web/index.php/
</VirtualHost>
```
Now we still need to generate the key and certificate and optionally have them
signed by some CA. The following commands make this very easy:
```
# Generate the private key
$ sudo openssl genrsa -out /etc/pki/tls/private/www.example.org.key 2048
$ sudo chmod 600 /etc/pki/tls/private/www.example.org.key
# Create the CSR (optionally, send this to CA to have signed)
$ sudo openssl req -subj "/CN=www.example.org" -sha256 -new -key /etc/pki/tls/private/www.example.org.key -out www.example.org.csr
# Create the (self signed) certificate and install it
$ sudo openssl req -subj "/CN=www.example.org" -sha256 -new -x509 -key /etc/pki/tls/private/www.example.org.key -out /etc/pki/tls/certs/www.example.org.crt
```
If you want to have the certificate signed by a CA, use the CSR generated
above and send it to the CA. Once you get a certificate back, overwrite the
self signed certificate in `/etc/pki/tls/certs/www.example.org.crt`
and make sure to also configure the `SSLCertificateChainFile`.
Next you can just place the two `VirtualHost` sections above in one
file, put it in `/etc/httpd/conf.d/www.example.org.conf` and enable
and start Apache.
```
$ sudo systemctl enable httpd
$ sudo systemctl start httpd
```
### Unanswered Questions
This stuff is so complex that there are still some issues that I do not know
how to solve. Hopefully this list will become smaller over time.
- The URL passed to PHP-FPM is still "URL encoded", for example `%20` is not
converted back to `SPACE`;
- Should we actually have the `RewriteCond` rules in the HTTP `VirtualHost`
section? It MUST always be rewritten?
### Resources
These resources are a MUST. Make sure to take note of everything that is
mentioned there! Do not trust what I write here without thinking for yourself
and making sure you understand everything.
- [PHP: The Right Way](http://www.phptherightway.com/#servers_and_deployment) (Servers and Deployment)
- [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/)
- [SSL Server Test](https://www.ssllabs.com/ssltest/) (Qualys)
- [SSL Decoder](https://ssldecoder.org/) (Remy van Elst)
- [Earlier blog post on HTTPS](https.html)
|