โปรโตคอลระดับ Application: การถ่ายโอนไฟล์และการเข้าถึงระยะไกล (Application Layer Protocols: File Transfer & Remote Access)

1. บทนำ

1.1 ความสำคัญของโปรโตคอลระดับ Application

โปรโตคอลระดับ Application (Application Layer Protocols) เป็นชั้นบนสุดในโมเดล OSI และ TCP/IP ทำหน้าที่เป็นส่วนติดต่อระหว่างผู้ใช้กับระบบเครือข่าย โดยโปรโตคอลเหล่านี้กำหนดวิธีการสื่อสารระหว่างแอปพลิเคชันต่างๆ ผ่านเครือข่าย

ในบริบทของความปลอดภัยเครือข่าย การเข้าใจโปรโตคอลเหล่านี้มีความสำคัญเนื่องจาก:

1.2 ภาพรวมของโปรโตคอลที่จะศึกษา

graph TB
    subgraph APP["Application Layer Protocols"]
        direction TB
        subgraph FT["File Transfer โปรโตคอลถ่ายโอนไฟล์"]
            FTP["FTP
Port 21/20"] TFTP["TFTP
Port 69"] SFTP["SFTP
Port 22"] FTPS["FTPS
Port 990"] end subgraph RA["Remote Access การเข้าถึงระยะไกล"] SSH["SSH
Port 22"] SCP["SCP
Port 22"] end end style APP fill:#282828,stroke:#ebdbb2,color:#ebdbb2 style FT fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style RA fill:#3c3836,stroke:#83a598,color:#ebdbb2 style FTP fill:#504945,stroke:#fb4934,color:#ebdbb2 style TFTP fill:#504945,stroke:#fe8019,color:#ebdbb2 style SFTP fill:#504945,stroke:#b8bb26,color:#ebdbb2 style FTPS fill:#504945,stroke:#fabd2f,color:#ebdbb2 style SSH fill:#504945,stroke:#83a598,color:#ebdbb2 style SCP fill:#504945,stroke:#d3869b,color:#ebdbb2

1.3 ประวัติและพัฒนาการของโปรโตคอล

flowchart LR
    subgraph ERA1["ยุคเริ่มต้น (1970s-1980s)"]
        A1["1971: FTP
RFC 114"] A2["1980: FTP Standard
RFC 765"] A3["1981: TFTP
RFC 783"] end subgraph ERA2["ยุคพัฒนา (1990s)"] B1["1995: SSH-1
Tatu Ylönen"] B2["1997: FTPS
RFC 2228"] B3["1999: SSH-2
IETF Draft"] end subgraph ERA3["ยุคปัจจุบัน (2000s-ปัจจุบัน)"] C1["2006: SSH-2
RFC 4251-4256"] C2["2006: SFTP
Draft-ietf"] C3["2012: ED25519
Modern Crypto"] end ERA1 --> ERA2 --> ERA3 style ERA1 fill:#3c3836,stroke:#fb4934,color:#ebdbb2 style ERA2 fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style ERA3 fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style A1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2

2. โปรโตคอลการถ่ายโอนไฟล์ (File Transfer Protocols)

2.1 FTP (File Transfer Protocol)

2.1.1 ความหมายและหลักการทำงาน

FTP (File Transfer Protocol) เป็นโปรโตคอลมาตรฐานสำหรับการถ่ายโอนไฟล์ระหว่างคอมพิวเตอร์ผ่านเครือข่าย TCP/IP ถูกกำหนดครั้งแรกใน RFC 959 และใช้งานมาตั้งแต่ปี 1971

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

2.1.2 สถาปัตยกรรมและ Port ที่ใช้

FTP ใช้ สองการเชื่อมต่อแยกกัน (Dual Connection Architecture):

ประเภทการเชื่อมต่อ Port หน้าที่
Control Connection 21 ส่งคำสั่งและรับการตอบกลับ
Data Connection 20 (Active) หรือ Dynamic (Passive) ส่งข้อมูลไฟล์จริง
sequenceDiagram
    participant C as Client
ไคลเอนต์ participant S as Server
เซิร์ฟเวอร์ Note over C,S: Control Connection (Port 21) C->>S: เชื่อมต่อ TCP Port 21 S-->>C: 220 Service Ready C->>S: USER username S-->>C: 331 Password Required C->>S: PASS password S-->>C: 230 User Logged In Note over C,S: Data Connection (Active Mode) C->>S: PORT h1,h2,h3,h4,p1,p2 S-->>C: 200 PORT OK C->>S: RETR filename S->>C: เชื่อมต่อ TCP จาก Port 20 ไปยัง Client Port S-->>C: ส่งข้อมูลไฟล์ S-->>C: 226 Transfer Complete

2.1.3 โหมดการเชื่อมต่อ: Active vs Passive

Active Mode (โหมดแอคทีฟ):

  1. Client เปิด port สุ่มและแจ้ง Server ผ่านคำสั่ง PORT
  2. Server เริ่มการเชื่อมต่อจาก port 20 ไปยัง port ที่ Client กำหนด
  3. ปัญหา: ไม่ทำงานกับ Firewall/NAT ฝั่ง Client

Passive Mode (โหมดพาสซีฟ):

  1. Client ส่งคำสั่ง PASV ไปยัง Server
  2. Server เปิด port สุ่มและแจ้ง Client
  3. Client เริ่มการเชื่อมต่อไปยัง port ที่ Server กำหนด
  4. เหมาะสำหรับ Client ที่อยู่หลัง Firewall/NAT
flowchart TB
    subgraph ACTIVE["Active Mode โหมดแอคทีฟ"]
        direction LR
        A1["Client
Random Port"] -->|"1. PORT command"| A2["Server
Port 21"] A2 -->|"2. Data Connection"| A1 A3["Server Port 20"] -->|"เริ่มเชื่อมต่อ"| A4["Client Port N"] end subgraph PASSIVE["Passive Mode โหมดพาสซีฟ"] direction LR B1["Client"] -->|"1. PASV command"| B2["Server
Port 21"] B2 -->|"2. 227 (IP,Port)"| B1 B1 -->|"3. Data Connection"| B3["Server
Port N"] end style ACTIVE fill:#3c3836,stroke:#fb4934,color:#ebdbb2 style PASSIVE fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style A1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A4 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style B3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2

2.1.4 จุดอ่อนด้านความปลอดภัย

FTP มีจุดอ่อนด้านความปลอดภัยที่สำคัญ:

"""
ตัวอย่างการดักจับ FTP credentials ด้วย Python
(สำหรับการศึกษาเท่านั้น - ห้ามใช้โดยไม่ได้รับอนุญาต)

โค้ดนี้แสดงให้เห็นว่า FTP ส่งข้อมูลแบบ cleartext
ซึ่งสามารถถูกดักจับได้ง่าย
"""

from scapy.all import sniff, TCP, Raw

def analyze_ftp_packet(packet):
    """
    วิเคราะห์แพ็กเก็ต FTP เพื่อหา credentials
    
    Parameters:
        packet: แพ็กเก็ตที่จับได้จากเครือข่าย
    
    Returns:
        None - แสดงผลลัพธ์ทางหน้าจอ
    """
    # ตรวจสอบว่าเป็น TCP และมีข้อมูล payload
    if packet.haslayer(TCP) and packet.haslayer(Raw):
        # ตรวจสอบว่าเป็น FTP (port 21)
        if packet[TCP].dport == 21 or packet[TCP].sport == 21:
            payload = packet[Raw].load.decode('utf-8', errors='ignore')
            
            # ตรวจหา USER command
            if payload.startswith('USER'):
                username = payload.split()[1].strip()
                print(f"[!] พบ Username: {username}")
            
            # ตรวจหา PASS command
            elif payload.startswith('PASS'):
                password = payload.split()[1].strip()
                print(f"[!] พบ Password: {password}")

# ตัวอย่างการใช้งาน (ต้องรันด้วยสิทธิ์ root)
# sniff(filter="tcp port 21", prn=analyze_ftp_packet, store=0)

# ผลลัพธ์ตัวอย่าง:
# [!] พบ Username: admin
# [!] พบ Password: secret123

2.2 TFTP (Trivial File Transfer Protocol)

2.2.1 ความหมายและหลักการทำงาน

TFTP (Trivial File Transfer Protocol) เป็นโปรโตคอลถ่ายโอนไฟล์แบบเรียบง่ายที่ออกแบบมาสำหรับการใช้งานที่ไม่ต้องการความซับซ้อน ถูกกำหนดใน RFC 1350

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

2.2.2 การใช้งานที่เหมาะสม

กรณีที่ควรใช้ TFTP:

sequenceDiagram
    participant C as Client
ไคลเอนต์ participant S as Server
เซิร์ฟเวอร์ Port 69 Note over C,S: TFTP Read Request (RRQ) C->>S: RRQ "filename" octet S-->>C: DATA Block 1 (512 bytes) C->>S: ACK Block 1 S-->>C: DATA Block 2 (512 bytes) C->>S: ACK Block 2 S-->>C: DATA Block 3 (<512 bytes = สิ้นสุด) C->>S: ACK Block 3 Note over C,S: ไฟล์ถูกถ่ายโอนสำเร็จ

2.2.3 ความเสี่ยงด้านความปลอดภัย

TFTP มีความเสี่ยงสูงมาก:

ความเสี่ยง คำอธิบาย ระดับความรุนแรง
ไม่มี Authentication ใครก็สามารถดาวน์โหลด/อัปโหลดไฟล์ได้ สูงมาก
ไม่มี Encryption ข้อมูลถูกส่งแบบ plaintext สูง
Directory Traversal อาจเข้าถึงไฟล์นอก root directory ได้ สูง
UDP Spoofing ง่ายต่อการปลอมแปลง source IP ปานกลาง

แนวทางการป้องกัน:


2.3 SFTP (SSH File Transfer Protocol)

2.3.1 ความหมายและหลักการทำงาน

SFTP (SSH File Transfer Protocol) เป็นโปรโตคอลถ่ายโอนไฟล์ที่ปลอดภัย ทำงานเป็น subsystem ของ SSH ไม่เกี่ยวข้องกับ FTP แบบดั้งเดิมแต่อย่างใด

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

2.3.2 สถาปัตยกรรมและการทำงาน

flowchart TB
    subgraph CLIENT["SFTP Client"]
        C1["User Interface
ส่วนติดต่อผู้ใช้"] C2["SFTP Protocol Handler
ตัวจัดการโปรโตคอล"] C3["SSH Client
ไคลเอนต์ SSH"] end subgraph SERVER["SFTP Server"] S1["SSH Server
เซิร์ฟเวอร์ SSH"] S2["SFTP Subsystem
ระบบย่อย SFTP"] S3["File System
ระบบไฟล์"] end C1 --> C2 C2 --> C3 C3 <-->|"SSH Encrypted Channel
ช่องทางเข้ารหัส Port 22"| S1 S1 --> S2 S2 --> S3 style CLIENT fill:#3c3836,stroke:#83a598,color:#ebdbb2 style SERVER fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style C1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style S1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style S2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style S3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2

2.3.3 การคำนวณ Throughput ของ SFTP

การเข้ารหัสของ SFTP มี overhead ที่ส่งผลต่อประสิทธิภาพ สามารถคำนวณ throughput โดยประมาณได้ดังนี้:

T effective = T raw 1 + O enc + O proto

คำอธิบายตัวแปร:

"""
โปรแกรมคำนวณ Throughput ของ SFTP

โปรแกรมนี้ช่วยประมาณ throughput ที่จะได้รับจริง
เมื่อใช้ SFTP เทียบกับ bandwidth ดิบของเครือข่าย
"""

def calculate_sftp_throughput(raw_bandwidth_mbps: float, 
                               encryption_overhead: float = 0.10,
                               protocol_overhead: float = 0.03) -> dict:
    """
    คำนวณ throughput ที่ใช้งานได้จริงของ SFTP
    
    Parameters:
        raw_bandwidth_mbps: แบนด์วิดท์ดิบเป็น Mbps
        encryption_overhead: overhead จากการเข้ารหัส (ค่าเริ่มต้น 10%)
        protocol_overhead: overhead จากโปรโตคอล (ค่าเริ่มต้น 3%)
    
    Returns:
        dict: ผลลัพธ์การคำนวณ
    """
    # คำนวณ throughput ที่ใช้งานได้จริง
    total_overhead = 1 + encryption_overhead + protocol_overhead
    effective_throughput = raw_bandwidth_mbps / total_overhead
    
    # คำนวณเวลาในการถ่ายโอนไฟล์ 1 GB
    file_size_mb = 1024 * 8  # 1 GB = 1024 MB = 8192 Mb
    transfer_time_seconds = file_size_mb / effective_throughput
    
    return {
        'raw_bandwidth_mbps': raw_bandwidth_mbps,
        'encryption_overhead': f"{encryption_overhead * 100:.1f}%",
        'protocol_overhead': f"{protocol_overhead * 100:.1f}%",
        'effective_throughput_mbps': round(effective_throughput, 2),
        'efficiency': f"{(effective_throughput/raw_bandwidth_mbps) * 100:.1f}%",
        'time_for_1gb_seconds': round(transfer_time_seconds, 1),
        'time_for_1gb_minutes': round(transfer_time_seconds / 60, 2)
    }

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    # ทดสอบกับ bandwidth ต่างๆ
    bandwidths = [100, 1000, 10000]  # 100 Mbps, 1 Gbps, 10 Gbps
    
    print("=" * 60)
    print("การคำนวณ SFTP Throughput")
    print("=" * 60)
    
    for bw in bandwidths:
        result = calculate_sftp_throughput(bw)
        print(f"\nBandwidth ดิบ: {bw} Mbps")
        print(f"  Throughput จริง: {result['effective_throughput_mbps']} Mbps")
        print(f"  ประสิทธิภาพ: {result['efficiency']}")
        print(f"  เวลาถ่ายโอน 1 GB: {result['time_for_1gb_minutes']} นาที")

# ผลลัพธ์ตัวอย่าง:
# ============================================================
# การคำนวณ SFTP Throughput
# ============================================================
#
# Bandwidth ดิบ: 100 Mbps
#   Throughput จริง: 88.5 Mbps
#   ประสิทธิภาพ: 88.5%
#   เวลาถ่ายโอน 1 GB: 1.54 นาที
#
# Bandwidth ดิบ: 1000 Mbps
#   Throughput จริง: 884.96 Mbps
#   ประสิทธิภาพ: 88.5%
#   เวลาถ่ายโอน 1 GB: 0.15 นาที

2.4 FTPS (FTP over SSL/TLS)

2.4.1 ความหมายและหลักการทำงาน

FTPS (FTP Secure) คือ FTP ที่เพิ่มชั้นความปลอดภัยด้วย SSL/TLS ถูกกำหนดใน RFC 4217 โดยยังคงใช้โครงสร้าง dual-connection เหมือน FTP แต่เพิ่มการเข้ารหัส

รูปแบบของ FTPS:

รูปแบบ คำอธิบาย Port
Implicit FTPS เข้ารหัสทันทีที่เชื่อมต่อ 990 (Control), 989 (Data)
Explicit FTPS เริ่มด้วย FTP ธรรมดา แล้วอัปเกรดด้วย AUTH TLS 21

2.4.2 กระบวนการ Handshake

sequenceDiagram
    participant C as Client
ไคลเอนต์ participant S as Server
เซิร์ฟเวอร์ Note over C,S: Explicit FTPS Handshake C->>S: เชื่อมต่อ TCP Port 21 S-->>C: 220 FTP Server Ready C->>S: AUTH TLS S-->>C: 234 Using TLS Note over C,S: TLS Handshake C->>S: ClientHello S-->>C: ServerHello + Certificate C->>S: Key Exchange S-->>C: Finished Note over C,S: Encrypted Session C->>S: USER username (encrypted) S-->>C: 331 Password Required C->>S: PASS password (encrypted) S-->>C: 230 Login Successful

2.4.3 เปรียบเทียบ FTPS กับ SFTP

คุณสมบัติ FTPS SFTP
พื้นฐาน FTP + SSL/TLS SSH Subsystem
Port 21, 990 + Data Port 22 เท่านั้น
การเชื่อมต่อ Multiple Single
Firewall ซับซ้อน (หลาย port) ง่าย (port เดียว)
Certificate ต้องใช้ X.509 ใช้ SSH Keys
ความเข้ากันได้ Legacy FTP clients ต้องการ SFTP client
มาตรฐาน RFC 4217 IETF Draft
"""
ตัวอย่างการเชื่อมต่อ FTPS ด้วย Python

โค้ดนี้แสดงวิธีการเชื่อมต่อและถ่ายโอนไฟล์ผ่าน FTPS
โดยใช้ทั้ง Implicit และ Explicit mode
"""

from ftplib import FTP_TLS
import ssl

def connect_ftps_explicit(host: str, username: str, password: str,
                          port: int = 21) -> FTP_TLS:
    """
    เชื่อมต่อ FTPS แบบ Explicit (AUTH TLS)
    
    Parameters:
        host: ชื่อโฮสต์หรือ IP address
        username: ชื่อผู้ใช้
        password: รหัสผ่าน
        port: พอร์ต (ค่าเริ่มต้น 21)
    
    Returns:
        FTP_TLS: อ็อบเจกต์การเชื่อมต่อ FTPS
    """
    # สร้างการเชื่อมต่อ FTP_TLS
    ftps = FTP_TLS()
    
    # กำหนด SSL context สำหรับความปลอดภัยที่ดีขึ้น
    context = ssl.create_default_context()
    context.check_hostname = True
    context.verify_mode = ssl.CERT_REQUIRED
    
    # เชื่อมต่อและล็อกอิน
    ftps.connect(host, port)
    ftps.auth()  # อัปเกรดเป็น TLS
    ftps.login(username, password)
    ftps.prot_p()  # เปิด Protected data connection
    
    print(f"[+] เชื่อมต่อ FTPS สำเร็จ: {host}:{port}")
    return ftps

def upload_file_ftps(ftps: FTP_TLS, local_path: str, remote_path: str):
    """
    อัปโหลดไฟล์ผ่าน FTPS
    
    Parameters:
        ftps: อ็อบเจกต์การเชื่อมต่อ FTPS
        local_path: path ของไฟล์ในเครื่อง
        remote_path: path ปลายทางบนเซิร์ฟเวอร์
    """
    with open(local_path, 'rb') as file:
        ftps.storbinary(f'STOR {remote_path}', file)
    print(f"[+] อัปโหลดสำเร็จ: {local_path} -> {remote_path}")

def download_file_ftps(ftps: FTP_TLS, remote_path: str, local_path: str):
    """
    ดาวน์โหลดไฟล์ผ่าน FTPS
    
    Parameters:
        ftps: อ็อบเจกต์การเชื่อมต่อ FTPS
        remote_path: path ของไฟล์บนเซิร์ฟเวอร์
        local_path: path ปลายทางในเครื่อง
    """
    with open(local_path, 'wb') as file:
        ftps.retrbinary(f'RETR {remote_path}', file.write)
    print(f"[+] ดาวน์โหลดสำเร็จ: {remote_path} -> {local_path}")

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    # การใช้งานจริง (ต้องเปลี่ยนค่าตามเซิร์ฟเวอร์จริง)
    """
    ftps = connect_ftps_explicit(
        host="ftp.example.com",
        username="user",
        password="password"
    )
    
    # แสดงรายการไฟล์
    ftps.dir()
    
    # อัปโหลด/ดาวน์โหลด
    upload_file_ftps(ftps, "local_file.txt", "remote_file.txt")
    download_file_ftps(ftps, "remote_file.txt", "downloaded.txt")
    
    # ปิดการเชื่อมต่อ
    ftps.quit()
    """
    print("ตัวอย่างการใช้งาน FTPS - ดูโค้ดในไฟล์")

3. การเข้าถึงระยะไกลอย่างปลอดภัย (Secure Remote Access)

3.1 SSH (Secure Shell)

3.1.1 ความหมายและหลักการทำงาน

SSH (Secure Shell) เป็นโปรโตคอลเครือข่ายที่ออกแบบมาเพื่อการสื่อสารที่ปลอดภัยระหว่างคอมพิวเตอร์สองเครื่อง ถูกพัฒนาโดย Tatu Ylönen ในปี 1995 เพื่อทดแทน Telnet, rlogin และ rsh ที่ไม่ปลอดภัย

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

3.1.2 สถาปัตยกรรม SSH

SSH ประกอบด้วย 3 ส่วนหลัก:

flowchart TB
    subgraph SSH["SSH Protocol Stack"]
        direction TB
        subgraph CONN["SSH Connection Layer
ชั้นการเชื่อมต่อ"] C1["Session Channel"] C2["X11 Forwarding"] C3["Port Forwarding"] C4["SFTP Subsystem"] end subgraph AUTH["SSH User Authentication Layer
ชั้นการยืนยันตัวตน"] A1["Password"] A2["Public Key"] A3["Keyboard-Interactive"] A4["GSSAPI"] end subgraph TRANS["SSH Transport Layer
ชั้นการขนส่ง"] T1["Key Exchange
การแลกเปลี่ยนกุญแจ"] T2["Server Authentication
การยืนยันเซิร์ฟเวอร์"] T3["Encryption
การเข้ารหัส"] T4["Integrity
การตรวจสอบความถูกต้อง"] end TCP["TCP Port 22"] end CONN --> AUTH --> TRANS --> TCP style SSH fill:#282828,stroke:#ebdbb2,color:#ebdbb2 style CONN fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style AUTH fill:#3c3836,stroke:#83a598,color:#ebdbb2 style TRANS fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style C1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style C4 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style A4 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style T1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style T2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style T3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style T4 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style TCP fill:#504945,stroke:#fb4934,color:#ebdbb2

3.1.3 กระบวนการยืนยันตัวตน

การยืนยันตัวตนด้วย Public Key:

sequenceDiagram
    participant C as Client
ไคลเอนต์ participant S as Server
เซิร์ฟเวอร์ Note over C,S: Transport Layer Setup C->>S: TCP Connection (Port 22) C->>S: SSH_MSG_KEXINIT S-->>C: SSH_MSG_KEXINIT Note over C,S: Key Exchange (e.g., ECDH) C->>S: SSH_MSG_KEX_ECDH_INIT S-->>C: SSH_MSG_KEX_ECDH_REPLY + Server's Public Key C->>S: SSH_MSG_NEWKEYS S-->>C: SSH_MSG_NEWKEYS Note over C,S: User Authentication with Public Key C->>S: SSH_MSG_USERAUTH_REQUEST (publickey) Note right of C: ส่ง username + algorithm + public key S-->>C: SSH_MSG_USERAUTH_PK_OK Note left of S: Server ยืนยันว่า key อยู่ใน authorized_keys C->>S: SSH_MSG_USERAUTH_REQUEST + Signature Note right of C: เซ็น session ID ด้วย private key S-->>C: SSH_MSG_USERAUTH_SUCCESS Note left of S: ตรวจสอบ signature สำเร็จ

3.1.4 อัลกอริทึมการเข้ารหัสที่รองรับ

ประเภท อัลกอริทึมที่แนะนำ อัลกอริทึมที่ควรหลีกเลี่ยง
Key Exchange curve25519-sha256, ecdh-sha2-nistp256 diffie-hellman-group1-sha1
Host Key ssh-ed25519, rsa-sha2-512 ssh-dss, ssh-rsa (SHA-1)
Cipher chacha20-poly1305, aes256-gcm 3des-cbc, arcfour
MAC hmac-sha2-512-etm, umac-128-etm hmac-md5, hmac-sha1

3.1.5 การจัดการ SSH Keys

"""
โปรแกรมจัดการ SSH Keys

โปรแกรมนี้ช่วยในการสร้าง จัดการ และตรวจสอบ SSH keys
รวมถึงการตั้งค่าการยืนยันตัวตนแบบ Public Key
"""

import subprocess
import os
from pathlib import Path
from typing import Optional, Tuple

class SSHKeyManager:
    """
    คลาสสำหรับจัดการ SSH Keys
    
    รองรับการสร้าง key ใหม่, การตรวจสอบ key ที่มีอยู่
    และการจัดการ authorized_keys
    """
    
    def __init__(self, ssh_dir: Optional[str] = None):
        """
        สร้าง SSHKeyManager
        
        Parameters:
            ssh_dir: path ของ .ssh directory (ค่าเริ่มต้น ~/.ssh)
        """
        if ssh_dir is None:
            self.ssh_dir = Path.home() / ".ssh"
        else:
            self.ssh_dir = Path(ssh_dir)
        
        # สร้าง directory ถ้ายังไม่มี
        self.ssh_dir.mkdir(mode=0o700, exist_ok=True)
    
    def generate_key(self, key_type: str = "ed25519",
                     key_name: str = "id_ed25519",
                     comment: str = "",
                     passphrase: str = "") -> Tuple[Path, Path]:
        """
        สร้าง SSH key pair ใหม่
        
        Parameters:
            key_type: ประเภทของ key (ed25519, rsa, ecdsa)
            key_name: ชื่อไฟล์ของ key
            comment: คอมเมนต์ที่แนบไปกับ key
            passphrase: รหัสผ่านสำหรับป้องกัน private key
        
        Returns:
            Tuple[Path, Path]: (private_key_path, public_key_path)
        """
        private_key_path = self.ssh_dir / key_name
        public_key_path = self.ssh_dir / f"{key_name}.pub"
        
        # สร้างคำสั่ง ssh-keygen
        cmd = [
            "ssh-keygen",
            "-t", key_type,
            "-f", str(private_key_path),
            "-N", passphrase,  # passphrase (ว่างถ้าไม่ต้องการ)
        ]
        
        if comment:
            cmd.extend(["-C", comment])
        
        # เพิ่ม options ตามประเภท key
        if key_type == "rsa":
            cmd.extend(["-b", "4096"])  # RSA ควรใช้อย่างน้อย 4096 bits
        
        # รันคำสั่ง
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"[+] สร้าง SSH key สำเร็จ:")
            print(f"    Private key: {private_key_path}")
            print(f"    Public key: {public_key_path}")
            return (private_key_path, public_key_path)
        else:
            raise Exception(f"สร้าง key ไม่สำเร็จ: {result.stderr}")
    
    def get_key_fingerprint(self, key_path: Path) -> str:
        """
        ดึง fingerprint ของ SSH key
        
        Parameters:
            key_path: path ของ key file
        
        Returns:
            str: fingerprint ของ key
        """
        cmd = ["ssh-keygen", "-l", "-f", str(key_path)]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            return result.stdout.strip()
        else:
            raise Exception(f"อ่าน fingerprint ไม่สำเร็จ: {result.stderr}")
    
    def add_to_authorized_keys(self, public_key_path: Path,
                                target_user: str = "",
                                target_host: str = "") -> bool:
        """
        เพิ่ม public key ไปยัง authorized_keys ของ remote server
        
        Parameters:
            public_key_path: path ของ public key
            target_user: username บน remote server
            target_host: hostname หรือ IP ของ remote server
        
        Returns:
            bool: สำเร็จหรือไม่
        """
        if not target_host:
            # เพิ่มในเครื่องตัวเอง
            auth_keys_path = self.ssh_dir / "authorized_keys"
            
            with open(public_key_path, 'r') as pub_key:
                key_content = pub_key.read().strip()
            
            # ตรวจสอบว่า key มีอยู่แล้วหรือไม่
            if auth_keys_path.exists():
                with open(auth_keys_path, 'r') as auth_keys:
                    if key_content in auth_keys.read():
                        print("[!] Key นี้มีอยู่แล้วใน authorized_keys")
                        return True
            
            # เพิ่ม key
            with open(auth_keys_path, 'a') as auth_keys:
                auth_keys.write(f"{key_content}\n")
            
            # ตั้งค่า permission
            auth_keys_path.chmod(0o600)
            print(f"[+] เพิ่ม key ไปยัง {auth_keys_path} สำเร็จ")
            return True
        else:
            # ใช้ ssh-copy-id สำหรับ remote server
            target = f"{target_user}@{target_host}" if target_user else target_host
            cmd = ["ssh-copy-id", "-i", str(public_key_path), target]
            
            print(f"[*] กำลังคัดลอก key ไปยัง {target}...")
            result = subprocess.run(cmd)
            
            return result.returncode == 0
    
    def list_keys(self) -> list:
        """
        แสดงรายการ SSH keys ทั้งหมดใน .ssh directory
        
        Returns:
            list: รายการของ key files
        """
        keys = []
        for file in self.ssh_dir.iterdir():
            if file.suffix == '.pub':
                private_key = file.with_suffix('')
                if private_key.exists():
                    fingerprint = self.get_key_fingerprint(file)
                    keys.append({
                        'name': private_key.name,
                        'private': private_key,
                        'public': file,
                        'fingerprint': fingerprint
                    })
        return keys

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    manager = SSHKeyManager()
    
    print("=" * 60)
    print("SSH Key Manager - ตัวอย่างการใช้งาน")
    print("=" * 60)
    
    # แสดงรายการ keys ที่มีอยู่
    print("\n[*] รายการ SSH Keys ที่มีอยู่:")
    existing_keys = manager.list_keys()
    for key in existing_keys:
        print(f"  - {key['name']}: {key['fingerprint']}")
    
    # ตัวอย่างการสร้าง key ใหม่ (comment out เพื่อไม่ให้รันจริง)
    """
    # สร้าง ED25519 key (แนะนำ)
    priv, pub = manager.generate_key(
        key_type="ed25519",
        key_name="id_ed25519_new",
        comment="user@hostname"
    )
    
    # สร้าง RSA key (สำหรับระบบเก่า)
    priv, pub = manager.generate_key(
        key_type="rsa",
        key_name="id_rsa_4096",
        comment="legacy-system"
    )
    """
    
    print("\n[*] เสร็จสิ้น")

3.1.6 การตั้งค่า SSH Server ที่ปลอดภัย

"""
สคริปต์สร้างการตั้งค่า SSH Server ที่ปลอดภัย

สคริปต์นี้สร้างไฟล์ sshd_config ที่มีการตั้งค่าความปลอดภัยที่แนะนำ
สำหรับ OpenSSH Server
"""

def generate_secure_sshd_config(
    port: int = 22,
    permit_root_login: str = "no",
    password_auth: bool = False,
    pubkey_auth: bool = True,
    allowed_users: list = None,
    max_auth_tries: int = 3,
    client_alive_interval: int = 300,
    client_alive_count_max: int = 2
) -> str:
    """
    สร้างการตั้งค่า sshd_config ที่ปลอดภัย
    
    Parameters:
        port: พอร์ตที่ใช้งาน
        permit_root_login: อนุญาตให้ root login หรือไม่
        password_auth: อนุญาตการยืนยันตัวตนด้วยรหัสผ่าน
        pubkey_auth: อนุญาตการยืนยันตัวตนด้วย public key
        allowed_users: รายชื่อผู้ใช้ที่อนุญาต
        max_auth_tries: จำนวนครั้งสูงสุดที่พยายาม login
        client_alive_interval: ช่วงเวลาตรวจสอบการเชื่อมต่อ (วินาที)
        client_alive_count_max: จำนวนครั้งสูงสุดที่ไม่มีการตอบกลับ
    
    Returns:
        str: เนื้อหาของ sshd_config
    """
    config = f"""# OpenSSH Server Configuration - Secure Settings
# สร้างโดยอัตโนมัติ - กรุณาตรวจสอบก่อนใช้งาน

# ========================================
# พื้นฐานการเชื่อมต่อ
# ========================================
Port {port}
Protocol 2
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::

# ========================================
# การยืนยันตัวตน
# ========================================
PermitRootLogin {permit_root_login}
PubkeyAuthentication {"yes" if pubkey_auth else "no"}
PasswordAuthentication {"yes" if password_auth else "no"}
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM yes

# จำกัดจำนวนครั้งในการ login
MaxAuthTries {max_auth_tries}
MaxSessions 10
LoginGraceTime 60

# ========================================
# อัลกอริทึมการเข้ารหัส (เฉพาะที่แนะนำ)
# ========================================
# Key Exchange
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256

# Host Key
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256

# Ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

# MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

# ========================================
# การตั้งค่าความปลอดภัยเพิ่มเติม
# ========================================
# ปิด features ที่ไม่จำเป็น
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
GatewayPorts no

# ตรวจสอบ permissions
StrictModes yes

# Logging
SyslogFacility AUTH
LogLevel VERBOSE

# ตรวจสอบการเชื่อมต่อ
ClientAliveInterval {client_alive_interval}
ClientAliveCountMax {client_alive_count_max}
TCPKeepAlive yes

# Banner
Banner /etc/ssh/banner

# ========================================
# SFTP
# ========================================
Subsystem sftp /usr/lib/openssh/sftp-server -f AUTH -l INFO

# ========================================
# จำกัดผู้ใช้ (ถ้ากำหนด)
# ========================================
"""
    
    if allowed_users:
        config += f"AllowUsers {' '.join(allowed_users)}\n"
    
    config += """
# ========================================
# Match Rules (ตัวอย่าง)
# ========================================
# จำกัด SFTP สำหรับกลุ่ม sftpusers
#Match Group sftpusers
#    ChrootDirectory /home/%u
#    ForceCommand internal-sftp
#    AllowTcpForwarding no
#    X11Forwarding no
"""
    
    return config

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    # สร้าง config ที่ปลอดภัย
    config = generate_secure_sshd_config(
        port=22,
        permit_root_login="no",
        password_auth=False,
        pubkey_auth=True,
        allowed_users=["admin", "developer"],
        max_auth_tries=3
    )
    
    print("=" * 60)
    print("Secure SSH Server Configuration")
    print("=" * 60)
    print(config)
    
    # บันทึกไฟล์ (comment out เพื่อไม่ให้รันจริง)
    # with open("/etc/ssh/sshd_config.secure", "w") as f:
    #     f.write(config)
    # print("\n[+] บันทึกไฟล์สำเร็จ")

3.2 SCP (Secure Copy)

3.2.1 ความหมายและหลักการทำงาน

SCP (Secure Copy Protocol) เป็นโปรโตคอลสำหรับคัดลอกไฟล์ระหว่างเครื่องผ่าน SSH โดยใช้การเข้ารหัสและการยืนยันตัวตนของ SSH

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

3.2.2 รูปแบบการใช้งาน

# รูปแบบพื้นฐาน
# scp [options] source destination

# คัดลอกไฟล์จากเครื่องไปยัง remote
scp local_file.txt user@remote:/path/to/destination/

# คัดลอกไฟล์จาก remote มายังเครื่อง
scp user@remote:/path/to/file.txt ./local_directory/

# คัดลอกไดเรกทอรีทั้งหมด (recursive)
scp -r local_directory/ user@remote:/path/to/destination/

# ใช้ port อื่น
scp -P 2222 file.txt user@remote:/path/

# ใช้ identity file (private key)
scp -i ~/.ssh/id_ed25519 file.txt user@remote:/path/

# คัดลอกระหว่าง remote servers
scp user1@host1:/file.txt user2@host2:/destination/

3.2.3 เปรียบเทียบ SCP กับ SFTP

คุณสมบัติ SCP SFTP
การใช้งาน Non-interactive, command-line Interactive หรือ automated
การทำงาน คัดลอกไฟล์เท่านั้น จัดการไฟล์ครบวงจร
Resume transfer ไม่รองรับ รองรับ
Progress แสดงความคืบหน้า รองรับ verbose mode
Protocol RCP over SSH SSH Subsystem
ความเร็ว เร็วกว่าเล็กน้อย ช้ากว่าเล็กน้อย (overhead)
"""
โปรแกรมจัดการการถ่ายโอนไฟล์ด้วย SCP/SFTP

โปรแกรมนี้แสดงวิธีการใช้ Python ในการถ่ายโอนไฟล์
ผ่าน SCP และ SFTP โดยใช้ paramiko library
"""

import paramiko
from scp import SCPClient
from pathlib import Path
from typing import Optional, Callable
import os

class SecureFileTransfer:
    """
    คลาสสำหรับถ่ายโอนไฟล์อย่างปลอดภัยผ่าน SSH
    
    รองรับทั้ง SCP และ SFTP
    """
    
    def __init__(self, hostname: str, username: str,
                 port: int = 22,
                 private_key_path: Optional[str] = None,
                 password: Optional[str] = None):
        """
        สร้างการเชื่อมต่อ SSH
        
        Parameters:
            hostname: ชื่อโฮสต์หรือ IP address
            username: ชื่อผู้ใช้
            port: พอร์ต SSH (ค่าเริ่มต้น 22)
            private_key_path: path ของ private key
            password: รหัสผ่าน (ถ้าไม่ใช้ key)
        """
        self.hostname = hostname
        self.username = username
        self.port = port
        
        # สร้าง SSH client
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        # เชื่อมต่อ
        connect_kwargs = {
            'hostname': hostname,
            'port': port,
            'username': username
        }
        
        if private_key_path:
            # ใช้ private key
            key = paramiko.Ed25519Key.from_private_key_file(private_key_path)
            connect_kwargs['pkey'] = key
        elif password:
            # ใช้รหัสผ่าน
            connect_kwargs['password'] = password
        
        self.ssh.connect(**connect_kwargs)
        print(f"[+] เชื่อมต่อ SSH สำเร็จ: {username}@{hostname}:{port}")
    
    def scp_upload(self, local_path: str, remote_path: str,
                   progress_callback: Optional[Callable] = None):
        """
        อัปโหลดไฟล์ด้วย SCP
        
        Parameters:
            local_path: path ของไฟล์ในเครื่อง
            remote_path: path ปลายทางบน remote server
            progress_callback: callback function สำหรับแสดงความคืบหน้า
        """
        def default_progress(filename, size, sent):
            percent = (sent / size) * 100
            print(f"\r[SCP] {filename}: {percent:.1f}%", end='', flush=True)
        
        callback = progress_callback or default_progress
        
        with SCPClient(self.ssh.get_transport(), progress=callback) as scp:
            scp.put(local_path, remote_path)
        
        print(f"\n[+] SCP อัปโหลดสำเร็จ: {local_path} -> {remote_path}")
    
    def scp_download(self, remote_path: str, local_path: str,
                     progress_callback: Optional[Callable] = None):
        """
        ดาวน์โหลดไฟล์ด้วย SCP
        
        Parameters:
            remote_path: path ของไฟล์บน remote server
            local_path: path ปลายทางในเครื่อง
            progress_callback: callback function สำหรับแสดงความคืบหน้า
        """
        def default_progress(filename, size, received):
            percent = (received / size) * 100
            print(f"\r[SCP] {filename}: {percent:.1f}%", end='', flush=True)
        
        callback = progress_callback or default_progress
        
        with SCPClient(self.ssh.get_transport(), progress=callback) as scp:
            scp.get(remote_path, local_path)
        
        print(f"\n[+] SCP ดาวน์โหลดสำเร็จ: {remote_path} -> {local_path}")
    
    def sftp_upload(self, local_path: str, remote_path: str):
        """
        อัปโหลดไฟล์ด้วย SFTP
        
        Parameters:
            local_path: path ของไฟล์ในเครื่อง
            remote_path: path ปลายทางบน remote server
        """
        sftp = self.ssh.open_sftp()
        
        try:
            # ตรวจสอบว่า local_path เป็นไดเรกทอรีหรือไม่
            if os.path.isdir(local_path):
                self._sftp_upload_directory(sftp, local_path, remote_path)
            else:
                sftp.put(local_path, remote_path)
                print(f"[+] SFTP อัปโหลดสำเร็จ: {local_path} -> {remote_path}")
        finally:
            sftp.close()
    
    def _sftp_upload_directory(self, sftp, local_dir: str, remote_dir: str):
        """
        อัปโหลดไดเรกทอรีทั้งหมดด้วย SFTP (recursive)
        
        Parameters:
            sftp: SFTP client object
            local_dir: path ของไดเรกทอรีในเครื่อง
            remote_dir: path ปลายทางบน remote server
        """
        # สร้างไดเรกทอรี remote ถ้ายังไม่มี
        try:
            sftp.mkdir(remote_dir)
        except IOError:
            pass  # มีอยู่แล้ว
        
        for item in os.listdir(local_dir):
            local_path = os.path.join(local_dir, item)
            remote_path = f"{remote_dir}/{item}"
            
            if os.path.isdir(local_path):
                self._sftp_upload_directory(sftp, local_path, remote_path)
            else:
                sftp.put(local_path, remote_path)
                print(f"  [+] {local_path} -> {remote_path}")
    
    def sftp_download(self, remote_path: str, local_path: str):
        """
        ดาวน์โหลดไฟล์ด้วย SFTP
        
        Parameters:
            remote_path: path ของไฟล์บน remote server
            local_path: path ปลายทางในเครื่อง
        """
        sftp = self.ssh.open_sftp()
        
        try:
            sftp.get(remote_path, local_path)
            print(f"[+] SFTP ดาวน์โหลดสำเร็จ: {remote_path} -> {local_path}")
        finally:
            sftp.close()
    
    def sftp_list_directory(self, remote_path: str = '.') -> list:
        """
        แสดงรายการไฟล์ใน remote directory
        
        Parameters:
            remote_path: path ของไดเรกทอรีที่ต้องการดู
        
        Returns:
            list: รายการของไฟล์และไดเรกทอรี
        """
        sftp = self.ssh.open_sftp()
        
        try:
            items = []
            for entry in sftp.listdir_attr(remote_path):
                items.append({
                    'name': entry.filename,
                    'size': entry.st_size,
                    'mode': oct(entry.st_mode)[-3:],
                    'is_dir': entry.st_mode & 0o40000 != 0
                })
            return items
        finally:
            sftp.close()
    
    def close(self):
        """ปิดการเชื่อมต่อ SSH"""
        self.ssh.close()
        print(f"[-] ปิดการเชื่อมต่อ SSH: {self.hostname}")
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    print("=" * 60)
    print("Secure File Transfer - ตัวอย่างการใช้งาน")
    print("=" * 60)
    
    # ตัวอย่างการใช้งาน (ต้องเปลี่ยนค่าตามเซิร์ฟเวอร์จริง)
    """
    with SecureFileTransfer(
        hostname="192.168.1.100",
        username="admin",
        private_key_path="~/.ssh/id_ed25519"
    ) as transfer:
        
        # SCP Upload
        transfer.scp_upload("local_file.txt", "/home/admin/file.txt")
        
        # SCP Download
        transfer.scp_download("/home/admin/file.txt", "downloaded.txt")
        
        # SFTP Upload directory
        transfer.sftp_upload("./local_dir", "/home/admin/remote_dir")
        
        # List remote directory
        files = transfer.sftp_list_directory("/home/admin")
        for f in files:
            print(f"  {'[D]' if f['is_dir'] else '[F]'} {f['name']} ({f['size']} bytes)")
    """
    
    print("\nดูตัวอย่างโค้ดในไฟล์สำหรับการใช้งานจริง")

4. เครื่องมือและการปฏิบัติ (Tools and Practice)

4.1 คำสั่งพื้นฐานที่ควรรู้

4.1.1 SSH Commands

# ========================================
# การเชื่อมต่อพื้นฐาน
# ========================================

# เชื่อมต่อ SSH ปกติ
ssh user@hostname

# ระบุ port
ssh -p 2222 user@hostname

# ใช้ private key ที่ระบุ
ssh -i ~/.ssh/id_ed25519 user@hostname

# เปิด verbose mode เพื่อ debug
ssh -v user@hostname    # -v, -vv, -vvv

# ========================================
# การจัดการ SSH Keys
# ========================================

# สร้าง ED25519 key (แนะนำ)
ssh-keygen -t ed25519 -C "comment"

# สร้าง RSA 4096-bit key
ssh-keygen -t rsa -b 4096 -C "comment"

# ดู fingerprint ของ key
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

# คัดลอก public key ไป remote server
ssh-copy-id user@hostname

# ========================================
# SSH Agent
# ========================================

# เริ่ม ssh-agent
eval $(ssh-agent -s)

# เพิ่ม key เข้า agent
ssh-add ~/.ssh/id_ed25519

# แสดง keys ใน agent
ssh-add -l

# ลบ keys ออกจาก agent
ssh-add -D

# ========================================
# Port Forwarding
# ========================================

# Local port forwarding
# เข้าถึง remote:3306 ผ่าน localhost:13306
ssh -L 13306:localhost:3306 user@hostname

# Remote port forwarding
# เปิด port 8080 บน remote ให้เข้าถึง localhost:80
ssh -R 8080:localhost:80 user@hostname

# Dynamic port forwarding (SOCKS proxy)
ssh -D 1080 user@hostname

# ========================================
# การใช้งานขั้นสูง
# ========================================

# ProxyJump (เข้าผ่าน bastion host)
ssh -J jump@bastion user@internal

# Agent forwarding (ใช้ key บน remote)
ssh -A user@hostname

# รันคำสั่งบน remote
ssh user@hostname "ls -la /var/log"

# รันหลายคำสั่ง
ssh user@hostname "cd /var/log && tail -n 100 syslog"

4.1.2 SCP/SFTP Commands

# ========================================
# SCP Commands
# ========================================

# อัปโหลดไฟล์
scp local.txt user@host:/path/

# ดาวน์โหลดไฟล์
scp user@host:/path/remote.txt ./

# คัดลอก directory (recursive)
scp -r local_dir/ user@host:/path/

# ใช้ compression
scp -C large_file.tar user@host:/path/

# จำกัด bandwidth (KB/s)
scp -l 1024 file.txt user@host:/path/

# ========================================
# SFTP Commands
# ========================================

# เชื่อมต่อ SFTP
sftp user@hostname

# คำสั่งใน SFTP session
sftp> pwd           # แสดง remote directory
sftp> lpwd          # แสดง local directory
sftp> ls            # list remote
sftp> lls           # list local
sftp> cd /path      # change remote dir
sftp> lcd /path     # change local dir
sftp> get file.txt  # download
sftp> put file.txt  # upload
sftp> mkdir dir     # create directory
sftp> rm file.txt   # delete file
sftp> bye           # disconnect

# Batch mode
sftp -b commands.txt user@hostname

3.3 SSHFS (SSH Filesystem)

3.3.1 ความหมายและหลักการทำงาน

SSHFS (SSH Filesystem) เป็นระบบไฟล์แบบ client-based ที่ช่วยให้สามารถ mount ไดเรกทอรีจาก remote server มาใช้งานบนเครื่อง local เสมือนเป็นไดเรกทอรีในเครื่องตัวเอง โดยใช้ SSH เป็น transport layer

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

3.3.2 สถาปัตยกรรมและการทำงาน

flowchart TB
    subgraph CLIENT["Local Machine เครื่อง Local"]
        APP["Application
แอปพลิเคชัน"] VFS["VFS (Virtual File System)
ระบบไฟล์เสมือน"] FUSE["FUSE Module
โมดูล FUSE"] SSHFS["SSHFS Client
ไคลเอนต์ SSHFS"] SSHC["SSH Client
ไคลเอนต์ SSH"] end subgraph SERVER["Remote Server เซิร์ฟเวอร์"] SSHS["SSH Server
เซิร์ฟเวอร์ SSH"] SFTP["SFTP Subsystem
ระบบย่อย SFTP"] FS["File System
ระบบไฟล์จริง"] end APP -->|"read/write"| VFS VFS -->|"FUSE calls"| FUSE FUSE -->|"SFTP requests"| SSHFS SSHFS --> SSHC SSHC <-->|"SSH Encrypted
Port 22"| SSHS SSHS --> SFTP SFTP --> FS style CLIENT fill:#3c3836,stroke:#83a598,color:#ebdbb2 style SERVER fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style APP fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style VFS fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style FUSE fill:#504945,stroke:#fabd2f,color:#ebdbb2 style SSHFS fill:#504945,stroke:#83a598,color:#ebdbb2 style SSHC fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style SSHS fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style SFTP fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style FS fill:#504945,stroke:#b8bb26,color:#ebdbb2

ขั้นตอนการทำงาน:

  1. Application ร้องขอการอ่าน/เขียนไฟล์ผ่าน system call ปกติ
  2. VFS รับ request และส่งต่อไปยัง FUSE module
  3. FUSE แปลง request เป็น SFTP protocol
  4. SSHFS ส่ง request ผ่าน SSH encrypted channel
  5. SSH Server รับและส่งต่อไปยัง SFTP subsystem
  6. SFTP ดำเนินการกับระบบไฟล์จริงและส่งผลลัพธ์กลับ

3.3.3 การติดตั้งและตั้งค่าเบื้องต้น

# ========================================
# การติดตั้ง SSHFS
# ========================================

# Ubuntu/Debian
sudo apt update
sudo apt install sshfs

# Fedora/RHEL/CentOS
sudo dnf install fuse-sshfs

# Arch Linux
sudo pacman -S sshfs

# macOS (ต้องติดตั้ง macFUSE ก่อน)
brew install macfuse
brew install sshfs

# ตรวจสอบการติดตั้ง
sshfs --version

# ========================================
# การเตรียม Mount Point
# ========================================

# สร้าง directory สำหรับ mount
mkdir -p ~/mnt/remote_server

# ตรวจสอบว่า user อยู่ใน group fuse (บาง distro)
groups $USER
# ถ้าไม่มี ให้เพิ่ม
sudo usermod -aG fuse $USER
# แล้ว logout/login ใหม่

3.3.4 คำสั่งพื้นฐานและ Options ที่สำคัญ

# ========================================
# การ Mount พื้นฐาน
# ========================================

# รูปแบบพื้นฐาน
sshfs [user@]host:[remote_path] mountpoint [options]

# Mount home directory ของ remote user
sshfs user@192.168.1.100: ~/mnt/remote

# Mount directory ที่ระบุ
sshfs user@server:/var/www ~/mnt/webserver

# Mount ด้วย port ที่กำหนด
sshfs -p 2222 user@server:/data ~/mnt/data

# ========================================
# Options ที่สำคัญ
# ========================================

# -o IdentityFile: ระบุ private key
sshfs -o IdentityFile=~/.ssh/id_ed25519 user@server:/path ~/mnt/remote

# -o reconnect: เชื่อมต่อใหม่อัตโนมัติเมื่อขาดการเชื่อมต่อ
sshfs -o reconnect user@server:/path ~/mnt/remote

# -o ServerAliveInterval: ส่ง keepalive ทุก N วินาที
sshfs -o ServerAliveInterval=15 user@server:/path ~/mnt/remote

# -o ServerAliveCountMax: จำนวนครั้งที่ไม่มีการตอบกลับก่อนตัดการเชื่อมต่อ
sshfs -o ServerAliveCountMax=3 user@server:/path ~/mnt/remote

# -o Compression=yes: เปิดการบีบอัดข้อมูล (ดีสำหรับ slow connection)
sshfs -o Compression=yes user@server:/path ~/mnt/remote

# -o Ciphers: ระบุ cipher (เลือก cipher ที่เร็วกว่า)
sshfs -o Ciphers=aes128-gcm@openssh.com user@server:/path ~/mnt/remote

# -o cache=yes: เปิด caching เพื่อเพิ่มประสิทธิภาพ
sshfs -o cache=yes user@server:/path ~/mnt/remote

# -o kernel_cache: ใช้ kernel caching
sshfs -o kernel_cache user@server:/path ~/mnt/remote

# -o auto_cache: automatic cache invalidation
sshfs -o auto_cache user@server:/path ~/mnt/remote

# ========================================
# Options สำหรับ Permission และ Ownership
# ========================================

# -o allow_other: อนุญาตให้ users อื่นเข้าถึง (ต้องตั้งค่าใน /etc/fuse.conf)
sshfs -o allow_other user@server:/path ~/mnt/remote

# -o default_permissions: ใช้ permission checking ของ kernel
sshfs -o default_permissions user@server:/path ~/mnt/remote

# -o uid, gid: กำหนด owner ของไฟล์ที่ mount
sshfs -o uid=$(id -u),gid=$(id -g) user@server:/path ~/mnt/remote

# -o umask: กำหนด umask สำหรับไฟล์
sshfs -o umask=0022 user@server:/path ~/mnt/remote

# ========================================
# การ Unmount
# ========================================

# วิธีที่ 1: fusermount (แนะนำ)
fusermount -u ~/mnt/remote

# วิธีที่ 2: umount
umount ~/mnt/remote

# Force unmount (กรณี busy)
fusermount -uz ~/mnt/remote

# หรือ
sudo umount -l ~/mnt/remote

3.3.5 การตั้งค่าสำหรับการใช้งานจริง

# ========================================
# การ Mount แบบ Production-Ready
# ========================================

# Full options สำหรับการใช้งานจริง
sshfs user@server:/data ~/mnt/data \
    -o IdentityFile=~/.ssh/id_ed25519 \
    -o reconnect \
    -o ServerAliveInterval=15 \
    -o ServerAliveCountMax=3 \
    -o Compression=no \
    -o Ciphers=chacha20-poly1305@openssh.com \
    -o cache=yes \
    -o kernel_cache \
    -o auto_cache \
    -o uid=$(id -u) \
    -o gid=$(id -g) \
    -o idmap=user \
    -o follow_symlinks \
    -o transform_symlinks

# ========================================
# การตั้งค่า Auto-Mount ด้วย /etc/fstab
# ========================================

# เพิ่มบรรทัดนี้ใน /etc/fstab
# user@server:/remote/path /local/mountpoint fuse.sshfs options 0 0

# ตัวอย่าง:
# admin@192.168.1.100:/var/www /mnt/webserver fuse.sshfs \
#     IdentityFile=/home/user/.ssh/id_ed25519,\
#     allow_other,\
#     reconnect,\
#     ServerAliveInterval=15,\
#     _netdev,\
#     users,\
#     idmap=user 0 0

# _netdev: รอ network ก่อน mount
# users: อนุญาตให้ user ทั่วไป mount/unmount

# Mount ทุก entry ใน fstab
sudo mount -a

3.3.6 การตั้งค่าใน /etc/fuse.conf

# แก้ไข /etc/fuse.conf เพื่อเปิดใช้งาน allow_other
sudo nano /etc/fuse.conf

# เพิ่มหรือ uncomment บรรทัดนี้:
user_allow_other

# บันทึกและออก

3.3.7 ตัวอย่างโปรแกรม Python สำหรับจัดการ SSHFS

"""
โปรแกรมจัดการ SSHFS Mount Points

โปรแกรมนี้ช่วยในการ mount, unmount และจัดการ
SSHFS connections อย่างมีประสิทธิภาพ
"""

import subprocess
import os
from pathlib import Path
from typing import Optional, List, Dict
from dataclasses import dataclass
import json

@dataclass
class SSHFSMount:
    """
    คลาสเก็บข้อมูลการ mount SSHFS
    
    Attributes:
        name: ชื่อเรียกสำหรับ mount point นี้
        user: ชื่อผู้ใช้บน remote server
        host: hostname หรือ IP address
        remote_path: path บน remote server
        local_path: path สำหรับ mount บนเครื่อง local
        port: SSH port (ค่าเริ่มต้น 22)
        identity_file: path ของ private key
        options: options เพิ่มเติม
    """
    name: str
    user: str
    host: str
    remote_path: str
    local_path: str
    port: int = 22
    identity_file: Optional[str] = None
    options: Optional[List[str]] = None


class SSHFSManager:
    """
    คลาสจัดการ SSHFS Mount Points
    
    รองรับการ mount, unmount, ตรวจสอบสถานะ
    และบันทึกการตั้งค่า
    """
    
    def __init__(self, config_file: Optional[str] = None):
        """
        สร้าง SSHFSManager
        
        Parameters:
            config_file: path ของไฟล์ config (JSON)
        """
        self.config_file = config_file or os.path.expanduser("~/.sshfs_mounts.json")
        self.mounts: Dict[str, SSHFSMount] = {}
        self._load_config()
    
    def _load_config(self):
        """โหลดการตั้งค่าจากไฟล์"""
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, 'r') as f:
                    data = json.load(f)
                    for name, mount_data in data.items():
                        self.mounts[name] = SSHFSMount(**mount_data)
                print(f"[+] โหลดการตั้งค่าจาก {self.config_file}")
            except Exception as e:
                print(f"[!] ไม่สามารถโหลดการตั้งค่า: {e}")
    
    def _save_config(self):
        """บันทึกการตั้งค่าลงไฟล์"""
        try:
            data = {}
            for name, mount in self.mounts.items():
                data[name] = {
                    'name': mount.name,
                    'user': mount.user,
                    'host': mount.host,
                    'remote_path': mount.remote_path,
                    'local_path': mount.local_path,
                    'port': mount.port,
                    'identity_file': mount.identity_file,
                    'options': mount.options
                }
            with open(self.config_file, 'w') as f:
                json.dump(data, f, indent=2)
            print(f"[+] บันทึกการตั้งค่าไปยัง {self.config_file}")
        except Exception as e:
            print(f"[!] ไม่สามารถบันทึกการตั้งค่า: {e}")
    
    def add_mount(self, mount: SSHFSMount) -> bool:
        """
        เพิ่มการตั้งค่า mount point ใหม่
        
        Parameters:
            mount: ข้อมูลการ mount
        
        Returns:
            bool: สำเร็จหรือไม่
        """
        if mount.name in self.mounts:
            print(f"[!] Mount '{mount.name}' มีอยู่แล้ว")
            return False
        
        self.mounts[mount.name] = mount
        self._save_config()
        print(f"[+] เพิ่ม mount '{mount.name}' สำเร็จ")
        return True
    
    def remove_mount(self, name: str) -> bool:
        """
        ลบการตั้งค่า mount point
        
        Parameters:
            name: ชื่อของ mount point
        
        Returns:
            bool: สำเร็จหรือไม่
        """
        if name not in self.mounts:
            print(f"[!] ไม่พบ mount '{name}'")
            return False
        
        # Unmount ก่อนถ้ายัง mount อยู่
        if self.is_mounted(name):
            self.unmount(name)
        
        del self.mounts[name]
        self._save_config()
        print(f"[+] ลบ mount '{name}' สำเร็จ")
        return True
    
    def mount(self, name: str) -> bool:
        """
        Mount filesystem
        
        Parameters:
            name: ชื่อของ mount point
        
        Returns:
            bool: สำเร็จหรือไม่
        """
        if name not in self.mounts:
            print(f"[!] ไม่พบ mount '{name}'")
            return False
        
        mount = self.mounts[name]
        
        # ตรวจสอบว่า mount อยู่แล้วหรือไม่
        if self.is_mounted(name):
            print(f"[!] '{name}' ถูก mount อยู่แล้ว")
            return True
        
        # สร้าง mount point directory ถ้ายังไม่มี
        local_path = Path(mount.local_path).expanduser()
        local_path.mkdir(parents=True, exist_ok=True)
        
        # สร้างคำสั่ง sshfs
        cmd = ["sshfs"]
        
        # เพิ่ม port ถ้าไม่ใช่ 22
        if mount.port != 22:
            cmd.extend(["-p", str(mount.port)])
        
        # สร้าง remote path
        remote = f"{mount.user}@{mount.host}:{mount.remote_path}"
        cmd.append(remote)
        cmd.append(str(local_path))
        
        # เพิ่ม options
        default_options = [
            "reconnect",
            "ServerAliveInterval=15",
            "ServerAliveCountMax=3"
        ]
        
        if mount.identity_file:
            identity_path = os.path.expanduser(mount.identity_file)
            default_options.append(f"IdentityFile={identity_path}")
        
        all_options = default_options + (mount.options or [])
        
        for opt in all_options:
            cmd.extend(["-o", opt])
        
        # รันคำสั่ง
        print(f"[*] กำลัง mount '{name}'...")
        print(f"    Command: {' '.join(cmd)}")
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            
            if result.returncode == 0:
                print(f"[+] Mount '{name}' สำเร็จ")
                print(f"    Remote: {remote}")
                print(f"    Local: {local_path}")
                return True
            else:
                print(f"[!] Mount ไม่สำเร็จ: {result.stderr}")
                return False
        except subprocess.TimeoutExpired:
            print(f"[!] Timeout: ไม่สามารถเชื่อมต่อได้")
            return False
        except Exception as e:
            print(f"[!] Error: {e}")
            return False
    
    def unmount(self, name: str) -> bool:
        """
        Unmount filesystem
        
        Parameters:
            name: ชื่อของ mount point
        
        Returns:
            bool: สำเร็จหรือไม่
        """
        if name not in self.mounts:
            print(f"[!] ไม่พบ mount '{name}'")
            return False
        
        mount = self.mounts[name]
        local_path = Path(mount.local_path).expanduser()
        
        if not self.is_mounted(name):
            print(f"[!] '{name}' ไม่ได้ถูก mount อยู่")
            return True
        
        print(f"[*] กำลัง unmount '{name}'...")
        
        try:
            # ลอง fusermount ก่อน
            result = subprocess.run(
                ["fusermount", "-u", str(local_path)],
                capture_output=True, text=True
            )
            
            if result.returncode == 0:
                print(f"[+] Unmount '{name}' สำเร็จ")
                return True
            
            # ถ้าไม่สำเร็จ ลอง lazy unmount
            print(f"[!] fusermount ไม่สำเร็จ กำลังลอง lazy unmount...")
            result = subprocess.run(
                ["fusermount", "-uz", str(local_path)],
                capture_output=True, text=True
            )
            
            if result.returncode == 0:
                print(f"[+] Lazy unmount '{name}' สำเร็จ")
                return True
            else:
                print(f"[!] Unmount ไม่สำเร็จ: {result.stderr}")
                return False
                
        except Exception as e:
            print(f"[!] Error: {e}")
            return False
    
    def is_mounted(self, name: str) -> bool:
        """
        ตรวจสอบว่า mount point ถูก mount อยู่หรือไม่
        
        Parameters:
            name: ชื่อของ mount point
        
        Returns:
            bool: mounted หรือไม่
        """
        if name not in self.mounts:
            return False
        
        mount = self.mounts[name]
        local_path = str(Path(mount.local_path).expanduser())
        
        # ตรวจสอบจาก /proc/mounts
        try:
            with open('/proc/mounts', 'r') as f:
                for line in f:
                    parts = line.split()
                    if len(parts) >= 2:
                        mount_point = parts[1]
                        mount_type = parts[2] if len(parts) > 2 else ""
                        if mount_point == local_path and "fuse" in mount_type:
                            return True
        except:
            pass
        
        return False
    
    def status(self) -> List[Dict]:
        """
        แสดงสถานะของทุก mount points
        
        Returns:
            List[Dict]: รายการสถานะ
        """
        status_list = []
        
        for name, mount in self.mounts.items():
            mounted = self.is_mounted(name)
            status_list.append({
                'name': name,
                'user': mount.user,
                'host': mount.host,
                'remote_path': mount.remote_path,
                'local_path': mount.local_path,
                'mounted': mounted,
                'status': '🟢 Mounted' if mounted else '🔴 Not Mounted'
            })
        
        return status_list
    
    def mount_all(self):
        """Mount ทุก mount points"""
        print("[*] กำลัง mount ทุก mount points...")
        for name in self.mounts:
            self.mount(name)
    
    def unmount_all(self):
        """Unmount ทุก mount points"""
        print("[*] กำลัง unmount ทุก mount points...")
        for name in self.mounts:
            self.unmount(name)


def print_status_table(status_list: List[Dict]):
    """
    แสดงตารางสถานะ
    
    Parameters:
        status_list: รายการสถานะจาก SSHFSManager.status()
    """
    if not status_list:
        print("ไม่มี mount points ที่บันทึกไว้")
        return
    
    # หา column widths
    name_width = max(len(s['name']) for s in status_list)
    host_width = max(len(f"{s['user']}@{s['host']}") for s in status_list)
    remote_width = max(len(s['remote_path']) for s in status_list)
    local_width = max(len(s['local_path']) for s in status_list)
    
    # Header
    print("\n" + "=" * 80)
    print(f"{'Name':<{name_width}} | {'Host':<{host_width}} | {'Remote':<{remote_width}} | Status")
    print("-" * 80)
    
    # Rows
    for s in status_list:
        host = f"{s['user']}@{s['host']}"
        print(f"{s['name']:<{name_width}} | {host:<{host_width}} | {s['remote_path']:<{remote_width}} | {s['status']}")
    
    print("=" * 80 + "\n")


# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    print("=" * 60)
    print("SSHFS Manager - ตัวอย่างการใช้งาน")
    print("=" * 60)
    
    # สร้าง manager
    manager = SSHFSManager()
    
    # ตัวอย่างการเพิ่ม mount point
    """
    # เพิ่ม mount point ใหม่
    web_server = SSHFSMount(
        name="webserver",
        user="admin",
        host="192.168.1.100",
        remote_path="/var/www/html",
        local_path="~/mnt/webserver",
        port=22,
        identity_file="~/.ssh/id_ed25519",
        options=["cache=yes", "kernel_cache"]
    )
    manager.add_mount(web_server)
    
    # เพิ่ม mount point อีกตัว
    backup_server = SSHFSMount(
        name="backup",
        user="backup",
        host="192.168.1.200",
        remote_path="/backup/daily",
        local_path="~/mnt/backup",
        identity_file="~/.ssh/id_ed25519"
    )
    manager.add_mount(backup_server)
    
    # Mount
    manager.mount("webserver")
    
    # ตรวจสอบสถานะ
    status = manager.status()
    print_status_table(status)
    
    # Unmount
    manager.unmount("webserver")
    """
    
    # แสดงสถานะปัจจุบัน
    status = manager.status()
    print_status_table(status)
    
    print("\nดูตัวอย่างโค้ดในไฟล์สำหรับการใช้งานจริง")

3.3.8 การเพิ่มประสิทธิภาพ SSHFS

ปัจจัยที่ส่งผลต่อประสิทธิภาพ:

ปัจจัย ผลกระทบ แนวทางแก้ไข
Latency ช้าลงมากสำหรับ small files ใช้ caching, batch operations
Encryption overhead CPU usage สูงขึ้น เลือก cipher ที่เร็ว
No local caching (default) อ่านซ้ำไฟล์เดิมช้า เปิด cache options
Small read-ahead Sequential read ช้า เพิ่ม read-ahead buffer

Options สำหรับเพิ่มประสิทธิภาพ:

# ========================================
# Performance Optimization Options
# ========================================

# การตั้งค่าสำหรับประสิทธิภาพสูงสุด
sshfs user@server:/path ~/mnt/remote \
    -o Ciphers=aes128-gcm@openssh.com \
    -o Compression=no \
    -o cache=yes \
    -o kernel_cache \
    -o auto_cache \
    -o large_read \
    -o big_writes \
    -o max_conns=10 \
    -o no_readahead

# คำอธิบาย:
# Ciphers=aes128-gcm  : cipher ที่เร็ว (มี hardware acceleration)
# Compression=no      : ไม่บีบอัด (ดีสำหรับ LAN เร็ว)
# cache=yes          : เปิด caching
# kernel_cache       : ใช้ kernel page cache
# auto_cache         : invalidate cache อัตโนมัติเมื่อไฟล์เปลี่ยน
# large_read         : ใช้ read buffer ขนาดใหญ่
# big_writes         : อนุญาต write ขนาดใหญ่กว่า 4KB
# max_conns          : จำนวน SSH connections (parallel transfers)

# ========================================
# การตั้งค่าสำหรับ WAN / High Latency
# ========================================

# สำหรับการเชื่อมต่อผ่าน Internet
sshfs user@server:/path ~/mnt/remote \
    -o Compression=yes \
    -o Ciphers=chacha20-poly1305@openssh.com \
    -o cache=yes \
    -o kernel_cache \
    -o auto_cache \
    -o reconnect \
    -o ServerAliveInterval=15 \
    -o ServerAliveCountMax=3 \
    -o TCPKeepAlive=yes

# คำอธิบาย:
# Compression=yes    : บีบอัดข้อมูล (ดีสำหรับ slow connection)
# chacha20-poly1305  : cipher ที่เร็วบน CPU ที่ไม่มี AES-NI
# reconnect          : เชื่อมต่อใหม่อัตโนมัติ
# ServerAliveInterval: ส่ง keepalive ทุก 15 วินาที

3.3.9 การคำนวณ Performance ของ SSHFS

T transfer = S B effective + N × RTT

คำอธิบายตัวแปร:

"""
โปรแกรมคำนวณและทดสอบประสิทธิภาพ SSHFS

โปรแกรมนี้ช่วยประมาณและทดสอบประสิทธิภาพของ SSHFS
ในสภาพแวดล้อมต่างๆ
"""

import time
import os
import tempfile
from typing import Dict, Tuple
import subprocess

def calculate_transfer_time(file_size_mb: float, 
                           bandwidth_mbps: float,
                           latency_ms: float,
                           overhead_percent: float = 15) -> Dict:
    """
    คำนวณเวลาในการถ่ายโอนไฟล์ผ่าน SSHFS
    
    Parameters:
        file_size_mb: ขนาดไฟล์เป็น MB
        bandwidth_mbps: แบนด์วิดท์เป็น Mbps
        latency_ms: latency เป็น milliseconds (one-way)
        overhead_percent: overhead จาก encryption และ protocol
    
    Returns:
        Dict: ผลการคำนวณ
    """
    # แปลงหน่วย
    file_size_bytes = file_size_mb * 1024 * 1024
    bandwidth_bps = (bandwidth_mbps * 1_000_000) / 8  # bytes per second
    rtt_seconds = (latency_ms * 2) / 1000  # round-trip time
    
    # คำนวณ effective bandwidth
    effective_bandwidth = bandwidth_bps * (1 - overhead_percent/100)
    
    # ประมาณจำนวน round-trips (SFTP ต้องการ acknowledgment)
    # สมมติว่าใช้ block size 64KB
    block_size = 64 * 1024
    num_blocks = file_size_bytes / block_size
    
    # เวลาในการถ่ายโอน
    transfer_time = file_size_bytes / effective_bandwidth
    
    # เวลาจาก latency (ประมาณ 1 RTT ต่อ 10 blocks)
    latency_time = (num_blocks / 10) * rtt_seconds
    
    total_time = transfer_time + latency_time
    
    # คำนวณ throughput จริง
    actual_throughput_mbps = (file_size_mb * 8) / total_time
    
    return {
        'file_size_mb': file_size_mb,
        'bandwidth_mbps': bandwidth_mbps,
        'latency_ms': latency_ms,
        'overhead_percent': overhead_percent,
        'effective_bandwidth_mbps': (effective_bandwidth * 8) / 1_000_000,
        'transfer_time_sec': round(transfer_time, 2),
        'latency_overhead_sec': round(latency_time, 2),
        'total_time_sec': round(total_time, 2),
        'actual_throughput_mbps': round(actual_throughput_mbps, 2),
        'efficiency_percent': round((actual_throughput_mbps / bandwidth_mbps) * 100, 1)
    }


def benchmark_sshfs(mount_path: str, file_sizes_mb: list = [1, 10, 100]) -> list:
    """
    ทดสอบประสิทธิภาพ SSHFS ด้วยการเขียนและอ่านไฟล์จริง
    
    Parameters:
        mount_path: path ของ SSHFS mount point
        file_sizes_mb: ขนาดไฟล์ที่จะทดสอบ
    
    Returns:
        list: ผลการทดสอบ
    """
    results = []
    
    if not os.path.ismount(mount_path):
        print(f"[!] {mount_path} ไม่ใช่ mount point")
        return results
    
    for size_mb in file_sizes_mb:
        print(f"\n[*] ทดสอบไฟล์ขนาด {size_mb} MB...")
        
        # สร้างข้อมูลทดสอบ
        data = os.urandom(size_mb * 1024 * 1024)
        test_file = os.path.join(mount_path, f"benchmark_{size_mb}mb.tmp")
        
        # ทดสอบเขียน
        start_time = time.time()
        with open(test_file, 'wb') as f:
            f.write(data)
            f.flush()
            os.fsync(f.fileno())
        write_time = time.time() - start_time
        write_speed = size_mb / write_time
        
        # ทดสอบอ่าน
        start_time = time.time()
        with open(test_file, 'rb') as f:
            read_data = f.read()
        read_time = time.time() - start_time
        read_speed = size_mb / read_time
        
        # ลบไฟล์ทดสอบ
        os.remove(test_file)
        
        result = {
            'size_mb': size_mb,
            'write_time_sec': round(write_time, 2),
            'write_speed_mbps': round(write_speed * 8, 2),
            'read_time_sec': round(read_time, 2),
            'read_speed_mbps': round(read_speed * 8, 2)
        }
        results.append(result)
        
        print(f"    Write: {result['write_speed_mbps']} Mbps ({write_time:.2f}s)")
        print(f"    Read:  {result['read_speed_mbps']} Mbps ({read_time:.2f}s)")
    
    return results


# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    print("=" * 60)
    print("SSHFS Performance Calculator")
    print("=" * 60)
    
    # ทดสอบการคำนวณสำหรับสถานการณ์ต่างๆ
    scenarios = [
        {"name": "LAN (Gigabit)", "bw": 1000, "latency": 0.5},
        {"name": "WAN (100Mbps)", "bw": 100, "latency": 20},
        {"name": "VPN (50Mbps)", "bw": 50, "latency": 50},
        {"name": "International (25Mbps)", "bw": 25, "latency": 150},
    ]
    
    file_size = 100  # MB
    
    print(f"\nการถ่ายโอนไฟล์ขนาด {file_size} MB:")
    print("-" * 60)
    
    for scenario in scenarios:
        result = calculate_transfer_time(
            file_size_mb=file_size,
            bandwidth_mbps=scenario['bw'],
            latency_ms=scenario['latency']
        )
        
        print(f"\n{scenario['name']}:")
        print(f"  Bandwidth: {scenario['bw']} Mbps, Latency: {scenario['latency']} ms")
        print(f"  เวลาถ่ายโอน: {result['total_time_sec']} วินาที")
        print(f"  Throughput จริง: {result['actual_throughput_mbps']} Mbps")
        print(f"  ประสิทธิภาพ: {result['efficiency_percent']}%")
    
    # Benchmark จริง (uncomment เพื่อใช้งาน)
    """
    print("\n" + "=" * 60)
    print("Benchmark ด้วยการถ่ายโอนจริง")
    print("=" * 60)
    
    mount_point = os.path.expanduser("~/mnt/remote")
    if os.path.ismount(mount_point):
        results = benchmark_sshfs(mount_point, [1, 10, 50])
    else:
        print(f"[!] กรุณา mount SSHFS ที่ {mount_point} ก่อน")
    """

3.3.10 การแก้ไขปัญหาที่พบบ่อย

ปัญหา สาเหตุ วิธีแก้ไข
"Transport endpoint is not connected" การเชื่อมต่อขาดหาย fusermount -uz แล้ว mount ใหม่
"Permission denied" SSH key ไม่ถูกต้อง ตรวจสอบ key และ authorized_keys
"Host key verification failed" Host key เปลี่ยน ลบ entry ใน known_hosts
Mount ช้ามาก Latency สูง หรือ no caching เพิ่ม cache options
"fuse: device not found" FUSE module ไม่ถูก load sudo modprobe fuse
Cannot access as another user ไม่ได้ตั้งค่า allow_other แก้ไข /etc/fuse.conf
# ========================================
# การแก้ไขปัญหาที่พบบ่อย
# ========================================

# ปัญหา: Transport endpoint is not connected
fusermount -uz ~/mnt/remote
# แล้ว mount ใหม่

# ปัญหา: Host key verification failed
ssh-keygen -R hostname
# แล้วเชื่อมต่อใหม่เพื่อยอมรับ key ใหม่

# ปัญหา: FUSE device not found
sudo modprobe fuse
# เพิ่มใน /etc/modules เพื่อ load อัตโนมัติ
echo "fuse" | sudo tee -a /etc/modules

# ปัญหา: Permission denied บน mount point
# ตรวจสอบ permissions
ls -la ~/.ssh/
# ควรเป็น 700 สำหรับ .ssh และ 600 สำหรับ keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519

# ปัญหา: ช้ามากเมื่อ list directory ใหญ่
# เพิ่ม options
sshfs -o cache=yes,cache_timeout=115200 \
      -o attr_timeout=115200 \
      user@server:/path ~/mnt/remote

# Debug mode
sshfs -o debug,sshfs_debug user@server:/path ~/mnt/remote

3.3.11 เปรียบเทียบ SSHFS กับทางเลือกอื่น

คุณสมบัติ SSHFS NFS SMB/CIFS SyncThing
การเข้ารหัส ✅ (SSH) ❌ (ต้องใช้ Kerberos) ✅ (SMB3)
ติดตั้งง่าย ปานกลาง ปานกลาง
ไม่ต้อง root ✅ (FUSE)
ทำงานผ่าน Internet ❌ (ไม่แนะนำ) ⚠️ (VPN)
ประสิทธิภาพ ปานกลาง สูงมาก สูง - (sync)
Real-time access
Offline access

4.1 คำสั่งพื้นฐานที่ควรรู้

4.1.1 SSH Commands

# ========================================
# การเชื่อมต่อพื้นฐาน
# ========================================

# เชื่อมต่อ SSH ปกติ
ssh user@hostname

# ระบุ port
ssh -p 2222 user@hostname

# ใช้ private key ที่ระบุ
ssh -i ~/.ssh/id_ed25519 user@hostname

# เปิด verbose mode เพื่อ debug
ssh -v user@hostname    # -v, -vv, -vvv

# ========================================
# การจัดการ SSH Keys
# ========================================

# สร้าง ED25519 key (แนะนำ)
ssh-keygen -t ed25519 -C "comment"

# สร้าง RSA 4096-bit key
ssh-keygen -t rsa -b 4096 -C "comment"

# ดู fingerprint ของ key
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

# คัดลอก public key ไป remote server
ssh-copy-id user@hostname

# ========================================
# SSH Agent
# ========================================

# เริ่ม ssh-agent
eval $(ssh-agent -s)

# เพิ่ม key เข้า agent
ssh-add ~/.ssh/id_ed25519

# แสดง keys ใน agent
ssh-add -l

# ลบ keys ออกจาก agent
ssh-add -D

# ========================================
# Port Forwarding
# ========================================

# Local port forwarding
# เข้าถึง remote:3306 ผ่าน localhost:13306
ssh -L 13306:localhost:3306 user@hostname

# Remote port forwarding
# เปิด port 8080 บน remote ให้เข้าถึง localhost:80
ssh -R 8080:localhost:80 user@hostname

# Dynamic port forwarding (SOCKS proxy)
ssh -D 1080 user@hostname

# ========================================
# การใช้งานขั้นสูง
# ========================================

# ProxyJump (เข้าผ่าน bastion host)
ssh -J jump@bastion user@internal

# Agent forwarding (ใช้ key บน remote)
ssh -A user@hostname

# รันคำสั่งบน remote
ssh user@hostname "ls -la /var/log"

# รันหลายคำสั่ง
ssh user@hostname "cd /var/log && tail -n 100 syslog"

4.1.2 SCP/SFTP Commands

# ========================================
# SCP Commands
# ========================================

# อัปโหลดไฟล์
scp local.txt user@host:/path/

# ดาวน์โหลดไฟล์
scp user@host:/path/remote.txt ./

# คัดลอก directory (recursive)
scp -r local_dir/ user@host:/path/

# ใช้ compression
scp -C large_file.tar user@host:/path/

# จำกัด bandwidth (KB/s)
scp -l 1024 file.txt user@host:/path/

# ========================================
# SFTP Commands
# ========================================

# เชื่อมต่อ SFTP
sftp user@hostname

# คำสั่งใน SFTP session
sftp> pwd           # แสดง remote directory
sftp> lpwd          # แสดง local directory
sftp> ls            # list remote
sftp> lls           # list local
sftp> cd /path      # change remote dir
sftp> lcd /path     # change local dir
sftp> get file.txt  # download
sftp> put file.txt  # upload
sftp> mkdir dir     # create directory
sftp> rm file.txt   # delete file
sftp> bye           # disconnect

# Batch mode
sftp -b commands.txt user@hostname

4.1.3 SSHFS Commands

# ========================================
# SSHFS Commands - Quick Reference
# ========================================

# ติดตั้ง sshfs
sudo apt install sshfs

# Mount remote filesystem
sshfs user@hostname:/remote/path /local/mountpoint

# Mount พร้อม options ที่แนะนำ
sshfs -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 \
      -o cache=yes,kernel_cache,auto_cache \
      user@hostname:/remote/path /local/mountpoint

# ใช้ identity file
sshfs -o IdentityFile=~/.ssh/id_ed25519 \
      user@hostname:/path /mountpoint

# Mount ผ่าน port อื่น
sshfs -p 2222 user@hostname:/path /mountpoint

# Unmount
fusermount -u /local/mountpoint

# Force unmount
fusermount -uz /local/mountpoint

# ตรวจสอบ mounts
mount | grep sshfs

4.2 ความปลอดภัยและแนวทางปฏิบัติที่ดี (Security Best Practices)

4.2.1 การป้องกันการโจมตี Brute Force

flowchart TB
    subgraph DEFENSE["การป้องกัน Brute Force"]
        D1["Fail2ban
บล็อก IP ที่พยายามหลายครั้ง"] D2["Rate Limiting
MaxAuthTries = 3"] D3["Key-only Auth
ปิด Password Auth"] D4["Port Knocking
ซ่อน Port"] D5["2FA/MFA
การยืนยันตัวตนหลายขั้น"] end A["Attacker
ผู้โจมตี"] -->|"Brute Force"| B["SSH Server"] B --> D1 B --> D2 B --> D3 B --> D4 B --> D5 style DEFENSE fill:#3c3836,stroke:#b8bb26,color:#ebdbb2 style A fill:#504945,stroke:#fb4934,color:#ebdbb2 style B fill:#504945,stroke:#83a598,color:#ebdbb2 style D1 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style D2 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style D3 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style D4 fill:#504945,stroke:#ebdbb2,color:#ebdbb2 style D5 fill:#504945,stroke:#ebdbb2,color:#ebdbb2

4.2.2 Checklist ความปลอดภัย

สำหรับ SSH Server:

สำหรับ SSH Client:

สำหรับ File Transfer:


5. สรุป

5.1 สรุปเนื้อหาหลัก

ในบทนี้ได้ศึกษาโปรโตคอลระดับ Application ที่เกี่ยวข้องกับการถ่ายโอนไฟล์และการเข้าถึงระยะไกล โดยมีประเด็นสำคัญดังนี้:

โปรโตคอลการถ่ายโอนไฟล์:

การเข้าถึงระยะไกล:

5.2 ตารางเปรียบเทียบโปรโตคอล

โปรโตคอล Port เข้ารหัส Authentication การใช้งานที่แนะนำ
FTP 21/20 ไม่ Username/Password ไม่แนะนำ
TFTP 69 ไม่ ไม่มี Network boot เท่านั้น
SFTP 22 ใช่ SSH Keys/Password ถ่ายโอนไฟล์ทั่วไป
FTPS 990/21 ใช่ Certificate/Password Legacy systems
SSH 22 ใช่ Keys/Password/MFA Remote access
SCP 22 ใช่ SSH Keys/Password Quick file copy
SSHFS 22 ใช่ SSH Keys/Password Mount remote filesystem

5.3 แนวทางการเลือกใช้

flowchart TB
    START["ต้องการถ่ายโอนไฟล์/เข้าถึงระยะไกล"] --> Q1{"ต้องการ
ความปลอดภัย?"} Q1 -->|"ใช่"| Q2{"ลักษณะการใช้งาน?"} Q1 -->|"ไม่ (เครือข่ายปิด)"| Q3{"ใช้สำหรับ
Network Boot?"} Q2 -->|"จัดการไฟล์ Interactive"| SFTP["ใช้ SFTP"] Q2 -->|"คัดลอกไฟล์ครั้งเดียว"| SCP["ใช้ SCP"] Q2 -->|"เข้าถึงไฟล์ต่อเนื่อง
เหมือน local"| SSHFS["ใช้ SSHFS"] Q2 -->|"Remote shell access"| SSH["ใช้ SSH"] Q3 -->|"ใช่"| TFTP["ใช้ TFTP
(จำกัดเครือข่าย)"] Q3 -->|"ไม่"| LEGACY{"ต้องรองรับ
Legacy Systems?"} LEGACY -->|"ใช่"| FTPS["ใช้ FTPS"] LEGACY -->|"ไม่"| SFTP style START fill:#282828,stroke:#ebdbb2,color:#ebdbb2 style Q1 fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style Q2 fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style Q3 fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style LEGACY fill:#3c3836,stroke:#fabd2f,color:#ebdbb2 style SFTP fill:#504945,stroke:#b8bb26,color:#ebdbb2 style SCP fill:#504945,stroke:#83a598,color:#ebdbb2 style SSHFS fill:#504945,stroke:#d3869b,color:#ebdbb2 style SSH fill:#504945,stroke:#8ec07c,color:#ebdbb2 style TFTP fill:#504945,stroke:#fe8019,color:#ebdbb2 style FTPS fill:#504945,stroke:#fb4934,color:#ebdbb2

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

6.1 RFC และมาตรฐาน

  1. RFC 959 - File Transfer Protocol (FTP)

  2. RFC 1350 - The TFTP Protocol (Revision 2)

  3. RFC 4217 - Securing FTP with TLS

  4. RFC 4251 - The Secure Shell (SSH) Protocol Architecture

  5. RFC 4252 - The Secure Shell (SSH) Authentication Protocol

  6. RFC 4253 - The Secure Shell (SSH) Transport Layer Protocol

  7. RFC 4254 - The Secure Shell (SSH) Connection Protocol

  8. draft-ietf-secsh-filexfer - SSH File Transfer Protocol

6.2 เอกสารและคู่มือ

  1. OpenSSH Manual Pages

  2. Mozilla SSH Guidelines

  3. NIST Special Publication 800-52 - Guidelines for TLS Implementations

  4. CIS Benchmark for OpenSSH

6.3 เครื่องมือและซอฟต์แวร์

  1. OpenSSH - https://www.openssh.com/
  2. Paramiko (Python SSH library) - https://www.paramiko.org/
  3. Fail2ban - https://www.fail2ban.org/
  4. Wireshark - https://www.wireshark.org/