0

Tìm hiểu thuật toán chọn Server và Location của Nginx và một số thủ thật hay dùng cho security

Giới thiệu

Nginx là một trong những web server nổi tiếng trên thế giới, nó có thể handle thành công nhiều kết nối cùng một lúc, đòng thời có thể dễ dàng hoạt động như một máy chủ độc lập, một mail server hay proxy server. Trong hướng dẫn dưới đây chúng ta có thể thảo luận thêm về việc Nginx xử lý những request như thế nào, điều này giúp bạn dễ dàng phỏng đoán cũng như định hướng được yêu cầu sẽ được xử lý tại đâu.

Định nghĩa một Nginx block

Nginx phân chia các config thành các block khác nhau được tạo trong cùng một cấp, mỗi block sẽ được bắt đầu bởi:

server {
  # config here
 .....
}

Mỗi khi có request từ client gửi đến, Nginx sẽ phân tích và xác định được block nào sẽ được sử dụng để xử lý yêu cầu đó. Và quá trình quyết định block nào xử lý chính là điều sẽ được thảo luận dưới đây.

Một block server là tập hợp con của file Nginx config ở đó định nghĩa những virtual server được sử dụng để xử lý những yêu cầu thuộc một loại đã được xác định. Thông thường khi config server thì sẽ cần định nghĩa một vài server block, và quyết định block nào xử lý request nào dựa trên domain name, port hoặc IP của request.

Trong 1 server block lại có chứa nhiều location block, được xử dụng để xác định resource nào sẽ sử dụng cho request, và hoàn toàn có thể chia nhỏ theo cách mình mong muốn.

Cách Nginx xác định server block nào sẽ handle request

Nginx thực hiện điều này thông qua một hệ thống kiểm tra để quyết định ra được block nào phù hợp nhất được sử dụng bằng cách sử dụng 2 directive là listenserver_name, thứ tự ưu tiên là listen rồi đến server_name

Directives: listen

Directive listen có thể được set bởi các options sau:

  • Một bộ IP / port
  • Một mình IP address, khi đó sẽ được nghe dưới port mặc định là 80
  • Một mình port number, khi đó nó sẽ lắng nghe được mọi interface trên cổng đó
  • Cuối cùng có thể 1 path dẫn đến Unix socket

Tuỳ chọn cuối cùng thường được sử dụng để chuyển giữa yêu cầu giữa các servers khác nhau. Khi cố gắng để xác định server block nào sẽ xử lý request, trước tiên Nginx sẽ cố gắng quyết định dựa trên tính đặc thù của directive listen bằng các quy tắc sau:

  • Một block không định nghĩa directive listenthì sẽ được gán với giá trị mặc định là 0.0.0.0:80.
  • Một block được định nghĩa listen only IP address 1.1.1.1 sẽ được gán port mặc định 1.1.1.1::80
  • Một block chỉ có port mà không có IP address 8080 sẽ được gán IP mặc định lắng nghe tất cả 0.0.0.0:8080

Dựa vào những quy tắc trên Nginx sẽ xác định được server block nào xử lý, nếu có nhiều server block matching thì nó sẽ dùng đến directive thứ 2 đẻ định nghĩa chính là server_name.

Directives: server_name

Sau khi đã lọc được những block với directive listen, Nginx sẽ tiếp tục tìm đến server_name để lấy được chính xác block nào xử lý dựa theo các nguyên tắc sau:

  • Trước tiên, Nginx sẽ cố gắng tìm một server block có server_name trùng hoàn toàn với giá trị trong Host của request, nếu có nhiều kết quả được tìm thấy thì nó sẽ lấy kết quả đầu tiên
  • Nếu không tìm được kết quả chính xác, Nginx sẽ tìm server block có server_name khớp với ký tự đại diện trên đầu (leading wildcard) (được biểu thị bằng dấu * ở đầu trong server_name). Nếu nhiều block matches thì nó sẽ lấy block nào match với số kí tự nhiều nhất
  • Nếu với leading wildcard mà không tìm thấy kết quả phù hợp, nó sẽ sử dụng matches với trailing wildcard (kí tự * được đặt ở cuối server_name). Tương tự như leading wildcard => Nếu nhiều block matches sẽ lấy block matches với số kí tự nhiều nhất
  • Nếu như vẫn không matches thì Nginx sẽ xác định block bằng các biểu thức regex (những server_name~ ở đầu.) server_name đầu tiên match regex với Host của request sẽ được sử dụng.
  • Cuối cùng mà vẫn không tìm thấy được thì nó sẽ đẩy request vào block mặc định cho request đó.

Ví dụ

server {
    listen 80;
    server_name *.host.com;
    ...
}

server {
    listen 80;
    server_name ngocnt.host.com;
    ...
}

=> Khi chúng request bởi ngocnt.host.com => Thì block thứ 2 sẽ được lựa chọn.

server {
    listen 80;
    server_name www.host.*;
    ...
}

server {
    listen 80;
    server_name *.host.vn;
    ...
}

server {
    listen 80;
    server_name *.vn;
    ...
}

=> Khi chúng ta request www.host.vn => block thứ 2 cũng sẽ được lựa chọn.

server {
    listen 80;
    server_name ngocnt.host.com;
    ...
}

server {
    listen 80;
    server_name host.com;
    ...
}

server {
    listen 80;
    server_name www.host.*;
    ...
}

=> Khi chúng ra truy cập www.host.com => block thứ 3 sẽ được chọn.

server {
    listen 80;
    server_name host.com;
    ...
}

server {
    listen 80;
    server_name ~^(www|ngocnt).*\.host\.com$;
    ...
}

server {
    listen 80;
    server_name ~^(subdomain|www|ngocnt).*\.host\.com$;
    ...
}

=> Khi chúng ra truy cập www.host.com => block thứ 2 sẽ được chọn.

Blocks: location

Một location block sẽ được đặt trong server block với cú pháp

location optional_modifier location_match {
	...
}

Trong đó các optional_modifier sẽ có các lựa chọn sau:

  • Nếu không có gì => Nó sẽ lấy phần đầu của request để matches
  • = block này sẽ được coi là match nếu URI request match chính xác với location này
  • ~ block này được coi là match nếu URI request match regex với location này có phân biệt chữ hoa, chữ thường.
  • ~* block này được coi là match nếu URI request match regex với location này không phân biệt chữ hoa, chữ thường.

Tương tự như bên block server, nó sẽ tìm những block với matches chính xác trước sau đó sẽ lựa chọn những regex matches để tìm được block phù hợp.

Dưới đây sẽ là một vài Cheat Sheet hay sử dụng liên quan đến security

Proxy Pass + Rewrite

location ~ <expr> {
   rewrite /<path to strip>/(.*) /$1 break;
   proxy_pass http://127.0.0.1:8080;
}

Proxy Pass + Host Header

location / {
    proxy_pass       http://localhost:8000;
    proxy_set_header Host $host;
}

Ẩn nginx version

serverserver_tokens: off;

Ẩn nginx server signature

more_set_headers "Server: Unknown";

** Keep only TLS 1.2 ( TLS 1.3 )**

ssl_protocols TLSv1.2

Force all connection over TLS

return 301 https //$host$request_uri

Cross-Site Scripting (XSS) header

add_header X-Xss-Protection "1; mode=block" always;

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí