๐Ÿ”ง ์„œ๋ฒ„ ์œ„์— WordPress๋ฅผ ์ง์ ‘ ์˜ฌ๋ ค๋ดค๋‹ค: LEMP ์Šคํƒ ๊ตฌ์ถ•๊ธฐ

์ฒ˜์Œ์— ์ด ๋ฏธ์…˜์„ ๋ฐ›์•˜์„ ๋•Œ ์†”์งํžˆ ์ข€ ๋ง‰๋ง‰ํ–ˆ์–ด์š”. “LEMP ์Šคํƒ์œผ๋กœ WordPress ์„œ๋ฒ„๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด์„ธ์š””๋ผ๋‹ˆ. ์นดํŽ˜24์—์„œ ๋ฒ„ํŠผ ๋ช‡ ๋ฒˆ ํด๋ฆญํ•˜๋ฉด ๋˜๋Š” ๊ฑธ ์™œ ์ง์ ‘ ํ•˜๋ƒ๊ณ ์š”? ๊ทธ๋Ÿฐ๋ฐ ์ด ๊ณผ์ •์„ ์ง์ ‘ ํ•ด๋ณด๊ณ  ๋‚˜์„œ ์›น์‚ฌ์ดํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋Œ์•„๊ฐ€๋Š”์ง€๋ฅผ ๋น„๋กœ์†Œ ์ดํ•ดํ•˜๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ๊ทธ ์ „ ๊ณผ์ •์„ ๊ธฐ๋กํ•œ ๋”ฅ๋‹ค์ด๋ธŒ ์‹ค์Šต ํ›„๊ธฐ์ž…๋‹ˆ๋‹ค.


๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ๊ณผ ๋ชฉํ‘œ

์‹œ๋‚˜๋ฆฌ์˜ค: CEO๊ฐ€ ํšŒ์‚ฌ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ๋ฅผ WordPress๋กœ ๋งŒ๋“ค์–ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์‚ฐ ์ ˆ์•ฝ์„ ์œ„ํ•ด ์ง์ ‘ ๋ฆฌ๋ˆ…์Šค ์„œ๋ฒ„์— ๊ตฌ์ถ•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•œ ์ค„์ด ์ €๋ฅผ Nginx, PHP, MariaDB์˜ ์„ธ๊ณ„๋กœ ๋Œ์–ด๋“ค์˜€์Šต๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ์—ญํ• ๋น„์œ 
Linux (Ubuntu)์šด์˜์ฒด์ œ, ๋ชจ๋“  ๊ฒƒ์˜ ํ† ๋Œ€๋•…
Nginx (E)์›น ์„œ๋ฒ„, ์š”์ฒญ์„ ์ œ์ผ ๋จผ์ € ๋ฐ›์Œ์›จ์ดํ„ฐ
MariaDB (M)๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ๊ธ€/๋Œ“๊ธ€/ํšŒ์› ์ €์žฅ์‹์žฌ๋ฃŒ ์ฐฝ๊ณ 
PHP-FPM (P)๋™์  ์ฝ˜ํ…์ธ  ์ฒ˜๋ฆฌ ์—”์ง„์š”๋ฆฌ์‚ฌ

์ด ๋„ค ๊ฐ€์ง€ ์กฐํ•ฉ์ด ๋ฐ”๋กœ LEMP ์Šคํƒ์ž…๋‹ˆ๋‹ค. (LAMP์—์„œ Apache ๋Œ€์‹  Nginx๋ฅผ ์“ฐ๊ธฐ ๋•Œ๋ฌธ์— E)


Step 1: Nginx ์›น ์„œ๋ฒ„ ์„ค์ • ๐ŸŒ

์›น ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์ œ์ผ ๋จผ์ € ๋ฐ›๋Š” ๊ด€๋ฌธ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” minsooword.kro.kr ๋„๋ฉ”์ธ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ /var/www/wordpress ํด๋”๋กœ ์—ฐ๊ฒฐํ•ด ์ฃผ๋Š” ๊ฐ€์ƒ ํ˜ธ์ŠคํŠธ(Virtual Host) ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € nginx๊ฐ€ ์ž˜ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋‹ค์Œ, ์‚ฌ์ดํŠธ ์„ค์ • ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

server {
    listen 80;
    server_name minsooword.kro.kr www.minsooword.kro.kr;

    root /var/www/wordpress;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }
}

์„ค์ • ํŒŒ์ผ์„ ๋งŒ๋“ค์—ˆ์œผ๋ฉด sites-enabled์— ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ๊ฑธ์–ด์„œ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด default ์„ค์ •์€ ๊ผญ ํ•ด์ œํ•ด์•ผ ์ถฉ๋Œ์ด ๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
sudo unlink /etc/nginx/sites-enabled/default
sudo nginx -t        # ๋ฌธ๋ฒ• ๊ฒ€์‚ฌ
sudo systemctl reload nginx

Step 2: PHP-FPM ์„ค์น˜ โ€” ๋™์  ์ฝ˜ํ…์ธ ์˜ ํ•ต์‹ฌ โš™๏ธ

Nginx๋Š” ์ •์  ํŒŒ์ผ(HTML, ์ด๋ฏธ์ง€)์€ ์ž˜ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, PHP ์ฝ”๋“œ๋Š” ์ง์ ‘ ์‹คํ–‰ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฑธ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ฒŒ PHP-FPM์ž…๋‹ˆ๋‹ค.

PHP๋Š” ์„œ๋ฒ„์—์„œ ๋จผ์ € ์‹คํ–‰๋˜์–ด HTML๋กœ ๋ณ€ํ™˜๋œ ํ›„ ๋ธŒ๋ผ์šฐ์ €์— ์ „๋‹ฌ๋˜๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์–ธ์–ด์˜ˆ์š”. ๊ทธ๋ž˜์„œ ๋กœ๊ทธ์ธํ•œ ์‚ฌ๋žŒ๋งˆ๋‹ค ๋‹ค๋ฅธ ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ด๋Š” ๊ฒƒ ๊ฐ™์€ ๋™์  ๊ธฐ๋Šฅ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

Nginx ์„ค์ • ํŒŒ์ผ์— PHP ์ฒ˜๋ฆฌ ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
}

Step 3: MariaDB โ€” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ์ฐฝ๊ณ  ๐Ÿ—„๏ธ

WordPress๋Š” ๊ธ€, ๋Œ“๊ธ€, ํšŒ์› ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฒŒ MariaDB์ž…๋‹ˆ๋‹ค.

์„ค์น˜ ํ›„ ๋ณด์•ˆ ์„ค์ •์„ ๋ฐ˜๋“œ์‹œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ์„ค์ • ์‹œ ์„ ํƒ ๊ฐ€์ด๋“œ:

  • Switch to unix_socket authentication? โ†’ n (๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฐฉ์‹ ์‚ฌ์šฉ)
  • Remove anonymous users? โ†’ y
  • Disallow root login remotely? โ†’ y
  • Remove test database? โ†’ y

์ดํ›„ WordPress ์ „์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์‚ฌ์šฉ์ž๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8mb4;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY '๋น„๋ฐ€๋ฒˆํ˜ธ';
GRANT ALL ON wordpress.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;

Step 4: WordPress ์„ค์น˜ ๐ŸŽ‰

๋“œ๋””์–ด ๋ณธ๋ก ! WordPress ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์˜ฌ๋ฆฌ๊ณ  ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz
sudo cp -a /tmp/wordpress/. /var/www/wordpress/

๊ทธ๋‹ค์Œ wp-config.php ํŒŒ์ผ์— ์•„๊นŒ ๋งŒ๋“  DB ์ •๋ณด๋ฅผ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', '๋น„๋ฐ€๋ฒˆํ˜ธ' );

์ดํ›„ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://minsooword.kro.kr ์— ์ ‘์†ํ•˜๋ฉด WordPress ์„ค์น˜ ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ๋œน๋‹ˆ๋‹ค. ์–ธ์–ด ์„ ํƒํ•˜๊ณ  ๊ด€๋ฆฌ์ž ๊ณ„์ • ๋งŒ๋“ค๋ฉด ๋!


Step 5: SSL ์ธ์ฆ์„œ ์ ์šฉ โ€” HTTPS๋กœ ๋ณด์•ˆ ๊ฐ•ํ™” ๐Ÿ”’

HTTP๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์•„ ์ค‘๊ฐ„์— ๋ˆ„๊ตฐ๊ฐ€ ํ›”์ณ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. HTTPS๋Š” ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•ฉ๋‹ˆ๋‹ค.

๊ณต์ธ IP๊ฐ€ ์—†๋Š” ํ™˜๊ฒฝ์ด๋ผ Let’s Encrypt ๋Œ€์‹  OpenSSL ์‚ฌ์„ค ์ธ์ฆ์„œ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/nginx/ssl/wordpress.key \
  -out /etc/nginx/ssl/wordpress.crt

Nginx ์„ค์ •์— HTTPS๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , HTTP๋กœ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ์š”์ฒญ์€ HTTPS๋กœ ๊ฐ•์ œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ(301)ํ•ฉ๋‹ˆ๋‹ค.

server {
    listen 80;
    server_name minsooword.kro.kr;
    return 301 https://$host$request_uri;  # ์˜๊ตฌ ์ด์ „ ์ฒ˜๋ฆฌ
}

server {
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/wordpress.crt;
    ssl_certificate_key /etc/nginx/ssl/wordpress.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    # ... (๋‚˜๋จธ์ง€ ์„ค์ •)
}

Step 6: UFW + Fail2Ban โ€” ๋ฐฉ์–ด์„  ๊ตฌ์ถ• ๐Ÿ›ก๏ธ

SSL์ด ํ†ต์‹  ์•”ํ˜ธํ™”๋ผ๋ฉด, UFW์™€ Fail2Ban์€ ๋ฌธ ์ž ๊ทธ๊ธฐ์™€ ์นจ์ž…์ž ์žก๊ธฐ์ž…๋‹ˆ๋‹ค.

UFW (๋ฐฉํ™”๋ฒฝ): ํ—ˆ์šฉ๋œ ํฌํŠธ(22, 80, 443)๋งŒ ์—ด๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ „๋ถ€ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

Fail2Ban: ๋กœ๊ทธ ํŒŒ์ผ์„ ์‹ค์‹œ๊ฐ„ ๊ฐ์‹œํ•ด์„œ, ๋กœ๊ทธ์ธ์„ ๋ฐ˜๋ณต ์‹คํŒจํ•˜๋Š” ์ˆ˜์ƒํ•œ IP๋ฅผ ์ž๋™์œผ๋กœ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์ฐจ๋ณ„ ๋Œ€์ž… ๊ณต๊ฒฉ(Brute-force)์„ ๋ง‰๋Š” ํ•ต์‹ฌ ๋„๊ตฌ์˜ˆ์š”.

WordPress ์ „์šฉ ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ /wp-login.php๋‚˜ /xmlrpc.php์— ๋ฐ˜๋ณต ์ ‘๊ทผํ•˜๋Š” IP๋ฅผ ์žก์Šต๋‹ˆ๋‹ค.

# wp-login.php์— POST๋กœ ๋ฐ˜๋ณต ์‹œ๋„ํ•˜๋Š” IP๋ฅผ ๊ณต๊ฒฉ์œผ๋กœ ํŒ๋‹จ
failregex = ^<HOST> .* "POST /wp-login\.php
             ^<HOST> .* "POST /xmlrpc\.php

๐Ÿ ์ตœ์ข… ์ ๊ฒ€: ๋ชจ๋“  ๊ฒŒ ์ œ๋Œ€๋กœ ๋Œ์•„๊ฐ€๋Š”์ง€ ํ™•์ธ

# ํ•œ ๋ฐฉ์— ์ „์ฒด ์ƒํƒœ ํ™•์ธํ•˜๊ธฐ
systemctl is-active nginx      # Nginx ์‹คํ–‰ ์ค‘?
systemctl is-active php8.1-fpm # PHP-FPM ์‹คํ–‰ ์ค‘?
systemctl is-active mariadb    # MariaDB ์‹คํ–‰ ์ค‘?
systemctl is-active fail2ban   # Fail2Ban ์‹คํ–‰ ์ค‘?
curl -k -s -o /dev/null -w "%{http_code}" https://minsooword.kro.kr  # 200์ด๋ฉด OK

๐Ÿ’ก ๋ฏธ์…˜์„ ๋งˆ์น˜๋ฉฐ

๋‹จ์ˆœํžˆ ์„ค์น˜ ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ๋๋‚ด์ง€ ์•Š๊ณ  ์ง์ ‘ ํ•ด๋ณด๋‹ˆ, ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์ด ์–ด๋–ค ๊ฒฝ๋กœ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€ ๋ชธ์œผ๋กœ ์ดํ•ดํ•˜๊ฒŒ ๋์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์š”์ฒญ โ†’ Nginx(์›น ์„œ๋ฒ„) โ†’ PHP-FPM(์•ฑ ์„œ๋ฒ„) โ†’ MariaDB(DB) โ†’ ํ™”๋ฉด ์ถœ๋ ฅ

์ด ํ๋ฆ„ ํ•˜๋‚˜๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋ฉด, ์•ž์œผ๋กœ ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒจ๋„ ์–ด๋””๋ฅผ ๋œฏ์–ด๋ด์•ผ ํ• ์ง€ ์•Œ ์ˆ˜ ์žˆ์–ด์š”. ์ฒ˜์Œ์—” ๋ง‰๋ง‰ํ–ˆ์ง€๋งŒ, ๋๋‚˜๊ณ  ๋‚˜๋‹ˆ ๊ฐ€์žฅ ๋ฟŒ๋“ฏํ•œ ๋ฏธ์…˜์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๐ŸŽ‰