Instalasi Nginx-Wordpress pada Linux¶
Persiapan¶
- DB Server
- Web server
- PHP
Proses¶
Instalasi - Wordpress¶
Variabel
FQDN_INSTALASI="sub.domain.tld"
PENGGUNA="nginx"
SUREL_LE="[email protected]"
DIREKTORI_INSTALASI="/home/$FQDN_INSTALASI"
DIREKTORI_CACHE="$DIREKTORI_INSTALASI/cache"
DIREKTORI_CONFIG="$DIREKTORI_INSTALASI/config"
DIREKTORI_LOG="$DIREKTORI_INSTALASI/log"
DIREKTORI_TMP="$DIREKTORI_INSTALASI/tmp"
DIREKTORI_WWW="$DIREKTORI_INSTALASI/www"
DB_NAME="skema_pangkalan_data"
DB_USER="pengguna_pangkalan_data"
DB_PASSWORD="kata sandi kuat sekali luar biasa"
DB_HOST="10.20.30.40"
DIREKTORI_TUJUAN="/lokasi/direktori/tujuan"
LOKASI_PHP=$(which php)
Unduh & ekstrak Wordpress
wget https://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz
mkdir -v -p $DIREKTORI_WWW $DIREKTORI_CACHE $DIREKTORI_CONFIG $DIREKTORI_LOG $DIREKTORI_TMP
mv -v wordpress/* $DIREKTORI_WWW
rm -v -rf latest.tar.gz wordpress
SELinux - setiap folder yang baru dibuat
semanage fcontext -a -t httpd_cache_t "$DIREKTORI_CACHE(/.*)?"
semanage fcontext -a -t httpd_config_t "$DIREKTORI_CONFIG(/.*)?"
semanage fcontext -a -t httpd_log_t "$DIREKTORI_LOG(/.*)?"
semanage fcontext -a -t httpd_tmp_t "$DIREKTORI_TMP(/.*)?"
semanage fcontext -a -t httpd_sys_content_t "$DIREKTORI_WWW(/.*)?"
semanage fcontext -a -t httpd_sys_rw_content_t "$DIREKTORI_WWW/wp-content(/.*)?"
semanage fcontext -a -t httpd_sys_script_exec_t "$DIREKTORI_WWW/wp-includes(/.*)?"
systemctl restart nginx php-fpm
restorecon -rv $DIREKTORI_CACHE
restorecon -rv $DIREKTORI_CONFIG
restorecon -rv $DIREKTORI_LOG
restorecon -rv $DIREKTORI_TMP
restorecon -rv $DIREKTORI_WWW
restorecon -rv $DIREKTORI_WWW/wp-content
restorecon -rv $DIREKTORI_WWW/wp-includes
systemctl restart nginx php-fpm
Memastikan pengaturan hak akses folder dan berkas
chown -Rv $PENGGUNA: $DIREKTORI_INSTALASI
find $DIREKTORI_INSTALASI -type d -exec chmod -v 0755 {} \;
find $DIREKTORI_INSTALASI -type f -exec chmod -v 0644 {} \;
Konfigurasi - Diffie-Hellman¶
Membuat kunci Diffie-Hellman
openssl dhparam -out /etc/nginx/dhparam.pem 2048
mkdir -v -p /var/www/_letsencrypt
chown -Rv nginx /var/www/_letsencrypt
Konfigurasi - Nginx¶
Berkas-berkas *.conf
cp /etc/nginx/nginx.conf{,.`date +%Y%m%d%H%M`}
cat << EOF > /etc/nginx/nginx.conf
user nginx;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 65535;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
types_hash_max_size 2048;
client_max_body_size 16M;
# MIME
include mime.types;
default_type application/octet-stream;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
# Limits
limit_req_log_level warn;
limit_req_zone \$binary_remote_addr zone=login:10m rate=10r/m;
# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites
ssl_dhparam /etc/nginx/dhparam.pem;
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
# Load configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
EOF
cat << EOF > $DIREKTORI_CONFIG/$FQDN_INSTALASI.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name $FQDN_INSTALASI;
set \$base /home/$FQDN_INSTALASI;
root \$base/www;
# SSL
ssl_certificate /etc/letsencrypt/live/$FQDN_INSTALASI/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$FQDN_INSTALASI/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/$FQDN_INSTALASI/chain.pem;
# security
include $DIREKTORI_CONFIG/security.conf;
# logging
access_log $DIREKTORI_LOG/$FQDN_INSTALASI.access.log;
error_log $DIREKTORI_LOG/$FQDN_INSTALASI.error.log warn;
# index.php
index index.php;
# index.php fallback
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
# additional config
include $DIREKTORI_CONFIG/general.conf;
include $DIREKTORI_CONFIG/wordpress.conf;
# handle .php
location ~ \.php\$ {
include $DIREKTORI_CONFIG/php_fastcgi.conf;
}
}
# subdomains redirect
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name *.$FQDN_INSTALASI;
# SSL
ssl_certificate /etc/letsencrypt/live/$FQDN_INSTALASI/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$FQDN_INSTALASI/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/$FQDN_INSTALASI/chain.pem;
return 301 https://$FQDN_INSTALASI\$request_uri;
}
# HTTP redirect
server {
listen 80;
listen [::]:80;
server_name .$FQDN_INSTALASI;
include $DIREKTORI_CONFIG/letsencrypt.conf;
location / {
return 301 https://$FQDN_INSTALASI\$request_uri;
}
}
EOF
cat << EOF > $DIREKTORI_CONFIG/letsencrypt.conf
# ACME-challenge
location ^~ /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
}
EOF
cat << EOF > $DIREKTORI_CONFIG/security.conf
# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline' 'unsafe-eval' 'unsafe-hashes'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# . files
location ~ /\.(?!well-known) {
deny all;
}
EOF
cat << EOF > $DIREKTORI_CONFIG/general.conf
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
# robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}
# assets, media
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)$ {
expires 7d;
access_log off;
}
# svg, fonts
location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
add_header Access-Control-Allow-Origin "*";
expires 7d;
access_log off;
}
# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
EOF
cat << EOF > $DIREKTORI_CONFIG/wordpress.conf
# WordPress: allow TinyMCE
location = /wp-includes/js/tinymce/wp-tinymce.php {
include $DIREKTORI_CONFIG/php_fastcgi.conf;
}
# WordPress: deny wp-content, wp-includes php files
location ~* ^/(?:wp-content|wp-includes)/.*\.php$ {
deny all;
}
# WordPress: deny wp-content/uploads nasty stuff
location ~* ^/wp-content/uploads/.*\.(?:s?html?|php|js|swf)$ {
deny all;
}
# WordPress: SEO plugin
location ~* ^/wp-content/plugins/wordpress-seo(?:-premium)?/css/main-sitemap\.xsl$ {}
# WordPress: deny wp-content/plugins (except earlier rules)
location ~ ^/wp-content/plugins {
deny all;
}
# WordPress: deny scripts and styles concat
location ~* \/wp-admin\/load-(?:scripts|styles)\.php {
deny all;
}
# WordPress: deny general stuff
location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|wp-comments-post\.php|readme\.html|license\.txt)$ {
deny all;
}
# WordPress: throttle wp-login.php
location = /wp-login.php {
limit_req zone=login burst=2 nodelay;
include $DIREKTORI_CONFIG/php_fastcgi.conf;
}
# WordPress: Wordfence plugin
location ~ ^/\.user\.ini {
deny all;
}
# WordPress: iThemes Security plugin
location ~ ^/nginx\.conf {
deny all;
}
EOF
cat << EOF > $DIREKTORI_CONFIG/php_fastcgi.conf
# 404
try_files \$fastcgi_script_name =404;
# default fastcgi_params
include fastcgi_params;
# fastcgi settings
fastcgi_pass unix:/var/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
# fastcgi params
fastcgi_param DOCUMENT_ROOT \$realpath_root;
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
fastcgi_param PHP_ADMIN_VALUE "open_basedir=\$base/:/usr/lib/php/:/tmp/";
EOF
Konfigurasi - logrotate¶
Mengatur agar log $FQDN dapat otomatis melakukan rotasi dan pengarsipan harian
cat << EOF >> /etc/logrotate.d/nginx
$DIREKTORI_LOG/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 640 nginx adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
EOF
Instalasi - Certbot¶
Instalasi persyaratan & menonaktifkan sementara konfigurasi SSL
dnf install -y certbot python3-certbot-nginx
sed -i -r 's/(listen .*443)/\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g' \
$DIREKTORI_CONFIG/$FQDN_INSTALASI.conf
ln -v -s $DIREKTORI_CONFIG/$FQDN_INSTALASI.conf /etc/nginx/conf.d/
nginx -t && systemctl reload nginx
Mendapatkan SSL & mengaktifkan kembali konfigurasi SSL
Lakukan perubahan alamat IP sebelum menjalankan perintah untuk mendapatkan sertifikat SSL
certbot certonly --webroot -d $FQDN_INSTALASI --email $SUREL_LE -w /var/www/_letsencrypt \
-n --agree-tos --force-renewal
sed -i -r 's/#?;#//g' $DIREKTORI_CONFIG/$FQDN_INSTALASI.conf
nginx -t && systemctl reload nginx
Mengatur Certbot agar memuat ulang Nginx saat setelah sukses pembaruan SSL
echo -e '#!/bin/bash\nnginx -t && systemctl reload nginx' | \
tee /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh
chmod a+x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh
nginx -t && systemctl reload nginx
Penyelesaian¶
Sinkronisasi - pemindahan dokumen www root¶
rsync dari peladen lama
# Variabel
PENGGUNA_TUJUAN="deploy"
IP_TUJUAN="188.166.216.45"
KUNCI_PRIVAT_SSH="/home/deploy/.ssh/do-proxsis-wp07-iyodev-com"
DIREKTORI_SUMBER="/var/www/html"
DIREKTORI_TUJUAN="/home/$PENGGUNA_TUJUAN/iyodev"
# Sinkronisasi sumber-tujuan
chmod -v 400 $KUNCI_PRIVAT_SSH
rsync -avP \
-e "ssh -i $KUNCI_PRIVAT_SSH" \
$DIREKTORI_SUMBER \
$PENGGUNA_TUJUAN@$IP_TUJUAN:$DIREKTORI_TUJUAN
Menempatkan isi direktori peladen lama ke www root peladen baru
cp -av $DIREKTORI_WWW{,.orig}
rm -rfv $DIREKTORI_WWW/*
cp -aRv $DIREKTORI_TUJUAN/* $DIREKTORI_WWW
chown -Rv $PENGGUNA: $DIREKTORI_INSTALASI
SELinux - memastikan pemulihan aturan terhadap folder & berkas
restorecon -rv $DIREKTORI_CACHE
restorecon -rv $DIREKTORI_CONFIG
restorecon -rv $DIREKTORI_LOG
restorecon -rv $DIREKTORI_TMP
restorecon -rv $DIREKTORI_WWW
restorecon -rv $DIREKTORI_WWW/wp-content
restorecon -rv $DIREKTORI_WWW/wp-includes
systemctl restart nginx php-fpm
matchpathcon -V $DIREKTORI_CACHE/*
matchpathcon -V $DIREKTORI_CONFIG/*
matchpathcon -V $DIREKTORI_LOG/*
matchpathcon -V $DIREKTORI_TMP/*
matchpathcon -V $DIREKTORI_WWW/*
matchpathcon -V $DIREKTORI_WWW/wp-content/*
matchpathcon -V $DIREKTORI_WWW/wp-includes/*
ls -ladZ $DIREKTORI_INSTALASI/*
systemctl restart nginx php-fpm
Konfigurasi - koneksi peladen pangkalan data¶
wp-config.php
sed -i -r "s#^define\('DB_NAME.*#define('DB_NAME', '$DB_NAME');#g" $DIREKTORI_WWW/wp-config.php
sed -i -r "s#^define\('DB_USER.*#define('DB_USER', '$DB_NAME');#g" $DIREKTORI_WWW/wp-config.php
sed -i -r "s#^define\('DB_PASSWORD.*#define('DB_PASSWORD', '$DB_PASSWORD');#g" $DIREKTORI_WWW/wp-config.php
sed -i -r "s#^define\('DB_HOST.*#define('DB_HOST', '$DB_HOST:3306');#g" $DIREKTORI_WWW/wp-config.php
# sed -i -r "s#^define\('WP_TEMP_DIR.*#define\('WP_TEMP_DIR', '$DIREKTORI_TMP');#g" \
# $DIREKTORI_WWW/wp-config.php
# sed -i -r "s#^define\('SUCURI_DATA_STORAGE.*#define\('SUCURI_DATA_STORAGE', '$DIREKTORI_LOG');#g" \
# $DIREKTORI_WWW/wp-config.php
grep -E 'DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|WP_TEMP_DIR|SUCURI_DATA_STORAGE' $DIREKTORI_WWW/wp-config.php
mencadangkan pangkalan data
PANGKALAN_DATA="pangkalan_data"
mysqldump --single-transaction --routines --add-drop-table --extended-insert -u root -p \
$PANGKALAN_DATA > $PANGKALAN_DATA-`date +%Y%d%m%H%M`.sql
Konfigurasi - cron¶
crontab -e
cat << EOF > crontab.txt
0 * * * * su nginx -s /bin/sh -c "cd $DIREKTORI_WWW/; $LOKASI_PHP -q wp-cron.php"
EOF
crontab crontab.txt
Isu & kendala¶
SELinux - debug
tail -F /var/log/messages | grep -E 'setroubleshoot|preventing|denied'
grep -E 'setroubleshoot|preventing' /var/log/messages
sealert -a /var/log/audit/audit.log
semanage fcontext -l | grep -E 'httpd_|nginx|wordpress|php-fpm'
# gunakan sementara
semanage permissive -a httpd_t
# hapus jika sudah selesai
semanage permissive -d httpd_t
PHP - Penyesuaian nilai parameter untuk tema Wordpress
Variabel
PHP_VER="7.4"
MAX_EXECUTION_TIME="180"
MAX_INPUT_VARS="3000"
POST_MAX_SIZE="16M"
UPLOAD_MAX_FILESIZE="16M"
cp -v /etc/php.ini{,.`date +%Y%m%d%H%M`}
sed -i -r "s#^max_execution_time =.*#max_execution_time = $MAX_EXECUTION_TIME#g" /etc/php.ini
sed -i -r "s#^;max_input_vars =.*#max_input_vars = $MAX_INPUT_VARS#g" /etc/php.ini
sed -i -r "s#^post_max_size =.*#post_max_size = $POST_MAX_SIZE#g" /etc/php.ini
sed -i -r "s#^upload_max_filesize =.*#upload_max_filesize = $UPLOAD_MAX_FILESIZE#g" /etc/php.ini
INIPATH="/etc/php/$PHPVER/fpm/php.ini"
cp -v $INIPATH{,.`date +%Y%m%d%H%M`}
sed -i -r "s#^max_execution_time =.*#max_execution_time = $MAX_EXECUTION_TIME#g" $INIPATH
sed -i -r "s#^;max_input_vars =.*#MAX_INPUT_VARS = $ZONA_WAKTU#g" $INIPATH
sed -i -r "s#^post_max_size =.*#post_max_size = $POST_MAX_SIZE#g" $INIPATH
sed -i -r "s#^upload_max_filesize =.*#upload_max_filesize = $UPLOAD_MAX_FILESIZE#g" $INIPATH
WP-Admin - CSS & Javascript tidak termuat
sed -i "/define('WP_DEBUG/ a // `date +%Y-%m-%d\ %H.%M` [AL]: WP-Admin - CSS & Javascript tidak termuat\ndefine('CONCATENATE_SCRIPTS', false);" $DIREKTORI_WWW/wp-config.php
systemctl restart php-fpm
Wordpress - Direktori sementara
cat << EOF >> $DIREKTORI_WWW/wp-config.php
// `date +%Y-%m-%d\ %H.%M` [AL]: Lokasi direktori sementara
define('WP_TEMP_DIR', '$DIREKTORI_TMP');
EOF
systemctl restart php-fpm
Plugin - Sucuri
cat << EOF >> $DIREKTORI_WWW/wp-config.php
// `date +%Y-%m-%d\ %H.%M` [AL]: Lokasi log Sucuri
define('SUCURI_DATA_STORAGE', '$DIREKTORI_LOG');
EOF
touch $DIREKTORI_LOG/sucuri-{blockedusers,failedlogins,hookdata,oldfailedlogins,plugindata,trustip}.php
chown -Rv $PENGGUNA: $DIREKTORI_LOG
systemctl restart php-fpm
Plugin - EWWW Image Optimizer
ausearch -c 'jpegtran' --raw | audit2allow -M ewww && semodule -i ewww.pp
systemctl restart nginx php-fpm
Plugin - iTheme Security
ausearch -c 'php-fpm' --raw | audit2allow -M php-fpm && semodule -i php-fpm.pp
systemctl restart nginx php-fpm
Log rotate
ausearch -c 'logrotate' --raw | audit2allow -M logrotate && semodule -i logrotate.pp
systemctl restart nginx php-fpm