راههای مختلفی برای ارتقای انعطافپذیری و امنیت یک اپلیکیشن Node.js وجود دارند. استفاده از یک پروکسی بازگشتی مانند Nginx به شما این امکان را میدهد که درخواست متعادلی را بارگذاری کنید، محتوای استاتیک داشته باشید و از قابلیت موسوم به «امنیت لایه انتقال» یا TLS استفاده نمایید. فعالسازی پروتکل رمزنگاریشده HTTPS در سرور موجب میشود که امنیت ارتباطی اپلیکیشن شما حفظ شود.
بکارگیری یک پروکسی بازگشتی همراه با TLS/SSL در کانتینرها شامل یک سری مراحل مستقیم در سیستمعامل میزبان میشود. به عنوان مثال، اگر گواهیها از طریق Let’s Encrypt برای اپلیکیشن در حال اجرا بر روی سرور بهدست میآیند، شما میبایست نرمافزار موردنیاز خود را مستقیماً بر روی هاست نصب کنید. کانتینرها شرایطی فراهم میکنند که بتوانید که از رویکرد متفاوت در این زمینه استفاده کنید. شما با استفاده از Docker Compose قادر خواهید بود که برای اپلیکیشن، وبسرور و کلاینت Certbot، کانتینر ایجاد کنید. کلاینت Certbot همان ابزاری است که امکان دسترسی به گواهیهای SSL را به شما میدهد. با دنبال کردن کردن مراحل زیر، میتوانید از مزایای یک جریان کاری انعطافپذیر و مقرونبهصرفه بهرهمند شوید.
در این آموزش، از یک اپلیکیشن Node.js همراه با یک پروکسی بازگشتی Nginx برای Docker Compose استفاده خواهیم کرد. گواهیهای TLS/SSL دامین مرتبط با اپلیکیشن بهدست میآیند و حتماً دقت میکنیم که رتبه امنیتی بالایی در SSL Labs داشته باشیم. نهایتاً یک عملیات cron برای تازهسازی گواهیها به گونهای تنظیم میکنیم که از امنیت دامین اطمینان حاصل گردد.
پیشنیازها
برای طیکردن مراحل این آموزش، موارد زیر موردنیاز هستند.
- یک سرور اوبونتو 04، یک کاربر غیر روت با دسترسیهای sudo و یک فایروال فعال. حتماً در این حالت، تنظیمات اولیه سرور را انجام داده باشید.
- نصب Docker و Docker Compose در سرور. برای اطلاعات بیشتر میتوانید به مقالات قبلی وبلاگ آریانت در این زمینه مراجعه کنید.
- یک دامین ثبتشده. در سراسر این مطلب از دامین نمونه com استفاده میشود. از برخی جاها مانند Freenom امکان دریافت دامین رایگان وجود دارد و در عین حال، میتوانید از مرجع ثبت دامین خودتان استفاده کنید.
- تنظیم این دو رکورد DNS نیز برای سرور؛ یک رکورد A با هدایت com به آدرس IP عمومی سرور و یک رکورد A با هدایت www.example.com باز هم به آدرس IP عمومی سرور.
گام ۱) شبیهسازی و تست اپلیکیشن Node
به عنوان گام نخست، منبع را با کد اپلیکیشن Node شبیهسازی میکنیم که شامل Dockerfile مورد استفاده برای ساخت ایمیج اپلیکیشن با Compose میشود. آزمایش اولیه به این صورت است که پس از ساخت اپلیکیشن، بدون استفاده از پروکسی بازگشتی یا SSL، با کمک فرمان docker run آن را اجرا میکنیم.
در دایرکتوری خانگی کاربری که دسترسی روت ندارد، منبع nodejs-image-demo را از گیتهاب دریافت کنید. این منبع شامل کدهای موردنیاز برای ساخت یک اپلیکیشن Node.js با داکر نیز میشود.
این منبع را داخل یک دایرکتوری با نام node_project ذخیره کنید.
[php]
git clone https://github.com/do-community/nodejs-image-demo.git node_project
[/php]
حالا مسیر را به دایرکتوری node_project تغییر دهید.
[php]
cd node_project
[/php]
این دایرکتوری شامل یک Dockerfile که در آن دستورالعملهای ساخت یک اپلیکیشن Node با استفاده از ایمیج Docker node:10 وجود دارد. میتوانید با استفاده از فرمان زیر به محتوای یک Dockerfile دسترسی داشته باشید.
[php]
cat Dockerfile
[/php]
خروجی
[php]
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" ]
[/php]
با این دستورالعمل ها یک ایمیج Node ساخته میشود که در آن کد پروژه از دایرتوری کنونی به کانتینر کپی شده و متعلقات آن نیز با فرمان npm install نصب میشوند. همچنین با جداسازی کپیهای package.json و package-lock.json از کپیهای ساید بخشهای کد اپلیکیشن، از مزایای کچینگ و لایهبندی ایمیج داکر استفاده میشود. نهایتاً این دستورالعملها مشخص میکنند که کانتینر با کاربری غیر روت با دسترسیهای مناسب تنظیمشده در کد اپلیکیشن و دایرکتوریهای node_modules اجرا میگردد.
برای تست اپلیکیشن بدون SSL میتوانید برای ساخت و برچسبزدن ایمیج از گزینه -t در فرمان docker build استفاده کنید. در این عنوان node-demo را برای ایمیج به کار میبریم، ولی شما میتوانید عنوان دلخواه خود را داشته باشید.
[php]
docker build -t node-demo .
[/php]
وقتی روند ساخت به پایان رسید، میتوانید با فرمان docker images لیستی از ایمیجهای خود در اختیار داشته باشید.
[php]
docker images
[/php]
در نتیجه، با خروجی زیر مواجه میشوید که تأیید ساخت ایمیج اپلیکیشن خواهد بود.
[php]
REPOSITORY TAG IMAGE ID CREATED SIZE
node-demo latest 23961524051d 7 seconds ago 73MB
node 10-alpine 8a752d5af4ce 3 weeks ago 70.7MB
[/php]
سپس یک کانتینر با فرمان docker run ایجاد میکنیم. سه گزینه در این فرمان به کار ما میآیند:
<ul>
<li>-p : این گزینه باعث انتشار پورت به کانتینر و ارجاع آن به یک پورت در هاست ما میشود. در اینجا از پورت 80 برای این منظور استفاده میکنیم، ولی شما میتوانید در صورت لزوم، شماره پورت دیگری وارد کنید.</li>
<li>-d : این گزینه موجب اجرای کانتینر در پسزمینه میشود.</li>
<li>–name: با استفاده از این گزینه میتوانیم یه عنوان خاص به کانتینر اختصاص دهیم.</li>
</ul>
فرمان زیر را برای ساخت کانتینر اجرا کنید.
[php]
docker run –name node-demo -p 80:8080 -d node-demo
[/php]
با استفاده فرمان docker ps، کانتینرهای در حال اجرا را بررسی نمایید.
[php]
docker ps
[/php]
در خروجی متوجه میشوید که کانتینر اپلیکیشن شما در حال اجراست.
[php]
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
[/php]
اکنون میتوانید با رفتن به آدرس دامین، از نصب خود مطمئن شوید. در نتیجه، با صفحه لندینگ اپلیکیشن به صورت زیر روبرو خواهید شد.

صفحه لندینگ اپلیکیشن Node
بعد از تست اپلیکیشن میتوانید کانتینر را متوقف کرده و ایمیجها را حذف کنید. دوباره فرمان docker ps را اجرا کنید تا شناسه کانتینر یا CONTAINER ID را در اختیار داشته باشید.
[php]
docker ps
[/php]
خروجی
[php]
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
[/php]
با فرمان docker stop کانتینر را متوقف نمایید. حتماً دقت کنید که شناسه کانتینر خودتان را در این فرمان وارد کنید.
[php]
docker stop 4133b72391da
[/php]
حالا میتوانید کانتینر متوقفشده و تمام ایمیجها را حذف کنید.
[php]
docker system prune -a
[/php]
برای تأیید حذف این موارد، کلید حرف Y را فشار دهید. به خاطر داشته باشید که این کار باعث پاکشدن حافظه نهان داکر نیز میشود.
پس از تست ایمیج اپلیکیشن میتوانید به سراغ سایر قسمتها با استفاده از Docker Compose بروید.
گام ۲) تعریف تنظیمات وبسرور
پس از قرارگیری Dockerfile در جای مناسب، میتوانیم یک فایل تنظیمات برای اجرای کانتینر Nginx بسازیم. کار با یک تنظیمات حداقلی شامل نام دامین، مسیر ریشه داکیومنت، اطلاعات پروکسی و یک بلوک موقعیت برای هدایت درخواست Certbot به دایرکتوری .well-known آغاز میشود. در دایرکتوری .well-known یک فایل موقتی برای تأیید اعتبار DNS دامین به سرور قرار میگیرد.
یک دایرکتوری در دایرکتوری کنونی پروژه برای فایل تنظیمات ایجاد میکنیم.
[php]
mkdir nginx-conf
[/php]
این فایل را با استفاده از ویرایشگر متنی nano یا هر ویرایشگر دلخواه دیگری باز کنید.
[php]
nano nginx-conf/nginx.conf
[/php]
بلوک سرور زیر را به درخواستهای کاربر پروکسی در کانتینر اپلیکیشن Node اضافه کنید. همچنین این کار برای هدایت درخواستهای Certbot به دایرکتوری .well-known صورت میگیرد. حتماً دقت کنید که example.com را با عنوان دامین خودتان جایگزین نمایید.
[php]
server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name example.com www.example.com;
location / {
proxy_pass http://nodejs:8080;
}
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
}
[/php]
این بلوک سرور به ما اجازه میدهد که کانتینر Nginx را به عنوان یک پروکسی بازگشتی اجرا کنیم. این کار باعث میشود که درخواست به کانتینر اپلیکیشن Node هدایت شوند. همچنین به این وسیله میتوانیم از پلاگین وبروت Certbot برای بهدست آوردن گواهیهای دامین خود استفاده کنیم. این پلاگین به روش تأییدیه HTTP-01 وابسته است. در این روش، از یک درخواست HTTP برای اثبات توانایی دسترسی Certbot به منابع سرور مربوط به دامین استفاده میشود.
وقتی ویرایش به اتمام رسید، فایل را ذخیره کرده و ببندید.
پس از کاملشدن جزئیات تنظیمات وبسرور میتوانیم به سراغ ساخت فایل docker-compose.yml برویم. این فایل به ما اجازه میدهد که سرویسهای اپلیکیشن و همینطور کانتینر Certbot را بسازیم.
گام ۳) ایجاد فایل Docker Compose
فایل docker-compose.yml سرویسهای ما، شامل اپلیکیشن Node و وبسرور را تعریف میکند. جزئیاتی مانند حجمها، شبکهها و اطلاعات پورت در این فایل مشخص میشوند؛ جزئیاتی که برای رد و بدل کردن اعتبارهای SSL بین کانتینرها حیاتی خواهند بود. همچنین این فایل برای تعریف اجرای فرمانهای خاص بعد از ساخت کانتینرها مورد استفاده قرار میگیرد. این فایل یک منبع مرکزی برای تعریف نحوه همکاری سرویسهای ما با یکدیگر است.
این فایل در دایرکتوری کنونی باز کنید.
[php]
nano docker-compose.yml
[/php]
ابتدا سرویس اپلیکیشن را تعریف نمایید.
[php]
version: ‘3’
services:
nodejs:
build:
context: .
dockerfile: Dockerfile
image: nodejs
container_name: nodejs
restart: unless-stopped
[/php]
تعریف سرویس nodejs شامل موارد زیر است:
- build: این عبارت تعریفکننده گزینههای تنظیمات شامل context و dockerfile است. این گزینهها در هنگام ساخت ایمیج اپلیکیشن توسط Compose به کار گرفته میشوند. اگر قصدتان استفاده از ایمیجهای آماده از منبعی مانند Docker Hub است، می توانید به جای آن، از image instruction استفاده کنید که شامل اطلاعاتی در مورد نام کاربری، منبع و برچسب ایمیج خواهد بود.
- context: در این بخش، کانتکست ساخت برای ساخت ایمیج اپلیکیشن تعریف میشود. در اینجا، از دایرکتوری کنونی پروژه استفاده میشود.
- dockerfile: در اینجا Dockerfile مورد استفاده Compose برای ساخت مشخص میشود. یعنی همان Dockerfile که در گام اول داشتیم.
- image, container_name: عنوانهای ایمیج و کانتینر
- restart: روند راهاندازی دوباره و ریاستارت در اینجا تعریف میشود. حالت پیشفرض به صورت «غیرفعال» و گزینه no است. با این وجود باید کانتینر را برای راهاندازی دوباره تنظیم کنیم، مگر اینکه متوقف شده باشد.
به خاطر داشته باشید که در این سرویس bind mount نداریم. به این دلیل که تنظیمات ما متمرکز بر «بهکارگیری» و نه «توسعه» است.
به منظور فعالسازی ارتباط بین اپلیکیشن و کانتینرهای وبسرور، یک شبکه بریج با نام app-network درنظر میگیریم.
[php]
services:
nodejs:
…
networks:
– app-network
[/php]
یک شبکه بریج تعریفشده توسط کاربر باعث ایجاد ارتباط بین کانتینرها در هاست Docker میشود. در نتیجه، ترافیک و ارتباط درون اپلیکیشن به راحتی انجام خواهد گرفت. چرا که در این حالت، تمام پورتها بین کانتینرها در شبکه بریج باز میشوند و در همین حال، دنیال خارج نمیتواند به این پورتها دسترسی داشته باشد. به این ترتیب، شما میتوانید پورتهای دلخواه خود را برای سرویسهای نهایی انتخاب کنید.
در مرحله بعد، سرویس وبسرور را تعریف میکنیم.
[php]
…
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
[/php]
برخی از تنظیماتی که قبلاً برای سرویس nodejs تعریف کردیم، به همان صورت باقی میمانند. با این وجود، برخی تغییرات را به صورت زیر نیز انجام دادهایم:
- image: این گزینه باعث میشود که Compose آخرین نسخه ایمیج Nginx بر پایه Alpine را از Docker Hub دریافت نماید.
- ports: در اینجا، پورت 80 برای فعالسازی گزینههای تنظیمات Nginx مورد استفاده قرار میگیرد.
همچنین حجمها و bind mount های زیر را تعریف کردهایم:
- web-root:/var/www/html: با این گزینه بخشهای استاتیک سایت را اضافه میکنیم. این بخشها در حجمی با عنوان web-root در دایرکتوری /var/www/htmlواقع در کانتینر کپی میشوند.
- ./nginx-conf:/etc/nginx/conf.d: این گزینه موجب ارتباط bind mount بین دایرکتوری تنظیمات Nginx در هاست با دایرکتوری متناظر آن در کانتینر میشود. در نتیجه، هر گونه تغییری که در فایلهای هاست ایجاد میکنیم، در کانتینر نیز منعکس خواهد شد.
- certbot-etc:/etc/letsencrypt: با این گزینه، گواهیها و کلیدهای Let’s Encrypt دامین در دایرکتوری مربوطه در کانتینر نیز مورد استفاده قرار خواهند گرفت.
- certbot-var:/var/lib/letsencrypt: این گزینه دایرکتوری پیشفرض Let’s Encrypt را با دایرکتوری متناسب با آن در کانتینر هماهنگ میکند.
سپس باید تنظیمات را برای کانتینر certbot اضافه کنیم. حتماً دقت کنید که اطلاعات مربوط به دامین و ایمیل را به شکل مناسب برای وبسایت خودتان وارد کنید.
[php]
…
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@example.com –agree-tos –no-eff-email –staging -d example.com -d www.example.com
[/php]
این تعریف موجب میشود که Compose ایمیج certbot/certbot را از Docker Hub دریافت کند. همچنین از حجمهای یاد شده برای به اشتراک گذاری منابع با کانتینر Nginx استفاده میشود. این منابع شامل مواردی چون گواهیها و کلیدهای دامین در certbot-etc، دایرکتوری Let’s Encrypt در certbot-var و کد اپلیکیشن در web-root میشوند.
همچنین از depends_on برای مشخصکردن این موضوع که کانتینر certbot میبایست حتماً بعد از اجرای سرویس وبسرور، شروع به کار کند، استفاده کردهایم.
به همین ترتیب، از گزینه command استفاده کردهایم که موجب اجرای فرمان بعد از شروع به کار کانتینر میشود. آیتمهای زیر در این فرمان به کار رفتهاند:
- –webroot: استفاده از پلاگین webroot برای جانمایی فایلها در فولدر webroot جهت تأییدیه.
- –webroot-path: مسیر دایرکتوری webroot
- –email: ایمیل ترجیحی برای ثبتنام و بازیابی
- –agree-tos: موافقت با توافقنامه عضویت ACME
- –no-eff-email: عدم به اشتراکگذاری ایمیل با بنیاد الکترونیک فرونتیر یا EFF. در صورت تمایل میتوانید این مورد را حذف کنید.
- –staging: این گزینه تمایل شما را برای استفاده از محیط استیجینگ Let’s Encrypt برای دریافت گواهیهای آزمایشی نشان میدهد. استفاده از این گزینه این امکان را برایتان ایجاد میکند و گزینههای مختلف تنظیمات را امتحان کنید و از برخی محدودیتهای درخواست دامین در امان بمانید.
- -d: برای مشخصکردن عنوان دامین موردنظر به منظور ثبت درخواست. در اینجا، از آدرسهای com و www.example.com استفاده کردهایم. حتماً دقت کنید که موارد مرتبط با دامین خود را جایگزین کنید.
به عنوان گام نهایی، تعاریف حجم و شبکه را اضافه میکنیم. در اینجا نیز باید کاربر غیر روت خودتان را وارد کنید.
[php]
…
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
[/php]
حجمهای یاد شده شامل گواهی Certbot و دایرکتوری کاری و همینطور، حجم مورد استفاده برای بخشهای استاتیک وبسایت، یعنی web-root میشوند. در بسیاری از موارد، درایور پیشفرض برای حجمهای Docker به صورت local driver است که در لینوکس گزینههای مشابه با فرمان mount را قبول میکند. در نتیجه، میتوانیم با استفاده از driver_opts، فهرستی از گزینههای درایور را بین دایرکتوری هاست و حجم رانتایم در اختیار داشته باشیم. در این حالت، محتوای دایرکتوری میتواند بین کانتینرها به اشتراک گذاشته شود.
نهایتاً فایل docker-compose.yml تکمیلشده به صورت زیر خواهد بود:
[php]
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@example.com –agree-tos –no-eff-email –staging -d example.com -d www.example.com
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
[/php]
بعد از تعریف سرویسها، همهچیز برای شروع به کار کانتینرها و آزمایش درخواستهای گواهی آماده خواهد بود.
گام ۴) دریافت اعتبار و گواهیهای SSL
میتوانیم با استفاده از docker-compose up کانتینرهای خود را اجرا کنیم. در نتیجه این فرمان، کانتینرهای ما ایجاد و اجرا میشوند و همین طور، سرویسها به ترتیبی که مشخص کردهایم، شروع به کار میکنند. در صورت درخواستهای دامین ما موفقیتآمیز باشند، وضعیت درست خروج را مشاهده خواهیم کرد و همینطور گواهیها در فولدر /etc/letsencrypt/live در کانتینر webserver قرار میگیرند.
سرویسها را با فرمان docker-compose up و گزینه -d ایجاد میکنیم. این گزینه موجب میشود که کانتینرهای nodejs و webserver در پسزمینه اجرا شوند.
[php]
docker-compose up -d
[/php]
در خروجی مشاهده میکنید که سرویسها بهدرستی ایجاد شدهاند.
[php]
Creating nodejs … done
Creating webserver … done
Creating certbot … done
[/php]
حالا با کمک فرمان docker-compose ps وضعیت سرویسهای خود را بررسی کنید.
[php]
docker-compose ps
[/php]
در صورتی که همه موارد با موفقیت به پیش رفته باشند، سرویسهای nodejs و webserver با وضعیت فعال یا Up و کانتینر certbot با پیام خروج صفر نشان داده میشوند.
[php]
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
[/php]
در صورتی که وضعیت به غیر از Up برای سرویسها و یا وضعیت خروجی غیر از صفر برای کانتینر certbot وجود داشته باشد، حتماً وضعیت سرویس را با فرمان docker-compose logs بررسی کنید.
[php]
docker-compose logs service_name
[/php]
همچنین با استفاده از فرمان docker-compose exec میتوانید وضعیت اعتبار گواهی را در کانتینر webserver بررسی کنید.
[php]
docker-compose exec webserver ls -la /etc/letsencrypt/live
[/php]
در صورتی که درخواست موفقیتآمیز باشد، با خروجی زیر مواجه خواهید شد.
[php]
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 example.com
[/php]
حالا که از موفقبودن درخواست خود مطمئن شدید، میتوانید تعریف سرویس certbot را برای برداشتن گزینه –staging ویرایش کنید.
برای این منظور، فایل docker-compose.yml را باز کنید.
[php]
nano docker-compose.yml
[/php]
بخش مربوط به تعریف سرویس certbot را پیدا کرده و گزینه –staging را با –force-renewal جایگزین کنید. در حال حاضر، تعریف سرویس certbot به صورت زیر خواهد بود.
[php]
…
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@example.com –agree-tos –no-eff-email –force-renewal -d example.com -d www.example.com
…
[/php]
حالا میتوانید فرمان docker-compose up را اجرا کنید تا کانتینر certbot و حجمهای مربوط به آن ایجاد شوند. همچنین از گزینه –no-deps برای عبور ار مرحله شروع سرویس webserver استفاده میکنیم؛ چرا که این سرویس هماکنون در حال اجراست.
[php]
docker-compose up –force-recreate –no-deps certbot
[/php]
خروجی نشاندهنده موفقیتآمیز بودن درخواست گواهی شماست.
[php]
certbot | IMPORTANT NOTES:
certbot | – Congratulations! Your certificate and chain have been saved at:
certbot | /etc/letsencrypt/live/example.com/fullchain.pem
certbot | Your key file has been saved at:
certbot | /etc/letsencrypt/live/example.com/privkey.pem
certbot | Your cert will expire on 2019-03-26. To obtain a new or tweaked
certbot | version of this certificate in the future, simply run certbot
certbot | again. To non-interactively renew *all* of your certificates, run
certbot | "certbot renew"
certbot | – Your account credentials have been saved in your Certbot
certbot | configuration directory at /etc/letsencrypt. You should make a
certbot | secure backup of this folder now. This configuration directory will
certbot | also contain certificates and private keys obtained by Certbot so
certbot | making regular backups of this folder is ideal.
certbot | – If you like Certbot, please consider supporting our work by:
certbot |
certbot | Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
certbot | Donating to EFF: https://eff.org/donate-le
certbot |
certbot exited with code 0
[/php]
پس از جانمایی گواهیها، می توانیم به سراغ اصلاح تنظیمات Nginx و وارد کردن SSL برویم.
گام ۵) اصلاح تنظیمات و سرویس وبسرور
فعالسازی SSL در تنظیمات Nginx شامل اضافهکردن یک ارجاع HTTP به HTTPS و مشخصکردن محل کلید و گواهی SSL است. همچنین این موضوع شامل مشخصکردن گروه Diffie-Hellman به منظور «محرمانگی پیشرو» یا PFF نیز میشود.
در اینجا میخواهیم سرویس webserver را به منظور اضافهکردن این موارد، بازسازی کنیم. بر این اساس، ابتدا باید آن را متوقف کنیم.
[php]
docker-compose stop webserver
[/php]
اول از همه، یک دایرکتوری در دایرکتوری کنونی پروژه ایجاد کنید تا کلید Diffie-Hellman در آن قرار گیرد.
[php]
mkdir dhparam
[/php]
کلید خود را با فرمان openssl ایجاد کنید.
[php]
sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048
[/php]
ایجاد کلیدها به چند دقیقه زمان احتیاج خواهد داشت.
به منظور اضافهکردن اطلاعات مربوط به Diffie-Hellman و SSL به تنظیمات Nginx، قبل از هر کاری، فایل تنظیمات Nginx را که قبلاً ایجاد کرده بودید، حذف کنید.
[php]
rm nginx-conf/nginx.conf
[/php]
یک نسخه دیگر از فایل باز کنید.
[php]
nano nginx-conf/nginx.conf
[/php]
کد زیر را برای هدایت HTTP به HTTPS و اضافهکردن اعتبارها، پروتکلهای و هدرهای امنیتی SSL اضافه کنید. حتماً به یاد داشته باشید که آدرس example.com را با دامین خودتان جایگزین کنید.
[php]
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
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 example.com www.example.com;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
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;
}
[/php]
بلوک HTTP server مشخص میکند که webroot برای درخواستهای تازهسازی Certbot در دایرکتوری .well-known/acme-challenge مورد استفاده قرار گیرد. همچنین این بلوک شامل یک ارجاعدهنده درخواستهای HTTP دایرکتوری روت به HTTPS میشود.
بلوک HTTPS server موجب فعالسازی ssl و http2 میشود. همچنین این بلوک شامل یک سری گزینهها برای اطمینان از کاربرد آخرین نسخه پروتکلها و رمزنگاریهای SSL و فعالبودن استپلینگ OSCP است. استپلینگ OSCP این امکان را به شما میدهد که در طول فرآیند اولیه TLS handshake، بتوانید پاسخ بر اساس فواصل زمانی از مرجع صدور گواهی داشته باشید. این کار مسلماً باعث افزایش سرعت فرآیند تأییدیه میشود.
این بلوک همچنین موقعیت اعتبارها و کلید SSL و Diffie-Hellman را مشخص میکند.
نهایتاً اطلاعات عبور پراکسی را در این بلوک وارد کردهایم که شامل یک بلوک location با پارامتر try_files میشود. این پارامتر درخواستها را به کانتینر اپلیکیشن Node.js ما هدایت میکند. همینطور یک بلوک location شامل هدرهای امنیتی که موجب کسب رتبه A از مراجعی مانند SSL Labs و وبسایت Security Headers میشود.
وقتی کار ویرایشتان تمام شد، فایل را ذخیره کرده و آن را ببندید.
قبل از اینکه سرویس webserver را دوباره بازسازی کنیم، باید برخی موارد را در تعریف سرویس در فایل docker-compose.yml اضافه نماییم؛ از جمله اطلاعات مرتبط با پورت HTTPS و همینطور تعریف حجم Diffie-Hellman.
بر این اساس، فایل را باز کنید.
[php]
nano docker-compose.yml
[/php]
در بخش تعریف سرویس webserver، ارجاع پورت و حجم با عنوان dhparam را اضافه کنید.
[php]
…
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
[/php]
حالا حجم dhparam را به تعریف حجمهای خود اضافه نمایید.
[php]
…
volumes:
…
dhparam:
driver: local
driver_opts:
type: none
device: /home/sammy/node_project/dhparam/
o: bind
[/php]
همانند حجم web-root، حجم dhparam نیز کلید Diffie-Hellman ذخیرهشده در هاست را با کانتینر webserver مرتبط میکند.
پس از انجام ویرایش، فایل را ذخیره کرده و ببندید.
اکنون سرویس webserver را بازسازی کنید.
[php]
docker-compose up -d –force-recreate –no-deps webserver
[/php]
با فرمان docker-compose ps سرویسها را بررسی کنید.
[php]
docker-compose ps
[/php]
در خروجی میبایست اجرای سرویسهای nodejs و webserver را مشاهده کنید.
[php]
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
[/php]
نهایتاً از دامین خود بازدید کنید تا از درستبودن تمام موارد مطابق انتظار مطمئن شوید. در نتیجه، صفحه لندینگ زیر برایتان نمایش داده میشود.

صفحه لندینگ اپلیکیشن Node
همچنین باید آیکون قفل را در نمایشگر امنیتی مرورگر خود ببینید. در صورت تمایل، می توانید به صفحه لندینگ SSL Labs Server Test و یا سرور تست Security Headers بروید. تنظیماتی که قبلاً اِعمال کردیم، میتوانند در هر دوی این صفحات رتبه A را برایتان ایجاد کنند.
گام ۶) تازهسازی گواهیها
گواهیهای Let’s Encrypt برای مدت ۹۰ روز معتبر خواهند بود. بر این اساس، میبایست یک الگوی تازهسازی گواهیها تنظیم کنید تا گواهیهای منقضیشده مورد استفاده قرار نگیرند. یکی از روشهای برای این منظور، ابزار زمانبندی انجام کارها موسوم به cron است. در اینجا ما با استفاده از یک اسکریپت، یک وظیفه cron برای تازهسازی گواهیها و بارگذاری دوباره تنظیمات Nginx ایجاد میکنیم.
برای این منظور، یک اسکریپت با نام ssl_renew.sh در دایرکتوری پروژه باز کنید.
[php]
nano ssl_renew.sh
[/php]
حالا کد زیر را به اسکریپت اضافه کنید.
[php]
#!/bin/bash
COMPOSE=”/usr/local/bin/docker-compose –no-ansi”
DOCKER=”/usr/bin/docker”
cd /home/ammy/node_project/
$COMPOSE run certbot renew –dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
[/php]
این اسکریپت ابتدا کد باینری docker-compose را به صورت یک متغیر با نام COMPOSE درنظر میگیرد. همچنین استفاده از گزینه –no-ansi باعث میشود که فرمانهای docker-compose بدون کنترل کاراکترهای ANSI اجرا شوند. همین کار با باینری docker نیز انجام میگیرد. نهایتاً دایرکتوری به ~/node_project تغییر پیدا میکند و فرمانهای docker-compose به صورت زیر اجار میگردند.
- docker-compose run: اجرای یک کانتینر certbot و بازیابی فرمان تعریفشده در سرویس certbot. به جای استفاده از زیرفرمان certonly، از renew استفاده میکنیم. در نتیجه، گواهیهایی که نزدیک به تاریخ انقضای خود هستند، تازهسازی میشوند. گزینه –dry-run برای تست اسکریپت وارد شده است.
- docker-compose kill: یک سیگنال SIGHUP به کانتینر webserver ارسال میشود تا تنظیمات Nginx بارگذاری شوند.
سپس اجرای فرمان docker system prune برای حذف کلیه کانتینرها و ایمیجهای بلااستفاده انجام میشود.
وقتی کار ویرایش به اتمام رسید، فایل را ببندید. حالا آن را به فایل اجرایی تبدیل کنید
[php]
chmod +x ssl_renew.sh
[/php]
حالا فایل روت crontab را باز کنید تا اسکریپت تازهسازی در فواصل مشخص اجرا شود.
[php]
sudo crontab -e
[/php]
در صورتی که برای اولین بار این فایل را ویرایش میکنید، از شما درخواست میشود که یک ویرایشگر انتخاب کنید.
[php]
no crontab for root – using an empty one
Select an editor. To change later, run ‘select-editor’.
<ol>
<li>/bin/ed</li>
<li>/bin/nano <—- easiest</li>
<li>/usr/bin/vim.basic</li>
<li>/usr/bin/vim.tiny</li>
</ol>
Choose 1-4 [2]:
…
[/php]
در پایین این فایل، خط زیر را اضافه کنید.
[php]
…
*/5 * * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
[/php]
در نتیجه، این کار هر پنج دقیقه یک بار انجام میشود و میتوانید درخواست تازهسازی را در این فاصله بررسی کنید. همچنین یک فیال ثبت تاریخچه با نام cron.log برای این کار ایجاد کردهایم.
بعد از پنج دقیقه، فایل cron.log را برای موفقبودن تازهسازی گواهی بررسی کنید.
[php]
tail -f /var/log/cron.log
[/php]
در نتیجه، میبایست خروجیای مشابه زیر مشاهده کنید.
[php]
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
** 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/example.com/fullchain.pem (success)
** DRY RUN: simulating ‘certbot renew’ close to cert expiry
** (The test certificates above have not been saved.)
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
Killing webserver … done
[/php]
حالا میتوانید فایل crontab برای تنظیم فواصل روزانه تازهسازی گواهیها تغییر دهید. برای اجرای اسکریپت در هر بعدازظهر، میتوانید اصلاح زیر را انجام دهید.
[php]
…
0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
[/php]
ضمن اینکه حالا میتوانید گزینه –dry-run را از اسکریپت ssl_renew.sh حذف کنید.
[php]
#!/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
[/php]
این وظیفه cron باعث میشود که از عدم منقضیشدن گواهیهای Let’s Encrypt اطمینان حاصل گردد. همچنین میتوانید با استفاده از ابزاری مانند Logrotate حجم کمتری برای فایلهای ثبت تاریخچه داشته باشید.
جمعبندی
در اینجا از کانتینرها برای تنظیم و اجرای یک اپلیکیشن Node همراه با پروکسی بازگشتی Nginx استفاده کردیم. همچنین گواهیهای امنیتی SSL برای دامین اپلیکیشن و یک وظیفه cron برای تازهسازی این گواهیها تنظیم شد. امیدواریم که این مطلب نیز مورد استفاده شما قرار گرفته باشد و در ادامه مطالب وبلاگ آریانت نیز با ما همراه باشید.