20 KiB
title | description | date | lastmod | draft | noindex | nav_weight | series | categories | tags | images | authors | ||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
My Nginx Setup Kickstart / Boilerplate | Settingan wajib saya untuk Nginx sebagai web server, reverse proxy; termasuk VTS module, analisis, dan logging. | 2024-04-25T00:00:09+07:00 | false | false | 1000 |
|
|
|
Sejak pertama kali saya menggunakan Nginx di pertengahan tahun 2011 lalu, Nginx langsung menjadi web server favorit saya. Apache yang sebelumnya merupakan "standard" web server di sistem operasi Linux sedikit demi sedikit mulai saya tinggalkan.
Seiring berjalannya waktu, beberapa web server baru mulai bermunculan, seperti Caddy dan Traefik. Sebagai seorang system administrator, tentu saja saya pernah mencoba menggunakannya, meskipun hanya sampai batas di penggunaan projek pribadi.
Namun, hati saya sepertinya selalu kembali ke Nginx. Aplikasi, service, atau apapun itu yang bisa saya expose melalui Nginx, akan saya expose menggunakan Nginx. Mungkin karena saya sudah terlalu nyaman dengan konfigurasi dan pengalaman menyenangkan bersama Nginx. XD
My use case
Karena saya memiliki IPv4 yang sangat terbatas, saya banyak menggunakan Nginx sebagai reverse proxy untuk service-service yang tidak memiliki IP publik (VM dengan jaringan lokal / internal). Hal ini sangat membantu menghemat alokasi IP publik. Di kasus ini, saya banyak bermain dengan proxy_cache
dan http upstream
untuk mengimplementasikan load balancing ataupun failover.
Ketika saya masih sering membuat program menggunakan PHP, saya menggunakan Nginx dan PHP-FPM tanpa adanya Apache (.htaccess
) dibelakangnya. Jadi saya sering bermain dengan Nginx rewrite
dan fastcgi_cache
. Saat saya mulai membuat aplikasi menggunakan Rust dan Go, Nginx selalu bertugas sebagai reverse proxy sekaligus melakukan SSL termination.
Selain HTTP reverse proxy, saya kadang menggunakan module Nginx stream
untuk TCP, UDP, bahkan Unix socket data stream.
Mengenai monitoring traffic, saya selalu menggunakan Nginx VTS module. Sudah tersedia nginx-vts-exporter untuk [Prometheus](https://prometheus .io/) yang sangat mudah dioperasikan untuk memproses data dari Nginx VTS module. Sedangkan untuk logging, beberapa log untuk virtual host yang saya nilai krusial dikirimkan secara real-time ke remote syslog server.
Sempurna sudah, semua fitur yang saya butuhkan terpenuhi oleh Nginx. Dan saatnya saya mulai mendokumentasikan proses instalasi dan konfigurasi untuk memenuhi apa yang saya butuhkan diatas.
{{< bs/alert info >}} {{< bs/alert-heading "INFO:" >}} Saya memiliki open-source project {{< bs/alert-link "nginx-kickstart" "https://github.com/ditatompel/nginx-kickstart" >}} (boilerplate) untuk mempermudah menginstall Nginx dari repositori officialnya dan mengkompile Nginx VTS module di FRESH Debian 12 atau Ubuntu 22.04 server. {{< /bs/alert >}}
Installasi Nginx (Official Repo)
Dokumentasi ini dibuat untuk Debian 12 dan Ubuntu 22.04, dan saya menggunakan official repositori dari Nginx, bukan repositori bawaan dari distro.
Pertama dan utama, selalu pastikan sistem dalam keadaan up-to-date dengan menjalankan perintah sudo aptget update && sudo apt-get dist-upgrade
. Kemudian install package-package yang dibutuhkan untuk installasi Nginx.
Untuk Debian:
apt install sudo curl gnupg2 ca-certificates lsb-release debian-archive-keyring
Untuk Ubuntu:
apt install sudo curl gnupg2 ca-certificates lsb-release ubuntu-keyring
Lalu import official signing key-nya Nginx:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Tambahkan Nginx stable package ke apt source list repositori kita:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
Prioritaskan official Nginx package:
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
| sudo tee /etc/apt/preferences.d/99nginx
Kemudian, install nginx
dan nginx-module-geoip
dengan menjalankan perintah:
sudo apt update && sudo apt install nginx nginx-module-geoip
Load http_geoip_module
dan stream_geoip_module
, letakan load_module
diatas event{}
block dan geoip_country
didalam http{}
block:
load_module modules/ngx_http_geoip_module.so;
load_module modules/ngx_stream_geoip_module.so;
event {
worker_connections 65535; # Nginx default: 1024
}
http {
geoip_country /usr/share/GeoIP/GeoIP.dat;
# ...
}
Mempersiapkan struktur direktori Nginx
Buat direktori sites-available
, sites-enabled
, ssl
, snippets
di dalam direktori /etc/nginx
dengan menjalankan perintah:
sudo mkdir -p /etc/nginx/{sites-available,sites-enabled,ssl,snippets}
Buat self-signed certificate (hanya digunakan sebagai konfigurasi awal yang nantinya digantikan oleh certbot
):
sudo openssl req -x509 -newkey rsa:4096 -days 365 -nodes \
-keyout /etc/nginx/ssl/privkey.pem \
-out /etc/nginx/ssl/fullchain.pem \
-subj '/CN=example.local/O=My Organization/C=US'
Buat DH-param dengan menjalankan perintah:
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
Cloudflare IP Trusted Proxy
Jika ada virtual host yang berada dibalik Cloudflare reverse proxy, sangat disarankan untuk menambahkan IP Cloudflare ke trusted proxy di konfigurasi Nginx.
Buat executable shell script /etc/nginx/cloudflare-ips.sh
berikut:
#!/usr/bin/env bash
# Nginx setup for cloudflare's IPs.
# https://github.com/ditatompel/nginx-kickstart/blob/main/etc/nginx/cloudflare-ips.sh
# This is modified version of itsjfx's cloudflare-nginx-ips
# Ref of original script:
# https://github.com/itsjfx/cloudflare-nginx-ips/blob/master/cloudflare-ips.sh
set -e
[ "$(id -u)" -ne 0 ] && echo "This script must be run as root" && exit 1
CF_REAL_IPS_PATH=/etc/nginx/snippets/cloudflare_real_ips.conf
CF_WHITELIST_PATH=/etc/nginx/snippets/cloudflare_whitelist.conf
CF_GEOIP_PROXY_PATH=/etc/nginx/snippets/cloudflare_geoip_proxy.conf
for file in $CF_REAL_IPS_PATH $CF_WHITELIST_PATH $CF_GEOIP_PROXY_PATH; do
echo "# https://www.cloudflare.com/ips" > $file
echo "# Generated at $(LC_ALL=C date)" >> $file
done
echo "geo \$realip_remote_addr \$cloudflare_ip {
default 0;" >> $CF_WHITELIST_PATH
for type in v4 v6; do
for ip in `curl -sL https://www.cloudflare.com/ips-$type`; do
echo "set_real_ip_from $ip;" >> $CF_REAL_IPS_PATH;
echo " $ip 1;" >> $CF_WHITELIST_PATH;
echo "geoip_proxy $ip;" >> $CF_GEOIP_PROXY_PATH;
done
done
echo "}
# if your vhost is behind CloudFlare proxy and you want your site only
# accessible from Cloudflare proxy, add this in your server{} block:
# if (\$cloudflare_ip != 1) {
# return 403;
# }" >> $CF_WHITELIST_PATH
nginx -t && systemctl reload nginx
# vim: set ts=4 sw=4 et:
Shell script diatas akan mendownload list IP milik Cloudflare untuk diproses dan disimpan di /etc/nginx/snippets/cloudflare_*.conf
. Silahkan buat cronjob
untuk menjalankan script tersebut secara berkala (per minggu / per bulan).
Untuk konfigurasi Nginx-nya, tambahkan konfigurasi berikut ke dalam http{}
block di /etc/nginx/nginx.conf
:
http {
# ...
# Cloudflare IPs
################
include /etc/nginx/snippets/cloudflare_real_ips.conf;
real_ip_header X-Forwarded-For; # atau CF-Connecting-IP jika menggunakan Cloudflare
# cloudflare map
include /etc/nginx/snippets/cloudflare_whitelist.conf;
# ...
Logging
Fitur logging dapat memperlambat kinerja server (terutama karena DISK I/O yang tinggi) di situs dengan traffic yang tinggi. Namun logging juga sangat penting untuk memonitoring dan menganalisa aktifitas server.
Log Format
Ada beberapa log format yang umum digunakan dan dapat diintegrasikan dengan aplikasi 3rd-party, misalnya format (V)COMMON
atau (V)COMBINED
.
VCOMBINED format
Tambahkan konfigurasi berikut ke dalam http{}
block:
http {
# ...
# VCOMBINED log format style
log_format vcombined '$host:$server_port '
'$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# ...
Saya biasanya menggunakan log format
VCOMBINED
yang kemudian saya integrasikan dengan GoAccess.
Custom JSON log
Untuk beberapa kasus, saya menggunakan Nginx integration di Grafana Cloud yang menggunakan custom access log format (JSON):
http {
# ...
# JSON style log format
log_format json_analytics escape=json '{'
'"msec": "$msec", ' # request unixtime in seconds with a milliseconds resolution
'"connection": "$connection", ' # connection serial number
'"connection_requests": "$connection_requests", ' # number of requests made in connection
'"pid": "$pid", ' # process pid
'"request_id": "$request_id", ' # the unique request id
'"request_length": "$request_length", ' # request length (including headers and body)
'"remote_addr": "$remote_addr", ' # client IP
'"remote_user": "$remote_user", ' # client HTTP username
'"remote_port": "$remote_port", ' # client port
'"time_local": "$time_local", '
'"time_iso8601": "$time_iso8601", ' # local time in the ISO 8601 standard format
'"request": "$request", ' # full path no arguments if the request
'"request_uri": "$request_uri", ' # full path and arguments if the request
'"args": "$args", ' # args
'"status": "$status", ' # response status code
'"body_bytes_sent": "$body_bytes_sent", ' # the number of body bytes exclude headers sent to a client
'"bytes_sent": "$bytes_sent", ' # the number of bytes sent to a client
'"http_referer": "$http_referer", ' # HTTP referer
'"http_user_agent": "$http_user_agent", ' # user agent
'"http_x_forwarded_for": "$http_x_forwarded_for", ' # http_x_forwarded_for
'"http_host": "$http_host", ' # the request Host: header
'"server_name": "$server_name", ' # the name of the vhost serving the request
'"request_time": "$request_time", ' # request processing time in seconds with msec resolution
'"upstream": "$upstream_addr", ' # upstream backend server for proxied requests
'"upstream_connect_time": "$upstream_connect_time", ' # upstream handshake time incl. TLS
'"upstream_header_time": "$upstream_header_time", ' # time spent receiving upstream headers
'"upstream_response_time": "$upstream_response_time", ' # time spent receiving upstream body
'"upstream_response_length": "$upstream_response_length", ' # upstream response length
'"upstream_cache_status": "$upstream_cache_status", ' # cache HIT/MISS where applicable
'"ssl_protocol": "$ssl_protocol", ' # TLS protocol
'"ssl_cipher": "$ssl_cipher", ' # TLS cipher
'"scheme": "$scheme", ' # http or https
'"request_method": "$request_method", ' # request method
'"server_protocol": "$server_protocol", ' # request protocol, like HTTP/1.1 or HTTP/2.0
'"pipe": "$pipe", ' # "p" if request was pipelined, "." otherwise
'"gzip_ratio": "$gzip_ratio", '
'"geoip_country_code": "$geoip_country_code"'
'}';
# ...
}
Conditional (dynamic) logging
Dengan map
, dan if
keyword, kita dapat menentukan apa saya yang akan di-log dan apa yang tidak. Misalnya, saya tidak melakukan logging jika URI ada kata "local" atau User Agent mengandung kata "Uptime-Kuma":
http {
# ...
map $request_uri$http_user_agent $is_loggable {
~*local 0;
~*Uptime-Kuma.* 0;
default 1;
}
access_log /var/log/nginx/access-vcombined.log vcombined if=$is_loggable;
# ...
}
Remote Log UDP (rsyslog)
Bagi saya, sentraliasi log sangat mempermudah pekerjaan saya dalam melakukan analisa dan troubleshooting server.
Di Nginx, kita dapat dengan mudah mengirimkan log ke remote server secara real-time. Misalnya, kita dapat mengirimkan log ke remote rsyslog
server (UDP) dengan contoh konfigurasi berikut:
http {
# ...
access_log syslog:server=192.168.0.7:514,facility=local7,tag=nginx,severity=info vcombined if=$is_loggable;
access_log syslog:server=192.168.0.7:514,facility=local7,tag=nginx_grafana,severity=info json_analytics if=$is_loggable;
# ...
}
Compile Nginx VTS Module
Nginx VTS module tidak tersedia di Official Nginx repositori, sehingga kita tidak dapat menginstallnya menggunakan apt
. Untuk mengkompile VTS module memerlukan C
compiler, git
, libpcre
, libssl
, dan zlib
. Install package yang dibutuhkan tersebut dengan menjalankan perintah:
sudo apt install git build-essential libpcre3-dev zlib1g-dev libssl-dev
Ini adalah bagian yang sangat penting, jika ingin menggunakan dynamically linked module, opsi mengkompile module harus sama dengan Nginx binary file yang akan digunakan, begitu pula dengan versi Nginx yang digunakan. Untuk mengetahui informasi yang kita butuhkan tersebut, jalankan perintah nginx -V
. Contoh output:
nginx version: nginx/1.26.0
built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
built with OpenSSL 3.0.2 15 Mar 2022
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -ffile-prefix-map=/data/builder/debuild/nginx-1.26.0/debian/debuild-base/nginx-1.26.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
Download Nginx source dengan versi yang sama persis dengan yang sedang kita gunakan, dalam contoh ini 1.26.0
.
curl -O https://nginx.org/download/nginx-1.26.0.tar.gz
Lalu extract arsip Nginx source code tersebut, kemudian masuk ke direktori didalamnya:
tar -xvzf nginx-1.26.0.tar.gz
cd nginx-1.26.0
Kemudian, clone repositori vozlt/nginx-module-vts
dan gunakan rilis tag terakhir. Saat artikel ini dibuat, rilis tag terakhir adalah v0.2.2
, maka:
git clone -b v0.2.2 https://github.com/vozlt/nginx-module-vts.git
Configure dengan argumen yang sama dari output nginx -V
diatas dan tambahkan --add-dynamic-module=./nginx-module-vts/
. Contoh di artikel ini:
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -ffile-prefix-map=/data/builder/debuild/nginx-1.26.0/debian/debuild-base/nginx-1.26.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' -add-dynamic-module=./nginx-module-vts/
Build, kemudian copy VTS module yang baru saja dicompile ke /etc/nginx/modules/
:
make -j$(nproc)
sudo cp objs/ngx_http_vhost_traffic_status_module.so /etc/nginx/modules/
Konfigurasi Nginx VTS Module
Edit file /etc/nginx/nginx.conf
dan load host_traffic_status_module
berikut diatas event{}
block:
load_module modules/ngx_http_vhost_traffic_status_module.so;
Kemudian didalam http{}
block, tambahkan konfigurasi berikut:
http {
# ...
geoip_country /usr/share/GeoIP/GeoIP.dat;
vhost_traffic_status_zone;
vhost_traffic_status_filter_by_set_key $geoip_country_code country::*;
# ...
}
Untuk menampilkan halaman VTS traffic status, tambahkan contoh konfigurasi berikut ke server{}
block (misalnya di /etc/nginx/conf.d/default.conf
):
server {
# ...
# contoh konfigurasi untuk menampilkan halaman Nginx VTS status
location /status {
vhost_traffic_status_bypass_limit on;
vhost_traffic_status_bypass_stats on;
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
access_log off;
# contoh membatasi akses ke URI dari IP tertentu
allow 127.0.0.1;
allow 192.168.0.0/24;
deny all;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# ...
}
Konfigurasi Akhir
Sebagai referensi konfigurasi akhir, silahkan lihat di repositori https://github.com/ditatompel/nginx-kickstart/tree/main/etc/nginx.