راههای مختلفی برای ارتقای انعطافپذیری و امنیت یک اپلیکیشن 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 ذخیره کنید.
git clone https://github.com/do-community/nodejs-image-demo.git node_project
حالا مسیر را به دایرکتوری node_project تغییر دهید.
cd node_project
این دایرکتوری شامل یک Dockerfile که در آن دستورالعملهای ساخت یک اپلیکیشن Node با استفاده از ایمیج Docker node:10 وجود دارد. میتوانید با استفاده از فرمان زیر به محتوای یک Dockerfile دسترسی داشته باشید.
cat Dockerfile
خروجی
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" ]
با این دستورالعمل ها یک ایمیج Node ساخته میشود که در آن کد پروژه از دایرتوری کنونی به کانتینر کپی شده و متعلقات آن نیز با فرمان npm install نصب میشوند. همچنین با جداسازی کپیهای package.json و package-lock.json از کپیهای ساید بخشهای کد اپلیکیشن، از مزایای کچینگ و لایهبندی ایمیج داکر استفاده میشود. نهایتاً این دستورالعملها مشخص میکنند که کانتینر با کاربری غیر روت با دسترسیهای مناسب تنظیمشده در کد اپلیکیشن و دایرکتوریهای node_modules اجرا میگردد.
برای تست اپلیکیشن بدون SSL میتوانید برای ساخت و برچسبزدن ایمیج از گزینه -t در فرمان docker build استفاده کنید. در این عنوان node-demo را برای ایمیج به کار میبریم، ولی شما میتوانید عنوان دلخواه خود را داشته باشید.
docker build -t node-demo .
وقتی روند ساخت به پایان رسید، میتوانید با فرمان docker images لیستی از ایمیجهای خود در اختیار داشته باشید.
docker images
در نتیجه، با خروجی زیر مواجه میشوید که تأیید ساخت ایمیج اپلیکیشن خواهد بود.
REPOSITORY TAG IMAGE ID CREATED SIZE node-demo latest 23961524051d 7 seconds ago 73MB node 10-alpine 8a752d5af4ce 3 weeks ago 70.7MB
سپس یک کانتینر با فرمان docker run ایجاد میکنیم. سه گزینه در این فرمان به کار ما میآیند:
<ul>
<li>-p : این گزینه باعث انتشار پورت به کانتینر و ارجاع آن به یک پورت در هاست ما میشود. در اینجا از پورت 80 برای این منظور استفاده میکنیم، ولی شما میتوانید در صورت لزوم، شماره پورت دیگری وارد کنید.</li>
<li>-d : این گزینه موجب اجرای کانتینر در پسزمینه میشود.</li>
<li>–name: با استفاده از این گزینه میتوانیم یه عنوان خاص به کانتینر اختصاص دهیم.</li>
</ul>
فرمان زیر را برای ساخت کانتینر اجرا کنید.
docker run --name node-demo -p 80:8080 -d node-demo
با استفاده فرمان docker ps، کانتینرهای در حال اجرا را بررسی نمایید.
docker ps
در خروجی متوجه میشوید که کانتینر اپلیکیشن شما در حال اجراست.
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
اکنون میتوانید با رفتن به آدرس دامین، از نصب خود مطمئن شوید. در نتیجه، با صفحه لندینگ اپلیکیشن به صورت زیر روبرو خواهید شد.
بعد از تست اپلیکیشن میتوانید کانتینر را متوقف کرده و ایمیجها را حذف کنید. دوباره فرمان docker ps را اجرا کنید تا شناسه کانتینر یا CONTAINER ID را در اختیار داشته باشید.
docker ps
خروجی
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
با فرمان docker stop کانتینر را متوقف نمایید. حتماً دقت کنید که شناسه کانتینر خودتان را در این فرمان وارد کنید.
docker stop 4133b72391da
حالا میتوانید کانتینر متوقفشده و تمام ایمیجها را حذف کنید.
docker system prune -a
برای تأیید حذف این موارد، کلید حرف Y را فشار دهید. به خاطر داشته باشید که این کار باعث پاکشدن حافظه نهان داکر نیز میشود.
پس از تست ایمیج اپلیکیشن میتوانید به سراغ سایر قسمتها با استفاده از Docker Compose بروید.
گام ۲) تعریف تنظیمات وبسرور
پس از قرارگیری Dockerfile در جای مناسب، میتوانیم یک فایل تنظیمات برای اجرای کانتینر Nginx بسازیم. کار با یک تنظیمات حداقلی شامل نام دامین، مسیر ریشه داکیومنت، اطلاعات پروکسی و یک بلوک موقعیت برای هدایت درخواست Certbot به دایرکتوری .well-known آغاز میشود. در دایرکتوری .well-known یک فایل موقتی برای تأیید اعتبار DNS دامین به سرور قرار میگیرد.
یک دایرکتوری در دایرکتوری کنونی پروژه برای فایل تنظیمات ایجاد میکنیم.
mkdir nginx-conf
این فایل را با استفاده از ویرایشگر متنی nano یا هر ویرایشگر دلخواه دیگری باز کنید.
nano nginx-conf/nginx.conf
بلوک سرور زیر را به درخواستهای کاربر پروکسی در کانتینر اپلیکیشن Node اضافه کنید. همچنین این کار برای هدایت درخواستهای Certbot به دایرکتوری .well-known صورت میگیرد. حتماً دقت کنید که example.com را با عنوان دامین خودتان جایگزین نمایید.
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; } }
این بلوک سرور به ما اجازه میدهد که کانتینر Nginx را به عنوان یک پروکسی بازگشتی اجرا کنیم. این کار باعث میشود که درخواست به کانتینر اپلیکیشن Node هدایت شوند. همچنین به این وسیله میتوانیم از پلاگین وبروت Certbot برای بهدست آوردن گواهیهای دامین خود استفاده کنیم. این پلاگین به روش تأییدیه HTTP-01 وابسته است. در این روش، از یک درخواست HTTP برای اثبات توانایی دسترسی Certbot به منابع سرور مربوط به دامین استفاده میشود.
وقتی ویرایش به اتمام رسید، فایل را ذخیره کرده و ببندید.
پس از کاملشدن جزئیات تنظیمات وبسرور میتوانیم به سراغ ساخت فایل docker-compose.yml برویم. این فایل به ما اجازه میدهد که سرویسهای اپلیکیشن و همینطور کانتینر Certbot را بسازیم.
گام ۳) ایجاد فایل Docker Compose
فایل docker-compose.yml سرویسهای ما، شامل اپلیکیشن Node و وبسرور را تعریف میکند. جزئیاتی مانند حجمها، شبکهها و اطلاعات پورت در این فایل مشخص میشوند؛ جزئیاتی که برای رد و بدل کردن اعتبارهای SSL بین کانتینرها حیاتی خواهند بود. همچنین این فایل برای تعریف اجرای فرمانهای خاص بعد از ساخت کانتینرها مورد استفاده قرار میگیرد. این فایل یک منبع مرکزی برای تعریف نحوه همکاری سرویسهای ما با یکدیگر است.
این فایل در دایرکتوری کنونی باز کنید.
nano docker-compose.yml
ابتدا سرویس اپلیکیشن را تعریف نمایید.
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped
تعریف سرویس nodejs شامل موارد زیر است:
- build: این عبارت تعریفکننده گزینههای تنظیمات شامل context و dockerfile است. این گزینهها در هنگام ساخت ایمیج اپلیکیشن توسط Compose به کار گرفته میشوند. اگر قصدتان استفاده از ایمیجهای آماده از منبعی مانند Docker Hub است، می توانید به جای آن، از image instruction استفاده کنید که شامل اطلاعاتی در مورد نام کاربری، منبع و برچسب ایمیج خواهد بود.
- context: در این بخش، کانتکست ساخت برای ساخت ایمیج اپلیکیشن تعریف میشود. در اینجا، از دایرکتوری کنونی پروژه استفاده میشود.
- dockerfile: در اینجا Dockerfile مورد استفاده Compose برای ساخت مشخص میشود. یعنی همان Dockerfile که در گام اول داشتیم.
- image, container_name: عنوانهای ایمیج و کانتینر
- restart: روند راهاندازی دوباره و ریاستارت در اینجا تعریف میشود. حالت پیشفرض به صورت «غیرفعال» و گزینه no است. با این وجود باید کانتینر را برای راهاندازی دوباره تنظیم کنیم، مگر اینکه متوقف شده باشد.
به خاطر داشته باشید که در این سرویس bind mount نداریم. به این دلیل که تنظیمات ما متمرکز بر «بهکارگیری» و نه «توسعه» است.
به منظور فعالسازی ارتباط بین اپلیکیشن و کانتینرهای وبسرور، یک شبکه بریج با نام app-network درنظر میگیریم.
services: nodejs: ... networks: - app-network
یک شبکه بریج تعریفشده توسط کاربر باعث ایجاد ارتباط بین کانتینرها در هاست Docker میشود. در نتیجه، ترافیک و ارتباط درون اپلیکیشن به راحتی انجام خواهد گرفت. چرا که در این حالت، تمام پورتها بین کانتینرها در شبکه بریج باز میشوند و در همین حال، دنیال خارج نمیتواند به این پورتها دسترسی داشته باشد. به این ترتیب، شما میتوانید پورتهای دلخواه خود را برای سرویسهای نهایی انتخاب کنید.
در مرحله بعد، سرویس وبسرور را تعریف میکنیم.
... 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
برخی از تنظیماتی که قبلاً برای سرویس 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 اضافه کنیم. حتماً دقت کنید که اطلاعات مربوط به دامین و ایمیل را به شکل مناسب برای وبسایت خودتان وارد کنید.
... 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 [email protected] --agree-tos --no-eff-email --staging -d example.com -d www.example.com
این تعریف موجب میشود که 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 استفاده کردهایم. حتماً دقت کنید که موارد مرتبط با دامین خود را جایگزین کنید.
به عنوان گام نهایی، تعاریف حجم و شبکه را اضافه میکنیم. در اینجا نیز باید کاربر غیر روت خودتان را وارد کنید.
... 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
حجمهای یاد شده شامل گواهی Certbot و دایرکتوری کاری و همینطور، حجم مورد استفاده برای بخشهای استاتیک وبسایت، یعنی web-root میشوند. در بسیاری از موارد، درایور پیشفرض برای حجمهای Docker به صورت local driver است که در لینوکس گزینههای مشابه با فرمان mount را قبول میکند. در نتیجه، میتوانیم با استفاده از driver_opts، فهرستی از گزینههای درایور را بین دایرکتوری هاست و حجم رانتایم در اختیار داشته باشیم. در این حالت، محتوای دایرکتوری میتواند بین کانتینرها به اشتراک گذاشته شود.
نهایتاً فایل docker-compose.yml تکمیلشده به صورت زیر خواهد بود:
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 [email protected] --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
بعد از تعریف سرویسها، همهچیز برای شروع به کار کانتینرها و آزمایش درخواستهای گواهی آماده خواهد بود.
گام ۴) دریافت اعتبار و گواهیهای SSL
میتوانیم با استفاده از docker-compose up کانتینرهای خود را اجرا کنیم. در نتیجه این فرمان، کانتینرهای ما ایجاد و اجرا میشوند و همین طور، سرویسها به ترتیبی که مشخص کردهایم، شروع به کار میکنند. در صورت درخواستهای دامین ما موفقیتآمیز باشند، وضعیت درست خروج را مشاهده خواهیم کرد و همینطور گواهیها در فولدر /etc/letsencrypt/live در کانتینر webserver قرار میگیرند.
سرویسها را با فرمان docker-compose up و گزینه -d ایجاد میکنیم. این گزینه موجب میشود که کانتینرهای nodejs و webserver در پسزمینه اجرا شوند.
docker-compose up -d
در خروجی مشاهده میکنید که سرویسها بهدرستی ایجاد شدهاند.
Creating nodejs ... done Creating webserver ... done Creating certbot ... done
حالا با کمک فرمان docker-compose ps وضعیت سرویسهای خود را بررسی کنید.
docker-compose ps
در صورتی که همه موارد با موفقیت به پیش رفته باشند، سرویسهای nodejs و webserver با وضعیت فعال یا Up و کانتینر certbot با پیام خروج صفر نشان داده میشوند.
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
در صورتی که وضعیت به غیر از Up برای سرویسها و یا وضعیت خروجی غیر از صفر برای کانتینر certbot وجود داشته باشد، حتماً وضعیت سرویس را با فرمان docker-compose logs بررسی کنید.
docker-compose logs service_name
همچنین با استفاده از فرمان docker-compose exec میتوانید وضعیت اعتبار گواهی را در کانتینر webserver بررسی کنید.
docker-compose exec webserver ls -la /etc/letsencrypt/live
در صورتی که درخواست موفقیتآمیز باشد، با خروجی زیر مواجه خواهید شد.
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
حالا که از موفقبودن درخواست خود مطمئن شدید، میتوانید تعریف سرویس certbot را برای برداشتن گزینه –staging ویرایش کنید.
برای این منظور، فایل docker-compose.yml را باز کنید.
nano docker-compose.yml
بخش مربوط به تعریف سرویس certbot را پیدا کرده و گزینه –staging را با –force-renewal جایگزین کنید. در حال حاضر، تعریف سرویس 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 [email protected] --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com ...
حالا میتوانید فرمان docker-compose up را اجرا کنید تا کانتینر certbot و حجمهای مربوط به آن ایجاد شوند. همچنین از گزینه –no-deps برای عبور ار مرحله شروع سرویس webserver استفاده میکنیم؛ چرا که این سرویس هماکنون در حال اجراست.
docker-compose up --force-recreate --no-deps certbot
خروجی نشاندهنده موفقیتآمیز بودن درخواست گواهی شماست.
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
پس از جانمایی گواهیها، می توانیم به سراغ اصلاح تنظیمات Nginx و وارد کردن SSL برویم.
گام ۵) اصلاح تنظیمات و سرویس وبسرور
فعالسازی SSL در تنظیمات Nginx شامل اضافهکردن یک ارجاع HTTP به HTTPS و مشخصکردن محل کلید و گواهی SSL است. همچنین این موضوع شامل مشخصکردن گروه Diffie-Hellman به منظور «محرمانگی پیشرو» یا PFF نیز میشود.
در اینجا میخواهیم سرویس webserver را به منظور اضافهکردن این موارد، بازسازی کنیم. بر این اساس، ابتدا باید آن را متوقف کنیم.
docker-compose stop webserver
اول از همه، یک دایرکتوری در دایرکتوری کنونی پروژه ایجاد کنید تا کلید Diffie-Hellman در آن قرار گیرد.
mkdir dhparam
کلید خود را با فرمان openssl ایجاد کنید.
sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048
ایجاد کلیدها به چند دقیقه زمان احتیاج خواهد داشت.
به منظور اضافهکردن اطلاعات مربوط به Diffie-Hellman و SSL به تنظیمات Nginx، قبل از هر کاری، فایل تنظیمات Nginx را که قبلاً ایجاد کرده بودید، حذف کنید.
rm nginx-conf/nginx.conf
یک نسخه دیگر از فایل باز کنید.
nano nginx-conf/nginx.conf
کد زیر را برای هدایت HTTP به HTTPS و اضافهکردن اعتبارها، پروتکلهای و هدرهای امنیتی SSL اضافه کنید. حتماً به یاد داشته باشید که آدرس example.com را با دامین خودتان جایگزین کنید.
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; }
بلوک 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.
بر این اساس، فایل را باز کنید.
nano docker-compose.yml
در بخش تعریف سرویس webserver، ارجاع پورت و حجم با عنوان dhparam را اضافه کنید.
... 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
حالا حجم dhparam را به تعریف حجمهای خود اضافه نمایید.
... volumes: ... dhparam: driver: local driver_opts: type: none device: /home/sammy/node_project/dhparam/ o: bind
همانند حجم web-root، حجم dhparam نیز کلید Diffie-Hellman ذخیرهشده در هاست را با کانتینر webserver مرتبط میکند.
پس از انجام ویرایش، فایل را ذخیره کرده و ببندید.
اکنون سرویس webserver را بازسازی کنید.
docker-compose up -d --force-recreate --no-deps webserver
با فرمان docker-compose ps سرویسها را بررسی کنید.
docker-compose ps
در خروجی میبایست اجرای سرویسهای nodejs و webserver را مشاهده کنید.
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
نهایتاً از دامین خود بازدید کنید تا از درستبودن تمام موارد مطابق انتظار مطمئن شوید. در نتیجه، صفحه لندینگ زیر برایتان نمایش داده میشود.
همچنین باید آیکون قفل را در نمایشگر امنیتی مرورگر خود ببینید. در صورت تمایل، می توانید به صفحه لندینگ SSL Labs Server Test و یا سرور تست Security Headers بروید. تنظیماتی که قبلاً اِعمال کردیم، میتوانند در هر دوی این صفحات رتبه A را برایتان ایجاد کنند.
گام ۶) تازهسازی گواهیها
گواهیهای Let’s Encrypt برای مدت ۹۰ روز معتبر خواهند بود. بر این اساس، میبایست یک الگوی تازهسازی گواهیها تنظیم کنید تا گواهیهای منقضیشده مورد استفاده قرار نگیرند. یکی از روشهای برای این منظور، ابزار زمانبندی انجام کارها موسوم به cron است. در اینجا ما با استفاده از یک اسکریپت، یک وظیفه cron برای تازهسازی گواهیها و بارگذاری دوباره تنظیمات Nginx ایجاد میکنیم.
برای این منظور، یک اسکریپت با نام ssl_renew.sh در دایرکتوری پروژه باز کنید.
nano ssl_renew.sh
حالا کد زیر را به اسکریپت اضافه کنید.
#!/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
این اسکریپت ابتدا کد باینری 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 برای حذف کلیه کانتینرها و ایمیجهای بلااستفاده انجام میشود.
وقتی کار ویرایش به اتمام رسید، فایل را ببندید. حالا آن را به فایل اجرایی تبدیل کنید
chmod +x ssl_renew.sh
حالا فایل روت crontab را باز کنید تا اسکریپت تازهسازی در فواصل مشخص اجرا شود.
sudo crontab -e
در صورتی که برای اولین بار این فایل را ویرایش میکنید، از شما درخواست میشود که یک ویرایشگر انتخاب کنید.
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]: ...
در پایین این فایل، خط زیر را اضافه کنید.
... */5 * * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
در نتیجه، این کار هر پنج دقیقه یک بار انجام میشود و میتوانید درخواست تازهسازی را در این فاصله بررسی کنید. همچنین یک فیال ثبت تاریخچه با نام cron.log برای این کار ایجاد کردهایم.
بعد از پنج دقیقه، فایل cron.log را برای موفقبودن تازهسازی گواهی بررسی کنید.
tail -f /var/log/cron.log
در نتیجه، میبایست خروجیای مشابه زیر مشاهده کنید.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** 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
حالا میتوانید فایل crontab برای تنظیم فواصل روزانه تازهسازی گواهیها تغییر دهید. برای اجرای اسکریپت در هر بعدازظهر، میتوانید اصلاح زیر را انجام دهید.
... 0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
ضمن اینکه حالا میتوانید گزینه –dry-run را از اسکریپت ssl_renew.sh حذف کنید.
#!/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 باعث میشود که از عدم منقضیشدن گواهیهای Let’s Encrypt اطمینان حاصل گردد. همچنین میتوانید با استفاده از ابزاری مانند Logrotate حجم کمتری برای فایلهای ثبت تاریخچه داشته باشید.
جمعبندی
در اینجا از کانتینرها برای تنظیم و اجرای یک اپلیکیشن Node همراه با پروکسی بازگشتی Nginx استفاده کردیم. همچنین گواهیهای امنیتی SSL برای دامین اپلیکیشن و یک وظیفه cron برای تازهسازی این گواهیها تنظیم شد. امیدواریم که این مطلب نیز مورد استفاده شما قرار گرفته باشد و در ادامه مطالب وبلاگ آریانت نیز با ما همراه باشید.