ArchLinux:Nginx: Difference between revisions

From Wiki³
 
(28 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{DISPLAYTITLE:{{TitleIcon|arch=true}} NGINX + PHP}}<metadesc>Web server setup for Arch Linux.</metadesc>
{{DISPLAYTITLE:{{TitleIcon|arch=true}} NGINX + PHP}}<metadesc>Setting up a full web stack on Arch Linux.</metadesc>
<div id="tocalign">__TOC__</div>
<div id="tocalign">__TOC__</div>
{{Back|Arch Linux}}
{{Back|Arch Linux}}
= {{Icon24|sitemap}} Introduction =
= {{Icon24|sitemap}} Introduction =


= {{Icon24|sitemap}} NGINX =
= {{Icon24|sitemap}} Install =
Beforehand be sure to determine weather the web server will be using MySQL (ie. MariaDB) or PostgreSQL.
Beforehand be sure to determine weather the web server will be using MySQL (ie. MariaDB) or PostgreSQL.


Line 10: Line 10:
{{Console|1=pikaur -S apache-tools composer curl minify nginx php-fpm sassc wget}}
{{Console|1=pikaur -S apache-tools composer curl minify nginx php-fpm sassc wget}}
Install all of the required PHP extensions.
Install all of the required PHP extensions.
{{Console|1=pikaur -S php-gd php-geoip php-imagic php-intl php-memcache php-pgsql php-sqlite php-sodium xdebug}}
{{Console|1=pikaur -S php-gd php-geoip php-imagick php-intl php-memcache php-odbc php-sqlite php-sodium xdebug}}
Next create the environment for the web server.
Next create the environment for the web server.
{{Console|1=sudo mkdir -p /nginx/conf.d /nginx/https /nginx/logs /nginx/sql /nginx/ssl /nginx/vhosts.d}}
{{Console|1=sudo mkdir -p /nginx/conf.d /nginx/https /nginx/logs /nginx/sql /nginx/ssl /nginx/vhosts.d}}
{{margin}}
{{Console|1=sudo chown -R http:http /nginx}}
{{margin}}
{{margin}}
{{Console|1=sudo chmod -R 770 /nginx}}
{{Console|1=sudo chmod -R 770 /nginx}}
Line 19: Line 21:
{{margin}}
{{margin}}
{{Console|1=sudo gpasswd -a {{cyanBold|username}} http}}
{{Console|1=sudo gpasswd -a {{cyanBold|username}} http}}
Set the default shell for {{mono|http}} to Bash.
{{Console|1=sudo chsh -s /bin/bash http}}
== {{Icon|notebook}} PostgreSQL ==
== {{Icon|notebook}} PostgreSQL ==
Using {{mono|postgresql}} as a back-end will require the following setup and configuration.
Using {{mono|postgresql}} as a back-end will require the following setup and configuration.
{{Console|1=pikaur -S postgresql}}
{{Console|1=pikaur -S postgresql php-pgsql}}
{{margin}}
{{margin}}
{{Console|1=sudo chown postgres:postgres /nginx/sql}}
{{Console|1=sudo chown postgres:postgres /nginx/sql}}
Line 42: Line 46:
Create a new postgres user account.
Create a new postgres user account.
{{Console|1=createuser -P --interactive<br/>Enter name of role to add: {{cyanBold|username}}<br/>Enter password for new role: {{cyanBold|********}}<br/>Enter it again: {{cyanBold|********}}<br/>Shall the new role be a superuser? (y/n) {{cyanBold|n}}<br/>Shall the new role be allowed to create databases? (y/n) {{cyanBold|y}}<br/>Shall the new role be allowed to create more new roles? (y/n) {{cyanBold|n}}}}
{{Console|1=createuser -P --interactive<br/>Enter name of role to add: {{cyanBold|username}}<br/>Enter password for new role: {{cyanBold|********}}<br/>Enter it again: {{cyanBold|********}}<br/>Shall the new role be a superuser? (y/n) {{cyanBold|n}}<br/>Shall the new role be allowed to create databases? (y/n) {{cyanBold|y}}<br/>Shall the new role be allowed to create more new roles? (y/n) {{cyanBold|n}}}}
== {{Icon|notebook}} MariaDB ==
== {{Icon|notebook}} MariaDB ==
Using {{mono|mariadb}} as a back-end will require the following setup and configuration.
Using {{mono|mariadb}} as a back-end will require the following setup and configuration.
Line 61: Line 64:
{{Console|1=sudo mysql_secure_installation}}
{{Console|1=sudo mysql_secure_installation}}
{{Note|1=The default mysql root password is none}}
{{Note|1=The default mysql root password is none}}
Finally add a user account.
Connect to mysql using the root account and the password you previously set.
{{Console|1=sudo mysql -u root -p<br/>MariaDB [(none)]&gt; GRANT ALL PRIVILEGES ON *.* TO '{{cyanBold|kyau}}'@'%' IDENTIFIED BY '{{cyanBold|remote_password}}' WITH GRANT OPTION;}}
{{Console|1=sudo mysql -u root -p}}
Add a new mysql user account.
{{Console|1=MariaDB [(none)]&gt; GRANT ALL PRIVILEGES ON *.* TO '{{cyanBold|kyau}}'@'localhost' {{greenBold|\}}<br/>&emsp;&emsp;IDENTIFIED BY '{{cyanBold|user_password}}' WITH GRANT OPTION;}}
 
= {{Icon24|sitemap}} Configuration =
== {{Icon|notebook}} PHP ==
First remove the default pool.
{{Console|1=sudo rm /etc/php/php-fpm.d/www.conf}}
Create the defaults for all pools.
{{Console|1=sudoedit /etc/php/php-fpm.d/defaults.inc}}
{{margin}}
{{Console|title=/etc/php/php-fpm.d/defaults.inc|prompt=false|1=user {{=}} http<br/>group {{=}} http<br/>listen {{=}} /run/php-fpm/php-fpm-$pool.sock<br/>listen.owner {{=}} http<br/>listen.group {{=}} http<br/>{{blackBold|; process configuration}}<br/>pm {{=}} dynamic<br/>pm.max_children {{=}} 5<br/>pm.start_servers {{=}} 2<br/>pm.min_spare_servers {{=}} 1<br/>pm.max_spare_servers {{=}} 3<br/>{{blackBold|; php.ini changes}}<br/>php_admin_flag[expose_php] {{=}} off<br/>php_admin_flag[log_errors] {{=}} on<br/>php_admin_flag[short_open_tag] {{=}} on<br/>php_admin_value[date.timezone] {{=}} America/Los_Angeles<br/>php_admin_value[error_log] {{=}} /nginx/logs/$pool/php.log<br/>php_admin_value[memory_limit] {{=}} 256M<br/>php_admin_value[post_max_size] {{=}} 2048M<br/>php_admin_value[session.save_path] {{=}} /tmp<br/>php_admin_value[upload_max_filesize] {{=}} 2048M}}
Enable all third party PHP extensions that were installed.
{{Console|1=sudo find . -type f -name '*.ini' -exec sed -i -e 's/^;extension/extension/g' {{greenBold|\}}<br/>&emsp;&emsp;-e 's/^;zend_extension/zend_extension/g' -e 's/^;xdebug/xdebug/g' {} +}}
Enable global PHP extensions.
{{Console|1=sudoedit /etc/php/conf.d/defaults.ini}}
{{margin}}
{{Console|title=/etc/php/conf.d/defaults.ini|prompt=false|1=extension{{=}}bz2<br/>extension{{=}}exif<br/>extension{{=}}gd<br/>extension{{=}}gettext<br/>extension{{=}}gmp<br/>extension{{=}}iconv<br/>extension{{=}}intl<br/>extension{{=}}sodium<br/>extension{{=}}mysqli<br/>extension{{=}}odbc<br/>extension{{=}}pdo_mysql<br/>extension{{=}}pdo_odbc<br/>extension{{=}}pdo_sqlite<br/>extension{{=}}sockets<br/>extension{{=}}sqlite3<br/>{{blackBold|; opcache}}<br/>zend_extension{{=}}opcache<br/>opcache.enable {{=}} 1<br/>opcache.interned_strings_buffer {{=}} 8<br/>opcache.max_accelerated_files {{=}} 10000<br/>opcache.memory_consumption {{=}} 128<br/>opcache.save_comments {{=}} 1<br/>opcache.revalidate_freq {{=}} 1}}
Create a php-fpm pool for the domain being setup (use a different pool for each site/domain).
{{Console|1=sudoedit /etc/php/php-fpm.d/domain_com.conf}}
{{margin}}
{{Console|title=/etc/php/php-fpm.d/domain_com.conf|prompt=false|1={{blackBold|; $KYAULabs: domain_com.conf,v 1.0.0 2021/05/01 12:36:14 kyau Exp $}}<br/><br/>[domain_com]<br/>include {{=}} /etc/php/php-fpm.d/defaults.inc<br/>env[HOSTNAME] {{=}} domain.com<br/>env[PATH] {{=}} /usr/local/bin:/usr/bin:/bin<br/>env[TMP] {{=}} /tmp<br/>env[TMPDIR] {{=}} /tmp<br/>env[TEMP] {{=}} /tmp<br/><br/>{{blackBold|; vim: ft{{=}}dosini sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}
{{margin}}
{{Note|1=One can temporarily disable sites/domains by renaming the config to {{mono|.conf.disable}} and reloading the php-fpm service}}
Be sure to set the file permissions properly.
{{Console|1=sudo chmod 644 /etc/php/conf.d/defaults.ini /etc/php/php-fpm.d/*}}
Start and enable the {{mono|php-fpm}} service.
{{Console|1=sudo systemctl enable --now php-fpm.service}}
 
== {{Icon|notebook}} NGINX ==
Create a blank configuration file.
{{Console|1=sudo install -g http -m 660 -o http /dev/null /nginx/conf.d/nginx.conf}}
Copy the MIME types file.
{{Console|1=sudo install -g http -m 660 -o http /etc/nginx/mime.types /nginx/conf.d/mime.types}}
Remove the default config in {{mono|nginx.conf}} and replace it with an include (to the new config location).
{{Console|1=sudoedit /etc/nginx/nginx.conf}}
{{margin}}
{{Console|title=/etc/nginx/nginx.conf|prompt=false|1=include /nginx/conf.d/nginx.conf;}}
Create the nginx config file.
{{Console|1=sudoedit /nginx/conf.d/nginx.conf}}
{{margin}}
{{Console|title=/nginx/conf.d/nginx.conf|prompt=false|1={{blackBold|# $KYAULabs: nginx.conf,v 1.1.7 2021/05/03 18:14:27 kyau Exp $}}<br/><br/>{{blackBold|# Help / Additional Info &#123;&#123;&#123;}}<br/>{{blackBold|# always test configuration before reload!}}<br/>{{blackBold|# $ sudo nginx -t}}<br/>{{blackBold|# reload the configuration by using reload not restart!}}<br/>{{blackBold|# $ sudo systemctl reload nginx}}<br/>{{blackBold|# <nowiki>https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/</nowiki>}}<br/>{{blackBold|# &#125;&#125;&#125;}}<br/><br/>{{blackBold|# enables the use of “just-in-time compilation” for the regular expressions}}<br/>{{blackBold|# known by the time of configuration parsing}}<br/>pcre_jit on;<br/>{{blackBold|# user and group credentials used by worker processes}}<br/>user http http;<br/>{{blackBold|# number of worker processes (auto will autodetect number of CPU cores)}}<br/>worker_processes auto;<br/>{{blackBold|# binds worker processes automatically to available CPUs}}<br/>worker_cpu_affinity auto;<br/>{{blackBold|# number of file descriptors used for nginx}}<br/>worker_rlimit_nofile 65535;<br/><br/>events &#123;<br/>    {{blackBold|# worker process will accept one/all (off/on) connection(s) at a time}}<br/>    multi_accept on;<br/>    {{blackBold|# maximum number of simultaneous connections that can be opened by a worker}}<br/>    worker_connections 4096;<br/>&#125;<br/><br/>http &#123;<br/>    {{blackBold|# mime types}}<br/>    include /nginx/conf.d/mime.types;<br/>    {{blackBold|# to boost I/O on HDD we can disable access logs}}<br/>    access_log off;<br/>    {{blackBold|# read and send using multi-threading, without blocking a worker process}}<br/>    aio threads;<br/>    {{blackBold|# hide index pages}}<br/>    autoindex off;<br/>    {{blackBold|# add to 'Content-Type' response header}}<br/>    charset utf-8;<br/>    {{blackBold|# request timed out -- default 60}}<br/>    client_body_timeout 10;<br/>    {{blackBold|# sets the maximum allowed size of the client request body -- default 1}}<br/>    client_max_body_size 16m;<br/>    {{blackBold|# default mime type}}<br/>    default_type text/plain;<br/>    {{blackBold|# enable gzipping of responses}}<br/>    gzip on;<br/>    {{blackBold|# disables gzipping of responses for msie6 and below}}<br/>    gzip_disable "msie6";<br/>    {{blackBold|# minimum length of a response that will be gzipped -- default 20}}<br/>    gzip_min_length 1024;<br/>    {{blackBold|# gzip compression level -- default 1}}<br/>    gzip_comp_level 6;<br/>    gzip_vary on;<br/>    gzip_proxied expired no-cache no-store private auth;<br/>    {{blackBold|# text/html is always compressed}}<br/>    gzip_types<br/>        text/css<br/>        text/javascript<br/>        text/xml<br/>        text/x-component<br/>        application/javascript<br/>        application/x-javascript<br/>        application/json<br/>        application/xml<br/>        application/rss+xml<br/>        application/atom+xml<br/>        font/truetype<br/>        font/opentype<br/>        application/vnd.ms-fontobject<br/>        image/svg+xml;<br/>    {{blackBold|# files that will be used as an index, checked in the specified order}}<br/>    index index.php index.html index.htm index.txt;<br/>    {{blackBold|# enables keep-alive connections with all browsers}}<br/>    keepalive_disable none;<br/>    {{blackBold|# keep-alive client connections stay active for -- default 75}}<br/>    keepalive_timeout 30s;<br/>    {{blackBold|# specifies log format}}<br/>    log_format main<br/>        '$remote_addr - $remote_user [$time_local] '<br/>        '"$request" $status $body_bytes_sent '<br/>        '"$http_referer" "$http_user_agent"';<br/>    {{blackBold|# disables logging of errors about not found files into error_log}}<br/>    log_not_found off;<br/>    {{blackBold|# cache open file descriptors, directories and file lookup errors}}<br/>    open_file_cache max=10240 inactive=20s;<br/>    open_file_cache_valid 30s;<br/>    open_file_cache_min_uses 2;<br/>    open_file_cache_errors on;<br/>    {{blackBold|# allow the server to close connection on non responding client, this will}}<br/>    {{blackBold|# free up memory}}<br/>    reset_timedout_connection on;<br/>    {{blackBold|# if client stops responding, free up memory -- default 60}}<br/>    send_timeout 8;<br/>    {{blackBold|# copies data between one FD and other from within the kernel faster than}}<br/>    {{blackBold|# read() + write()}}<br/>    sendfile on;<br/>    {{blackBold|# bucket size for the server names hash tables}}<br/>    server_names_hash_bucket_size 128;<br/>    {{blackBold|# disables emitting nginx version on error pages and in the "server"}}<br/>    {{blackBold|# response header field}}<br/>    server_tokens off;<br/>    {{blackBold|# send headers in one piece, it is better than sending them one by one}}<br/>    tcp_nopush on;<br/>    {{blackBold|# don't buffer data sent, good for small data bursts in real time}}<br/>    tcp_nodelay on;<br/>    {{blackBold|# hash table maximum size -- default 1024}}<br/>    types_hash_max_size 4096;<br/><br/>    {{blackBold|# include domain configuration files}}<br/>    include /nginx/vhosts.d/*.conf;<br/><br/>    {{blackBold|# redirect all non-encrypted (http) traffic to encrypted (https)}}<br/>    server &#123;<br/>        server_name _;<br/>        listen *:80 default_server;<br/>        listen [::]:80 default_server;<br/>        return 301 <nowiki>https://$host$request_uri;</nowiki><br/>    &#125;<br/>&#125;<br/><br/>{{blackBold|# vim: ft{{=}}nginx sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}
Create a vhost defaults config.
{{Console|1=sudoedit /nginx/conf.d/vhost_defaults}}
{{margin}}
{{Console|title=/nginx/conf.d/vhost_defaults|prompt=false|1={{blackBold|# $KYAULabs: vhost.defaults,v 1.0.7 2021/05/16 12:48:55 kyau Exp $}}<br/><br/>{{blackBold|## SSL/TLS (<nowiki>https://cipherlist.dev/</nowiki>)}}<br/>ssl_dhparam /nginx/ssl/dhparam4096.pem; {{blackBold|# openssl dhparam -out dhparam4096.pem 4096}}<br/>ssl_protocols TLSv1.3; {{blackBold|# Requires nginx >{{=}} 1.13.0}}<br/>ssl_ciphers EECDH+CHACHA20:EECDH+AES;<br/>ssl_ecdh_curve X25519; {{blackBold|# Requires nginx >{{=}} 1.1.0}}<br/>ssl_session_cache shared:SSL:10m;<br/>ssl_session_tickets off; {{blackBold|# Requires nginx >{{=}} 1.5.9}}<br/>ssl_session_timeout 10m;<br/>ssl_stapling on; {{blackBold|# Requires nginx >{{=}} 1.3.7}}<br/>ssl_stapling_verify on; {{blackBold|# Requires nginx >{{=}} 1.3.7}}<br/>ssl_prefer_server_ciphers on;<br/>resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=60s; {{blackBold|# Change if you run your own DNS servers}}<br/>resolver_timeout 2s;<br/><br/>{{blackBold|# security settings}}<br/>add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;<br/>add_header X-Frame-Options DENY;<br/>add_header X-Content-Type-Options nosniff;<br/>add_header X-XSS-Protection "1; mode=block";<br/><br/>{{blackBold|# vim: ft{{=}}nginx sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}
Create a URI defaults config.
{{Console|1=sudoedit /nginx/conf.d/uri_defaults}}
{{Console|title=/nginx/conf.d/uri_defaults|prompt=false|1={{blackBold|# $KYAULabs: uri_defaults,v 1.0.2 2021/05/05 17:55:10 kyau Exp $}}<br/><br/>{{blackBold|# remove `robots.txt` and all favicons from the logs}}<br/>location ~* "^/(favicon\.\S{3,5}{{!}}robots\.txt)$" &#123;<br/>    access_log off;<br/>    allow all;<br/>    log_not_found off;<br/>&#125;<br/><br/>{{blackBold|# disable dot (hidden) files while allowing `.well-known`}}<br/>location ~* /\.(?!well-known).* &#123;<br/>    access_log off;<br/>    deny all;<br/>    log_not_found off;<br/>&#125;<br/><br/>{{blackBold|# deny access to any sensitive material}}<br/>location ~* (?:#.*#{{!}}\.(?:bak{{!}}conf{{!}}dist{{!}}fla{{!}}in[ci]{{!}}log{{!}}orig{{!}}phps{{!}}psd{{!}}sass{{!}}scss{{!}}sh{{!}}sql{{!}}sw[op]){{!}}~)$ &#123;<br/>    access_log on;<br/>    deny all;<br/>    log_not_found on;<br/>&#125;<br/><br/>{{blackBold|# asset/media cache}}<br/>location ~* \.(?:css(\.map)?{{!}}js(\.map)?{{!}}jpe?g{{!}}png{{!}}gif{{!}}ico{{!}}cur{{!}}heic{{!}}webp{{!}}tiff?{{!}}mp3{{!}}m4a{{!}}aac{{!}}ogg{{!}}midi?{{!}}wav{{!}}mp4{{!}}mov{{!}}webm{{!}}mpe?g{{!}}avi{{!}}ogv{{!}}flv{{!}}wmv)$ &#123;<br/>    access_log off;<br/>    expires 7d;<br/>&#125;<br/><br/>{{blackBold|# fonts/svg cache and access}}<br/>location ~* \.(?:svgz?{{!}}ttf{{!}}ttc{{!}}otf{{!}}eot{{!}}woff2?)$ &#123;<br/>    access_log off;<br/>    add_header Access-Control-Allow-Origin "*";<br/>    expires 7d;<br/>&#125;<br/><br/>{{blackBold|# html processing}}<br/>location ~* \.html$ &#123;<br/>    try_files $uri $uri/ /index.html {{=}}404;<br/>&#125;<br/><br/>{{blackBold|# http error pages}}<br/>error_page 404 /404;<br/>error_page 500 502 503 504 /50x;<br/>location = /404 &#123;<br/>    root /nginx/https/error;<br/>    index 404.php;<br/>&#125;<br/>location = /50x &#123;<br/>    root /nginx/https/error;<br/>    index 50x.php;<br/>&#125;<br/><br/>{{blackBold|# vim: ft{{=}}nginx sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}
=== FastCGI ===
Create a {{mono|fastcgi_params}} config file (PHP environmental variable defaults).
{{Console|1=sudoedit /nginx/conf.d/fastcgi_params}}
{{margin}}
{{Console|title=/nginx/conf.d/fastcgi_params|prompt=false|1={{blackBold|# $KYAULabs: fastcgi_params,v 1.0.5 2021/05/03 17:31:37 kyau Exp $}}<br/><br/>fastcgi_param QUERY_STRING $query_string;<br/>fastcgi_param REQUEST_METHOD $request_method;<br/>fastcgi_param CONTENT_TYPE $content_type;<br/>fastcgi_param CONTENT_LENGTH $content_length;<br/><br/>fastcgi_param SCRIPT_FILENAME $request_filename;<br/>fastcgi_param SCRIPT_NAME $fastcgi_script_name;<br/>fastcgi_param REQUEST_URI $request_uri;<br/>fastcgi_param DOCUMENT_URI $document_uri;<br/>fastcgi_param DOCUMENT_ROOT $document_root;<br/>fastcgi_param SERVER_PROTOCOL $server_protocol;<br/>fastcgi_param REQUEST_SCHEME $scheme;<br/>fastcgi_param HTTPS $https if_not_empty;<br/><br/>fastcgi_param GATEWAY_INTERFACE CGI/1.1;<br/>{{blackBold|#fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;}}<br/>fastcgi_param SERVER_SOFTWARE nginx;<br/><br/>fastcgi_param REMOTE_ADDR $remote_addr;<br/>fastcgi_param REMOTE_PORT $remote_port;<br/>fastcgi_param SERVER_ADDR $server_addr;<br/>fastcgi_param SERVER_PORT $server_port;<br/>fastcgi_param SERVER_NAME $server_name;<br/><br/>{{blackBold|# PHP only, required if PHP was built with --enable-force-cgi-redirect}}<br/>fastcgi_param REDIRECT_STATUS 200;<br/><br/>{{blackBold|# Mitigate <nowiki>https://httpoxy.org/</nowiki> vulnerabilities}}<br/>fastcgi_param HTTP_PROXY "";<br/><br/>{{blackBold|# vim: ft{{=}}nginx sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}
=== SSL/TLS ===
Create the {{mono|dhparam}} as indicated above.
{{Console|1=sudo -u http openssl dhparam -out /nginx/ssl/dhparam4096.pem 4096}}
Set permissions properly.
{{Console|1=sudo chmod 660 /nginx/ssl/dhparam4096.pem}}
=== Virtual Hosts ===
Virtual Hosts are created in domain config files keeping all subdomains in a single file. The following template can be used, simply replace {{mono|domain_com}} and {{mono|domain.com}} with the actual domain name.
{{Console|1=sudoedit /nginx/vhosts.d/{{cyanBold|domain_com}}.conf}}
{{margin}}
{{Console|title=/nginx/vhosts.d/domain_com.conf|prompt=false|1={{blackBold|# $KYAULabs: domain_com.conf,v 1.1.5 2021/05/05 18:06:34 kyau Exp $}}<br/><br/>{{blackBold|## Redirect all WWW to Non-WWW (SSL)}}<br/>server &#123;<br/> listen *:443 ssl http2;<br/> listen [::]:443 ssl http2;<br/> server_name www.{{cyanBold|domain.com}};<br/><br/> #access_log /nginx/logs/{{cyanBold|domain_com}}/www-access.log;<br/> error_log /nginx/logs/{{cyanBold|domain_com}}/www-error.log;<br/><br/> ssl_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/fullchain.pem;<br/> ssl_certificate_key /etc/letsencrypt/live/{{cyanBold|domain.com}}/privkey.pem;<br/> ssl_trusted_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/chain.pem;<br/><br/> include /nginx/conf.d/vhost_defaults;<br/><br/> return 301 <nowiki>https://domain.com$request_uri</nowiki>;<br/>&#125;<br/><br/>{{blackBold|## domain.com}}<br/>server &#123;<br/> listen *:443 ssl http2;<br/> listen [::]:443 ssl http2;<br/> server_name {{cyanBold|domain.com}};<br/><br/> #access_log /nginx/logs/{{cyanBold|domain_com}}/domain_com-access.log;<br/> error_log /nginx/logs/{{cyanBold|domain_com}}/domain_com-error.log;<br/><br/> ssl_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/fullchain.pem;<br/> ssl_certificate_key /etc/letsencrypt/live/{{cyanBold|domain.com}}/privkey.pem;<br/> ssl_trusted_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/chain.pem;<br/><br/> include /nginx/conf.d/vhost_defaults;<br/><br/> root /nginx/https/{{cyanBold|domain_com}}/www;<br/><br/> location / &#123;<br/> try_files $uri $uri/ @rewrite;<br/> &#125;<br/><br/> location @rewrite &#123;<br/> {{blackBold|#rewrite ^/directory/$ /script.php?x{{=}}directory last;}}<br/> &#125;<br/><br/> {{blackBold|#location ^~ /app/ &#123;<br/> #proxy_pass <nowiki>http://127.0.0.1:8080/</nowiki>;<br/> #proxy_http_version 1.1;<br/> #proxy_set_header Connection "upgrade";<br/> #proxy_set_header Upgrade $http_upgrade;<br/> #proxy_set_header X-Forwarded-For $remote_addr;<br/> #proxy_set_header X-Forwarded-Proto $scheme;<br/> # by defaults nginx times out connections in one minute<br/> #proxy_read_timeout 1d;<br/> #&#125;}}<br/><br/> include /nginx/conf.d/uri_defaults;<br/><br/> {{blackBold|# php scripts}}<br/> location ~* [^/]\.php(/{{!}}$) &#123;<br/> try_files $fastcgi_script_name =404;<br/> include /nginx/conf.d/fastcgi_params;<br/> fastcgi_buffers 8 16k;<br/> fastcgi_buffer_size 32k;<br/> fastcgi_index index.php;<br/> fastcgi_split_path_info ^(.+?\.php)(/.*)$;<br/> fastcgi_pass unix:/run/php-fpm/php-fpm-domain_com.sock;<br/> &#125;<br/>&#125;<br/><br/>{{blackBold|## api.domain.com}}<br/>server &#123;<br/> listen *:443 ssl http2;<br/> listen [::]:443 ssl http2;<br/> server_name api.{{cyanBold|domain.com}};<br/><br/> #access_log /nginx/logs/{{cyanBold|domain_com}}/api-access.log;<br/> error_log /nginx/logs/{{cyanBold|domain_com}}/api-error.log;<br/><br/> ssl_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/fullchain.pem;<br/> ssl_certificate_key /etc/letsencrypt/live/{{cyanBold|domain.com}}/privkey.pem;<br/> ssl_trusted_certificate /etc/letsencrypt/live/{{cyanBold|domain.com}}/chain.pem;<br/><br/> include /nginx/conf.d/vhost_defaults;<br/><br/> root /nginx/https/{{cyanBold|domain_com}}/api;<br/><br/> location / &#123;<br/> try_files $uri $uri/ @rewrite;<br/> &#125;<br/><br/> location @rewrite &#123;<br/> {{blackBold|#rewrite ^/directory/$ /script.php?x{{=}}directory last;}}<br/> &#125;<br/><br/> include /nginx/conf.d/uri_defaults;<br/><br/> {{blackBold|# php scripts}}<br/> location ~* [^/]\.php(/{{!}}$) &#123;<br/> try_files $fastcgi_script_name =404;<br/> include /nginx/conf.d/fastcgi_params;<br/> fastcgi_buffers 8 16k;<br/> fastcgi_buffer_size 32k;<br/> fastcgi_index index.php;<br/> fastcgi_split_path_info ^(.+?\.php)(/.*)$;<br/> fastcgi_pass unix:/run/php-fpm/php-fpm-domain_com.sock;<br/> &#125;<br/>&#125;<br/><br/>{{blackBold|# vim: ft{{=}}nginx sts{{=}}4 sw{{=}}4 ts{{=}}4 noet :}}}}




[[Category:Arch Linux]]
[[Category:Arch Linux]]

Latest revision as of 00:46, 17 May 2021

Icon Introduction

Icon Install

Beforehand be sure to determine weather the web server will be using MySQL (ie. MariaDB) or PostgreSQL.

Begin by installing NGINX, PHP and other required utilities.

# pikaur -S apache-tools composer curl minify nginx php-fpm sassc wget

Install all of the required PHP extensions.

# pikaur -S php-gd php-geoip php-imagick php-intl php-memcache php-odbc php-sqlite php-sodium xdebug

Next create the environment for the web server.

# sudo mkdir -p /nginx/conf.d /nginx/https /nginx/logs /nginx/sql /nginx/ssl /nginx/vhosts.d
 
# sudo chown -R http:http /nginx
 
# sudo chmod -R 770 /nginx
 
# sudo chmod 750 /nginx/sql
 
# sudo gpasswd -a username http

Set the default shell for http to Bash.

# sudo chsh -s /bin/bash http

Icon PostgreSQL

Using postgresql as a back-end will require the following setup and configuration.

# pikaur -S postgresql php-pgsql
 
# sudo chown postgres:postgres /nginx/sql
 
# sudo gpasswd -a username postgres

Swap over to the postgresql user account.

# sudo -iu postgres

Run the database initialization.

# initdb --locale en_US.UTF-8 -E UTF8 -D '/nginx/sql/data'

Return to the normal user account.

# exit

Modify the systemd service file to reflect the new data directory.

# sudo systemctl edit postgresql.service
 
filename: postgresql.service
Environment=PGROOT=/nginx/sql
PIDFile=/nginx/sql/postmaster.pid

Start and enable the systemd service.

# sudo systemctl enable --now postgresql.service

Swap back over to the postgresql user account.

# sudo -iu postgres

Create a new postgres user account.

# createuser -P --interactive
Enter name of role to add: username
Enter password for new role: ********
Enter it again: ********
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) y
Shall the new role be allowed to create more new roles? (y/n) n

Icon MariaDB

Using mariadb as a back-end will require the following setup and configuration.

# pikaur -S mariadb
 
# sudo chown mysql:mysql /nginx/sql

Give the current logged in user access.

# sudo gpasswd -a username mysql

Create and initialize the data directory.

# mariadb-install-db --user=mysql --basedir=/usr --datadir=/nginx/sql
 
# sudoedit /etc/my.cnf.d/server.cnf
 
filename: /etc/my.cnf.d/server.cnf
[mysqld]
datadir=/nginx/sql

Start and enable the MySQL service.

# sudo systemctl enable --now mariadb.service

Secure the installation and set the root password.

# sudo mysql_secure_installation
IconThe default mysql root password is none

Connect to mysql using the root account and the password you previously set.

# sudo mysql -u root -p

Add a new mysql user account.

# MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'kyau'@'localhost' \
  IDENTIFIED BY 'user_password' WITH GRANT OPTION;

Icon Configuration

Icon PHP

First remove the default pool.

# sudo rm /etc/php/php-fpm.d/www.conf

Create the defaults for all pools.

# sudoedit /etc/php/php-fpm.d/defaults.inc
 
filename: /etc/php/php-fpm.d/defaults.inc
user = http
group = http
listen = /run/php-fpm/php-fpm-$pool.sock
listen.owner = http
listen.group = http
; process configuration
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
; php.ini changes
php_admin_flag[expose_php] = off
php_admin_flag[log_errors] = on
php_admin_flag[short_open_tag] = on
php_admin_value[date.timezone] = America/Los_Angeles
php_admin_value[error_log] = /nginx/logs/$pool/php.log
php_admin_value[memory_limit] = 256M
php_admin_value[post_max_size] = 2048M
php_admin_value[session.save_path] = /tmp
php_admin_value[upload_max_filesize] = 2048M

Enable all third party PHP extensions that were installed.

# sudo find . -type f -name '*.ini' -exec sed -i -e 's/^;extension/extension/g' \
  -e 's/^;zend_extension/zend_extension/g' -e 's/^;xdebug/xdebug/g' {} +

Enable global PHP extensions.

# sudoedit /etc/php/conf.d/defaults.ini
 
filename: /etc/php/conf.d/defaults.ini
extension=bz2
extension=exif
extension=gd
extension=gettext
extension=gmp
extension=iconv
extension=intl
extension=sodium
extension=mysqli
extension=odbc
extension=pdo_mysql
extension=pdo_odbc
extension=pdo_sqlite
extension=sockets
extension=sqlite3
; opcache
zend_extension=opcache
opcache.enable = 1
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.memory_consumption = 128
opcache.save_comments = 1
opcache.revalidate_freq = 1

Create a php-fpm pool for the domain being setup (use a different pool for each site/domain).

# sudoedit /etc/php/php-fpm.d/domain_com.conf
 
filename: /etc/php/php-fpm.d/domain_com.conf
; $KYAULabs: domain_com.conf,v 1.0.0 2021/05/01 12:36:14 kyau Exp $

[domain_com]
include = /etc/php/php-fpm.d/defaults.inc
env[HOSTNAME] = domain.com
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

; vim: ft=dosini sts=4 sw=4 ts=4 noet :
 
IconOne can temporarily disable sites/domains by renaming the config to .conf.disable and reloading the php-fpm service

Be sure to set the file permissions properly.

# sudo chmod 644 /etc/php/conf.d/defaults.ini /etc/php/php-fpm.d/*

Start and enable the php-fpm service.

# sudo systemctl enable --now php-fpm.service

Icon NGINX

Create a blank configuration file.

# sudo install -g http -m 660 -o http /dev/null /nginx/conf.d/nginx.conf

Copy the MIME types file.

# sudo install -g http -m 660 -o http /etc/nginx/mime.types /nginx/conf.d/mime.types

Remove the default config in nginx.conf and replace it with an include (to the new config location).

# sudoedit /etc/nginx/nginx.conf
 
filename: /etc/nginx/nginx.conf
include /nginx/conf.d/nginx.conf;

Create the nginx config file.

# sudoedit /nginx/conf.d/nginx.conf
 
filename: /nginx/conf.d/nginx.conf
# $KYAULabs: nginx.conf,v 1.1.7 2021/05/03 18:14:27 kyau Exp $

# Help / Additional Info {{{
# always test configuration before reload!
# $ sudo nginx -t
# reload the configuration by using reload not restart!
# $ sudo systemctl reload nginx
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# }}}

# enables the use of “just-in-time compilation” for the regular expressions
# known by the time of configuration parsing
pcre_jit on;
# user and group credentials used by worker processes
user http http;
# number of worker processes (auto will autodetect number of CPU cores)
worker_processes auto;
# binds worker processes automatically to available CPUs
worker_cpu_affinity auto;
# number of file descriptors used for nginx
worker_rlimit_nofile 65535;

events {
# worker process will accept one/all (off/on) connection(s) at a time
multi_accept on;
# maximum number of simultaneous connections that can be opened by a worker
worker_connections 4096;
}

http {
# mime types
include /nginx/conf.d/mime.types;
# to boost I/O on HDD we can disable access logs
access_log off;
# read and send using multi-threading, without blocking a worker process
aio threads;
# hide index pages
autoindex off;
# add to 'Content-Type' response header
charset utf-8;
# request timed out -- default 60
client_body_timeout 10;
# sets the maximum allowed size of the client request body -- default 1
client_max_body_size 16m;
# default mime type
default_type text/plain;
# enable gzipping of responses
gzip on;
# disables gzipping of responses for msie6 and below
gzip_disable "msie6";
# minimum length of a response that will be gzipped -- default 20
gzip_min_length 1024;
# gzip compression level -- default 1
gzip_comp_level 6;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
# text/html is always compressed
gzip_types
text/css
text/javascript
text/xml
text/x-component
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/atom+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
# files that will be used as an index, checked in the specified order
index index.php index.html index.htm index.txt;
# enables keep-alive connections with all browsers
keepalive_disable none;
# keep-alive client connections stay active for -- default 75
keepalive_timeout 30s;
# specifies log format
log_format main
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# disables logging of errors about not found files into error_log
log_not_found off;
# cache open file descriptors, directories and file lookup errors
open_file_cache max=10240 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# allow the server to close connection on non responding client, this will
# free up memory
reset_timedout_connection on;
# if client stops responding, free up memory -- default 60
send_timeout 8;
# copies data between one FD and other from within the kernel faster than
# read() + write()
sendfile on;
# bucket size for the server names hash tables
server_names_hash_bucket_size 128;
# disables emitting nginx version on error pages and in the "server"
# response header field
server_tokens off;
# send headers in one piece, it is better than sending them one by one
tcp_nopush on;
# don't buffer data sent, good for small data bursts in real time
tcp_nodelay on;
# hash table maximum size -- default 1024
types_hash_max_size 4096;

# include domain configuration files
include /nginx/vhosts.d/*.conf;

# redirect all non-encrypted (http) traffic to encrypted (https)
server {
server_name _;
listen *:80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
}

# vim: ft=nginx sts=4 sw=4 ts=4 noet :

Create a vhost defaults config.

# sudoedit /nginx/conf.d/vhost_defaults
 
filename: /nginx/conf.d/vhost_defaults
# $KYAULabs: vhost.defaults,v 1.0.7 2021/05/16 12:48:55 kyau Exp $

## SSL/TLS (https://cipherlist.dev/)
ssl_dhparam /nginx/ssl/dhparam4096.pem; # openssl dhparam -out dhparam4096.pem 4096
ssl_protocols TLSv1.3; # Requires nginx >= 1.13.0
ssl_ciphers EECDH+CHACHA20:EECDH+AES;
ssl_ecdh_curve X25519; # Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_session_timeout 10m;
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx >= 1.3.7
ssl_prefer_server_ciphers on;
resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=60s; # Change if you run your own DNS servers
resolver_timeout 2s;

# security settings
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

# vim: ft=nginx sts=4 sw=4 ts=4 noet :

Create a URI defaults config.

# sudoedit /nginx/conf.d/uri_defaults
filename: /nginx/conf.d/uri_defaults
# $KYAULabs: uri_defaults,v 1.0.2 2021/05/05 17:55:10 kyau Exp $

# remove `robots.txt` and all favicons from the logs
location ~* "^/(favicon\.\S{3,5}|robots\.txt)$" {
access_log off;
allow all;
log_not_found off;
}

# disable dot (hidden) files while allowing `.well-known`
location ~* /\.(?!well-known).* {
access_log off;
deny all;
log_not_found off;
}

# deny access to any sensitive material
location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|phps|psd|sass|scss|sh|sql|sw[op])|~)$ {
access_log on;
deny all;
log_not_found on;
}

# asset/media cache
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
access_log off;
expires 7d;
}

# fonts/svg cache and access
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
access_log off;
add_header Access-Control-Allow-Origin "*";
expires 7d;
}

# html processing
location ~* \.html$ {
try_files $uri $uri/ /index.html =404;
}

# http error pages
error_page 404 /404;
error_page 500 502 503 504 /50x;
location = /404 {
root /nginx/https/error;
index 404.php;
}
location = /50x {
root /nginx/https/error;
index 50x.php;
}

# vim: ft=nginx sts=4 sw=4 ts=4 noet :

FastCGI

Create a fastcgi_params config file (PHP environmental variable defaults).

# sudoedit /nginx/conf.d/fastcgi_params
 
filename: /nginx/conf.d/fastcgi_params
# $KYAULabs: fastcgi_params,v 1.0.5 2021/05/03 17:31:37 kyau Exp $

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
#fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param SERVER_SOFTWARE nginx;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

# Mitigate https://httpoxy.org/ vulnerabilities
fastcgi_param HTTP_PROXY "";

# vim: ft=nginx sts=4 sw=4 ts=4 noet :

SSL/TLS

Create the dhparam as indicated above.

# sudo -u http openssl dhparam -out /nginx/ssl/dhparam4096.pem 4096

Set permissions properly.

# sudo chmod 660 /nginx/ssl/dhparam4096.pem

Virtual Hosts

Virtual Hosts are created in domain config files keeping all subdomains in a single file. The following template can be used, simply replace domain_com and domain.com with the actual domain name.

# sudoedit /nginx/vhosts.d/domain_com.conf
 
filename: /nginx/vhosts.d/domain_com.conf
# $KYAULabs: domain_com.conf,v 1.1.5 2021/05/05 18:06:34 kyau Exp $

## Redirect all WWW to Non-WWW (SSL)
server {
listen *:443 ssl http2;
listen [::]:443 ssl http2;
server_name www.domain.com;

#access_log /nginx/logs/domain_com/www-access.log;
error_log /nginx/logs/domain_com/www-error.log;

ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;

include /nginx/conf.d/vhost_defaults;

return 301 https://domain.com$request_uri;
}

## domain.com
server {
listen *:443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com;

#access_log /nginx/logs/domain_com/domain_com-access.log;
error_log /nginx/logs/domain_com/domain_com-error.log;

ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;

include /nginx/conf.d/vhost_defaults;

root /nginx/https/domain_com/www;

location / {
try_files $uri $uri/ @rewrite;
}

location @rewrite {
#rewrite ^/directory/$ /script.php?x=directory last;
}

#location ^~ /app/ {
#proxy_pass http://127.0.0.1:8080/;
#proxy_http_version 1.1;
#proxy_set_header Connection "upgrade";
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header X-Forwarded-For $remote_addr;
#proxy_set_header X-Forwarded-Proto $scheme;
# by defaults nginx times out connections in one minute
#proxy_read_timeout 1d;
#}


include /nginx/conf.d/uri_defaults;

# php scripts
location ~* [^/]\.php(/|$) {
try_files $fastcgi_script_name =404;
include /nginx/conf.d/fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_pass unix:/run/php-fpm/php-fpm-domain_com.sock;
}
}

## api.domain.com
server {
listen *:443 ssl http2;
listen [::]:443 ssl http2;
server_name api.domain.com;

#access_log /nginx/logs/domain_com/api-access.log;
error_log /nginx/logs/domain_com/api-error.log;

ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/domain.com/chain.pem;

include /nginx/conf.d/vhost_defaults;

root /nginx/https/domain_com/api;

location / {
try_files $uri $uri/ @rewrite;
}

location @rewrite {
#rewrite ^/directory/$ /script.php?x=directory last;
}

include /nginx/conf.d/uri_defaults;

# php scripts
location ~* [^/]\.php(/|$) {
try_files $fastcgi_script_name =404;
include /nginx/conf.d/fastcgi_params;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_pass unix:/run/php-fpm/php-fpm-domain_com.sock;
}
}

# vim: ft=nginx sts=4 sw=4 ts=4 noet :