Install nginx with ngx_pagespeed, Letsencrypt, PHP8-FPM, MariaDB on Ubuntu 20.04 LTS

1. Update / upgrade existing packages

apt-get update && apt-get dist-upgradeCode language: JavaScript (javascript)

2. Install nginx(libressl)+PageSpeed

apt-get install libxml2-dev libxslt1-dev libgd-dev libatomic-ops-dev libperl-dev
cd /usr/local/src
wget https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.3.1.tar.gz && tar xzvf libressl-3.3.1.tar.gz
bash <(curl -f -L -sS https://ngxpagespeed.com/install) --nginx-version latestCode language: PHP (php)

중간에 요구하는 nginx 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 --with-threads --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_v2_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-libatomic --with-cc-opt='-O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -flto=auto -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat --with-openssl=/usr/local/src/libressl-3.3.1/Code language: JavaScript (javascript)

nginx module 디렉토리를 /etc/nginx/modules로 symlink해 준다.

sudo ln -s /usr/lib/nginx/modules /etc/nginx/modules

/lib/systemd/system/nginx.service에 init script를 작성한다. 참조

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.targetCode language: JavaScript (javascript)

가상호스트 설정파일을 넣을 디렉토리들을 생성해준다.

mkdir /etc/nginx/sites-enabled /etc/nginx/sites-available

/etc/nginx/nginx.conf를 nginx.conf.old로 이름 변경하고, nginx.conf를 아래와 같이 재작성한다.

user www-data;
worker_processes auto;
worker_rlimit_nofile 102400;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 10240;
    use epoll;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    server_tokens off;
    sendfile on;
    keepalive_timeout 65;
    client_max_body_size 100M;
    client_body_buffer_size 1M;

    include /etc/nginx/sites-enabled/*.conf;
}Code language: PHP (php)

/etc/nginx/sites-available/{}.conf 아래에는 대략적으로 이러한 내용의 설정이 작성될 수 있다. 아예 http는 배제하고 https로 전부 리다이렉트 설정한 모습이다. 주석 처리한 것은 필요시 해제하여 적절하게 사용할 수 있다.

server {
    include listen.conf;
    server_name your.domain;
    rewrite ^(.*)$ https://your.domain$request_uri;
}

server {
    include listen_ssl.conf;
    index index.php;
    root /home/test/www;
    server_name test.ghkim.net;

    #access_log /var/log/nginx/your.domain.access.log main buffer=512k flush=20s;
    #error_log /var/log/nginx/your.domain.error.log;

    ssl_certificate /somewhere/to/fullchain.pem;
    ssl_certificate_key /somewhere/to/privkey.pem;
    ssl_dhparam /etc/nginx/dh.param;
    include ssl.conf;

    include location_default.conf;

    include pagespeed.conf;
    include security.conf;
    include expires.conf;
    #include php.conf;
}Code language: PHP (php)

/etc/nginx/listen.conf는 아래와 같은 내용으로 저장한다.

listen 80;
listen [::]:80;Code language: CSS (css)

/etc/nginx/listen_ssl.conf는 아래와 같은 내용으로 저장한다. 현재 http/3(QUIC)이 논의되고 있지만 아직 초안 단계로, 현재로서는 http/2만 적용하도록 한다.

listen 443 ssl http2;
listen [::]:443 ssl http2;Code language: CSS (css)

ssl 보안 강화를 위하여 dhparam을 만들어 준다.

openssl dhparam -out /etc/nginx/dh.param 4096

/etc/nginx/ssl.conf에는 아래와 같은 내용으로 저장한다. 현재 nginx의 TLSv1.3은 openssl 1.1.1 이상을 사용한 경우에만 지원된다(Libressl은 지원하지만 nginx에서 반영해야 함, 참조).

resolver 8.8.8.8 8.8.4.4 valid=300s;

ssl_protocols TLSv1.2;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";

ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_ecdh_curve secp384r1;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;Code language: JavaScript (javascript)

/etc/nginx/expires.conf는 아래와 같이 저장한다.

location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1M;
    add_header Pragma public;
    add_header Cache-Control "public, max-age=2592000";
}Code language: PHP (php)

/etc/nginx/location_default.conf는 아래와 같이 저장한다.

location / {
    try_files $uri $uri/ =404;
}Code language: PHP (php)

/etc/nginx/pagespeed.conf에는 아래와 같은 내용으로 저장한다. 참조

pagespeed on;

# Needs to exist and be writable by nginx.  Use tmpfs for best performance.
pagespeed FileCachePath /var/ngx_pagespeed_cache;

# Ensure requests for pagespeed optimized resources go to the pagespeed handler
# and no extraneous headers get set.
location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
  add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }Code language: PHP (php)

/etc/nginx/security.conf에는 아래와 같은 내용으로 저장한다. 원본은 어디서 주워온 것인데 출처가 기억나지 않는다. modsecurity는 몇몇 이유로 이번에는 설정하지 않는다.

## Block SQL injections
set $block_sql_injections 0;
if ($query_string ~ "union.*select.*\(") {
set $block_sql_injections 1;
}
if ($query_string ~ "union.*all.*select.*") {
set $block_sql_injections 1;
}
if ($query_string ~ "concat.*\(") {
set $block_sql_injections 1;
}
if ($block_sql_injections = 1) {
return 403;
}

## Block file injections
set $block_file_injections 0;
if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") {
set $block_file_injections 1;
}
if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") {
set $block_file_injections 1;
}
if ($block_file_injections = 1) {
return 403;
}

## Block common exploits
set $block_common_exploits 0;
if ($query_string ~ "(<|%3C).*script.*(>|%3E)") {
set $block_common_exploits 1;
}
if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") {
set $block_common_exploits 1;
}
if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") {
set $block_common_exploits 1;
}
if ($query_string ~ "proc/self/environ") {
set $block_common_exploits 1;
}
if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") {
set $block_common_exploits 1;
}
if ($query_string ~ "base64_(en|de)code\(.*\)") {
set $block_common_exploits 1;
}
if ($block_common_exploits = 1) {
return 403;
}

## Block spam
set $block_spam 0;
if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b") {
set $block_spam 1;
}
if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b") {
set $block_spam 1;
}
if ($query_string ~ "\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b") {
set $block_spam 1;
}
if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b") {
set $block_spam 1;
}
if ($block_spam = 1) {
return 403;
}

## Block user agents
set $block_user_agents 0;

# Don't disable wget if you need it to run cron jobs!
if ($http_user_agent ~ "Wget") {
set $block_user_agents 1;
}

# Disable Akeeba Remote Control 2.5 and earlier
if ($http_user_agent ~ "Indy Library") {
set $block_user_agents 1;
}

# Common bandwidth hoggers and hacking tools.
if ($http_user_agent ~ "libwww-perl") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "GetRight") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "GetWeb!") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Go!Zilla") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Download Demon") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Go-Ahead-Got-It") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "TurnitinBot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "GrabNet") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "zgrab") {
set $block_user_agents 1;
}

if ($block_user_agents = 1) {
return 403;
}Code language: PHP (php)

/etc/nginx/php.conf는 아래와 같이 저장한다.

location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    try_files $fastcgi_script_name =404;
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;
    fastcgi_index index.php;
    include fastcgi.conf;
    fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;

    fastcgi_buffer_size 128k;
    fastcgi_buffers 256 16k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_send_timeout 240;
    fastcgi_read_timeout 240;
    fastcgi_intercept_errors on;
}Code language: PHP (php)

nginx service를 부팅시 자동 시작하도록 등록하고, 필요시 실행한다(다만, 4번의 Let’s encrypt까지 작업을 모두 완료해야 실행될 것임)

systemctl enable nginx.service
systemctl start nginx.serviceCode language: CSS (css)

3. Install PHP8-FPM

추가 설치한 각종 라이브러리는 필요에 따라 조정하여 사용한다.

sudo add-apt-repository ppa:ondrej/php
sudo apt-get install php8.0-fpm php8.0-gd php8.0-curl php8.0-mbstring php8.0-apcu php8.0-intl php8.0-zip php8.0-mysql php8.0-xml php8.0-imagickCode language: JavaScript (javascript)

아래의 php.ini 설정을 변경한다.

upload_max_filesize = 100M
allow_url_fopen = Off
post_max_size = 100M

/etc/php/8.0/fpm/conf.d/10-opcache.ini에서 필요에 따라 아래 구문을 추가한다. 1235, 1254, 1255 중 가능한 것을 사용한다. 메모리 문제로 대형 어플리케이션의 경우 설정을 낮춰야 한다. 자세한 설명은 참조. 기본은 tracing(1254)이다.

opcache.jit=1255
opcache.jit_buffer_size=100M

이후 php8.0-fpm을 실행하고 부팅시 자동 시작하도록 등록한다.

sudo systemctl start php8.0-fpm
sudo systemctl enable php8.0-fpmCode language: CSS (css)

4. Install Let’s Encrypt

sudo snap install core; sudo snap refresh core
sudo apt-get remove certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot certonly --nginxCode language: JavaScript (javascript)

이후 적절한 정보를 입력하고 인증서를 받는다. 이후 nginx 설정을 변경하여 ssl 인증서 위치를 정확히 기입해 준다. 그다음 automatic renewal이 잘 되는지 테스트해 보자.

sudo certbot renew --dry-run

5. Install MariaDB 10.5

현재 Ubuntu 20.04 LTS의 기본 제공 패키지는 10.3버전으로, 10.5 버전을 이용하려면 아래와 같이 하면 된다.

sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] https://ftp.harukasan.org/mariadb/repo/10.5/ubuntu focal main'
sudo apt update
sudo apt install mariadb-server mariadb-client
sudo mysql_secure_installationCode language: JavaScript (javascript)


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *