Proxy Servers and Network Tunneling

1. ความเป็นมาและภาพรวม

1.1 บทนำ

ในยุคที่เครือข่ายอินเทอร์เน็ตมีบทบาทสำคัญในชีวิตประจำวัน การควบคุมการเข้าถึง (Access Control) และการปกปิดตัวตน (Anonymization) กลายเป็นประเด็นหลักของ Network Security Proxy Server และ Network Tunneling เป็นเทคโนโลยีที่ช่วยให้บรรลุเป้าหมายเหล่านี้ได้อย่างมีประสิทธิภาพ

วัตถุประสงค์ของบทเรียน:

1.2 ภาพรวมความสัมพันธ์ระหว่างเทคโนโลยี

%%{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

1.3 ประวัติโดยย่อ (Timeline)

%%{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

2. Proxy Servers

2.1 ความหมายและหลักการทำงาน (Definition and Working Principle)

Proxy Server (พร็อกซี่เซิร์ฟเวอร์) คือระบบที่ทำหน้าที่เป็น ตัวกลาง (Intermediary) ระหว่าง Client และ Server โดย Client จะส่ง Request ไปยัง Proxy แทนที่จะส่งตรงไปยัง Destination Server

คุณสมบัติหลักของ Proxy:

หลักการทำงานพื้นฐาน (3 ขั้นตอน):

  1. Client → Proxy: Client ส่ง Request ไปยัง Proxy Server
  2. Proxy → Server: Proxy ส่งต่อ Request ในนาม Client
  3. Server → Proxy → Client: Server ตอบกลับ Proxy แล้ว Proxy ส่งต่อให้ Client

2.2 Forward Proxy (ฟอร์เวิร์ดพร็อกซี่)

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:

2.2.1 การตั้งค่า Squid 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()

2.2.2 การใช้งาน Squid 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

2.3 Reverse Proxy (รีเวิร์สพร็อกซี่)

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:

2.3.1 ตัวอย่างการตั้งค่า Nginx 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";
    }
}

2.4 SOCKS Proxy (ซ็อกส์พร็อกซี่)

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:

Sizerequest = 4 + 1 + lendomain + 2

โดยที่:

ตัวอย่างการคำนวณ: example.com มี 11 ตัวอักษร

Sizerequest = 4 + 1 + 11 + 2 = 18 Bytes
#!/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")

2.5 การเปรียบเทียบ Proxy ประเภทต่าง ๆ

คุณสมบัติ 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

3. SSH Tunneling

3.1 หลักการของ SSH Tunneling

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

3.2 Local Port Forwarding (การส่งต่อ Port แบบ Local)

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

ขั้นตอนการใช้งาน:

  1. สร้าง SSH Tunnel:
# 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
  1. เชื่อมต่อ Database ผ่าน Tunnel:
# เชื่อมต่อ PostgreSQL ผ่าน Tunnel ที่สร้างไว้
psql -h localhost -p 5433 -U dbadmin -d mydb

การคำนวณ Latency ของ Tunnel:

RTTtunnel = RTTclientSSH + RTTSSHdest + Tencrypt

ตัวอย่างการคำนวณ:

RTTtunnel = 20 + 1 + 2 = 23 ms

เทียบกับ 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']}")

3.3 Remote Port Forwarding (การส่งต่อ Port แบบ Remote)

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

ขั้นตอนการใช้งาน:

  1. บน Developer Machine:
# เปิด 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
  1. แก้ไข /etc/ssh/sshd_config บน SSH Server:
# อนุญาตให้ Remote Forwarding เปิด Port บน All Interfaces
GatewayPorts yes
AllowTcpForwarding yes
  1. Client เข้าถึง Web App:
# Client สามารถเข้าถึง Developer's App ได้ที่
curl http://203.0.113.10:8080

3.4 Dynamic Port Forwarding (การส่งต่อ Port แบบ Dynamic)

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

ขั้นตอนการตั้งค่า:

  1. สร้าง SOCKS Proxy ด้วย SSH Dynamic Forwarding:
# สร้าง 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
  1. ตั้งค่า Browser ให้ใช้ SOCKS Proxy:
# 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:

BWeffective = BWSSH × 1 1+OHcrypto

โดยที่:

ตัวอย่างการคำนวณ:

กำหนด BW_SSH = 100 Mbps, OH_crypto = 0.07 (7%)

BWeffective = 100 × 1 1+0.07 = 100 × 0.935 93.5 Mbps

ดังนั้น Bandwidth จริงที่ได้รับ ≈ 93.5 Mbps (สูญเสีย ~6.5 Mbps จาก Encryption Overhead)


4. NAT (Network Address Translation)

4.1 หลักการของ NAT

NAT (Network Address Translation) คือกระบวนการแปลง IP Address (และ Port) ในส่วนหัวของ IP Packet เพื่อให้ Device หลาย ๆ เครื่องในเครือข่ายภายในสามารถใช้ IP Address สาธารณะ (Public IP) เพียงหนึ่งเดียวในการเข้าถึงอินเทอร์เน็ต

แรงผลักดันการเกิด NAT:

NIPv4 = 232 = 4,294,967,296 addresses

ข้อเท็จจริง: ปัจจุบันมีอุปกรณ์เชื่อมต่ออินเทอร์เน็ตมากกว่า 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

4.2 ประเภทของ NAT

4.2.1 Static NAT (One-to-One NAT)

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

4.2.2 Dynamic NAT (Pool NAT)

Dynamic NAT แมป Private IP กับ Public IP จาก Pool แบบ Dynamic

ตัวอย่างการคำนวณ Pool Size:

โจทย์: มี 500 เครื่องในเครือข่าย แต่ใช้อินเทอร์เน็ตพร้อมกันสูงสุด 20% → ต้องการ Public IP กี่หมายเลข?

NpublicIP = Ndevices × Rconcurrent NpublicIP = 500 × 0.20 = 100 IP addresses

ดังนั้น Pool NAT ต้องมี Public IP อย่างน้อย 100 หมายเลข

4.2.3 PAT / NAPT (Port Address Translation)

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

NmaxSessions = 65535 - 1024 + 1 = 64,512 sessions/IP

ดังนั้น 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}%)")

4.3 NAT Traversal

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:

Tdiscovery = RTTclientSTUN + Tprocess

ตัวอย่าง: RTT = 30 ms, T_process = 1 ms → T_discovery = 31 ms


5. Lab Exercises

5.1 Lab 1: ทดสอบ SSH Local Port Forwarding

วัตถุประสงค์: เข้าถึง Web Server ภายในผ่าน SSH Tunnel

สภาพแวดล้อม (ข้อมูลตัวอย่าง):

ขั้นตอน:

  1. ตรวจสอบว่าไม่สามารถเข้าถึง Web Server โดยตรง:
# ทดสอบ Direct Connection (ควรจะ Fail)
curl http://192.168.1.20
# Expected: Connection refused หรือ Timeout
  1. สร้าง SSH Tunnel:
# สร้าง 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
  1. ทดสอบการเข้าถึงผ่าน Tunnel:
# เข้าถึง Internal Web Server ผ่าน Tunnel
curl http://localhost:8080
# Expected: HTML Content ของ Web Server

# ดู HTTP Headers
curl -I http://localhost:8080
  1. ดู Process ของ SSH Tunnel:
# ค้นหา 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")

5.2 Lab 2: สร้าง SOCKS Proxy ด้วย SSH Dynamic Forwarding

วัตถุประสงค์: ใช้ SSH Server เป็น Proxy สำหรับ Traffic ทั้งหมด

ขั้นตอน:

  1. สร้าง SOCKS5 Proxy:
# สร้าง SOCKS5 Proxy ที่ localhost:1080
ssh -N -f -D 1080 student@192.168.1.10

# ตรวจสอบ Proxy
ss -tlnp | grep 1080
  1. ทดสอบการใช้ Proxy:
# ทดสอบผ่าน curl
curl --socks5-hostname localhost:1080 https://ifconfig.me
# Expected: IP ของ SSH Server ไม่ใช่ IP ของ Client

# เปรียบเทียบ IP โดยไม่ใช้ Proxy
curl https://ifconfig.me
  1. ทดสอบ DNS Resolution ผ่าน Proxy:
# DNS ควร Resolve ผ่าน SSH Server (ป้องกัน DNS Leak)
curl --socks5-hostname localhost:1080 https://ifconfig.me/host
  1. ตั้งค่า Firefox ใช้ SOCKS Proxy:
# เปิด Firefox พร้อม Proxy Configuration
firefox --new-instance &

# หรือใช้ chromium
chromium --proxy-server="socks5://localhost:1080" \
         --proxy-bypass-list="localhost,127.0.0.1" &

5.3 Lab 3: ตั้งค่า Squid Forward Proxy

วัตถุประสงค์: ตั้งค่า Squid Proxy พร้อม Access Control

ขั้นตอน:

  1. ติดตั้ง Squid:
sudo apt update && sudo apt install -y squid

# ตรวจสอบ Status
sudo systemctl status squid
  1. Backup และแก้ไข Configuration:
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
  1. ทดสอบ Proxy:
# ทดสอบ 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
  1. วิเคราะห์ Log:
# นับจำนวน Request ต่อ Domain
sudo awk '{print $7}' /var/log/squid/access.log | \
    grep -oP '(?<=://)[^/]+' | sort | uniq -c | sort -rn | head -10

5.4 Lab 4: ตรวจสอบ NAT ด้วย iptables

วัตถุประสงค์: ทำความเข้าใจ NAT Rules และ Connection Tracking

ขั้นตอน:

  1. ดู NAT Rules ที่มีอยู่:
# ดู 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
  1. เพิ่ม NAT Rule สำหรับ Masquerade:
# 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
  1. สร้าง Port Forwarding (DNAT):
# 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
  1. ตรวจสอบผลลัพธ์:
# ดู NAT Table ทั้งหมด
sudo iptables -t nat -L -n -v --line-numbers

# ตรวจสอบ Connection Stats
sudo conntrack -L -p tcp 2>/dev/null | grep "dport=80"

6. สรุป

บทเรียนสัปดาห์ที่ 7 ครอบคลุมเทคโนโลยีสำคัญในด้าน Network Security ดังนี้:

6.1 Proxy Servers

Forward Proxy ทำหน้าที่เป็นตัวกลางฝั่ง Client ใช้สำหรับควบคุมการเข้าถึงอินเทอร์เน็ตและซ่อน IP ของ Client ส่วน Reverse Proxy ทำงานฝั่ง Server เพื่อปกป้อง Backend และทำ Load Balancing ขณะที่ SOCKS5 Proxy ทำงานที่ Layer ต่ำกว่าและรองรับ Protocol ทุกประเภทรวมถึง UDP

6.2 SSH Tunneling

SSH Tunneling มี 3 รูปแบบหลัก:

6.3 NAT

NAT แก้ปัญหาการขาดแคลน IPv4 Address โดย PAT/NAPT รองรับ Session ได้สูงสุด 64,512 Sessions ต่อ Public IP Address หนึ่งหมายเลข และ NAT Traversal เช่น STUN/TURN ช่วยให้ P2P Communication เป็นไปได้แม้อยู่หลัง NAT

6.4 ตารางสรุปเปรียบเทียบ

เทคโนโลยี 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 ทุกประเภท ต่ำ ต่ำ

7. เอกสารอ้างอิง

  1. RFC 1928 — SOCKS Protocol Version 5. IETF, 1996. https://www.rfc-editor.org/rfc/rfc1928
  2. RFC 3022 — Traditional IP Network Address Translator (Traditional NAT). IETF, 2001. https://www.rfc-editor.org/rfc/rfc3022
  3. RFC 5389 — Session Traversal Utilities for NAT (STUN). IETF, 2008. https://www.rfc-editor.org/rfc/rfc5389
  4. SSH, The Secure Shell: The Definitive Guide — Barrett, D.J. and Silverman, R.E. O'Reilly Media, 2005.
  5. Squid Proxy Server 3.1: Beginner's Guide — Chomać, O. Packt Publishing, 2011.
  6. Nginx: A Practical Guide — Sarkar, S. Apress, 2017.
  7. Network Security Essentials — Stallings, W. Pearson, 6th Edition, 2017.
  8. OWASP: Proxy Configuration Guidehttps://owasp.org/www-community/controls/Proxy_Configuration
  9. Squid Documentationhttp://www.squid-cache.org/Doc/
  10. OpenSSH Manual Pageshttps://www.openssh.com/manual.html