Nếu bàn là một admin server và bạn có một web server mà bạn chọn như Apache hoặc Nginx. Apache là một web server rất nổi tiếng có từ những năm 1990s. Còn Nginx được phát triển sau này vào năm 2004, tuy nhiên Nginx phát triển rất nhanh và trở thành một web server rất mạnh mẽ, tốc độ xử lý rất nhanh cho những file html, và những file tỉnh khác.
Cả 2 Apache và Nginx đề hỗ trợ virtua hosting, có nghĩa là bạn có thể host rất nhiều website hoặc những ứng dụng web trên cùng một máy chủ. Tuy nhiên, bạn gặp phải tình huống là bạn có một website đang chạy trên web server này nhưng một ứng dụng web khác lại đòi hỏi phải chạy trên loại web server khác trên cùng một máy chủ. Port 80 và 443 đại điện trên địa chỉ IP Public và chỉ có thể được sử dụng cho một process. Nếu Apache đang sử dụng port này thì Nginx không thể sử dụng được nữa. Vì thể chúng ta phải làm gì trong tình huống này?
Bạn có thể cấu hình Nginx như là một reverse proxy cho Apache, Nginx có thể redirect nhưng request HTTP tới Apache. Theo kinh nghiệm của mình, tôi thấy rằng đây không phải lúc nào cũng là cách tốt nhất vì nó đã từng gây ra các vấn đề kỳ lạ mà tôi không thể khắc phục. Thay vì đó tôi sẽ sử dụng HAProxy để làm reverse proxy cho cả hai Apache và Nginx. HAProxy miễn phí, Open source là một Load Balancer có độ sẵn sàng cao và là một proxy server cho những ứng dụng TCP và những ứng dụng trên nền HTTP.
Cấu hình Apache, Nginx và HAProxy trên cùng máy chủ
Bên dưới là một số thông tin chuẩn bị để cấu hình.
- Nginx lắng nghe ở 127.0.0.1:80 và 127.0.0.1:443
- Apache lắng nghe 127.0.0.2:80 và 127.0.0.2:443
- HAProxy lắng nghe port 80 và 443 trên địa chỉ IP Public. Nginx chuyển hướng kết nối HTTP ở port 80 thành port 443. Khi một kết nối đến port 443, nó sẽ chọn giữa Nginx và Apache dựa vào SNI (Server Name Indication) header trong kết nối HTTPS.
Thực tế, Cloudflare nhà cung cấp dịch vụ CDN cũng sử dụng SNI header để xác định làm sao route kết nối HTTPS tới máy chủ web.
Bước 1: Tạm dừng dịch vụ Nginx và Apache
Để dừng dịch vụ Nginx trên Debian, Ubuntu và CentOS, chạy lệnh như bên dưới.
sudo systemctl stop nginx
Đê dừng dịch vụ Apache trên Debian/Ubuntu, chạy lện bên dưới.
sudo systemctl stop apache2
Để dừng dịch vụ Apache trên CentOS, chạy lệnh.
sudo systemctl stop httpd
Bước 2: Thay đổi port lắng nghe trên Nginx
Chúng ta cần thay đổi port Nginx lắng nghe ở địa chỉ 127.0.0.1:80. Mở file cấu hình Nginx ở thư mục /etc/nginx/conf.d/
hoặc /etc/nginx/sites-enabled/
listen 80;
Thay đổi thành
listen 127.0.0.1:80;
Nếu port https (443) được mở trên Nginx, bạn cũng phải thay đổi như bên dưới, tìm:
listen 443 ssl;
Thay đổi thành
listen 127.0.0.1:443 ssl;
File cấu hình chính của Nginx/etc/nginx/nginx.conf
bao gồm virtual host mặt định lắng nghe ở port 80 và 443, vì thế bạn cần phải chỉnh sửa file này.
Khởi động Nginx để thay đổi cấu hình.
sudo systemctl restart nginx
Bước 3: Thay đổi port lắng nghe trên Apache
Chúng ta cũng cần phải thay đổi port lắng nghe trên Apache 127.0.0.2:80.
Debian/Ubuntu
Trên Debian và Ubuntu, chỉnh sửa file /etc/apache2/ports.conf
.
sudo nano /etc/apache2/ports.conf
Tìm 2 dòng
Listen 80 Listen 443
Thay đổi thành
Listen 127.0.0.2:80 Listen 127.0.0.2:443
Lưu cấu hình và tắt mở file, vào thư mục /etc/apache2/sites-enabled/
để chỉnh sửa virtual host.
Thay đổi
<VirtualHost *:80>
Thành
<VirtualHost 127.0.0.2:80>
Nếu bạn có sử dụng SSL thì cũng cần file thay đổi cấu hình virtual host cho SSL.
<VirtualHost *:443>
Thành
<VirtualHost 127.0.0.2:443>
Khởi động lại Apache
sudo systemctl restart apache2
Trên CentOS
Trên CentOS, chỉnh sửa file /etc/httpd/conf/httpd.conf
.
sudo nano /etc/httpd/conf/httpd.conf
Tìm
Listen 80
Thay đổi thành
Listen 127.0.0.2:80
Lưu lại và tắt mở file, sau đó vào thử mục/etc/httpd/conf.d/
, để chỉnh sửa virtual host.
<VirtualHost *:80>
Thay đổi thành
<VirtualHost 127.0.0.2:80>
Nếu bạn có sử dụng SSL virtual host thì cũng cần thay đổi cấu hình virtual host cho SSL
<VirtualHost *:443>
Thay đổi thành
<VirtualHost 127.0.0.2:443>
Trong file cấu hình /etc/httpd/conf.d/ssl.conf
Listen 443 https
Thay đổi thành
Listen 127.0.0.2:443 https
Lưu lại và đóng mở file, khởi động lại Apache để thay đổi cấu hình.
sudo systemctl restart httpd
Bước 4: Cấu hình HAProxy
Cài đặt và cấu hình HAProxy.
Trên Debian/Ubuntu
sudo apt install haproxy
Trên CentOS
sudo dnf install haproxy
Chỉnh sửa file cấu hình HAProxy.
sudo nano /etc/haproxy/haproxy.cfg
Thêm đoạn cấu hình bên dưới vào cuối file, đoạn cấu hình này sẽ cho HAProxy lắng nghe ở port 80 trên địa chỉ IP Public và chuyển hướng kết nối ở port 80 thành port 443. Thay đổi IP 12.34.56.78 thành địa chỉ IP phù hợp với IP trên máy chủ của bạn.
frontend http bind 12.34.56.78:80 mode http redirect scheme https code 301
Chúng ta cũng cần thêm đoạn cấu hình HTTPS ở front end.
frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 }
Sau đó chúng ta định nghĩa Nginx và Apache back end và tham số check
nói cho HAProxy thực hiện health checks với back end bằng cách gửi TCP packet.
backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.1:443 check backend apache mode tcp option ssl-hello-chk server apache 127.0.0.2:443 check
Bạn cũng có thể định nghĩa thêm default back end với tham số bên dưới.
default_backend nginx
Chúng ta sẽ sử dụng SNI header trong kết nôi HTTPS để chuyển hướng chính xác tới back end. Ví dụ, nếu Nginx phục vụ cho domain1.com và Apache phục vụ cho domain2.com thì chúng ta thêm 2 dòng cấu hình sau:
use_backend nginx if { req_ssl_sni -i domain1.com } use_backend apache if { req_ssl_sni -i domain2.com }
Chú ý 2 tham số default_backend
vàuse_backend
được định nghĩa ở trên.
Trong cấu hình ở trên chúng ta sử SNI (Server Name Indication) trong TLS để phân loại HTTPS tracffic khác nhau.
- Khi kết nối đến domain1.com, mà domain1.com ở trong TLS Client Hello, HAProxy sẽ chuyển hướng kết nối đến
nginx
backend. - Khi kết nối đến domain2.com, mà domain2.com ở trong TLS Client Hello, HAProxy sẽ chuyển hướng kết nôi đến
apache
backend.
Nếu bạn không chỉ ra domain name trong TLS Client Hello, HAproxy sẽ sử dụng default back end nginx như đã khai báo ở trên.
Lưu lại và đóng file cấu hình, khởi động lại HAProxy.
sudo systemctl restart haproxy
Bây giờ Apache, Nginx và HAProxy chạy trên cùng máy chủ.
Làm sao để chuyển hướng địa chỉ IP của Client tới Back end
Mặt đinh, Apache và Nginx chỉ có thể thấy địa chỉ IP của HAProxy.
By default, Apache and Nginx can only see HAProxy’s IP address. Để lấy địa chỉ IP thực của của client, thêm tham số send-proxy-v2
vào đoạn cấu hình back end của HAProxy như bên dưới.
server nginx 127.0.0.1:443 send-proxy-v2 check server apache 127.0.0.2:443 send-proxy-v2 check
Chúng ta cũng cần thêm một vài tham số cấu hình cho Nginx và Apache để đảm bảo hoạt động, nếu không sẽ không chạy.
Nginx
Thêm tham số proxy_protocol
vào Nginx listen
như bên dưới.
listen 127.0.0.2:443 ssl http2 proxy_protocol;
Sau đó thêm 2 tham số Nginx vào chỗ http { }
trong file cấu hình /etc/nginx/nginx.conf
set_real_ip_from 127.0.0.1; real_ip_header proxy_protocol;
Lưu lại và thoát, sau đó restart lại Nginx.
sudo systemctl reload nginx
Chú ý: Nếu website của bạn đang chạy phía sau Cloudflare CDN, bạn nên thay đôi
Chú ý: Nếu website của bạn đang chạy phía sau Cloudflare CDN, bạn nên thay đổi real_ip_header proxy_protocol;
thành real_ip_header CF-Connecting-IP;
để hiển thị địa chỉ IP thực của client. Bạn cũng có thể thêm tham số real_ip_header
tới những block file cấu hình riêng để override file cấu hình global trong /etc/nginx/nginx.conf
Cuối cùng, khởi động lại HAProxy.
sudo systemctl restart haproxy
Apache
Nếu bạn sử dụng Apache trên Debian/Ubuntu, bạn cần phải enable remoteip module. (Mặt đinh, module này đã được enable trong CentOS)
sudo a2enmod remoteip
Sau đó bạn thêm 3 dòng cấu hình trong cấu hình virtual host của Apache.
RemoteIPProxyProtocol On RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy 127.0.0.1
Ví dụ như bên dưới.
<VirtualHost 127.0.0.2:443> ServerName tungdt.net RemoteIPProxyProtocol On RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy 127.0.0.1
Lưu lại và thoát, sau đó bạn cũng cần thay đổi tham số trong định dạng combined
log file.
sudo nano /etc/apache2/apache2.conf
Hoặc
sudo nano /etc/httpd/conf/httpd.conf
Tìm dòng bên dưới
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
Thay thế thành
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
Lưu lại và thoát, sau đó khởi động lại Apache
sudo systemctl restart apache2
Hoặc
sudo systemctl restart httpd
Chú ý: Tham số RemoteIPProxyProtocol On
chỉ có ở phiên bản Apache 2.4.31 và mới nhất. Để kiểm tra phiên bản Apache chạy lệnh
sudo apache2 -v
Hoặc
sudo httpd -v
Trên Ubuntu 18.04 có sẵn phiên bản Apache 2.4.29. Nếu webserver Apache của bạn không phù hợp với yêu cầu thi bạn nên bỏ tham số send-proxy-v2
trong file cấu hình back end HAProxy như đã định nghĩa. Trên CentOS 8 phiên bản Apache 2.4.37.
Cuối cùng, khởi động lại HAProxy
sudo systemctl restart haproxy
Cài đặt và cấu hình Let’s Encrypt SSL Certificate
Để cài đặt Let’s Encrypt certificate cho virtual host/server block, chỉ có thể sử dụng kiểu dns-01
.Còn 2 kiểu http-01
vàtls-alpn-01
sẽ không hoạt động.
Đẩu tiền, bạn cần phải cài đặt Certbot DNS plugin, chúng có vài DNS plugins có sẵn trong Debian và Ubuntu, bạn có thể tìm với từ khóa.
apt search python3-certbot-dns
Thông tin hiển thị kết quả tìm:
python3-certbot-dns-cloudflare/bionic,bionic 0.23.0-1 all Cloudflare DNS plugin for Certbot python3-certbot-dns-digitalocean/bionic,bionic 0.23.0-1 all DigitalOcean DNS plugin for Certbot python3-certbot-dns-dnsimple/bionic,bionic 0.23.0-1 all DNSimple DNS plugin for Certbot python3-certbot-dns-google/bionic,bionic 0.23.0-1 all Google DNS plugin for Certbot python3-certbot-dns-rfc2136/bionic,bionic 0.23.0-1 all RFC 2136 DNS plugin for Certbot python3-certbot-dns-route53/bionic,bionic 0.23.0-1 all Route53 DNS plugin for Certbot
Trên CentOS 8, bạn có thể tìm Certbot DNS plugins từ EPEL repository. Chú ý bạn cần cài đặt Certbot từ repository mặc định của CentOS.
sudo dnf install certbot
Tìm DNS plugins từ EPEL repository.
sudo dnf install epel-release dnf search certbot-dns
Thôn tin hiển thị kết quả tìm:
python3-certbot-dns-ovh.noarch : OVH DNS Authenticator plugin for Certbot python3-certbot-dns-nsone.noarch : NS1 DNS Authenticator plugin for Certbot python3-certbot-dns-gehirn.noarch : Gehirn Infrastructure Service DNS Authenticator plugin for Certbot python3-certbot-dns-google.noarch : Google Cloud DNS Authenticator plugin for Certbot python3-certbot-dns-linode.noarch : Linode DNS Authenticator plugin for Certbot python3-certbot-dns-luadns.noarch : LuaDNS Authenticator plugin for Certbot python3-certbot-dns-rfc2136.noarch : RFC 2136 DNS Authenticator plugin for Certbot python3-certbot-dns-route53.noarch : Route53 DNS Authenticator plugin for Certbot python3-certbot-dns-cloudxns.noarch : CloudXNS DNS Authenticator plugin for Certbot python3-certbot-dns-dnsimple.noarch : DNSimple DNS Authenticator plugin for Certbot python3-certbot-dns-cloudflare.noarch : Cloudflare DNS Authenticator plugin for Certbot python3-certbot-dns-cloudflare.noarch : Cloudflare DNS Authenticator plugin for Certbot python3-certbot-dns-dnsmadeeasy.noarch : DNS Made Easy DNS Authenticator plugin for Certbot python3-certbot-dns-sakuracloud.noarch : Sakura Cloud DNS Authenticator plugin for Certbot
Việc cài đặt những plugin này dựa vào dịch vụ DNS hosting mà bạn đang sử dụng. Tôi đang sử dụng Cloudflare, vì thế tôi sẽ sử dụng Cloudflare làm ví dụ. Chạy những lệnh bên dưới để cài đặt gói python3-certbot-dns-cloudflare
trên Debian/Ubuntu.
sudo apt install python3-certbot-dns-cloudflare
Sau đó tạo file cấu hình cho Cloudflare.
sudo nano /etc/letsencrypt/cloudflare.ini
Chúng ta cần thêm địa chỉ email Cloudflare và API key vào file cấu hình này.
# Cloudflare API credentials used by Certbot dns_cloudflare_email = tungdt@tungdt.com dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234567
Bạn có thể tìm API key của Cloudflare ở địa chỉ https://dash.cloudflare.com/profile
. Chú ý Certbot Cloudflare plugin hiện tại không hỗ trợ API Tokens của Cloudflare, vì thế bạn phải sử dụng “Global API Key” để xác thực.
Lưu lại và thoát, API key sẽ bypasses two-factor authentication củaCloudflare, vì thế bạn chỉ nên cho phép user root đọc được file này.
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
Bây giờ bạn có thể chạy lệnh certbot.
sudo certbot --agree-tos -a dns-cloudflare -i nginx --redirect --hsts --staple-ocsp --email you@example.com -d www.your-domain.com,your-domain.com
Trong lệnh trên, chúng ta chỉ ra sử dụng dns-cloudflare
như là authenticator cài đặt TLS certificate và sử dụng nginx
plugin để tạo HTTPS server block. Nếu bạn sử dụng Apache, bạn thay thế nginx
thànhapache
.
Lệnh này sẽ hỏi bạn nhập đường dẫn chứa file .ini
, nhập đường dẫn /etc/letsencrypt/cloudflare.ini
.
Sau khi SSL certificate được cài đặt vào file cấu hình webserver. Bạn cần cấu hình web server lắng nghe trên 127.0.0.1:443 hoặc 127.0.0.2:443. Ví dụ, nếu bạn sử dụng Nginx
listen 443 ssl
Thay đổi thành
listen 127.0.0.1:443 ssl http2
Và cũng thay đổi port 80 thành
listen 127.0.0.1:80;
Lưu lại và thoát, sau đó khởi động lại web server.
Một số lỗi thông dụng
Sai tên miền
Nếu bạn có nhiều Nginx virtual host trên một máy chủ và khi bạn nhập một tên miền, trình duyệt web sẽ đưa bạn đến một tên miền khác được lưu trữ trên cùng một máy chủ, có thể file cấu hình Nginx virtual host không được lưu dưới dạng đuôi mở rộng .conf, vì vậy Nginx nhận biết cấu hình virtual host cho tên miền này. Cũng có thể là bạn đã thiết lập bản ghi AAAA cho tên miền, nhưng bạn chưa định nghĩa cấu hình Nginx để phân phối tên miền trong IPv6.
Vấn đề ở trên cũng tương tự cho Apache.
Vi phạm giao thức mạng
Nếu bạn thiết lập send-proxy-v2
header trong cấu hình HAProxy, nhưng bạn không enable proxy_protocol
trong Nginx hoặc Apache, thì website của bạn sẽ hiển thị lỗi như bên dưới.
The site has experienced a network protocol violation that cannot be repaired.
Lưu ý, sau khi thay đổi cấu hình bạn phải restart lại Nginx hoặc Apache.
Lỗi PR_CONNECT_RESET_ERROR
Nếu bạn enable proxy_protocol
trong Nginx hoặc Apache, nhưng bạn không thiết lập send-proxy-v2
header trong cấu hình HAProxy, website của bạn sẽ hiển thị lỗi như bên dưới.
secure connection failed. PR_CONNECT_RESET_ERROR
Và bạn cũng tìm thấy lỗi trong log file Nginx hoặc Apache như bên dưới
broken header while reading PROXY protocol
Lưu ý, sau khi thay đổi cấu hình bạn phải restart lại Nginx hoặc Apache.
Chú ý, nếu bạn enable proxy protocol
một trong các file cấu hình virtual host của Nginx hoặc Apache, thì cũng ngầm hiểu rằng nó cũng enable cho tất cả virtual host khác. Không có cách nào để disable proxy_protocol
cho những virtual host riêng lẻ. Chính vì thế nếu bạn dự định chạy Apache và Nginx trên cùng máy chủ thì bạn nên enable proxy protocol trong Nginx và không enable trong Apache. Nếu một virual host không cần nhận IP thực của client thì cấu hình nó trong Apache.
Chú ý: Bạn có thể thiết lập Apache/Nginx virtual host mới trên địa chỉ loopback khác như 127.0.0.4. Bằng cách này, virtual host mới không enable proxy protocol.
Lỗi PR_END_OF_FILE_ERROR
Nếu bạn enable proxy protocol trong Apache/Nginx, nhưng bạn truy cập trực tiếp virtual host (bypass HAProxy), bạn sẽ thấy lỗi như bên dưới.
PR_END_OF_FILE_ERROR
Kết thúc
Tôi hi vọng bài viết này sẽ giúp bạn hiểu rõ cách Apache, Nginx và HAProxy chạy cùng trên một máy chủ.