<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="https://publishpress.com/"
	>

<channel>
	<title>Web Server Archives - Tomoshare</title>
	<atom:link href="https://blog.tomosia.com.vn/danh-muc/web-server/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.tomosia.com.vn/danh-muc/web-server/</link>
	<description>Kênh chia sẻ kiến thức Tomosia Việt Nam</description>
	<lastBuildDate>Fri, 30 Jan 2026 09:56:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.tomosia.com.vn/wp-content/uploads/2023/09/cropped-icon-32x32.png</url>
	<title>Web Server Archives - Tomoshare</title>
	<link>https://blog.tomosia.com.vn/danh-muc/web-server/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</title>
		<link>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/</link>
					<comments>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/#respond</comments>
		
		<dc:creator><![CDATA[Nguyen Luan]]></dc:creator>
		<pubDate>Fri, 30 Jan 2026 09:56:08 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=4177</guid>

					<description><![CDATA[<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối tân, camera khắp nơi, thẻ từ thang máy hiện đại. Lễ tân đưa cho bạn chìa khóa phòng 101. Bạn lên phòng, mở cửa và nghỉ ngơi. Nhưng sau đó, vì tò mò, bạn thử cạo sửa con số trên chìa khóa thành 102 và cắm vào ổ khóa phòng bên cạnh. <em>Cạch!</em> Cửa mở toang.</p>



<p>Trong thế giới thực, điều này thật nực cười. Nhưng trong thế giới lập trình Web và API, tình huống này xảy ra thường xuyên đến mức nó trở thành &#8220;cơn đau đầu&#8221; kinh niên của giới bảo mật. Nó được gọi là <strong>IDOR</strong> (Insecure Direct Object Reference) hay <strong>BOLA</strong> (Broken Object Level Authorization).</p>



<p>Nếu bạn là một Backend Developer, và bạn tin rằng chỉ cần có <mark style="background-color:#e9ecef" class="has-inline-color"><code>Authentication</code> </mark>(Đăng nhập) là hệ thống đã an toàn, thì bài viết này sẽ thay đổi tư duy lập trình của bạn. Chúng ta sẽ không chỉ nói về bề nổi, mà sẽ đi sâu vào bản chất kiến trúc của lỗi này.</p>



<h3 id="1-ban-chat-ky-thuat-cua-idor" class="wp-block-heading">1. Bản chất kỹ thuật của IDOR</h3>



<p><strong>IDOR</strong> (Insecure Direct Object Reference) không chỉ là một lỗi code, nó là một <strong>lỗi về thiết kế Logic (Business Logic Flaw)</strong>.</p>



<p>Trong danh sách <strong>OWASP API Security Top 10</strong>, IDOR chính là bản chất của lỗ hổng số 1: <strong><a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/">API1: 2023 Broken Object Level Authorization (BOLA)</a></strong>.</p>



<p>Hiểu sâu hơn, IDOR xảy ra khi 3 điều kiện sau hội tụ:</p>



<ol class="wp-block-list">
<li><strong>Direct Reference:</strong> Ứng dụng để lộ mã định danh nội bộ (Internal Identifier) của đối tượng (như Database Primary Key, Filename) trực tiếp ra ngoài URL hoặc Body request.</li>



<li><strong>User Input:</strong> Ứng dụng tin tưởng và sử dụng trực tiếp đầu vào này để truy vấn dữ liệu.</li>



<li><strong>Missing Authorization:</strong> Ứng dụng bỏ qua bước kiểm tra quyền truy cập <em>tại thời điểm</em> truy xuất đối tượng.</li>
</ol>



<p><strong>Sai lầm phổ biến:</strong> Nhiều Dev nhầm lẫn giữa <em>Authentication</em> (Bạn là ai?) và <em>Authorization</em> (Bạn được làm gì?). IDOR xảy ra khi bạn đã qua cửa Authentication, nhưng hệ thống quên kiểm tra Authorization ở mức độ đối tượng (Object-level).</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="559" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png" alt="" class="wp-image-4187" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-300x164.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-768x419.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-380x207.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-800x436.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1160x633.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1.png 1408w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="2-giai-phau-co-che-tan-cong" class="wp-block-heading">2. Giải phẫu cơ chế tấn công</h3>



<p>Hãy mổ xẻ một HTTP Request để thấy IDOR có thể lẩn trốn ở đâu. Không chỉ ở URL Parameter, nó có thể nằm ở Header hoặc Body.</p>



<p><strong>Kịch bản:</strong> Ứng dụng Chat cho phép người dùng tải lại lịch sử chat.</p>



<p><strong>Request bình thường:</strong></p>



<pre class="wp-block-code"><code>GET /api/v1/messages/history HTTP/1.1
Host: example.com
Cookie: session_id=abc123xyz...
X-User-ID: 1005  &lt;-- IDOR tiềm ẩn ở Header</code></pre>



<ol class="wp-block-list">
<li><strong>Hacker quan sát:</strong> Thấy Header <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID: 1005</mark></code> được gửi kèm mỗi request.</li>



<li><strong>Thử nghiệm:</strong> Hacker dùng Postman hoặc Burp Suite để chặn request, sửa <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID</mark></code> thành <code>1006</code>.</li>



<li><strong>Kết quả:</strong> Server, thay vì lấy ID từ session an toàn (Server-side), lại tin tưởng lấy ID từ Header (Client-side) để query database <code><mark style="background-color:#e9ecef" class="has-inline-color">SELECT * FROM messages WHERE user_id = 1006</mark></code>.</li>



<li><strong>Hậu quả:</strong> Hacker đọc được toàn bộ tin nhắn của người dùng 1006.</li>
</ol>



<p><em><strong>IDOR có thể xuất hiện ở:</strong></em></p>



<ul class="wp-block-list">
<li><strong>URL Path:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/users/100/profile</mark></code></li>



<li><strong>Query Params:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/invoices?id=100</mark></code></li>



<li><strong>Body (JSON/XML):</strong> <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/change-password</code> với body <code>{"user_id": 100, "new_pass": "..."}</code></mark></li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png" alt="" class="wp-image-4190" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="3-tai-sao-idor-lai-la-sat-thu-tham-lang" class="wp-block-heading">3. Tại sao IDOR lại là &#8220;Sát thủ thầm lặng&#8221;?</h3>



<p>IDOR nguy hiểm vì tính &#8220;hợp lệ&#8221; về mặt cú pháp của nó.</p>



<ul class="wp-block-list">
<li><strong>Vượt mặt WAF (Web Application Firewall):</strong> Các tường lửa thường chặn các ký tự lạ như <code><mark style="background-color:#e9ecef" class="has-inline-color">' OR 1=1 --</mark></code> (SQL Injection) hay <code><mark style="background-color:#e9ecef" class="has-inline-color">&lt;script&gt;</mark></code> (XSS). Nhưng với IDOR, payload chỉ là một con số: <code>id=1006</code>. Đối với WAF, đây là một con số hoàn toàn sạch và hợp lệ.</li>



<li><strong>Khó phát hiện bằng Automation Tool:</strong> Các máy quét (Scanner) không hiểu ngữ cảnh nghiệp vụ. Nếu máy quét gửi <code><mark style="background-color:#e9ecef" class="has-inline-color">id=1006</mark></code> và nhận về <code>200 OK</code>, nó đánh dấu là &#8220;Thành công&#8221;. Nó không biết rằng dữ liệu trả về đó <em>không thuộc về</em> người đang gửi request. Chỉ có con người (Pentesters) hoặc Unit Test được viết kỹ lưỡng mới phát hiện ra.</li>



<li><strong>Hệ quả dây chuyền:</strong> IDOR thường là bước đệm để leo thang đặc quyền (Privilege Escalation), dẫn đến việc chiếm đoạt toàn bộ hệ thống (Account Takeover).</li>
</ul>



<p>Sự nguy hiểm của IDOR nằm ở chỗ nó là một lỗ hổng logic, không phải lỗ hổng cú pháp. Các công cụ quét bảo mật tĩnh (SAST) truyền thống thường thất bại trong việc phát hiện IDOR vì chúng không thể hiểu ngữ cảnh &#8220;ai sở hữu cái gì&#8221; trong một ứng dụng cụ thể. Điều này dẫn đến một nghịch lý: trong khi các lỗ hổng kỹ thuật như <strong>SQL Injection</strong> đang giảm dần nhờ các framework hiện đại, IDOR vẫn tồn tại dai dẳng và chiếm tỷ lệ cao trong các chương trình săn lỗi nhận thưởng (Bug Bounty), đặc biệt là trong các lĩnh vực y tế, chính phủ và dịch vụ chuyên nghiệp.</p>



<h3 id="4-phan-loai-idor-chuyen-sau" class="wp-block-heading">4. Phân loại IDOR chuyên sâu.</h3>



<h4 id="4-1-leo-thang-dac-quyen-ngang-horizontal-escalation" class="wp-block-heading">4.1. Leo thang đặc quyền ngang (Horizontal Escalation)</h4>



<p>Đây là dạng phổ biến nhất. Người dùng A truy cập dữ liệu của Người dùng B có cùng vai trò.</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong> Xem đơn hàng, tải hóa đơn, xem ảnh riêng tư.</li>



<li><strong>Rủi ro:</strong> Rò rỉ thông tin cá nhân (PII Breach), vi phạm GDPR/CCPA.</li>
</ul>



<h4 id="4-2-leo-thang-dac-quyen-doc-vertical-escalation" class="wp-block-heading">4.2. Leo thang đặc quyền dọc (Vertical Escalation)</h4>



<p>Người dùng thường (User) tìm cách gọi các API dành riêng cho Quản trị viên (Admin/Manager).</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong>
<ul class="wp-block-list">
<li>Hacker tìm thấy API: <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/users/1001/roles</code>.</mark></li>



<li>Hacker gửi request với body: <code><mark style="background-color:#e9ecef" class="has-inline-color">{"role": "ADMIN"}</mark></code>.</li>



<li>Nếu Backend không check xem &#8220;người gọi API này có phải là Super Admin không&#8221;, Hacker lập tức trở thành Admin.</li>
</ul>
</li>
</ul>



<h4 id="4-3-idor-tren-file-tinh-static-file-idor" class="wp-block-heading">4.3. IDOR trên File tĩnh (Static File IDOR)</h4>



<p>Tên file thường dễ đoán.</p>



<ul class="wp-block-list">
<li>Ví dụ: <code><mark style="background-color:#e9ecef" class="has-inline-color">https://example.com/uploads/receipts/receipt_001.pdf.</mark></code></li>



<li>Hacker viết script lặp từ <code>001</code> đến <code>9999</code> để tải toàn bộ hóa đơn của hệ thống.</li>
</ul>



<h3 id="5-dan-chung-thuc-te-khi-ong-lon-cung-mac-sai-lam" class="wp-block-heading">5. Dẫn chứng thực tế: Khi &#8220;Ông lớn&#8221; cũng mắc sai lầm</h3>



<h4 id="5-1-mcdonalds-su-co-mchire-7-2025" class="wp-block-heading">5.1. McDonald&#8217;s &amp; sự cố McHire (7/2025)</h4>



<p>Link: <a href="https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/">https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/</a></p>



<p><strong>Bối cảnh</strong>: Hệ thống bị ảnh hưởng là&nbsp;<strong>McHire</strong>, nền tảng tuyển dụng toàn cầu của McDonald&#8217;s, được vận hành bởi đối tác công nghệ bên thứ ba là&nbsp;<strong>Paradox.ai</strong>. Paradox.ai cung cấp giải pháp chatbot AI tên là &#8220;Olivia&#8221; để tự động hóa quy trình phỏng vấn, thu thập thông tin ứng viên và lên lịch làm việc. Sự phụ thuộc vào bên thứ ba này đã mở rộng bề mặt tấn công của McDonald&#8217;s ra ngoài hạ tầng kiểm soát trực tiếp của họ.<sup></sup>&nbsp;&nbsp;&nbsp;</p>



<p><strong>Nguyên nhân chính</strong><br>Sự cố xảy ra do kết hợp 2 lỗi nghiêm trọng:</p>



<ol class="wp-block-list">
<li><strong>Xác thực yếu (Weak Authentication)</strong>
<ul class="wp-block-list">
<li>Một tài khoản admin thử nghiệm của Paradox.ai dùng mật khẩu <strong>“123456”</strong>, không có MFA.</li>



<li>Tài khoản này tồn tại từ <strong>2019</strong>, không bị vô hiệu hóa dù có quyền truy cập production.</li>
</ul>
</li>



<li><strong>Lỗ hổng IDOR (Missing Object Level Authorization)</strong>
<ul class="wp-block-list">
<li>API backend cho phép truy xuất hồ sơ ứng viên bằng <code><mark style="background-color:#e9ecef" class="has-inline-color">applicant_id</mark></code>.</li>



<li>Không kiểm tra quyền truy cập → chỉ cần đổi ID là đọc được dữ liệu của bất kỳ ứng viên nào.</li>



<li>Từ 1 tài khoản → mở rộng ra <strong>64 triệu hồ sơ toàn cầu</strong>.</li>
</ul>
</li>
</ol>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ <strong>PII</strong>: tên, email, số điện thoại, địa chỉ nhà.</li>



<li>Lộ dữ liệu nhạy cảm: log chat với AI (tính cách, sở thích, thông tin cá nhân).</li>



<li>Lộ token phiên → có thể giả danh ứng viên.</li>



<li>Nguy cơ cao cho <strong>social engineering, lừa đảo, chiếm tài khoản</strong>.</li>
</ul>



<p>Sự kết hợp giữa <strong>quản lý tài khoản kém</strong> (tài khoản ma, không MFA) và <strong>thiếu kiểm soát quyền API</strong> (IDOR) tạo ra “cơn bão hoàn hảo” cho rò rỉ dữ liệu quy mô toàn cầu – một xu hướng cực kỳ đáng báo động trong năm 2025</p>



<h4 id="5-2-mozilla-firefox-accounts-lo-hong-xoa-tai-khoan-5-2025" class="wp-block-heading">5.2. Mozilla Firefox Accounts – Lỗ hổng xóa tài khoản (5/2025)</h4>



<p>Link: <a href="https://hackerone.com/reports/3154983">https://hackerone.com/reports/3154983</a></p>



<p><strong>Bản chất sự cố</strong></p>



<ul class="wp-block-list">
<li>Lỗ hổng IDOR nghiêm trọng trong API xóa tài khoản (<code><mark style="background-color:#e9ecef" class="has-inline-color">/v1/account/destroy</mark></code>).</li>



<li>Cho phép <strong>xóa vĩnh viễn tài khoản người khác</strong> mà không cần tương tác từ nạn nhân (zero-click).</li>



<li>Ảnh hưởng trực tiếp đến Integrity (toàn vẹn) và Availability (sẵn sàng) của dữ liệu.</li>
</ul>



<p><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>Lỗi Session Misbinding: backend không kiểm tra phiên làm việc có khớp với tài khoản bị xóa hay không.</li>



<li>API tin vào payload JSON (email, authPW) thay vì session của người gửi request.</li>



<li>Đây là dạng IDOR ghi/xóa, nguy hiểm hơn IDOR đọc dữ liệu.</li>
</ul>



<p><strong>Kịch bản khai thác</strong></p>



<ol class="wp-block-list">
<li>Kẻ tấn công tạo tài khoản Firefox, đăng nhập bằng SSO (Google).</li>



<li>Chặn request xóa tài khoản của chính mình.</li>



<li>Đổi email trong payload thành email nạn nhân (cũng dùng SSO, chưa set mật khẩu).</li>



<li>Gửi request → hệ thống xóa tài khoản nạn nhân.</li>
</ol>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Mất vĩnh viễn dữ liệu: mật khẩu đã lưu, lịch sử duyệt web, tab, sync data.</li>



<li>Không thể khôi phục, không cần nạn nhân click hay xác nhận.</li>



<li>Được đánh giá <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><strong>Critical </strong></mark>trên HackerOne.</li>
</ul>



<p><strong>Bài học cốt lõi</strong>: IDOR không chỉ gây rò rỉ dữ liệu (read) mà còn có thể phá hủy dữ liệu (write/delete) nếu backend không ràng buộc chặt chẽ giữa session và đối tượng tài nguyên.</p>



<h4 id="5-3-gitlab-lo-hong-machine-learning-model-registry-2024-2025" class="wp-block-heading">5.3. GitLab – Lỗ hổng Machine Learning Model Registry (2024–2025)</h4>



<p>Link: <a href="https://hackerone.com/reports/2528293">https://hackerone.com/reports/2528293</a></p>



<p><strong>Bối cảnh</strong></p>



<ul class="wp-block-list">
<li>GitLab triển khai Machine Learning Model Registry để quản lý phiên bản mô hình AI.</li>



<li>Lỗ hổng được báo cáo từ 5/2024, vá và công bố kéo dài sang <strong>2025</strong>, cho thấy độ phức tạp của bảo mật trên nền tảng lớn.</li>



<li>Sự cố ảnh hưởng trực tiếp đến tài sản trí tuệ AI (model weights, biases).</li>
</ul>



<p><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>GitLab dùng <strong>ID tịnh tiến (1,2,3,&#8230;)</strong> cho <mark style="background-color:#e9ecef" class="has-inline-color"><code>model_id</code> </mark>thay vì UUID ngẫu nhiên.</li>



<li>API tải/xem mô hình không kiểm tra quyền truy cập dự án (Missing Object Level Authorization).</li>



<li>Đây là IDOR điển hình + thiết kế API kém an toàn.</li>
</ul>



<p><strong>Cách khai thác</strong></p>



<ul class="wp-block-list">
<li>Kẻ tấn công chỉ cần tài khoản GitLab miễn phí.</li>



<li>Lặp ID từ 1 → hàng triệu để liệt kê và tải mô hình của dự án khác.</li>



<li>Không cần brute-force, không cần quyền dự án.</li>
</ul>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ mô hình ML độc quyền của doanh nghiệp.</li>



<li>Nguy cơ mất bí mật thương mại, thiệt hại kinh tế lớn trong bối cảnh AI là tài sản cốt lõi.</li>



<li>Lỗ hổng có thể bị khai thác tự động, quy mô lớn (enumeration).</li>
</ul>



<p><strong>Bài học cốt lõi</strong>: Không dùng ID tịnh tiến cho API công khai. Nó biến IDOR từ lỗ hổng tiềm ẩn thành thảm họa có thể khai thác hàng loạt, đặc biệt nguy hiểm với tài sản AI.</p>



<h3 id="6-ngan-chan-idor-hieu-qua-best-practices" class="wp-block-heading">6. Ngăn chặn IDOR hiệu quả (Best Practices)</h3>



<p>Khi một tài khoản test có thể mở khóa 64 triệu hồ sơ, một email có thể xóa tài khoản người khác, và một con số tăng dần có thể làm bay cả mô hình AI, thì rõ ràng vấn đề không nằm ở “hacker quá giỏi” — mà ở chỗ chúng ta cần chặn IDOR cho đúng cách. </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png" alt="" class="wp-image-4197" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><em>&#8220;Đừng bao giờ tin tưởng Client&#8221;</em>. Dưới đây là chiến lược phòng thủ đa lớp <strong>(Defense in Depth)</strong>.</p>



<p><strong>Lớp 1: Kiểm soát truy cập tại tầng Data (Data-Layer Authorization)</strong></p>



<p>Đây là chốt chặn cuối cùng và quan trọng nhất. Mọi câu lệnh truy vấn liên quan đến dữ liệu người dùng <strong>phải</strong> kèm theo điều kiện về chủ sở hữu.</p>



<p><strong>❌ Code rủi ro (Spring Data JPA):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Controller
public Order getOrder(@PathVariable Long id) {
    return orderRepo.findById(id); // Chỉ tìm theo ID đơn hàng
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Controller</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id); </span><span style="color: #6A9955">// Chỉ tìm theo ID đơn hàng</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><strong>✅ Code an toàn (Repository Pattern):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Repository
// Luôn gắn kèm userId vào câu query
@Query(&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;)
Optional&lt;Order&gt; findByIdAndOwner(@Param(&quot;id&quot;) Long id, @Param(&quot;currentUserId&quot;) Long userId);

// Service Layer
public Order getOrder(Long id, User currentUser) {
    return orderRepo.findByIdAndOwner(id, currentUser.getId())
        .orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Order not found or access denied&quot;));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Repository</span></span>
<span class="line"><span style="color: #6A9955">// Luôn gắn kèm userId vào câu query</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">Query</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #4EC9B0">Optional</span><span style="color: #D4D4D4">&lt;Order&gt; </span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;id&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, @</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;currentUserId&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> userId);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Service Layer</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> currentUser) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(id, </span><span style="color: #9CDCFE">currentUser</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getId</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">orElseThrow</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ResourceNotFoundException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Order not found or access denied&quot;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><strong>Lớp 2: Kiểm soát truy cập tại tầng Controller (Method-Level Security)</strong></p>



<p>Sử dụng các Annotation bảo mật của Framework (như Spring Security) để chặn ngay từ cửa vào.</p>



<p><strong>✅ Ví dụ với Spring Security (<code><mark style="background-color:#e9ecef" class="has-inline-color">@PreAuthorize</mark></code>):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="@GetMapping(&quot;/invoices/{id}&quot;)
// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này
@PreAuthorize(&quot;@securityService.isOwner(authentication, #id)&quot;) 
public Invoice getInvoice(@PathVariable Long id) {
    return invoiceRepo.findById(id);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">GetMapping</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/invoices/{id}&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #6A9955">// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">PreAuthorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;@securityService.isOwner(authentication, #id)&quot;</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Invoice</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getInvoice</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">invoiceRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><em>Cách này giúp code business logic sạch hơn, tách biệt logic bảo mật ra khỏi logic nghiệp vụ.</em></p>



<p><strong>Lớp 3: Làm khó Hacker bằng UUID (Obfuscation)</strong></p>



<p>Thay vì dùng Auto-Increment Integer (<code>1, 2, 3</code>), hãy dùng <strong>UUID</strong> (Random String: <code><mark style="background-color:#e9ecef" class="has-inline-color">a0eebc99-9c0b...</mark></code>) hoặc các thuật toán băm ID (như Hashids).</p>



<ul class="wp-block-list">
<li><strong>Lợi ích:</strong> Chặn đứng tấn công kiểu liệt kê (Enumeration Attack). Hacker không thể viết vòng lặp <code><mark style="background-color:#e9ecef" class="has-inline-color">for (i=0; i&lt;1000; i++)</mark></code> để quét dữ liệu.</li>



<li><strong>Cảnh báo:</strong> UUID <strong>KHÔNG THỂ THAY THẾ</strong> được <em>Lớp 1</em> và <em>Lớp 2</em>. Nếu hacker bằng cách nào đó biết được UUID của nạn nhân, IDOR vẫn xảy ra nếu thiếu Authorization check. UUID chỉ là &#8220;lớp sương mù&#8221;, không phải &#8220;bức tường thép&#8221;.</li>
</ul>



<p><strong>Lớp 4: Kiểm thử tự động (Automated Security Testing)</strong></p>



<p>Đừng đợi Pentester tìm lỗi. Hãy viết Test Case cho IDOR.</p>



<ul class="wp-block-list">
<li><strong>Unit Test:</strong> Tạo 2 user (Alice, Bob). Đăng nhập bằng Alice, cố gắng request dữ liệu ID của Bob. Assert rằng kết quả trả về phải là <code><mark style="background-color:#e9ecef" class="has-inline-color">403 Forbidden</mark></code> hoặc <code><mark style="background-color:#e9ecef" class="has-inline-color">404 Not Found</mark></code>.</li>
</ul>



<h3 id="7-ket-luan" class="wp-block-heading">7. Kết luận</h3>



<p>IDOR không phải là một lỗi kỹ thuật phức tạp, nhưng nó phản ánh sự thiếu sót trong tư duy thiết kế hệ thống.</p>



<p>Hãy nhớ nguyên tắc vàng: <strong>Mọi request truy cập dữ liệu đều phải trả lời được hai câu hỏi:</strong></p>



<ol class="wp-block-list">
<li><em>Người dùng này là ai? (Authentication)</em></li>



<li><em>Dữ liệu này có phải của họ không? (Authorization)</em></li>
</ol>



<p>Nếu thiếu câu hỏi số 2, chúng ta đang mở toang cánh cửa &#8220;phòng 102&#8221; cho bất kỳ ai cầm chìa khóa &#8220;phòng 101&#8221;.</p>



<p></p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn từng bước thiết lập Let&#8217;s Encrypt SSL/TLS cho Nginx trên Docker.</title>
		<link>https://blog.tomosia.com.vn/huong-dan-tung-buoc-thiet-lap-lets-encrypt-ssl-tls-cho-nginx-tren-docker/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-tung-buoc-thiet-lap-lets-encrypt-ssl-tls-cho-nginx-tren-docker/#respond</comments>
		
		<dc:creator><![CDATA[linh phan]]></dc:creator>
		<pubDate>Mon, 22 Dec 2025 01:11:37 +0000</pubDate>
				<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Web server]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3683</guid>

					<description><![CDATA[<p>Trong bài viết này, mình sẽ hướng dẫn từng bước cách thiết lập HTTPS với Let&#8217;s Encrypt cho&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-tung-buoc-thiet-lap-lets-encrypt-ssl-tls-cho-nginx-tren-docker/">Hướng dẫn từng bước thiết lập Let&#8217;s Encrypt SSL/TLS cho Nginx trên Docker.</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Trong bài viết này, mình sẽ hướng dẫn <strong>từng bước</strong> cách thiết lập <strong>HTTPS với Let&#8217;s Encrypt</strong> cho <strong>Nginx chạy trên Docker</strong>, theo hướng <strong>thực tế – dễ vận hành – dùng được cho production</strong>.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p><strong>Nội dung bao gồm:</strong><br>・Thiết lập Nginx chạy HTTPS trên Docker<br>・Cấp SSL/TLS certificate bằng Certbot<br>・Tự động gia hạn certificate<br>・Redirect HTTP → HTTPS<br>・Áp dụng các security headers cơ bản</p>
</div></div>



<p><strong>Yêu cầu</strong><br>・Docker đã cài đặt<br>・Docker Compose đã cài đặt<br>・Quyền quản trị hệ thống<br>・Domain đã trỏ về IP server<br>・Port 80 và 443 mở và trỏ về server</p>



<p><br><strong>Thiết lập Nginx Webserver với Let&#8217;s Encrypt trên Docker</strong><br>Quy trình thiết lập gồm các bước sau:・Tạo Docker Compose file<br>・Cấu hình Nginx server<br>・Tạo script setup SSL<br>・Chạy Certbot client<br>・Tự động gia hạn certificate</p>



<h2 id="buoc-1-tao-cau-truc-thu-muc-va-env" class="wp-block-heading">Bước 1: Tạo cấu trúc thư mục và <code>.env</code></h2>



<p>Tạo các thư mục cần thiết trong dự án:</p>



<pre class="wp-block-code has-d-4-d-4-d-4-color has-text-color has-875-rem-font-size"><code>mkdir -p docker/nginx/etc/nginx/templates
mkdir -p docker/nginx/etc/nginx/conf
mkdir -p docker/nginx/certs</code></pre>



<p>Cấu trúc thư mục:</p>



<pre class="wp-block-code"><code>docker/
└── nginx/
    ├── setup-ssl.sh
    ├── entrypoint.sh
    ├── etc/nginx/
    │   ├── nginx.conf
    │   ├── conf/
    │   └── templates/
    │       ├── default.conf.template
    │       ├── https.conf.template
    │       └── upstream.conf.template
    └── certs/              # Thư mục chứa certificates (gitignored)</code></pre>



<p><strong>File Structure</strong><br>・<code>entrypoint.sh</code>: Script tự động generate nginx configs từ templates<br>・<code>setup-ssl.sh</code>: Script tạo SSL certificate trên host (chạy ngoài container)<br>・<code>templates/</code>: Nginx config templates<br>・<code>https.conf.template</code>: HTTPS config cho domain<br>・<code>upstream.conf.template</code>: PHP-FPM upstream config<br>・<code>certs/</code>: Thư mục chứa Let&#8217;s Encrypt certificates trên host (gitignored, mounted vào container)</p>



<p><strong>Environment Variables</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Biến</th><th>Mô tả</th><th>Default</th></tr></thead><tbody><tr><td><code>DOCKER_NGINX_EXTERNAL_PORT</code></td><td>HTTP external port</td><td><code>8000</code></td></tr><tr><td><code>DOCKER_NGINX_HTTPS_PORT</code></td><td>HTTPS external port</td><td><code>8443</code></td></tr><tr><td><code>HTTPS_DOMAIN</code></td><td>Domain dùng HTTPS</td><td><code>example.com</code></td></tr><tr><td><code>ENABLE_HTTPS</code></td><td>Bật HTTPS</td><td><code>true</code></td></tr></tbody></table></figure>



<h2 id="buoc-2-tao-dockerfile-cho-nginx" class="wp-block-heading">Bước 2: Tạo Dockerfile cho Nginx</h2>



<p>Tạo file docker/Dockerfile với multi-stage build:</p>



<pre class="wp-block-code"><code>FROM nginx:1.28.0-alpine as nginx

WORKDIR /var/www/html
COPY docker/nginx/ /
COPY docker/nginx/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

EXPOSE 80 443

ENTRYPOINT &#91;"/usr/local/bin/entrypoint.sh"]
CMD &#91;"nginx", "-g", "daemon off;"]</code></pre>



<h2 id="buoc-3-tao-docker-compose-file" class="wp-block-heading">Bước 3: Tạo Docker Compose file</h2>



<p>Tạo file docker-compose.yml để chạy Nginx với HTTPS:</p>



<pre class="wp-block-code"><code>services:
  nginx:
    build:
      context: .
      dockerfile: docker/Dockerfile
      target: nginx
    environment:
      ENABLE_BASIC_AUTH: "${ENABLE_BASIC_AUTH-false}"
      PHP_FPM_HOST: app
      PHP_FPM_PORT: 9000
      ENABLE_HTTP_LOCALHOST: "false"
      ENABLE_HTTPS: "true"
      HTTPS_DOMAIN: "${HTTPS_DOMAIN-example.com}"
    ports:
      - "${DOCKER_NGINX_EXTERNAL_PORT-8000}:80"
      - "${DOCKER_NGINX_HTTPS_PORT-8443}:443"
    depends_on:
      - app
      - certbot
    volumes:
      - ".:/var/www/html"
      - "./docker/nginx/certs:/etc/letsencrypt"

  certbot:
    image: certbot/certbot:latest
    volumes:
      - "./docker/nginx/certs:/etc/letsencrypt"
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h &amp; wait $${!}; done;'"

  app:
    build:
      context: .
      dockerfile: docker/Dockerfile
      target: app
    volumes:
      - ".:/var/www/html"
    environment:
      PHP_OPCACHE_VALIDATE_TIMESTAMPS: 1
    command: &#91;"development"]</code></pre>



<p>Giải thích:<br><code>webserver</code> (nginx): container Nginx với HTTPS<br><code>certbot</code>: container tự động gia hạn certificate<br><code>app</code>: container Laravel application<br>Volume <code>./docker/nginx/certs:/etc/letsencrypt</code>: chia sẻ certificates giữa containers</p>



<h2 id="buoc-4-tao-file-cau-hinh-nginx" class="wp-block-heading">Bước 4: Tạo file cấu hình Nginx</h2>



<p><strong>4.1. Tạo template HTTP (cho Let&#8217;s Encrypt validation)</strong><br>Tạo file <code>docker/nginx/etc/nginx/templates/default.conf.template</code>:</p>



<pre class="wp-block-code"><code>server {
    listen 80;
    server_name _;

    root /var/www/html/public;
    index index.php;

    client_max_body_size 40m;

    access_log /dev/stdout;
    error_log /dev/stderr;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    proxy_hide_header X-Powered-By;

    charset utf-8;

    location = /health {
        return 200 'OK!';
        add_header Content-Type text/plain;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location / {
        auth_basic $basic_auth;
        auth_basic_user_file /etc/nginx/.htpasswd;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        set $path_info $fastcgi_path_info;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass php-fpm;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}</code></pre>



<p>Lưu ý: Block <code>location ~ /\.(?!well-known).*</code> chặn các file ẩn nhưng cho phép <code>.well-known</code> để Let&#8217;s Encrypt validate.</p>



<p><strong>4.2. Tạo template HTTPS</strong><br>Tạo file <code>docker/nginx/etc/nginx/templates/https.conf.template</code>:</p>



<pre class="wp-block-code"><code># HTTPS server for ${HTTPS_DOMAIN}
server {
    listen 443 ssl;
    http2 on;
    server_name ${HTTPS_DOMAIN};

    root /var/www/html/public;
    index index.php;

    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/${HTTPS_DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${HTTPS_DOMAIN}/privkey.pem;

    client_max_body_size 40m;

    access_log /dev/stdout;
    error_log /dev/stderr;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    proxy_hide_header X-Powered-By;

    charset utf-8;

    location = /health {
        return 200 'OK!';
        add_header Content-Type text/plain;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location / {
        auth_basic $basic_auth;
        auth_basic_user_file /etc/nginx/.htpasswd;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        set $path_info $fastcgi_path_info;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_pass php-fpm;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

# Redirect HTTP to HTTPS for ${HTTPS_DOMAIN}
server {
    listen 80;
    server_name ${HTTPS_DOMAIN};

    location / {
        return 301 https://$host$request_uri;
    }
}</code></pre>



<p>Giải thích:<br>Server block đầu: HTTPS trên port 443 với HTTP/2<br>Server block thứ hai: redirect HTTP → HTTPS<br>Security headers: HSTS, X-Frame-Options, X-Content-Type-Options</p>



<h2 id="buoc-5-tao-entrypoint-script" class="wp-block-heading">Bước 5: Tạo Entrypoint script</h2>



<p>Tạo file <code>docker/nginx/entrypoint.sh</code> để tự động generate config từ template:</p>



<pre class="wp-block-code"><code>#!/bin/sh
set -e

# Set BASIC_AUTH_VALUE based on ENABLE_BASIC_AUTH
if &#91; "$ENABLE_BASIC_AUTH" = "true" ]; then
    export BASIC_AUTH_VALUE="Administrator's Area"
else
    export BASIC_AUTH_VALUE="off"
fi

# Generate map config from template
envsubst '$$BASIC_AUTH_VALUE' &lt; /etc/nginx/templates/map.conf.template &gt; /etc/nginx/conf.d/map.conf

# Generate upstream config
envsubst '$$PHP_FPM_HOST $$PHP_FPM_PORT' &lt; /etc/nginx/templates/upstream.conf.template &gt; /etc/nginx/conf.d/upstream.conf

# Generate nginx configs from templates
if &#91; "$ENABLE_HTTP_LOCALHOST" = "true" ]; then
    envsubst '$$PHP_FPM_HOST $$PHP_FPM_PORT' &lt; /etc/nginx/templates/localhost.conf.template &gt; /etc/nginx/conf.d/server-localhost.conf
fi

# Handle HTTPS setup
if &#91; "$ENABLE_HTTPS" = "true" ] &amp;&amp; &#91; -n "$HTTPS_DOMAIN" ]; then
    # Create directories if they don't exist
    mkdir -p /etc/letsencrypt/live/${HTTPS_DOMAIN}

    if &#91; -f "/etc/letsencrypt/live/${HTTPS_DOMAIN}/fullchain.pem" ]; then
        envsubst '$$PHP_FPM_HOST $$PHP_FPM_PORT $$HTTPS_DOMAIN' &lt; /etc/nginx/templates/https.conf.template &gt; /etc/nginx/conf.d/server-https.conf
    fi
fi

# Execute the main command
exec "$@"</code></pre>



<p>Script này:<br>・Tạo config từ template dựa trên biến môi trường<br>・Chỉ tạo HTTPS config nếu certificate tồn tại<br>・Dùng <code>envsubst</code> để thay thế biến</p>



<h2 id="buoc-6-tao-script-setup-ssl" class="wp-block-heading">Bước 6: Tạo script setup SSL</h2>



<p>Tạo file <code>docker/nginx/setup-ssl.sh</code> để tạo certificate trên host:</p>



<pre class="wp-block-code"><code>#!/bin/bash
set -e

# Get script directory and project root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE&#91;0]}")" &amp;&amp; pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." &amp;&amp; pwd)"

# Configuration
DOMAIN="${HTTPS_DOMAIN:-example.com}"
CERT_DIR="${PROJECT_ROOT}/docker/nginx/certs"

echo "Setting up SSL certificate for ${DOMAIN} on host..."
echo "Certificate will be saved to: ${CERT_DIR}"

# Create directories on host
mkdir -p "${CERT_DIR}"

# Check if certificate already exists
if &#91; -f "${CERT_DIR}/live/${DOMAIN}/fullchain.pem" ]; then
    echo "Certificate already exists for ${DOMAIN}"
    echo "Location: ${CERT_DIR}/live/${DOMAIN}/"
    exit 0
fi

cd "${PROJECT_ROOT}"

# Request certificate using docker (certificate saved on host, mounted into container)
echo "Requesting certificate (port 80 must be available)..."
docker run --rm \
    -v "${CERT_DIR}:/etc/letsencrypt" \
    -p 80:80 \
    certbot/certbot certonly \
    --standalone \
    --agree-tos \
    -d "${DOMAIN}"

echo ""
echo "Certificate created successfully on host!"
echo "Location: ${CERT_DIR}/live/${DOMAIN}/"
echo "Files: fullchain.pem, privkey.pem"
echo ""
echo "Certificate will be automatically mounted into nginx container."
echo "You can now run: docker-compose up -d"</code></pre>



<p>Lưu ý: Script này chạy trên host, không trong container. Certificate được lưu trên host và mount vào container.</p>



<h2 id="buoc-7-chay-certbot-de-lay-certificate" class="wp-block-heading">Bước 7: Chạy Certbot để lấy certificate</h2>



<p><strong>7.1. Test với dry-run (khuyến nghị)</strong><br>Trước khi lấy certificate thật, test với <code>--dry-run</code>:</p>



<pre class="wp-block-code"><code>chmod +x docker/nginx/setup-ssl.sh

# Test với dry-run
docker run --rm \
    -v "$(pwd)/docker/nginx/certs:/etc/letsencrypt" \
    -p 80:80 \
    certbot/certbot certonly \
    --standalone \
    --dry-run \
    --agree-tos \
    -d your-domain.com</code></pre>



<p>Lưu ý: Thay <code>your-domain.com</code> bằng domain của bạn.</p>



<p><strong>7.2. Lấy certificate thật</strong></p>



<p>Nếu dry-run thành công, chạy script để lấy certificate:</p>



<pre class="wp-block-code"><code>HTTPS_DOMAIN=your-domain.com ./docker/nginx/setup-ssl.sh</code></pre>



<p>Hoặc dùng domain mặc định:</p>



<pre class="wp-block-code"><code>./docker/nginx/setup-ssl.sh</code></pre>



<p>Quá trình:</p>



<ol class="wp-block-list">
<li>Script kiểm tra certificate đã tồn tại chưa</li>



<li>Nếu chưa, chạy Certbot với &#8211;standalone mode</li>



<li>Certbot bind port 80 để Let&#8217;s Encrypt validate</li>



<li>Certificate được lưu tại ./docker/nginx/certs/live/your-domain.com/<br><br>Lưu ý:<br>・Đảm bảo port 80 mở và trỏ về server.<br>・Nếu Nginx đang chạy trên port 80, tạm dừng trước khi chạy script.</li>
</ol>



<h2 id="buoc-8-khoi-dong-docker-compose" class="wp-block-heading">Bước 8: Khởi động Docker Compose</h2>



<p>Sau khi có certificate, khởi động services:</p>



<pre class="wp-block-code"><code>docker-compose up -d</code></pre>



<p>Kiểm tra containers đang chạy:</p>



<pre class="wp-block-code"><code>docker-compose ps</code></pre>



<p>Kiểm tra logs:</p>



<pre class="wp-block-code"><code>docker-compose logs nginx
docker-compose logs certbot</code></pre>



<h2 id="buoc-9-kiem-tra-https" class="wp-block-heading">Bước 9: Kiểm tra HTTPS</h2>



<p>Truy cập domain qua HTTPS:</p>



<pre class="wp-block-code"><code>curl -I https://your-domain.com:8443</code></pre>



<p>Hoặc mở trình duyệt và truy cập: <code>https://your-domain.com:8443</code><br><br>Bạn sẽ thấy:<br><br>・Certificate hợp lệ (không có cảnh báo)<br><br>・Tự động redirect HTTP → HTTPS<br><br>・Security headers được áp dụng</p>



<h2 id="buoc-10-gia-han-certificates" class="wp-block-heading">Bước 10: Gia hạn certificates</h2>



<p>Let&#8217;s Encrypt certificates có thời hạn 90 ngày. Có 2 cách gia hạn:<br><br><strong>10.1. Tự động gia hạn (đã cấu hình)</strong></p>



<p>Service <code>certbot</code> trong <code>docker-compose.yml</code> tự động gia hạn mỗi 12 giờ:</p>



<pre class="wp-block-code"><code>certbot:
  image: certbot/certbot:latest
  volumes:
    - "./docker/nginx/certs:/etc/letsencrypt"
  entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h &amp; wait $${!}; done;'"</code></pre>



<p>Certbot chỉ gia hạn khi certificate sắp hết hạn (trong vòng 30 ngày).</p>



<p><strong>10.2. Gia hạn thủ công</strong><br><br>Nếu cần gia hạn ngay:</p>



<pre class="wp-block-code"><code>docker run --rm \
    -v "$(pwd)/docker/nginx/certs:/etc/letsencrypt" \
    certbot/certbot renew</code></pre>



<p>Hoặc trong container:</p>



<pre class="wp-block-code"><code>docker-compose exec certbot certbot renew</code></pre>



<p>Sau khi gia hạn, reload Nginx để áp dụng certificate mới:</p>



<pre class="wp-block-code"><code>docker-compose exec nginx nginx -s reload</code></pre>



<p>Hoặc restart container:</p>



<pre class="wp-block-code"><code>docker-compose restart nginx</code></pre>



<h2 id="troubleshooting" class="wp-block-heading">Troubleshooting</h2>



<p><strong>Lỗi: &#8220;Port 80 is already in use&#8221;</strong><br><br>Nguyên nhân: Port 80 đang được sử dụng bởi service khác.<br><br>Giải pháp:</p>



<pre class="wp-block-code"><code># Kiểm tra process đang dùng port 80
sudo lsof -i :80

# Tạm dừng Nginx nếu đang chạy
docker-compose down

# Sau đó chạy lại setup-ssl.sh
./docker/nginx/setup-ssl.sh</code></pre>



<p><strong>Lỗi: &#8220;Failed to obtain certificate&#8221;</strong><br><br>Nguyên nhân:<br>・Domain chưa trỏ về IP server<br>・Port 80 bị firewall chặn<br>・Let&#8217;s Encrypt không thể truy cập domain<br><br>Giải pháp:</p>



<pre class="wp-block-code"><code># Kiểm tra DNS
nslookup your-domain.com

# Kiểm tra port 80
curl -I http://your-domain.com

# Kiểm tra firewall
sudo ufw status</code></pre>



<p><strong>Lỗi: &#8220;Nginx không load HTTPS config&#8221;</strong><br><br>Nguyên nhân:<br>・Certificate chưa tồn tại<br>・Biến môi trường chưa đúng<br>・Volume mount sai<br><br>Giải pháp:</p>



<pre class="wp-block-code"><code># Kiểm tra certificate
ls -la docker/nginx/certs/live/your-domain.com/

# Kiểm tra biến môi trường
docker-compose config

# Kiểm tra volume mount trong container
docker-compose exec nginx ls -la /etc/letsencrypt/live/</code></pre>



<h2 id="tong-ket" class="wp-block-heading">Tổng kết</h2>



<p>Sau khi hoàn thành các bước trên, bạn đã:<br>・Thiết lập Nginx với HTTPS trên Docker<br>・Cấu hình Let&#8217;s Encrypt SSL/TLS certificates<br>・Tự động gia hạn certificates<br>・Redirect HTTP → HTTPS<br>・Áp dụng security headers</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-tung-buoc-thiet-lap-lets-encrypt-ssl-tls-cho-nginx-tren-docker/">Hướng dẫn từng bước thiết lập Let&#8217;s Encrypt SSL/TLS cho Nginx trên Docker.</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-tung-buoc-thiet-lap-lets-encrypt-ssl-tls-cho-nginx-tren-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Đưa Localhost lên Internet miễn phí với Cloudflare Tunnel: Giải pháp thay thế Ngrok để Test Webhook &#038; Demo App</title>
		<link>https://blog.tomosia.com.vn/dua-localhost-len-internet-mien-phi-voi-cloudflare-tunnel-giai-phap-thay-the-ngrok-de-test-webhook-demo-app/</link>
					<comments>https://blog.tomosia.com.vn/dua-localhost-len-internet-mien-phi-voi-cloudflare-tunnel-giai-phap-thay-the-ngrok-de-test-webhook-demo-app/#comments</comments>
		
		<dc:creator><![CDATA[Hoang Nam]]></dc:creator>
		<pubDate>Tue, 09 Dec 2025 01:41:48 +0000</pubDate>
				<category><![CDATA[Solution]]></category>
		<category><![CDATA[Kinh nghiệm]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Hosting]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3752</guid>

					<description><![CDATA[<p>Bạn cần test webhook ZaloPay, Stripe hay demo app cho khách hàng ngay trên localhost? Hướng dẫn sử&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/dua-localhost-len-internet-mien-phi-voi-cloudflare-tunnel-giai-phap-thay-the-ngrok-de-test-webhook-demo-app/">Đưa Localhost lên Internet miễn phí với Cloudflare Tunnel: Giải pháp thay thế Ngrok để Test Webhook &amp; Demo App</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><em>Bạn cần test webhook ZaloPay, Stripe hay demo app cho khách hàng ngay trên localhost? Hướng dẫn sử dụng Cloudflare Tunnel để expose local server ra internet với HTTPS miễn phí, bảo mật, không cần mở port.</em></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="mo-dau-noi-dau-cua-developer" class="wp-block-heading">Mở đầu: Nỗi đau của Developer</h2>



<p>Bạn đang phát triển một ứng dụng local, mọi thứ chạy mượt mà ở&nbsp;localhost:3000. Nhưng vấn đề nảy sinh khi:</p>



<ul class="wp-block-list">
<li>Bạn cần <strong>test Webhook</strong> từ các dịch vụ bên ngoài như ZaloPay, Stripe, Telegram (các bên này bắt buộc phải có HTTPS).</li>



<li>Khách hàng muốn <strong>xem demo ngay lập tức</strong>, nhưng bạn chưa kịp deploy lên server.</li>



<li>Bạn chán ngấy cảnh dùng <strong>Ngrok bản free</strong> vì tên miền bị đổi liên tục sau mỗi lần restart.</li>
</ul>



<p>Giải pháp tối ưu nhất hiện nay chính là&nbsp;<strong>Cloudflare Tunnel</strong>.</p>



<h2 id="1-cloudflare-tunnel-la-gi" class="wp-block-heading">1. Cloudflare Tunnel là gì?</h2>



<p><strong>Cloudflare Tunnel</strong>&nbsp;(trước đây là Argo Tunnel) là công cụ giúp bạn kết nối máy chủ local (hoặc máy ảo) ra internet mà&nbsp;<strong>không cần mở port (port forwarding)</strong>&nbsp;trên router.</p>



<p>Thay vì cho phép người lạ truy cập trực tiếp vào IP của bạn (rất rủi ro), Tunnel tạo một đường hầm bảo mật (outbound connection) từ máy bạn đến mạng lưới Cloudflare Edge. Từ đó, Cloudflare sẽ &#8220;public&#8221; ứng dụng của bạn ra ngoài bằng một tên miền HTTPS xịn xò.</p>



<h3 id="tai-sao-nen-dung-cloudflare-tunnel-thay-vi-ngrok" class="wp-block-heading">Tại sao nên dùng Cloudflare Tunnel thay vì Ngrok?</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>Tiêu chí</td><td>Ngrok (Free)</td><td>Cloudflare Tunnel</td></tr><tr><td><strong>Domain</strong></td><td>Random, đổi liên tục</td><td><strong>Cố định</strong>&nbsp;(theo domain của bạn)</td></tr><tr><td><strong>Chi phí</strong></td><td>Giới hạn tính năng</td><td><strong>Miễn phí</strong>&nbsp;hoàn toàn</td></tr><tr><td><strong>Bảo mật</strong></td><td>Thấp</td><td>Rất cao (Cloudflare Shield)</td></tr><tr><td><strong>Cài đặt</strong></td><td>Nhanh</td><td>Cần domain riêng (nhưng đáng giá)</td></tr></tbody></table></figure>



<h2 id="2-mo-hinh-hoat-dong" class="wp-block-heading">2. Mô hình hoạt động</h2>



<p>Hãy tưởng tượng luồng dữ liệu sẽ đi như sau:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>User Browser</strong> -> <strong>Cloudflare Edge (HTTPS)</strong> -> <strong>Cloudflared (trên máy bạn)</strong> -> <strong>Localhost (3000, 8080&#8230;)</strong></p>
</blockquote>



<p>Bạn có thể chạy nhiều dịch vụ cùng lúc qua một đường hầm duy nhất:</p>



<ul class="wp-block-list">
<li>api.domain.com -> trỏ về localhost:3000</li>



<li>admin.domain.com -> trỏ về localhost:8080</li>
</ul>



<h2 id="3-chuan-bi-do-nghe" class="wp-block-heading">3. Chuẩn bị &#8220;đồ nghề&#8221;</h2>



<p>Trước khi bắt đầu, bạn cần:</p>



<ol class="wp-block-list">
<li><strong>Tài khoản Cloudflare</strong> (đăng ký miễn phí).</li>



<li><strong>Một tên miền (Domain)</strong> đã thêm vào Cloudflare.
<ul class="wp-block-list">
<li><em>Lưu ý:</em> Freenom hiện tại không ổn định. Bạn nên mua các domain giá rẻ như .xyz, .dev, .click (chỉ khoảng 1-2$/năm) tại Namecheap hoặc Hostinger để dùng lâu dài.</li>
</ul>
</li>



<li><strong>Máy tính</strong> (Linux/Mac/Windows) để chạy tool.</li>
</ol>



<h2 id="4-huong-dan-trien-khai-chi-tiet-step-by-step" class="wp-block-heading">4. Hướng dẫn triển khai chi tiết (Step-by-step)</h2>



<h3 id="buoc-1-cai-dat-cloudflared" class="wp-block-heading">Bước 1: Cài đặt&nbsp;cloudflared</h3>



<p>Công cụ&nbsp;cloudflared&nbsp;là cầu nối giữa máy bạn và Cloudflare.</p>



<p><strong>Trên Linux (Ubuntu/Debian):</strong>codeBash</p>



<pre class="wp-block-code"><code>curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb</code></pre>



<p><strong>Trên Windows:</strong><br>Tải file&nbsp;.msi&nbsp;mới nhất từ GitHub chính chủ:&nbsp;<a target="_blank" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Fgithub.com%2Fcloudflare%2Fcloudflared%2Freleases" rel="noreferrer noopener">Cloudflare Downloads</a><br><em>(Mở PowerShell dưới quyền Administrator để chạy các lệnh sau này).</em></p>



<h3 id="buoc-2-dang-nhap-cloudflare" class="wp-block-heading">Bước 2: Đăng nhập Cloudflare</h3>



<p>Chạy lệnh sau để xác thực:codeBash</p>



<pre class="wp-block-code"><code>cloudflared tunnel login</code></pre>



<p>Trình duyệt sẽ bật lên, bạn hãy chọn tên miền muốn sử dụng và bấm&nbsp;<strong>Authorize</strong>.</p>



<h3 id="buoc-3-tao-tunnel" class="wp-block-heading">Bước 3: Tạo Tunnel</h3>



<p>Đặt tên cho tunnel của bạn (ví dụ:&nbsp;my-local-server):codeBash</p>



<pre class="wp-block-code"><code>cloudflared tunnel create my-local-server</code></pre>



<p>Sau khi tạo xong, Cloudflare sẽ sinh ra một&nbsp;<strong>Tunnel ID</strong>&nbsp;và file credentials (thường nằm ở&nbsp;~/.cloudflared/).</p>



<h3 id="buoc-4-cau-hinh-dinh-tuyen-routing" class="wp-block-heading">Bước 4: Cấu hình định tuyến (Routing)</h3>



<p>Đây là bước quan trọng nhất để map domain về localhost.<br>Tạo file&nbsp;config.yml&nbsp;trong thư mục&nbsp;~/.cloudflared/&nbsp;(hoặc cùng thư mục chạy tool) với nội dung:codeYaml</p>



<pre class="wp-block-code"><code>tunnel: &lt;TUNNEL-NAME-HOẶC-ID&gt;
credentials-file: /duong/dan/den/file/json/credentials.json

ingress:
  # Service 1: API Backend
  - hostname: api.namdevlabs.com
    service: http://localhost:3000

  # Service 2: Webhook test (ZaloPay, Stripe)
  - hostname: hook.namdevlabs.com
    service: http://localhost:5678

  # Service 3: Dashboard Admin
  - hostname: admin.namdevlabs.com
    service: http://localhost:8080

  # Rule cuối cùng bắt buộc: Trả về 404 nếu không khớp domain nào
  - service: http_status:404</code></pre>



<h3 id="buoc-5-gan-dns-cho-subdomain" class="wp-block-heading">Bước 5: Gắn DNS cho Subdomain</h3>



<p>Bạn cần báo cho Cloudflare biết subdomain nào sẽ đi vào tunnel này.codeBash</p>



<pre class="wp-block-code"><code># Cú pháp: cloudflared tunnel route dns &lt;TUNNEL_NAME&gt; &lt;SUBDOMAIN&gt;
cloudflared tunnel route dns my-local-server api.namdevlabs.com
cloudflared tunnel route dns my-local-server hook.namdevlabs.com</code></pre>



<p>Lệnh này sẽ tự động tạo bản ghi CNAME trên Cloudflare Dashboard.</p>



<h3 id="buoc-6-kich-hoat-tunnel" class="wp-block-heading">Bước 6: Kích hoạt Tunnel</h3>



<p>Chạy tunnel với file config vừa tạo:codeBash</p>



<pre class="wp-block-code"><code>cloudflared tunnel run my-local-server</code></pre>



<p><em>Mẹo:</em>&nbsp;Nếu file config không nằm ở vị trí mặc định, dùng flag&nbsp;&#8211;config:<br>cloudflared tunnel &#8211;config /path/to/config.yml run</p>



<p> <strong>Xong!</strong> Bây giờ bạn có thể truy cập https://api.namdevlabs.com và thấy nó trỏ thẳng về localhost:3000 của bạn với HTTPS xanh mượt.</p>



<h2 id="5-meo-nang-cao-cho-pro-developer" class="wp-block-heading">5. Mẹo nâng cao cho Pro Developer</h2>



<ul class="wp-block-list">
<li><strong>Chạy ngầm (Service Mode):</strong> Để tunnel tự chạy khi khởi động máy (dành cho server home lab):codeBash<code>sudo cloudflared service install sudo systemctl start cloudflared</code></li>



<li><strong>Bảo mật Zero Trust:</strong> Bạn sợ người lạ vào link demo? Vào Cloudflare Dashboard -> Zero Trust, bật tính năng yêu cầu đăng nhập bằng Email/Google trước khi truy cập vào domain.</li>



<li><strong>Debug lỗi:</strong> Nếu không truy cập được, hãy thêm flag &#8211;loglevel debug khi chạy để xem log chi tiết.</li>
</ul>



<h2 id="6-tong-ket" class="wp-block-heading">6. Tổng kết</h2>



<p>Cloudflare Tunnel thực sự là một vũ khí lợi hại trong bộ công cụ của developer. Nó giúp bạn:</p>



<ol class="wp-block-list">
<li><strong>Tiết kiệm tiền:</strong> Không tốn phí server staging.</li>



<li><strong>Tiết kiệm thời gian:</strong> Không cần deploy để demo.</li>



<li><strong>An toàn:</strong> Không mở port, hạn chế tấn công DDoS.</li>
</ol>



<p>Hy vọng bài viết giúp bạn giải quyết được bài toán đau đầu về Webhook và Demo. Chúc bạn code vui!</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>
<p>The post <a href="https://blog.tomosia.com.vn/dua-localhost-len-internet-mien-phi-voi-cloudflare-tunnel-giai-phap-thay-the-ngrok-de-test-webhook-demo-app/">Đưa Localhost lên Internet miễn phí với Cloudflare Tunnel: Giải pháp thay thế Ngrok để Test Webhook &amp; Demo App</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/dua-localhost-len-internet-mien-phi-voi-cloudflare-tunnel-giai-phap-thay-the-ngrok-de-test-webhook-demo-app/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Forward proxy vs. Reverse proxy</title>
		<link>https://blog.tomosia.com.vn/forward-proxy-vs-reverse-proxy/</link>
					<comments>https://blog.tomosia.com.vn/forward-proxy-vs-reverse-proxy/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Fri, 08 Dec 2023 03:36:29 +0000</pubDate>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Web server]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2428</guid>

					<description><![CDATA[<p>Trong mạng máy tính, proxy là một máy chủ trung gian giữa client và các server khác trên&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/forward-proxy-vs-reverse-proxy/">Forward proxy vs. Reverse proxy</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Trong mạng máy tính, proxy là một máy chủ trung gian giữa client và các server khác trên Internet. Khi client gửi yêu cầu đến server, request sẽ được gửi đến proxy trước. Sau đó, proxy sẽ gửi request đến server thay cho client và trả về response cho client.</p>



<h2 id="tai-sao-lai-can-co-proxy" class="wp-block-heading">Tại sao lại cần có proxy?</h2>



<p>Thay vì kết nối trực tiếp tới server, client gửi request đến proxy để đánh giá và thực hiện những request đó.</p>



<ul class="wp-block-list">
<li><strong>Bảo vệ quyền riêng tư:</strong>&nbsp;Proxy có thể được sử dụng để ẩn địa chỉ IP của client,&nbsp;giúp bảo vệ quyền riêng tư của người dùng khỏi các trang web và ứng dụng mà họ truy cập.</li>



<li><strong>Cân bằng tải (Load balancing):</strong>&nbsp;Proxy có thể được sử dụng để phân phối lưu lượng truy cập giữa nhiều server,&nbsp;giúp cải thiện hiệu suất và độ tin cậy của hệ thống.</li>



<li><strong>Bảo mật (Security):</strong>&nbsp;Proxy có thể được sử dụng để lọc lưu lượng truy cập,&nbsp;giúp ngăn chặn các cuộc tấn công mạng.</li>



<li><strong>Giảm chi phí:</strong>&nbsp;Proxy có thể được sử dụng để truy cập các tài nguyên từ xa,&nbsp;giúp giảm chi phí cho doanh nghiệp.</li>
</ul>



<p>Có 2 loại proxy thường gặp đó là Forward proxy và Reverse proxy.</p>



<h2 id="forward-proxy" class="wp-block-heading">Forward proxy</h2>



<ul class="wp-block-list">
<li>Là một&nbsp;<em>client proxy</em>, hoạt động thay mặt cho các thiết bị clients để gửi yêu cầu đến server.</li>



<li>Nó <strong>nằm giữa clients và Internet</strong>, chuyển tiếp các requests của clients đến server thông qua Internet.</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-1024x512.png" alt="" class="wp-image-2437" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-1024x512.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-300x150.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-768x384.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-1536x768.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-2048x1024.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-380x190.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-800x400.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/forward_proxy_flow-1160x580.png 1160w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h4 id="use-cases" class="wp-block-heading">Use cases:</h4>



<ul class="wp-block-list">
<li><strong>Access control and content filtering: </strong>forward proxy kiểm tra các request có hợp lệ hay không để thực hiện forward hoặc block request.</li>



<li><strong>Log/Monitor request</strong>: quản lý và giám sát các request, thường được sử dụng trong môi trường công ty, trường học hay các tổ chức để kiểm soát việc truy cập Internet và bảo vệ khỏi các nội dung độc hại.</li>



<li><strong>User</strong> <strong>Privacy</strong>: forward proxy nằm giữa client và Internet, nó thay mặt client gửi request đến server qua Internet, có thể che dấu danh tính, địa chỉ IP và bảo vệ quyền riêng tư của người dùng thực.</li>



<li><strong>Cache responses</strong>: giảm độ trễ, tăng performance, tối ưu bandwidth, proxy cache lại response để trả về cho client trong các lần request tiếp theo thay vì request trực tiếp đến server.</li>
</ul>



<h4 id="services" class="wp-block-heading">Services:</h4>



<ul class="wp-block-list">
<li>Squid</li>



<li>Privoxy</li>



<li>VPN services</li>



<li>Cloudflare WARP</li>
</ul>



<p></p>



<h2 id="reverse-proxy" class="wp-block-heading">Reverse proxy</h2>



<ul class="wp-block-list">
<li>Là một <em>server proxy,</em> <strong>nằm giữa Internet và server</strong> để xử lý các request thay mặt cho server.</li>



<li>Chúng hoạt động như những servers bình thường. <strong>Reverse proxy</strong> chuyển tiếp request đến một hoặc nhiều server thật, kết quả sau đó trả về cho client, làm cho client không biết về những server thật nói trên.</li>



<li><strong>Reverse proxy</strong> được cài đặt trong một private network của một hoặc nhiều server, và tất cả lưu lượng truy cập đều phải đi qua proxy này.</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-1024x512.png" alt="" class="wp-image-2440" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-1024x512.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-300x150.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-768x384.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-1536x768.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-2048x1024.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-380x190.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-800x400.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/reverse_proxy_flow-1160x580.png 1160w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h4 id="use-cases-2" class="wp-block-heading">Use cases:</h4>



<ul class="wp-block-list">
<li><strong>Load Balancing:</strong> cân bằng tải, phân bổ lượng truy cập trang web đến các máy chủ khác nhau, nhằm mục đích tăng throughput, giảm letancy, phục vụ số lượng client lớn hơn.</li>



<li><strong>Security</strong>: có thể coi <strong>reverse proxy</strong> là một Web Application Firewall (WAF), có nhiệm vụ chặn các truy cập khả nghi đến server, làm rate limit, một phần nào đó ngăn chặn tấn công DDoS.</li>



<li><strong>Cache responses</strong>: giảm độ trễ, tăng performance, tối ưu bandwidth, proxy cache lại response để trả về cho client trong các lần request tiếp theo thay vì request trực tiếp đến server.</li>



<li><strong>SSL encryption/decryption</strong>: <strong>reverse proxy</strong> có thể xử lý việc SSL termination (dừng), giải mã lưu lượng được mã hóa SSL từ client và chuyển tiếp nó đến máy chủ phụ trợ ở dạng không được mã hóa và ngược lại.</li>
</ul>



<h4 id="services-2" class="wp-block-heading">Services:</h4>



<ul class="wp-block-list">
<li>Nginx</li>



<li>Apache HTTP</li>



<li>HAProxy</li>



<li>CDN services</li>
</ul>



<p></p>



<h2 id="diem-khac-biet-chinh" class="wp-block-heading"><strong>Điểm khác biệt chính</strong></h2>



<ul class="wp-block-list">
<li>Forward proxy: 
<ul class="wp-block-list">
<li>Xử lý request từ client tới Internet.</li>



<li>Bảo vệ quyền riêng tư client và lọc nội dung.</li>
</ul>
</li>



<li>Reverse proxy:
<ul class="wp-block-list">
<li>Xử lý request từ Internet tới web servers.</li>



<li>Performance và security.</li>
</ul>
</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="963" height="1024" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-963x1024.jpg" alt="" class="wp-image-2445" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-963x1024.jpg 963w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-282x300.jpg 282w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-768x816.jpg 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-380x404.jpg 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-800x850.jpg 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536-1160x1233.jpg 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/257642d6-9742-432b-9ca8-2a866dea04dd_1445x1536.jpg 1445w" sizes="auto, (max-width: 963px) 100vw, 963px" /></figure>



<p class="has-small-font-size"><em>Nguồn: <a href="https://blog.bytebytego.com">https://blog.bytebytego.com</a></em></p>



<p>Cảm ơn mọi người đã dành thời gian để đọc 🙇‍♂️</p>
<p>The post <a href="https://blog.tomosia.com.vn/forward-proxy-vs-reverse-proxy/">Forward proxy vs. Reverse proxy</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/forward-proxy-vs-reverse-proxy/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Cài đặt nginx trên Amazon Linux 2 (ECS Optimized)</title>
		<link>https://blog.tomosia.com.vn/cai-dat-nginx-tren-amazon-linux-2-ecs-optimized/</link>
					<comments>https://blog.tomosia.com.vn/cai-dat-nginx-tren-amazon-linux-2-ecs-optimized/#comments</comments>
		
		<dc:creator><![CDATA[Nguyen Anh]]></dc:creator>
		<pubDate>Fri, 24 Nov 2023 06:43:30 +0000</pubDate>
				<category><![CDATA[Kinh nghiệm]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[AWS]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=1978</guid>

					<description><![CDATA[<p>Đối với một số bài toàn sử dụng ECS docker và muốn chạy nhiều dịch vụ trên một&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/cai-dat-nginx-tren-amazon-linux-2-ecs-optimized/">Cài đặt nginx trên Amazon Linux 2 (ECS Optimized)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Đối với một số bài toàn sử dụng ECS docker và muốn chạy nhiều dịch vụ trên một EC2 để tiết kiệm chi phí thì đôi lúc chúng ta cần setup nginx và đây là cách hướng dẫn. Trong bài toán này hệ thống sử dụng ECS và EC2 Amazon Linux 2 (ECS Optimized).</p>



<p>1. <strong>Sử dụng kho lưu trữ Extra Packages for Enterprise Linux (EPEL)</strong>:</p>



<p>Kho lưu trữ EPEL thường chứa các gói phần mềm bổ sung mà có thể không có sẵn trong các kho lưu trữ mặc định của Amazon Linux.</p>



<p>Đầu tiên, cài đặt kho lưu trữ EPEL:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="sudo amazon-linux-extras install epel -y" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">sudo amazon</span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">linux</span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">extras install epel </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">y</span></span></code></pre></div>



<p>2<strong>. Cài đặt Nginx</strong>:</p>



<p>Sau khi thêm kho lưu trữ EPEL, bạn có thể thử cài đặt Nginx bằng <code>yum</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="sudo yum install nginx -y" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">sudo yum install nginx </span><span style="color: #F286C4">-</span><span style="color: #F6F6F4">y</span></span></code></pre></div>



<p>3. <strong>Khởi động và Kích hoạt Nginx</strong>:</p>



<p>Sau khi cài đặt, khởi động và kích hoạt dịch vụ Nginx:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="sudo systemctl start nginx
sudo systemctl enable nginx" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">sudo systemctl start nginx</span></span>
<span class="line"><span style="color: #F6F6F4">sudo systemctl enable nginx</span></span></code></pre></div>



<p>4. <strong>Kiểm tra Trạng thái Nginx</strong>:</p>



<p>Kiểm tra trạng thái của dịch vụ Nginx để xác nhận rằng nó đang chạy:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JavaScript</span><span role="button" tabindex="0" data-code="sudo systemctl status nginx" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">sudo systemctl status nginx</span></span></code></pre></div>
<p>The post <a href="https://blog.tomosia.com.vn/cai-dat-nginx-tren-amazon-linux-2-ecs-optimized/">Cài đặt nginx trên Amazon Linux 2 (ECS Optimized)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/cai-dat-nginx-tren-amazon-linux-2-ecs-optimized/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Logrotate: Compress và push file log lên S3</title>
		<link>https://blog.tomosia.com.vn/logrotate-compress-va-push-file-log-len-s3/</link>
					<comments>https://blog.tomosia.com.vn/logrotate-compress-va-push-file-log-len-s3/#comments</comments>
		
		<dc:creator><![CDATA[Thuc Phan]]></dc:creator>
		<pubDate>Wed, 22 Nov 2023 01:54:57 +0000</pubDate>
				<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Solution]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[logrotate]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=1939</guid>

					<description><![CDATA[<p>Log files giúp bạn hiểu rõ về lịch sử hoạt động của máy chủ nhưng chúng có thể&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/logrotate-compress-va-push-file-log-len-s3/">Logrotate: Compress và push file log lên S3</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Log files giúp bạn hiểu rõ về lịch sử hoạt động của máy chủ nhưng chúng có thể trở nên nặng nề và khó quản lý</p>



<p>Logrotate không chỉ giúp giảm kích thước file log một cách tự động mà còn có thể nén file để tiết kiệm không gian lưu trữ</p>



<p>Trong bài viết này, chúng ta sẽ tìm hiểu cách Logrotate được cấu hình để tự động nén và đẩy file lên S3, giúp bạn duy trì hệ thống được sạch sẽ và dễ quản lý hơn</p>



<h2 id="config" class="wp-block-heading">Config</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="cd /etc/logrotate.d
touch rails_log_rotation" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">cd</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/etc/logrotate.d</span></span>
<span class="line"><span style="color: #62E884">touch</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">rails_log_rotation</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="/home/ubuntu/path-to-deploy/current/log/production.log {
  daily
  dateext
  missingok
  rotate 90
  compress
  copytruncate
  lastaction
    aws s3 sync /home/ubuntu/path-to-deploy/current/log/ s3://path-to-bucket-name --exclude &quot;*&quot; --include &quot;production.log*.gz&quot; 2&gt;&amp;1
  endscript
  create 0644 root root
  su root root
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">/home/ubuntu/path-to-deploy/current/log/production.log</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">daily</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">dateext</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">missingok</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">rotate</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">90</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">compress</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">copytruncate</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">lastaction</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #62E884">aws</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">s3</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">sync</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/home/ubuntu/path-to-deploy/current/log/</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">s3://path-to-bucket-name</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">--exclude</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">*</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">--include</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">production.log*.gz</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">2&gt;&amp;1</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">endscript</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">create</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">0644</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">root</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">root</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">su</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">root</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">root</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p><code><strong>home/ubuntu/path-to-deploy/current/log/production.log</strong></code></p>



<ul class="wp-block-list">
<li>Link đến file log cần được quản lý bởi logrotate</li>
</ul>



<p><code><strong>daily</strong></code></p>



<ul class="wp-block-list">
<li>Thực hiện xoay vòng log hàng ngày (được thực hiện bằng cron-job)</li>
</ul>



<ul class="wp-block-list">
<li>Có nghĩa là file <code>/home/ubuntu/path-to-deploy/current/log/production.log</code> sẽ được làm mới mỗi ngày</li>
</ul>



<ul class="wp-block-list">
<li>Check cron configuration</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="cd /etc/
ls | grep cron
cron.d
cron.daily
cron.hourly
cron.monthly
cron.weekly
crontab" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">cd</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/etc/</span></span>
<span class="line"><span style="color: #62E884">ls</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">|</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">grep</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">cron</span></span>
<span class="line"><span style="color: #62E884">cron.d</span></span>
<span class="line"><span style="color: #62E884">cron.daily</span></span>
<span class="line"><span style="color: #62E884">cron.hourly</span></span>
<span class="line"><span style="color: #62E884">cron.monthly</span></span>
<span class="line"><span style="color: #62E884">cron.weekly</span></span>
<span class="line"><span style="color: #62E884">crontab</span></span></code></pre></div>



<p><code><strong>dateext</strong></code></p>



<ul class="wp-block-list">
<li>Thêm ngày vào filename của file log được xoay vòng để đảm bảo không bị trùng tên</li>
</ul>



<p><code><strong>missingok</strong></code></p>



<ul class="wp-block-list">
<li>Cho phép logrotate bỏ qua nếu không tìm thấy file</li>
</ul>



<p><code><strong>rotate 90</strong></code></p>



<ul class="wp-block-list">
<li>Giữ lại tối đa 90 file logs đã quay vòng</li>



<li>Trong trường hợp này, có thể hiểu là cho phép lưu trữ file logs của 90 ngày gần nhất</li>
</ul>



<p><code><strong>compress</strong></code></p>



<ul class="wp-block-list">
<li>Nén file log đã xoay vòng để tiết kiệm không gian lưu trữ</li>
</ul>



<p><code><strong>copytruncate</strong></code></p>



<ul class="wp-block-list">
<li>Sao chép nội dung từ file cũ vào file mới, sau đó quay lại đầu file cũ và cắt bớt nó. Điều này giúp logrotate có thể xoay vòng mà không làm ảnh hưởng đến ứng dụng đang chạy</li>
</ul>



<p><code><strong>lastaction</strong></code> và <code><strong>endscript</strong></code> </p>



<ul class="wp-block-list">
<li>Bọc một đoạn code hoặc command được thực thi sau khi xoay vòng</li>



<li>Trong trường hợp này, sử dụng AWS CLI để đồng bộ hóa các file logs đã xoay vòng với Amazon S3</li>



<li>Loại trừ tất cả các files, chỉ bao gồm các file logs đã nén <code>(production.log*.gz)</code></li>



<li>Lưu ý
<ul class="wp-block-list">
<li>Bạn cần thay thế <code>path-to-bucket-name</code> bằng đường dẫn thích hợp đến S3 Bucket của bạn</li>



<li>EC2 phải tương tác được với S3 Bucket của bạn (có thể dùng <code>aws configure</code> hoặc <code>attached IAM role</code> đều được)</li>
</ul>
</li>
</ul>



<p><code><strong>create 0644 root root</strong></code></p>



<ul class="wp-block-list">
<li>Tạo một file log mới với quyền truy cập <code>0644</code> và chủ sở hữu là <code>root</code></li>
</ul>



<p><code><strong>su root root</strong></code></p>



<ul class="wp-block-list">
<li>Chuyển quyền sở hữu của file sang <code>root</code> sau khi tạo mới để đảm bảo an toàn và bảo mật</li>
</ul>



<p><strong>Tóm lại</strong></p>



<ul class="wp-block-list">
<li>Config này giúp bạn quản lý và tự động xoay vòng file <code>production.log</code> mỗi ngày</li>



<li>Nén file để tiết kiệm không gian và đồng thời sao chép lên Amazon S3 để lưu trữ</li>
</ul>



<p><strong>Manual test config</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">ShellScript</span><span role="button" tabindex="0" data-code="sudo logrotate -f /etc/logrotate.conf" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">sudo</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">logrotate</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-f</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">/etc/logrotate.conf</span></span></code></pre></div>
<p>The post <a href="https://blog.tomosia.com.vn/logrotate-compress-va-push-file-log-len-s3/">Logrotate: Compress và push file log lên S3</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/logrotate-compress-va-push-file-log-len-s3/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title>Cách tạo swap file trên Ubuntu</title>
		<link>https://blog.tomosia.com.vn/cach-tao-swap-file-tren-ubuntu/</link>
					<comments>https://blog.tomosia.com.vn/cach-tao-swap-file-tren-ubuntu/#comments</comments>
		
		<dc:creator><![CDATA[Thâm Davies]]></dc:creator>
		<pubDate>Thu, 05 Oct 2023 06:27:54 +0000</pubDate>
				<category><![CDATA[Web Server]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=408</guid>

					<description><![CDATA[<p>Trước khi bắt đầu Swap không phải là sự thay thế cho bộ nhớ vật lý, nó là&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/cach-tao-swap-file-tren-ubuntu/">Cách tạo swap file trên Ubuntu</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading" id="trước-khi-bắt-đầu"><span id="truoc-khi-bat-dau"><a href="https://www.thamdavies.com/blog/cach-tao-swap-file-tren-ubuntu#tr%C6%B0%E1%BB%9Bc-khi-b%E1%BA%AFt-%C4%91%E1%BA%A7u"></a>Trước khi bắt đầu</span></h3>



<p>Swap không phải là sự thay thế cho bộ nhớ vật lý, nó là một phần của ổ đĩa, chính vì thế nó có thời gian truy cập chậm hơn bộ nhớ vật lý. Nếu hệ thống của bạn liên tục hết bộ nhớ, bạn nên bổ sung thêm RAM.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Các thao tác của mình được thực hiện trên Ubuntu 20.04</p>
</blockquote>



<h3 class="wp-block-heading" id="tạo-một-swap-file"><span id="tao-mot-swap-file"><a href="https://www.thamdavies.com/blog/cach-tao-swap-file-tren-ubuntu#t%E1%BA%A1o-m%E1%BB%99t-swap-file"></a>Tạo một Swap File</span></h3>



<p>Tại sao chúng ta cần swap?</p>



<p>Có một tình huống hết sức cảm động như thế này. Bạn đang chạy một hệ thống săn voucher, trong các ngày bình thường thì lượng user truy cập không nhiều vì có voucher đâu mà săn 😛&nbsp;</p>



<p>Bỗng dưng tới ngày 12.12, hay các ngày số đẹp mà tiki hay lazada hay dụ các bạn vào hệ thống để săn sale ấy, thì lượng truy cập tăng đột biến và đây là cấu hình server của bạn,</p>



<ul class="wp-block-list">
<li>ổ cứng: 40GB</li>



<li>RAM: 2GB</li>
</ul>



<p>khiến cho RAM server của bạn ngỏm củ tỏi dẫn tới việc sập hệ thống. Tới đây các bạn đã thấy cảm động chưa 😅</p>



<p>Nguyên nhân sập hệ thống thì đã rõ, là do hết dung lượng RAM, vậy làm sao để giải quyết vấn đề này?</p>



<ul class="wp-block-list">
<li>Mua thêm 2GB RAM → 4GB</li>



<li><strong>Tận dụng swap space</strong>&nbsp;→ we are here, chúng ta sẽ mượn của ổ đĩa 2GB để tạo swap space, server sẽ sử dụng dung lượng mình đã swap này khi RAM đạt tới ngưỡng nhất định gọi là&nbsp;<strong>swappiness</strong>, mình sẽ nói ở phần dưới nhé.</li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Lưu ý đây là cách giải quyết tạm thời đề phòng server sập thôi nhé, đối với mình thì cách đầu tiên là ngon nhất, vì nó tận dụng tối đa sức mạnh của RAM</p>
</blockquote>



<p>Trong bài này, mình sẽ tạo swap file 2GB. Nếu bạn muốn tạo nhiều hơn thì thay 2G thành con số bạn mong muốn.</p>



<ol class="wp-block-list">
<li>Tạo swap file:</li>
</ol>



<pre class="wp-block-code"><code>sudo fallocate -l 2G /swapfile
</code></pre>



<ol class="wp-block-list" start="2">
<li>Set quyền cho file để user không nằm trong sudo không đọc và ghi file:</li>
</ol>



<pre class="wp-block-code"><code>sudo chmod 600 /swapfile
</code></pre>



<ol class="wp-block-list" start="3">
<li>Thiết lập swapfile dưới dạng vùng hoán đổi Linux:</li>
</ol>



<pre class="wp-block-code"><code>sudo mkswap /swapfile
</code></pre>



<p>Output</p>



<pre class="wp-block-code"><code>Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
no label, UUID=fde7d2c8-06ea-400a-9027-fd731d8ab4c8
</code></pre>



<ol class="wp-block-list" start="4">
<li>Sau khi tạo thì enable nó lên để sử dụng nhé</li>
</ol>



<pre class="wp-block-code"><code>sudo swapon /swapfile
</code></pre>



<p>Để thực hiện thay đổi vĩnh viễn, hãy mở tệp&nbsp;<code>/etc/fstab</code>&nbsp;và thêm dòng sau:</p>



<pre class="wp-block-code"><code>/swapfile swap swap defaults 0 0
</code></pre>



<ol class="wp-block-list" start="5">
<li>Xác nhận swap file đã được enable hay chưa:</li>
</ol>



<pre class="wp-block-code"><code>sudo swapon --show
</code></pre>



<p>Output</p>



<pre class="wp-block-code"><code>NAME      TYPE      SIZE  USED PRIO
/swapfile file        2G    0B   -1
</code></pre>



<pre class="wp-block-code"><code>sudo free -h
</code></pre>



<p>Output</p>



<pre class="wp-block-code"><code>              total        used        free      shared  buff/cache   available
Mem:          981Mi        97Mi        68Mi       0.0Ki       814Mi       735Mi
Swap:         2.0Gi        10Mi       1.9Gi
</code></pre>



<h3 class="wp-block-heading" id="điều-chỉnh-giá-trị-swap---swappiness"><span id="dieu-chinh-gia-tri-swap-swappiness"><a href="https://www.thamdavies.com/blog/cach-tao-swap-file-tren-ubuntu#%C4%91i%E1%BB%81u-ch%E1%BB%89nh-gi%C3%A1-tr%E1%BB%8B-swap---swappiness"></a>Điều chỉnh giá trị swap &#8211; swappiness</span></h3>



<p>Swappiness cho phép chúng ta thiết lập thông số để Linux sử dụng swap space, trên Ubuntu giá trị này mặc định là 60, bạn kiểm tra bằng lệnh dưới đây:</p>



<pre class="wp-block-code"><code>cat /proc/sys/vm/swappiness
</code></pre>



<p>Vậy làm thế nào để sửa?</p>



<pre class="wp-block-code"><code>sudo vim /etc/sysctl.conf
# Sau đó thêm cuối dòng
vm.swappiness=90

# Chạy lệnh dưới đây để reload lại config ở trên nhé
sudo sysctl -p /etc/sysctl.conf
</code></pre>



<h3 class="wp-block-heading" id="xoá-swap-file"><span id="xoa-swap-file"><a href="https://www.thamdavies.com/blog/cach-tao-swap-file-tren-ubuntu#xo%C3%A1-swap-file"></a>Xoá swap file</span></h3>



<pre class="wp-block-code"><code>sudo swapoff -v /swapfile
sudo rm /swapfile
</code></pre>



<h3 class="wp-block-heading" id="-lời-cảm-ơn"><span id="%f0%9f%92%8c-loi-cam-on"><a href="https://www.thamdavies.com/blog/cach-tao-swap-file-tren-ubuntu#-l%E1%BB%9Di-c%E1%BA%A3m-%C6%A1n"></a>💌 Lời cảm ơn</span></h3>



<p>Cảm ơn các bạn đã theo dõi hết bài 😍. Trên đây là toàn bộ những gì chưa sẻ về&nbsp;<strong>Cách tạo swap file trên Ubuntu</strong>&nbsp;hy vọng nó giúp bạn phần nào trong hành trình code của bạn. 💪</p>
<p>The post <a href="https://blog.tomosia.com.vn/cach-tao-swap-file-tren-ubuntu/">Cách tạo swap file trên Ubuntu</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/cach-tao-swap-file-tren-ubuntu/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn cấu hình Basic Authentication trên Nginx</title>
		<link>https://blog.tomosia.com.vn/huong-dan-cau-hinh-basic-authentication-tren-nginx/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-cau-hinh-basic-authentication-tren-nginx/#comments</comments>
		
		<dc:creator><![CDATA[Việt Nguyễn]]></dc:creator>
		<pubDate>Tue, 26 Sep 2023 21:43:32 +0000</pubDate>
				<category><![CDATA[Web Server]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[basic auth]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=45</guid>

					<description><![CDATA[<p>Trong quá trình phát triển web, có những lúc chúng ta sẽ cần phải giới hạn người dùng&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-cau-hinh-basic-authentication-tren-nginx/">Hướng dẫn cấu hình Basic Authentication trên Nginx</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Trong quá trình phát triển web, có những lúc chúng ta sẽ cần phải giới hạn người dùng truy cập đến website của mình và để giới hạn chúng ta có thể yêu cầu người dùng xác thực qua&nbsp;<code>tài khoản</code>&nbsp;và&nbsp;<code>mật khẩu</code>.</p>



<p>Bài viết này sẽ hướng dẫn cho bạn các bước để cấu hình <a rel="noreferrer noopener" href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/" target="_blank"><strong>Basic Authentication</strong></a> bảo vệ server <a rel="noreferrer noopener" href="https://www.nginx.com/" target="_blank"><strong>Nginx</strong></a> chạy trên môi trường CentOS và Ubuntu Server.</p>



<h2 id="1-cai-dat-htpasswd" class="wp-block-heading">1. Cài đặt htpasswd</h2>



<p>Tiến hành cài gói <strong>apache2-utils</strong> (Debian, Ubuntu) hoặc <strong>httpd-tools</strong> (CentOS)</p>



<p><strong>Ubuntu, Debian:</strong></p>



<pre class="wp-block-preformatted">sudo apt-get update 
sudo apt-get install apache2-utils </pre>



<p><strong>CentOS</strong>:</p>



<pre class="wp-block-preformatted">sudo yum install httpd-tools </pre>



<h2 id="2-tao-password-file" class="wp-block-heading">2. Tạo Password File</h2>



<p>Sau khi htpasswd được cài đặt, hãy tạo file mật khẩu bằng câu lệnh sau với flag -c nếu file <strong>&#8220;.htpasswd&#8221;</strong> chưa tồn tại:</p>



<pre class="wp-block-preformatted">sudo htpasswd -c /etc/nginx/.htpasswd username </pre>



<p>Thay thế <strong>&#8220;username&#8221;</strong> với tên đăng nhập bạn mong muốn. Sẽ có prompt chờ bạn nhập thông tin cho user trên</p>



<p>Lưu ý: nếu đã có file  <strong>/etc/nginx/.htpass</strong>wd rồi thì KHÔNG sử dụng <strong>flag -c</strong></p>



<pre class="wp-block-preformatted">sudo htpasswd /etc/nginx/.htpasswd tomosia-admin</pre>



<p>Nếu user tomosia-admin đã tồn tại thì lệnh trên sẽ cập nhật lại password</p>



<h2 id="3-cau-hinh-xac-thuc-mat-khau-cho-nginx" class="wp-block-heading">3. Cấu hình xác thực mật khẩu cho Nginx</h2>



<p>Mở tệp cấu hình nginx của bạn, thường nó sẽ nằm ở đường dẫn <strong>/etc/nginx/nginx.conf</strong> hoặc cấu hình cho 1 site nhất định tại <strong>/etc/nginx/sites-available/example.com</strong> hoặc <strong>/etc/nginx/conf.d/example.com</strong>. Sau đó thêm dòng sau vào file cấu hình</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="auth_basic &quot;Restricted Access&quot;;
auth_basic_user_file /etc/nginx/.htpasswd;" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">auth_basic </span><span style="color: #F1FA8C">&quot;Restricted Access&quot;</span><span style="color: #F8F8F2">;</span></span>
<span class="line"><span style="color: #FF79C6">auth_basic_user_file </span><span style="color: #F8F8F2">/etc/nginx/.htpasswd;</span></span></code></pre></div>



<p>Nếu muốn thiết lập basic auth cho toàn bộ website trên server thì cần thêm vào khối <strong>http</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="http {
   ...
   auth_basic &quot;Restricted Access!&quot;; 
   auth_basic_user_file /etc/nginx/.htpasswd;
   ...
}" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">http</span><span style="color: #F8F8F2"> {</span></span>
<span class="line"><span style="color: #F8F8F2">   ...</span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #FF79C6"> auth_basic </span><span style="color: #F1FA8C">&quot;Restricted Access!&quot;</span><span style="color: #F8F8F2">; </span></span>
<span class="line"><span style="color: #F8F8F2">  </span><span style="color: #FF79C6"> auth_basic_user_file </span><span style="color: #F8F8F2">/etc/nginx/.htpasswd;</span></span>
<span class="line"><span style="color: #F8F8F2">   ...</span></span>
<span class="line"><span style="color: #F8F8F2">}</span></span></code></pre></div>



<p>Nếu muốn thiết lập cho một website hoặc domain nhất định cần thêm vào khối <strong>server</strong> tương ứng</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="server {
    ...
    auth_basic &quot;Restricted Access!&quot;;
    auth_basic_user_file /etc/nginx/.htpasswd;
    ...
}" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">server</span><span style="color: #F8F8F2"> {</span></span>
<span class="line"><span style="color: #F8F8F2">    ...</span></span>
<span class="line"><span style="color: #F8F8F2">   </span><span style="color: #FF79C6"> auth_basic </span><span style="color: #F1FA8C">&quot;Restricted Access!&quot;</span><span style="color: #F8F8F2">;</span></span>
<span class="line"><span style="color: #F8F8F2">   </span><span style="color: #FF79C6"> auth_basic_user_file </span><span style="color: #F8F8F2">/etc/nginx/.htpasswd;</span></span>
<span class="line"><span style="color: #F8F8F2">    ...</span></span>
<span class="line"><span style="color: #F8F8F2">}</span></span></code></pre></div>



<p>Nếu muốn thiết lập cho một location nhất định hãy thêm vào khối <strong>location</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#282A36"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="location /swagger-ui/ {
    ...
    auth_basic &quot;Restricted Access!&quot;;
    auth_basic_user_file /etc/nginx/.htpasswd;
    ...
}" style="color:#F8F8F2;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #FF79C6">location</span><span style="color: #F8F8F2"> /swagger-ui/ {</span></span>
<span class="line"><span style="color: #F8F8F2">    ...</span></span>
<span class="line"><span style="color: #F8F8F2">   </span><span style="color: #FF79C6"> auth_basic </span><span style="color: #F1FA8C">&quot;Restricted Access!&quot;</span><span style="color: #F8F8F2">;</span></span>
<span class="line"><span style="color: #F8F8F2">   </span><span style="color: #FF79C6"> auth_basic_user_file </span><span style="color: #F8F8F2">/etc/nginx/.htpasswd;</span></span>
<span class="line"><span style="color: #F8F8F2">    ...</span></span>
<span class="line"><span style="color: #F8F8F2">}</span></span></code></pre></div>



<h2 id="4-restart-nginx" class="wp-block-heading">4. Restart Nginx</h2>



<pre class="wp-block-preformatted">sudo service nginx reload #debian/ubuntu
sudo systemctl restart nginx #redhat/centos</pre>



<h2 id="5-kiem-tra" class="wp-block-heading">5. Kiểm tra</h2>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="793" height="468" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/10/image.png" alt="" class="wp-image-282" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image.png 793w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-300x177.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-768x453.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/10/image-380x224.png 380w" sizes="auto, (max-width: 793px) 100vw, 793px" /></figure>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>



<p></p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-cau-hinh-basic-authentication-tren-nginx/">Hướng dẫn cấu hình Basic Authentication trên Nginx</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-cau-hinh-basic-authentication-tren-nginx/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
	</channel>
</rss>
