ในยุคที่เครือข่ายอินเทอร์เน็ตมีบทบาทสำคัญในชีวิตประจำวัน การควบคุมการเข้าถึง (Access Control) และการปกปิดตัวตน (Anonymization) กลายเป็นประเด็นหลักของ Network Security Proxy Server และ Network Tunneling เป็นเทคโนโลยีที่ช่วยให้บรรลุเป้าหมายเหล่านี้ได้อย่างมีประสิทธิภาพ
วัตถุประสงค์ของบทเรียน:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836', 'attributeBackgroundColorOdd': '#3c3836', 'attributeBackgroundColorEven': '#504945' }}}%%
graph TD
A["🌐 อินเทอร์เน็ต
(Internet)"]
B["🔒 Proxy Server
(พร็อกซี่เซิร์ฟเวอร์)"]
C["🏠 เครือข่ายภายใน
(Internal Network)"]
D["🔑 SSH Tunnel
(อุโมงค์ SSH)"]
E["🔄 NAT Gateway
(เกตเวย์ NAT)"]
F["💻 Client
(เครื่องลูกข่าย)"]
G["🖥️ Server
(เซิร์ฟเวอร์)"]
F -->|"ส่งคำขอ (Request)"| B
B -->|"Forward Request"| A
A -->|"Response"| B
B -->|"ส่งกลับ (Relay)"| F
F -->|"เข้ารหัส (Encrypted)"| D
D -->|"Tunnel Traffic"| G
C -->|"Private IP"| E
E -->|"Public IP"| A
style A fill:#458588,color:#ebdbb2,stroke:#83a598
style B fill:#d65d0e,color:#ebdbb2,stroke:#fe8019
style C fill:#689d6a,color:#ebdbb2,stroke:#8ec07c
style D fill:#b16286,color:#ebdbb2,stroke:#d3869b
style E fill:#d79921,color:#282828,stroke:#fabd2f
style F fill:#3c3836,color:#ebdbb2,stroke:#a89984
style G fill:#3c3836,color:#ebdbb2,stroke:#a89984
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
flowchart TB
subgraph ERA1["ยุคที่ 1: 1990s"]
A1["1994 - Proxy แรกของโลก - CERN httpd proxy"]
A2["1996 - Squid Proxy - พัฒนาครั้งแรก"]
end
subgraph ERA2["ยุคที่ 2: 2000s"]
B1["2002 - SSH Tunneling - แพร่หลาย"]
B2["2004 - SOCKS5 RFC - (RFC 1928)"]
end
subgraph ERA3["ยุคที่ 3: 2010s"]
C1["2012 - Reverse Proxy - Nginx/HAProxy"]
C2["2015 - Docker NAT - Container Networking"]
end
subgraph ERA4["ยุคที่ 4: 2020s"]
D1["2020 - WireGuard - Modern Tunneling"]
D2["2024 - Zero Trust - Proxy Architecture"]
end
A1 --> A2 --> B1 --> B2 --> C1 --> C2 --> D1 --> D2
style ERA1 fill:#32302f,stroke:#d79921,color:#ebdbb2
style ERA2 fill:#32302f,stroke:#458588,color:#ebdbb2
style ERA3 fill:#32302f,stroke:#689d6a,color:#ebdbb2
style ERA4 fill:#32302f,stroke:#b16286,color:#ebdbb2
Proxy Server (พร็อกซี่เซิร์ฟเวอร์) คือระบบที่ทำหน้าที่เป็น ตัวกลาง (Intermediary) ระหว่าง Client และ Server โดย Client จะส่ง Request ไปยัง Proxy แทนที่จะส่งตรงไปยัง Destination Server
คุณสมบัติหลักของ Proxy:
หลักการทำงานพื้นฐาน (3 ขั้นตอน):
Forward Proxy คือ Proxy ที่ทำงานในฝั่ง Client โดยรับคำขอจาก Client แล้วส่งต่อไปยังอินเทอร์เน็ต
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
sequenceDiagram
participant C as 💻 Client
(192.168.1.100)
participant P as 🔄 Forward Proxy
(192.168.1.1:3128)
participant S as 🌐 Web Server
(93.184.216.34)
Note over C,S: Forward Proxy Flow
C->>P: GET http://example.com HTTP/1.1
Host: example.com
Note over P: ตรวจสอบ ACL
บันทึก Log
P->>S: GET http://example.com HTTP/1.1
Via: 1.1 proxy.local
S-->>P: HTTP/1.1 200 OK
Content: HTML...
Note over P: Cache Response
P-->>C: HTTP/1.1 200 OK
(X-Cache: MISS)
กรณีการใช้งาน Forward Proxy:
#!/usr/bin/env python3
"""
ตัวอย่าง Forward Proxy อย่างง่ายด้วย Python
---
โมดูลนี้สาธิตการทำงานของ Forward Proxy เบื้องต้น
เพื่อการเรียนรู้เท่านั้น ไม่เหมาะสำหรับ Production
"""
import socket
import threading
import select
# ข้อมูลตัวอย่าง: กำหนดค่า Proxy
PROXY_HOST = "0.0.0.0"
PROXY_PORT = 8080
BUFFER_SIZE = 4096
def handle_client(client_socket: socket.socket, client_addr: tuple) -> None:
"""
จัดการการเชื่อมต่อจาก Client แต่ละราย
Args:
client_socket: Socket ของ Client ที่เชื่อมต่อ
client_addr: (IP, Port) ของ Client
"""
print(f"[+] รับการเชื่อมต่อจาก: {client_addr[0]}:{client_addr[1]}")
try:
# รับ Request จาก Client
request = client_socket.recv(BUFFER_SIZE)
if not request:
return
# แยกวิเคราะห์ HTTP Request
first_line = request.decode("utf-8", errors="ignore").split(" - ")[0]
method, url, _ = first_line.split(" ", 2)
print(f"[*] Method: {method}, URL: {url}")
# สกัด Host และ Port จาก URL
if "://" in url:
host_port = url.split("://")[1].split("/")[0]
else:
host_port = url.split("/")[0]
# กำหนด Port เริ่มต้น
if ":" in host_port:
host, port = host_port.split(":", 1)
port = int(port)
else:
host = host_port
port = 80 # HTTP เริ่มต้น
print(f"[*] กำลังเชื่อมต่อไปยัง: {host}:{port}")
# เชื่อมต่อไปยัง Destination Server
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.connect((host, port))
# ส่ง Request ไปยัง Server
server_socket.sendall(request)
# รับและส่งต่อ Response
while True:
readable, _, _ = select.select(
[client_socket, server_socket], [], [], 5
)
if not readable:
break
for sock in readable:
data = sock.recv(BUFFER_SIZE)
if not data:
break
# ส่งต่อข้อมูลระหว่าง Client และ Server
if sock is server_socket:
client_socket.sendall(data)
else:
server_socket.sendall(data)
server_socket.close()
print(f"[+] ปิดการเชื่อมต่อ: {host}:{port}")
except Exception as e:
print(f"[-] Error: {e}")
finally:
client_socket.close()
def start_proxy():
"""เริ่มต้น Forward Proxy Server"""
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((PROXY_HOST, PROXY_PORT))
server.listen(100)
print(f"[*] Simple Forward Proxy รันอยู่ที่ {PROXY_HOST}:{PROXY_PORT}")
print("[*] กด Ctrl+C เพื่อหยุดการทำงาน")
while True:
client_sock, addr = server.accept()
# สร้าง Thread ใหม่สำหรับแต่ละ Client
t = threading.Thread(
target=handle_client,
args=(client_sock, addr)
)
t.daemon = True
t.start()
# ตัวอย่างการใช้งาน
if __name__ == "__main__":
# ทดสอบด้วย: curl -x http://localhost:8080 http://example.com
start_proxy()
การติดตั้ง Squid บน Ubuntu/Debian:
# ติดตั้ง Squid
sudo apt update && sudo apt install squid -y
# แก้ไขไฟล์ configuration หลัก
sudo nano /etc/squid/squid.conf
ตัวอย่างไฟล์ squid.conf สำหรับองค์กร:
# กำหนด ACL สำหรับเครือข่ายภายใน
acl localnet src 192.168.1.0/24
acl localnet src 10.0.0.0/8
# กำหนด Port ที่อนุญาต
acl Safe_ports port 80 # HTTP
acl Safe_ports port 443 # HTTPS
acl Safe_ports port 21 # FTP
# บล็อกเว็บไซต์ที่ไม่เหมาะสม
acl blocked_sites dstdomain "/etc/squid/blocked_sites.txt"
http_access deny blocked_sites
# อนุญาตเครือข่ายภายใน
http_access allow localnet Safe_ports
# ปิดการเข้าถึงอื่น ๆ
http_access deny all
# กำหนด Port ของ Proxy
http_port 3128
# ขนาด Cache
cache_mem 256 MB
maximum_object_size 10 MB
cache_dir ufs /var/spool/squid 1000 16 256
# Log format
access_log /var/log/squid/access.log squid
Reverse Proxy คือ Proxy ที่ทำงานในฝั่ง Server รับคำขอจากอินเทอร์เน็ตแทน Backend Server และส่งต่อไปยัง Server ที่เหมาะสม
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph LR
subgraph INTERNET["🌐 อินเทอร์เน็ต"]
C1["👤 User A"]
C2["👤 User B"]
C3["👤 User C"]
end
subgraph DMZ["🔒 DMZ Zone"]
RP["🔄 Reverse Proxy
Nginx/HAProxy
203.0.113.1:443"]
end
subgraph INTERNAL["🏠 Internal Network"]
S1["🖥️ App Server 1
10.0.0.1:8001"]
S2["🖥️ App Server 2
10.0.0.2:8002"]
S3["🖥️ DB Server
10.0.0.3:5432"]
end
C1 -->|"HTTPS :443"| RP
C2 -->|"HTTPS :443"| RP
C3 -->|"HTTPS :443"| RP
RP -->|"/api/* → 8001"| S1
RP -->|"/web/* → 8002"| S2
S1 -->|"DB Query"| S3
S2 -->|"DB Query"| S3
style INTERNET fill:#32302f,stroke:#458588,color:#ebdbb2
style DMZ fill:#32302f,stroke:#d65d0e,color:#ebdbb2
style INTERNAL fill:#32302f,stroke:#689d6a,color:#ebdbb2
style RP fill:#d65d0e,color:#ebdbb2,stroke:#fe8019
style S1 fill:#458588,color:#ebdbb2,stroke:#83a598
style S2 fill:#458588,color:#ebdbb2,stroke:#83a598
style S3 fill:#689d6a,color:#ebdbb2,stroke:#8ec07c
ประโยชน์ของ Reverse Proxy:
# /etc/nginx/sites-available/reverse-proxy.conf
# กำหนด Upstream (Backend Servers)
upstream app_backend {
# Round Robin Load Balancing
server 10.0.0.1:8001 weight=3;
server 10.0.0.2:8002 weight=2;
server 10.0.0.3:8003 backup; # Backup server
keepalive 32;
}
server {
listen 443 ssl http2;
server_name www.example.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/example.crt;
ssl_certificate_key /etc/ssl/private/example.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000";
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeout settings
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Cache static files
location ~* \.(jpg|jpeg|png|css|js|ico)$ {
proxy_pass http://app_backend;
proxy_cache_valid 200 1d;
add_header Cache-Control "public, max-age=86400";
}
}
SOCKS (Socket Secure) Proxy เป็น Protocol ระดับต่ำกว่า HTTP Proxy โดยทำงานที่ Layer 5 (Session Layer) ของโมเดล OSI สามารถจัดการ Traffic ได้ทุกประเภท ไม่จำกัดแค่ HTTP
เวอร์ชันของ SOCKS:
| เวอร์ชัน | RFC | ความสามารถหลัก | การยืนยันตัวตน |
|---|---|---|---|
| SOCKS4 | — | TCP เท่านั้น | ไม่รองรับ |
| SOCKS4a | — | TCP + DNS Resolution | ไม่รองรับ |
| SOCKS5 | RFC 1928 | TCP + UDP + DNS | รองรับ (Username/Password, GSS-API) |
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
sequenceDiagram
participant C as 💻 Client
participant S5 as 🔒 SOCKS5 Proxy
participant D as 🌐 Destination
Note over C,D: SOCKS5 Handshake Protocol
C->>S5: [1] Greeting
VER=5, NMETHODS=1, METHODS=[0x00]
S5-->>C: [2] Method Selection
VER=5, METHOD=0x00 (No Auth)
C->>S5: [3] Connection Request
VER=5, CMD=CONNECT, ATYP=DOMAINNAME
DST.ADDR="example.com", DST.PORT=80
Note over S5: Resolve DNS
Connect to example.com:80
S5-->>C: [4] Reply
VER=5, REP=0x00 (Success)
BND.ADDR=93.184.216.34, BND.PORT=52341
Note over C,D: Data Transfer Phase
C->>S5: [5] HTTP GET /
S5->>D: HTTP GET /
D-->>S5: HTTP 200 OK
S5-->>C: HTTP 200 OK
โครงสร้าง SOCKS5 Packet (ตัวอย่างการคำนวณ):
ตัวอย่างข้อมูล: Client ต้องการเชื่อมต่อไปยัง example.com:80
Greeting Message (ขั้นตอนที่ 1):
Byte[0] = 0x05 → SOCKS version 5
Byte[1] = 0x01 → จำนวน Authentication Methods = 1
Byte[2] = 0x00 → Method: No Authentication
Connection Request (ขั้นตอนที่ 3):
Byte[0] = 0x05 → Version 5
Byte[1] = 0x01 → CMD: CONNECT
Byte[2] = 0x00 → Reserved
Byte[3] = 0x03 → ATYP: Domain Name
Byte[4] = 0x0B → ความยาวของ Domain = 11 ตัวอักษร
Byte[5-15] → "example.com" ใน ASCII
Byte[16-17] → Port 80 = 0x00 0x50
การคำนวณขนาด Packet:
โดยที่:
ตัวอย่างการคำนวณ: example.com มี 11 ตัวอักษร
#!/usr/bin/env python3
"""
ตัวอย่างการสร้าง SOCKS5 Connection Request Packet
---
แสดงวิธีการสร้าง Packet ตาม RFC 1928
"""
import struct
import socket
def build_socks5_request(host: str, port: int) -> bytes:
"""
สร้าง SOCKS5 Connection Request Packet
Args:
host: ที่อยู่ Destination (Domain Name หรือ IP)
port: Port ปลายทาง
Returns:
bytes: SOCKS5 Request Packet
Example:
>>> packet = build_socks5_request("example.com", 80)
>>> print(f"Packet size: {len(packet)} bytes")
Packet size: 18 bytes
"""
# Encode domain name เป็น bytes
host_bytes = host.encode("utf-8")
host_len = len(host_bytes)
# คำนวณขนาด Packet
# 4 (header) + 1 (len) + host_len + 2 (port)
packet_size = 4 + 1 + host_len + 2
print(f"[*] คำนวณขนาด Packet: 4 + 1 + {host_len} + 2 = {packet_size} bytes")
# สร้าง Packet ด้วย struct.pack
# >: Big-Endian
# B: unsigned char (1 byte)
# H: unsigned short (2 bytes)
packet = struct.pack(
f">BBBB B{host_len}s H",
0x05, # VER = 5 (SOCKS5)
0x01, # CMD = CONNECT
0x00, # RSV = Reserved
0x03, # ATYP = Domain Name
host_len, # ความยาว Domain
host_bytes, # Domain Name
port # Port (Big-Endian)
)
return packet
def demo_socks5_handshake():
"""สาธิตขั้นตอน SOCKS5 Handshake แบบ Step-by-Step"""
print("=" * 60)
print("SOCKS5 Handshake Simulation")
print("=" * 60)
# Step 1: Greeting
greeting = bytes([0x05, 0x01, 0x00])
print(f" - [Step 1] Greeting Packet:")
print(f" Raw: {greeting.hex()}")
print(f" VER={greeting[0]}, NMETHODS={greeting[1]}, METHOD={greeting[2]}")
# Step 2: Build Request
target_host = "example.com"
target_port = 80
request = build_socks5_request(target_host, target_port)
print(f" - [Step 3] Connection Request:")
print(f" Target: {target_host}:{target_port}")
print(f" Raw: {request.hex()}")
print(f" Size: {len(request)} bytes")
# แสดง Port ในรูป Hex
port_hex = f"0x{target_port:04X}"
print(f" - [Info] Port {target_port} = {port_hex} (Big-Endian: {target_port >> 8:02X} {target_port & 0xFF:02X})")
# ตัวอย่างการใช้งาน
if __name__ == "__main__":
demo_socks5_handshake()
# ทดสอบกับ domain ต่าง ๆ
print(" - " + "=" * 60)
print("ตัวอย่างการคำนวณขนาด Packet:")
print("=" * 60)
test_cases = [
("google.com", 443),
("mail.example.co.th", 25),
("a.io", 8080),
]
for host, port in test_cases:
size = 4 + 1 + len(host) + 2
print(f" {host}:{port} → Packet Size = 4+1+{len(host)}+2 = {size} bytes")
| คุณสมบัติ | Forward Proxy | Reverse Proxy | SOCKS5 Proxy |
|---|---|---|---|
| ตำแหน่งการทำงาน | ฝั่ง Client | ฝั่ง Server | ฝั่ง Client |
| Layer | 7 (Application) | 7 (Application) | 5 (Session) |
| Protocol รองรับ | HTTP/HTTPS | HTTP/HTTPS | ทุก Protocol |
| การซ่อน IP | ซ่อน IP Client | ซ่อน IP Server | ซ่อน IP Client |
| การยืนยันตัวตน | Basic/NTLM | ไม่จำเป็น | Username/Password |
| UDP Support | ❌ | ❌ | ✅ (SOCKS5) |
| Load Balancing | ❌ | ✅ | ❌ |
| SSL Termination | ❌ | ✅ | ❌ |
| ตัวอย่างซอฟต์แวร์ | Squid, CCProxy | Nginx, HAProxy | Dante, 3proxy |
| กรณีใช้งานทั่วไป | Corporate Internet | Web Server Protection | VPN-like Access |
SSH Tunneling (การสร้างอุโมงค์ SSH) คือเทคนิคการส่งผ่าน Traffic ของ Protocol อื่น ๆ ภายใน SSH Connection ที่เข้ารหัสแล้ว ทำให้ Traffic ที่ไม่ปลอดภัยถูกห่อหุ้ม (Encapsulate) และเข้ารหัส (Encrypt)
หลักการทำงาน:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph TD
subgraph CLIENT["💻 Client Machine"]
A["Application - (HTTP, DB, etc.)"]
LP["Local Port - (localhost:XXXX)"]
end
subgraph TUNNEL["🔐 SSH Encrypted Tunnel"]
T["SSH Channel - (Encrypted Traffic) - AES-256-CTR"]
end
subgraph SSH_SERVER["🖥️ SSH Server"]
SP["SSH Daemon - (Port 22)"]
FP["Port Forwarder"]
end
subgraph DESTINATION["🎯 Destination"]
DS["Service - (DB:5432, HTTP:80, etc.)"]
end
A -->|"Plain Traffic"| LP
LP -->|"Encrypted"| T
T -->|"Decrypted"| SP
SP --> FP
FP -->|"Plain Traffic"| DS
style CLIENT fill:#32302f,stroke:#458588,color:#ebdbb2
style TUNNEL fill:#32302f,stroke:#b16286,color:#ebdbb2
style SSH_SERVER fill:#32302f,stroke:#d65d0e,color:#ebdbb2
style DESTINATION fill:#32302f,stroke:#689d6a,color:#ebdbb2
style T fill:#b16286,color:#ebdbb2,stroke:#d3869b
ประเภทของ SSH Tunneling:
| ประเภท | คำสั่ง | ทิศทาง | กรณีใช้งาน |
|---|---|---|---|
| Local Port Forwarding | ssh -L |
Local → Remote | เข้าถึง DB ภายในจากภายนอก |
| Remote Port Forwarding | ssh -R |
Remote → Local | เปิด Service ให้เข้าถึงจาก Internet |
| Dynamic Port Forwarding | ssh -D |
All Directions | สร้าง SOCKS Proxy |
Local Port Forwarding ส่งต่อ Port บนเครื่อง Local ไปยัง Port บน Remote Host ผ่าน SSH Server
คำสั่ง:
ssh -L [local_addr:]local_port:remote_host:remote_port user@ssh_server
สถานการณ์ตัวอย่าง:
โจทย์: Developer ต้องการเข้าถึง PostgreSQL Database (Port 5432) ที่อยู่บน Internal Server ซึ่งไม่ได้ Public แต่มี SSH Server ที่เข้าถึงได้จากภายนอก
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph LR
subgraph HOME["🏠 Developer's Machine"]
DEV["psql client - localhost:5433"]
end
subgraph INTERNET["🌐 Internet"]
I[" "]
end
subgraph CLOUD["☁️ Cloud Network"]
SSH["SSH Server - 203.0.113.10:22"]
DB["PostgreSQL - 10.0.0.5:5432 - (Internal Only)"]
end
DEV -->|"ssh -L 5433:10.0.0.5:5432 - user@203.0.113.10"| I
I -->|"SSH Tunnel :22"| SSH
SSH -->|"Forward to - 10.0.0.5:5432"| DB
style HOME fill:#32302f,stroke:#689d6a,color:#ebdbb2
style CLOUD fill:#32302f,stroke:#458588,color:#ebdbb2
style SSH fill:#d65d0e,color:#ebdbb2,stroke:#fe8019
style DB fill:#689d6a,color:#ebdbb2,stroke:#8ec07c
ขั้นตอนการใช้งาน:
# Local Port 5433 → Remote DB 10.0.0.5:5432 ผ่าน SSH Server
ssh -L 5433:10.0.0.5:5432 developer@203.0.113.10
# หรือแบบ Background (-N ไม่ execute command, -f ส่งไป background)
ssh -N -f -L 5433:10.0.0.5:5432 developer@203.0.113.10
# เชื่อมต่อ PostgreSQL ผ่าน Tunnel ที่สร้างไว้
psql -h localhost -p 5433 -U dbadmin -d mydb
การคำนวณ Latency ของ Tunnel:
ตัวอย่างการคำนวณ:
เทียบกับ Direct Connection (ไม่มี Tunnel) = 21 ms → Overhead ≈ 9.5%
#!/usr/bin/env python3
"""
สาธิต Local Port Forwarding ด้วย paramiko
---
แสดงการสร้าง SSH Tunnel สำหรับ Database Connection
"""
import threading
import socket
import paramiko
def create_ssh_tunnel(
ssh_host: str,
ssh_port: int,
ssh_user: str,
ssh_key_path: str,
remote_host: str,
remote_port: int,
local_port: int
) -> None:
"""
สร้าง SSH Local Port Forwarding Tunnel
Args:
ssh_host: IP/Hostname ของ SSH Server
ssh_port: Port ของ SSH Server (ปกติ 22)
ssh_user: ชื่อผู้ใช้ SSH
ssh_key_path: Path ของ SSH Private Key
remote_host: Host ปลายทางที่ต้องการเข้าถึง
remote_port: Port ของ Service ปลายทาง
local_port: Port บนเครื่อง Local ที่จะ Listen
Example:
>>> # สร้าง Tunnel ไปยัง PostgreSQL
>>> create_ssh_tunnel(
... ssh_host="203.0.113.10",
... ssh_port=22,
... ssh_user="developer",
... ssh_key_path="~/.ssh/id_rsa",
... remote_host="10.0.0.5",
... remote_port=5432,
... local_port=5433
... )
"""
print(f"[*] กำลังสร้าง SSH Tunnel...")
print(f" localhost:{local_port} → {ssh_host}:{ssh_port} → {remote_host}:{remote_port}")
# สร้าง SSH Client
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# เชื่อมต่อ SSH Server
client.connect(
hostname=ssh_host,
port=ssh_port,
username=ssh_user,
key_filename=ssh_key_path,
timeout=10
)
print(f"[+] SSH Connected to {ssh_host}:{ssh_port}")
# สร้าง Transport Channel
transport = client.get_transport()
# สร้าง Local Listener Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("127.0.0.1", local_port))
server_socket.listen(5)
print(f"[+] Listening on localhost:{local_port}")
print("[*] กด Ctrl+C เพื่อหยุด Tunnel")
while True:
# รับ Connection จาก Local Application
local_conn, local_addr = server_socket.accept()
print(f"[+] Local Connection จาก: {local_addr}")
# สร้าง SSH Channel ไปยัง Remote
channel = transport.open_channel(
"direct-tcpip",
(remote_host, remote_port),
local_addr
)
if channel is None:
print("[-] ไม่สามารถสร้าง SSH Channel")
local_conn.close()
continue
# สร้าง Thread สำหรับ Forward Data
forward_thread = threading.Thread(
target=_forward_data,
args=(local_conn, channel)
)
forward_thread.daemon = True
forward_thread.start()
except Exception as e:
print(f"[-] Error: {e}")
finally:
client.close()
def _forward_data(local_sock: socket.socket, ssh_channel: paramiko.Channel) -> None:
"""ส่งต่อข้อมูลระหว่าง Local Socket และ SSH Channel"""
import select
while True:
r, _, _ = select.select([local_sock, ssh_channel], [], [], 1)
if local_sock in r:
data = local_sock.recv(4096)
if not data:
break
ssh_channel.sendall(data)
if ssh_channel in r:
data = ssh_channel.recv(4096)
if not data:
break
local_sock.sendall(data)
local_sock.close()
ssh_channel.close()
# ตัวอย่างการใช้งาน
if __name__ == "__main__":
# ข้อมูลตัวอย่าง (สมมติ)
TUNNEL_CONFIG = {
"ssh_host": "203.0.113.10", # SSH Server
"ssh_port": 22,
"ssh_user": "developer",
"ssh_key_path": "~/.ssh/id_rsa",
"remote_host": "10.0.0.5", # Database Server (Internal)
"remote_port": 5432, # PostgreSQL
"local_port": 5433 # Local listening port
}
print("SSH Local Port Forwarding Demo")
print("คำสั่ง SSH ที่เทียบเท่า:")
print(f" ssh -N -f -L {TUNNEL_CONFIG['local_port']}:"
f"{TUNNEL_CONFIG['remote_host']}:{TUNNEL_CONFIG['remote_port']} "
f"{TUNNEL_CONFIG['ssh_user']}@{TUNNEL_CONFIG['ssh_host']}")
Remote Port Forwarding เปิด Port บน SSH Server แล้วส่งต่อ Traffic ไปยัง Port บนเครื่อง Local หรือเครื่องอื่น
คำสั่ง:
ssh -R [remote_addr:]remote_port:local_host:local_port user@ssh_server
สถานการณ์ตัวอย่าง:
โจทย์: Developer ต้องการให้ Client ทดสอบ Web Application ที่รันอยู่บน Local Machine โดยไม่ต้อง Deploy ขึ้น Server
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph RL
subgraph CLIENT_SIDE["🌐 Internet Client"]
C["Client Browser - → 203.0.113.10:8080"]
end
subgraph SERVER_SIDE["☁️ Public SSH Server"]
S["SSH Server - 203.0.113.10 - Listening :8080"]
end
subgraph DEV_MACHINE["🏠 Developer's Machine (NAT/Firewall)"]
APP["Web App - localhost:3000 - (React Dev Server)"]
end
C -->|"HTTP Request - :8080"| S
S <-->|"SSH Tunnel :22 - (Reverse)"| APP
style CLIENT_SIDE fill:#32302f,stroke:#458588,color:#ebdbb2
style SERVER_SIDE fill:#32302f,stroke:#d65d0e,color:#ebdbb2
style DEV_MACHINE fill:#32302f,stroke:#689d6a,color:#ebdbb2
ขั้นตอนการใช้งาน:
# เปิด Port 8080 บน SSH Server แล้ว Forward มาที่ localhost:3000
ssh -R 8080:localhost:3000 developer@203.0.113.10
# ให้ Listen บน All Interface (ต้องตั้ง GatewayPorts yes ใน sshd_config)
ssh -R 0.0.0.0:8080:localhost:3000 developer@203.0.113.10
/etc/ssh/sshd_config บน SSH Server:# อนุญาตให้ Remote Forwarding เปิด Port บน All Interfaces
GatewayPorts yes
AllowTcpForwarding yes
# Client สามารถเข้าถึง Developer's App ได้ที่
curl http://203.0.113.10:8080
Dynamic Port Forwarding สร้าง SOCKS Proxy บนเครื่อง Local โดยใช้ SSH Server เป็นตัวส่งต่อ Traffic ทั้งหมด เปรียบได้กับ VPN อย่างง่าย
คำสั่ง:
ssh -D [local_addr:]local_port user@ssh_server
สถานการณ์ตัวอย่าง:
โจทย์: พนักงานอยู่ต่างประเทศและต้องการเข้าถึง Internal Resources ขององค์กร ซึ่งถูก Block จาก IP ต่างประเทศ โดยมี SSH Server อยู่ที่ Office
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph TD
subgraph ABROAD["🌏 ต่างประเทศ"]
EMP["👤 Employee - Browser + SOCKS5 - localhost:1080"]
end
subgraph TUNNEL["🔐 SSH Encrypted Channel"]
T["Port 22 / AES-256"]
end
subgraph OFFICE["🏢 Office Network (Thailand)"]
SSH_SRV["SSH Jump Server - 10.10.0.1:22"]
INTRANET["🌐 Intranet - 10.10.0.0/24"]
HR_SYS["HR System - 10.10.0.10:80"]
FILE_SRV["File Server - 10.10.0.20:445"]
end
EMP -->|"ssh -D 1080 - user@office-ssh.com"| T
T -->|"SSH :22"| SSH_SRV
SSH_SRV --> INTRANET
INTRANET --> HR_SYS
INTRANET --> FILE_SRV
style ABROAD fill:#32302f,stroke:#cc241d,color:#ebdbb2
style TUNNEL fill:#32302f,stroke:#b16286,color:#ebdbb2
style OFFICE fill:#32302f,stroke:#689d6a,color:#ebdbb2
style SSH_SRV fill:#d65d0e,color:#ebdbb2,stroke:#fe8019
ขั้นตอนการตั้งค่า:
# สร้าง SOCKS5 Proxy ที่ localhost:1080
ssh -N -f -D 1080 employee@office-ssh.example.com
# หรือระบุ Interface เฉพาะ
ssh -N -f -D 127.0.0.1:1080 employee@office-ssh.example.com
# Firefox: Settings → Network Settings → Manual Proxy
# SOCKS Host: 127.0.0.1 | Port: 1080 | SOCKS v5
# ทดสอบด้วย curl
curl --socks5 127.0.0.1:1080 http://10.10.0.10/
# ทดสอบด้วย chromium
chromium --proxy-server="socks5://127.0.0.1:1080"
การคำนวณ Bandwidth ของ SSH Tunnel:
โดยที่:
ตัวอย่างการคำนวณ:
กำหนด BW_SSH = 100 Mbps, OH_crypto = 0.07 (7%)
ดังนั้น Bandwidth จริงที่ได้รับ ≈ 93.5 Mbps (สูญเสีย ~6.5 Mbps จาก Encryption Overhead)
NAT (Network Address Translation) คือกระบวนการแปลง IP Address (และ Port) ในส่วนหัวของ IP Packet เพื่อให้ Device หลาย ๆ เครื่องในเครือข่ายภายในสามารถใช้ IP Address สาธารณะ (Public IP) เพียงหนึ่งเดียวในการเข้าถึงอินเทอร์เน็ต
แรงผลักดันการเกิด NAT:
ข้อเท็จจริง: ปัจจุบันมีอุปกรณ์เชื่อมต่ออินเทอร์เน็ตมากกว่า 15,000 ล้านเครื่อง แต่ IPv4 มีที่อยู่ได้เพียง ~4.3 พันล้าน ดังนั้น NAT จึงเป็นวิธีแก้ปัญหาการขาดแคลน IP Address
กลไกพื้นฐาน:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
sequenceDiagram
participant PC as 💻 PC
192.168.1.100:55000
participant GW as 🔄 NAT Gateway
Internal: 192.168.1.1
External: 203.0.113.1
participant WEB as 🌐 Web Server
93.184.216.34:80
Note over PC,WEB: NAT Translation Process
PC->>GW: SRC: 192.168.1.100:55000
DST: 93.184.216.34:80
Note over GW: NAT Table Entry:
192.168.1.100:55000 ↔ 203.0.113.1:40001
GW->>WEB: SRC: 203.0.113.1:40001
DST: 93.184.216.34:80
WEB-->>GW: SRC: 93.184.216.34:80
DST: 203.0.113.1:40001
Note over GW: Lookup NAT Table:
40001 → 192.168.1.100:55000
GW-->>PC: SRC: 93.184.216.34:80
DST: 192.168.1.100:55000
Static NAT แมป Private IP หนึ่งหมายเลขกับ Public IP หนึ่งหมายเลขแบบถาวร
ตัวอย่างข้อมูล:
| Private IP | Public IP | ใช้สำหรับ |
|---|---|---|
| 10.0.0.10 | 203.0.113.10 | Web Server |
| 10.0.0.11 | 203.0.113.11 | Mail Server |
| 10.0.0.12 | 203.0.113.12 | FTP Server |
# ตั้งค่า Static NAT ด้วย iptables
# แมป 203.0.113.10 → 10.0.0.10 (Inbound)
iptables -t nat -A PREROUTING \
-d 203.0.113.10 \
-j DNAT --to-destination 10.0.0.10
# แมป 10.0.0.10 → 203.0.113.10 (Outbound)
iptables -t nat -A POSTROUTING \
-s 10.0.0.10 \
-j SNAT --to-source 203.0.113.10
Dynamic NAT แมป Private IP กับ Public IP จาก Pool แบบ Dynamic
ตัวอย่างการคำนวณ Pool Size:
โจทย์: มี 500 เครื่องในเครือข่าย แต่ใช้อินเทอร์เน็ตพร้อมกันสูงสุด 20% → ต้องการ Public IP กี่หมายเลข?
ดังนั้น Pool NAT ต้องมี Public IP อย่างน้อย 100 หมายเลข
PAT (Port Address Translation) หรือ NAPT (Network Address and Port Translation) คือ NAT ที่แมป Private IP หลาย ๆ หมายเลขกับ Public IP เพียง หนึ่งหมายเลข โดยใช้ Port ต่างกัน (ที่พบบ่อยที่สุด)
ตาราง NAT Table ตัวอย่าง:
| Private IP | Private Port | Public IP | Public Port | Protocol | Destination |
|---|---|---|---|---|---|
| 192.168.1.100 | 55000 | 203.0.113.1 | 40001 | TCP | 93.184.216.34:80 |
| 192.168.1.101 | 55001 | 203.0.113.1 | 40002 | TCP | 8.8.8.8:53 |
| 192.168.1.102 | 55002 | 203.0.113.1 | 40003 | UDP | 8.8.4.4:53 |
| 192.168.1.100 | 55003 | 203.0.113.1 | 40004 | TCP | 172.217.0.1:443 |
การคำนวณจำนวน Session สูงสุด:
Port range ที่ใช้ได้: 1024–65535
ดังนั้น IP Address สาธารณะ 1 หมายเลขรองรับ Session พร้อมกันได้สูงสุด 64,512 sessions
#!/usr/bin/env python3
"""
จำลอง NAT Table สำหรับการเรียนรู้
---
แสดงกลไกการทำงานของ PAT/NAPT Translation
"""
import random
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class NatEntry:
"""ข้อมูล Entry ใน NAT Table"""
private_ip: str
private_port: int
public_ip: str
public_port: int
protocol: str
dst_ip: str
dst_port: int
ttl: int = 300 # วินาที
class NatTable:
"""
จำลอง NAT Table สำหรับ PAT/NAPT
Attributes:
public_ip: Public IP ของ NAT Gateway
entries: Dictionary เก็บ NAT Entries
port_pool: Set ของ Port ที่ยังว่างอยู่
"""
def __init__(self, public_ip: str):
"""
เริ่มต้น NAT Table
Args:
public_ip: Public IP ของ NAT Gateway
"""
self.public_ip = public_ip
self.entries: dict[str, NatEntry] = {}
# สร้าง Port Pool: 1024–65535
self.port_pool = set(range(1024, 65536))
print(f"[*] NAT Gateway เริ่มต้น: {public_ip}")
print(f"[*] Port Pool: {len(self.port_pool):,} ports พร้อมใช้งาน")
def translate_outbound(
self,
private_ip: str,
private_port: int,
dst_ip: str,
dst_port: int,
protocol: str = "TCP"
) -> Optional[NatEntry]:
"""
แปล Address สำหรับ Packet ขาออก (Outbound)
Args:
private_ip: Private IP ของ Client
private_port: Port ของ Client
dst_ip: Destination IP
dst_port: Destination Port
protocol: TCP หรือ UDP
Returns:
NatEntry: Entry ที่สร้างหรือค้นหาใน NAT Table
"""
# สร้าง Key สำหรับค้นหา Entry ที่มีอยู่แล้ว
key = f"{private_ip}:{private_port}:{dst_ip}:{dst_port}:{protocol}"
if key in self.entries:
print(f"[Cache] ใช้ Entry เดิม: {private_ip}:{private_port} → {dst_ip}:{dst_port}")
return self.entries[key]
# ตรวจสอบว่ายังมี Port ว่างอยู่
if not self.port_pool:
print("[-] Port Pool หมดแล้ว!")
return None
# เลือก Port ใหม่จาก Pool
new_port = random.choice(list(self.port_pool))
self.port_pool.discard(new_port)
# สร้าง NAT Entry ใหม่
entry = NatEntry(
private_ip=private_ip,
private_port=private_port,
public_ip=self.public_ip,
public_port=new_port,
protocol=protocol,
dst_ip=dst_ip,
dst_port=dst_port
)
self.entries[key] = entry
print(f"[NAT] {private_ip}:{private_port} → {self.public_ip}:{new_port} | Dst: {dst_ip}:{dst_port}")
return entry
def translate_inbound(
self,
public_port: int,
src_ip: str,
src_port: int
) -> Optional[NatEntry]:
"""
แปล Address สำหรับ Packet ขาเข้า (Inbound)
Args:
public_port: Public Port ที่รับ Packet มา
src_ip: IP ต้นทางของ Packet
src_port: Port ต้นทางของ Packet
Returns:
NatEntry: Entry ที่ตรงกัน
"""
# ค้นหา Entry ที่ตรงกับ Public Port
for entry in self.entries.values():
if (entry.public_port == public_port and
entry.dst_ip == src_ip and
entry.dst_port == src_port):
print(f"[NAT] Inbound: {src_ip}:{src_port}→{self.public_ip}:{public_port}"
f" → {entry.private_ip}:{entry.private_port}")
return entry
print(f"[-] ไม่พบ Entry สำหรับ Public Port {public_port}")
return None
def show_table(self):
"""แสดง NAT Table ทั้งหมด"""
print(" - " + "=" * 80)
print(f"{'NAT TABLE':^80}")
print("=" * 80)
print(f"{'Private IP:Port':<22} {'Public IP:Port':<22} {'Protocol':<8} {'Destination':<25}")
print("-" * 80)
for entry in self.entries.values():
private = f"{entry.private_ip}:{entry.private_port}"
public = f"{entry.public_ip}:{entry.public_port}"
dst = f"{entry.dst_ip}:{entry.dst_port}"
print(f"{private:<22} {public:<22} {entry.protocol:<8} {dst:<25}")
print("=" * 80)
print(f"Total Entries: {len(self.entries)} | Available Ports: {len(self.port_pool):,}")
# ตัวอย่างการใช้งาน
if __name__ == "__main__":
# สร้าง NAT Table
nat = NatTable(public_ip="203.0.113.1")
print(" - --- จำลองการส่ง Traffic จาก 3 Clients --- - ")
# Client 1: เข้าถึง Google
nat.translate_outbound("192.168.1.100", 55000, "142.250.185.46", 80, "TCP")
# Client 2: DNS Query
nat.translate_outbound("192.168.1.101", 55001, "8.8.8.8", 53, "UDP")
# Client 3: HTTPS
nat.translate_outbound("192.168.1.102", 55002, "172.217.0.1", 443, "TCP")
# Client 1 ส่ง Request อีกครั้ง (ใช้ Entry เดิม)
nat.translate_outbound("192.168.1.100", 55000, "142.250.185.46", 80, "TCP")
# แสดง NAT Table
nat.show_table()
# คำนวณ Port ที่ยังใช้ได้
used_ports = 65536 - 1024 - len(nat.port_pool)
print(f" - สถิติ: ใช้ Port ไปแล้ว {used_ports} ports ({used_ports/64512*100:.2f}%)")
NAT Traversal คือเทคนิคที่ใช้ให้ Device สองตัวที่อยู่หลัง NAT ต่างกันสามารถสื่อสารกันได้โดยตรง (Peer-to-Peer)
ปัญหาของ NAT กับ P2P Communication:
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#a89984', 'lineColor': '#d79921', 'secondaryColor': '#3c3836', 'tertiaryColor': '#504945', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#a89984', 'clusterBkg': '#32302f', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836' }}}%%
graph TD
subgraph NAT_A["🏠 NAT A"]
PA["Peer A - 192.168.1.100"]
GWA["Gateway A - 1.2.3.4"]
end
subgraph STUN_SERVER["☁️ STUN Server"]
SS["stun.example.com - 5.6.7.8:3478"]
end
subgraph NAT_B["🏠 NAT B"]
PB["Peer B - 10.0.0.200"]
GWB["Gateway B - 9.10.11.12"]
end
PA -->|"STUN Request - What is my public IP?"| SS
SS -->|"Your IP: 1.2.3.4:XXXXX"| PA
PB -->|"STUN Request"| SS
SS -->|"Your IP: 9.10.11.12:YYYYY"| PB
PA <-->|"Direct P2P - (UDP Hole Punching)"| PB
style NAT_A fill:#32302f,stroke:#689d6a,color:#ebdbb2
style NAT_B fill:#32302f,stroke:#458588,color:#ebdbb2
style STUN_SERVER fill:#32302f,stroke:#d79921,color:#ebdbb2
style SS fill:#d79921,color:#282828,stroke:#fabd2f
เทคนิค NAT Traversal:
| เทคนิค | ย่อมาจาก | หลักการ | ใช้ใน |
|---|---|---|---|
| STUN | Session Traversal Utilities for NAT | ค้นหา Public IP/Port | WebRTC, VoIP |
| TURN | Traversal Using Relays around NAT | ใช้ Relay Server | WebRTC Fallback |
| ICE | Interactive Connectivity Establishment | รวม STUN+TURN | WebRTC |
| UDP Hole Punching | — | เปิด Hole ใน NAT พร้อมกัน | P2P Applications |
| UPnP | Universal Plug and Play | ขอให้ Router เปิด Port | BitTorrent, Gaming |
การคำนวณ STUN Binding:
ตัวอย่าง: RTT = 30 ms, T_process = 1 ms → T_discovery = 31 ms
วัตถุประสงค์: เข้าถึง Web Server ภายในผ่าน SSH Tunnel
สภาพแวดล้อม (ข้อมูลตัวอย่าง):
ขั้นตอน:
# ทดสอบ Direct Connection (ควรจะ Fail)
curl http://192.168.1.20
# Expected: Connection refused หรือ Timeout
# สร้าง Tunnel: localhost:8080 → 192.168.1.20:80 ผ่าน SSH Server
ssh -N -f -L 8080:192.168.1.20:80 student@192.168.1.10
# ตรวจสอบว่า Tunnel ทำงานแล้ว
ss -tlnp | grep 8080
# Expected: LISTEN 0 128 127.0.0.1:8080
# เข้าถึง Internal Web Server ผ่าน Tunnel
curl http://localhost:8080
# Expected: HTML Content ของ Web Server
# ดู HTTP Headers
curl -I http://localhost:8080
# ค้นหา SSH Process
ps aux | grep "ssh.*8080"
# Expected: ssh -N -f -L 8080:192.168.1.20:80 student@192.168.1.10
# หยุด Tunnel
kill $(pgrep -f "ssh.*8080")
วัตถุประสงค์: ใช้ SSH Server เป็น Proxy สำหรับ Traffic ทั้งหมด
ขั้นตอน:
# สร้าง SOCKS5 Proxy ที่ localhost:1080
ssh -N -f -D 1080 student@192.168.1.10
# ตรวจสอบ Proxy
ss -tlnp | grep 1080
# ทดสอบผ่าน curl
curl --socks5-hostname localhost:1080 https://ifconfig.me
# Expected: IP ของ SSH Server ไม่ใช่ IP ของ Client
# เปรียบเทียบ IP โดยไม่ใช้ Proxy
curl https://ifconfig.me
# DNS ควร Resolve ผ่าน SSH Server (ป้องกัน DNS Leak)
curl --socks5-hostname localhost:1080 https://ifconfig.me/host
# เปิด Firefox พร้อม Proxy Configuration
firefox --new-instance &
# หรือใช้ chromium
chromium --proxy-server="socks5://localhost:1080" \
--proxy-bypass-list="localhost,127.0.0.1" &
วัตถุประสงค์: ตั้งค่า Squid Proxy พร้อม Access Control
ขั้นตอน:
sudo apt update && sudo apt install -y squid
# ตรวจสอบ Status
sudo systemctl status squid
sudo cp /etc/squid/squid.conf /etc/squid/squid.conf.bak
# แก้ไข Configuration
sudo tee /etc/squid/squid.conf << 'EOF'
# อนุญาต Local Network
acl localnet src 192.168.1.0/24
acl Safe_ports port 80
acl Safe_ports port 443
acl Safe_ports port 21
# บล็อก Social Media (ตัวอย่าง)
acl blocked_social dstdomain .facebook.com .twitter.com .tiktok.com
http_access deny blocked_social
http_access allow localnet Safe_ports
http_access deny all
http_port 3128
cache_mem 128 MB
access_log /var/log/squid/access.log
EOF
sudo systemctl restart squid
# ทดสอบ HTTP ผ่าน Proxy
curl -x http://192.168.1.10:3128 http://example.com
# ทดสอบว่าเว็บที่บล็อกไม่สามารถเข้าถึงได้
curl -x http://192.168.1.10:3128 http://www.facebook.com
# Expected: Access denied
# ดู Access Log
sudo tail -f /var/log/squid/access.log
# นับจำนวน Request ต่อ Domain
sudo awk '{print $7}' /var/log/squid/access.log | \
grep -oP '(?<=://)[^/]+' | sort | uniq -c | sort -rn | head -10
วัตถุประสงค์: ทำความเข้าใจ NAT Rules และ Connection Tracking
ขั้นตอน:
# ดู PREROUTING และ POSTROUTING Rules
sudo iptables -t nat -L -n -v
# ดู Connection Tracking Table
sudo conntrack -L 2>/dev/null | head -20
# หรือ
cat /proc/net/nf_conntrack | head -20
# Enable IP Forwarding
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# Masquerade (NAPT) สำหรับ Interface eth0
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# ตรวจสอบ Rule
sudo iptables -t nat -L POSTROUTING -n -v
# Forward Port 80 จาก Public Interface ไปยัง Internal Server
sudo iptables -t nat -A PREROUTING \
-i eth0 -p tcp --dport 80 \
-j DNAT --to-destination 192.168.1.20:80
# อนุญาต Forwarded Traffic
sudo iptables -A FORWARD \
-d 192.168.1.20 -p tcp --dport 80 \
-m state --state NEW,ESTABLISHED \
-j ACCEPT
# ดู NAT Table ทั้งหมด
sudo iptables -t nat -L -n -v --line-numbers
# ตรวจสอบ Connection Stats
sudo conntrack -L -p tcp 2>/dev/null | grep "dport=80"
บทเรียนสัปดาห์ที่ 7 ครอบคลุมเทคโนโลยีสำคัญในด้าน Network Security ดังนี้:
Forward Proxy ทำหน้าที่เป็นตัวกลางฝั่ง Client ใช้สำหรับควบคุมการเข้าถึงอินเทอร์เน็ตและซ่อน IP ของ Client ส่วน Reverse Proxy ทำงานฝั่ง Server เพื่อปกป้อง Backend และทำ Load Balancing ขณะที่ SOCKS5 Proxy ทำงานที่ Layer ต่ำกว่าและรองรับ Protocol ทุกประเภทรวมถึง UDP
SSH Tunneling มี 3 รูปแบบหลัก:
NAT แก้ปัญหาการขาดแคลน IPv4 Address โดย PAT/NAPT รองรับ Session ได้สูงสุด 64,512 Sessions ต่อ Public IP Address หนึ่งหมายเลข และ NAT Traversal เช่น STUN/TURN ช่วยให้ P2P Communication เป็นไปได้แม้อยู่หลัง NAT
| เทคโนโลยี | Layer | ประเภท Traffic | ความปลอดภัย | ความซับซ้อน |
|---|---|---|---|---|
| Forward Proxy | 7 | HTTP/HTTPS | ปานกลาง | ต่ำ |
| Reverse Proxy | 7 | HTTP/HTTPS | สูง | ปานกลาง |
| SOCKS5 Proxy | 5 | ทุกประเภท | ปานกลาง | ต่ำ |
| SSH Local Tunnel | 4–7 | ทุกประเภท | สูงมาก | ต่ำ–ปานกลาง |
| SSH Dynamic Proxy | 4–7 | ทุกประเภท | สูงมาก | ต่ำ |
| Static NAT | 3 | ทุกประเภท | ต่ำ | ต่ำ |
| PAT/NAPT | 3–4 | ทุกประเภท | ต่ำ | ต่ำ |