Có nhiều cách để nâng cao tính linh hoạt và bảo mật cho ứng dụng Node.js của bạn. Một trong những giải pháp hiệu quả là sử dụng reverse proxy như Nginx, giúp cân bằng tải, lưu cache nội dung tĩnh và triển khai Transport Layer Security (TLS). Việc kích hoạt HTTPS trên máy chủ giúp mã hóa giao tiếp giữa người dùng và ứng dụng, đảm bảo tính bảo mật cho mọi dữ liệu truyền tải.
Khi triển khai reverse proxy với TLS/SSL trong môi trường container, quy trình sẽ khác so với việc làm việc trực tiếp trên hệ điều hành máy chủ. Chẳng hạn, khi bạn lấy chứng chỉ từ Let’s Encrypt cho một ứng dụng chạy trên máy chủ, bạn sẽ cài đặt phần mềm cần thiết trực tiếp trên máy chủ đó. Tuy nhiên, khi làm việc với container, bạn sẽ có một cách tiếp cận linh hoạt hơn. Với Docker Compose, bạn có thể tạo các container cho ứng dụng, máy chủ web và Certbot công cụ giúp bạn lấy chứng chỉ. Việc sử dụng Docker Compose giúp tận dụng tính mô-đun và di động của container, mang lại lợi ích lớn trong việc triển khai và quản lý.
Để triển khai ứng dụng Node.js an toàn với Docker, bạn cần một nền tảng mạnh mẽ. VPS hosting cung cấp tài nguyên linh hoạt, giúp tối ưu hóa hiệu suất và bảo mật. Khám phá các giải pháp VPS hosting chất lượng tại DataOnline để đảm bảo ứng dụng của bạn luôn hoạt động ổn định.
Trong bài hướng dẫn này, bạn sẽ học cách triển khai một ứng dụng Node.js với reverse proxy Nginx sử dụng Docker Compose. Bạn sẽ lấy chứng chỉ TLS/SSL cho tên miền liên kết với ứng dụng và đảm bảo rằng ứng dụng của bạn đạt được điểm số bảo mật cao từ SSL Labs. Cuối cùng, bạn sẽ thiết lập một cron job để tự động gia hạn chứng chỉ, bảo vệ tên miền của bạn lâu dài.
Yêu Cầu
Để theo dõi hướng dẫn này, bạn cần:
-
Một máy chủ Ubuntu 18.04, một tài khoản không phải root có quyền sudo và một tường lửa hoạt động. Để biết cách thiết lập, hãy đọc hướng dẫn Thiết lập máy chủ ban đầu của DataOnline.
-
Docker và Docker Compose được cài đặt trên máy chủ của bạn. Để biết cách cài đặt Docker, hãy làm theo Bước 1 và 2 của Cách Cài Đặt và Sử Dụng Docker trên Ubuntu 18.04. Để cài đặt Compose, hãy làm theo Bước 1 của Cách Cài Đặt Docker Compose trên Ubuntu 18.04.
-
Một tên miền đã đăng ký. Hướng dẫn này sẽ sử dụng your_domain làm ví dụ. Bạn có thể lấy miễn phí tại Freenom hoặc sử dụng nhà đăng ký tên miền mà bạn ưa thích.
-
Cả hai bản ghi DNS sau đây được thiết lập cho máy chủ của bạn. Bạn có thể tham khảo phần giới thiệu về DNS của DataOnline nếu bạn đang sử dụng:
-
Một bản ghi A với your_domain trỏ tới địa chỉ IP công cộng của máy chủ.
-
Một bản ghi A với www.your_domain trỏ tới địa chỉ IP công cộng của máy chủ.
-
Khi mọi thứ đã sẵn sàng, bạn có thể bắt đầu với bước đầu tiên.
Bước 1 – Nhân bản và kiểm tra ứng dụng Node
Là bước đầu tiên, bạn sẽ nhân bản repository chứa mã nguồn ứng dụng Node, bao gồm cả Dockerfile dùng để build image ứng dụng với Docker Compose. Sau đó, bạn sẽ kiểm tra ứng dụng bằng cách build và chạy nó với lệnh docker run, chưa có reverse proxy hay SSL.
Trong thư mục home của tài khoản không phải root, clone repository nodejs-image-demo từ tài khoản GitHub của DataOnline Community. Repository này bao gồm mã nguồn từ hướng dẫn Cách Xây Dựng Ứng Dụng Node.js với Docker.
Clone repository vào một thư mục – ví dụ sử dụng tên node_project (bạn có thể đặt tên khác nếu thích):
git clone https://github.com/do-community/nodejs-image-demo.git node_project
Chuyển vào thư mục node_project:
cd node_project
Trong thư mục này, có một Dockerfile chứa hướng dẫn để build ứng dụng Node sử dụng image node:10 và nội dung của thư mục dự án hiện tại. Bạn có thể xem nội dung Dockerfile với lệnh:
cat Dockerfile
Output FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8080 CMD [ "node", "app.js" ]
Các hướng dẫn này xây dựng một image Node bằng cách sao chép mã nguồn dự án từ thư mục hiện tại vào container và cài đặt các phụ thuộc bằng npm install
. Chúng cũng tận dụng tính năng cache và phân lớp image của Docker bằng cách tách riêng việc sao chép package.json
và package-lock.json
trước khi sao chép phần còn lại của mã nguồn ứng dụng. Cuối cùng, các hướng dẫn này quy định rằng container sẽ chạy dưới quyền của người dùng node
(không phải root), với các quyền phù hợp được thiết lập cho mã nguồn ứng dụng và thư mục node_modules
.
Để kiểm tra ứng dụng mà không cần SSL, bạn có thể build và gán tag cho image bằng lệnh docker build
cùng với tham số -t
. Ví dụ dưới đây đặt tên cho image là node-demo, nhưng bạn có thể đặt tên khác tùy thích:
docker build -t node-demo .
Sau khi quá trình build hoàn tất, bạn có thể liệt kê các image bằng:
docker images
Output REPOSITORY TAG IMAGE ID CREATED SIZE node-demo latest 23961524051d 7 seconds ago 73MB node 10-alpine 8a752d5af4ce 3 weeks ago 70.7MB
Tiếp theo, tạo container với lệnh docker run. Ba tham số được sử dụng:
-
-p: Công khai cổng trên container và ánh xạ nó tới cổng trên máy chủ. Ví dụ này dùng cổng 80 trên máy chủ.
-
-d: Chạy container dưới nền.
-
–name: Đặt tên dễ nhớ cho container.
Chạy lệnh sau để tạo container:
docker run --name node-demo -p 80:8080 -d node-demo
Kiểm tra các container đang chạy với:
docker ps
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4133b72391da node-demo "node app.js" 17 seconds ago Up 16 seconds 0.0.0.0:80->8080/tcp node-demo
Bây giờ, bạn có thể truy cập tên miền của bạn để kiểm tra thiết lập http://your_domain (Hãy thay your_domain bằng tên miền của bạn.)
Ứng dụng sẽ hiển thị trang landing mặc định.
Với Dockerfile của ứng dụng đã có, bạn sẽ tạo một file cấu hình cho container Nginx. Bạn có thể bắt đầu với một cấu hình tối giản bao gồm tên miền, thư mục gốc (document root), thông tin proxy và một location block để định tuyến yêu cầu của Certbot tới thư mục .well-known – nơi Certbot sẽ đặt file tạm thời xác thực rằng DNS của tên miền trỏ về máy chủ.
Trước tiên, tạo một thư mục trong thư mục dự án hiện tại (node_project) để chứa file cấu hình:
mkdir nginx-conf
Tạo và mở file với nano (hoặc editor ưa thích):
nano nginx-conf/nginx.conf
Thêm khối server sau để proxy các yêu cầu người dùng đến container Node và định tuyến yêu cầu của Certbot tới thư mục .well-known:
server { listen 80; listen [::]:80; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name your_domain www.your_domain; location / { proxy_pass http://nodejs:8080; } location ~ /.well-known/acme-challenge { allow all; root /var/www/html; } }
File docker-compose.yml
sẽ định nghĩa các service của bạn, bao gồm ứng dụng Node và máy chủ web. File này sẽ chỉ định các thông tin như volumes đặt tên (quan trọng để chia sẻ chứng chỉ SSL giữa các container), cũng như thông tin về mạng và cổng. Ngoài ra, bạn cũng có thể chỉ định các lệnh cần chạy khi các container được tạo.
Tạo và mở file docker-compose.yml
trong thư mục dự án hiện tại:
nano docker-compose.yml
Đầu tiên, định nghĩa service cho ứng dụng:
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped
Định nghĩa service nodejs bao gồm các thành phần sau:
-
build
: Xác định các tùy chọn cấu hình, bao gồmcontext
vàdockerfile
, được áp dụng khi Compose build image của ứng dụng. Nếu bạn muốn sử dụng một image có sẵn từ một registry như Docker Hub, bạn có thể sử dụng lệnh image thay thế, kèm theo thông tin về tên người dùng, repository và tag của image. -
context
: Xác định ngữ cảnh build cho image ứng dụng. Trong trường hợp này, nó là thư mục dự án hiện tại, được biểu diễn bằng.
-
dockerfile
: Chỉ định Dockerfile mà Compose sẽ sử dụng để build image – chính là Dockerfile đã được xem xét ở Bước 1. -
image, container_name
: Gán tên cho image và container. -
restart
: Xác định chính sách khởi động lại. Mặc định làno
, nhưng trong ví dụ này, container được thiết lập đểrestart unless-stopped
(tự động khởi động lại trừ khi bị dừng thủ công).
Lưu ý rằng bạn không sử dụng bind mounts với service này, vì thiết lập hiện tại tập trung vào triển khai (deployment) thay vì phát triển (development). Để tìm hiểu thêm, bạn có thể đọc tài liệu của Docker về bind mounts và volumes.
Để cho phép giao tiếp giữa container ứng dụng và container máy chủ web, hãy thêm một mạng bridge có tên app-network ngay sau phần định nghĩa restart
:
services: nodejs: ... networks: - app-network
Một mạng bridge do người dùng định nghĩa như thế này cho phép các container trên cùng một Docker daemon host có thể giao tiếp với nhau. Điều này giúp tối ưu hóa lưu lượng truy cập và giao tiếp trong ứng dụng của bạn, vì nó mở tất cả các cổng giữa các container trên cùng một mạng bridge, nhưng không mở bất kỳ cổng nào ra bên ngoài.
Nhờ đó, bạn có thể chủ động lựa chọn chỉ mở các cổng cần thiết để cung cấp dịch vụ frontend của mình, thay vì để lộ toàn bộ hệ thống.
Tiếp theo, định nghĩa service webserver:
... webserver: image: nginx:mainline-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt depends_on: - nodejs networks: - app-network
Một số thiết lập của service nodejs vẫn được giữ nguyên, nhưng có một số thay đổi quan trọng như sau:
-
image
: Chỉ định rằng Compose sẽ kéo image Nginx mới nhất dựa trên Alpine từ Docker Hub. Để tìm hiểu thêm về Alpine images, bạn có thể tham khảo Bước 3 của hướng dẫn Cách Xây Dựng Ứng Dụng Node.js với Docker. -
ports: Mở cổng
80
, cho phép cấu hình Nginx mà bạn đã thiết lập hoạt động chính xác.
Ngoài ra, một số volumes được đặt tên và bind mounts cũng được xác định:
-
web-root:/var/www/html
: Thêm tài nguyên tĩnh của trang web vào một volume có tênweb-roo
t, sau đó ánh xạ nó đến thư mục/var/www/html
trên container. -
./nginx-conf:/etc/nginx/conf.d
: Bind mount thư mục cấu hình Nginx trên máy chủ host vào thư mục tương ứng trên container. Điều này giúp đảm bảo rằng bất kỳ thay đổi nào bạn thực hiện trên host cũng sẽ được phản ánh ngay trong container. -
certbot-etc:/etc/letsencrypt
: Mount thư mục chứa chứng chỉ và khóa Let’s Encrypt cho tên miền của bạn vào thư mục tương ứng trên container. -
certbot-var:/var/lib/letsencrypt
: Mount thư mục làm việc mặc định của Let’s Encrypt vào thư mục tương ứng trên container.
Các cấu hình này giúp đảm bảo rằng chứng chỉ SSL được lưu trữ chính xác và bất kỳ cập nhật nào đối với cấu hình Nginx hoặc chứng chỉ cũng sẽ tự động có hiệu lực mà không cần build lại container.
Tiếp theo, định nghĩa service cho Certbot:
... certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain
Định nghĩa này yêu cầu Compose kéo image certbot/certbot
từ Docker Hub. Nó cũng sử dụng các volumes được đặt tên để chia sẻ tài nguyên với container Nginx, bao gồm:
-
certbot-etc: Chứa chứng chỉ và khóa của tên miền.
-
certbot-var: Chứa thư mục làm việc của Let’s Encrypt.
-
web-root: Chứa mã nguồn ứng dụng.
Ngoài ra, bạn cũng sử dụng depends_on để đảm bảo rằng container certbot chỉ được khởi chạy sau khi service webserver đang chạy.
Tùy chọn command chỉ định lệnh sẽ chạy khi container được khởi động. Trong đó, nó bao gồm subcommand certonly
với các tham số sau:
-
–webroot: Yêu cầu Certbot sử dụng plugin webroot để đặt các file xác thực vào thư mục webroot.
-
–webroot-path: Xác định đường dẫn đến thư mục webroot.
-
–email: Địa chỉ email của bạn để đăng ký và khôi phục tài khoản.
-
–agree-tos: Xác nhận rằng bạn đồng ý với Thỏa thuận Người đăng ký ACME.
-
–no-eff-email: Chỉ định rằng bạn không muốn chia sẻ email với Electronic Frontier Foundation (EFF). Nếu bạn muốn chia sẻ, có thể bỏ qua tùy chọn này.
-
–staging: Yêu cầu Certbot sử dụng môi trường thử nghiệm của Let’s Encrypt để lấy chứng chỉ test. Tùy chọn này giúp bạn kiểm tra cấu hình mà không bị giới hạn số lượng yêu cầu chứng chỉ trong một khoảng thời gian. Để biết thêm thông tin, hãy tham khảo tài liệu về giới hạn yêu cầu (rate limits) của Let’s Encrypt.
-
-d: Xác định danh sách tên miền sẽ áp dụng chứng chỉ. Trong trường hợp này, bạn sử dụng your_domain và www.your_domain. Hãy thay thế bằng tên miền thực tế của bạn.
Thêm định nghĩa volume và network. Hãy chắc chắn thay thế username bằng tài khoản người dùng không phải root của bạn.
Cuối cùng, thêm định nghĩa cho volumes và network:
... volumes: certbot-etc: certbot-var: web-root: driver: local driver_opts: type: none device: /home/sammy/node_project/views/ o: bind networks: app-network: driver: bridge
Các volumes được đặt tên của bạn bao gồm chứng chỉ Certbot, thư mục làm việc của Certbot, và volume chứa các tài nguyên tĩnh của trang web, web-root.
Trong hầu hết các trường hợp, driver mặc định cho Docker volumes là local driver, và trên Linux, nó chấp nhận các tùy chọn tương tự như lệnh mount
. Nhờ đó, bạn có thể chỉ định một danh sách các tùy chọn driver với driver_opts, cho phép mount thư mục views trên máy chủ (host) – thư mục này chứa các tài nguyên tĩnh của ứng dụng, vào volume khi container chạy. Nội dung thư mục này sau đó có thể được chia sẻ giữa các container.
Để biết thêm chi tiết về nội dung của thư mục views, vui lòng tham khảo Bước 2 của hướng dẫn Cách Xây Dựng Ứng Dụng Node.js với Docker.
Dưới đây là file docker-compose.yml hoàn chỉnh:
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped networks: - app-network webserver: image: nginx:mainline-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt depends_on: - nodejs networks: - app-network certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain volumes: certbot-etc: certbot-var: web-root: driver: local driver_opts: type: none device: /home/sammy/node_project/views/ o: bind networks: app-network: driver: bridge
Với các định nghĩa service đã được thiết lập, bạn đã sẵn sàng khởi động các container và kiểm tra yêu cầu chứng chỉ của mình.
Bước 4 – Lấy chứng chỉ SSL và thông tin xác thực
Output sẽ xác nhận các service đã được tạo:
Output Creating nodejs ... done Creating webserver ... done Creating certbot ... done
Sử dụng lệnh:
docker-compose ps
Nếu mọi thứ diễn ra thành công, các service nodejs và webserver sẽ ở trạng thái Up, và container certbot sẽ thoát với mã trạng thái 0, cho biết quá trình thực thi đã hoàn tất mà không gặp lỗi.
Output Name Command State Ports ------------------------------------------------------------------------ certbot certbot certonly --webroot ... Exit 0 nodejs node app.js Up 8080/tcp webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
Nếu trạng thái của nodejs và webserver là “Up” và certbot có exit code 0, nghĩa là yêu cầu chứng chỉ đã thành công. Để kiểm tra các chứng chỉ đã được mount vào container webserver, chạy:
docker-compose logs certbot
Bây giờ, bạn có thể kiểm tra xem chứng chỉ SSL đã được mount vào container webserver hay chưa bằng cách sử dụng lệnh:
docker-compose exec webserver ls -la /etc/letsencrypt/live
Output sẽ hiển thị thư mục chứa chứng chỉ, ví dụ:
Output total 16 drwx------ 3 root root 4096 Dec 23 16:48 . drwxr-xr-x 9 root root 4096 Dec 23 16:48 .. -rw-r--r-- 1 root root 740 Dec 23 16:48 README drwxr-xr-x 2 root root 4096 Dec 23 16:48 your_domain
Khi biết yêu cầu chứng chỉ thành công, bạn có thể chỉnh sửa định nghĩa service certbot trong docker-compose.yml để bỏ tham số –staging và thay thế bằng –force-renewal nhằm yêu cầu chứng chỉ mới với cùng tên miền.
Mở file docker-compose.yml:
nano docker-compose.yml
Tìm phần định nghĩa service certbot và thay đổi lệnh thành:
... certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain ...
docker-compose up --force-recreate --no-deps certbot
Output sẽ xác nhận yêu cầu chứng chỉ thành công.
Output Recreating certbot ... done Attaching to certbot certbot | Account registered. certbot | Renewing an existing certificate for your_domain and www.your_domain certbot | certbot | Successfully received certificate. certbot | Certificate is saved at: /etc/letsencrypt/live/your_domain/fullchain.pem certbot | Key is saved at: /etc/letsencrypt/live/your_domain phd.com/privkey.pem certbot | This certificate expires on 2022-11-03. certbot | These files will be updated when the certificate renews. certbot | NEXT STEPS: certbot | - The certificate will need to be renewed before it expires. Cert bot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setu p for instructions. certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log certbot | certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - certbot | If you like Certbot, please consider supporting our work by: certbot | * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/do nate certbot | * Donating to EFF: https://eff.org/donate-le certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - certbot exited with code 0
Khi chứng chỉ đã được thiết lập, bạn có thể tiếp tục chỉnh sửa cấu hình Nginx để kích hoạt SSL.
Bước 5 – Chỉnh sửa cấu hình máy chủ Web và định nghĩa Service
Để kích hoạt SSL trong cấu hình Nginx, bạn cần thêm chuyển hướng HTTP sang HTTPS và chỉ định vị trí của chứng chỉ SSL, khóa và các thiết lập bảo mật (bao gồm cả Diffie-Hellman để hỗ trợ Perfect Forward Secrecy).
Trước tiên, dừng service webserver:
docker-compose stop webserver
Tạo một thư mục cho Diffie-Hellman key:
mkdir dhparam
Sinh khóa Diffie-Hellman với lệnh:
sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048
Sau đó, xóa file cấu hình Nginx cũ:
rm nginx-conf/nginx.conf
Tạo lại file cấu hình mới:
nano nginx-conf/nginx.conf
Thêm mã sau để chuyển hướng HTTP sang HTTPS và thêm thông tin SSL, giao thức, các tham số bảo mật. Hãy thay your_domain bằng tên miền của bạn:
server { listen 80; listen [::]:80; server_name your_domain www.your_domain; location ~ /.well-known/acme-challenge { allow all; root /var/www/html; } location / { rewrite ^ https://$host$request_uri? permanent; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name your_domain www.your_domain; server_tokens off; ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem; ssl_buffer_size 8k; ssl_dhparam /etc/ssl/certs/dhparam-2048.pem; ssl_protocols TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; ssl_ecdh_curve secp384r1; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8; location / { try_files $uri @nodejs; } location @nodejs { proxy_pass http://nodejs:8080; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # enable strict transport security only if you understand the implications } root /var/www/html; index index.html index.htm index.nginx-debian.html; }
Khối server HTTP xác định webroot cho các yêu cầu gia hạn chứng chỉ của Certbot tại thư mục .well-known/acme-challenge
. Nó cũng bao gồm chỉ thị rewrite, giúp chuyển hướng tất cả các yêu cầu HTTP đến thư mục gốc sang HTTPS. Khối server HTTPS kích hoạt SSL và HTTP/2. Để tìm hiểu thêm về cách HTTP/2 cải thiện giao thức HTTP và lợi ích của nó đối với hiệu suất trang web, bạn có thể tham khảo bài viết Cách Cấu Hình Nginx với Hỗ Trợ HTTP/2 trên Ubuntu 18.04.
Khối này cũng chứa một loạt các tùy chọn bảo mật để đảm bảo rằng bạn đang sử dụng các giao thức SSL và bộ mã hóa (ciphers) mới nhất, đồng thời kích hoạt OCSP Stapling.
OCSP Stapling giúp máy chủ cung cấp phản hồi có dấu thời gian từ tổ chức cấp chứng chỉ (CA) ngay trong quá trình bắt tay TLS ban đầu, giúp tăng tốc độ xác thực.Khối này cũng xác định chứng chỉ SSL, khóa riêng (private key) và thông tin Diffie-Hellman.
Cuối cùng, bạn đã di chuyển thông tin proxy_pass vào khối này, bao gồm:
Một location block với chỉ thị try_files, giúp ánh xạ các yêu cầu đến container ứng dụng Node.js.Một location block riêng biệt cho alias này, bao gồm các tiêu đề bảo mật (security headers) giúp bạn đạt xếp hạng A trên các bài kiểm tra bảo mật như SSL Labs và Security Headers.Các headers bảo mật được bao gồm là: X-Frame-Options
,X-Content-Type-Options
,Referrer-Policy
,Content-Security-Policy
,X-XSS-Protection
,Header HTTP Strict Transport Security (HSTS) đã bị ghi chú (comment) – chỉ kích hoạt nếu bạn hiểu rõ tác động của nó và đã đánh giá kỹ về chức năng preload của nó.
Sau khi chỉnh sửa xong, hãy lưu và đóng file.
Trước khi tạo lại service webserver
, bạn cần bổ sung một số thông tin vào định nghĩa service trong file docker-compose.yml
, bao gồm:
Thông tin cổng liên quan đến HTTPS và Định nghĩa volume cho Diffie-Hellman
Tiếp theo, mở file docker-compose.yml để cập nhật định nghĩa service webserver. Thêm ánh xạ cổng cho HTTPS và định nghĩa volume cho Diffie-Hellman key:
nano docker-compose.yml
Trong phần webserver của file docker-compose.yml, thay đổi như sau:
... webserver: image: nginx:latest container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - dhparam:/etc/ssl/certs depends_on: - nodejs networks: - app-network
Sau đó, thêm volume cho dhparam vào cuối file docker-compose.yml:
... volumes: ... webroot: ... dhparam: driver: local driver_opts: type: none device: /home/sammy/node_project/dhparam/ o: bind
Lưu và đóng file, sau đó tái tạo service webserver:
docker-compose up -d --force-recreate --no-deps webserver
Kiểm tra trạng thái các service với:
docker-compose ps
Output Name Command State Ports ---------------------------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 nodejs node app.js Up 8080/tcp webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
Cuối cùng, bạn có thể truy cập tên miền của mình để đảm bảo mọi thứ hoạt động như mong đợi. Mở trình duyệt và điều hướng đến: https://your_domain
. Hãy thay your_domain
bằng tên miền thực tế của bạn.
Một biểu tượng ổ khóa sẽ xuất hiện trên chỉ báo bảo mật của trình duyệt. Nếu muốn, bạn có thể truy cập trang kiểm tra máy chủ SSL Labs hoặc trang kiểm tra Security Headers để đánh giá mức độ bảo mật của trang web. Các tùy chọn cấu hình hiện tại sẽ giúp trang web của bạn đạt xếp hạng A trên bài kiểm tra SSL Labs. Để đạt xếp hạng A trên bài kiểm tra Security Headers, bạn cần bỏ ghi chú (uncomment) dòng Strict Transport Security (HSTS) trong file:
… location @nodejs { proxy_pass http://nodejs:8080; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # enable strict transport security only if you understand the implications } …
Một lần nữa, chỉ kích hoạt tùy chọn này nếu bạn hiểu rõ tác động của nó và đã đánh giá kỹ về chức năng preload của HSTS.
Bước 6 – Gia hạn chứng chỉ
Chứng chỉ Let’s Encrypt có thời hạn 90 ngày. Bạn có thể thiết lập một quá trình tự động gia hạn để tránh chứng chỉ hết hạn. Một cách là tạo một cron job sử dụng script để gia hạn chứng chỉ và tải lại cấu hình Nginx.
Tạo một script có tên ssl_renew.sh trong thư mục dự án:
nano ssl_renew.sh
Thêm đoạn mã sau vào script, đoạn mã này sẽ gia hạn chứng chỉ và tải lại cấu hình Nginx:
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --ansi never" DOCKER="/usr/bin/docker" cd /home/sammy/node_project/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af
Script này trước tiên gán binary docker-compose
vào một biến có tên COMPOSE
và chỉ định tùy chọn --no-ansi
, giúp chạy các lệnh docker-compose
mà không hiển thị các ký tự điều khiển ANSI. Sau đó, nó thực hiện tương tự với binary docker
. Cuối cùng, script chuyển đến thư mục ~/node_project
và chạy các lệnh docker-compose
sau:
-
docker-compose run
:-
Lệnh này sẽ khởi chạy một container certbot và ghi đè lệnh mặc định được định nghĩa trong service certbot.
-
Thay vì sử dụng subcommand
certonly
, nó sử dụng subcommandrenew
, giúp gia hạn các chứng chỉ sắp hết hạn. -
Ngoài ra, nó cũng bao gồm tùy chọn
--dry-run
để kiểm tra script mà không thực sự thực thi quá trình gia hạn.
-
-
docker-compose kill
:-
Lệnh này sẽ gửi tín hiệu SIGHUP đến container webserver, yêu cầu tải lại cấu hình Nginx mà không cần khởi động lại container.
-
-
Sau đó, script chạy lệnh
docker system prune
để xóa tất cả các container và image không sử dụng, giúp giải phóng dung lượng ổ đĩa.
Lưu file và làm cho nó có thể thực thi:
chmod +x ssl_renew.sh
Tiếp theo, mở file crontab của root để chạy script gia hạn theo khoảng thời gian nhất định:
sudo crontab -e
Nếu lần đầu tiên chỉnh sửa, chọn trình soạn thảo (ví dụ: nano). Sau đó, thêm dòng sau vào cuối file:
crontab no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/ed 2. /bin/nano <---- easiest 3. /usr/bin/vim.basic 4. /usr/bin/vim.tiny Choose 1-4 [2]: ...
Sau khi chỉnh sửa xong, hãy đóng file và làm cho nó có thể thực thi bằng lệnh:
chmod +x ssl_renew.sh
Tiếp theo, mở file crontab của root để thiết lập lịch chạy tự động cho script gia hạn chứng chỉ:
sudo crontab -e
Nếu đây là lần đầu tiên bạn chỉnh sửa file này, hệ thống sẽ yêu cầu bạn chọn một trình soạn thảo:
crontab no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/ed 2. /bin/nano <---- easiest 3. /usr/bin/vim.basic 4. /usr/bin/vim.tiny Choose 1-4 [2]: ...
Thiết lập cron job để kiểm tra gia hạn chứng chỉ mỗi 5 phút
Ở cuối file, thêm dòng sau:
crontab ... */5 * * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
Dòng này sẽ thiết lập công việc chạy mỗi 5 phút, giúp bạn kiểm tra xem quá trình gia hạn chứng chỉ có hoạt động đúng hay không. Ngoài ra, nó cũng tạo một file log cron.log để ghi lại các kết quả của cron job.
Kiểm tra xem gia hạn chứng chỉ có thành công không
Sau 5 phút, kiểm tra file log bằng lệnh:
tail -f /var/log/cron.log
Sau vài khoảnh khắc, nếu quá trình gia hạn giả lập thành công, bạn sẽ thấy output tương tự như sau:
Output - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Killing webserver ... done
Output … Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/your_domain/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Saving debug log to /var/log/letsencrypt/letsencrypt.log Killing webserver ... Killing webserver ... done Deleted Containers: 00cad94050985261e5b377de43e314b30ad0a6a724189753a9a23ec76488fd78 Total reclaimed space: 824.5kB
Để thoát khỏi chế độ theo dõi log, nhấn CTRL + C
.
Thiết lập cron job để chạy hàng ngày vào buổi trưa
Bây giờ, bạn có thể chỉnh sửa lại file crontab
để đặt lịch chạy hàng ngày. Nếu muốn chạy script vào 12 giờ trưa mỗi ngày, hãy thay đổi dòng cuối cùng thành:
crontab ... 0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
Bạn cũng có thể xóa tùy chọn --dry-run
khỏi script ssl_renew.sh để thực hiện quá trình gia hạn thực tế.
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --no-ansi" DOCKER="/usr/bin/docker" cd /home/sammy/node_project/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af
Cron job của bạn sẽ đảm bảo rằng chứng chỉ Let’s Encrypt không bị hết hạn bằng cách tự động gia hạn khi đủ điều kiện.
Bạn cũng có thể thiết lập log rotation bằng tiện ích Logrotate để xoay vòng và nén các file log, giúp quản lý dung lượng lưu trữ hiệu quả hơn.
Kết luận
Chúc mừng! Bạn đã thành công trong việc thiết lập và chạy một ứng dụng Node.js với reverse proxy Nginx trong môi trường container. Bạn cũng đã bảo mật ứng dụng của mình bằng chứng chỉ SSL cho tên miền và thiết lập một cron job tự động gia hạn chứng chỉ, giúp duy trì bảo mật lâu dài cho ứng dụng.
Khi cấu hình Nginx và Let’s Encrypt, một VPS đáng tin cậy là yếu tố then chốt. Mua VPS giá rẻ tại DataOnline để tiết kiệm chi phí mà vẫn đảm bảo tốc độ, bảo mật, và khả năng mở rộng cho ứng dụng Node.js của bạn.