Networking Beginner Guide

Networking for beginnersเครือข่ายคอมพิวเตอร์สำหรับผู้เริ่มต้น

Networking Beginner Guide

How data gets from one machine to another, tried on real onesข้อมูลเดินทางจากเครื่องหนึ่งไปอีกเครื่องได้อย่างไร ลองกับเครื่องจริง

Every website, API call and deployment depends on the network, and most "the server is down" problems are really network problems. This guide builds a small network of four Linux machines (a client, a router, a web server and a DNS server) and follows the data through it: addresses, subnets, routes, names, ports, connections, encryption, address translation and firewalls. Then it puts it all together into a troubleshooting routine. ทุกเว็บไซต์ ทุกการเรียก API และทุกการ deploy ล้วนพึ่งพาเครือข่าย และปัญหา "เซิร์ฟเวอร์ล่ม" ส่วนใหญ่ที่จริงคือปัญหาเครือข่าย คู่มือนี้สร้างเครือข่ายเล็กๆ ของเครื่อง Linux สี่เครื่อง (client, router, เว็บเซิร์ฟเวอร์ และ DNS เซิร์ฟเวอร์) แล้วติดตามข้อมูลที่เดินทางผ่านมัน ตั้งแต่ address, ซับเน็ต, เส้นทาง, ชื่อ, พอร์ต, การเชื่อมต่อ, การเข้ารหัส, การแปลง address ไปจนถึงไฟร์วอลล์ แล้วรวมทุกอย่างเป็นขั้นตอนการแก้ปัญหา

Every command on this page was run for real on the lab machines, and the output under it is what they printed. The subnet maths in Lessons 2 and 3 runs in your browser: edit it and press Run. The outputs shown first were recorded with Node.js 22, and the same code was run in a real browser before publishing, with identical output. ทุกคำสั่งในหน้านี้ถูกรันจริงบนเครื่องในแล็บ และผลลัพธ์ด้านล่างคือสิ่งที่เครื่องพิมพ์ออกมา ส่วนการคำนวณซับเน็ตในบทที่ 2 และ 3 รันในเบราว์เซอร์ของคุณ แก้โค้ดแล้วกด Run ได้ ผลลัพธ์ที่แสดงไว้ก่อนบันทึกจาก Node.js 22 และก่อนเผยแพร่ โค้ดชุดเดียวกันถูกรันในเบราว์เซอร์จริงแล้วได้ผลตรงกัน

Shortcut: pressคีย์ลัด: กด Ctrl+Enter (⌘+Enter Mac) inside a code box to run it.ในกล่องโค้ดเพื่อรันทันที

  • Labแล็บ Debian 13
  • Machinesเครื่อง 4
  • Networksเครือข่าย 2
  • Labs run inแล็บรันใน your browserเบราว์เซอร์ของคุณ

Lesson 1 · Setupบทที่ 1 · เตรียมแล็บ

The labห้องแล็บ

A network is machines (hosts) connected so they can send each other data. Data travels in small pieces called packets; each packet carries a source address, a destination address and part of the message. When the destination is on another network, a router passes the packet on. That's the whole idea; the rest of this guide is the details. เครือข่าย คือเครื่องต่างๆ (host) ที่เชื่อมต่อกันเพื่อส่งข้อมูลถึงกันได้ ข้อมูลเดินทางเป็นชิ้นเล็กๆ ที่เรียกว่า แพ็กเก็ต แต่ละแพ็กเก็ตมี address ต้นทาง address ปลายทาง และส่วนหนึ่งของข้อความ เมื่อปลายทางอยู่ในอีกเครือข่าย router จะส่งต่อแพ็กเก็ตให้ นี่คือแนวคิดทั้งหมด ที่เหลือในคู่มือนี้คือรายละเอียด

To try this safely and get the same results every time, I built a lab with Docker: four small Debian Linux machines on two private networks. client is like your laptop on a home network; web and dns are servers in an office network; router connects the two. The lab is cut off from the internet. เพื่อให้ทดลองได้อย่างปลอดภัยและได้ผลเหมือนเดิมทุกครั้ง ผมสร้างแล็บด้วย Docker ประกอบด้วยเครื่อง Debian Linux ขนาดเล็กสี่เครื่องบนเครือข่ายส่วนตัวสองวง client เหมือนโน้ตบุ๊กของเราในเครือข่ายบ้าน web และ dns คือเซิร์ฟเวอร์ในเครือข่ายสำนักงาน และ router เชื่อมทั้งสองวงเข้าด้วยกัน แล็บนี้ไม่ได้ต่อกับอินเทอร์เน็ต

The lab: client 10.10.1.10 on the home network 10.10.1.0/24; the router has 10.10.1.1 on home and 10.10.2.1 on office; web 10.10.2.20 and dns 10.10.2.53 are on the office network 10.10.2.0/24. home · 10.10.1.0/24 office · 10.10.2.0/24 client 10.10.1.10 router eth0 10.10.1.1 eth1 10.10.2.1 web 10.10.2.20 dns 10.10.2.53
Two networks joined by a router. /24 is explained in Lesson 3.เครือข่ายสองวงที่เชื่อมด้วย router ส่วน /24 จะอธิบายในบทที่ 3
Build the same lab yourselfสร้างแล็บแบบเดียวกันด้วยตัวเอง

You need Docker (Docker Desktop on macOS and Windows). Put these files in one folder, run docker compose build, make the certificates with the command in the first comment of make-certs.sh, then run docker compose up -d. compose.yaml is where the networks, addresses and routes are set.ต้องมี Docker (Docker Desktop บน macOS และ Windows) วางไฟล์เหล่านี้ไว้ในโฟลเดอร์เดียวกัน รัน docker compose build สร้าง certificate ด้วยคำสั่งในคอมเมนต์แรกของ make-certs.sh แล้วรัน docker compose up -d ไฟล์ compose.yaml คือที่กำหนดเครือข่าย address และเส้นทาง

Your computer, in the lab folderคอมพิวเตอร์ของเรา ในโฟลเดอร์แล็บDebian 13 · Docker lab
$ docker compose up -d
 Network netlab_office Creating 
 Network netlab_office Created 
 Network netlab_home Creating 
 Network netlab_home Created 
 Container netlab-web-1 Creating 
 Container netlab-dns-1 Creating 
 Container netlab-client-1 Creating 
 Container netlab-router-1 Creating 
 Container netlab-dns-1 Created 
 Container netlab-router-1 Created 
 Container netlab-client-1 Created 
 Container netlab-web-1 Created 
 Container netlab-web-1 Starting 
 Container netlab-client-1 Starting 
 Container netlab-dns-1 Starting 
 Container netlab-router-1 Starting 
 Container netlab-dns-1 Started 
 Container netlab-client-1 Started 
 Container netlab-web-1 Started 
 Container netlab-router-1 Started 

$ docker compose ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE   STATUS
client    Up 1 second
dns       Up 1 second
router    Up 1 second
web       Up 1 second

$ docker compose exec client bash

The last command opens a shell on the client. From now on, each terminal's title says which machine the commands were typed on. You don't need Docker to follow along: the commands are the same on any Linux server.คำสั่งสุดท้ายเปิด shell บนเครื่อง client จากนี้ไป ชื่อของเทอร์มินัลแต่ละอันจะบอกว่าคำสั่งถูกพิมพ์บนเครื่องไหน ไม่ต้องมี Docker ก็อ่านตามได้ เพราะคำสั่งเหมือนกันบนเซิร์ฟเวอร์ Linux ทุกเครื่อง

clientclientDebian 13 · Docker lab
$ hostname; ip -br -4 addr show scope global
client
eth0@if474       UP             10.10.1.10/24 

eth0 is the client's network interface, its "network card". It is up and has the address 10.10.1.10.eth0 คือ network interface ของ client หรือ "การ์ดเครือข่าย" ของมัน ซึ่งกำลังทำงาน (UP) และมี address 10.10.1.10

Lesson 2 · Addressesบทที่ 2 · Address

IP addressesIP address

An IPv4 address is 32 bits, written as four numbers from 0 to 255 separated by dots. Each number is one byte, called an octet. To the computer, 10.10.1.10 is just one 32-bit number. Run the lab to see the bits. IPv4 address มีขนาด 32 บิต เขียนเป็นตัวเลขสี่ตัวตั้งแต่ 0 ถึง 255 คั่นด้วยจุด ตัวเลขแต่ละตัวคือหนึ่งไบต์ เรียกว่า octet สำหรับคอมพิวเตอร์ 10.10.1.10 เป็นเพียงตัวเลข 32 บิตตัวเดียว ลองรันแล็บเพื่อดูบิต

Some ranges are private: they're for networks inside homes, offices and clouds, and the internet doesn't route them. Many networks can reuse the same private addresses. To reach the internet, a router translates them to a public address (Lesson 10). บางช่วงเป็น address ส่วนตัว (private) ใช้สำหรับเครือข่ายในบ้าน สำนักงาน และคลาวด์ และอินเทอร์เน็ตจะไม่ส่งต่อ address เหล่านี้ หลายเครือข่ายจึงใช้ address ส่วนตัวชุดเดียวกันซ้ำได้ เมื่อต้องการออกอินเทอร์เน็ต router จะแปลงเป็น address สาธารณะ (บทที่ 10)

Rangeช่วงUsed forใช้สำหรับ
10.0.0.0/8private: big company and cloud networks (and this lab)ส่วนตัว: เครือข่ายบริษัทใหญ่และคลาวด์ (รวมถึงแล็บนี้)
172.16.0.0/12private: Docker uses 172.17.0.0/16 by defaultส่วนตัว: Docker ใช้ 172.17.0.0/16 เป็นค่าเริ่มต้น
192.168.0.0/16private: most home routersส่วนตัว: router ตามบ้านส่วนใหญ่
127.0.0.0/8loopback: 127.0.0.1 (localhost) is always the machine itselfloopback: 127.0.0.1 (localhost) คือตัวเครื่องเองเสมอ
169.254.0.0/16link-local: a machine that got no address picks one here; also cloud metadata (169.254.169.254)link-local: เครื่องที่ไม่ได้รับ address จะเลือกเองจากช่วงนี้ และยังใช้กับ metadata ของคลาวด์ (169.254.169.254)

A machine has one address per interface. The router is on both networks, so it has two: เครื่องหนึ่งมีหนึ่ง address ต่อหนึ่ง interface router อยู่ในทั้งสองเครือข่าย จึงมีสอง address

routerrouterDebian 13 · Docker lab
$ ip -br -4 addr show scope global
eth0@if482       UP             10.10.1.1/24 
eth1@if486       UP             10.10.2.1/24 
webwebDebian 13 · Docker lab
$ ip -br -4 addr show scope global
eth0@if485       UP             10.10.2.20/24 
IPv6

IPv4 has only about 4.3 billion addresses, which ran out years ago. IPv6 addresses are 128 bits, written in hexadecimal like 2001:db8::1. Most ideas in this guide (subnets, routes, DNS, ports) work the same way with IPv6; the lab uses IPv4 to keep the numbers short.IPv4 มี address เพียงประมาณ 4.3 พันล้าน ซึ่งหมดไปหลายปีแล้ว IPv6 มี address ขนาด 128 บิต เขียนเป็นเลขฐานสิบหก เช่น 2001:db8::1 แนวคิดส่วนใหญ่ในคู่มือนี้ (ซับเน็ต เส้นทาง DNS พอร์ต) ใช้กับ IPv6 ได้เหมือนกัน แล็บนี้ใช้ IPv4 เพื่อให้ตัวเลขสั้น

Lesson 3 · Addressesบทที่ 3 · Address

Subnets and CIDRซับเน็ตและ CIDR

An address has two parts: the network part, shared by every machine on the same network, and the host part, unique to each machine. The /24 after an address (CIDR notation) says how many bits, from the left, are the network part. In 10.10.1.10/24 the first 24 bits (10.10.1) are the network and the last 8 bits (.10) are the host. The same information written as a mask is 255.255.255.0. address มีสองส่วน ส่วน network ที่ทุกเครื่องในเครือข่ายเดียวกันใช้ร่วมกัน และส่วน host ที่ไม่ซ้ำกันในแต่ละเครื่อง /24 ที่ต่อท้าย address (การเขียนแบบ CIDR) บอกว่ากี่บิตจากทางซ้ายเป็นส่วน network ใน 10.10.1.10/24 24 บิตแรก (10.10.1) คือ network และ 8 บิตสุดท้าย (.10) คือ host ข้อมูลเดียวกันเมื่อเขียนเป็น mask คือ 255.255.255.0

The first address names the network itself and the last is the broadcast address, which means "everyone on this network", so neither can be given to a machine. A /24 therefore has 256 − 2 = 254 usable addresses. Each extra prefix bit halves the network. address แรกเป็นชื่อของเครือข่ายเอง และ address สุดท้ายเป็น broadcast ซึ่งหมายถึง "ทุกเครื่องในเครือข่ายนี้" จึงกำหนดให้เครื่องใดไม่ได้ทั้งคู่ /24 จึงมี address ที่ใช้ได้ 256 − 2 = 254 ทุกบิตที่เพิ่มใน prefix ทำให้เครือข่ายเล็กลงครึ่งหนึ่ง

CIDRMaskUsable hostshost ที่ใช้ได้
/16255.255.0.065,534
/24255.255.255.0254
/26255.255.255.19262
/30255.255.255.2522
/32255.255.255.255one exact addressaddress เดียวพอดี

Why it matters: before sending anything, a machine compares the destination with its own network. Same network: it sends the packet directly. Different network: it sends it to its gateway, the router. This is exactly what the client does: เหตุที่สำคัญคือ ก่อนส่งอะไร เครื่องจะเทียบปลายทางกับเครือข่ายของตัวเอง ถ้าอยู่เครือข่ายเดียวกันก็ส่งแพ็กเก็ต ตรงถึงปลายทาง ถ้าคนละเครือข่ายก็ส่งให้ gateway ซึ่งก็คือ router นี่คือสิ่งที่ client ทำจริงๆ

Lesson 4 · Addressesบทที่ 4 · Address

Routingการจัดเส้นทาง

Every machine has a routing table: a list of networks and where to send packets for each. The most specific match wins; the default route catches everything else. ทุกเครื่องมี ตารางเส้นทาง (routing table) คือรายการเครือข่ายและที่ที่ต้องส่งแพ็กเก็ตไปสำหรับแต่ละเครือข่าย รายการที่ตรงที่สุดจะถูกใช้ และ default route รับทุกอย่างที่เหลือ

clientclientDebian 13 · Docker lab
$ ip route
default via 10.10.1.1 dev eth0 
10.10.1.0/24 dev eth0 proto kernel scope link src 10.10.1.10 

$ ip route get 10.10.1.1; ip route get 10.10.2.20
10.10.1.1 dev eth0 src 10.10.1.10 uid 0 
    cache 
10.10.2.20 via 10.10.1.1 dev eth0 src 10.10.1.10 uid 0 
    cache 

Line two of ip route: 10.10.1.0/24 is right on eth0. Line one: everything else goes via 10.10.1.1, the router. ip route get asks the kernel which rule it would use: the router directly, but web via the router.บรรทัดที่สองของ ip route บอกว่า 10.10.1.0/24 อยู่บน eth0 โดยตรง บรรทัดแรกบอกว่าที่เหลือทั้งหมดไป via 10.10.1.1 ซึ่งคือ router ส่วน ip route get ถาม kernel ว่าจะใช้กฎข้อไหน router ส่งตรงได้ แต่ web ต้องผ่าน (via) router

ping sends an ICMP "echo request" and waits for the reply. Look at ttl: every router a packet passes lowers its time to live by one, so the reply from web, one router away, arrives with 63 instead of 64. ping ส่ง ICMP "echo request" แล้วรอคำตอบ ให้ดูที่ ttl ทุกครั้งที่แพ็กเก็ตผ่าน router ค่า time to live จะลดลงหนึ่ง คำตอบจาก web ที่อยู่ห่างไปหนึ่ง router จึงมาถึงด้วยค่า 63 แทน 64

clientclientDebian 13 · Docker lab
$ ping -c 2 10.10.1.1
PING 10.10.1.1 (10.10.1.1) 56(84) bytes of data.
64 bytes from 10.10.1.1: icmp_seq=1 ttl=64 time=0.169 ms
64 bytes from 10.10.1.1: icmp_seq=2 ttl=64 time=0.333 ms

--- 10.10.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1058ms
rtt min/avg/max/mdev = 0.169/0.251/0.333/0.082 ms

$ ping -c 3 10.10.2.20
PING 10.10.2.20 (10.10.2.20) 56(84) bytes of data.
64 bytes from 10.10.2.20: icmp_seq=1 ttl=63 time=0.211 ms
64 bytes from 10.10.2.20: icmp_seq=2 ttl=63 time=0.289 ms
64 bytes from 10.10.2.20: icmp_seq=3 ttl=63 time=0.113 ms

--- 10.10.2.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2050ms
rtt min/avg/max/mdev = 0.113/0.204/0.289/0.072 ms

$ traceroute -n 10.10.2.20
traceroute to 10.10.2.20 (10.10.2.20), 30 hops max, 60 byte packets
 1  10.10.1.1  0.076 ms  0.029 ms  0.024 ms
 2  10.10.2.20  0.058 ms  0.057 ms  0.035 ms

traceroute uses TTL on purpose: it sends packets with TTL 1, 2, 3… and each router that drops one sends back an error, revealing itself. Hop 1 is the router, hop 2 is web. -c sets how many pings to send; without it Linux pings until you press Ctrl+C.traceroute ใช้ TTL อย่างตั้งใจ มันส่งแพ็กเก็ตที่มี TTL 1, 2, 3… และ router แต่ละตัวที่ทิ้งแพ็กเก็ตจะส่งข้อผิดพลาดกลับมา ซึ่งเผยตัวมันเอง hop 1 คือ router และ hop 2 คือ web ส่วน -c กำหนดจำนวนครั้งที่จะ ping ถ้าไม่ใส่ Linux จะ ping ไปเรื่อยๆ จนกว่าจะกด Ctrl+C

The router knows both of its networks and nothing else: it has no default route, because the lab has no internet. So a packet for anywhere else gets an error back from the router: router รู้จักเครือข่ายของตัวเองทั้งสองวงและไม่รู้จักอะไรอีก มันไม่มี default route เพราะแล็บไม่มีอินเทอร์เน็ต แพ็กเก็ตที่จะไปที่อื่นจึงได้รับข้อผิดพลาดกลับมาจาก router

routerrouterDebian 13 · Docker lab
$ ip route
10.10.1.0/24 dev eth0 proto kernel scope link src 10.10.1.1 
10.10.2.0/24 dev eth1 proto kernel scope link src 10.10.2.1 
clientclientDebian 13 · Docker lab
$ ping -c 2 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
From 10.10.1.1 icmp_seq=1 Destination Net Unreachable
From 10.10.1.1 icmp_seq=2 Destination Net Unreachable

--- 1.1.1.1 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1018ms
Forwardingการส่งต่อ

A Linux machine only passes packets between its interfaces when IP forwarding is on (net.ipv4.ip_forward=1). That single setting is what makes the router container a router. On the internet, routers learn routes from each other with protocols such as BGP; small networks just use a default route.เครื่อง Linux จะส่งต่อแพ็กเก็ตระหว่าง interface ก็ต่อเมื่อเปิด IP forwarding (net.ipv4.ip_forward=1) การตั้งค่าเดียวนี้ทำให้ container router เป็น router บนอินเทอร์เน็ต router เรียนรู้เส้นทางจากกันและกันด้วยโปรโตคอลอย่าง BGP ส่วนเครือข่ายเล็กๆ ใช้แค่ default route

Lesson 5 · Addressesบทที่ 5 · Address

MAC addresses and ARPMAC address และ ARP

IP addresses get a packet across networks, but on the wire between two neighbours (Ethernet, Wi-Fi) frames are delivered by MAC address: a 48-bit hardware address of the network card. To send to 10.10.1.1, the client first has to learn the router's MAC address. It asks the whole network with ARP: "who has 10.10.1.1?" IP address พาแพ็กเก็ตข้ามเครือข่าย แต่บนสายระหว่างเครื่องที่อยู่ติดกัน (Ethernet หรือ Wi-Fi) เฟรมถูกส่งตาม MAC address ซึ่งเป็น address ฮาร์ดแวร์ขนาด 48 บิตของการ์ดเครือข่าย ก่อนส่งถึง 10.10.1.1 client ต้องรู้ MAC address ของ router ก่อน มันจึงถามทั้งเครือข่ายด้วย ARP ว่า "ใครมี 10.10.1.1?"

clientclientDebian 13 · Docker lab
$ ip -br link show eth0
eth0@if502       UP             26:5a:6a:d0:56:a6 <BROADCAST,MULTICAST,UP,LOWER_UP> 

$ ip neigh flush all; ping -c 1 10.10.2.20 > /dev/null; ip neigh
10.10.1.1 dev eth0 lladdr 6e:63:cb:86:b4:8e REACHABLE 

After pinging web, the client's neighbour table holds only the router. The client never learns web's MAC address: web is on another network, so every packet for it goes to the router's MAC address with web's IP address inside.หลัง ping web ตาราง neighbour ของ client มีเพียง router client ไม่เคยรู้ MAC address ของ web เพราะ web อยู่อีกเครือข่าย ทุกแพ็กเก็ตที่จะไป web จึงถูกส่งไปที่ MAC address ของ router โดยมี IP address ของ web อยู่ข้างใน

Here is that question and answer on the wire. tcpdump prints the packets an interface sees; I started it on the router, emptied the client's table and pinged the router once. นี่คือคำถามและคำตอบนั้นบนสาย tcpdump พิมพ์แพ็กเก็ตที่ interface มองเห็น ผมเริ่มมันบน router ล้างตารางของ client แล้ว ping router หนึ่งครั้ง

routerrouterDebian 13 · Docker lab
$ tcpdump -n -e -i eth0 -c 2 arp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:09:11.029006 26:5a:6a:d0:56:a6 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.10.1.1 tell 10.10.1.10, length 28
18:09:11.029011 6e:63:cb:86:b4:8e > 26:5a:6a:d0:56:a6, ethertype ARP (0x0806), length 42: Reply 10.10.1.1 is-at 6e:63:cb:86:b4:8e, length 28
2 packets captured
2 packets received by filter
0 packets dropped by kernel

The request goes to ff:ff:ff:ff:ff:ff, the broadcast MAC address, so every machine on the network hears it. Only the router answers, and only to the client. These MAC addresses were made up by Docker; real ones start with a code for the card's maker.คำถามถูกส่งไปที่ ff:ff:ff:ff:ff:ff ซึ่งเป็น MAC address แบบ broadcast ทุกเครื่องในเครือข่ายจึงได้ยิน แต่มีเพียง router ที่ตอบ และตอบเฉพาะ client MAC address เหล่านี้ Docker สร้างขึ้นเอง ส่วนของจริงจะขึ้นต้นด้วยรหัสของผู้ผลิตการ์ด

Lesson 6 · Servicesบทที่ 6 · บริการ

DNSDNS

People use names; packets need addresses. DNS (the Domain Name System) turns one into the other. A machine sends its question to the DNS server listed in /etc/resolv.conf; the lab's DNS server knows the names ending in .internal, a suffix reserved for private networks. คนใช้ชื่อ แต่แพ็กเก็ตต้องใช้ address DNS (Domain Name System) แปลงชื่อเป็น address เครื่องจะส่งคำถามไปยัง DNS เซิร์ฟเวอร์ที่ระบุใน /etc/resolv.conf DNS เซิร์ฟเวอร์ของแล็บรู้จักชื่อที่ลงท้ายด้วย .internal ซึ่งเป็นส่วนท้ายที่สงวนไว้สำหรับเครือข่ายส่วนตัว

clientclientDebian 13 · Docker lab
$ cat /etc/resolv.conf
nameserver 10.10.2.53

$ dig web.internal
; <<>> DiG 9.20.29-1~deb13u1-Debian <<>> web.internal
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9527
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;web.internal.			IN	A

;; ANSWER SECTION:
web.internal.		300	IN	A	10.10.2.20

;; Query time: 1 msec
;; SERVER: 10.10.2.53#53(10.10.2.53) (UDP)
;; WHEN: Mon Oct 05 18:19:07 UTC 2026
;; MSG SIZE  rcvd: 57

status: NOERROR means the question worked. The answer section says web.internal has an A record (an IPv4 address) of 10.10.2.20, and 300 is the TTL: other resolvers may cache the answer for 300 seconds. SERVER shows who answered.status: NOERROR แปลว่าการถามสำเร็จ answer section บอกว่า web.internal มี A record (IPv4 address) เป็น 10.10.2.20 และ 300 คือ TTL หมายความว่าตัวถามอื่นเก็บคำตอบไว้ใช้ซ้ำได้ 300 วินาที ส่วน SERVER บอกว่าใครเป็นผู้ตอบ

Your computer: the DNS server's logคอมพิวเตอร์ของเรา: log ของ DNS เซิร์ฟเวอร์Debian 13 · Docker lab
$ docker compose logs --no-log-prefix dns | tail -n 2
dnsmasq[8]: query[A] web.internal from 10.10.1.10
dnsmasq[8]: config web.internal is 10.10.2.20

The last two lines of the server's log are about that one question. The query crossed the router: the client is 10.10.1.10, the DNS server is on the office network.สองบรรทัดสุดท้ายใน log ของเซิร์ฟเวอร์เป็นของคำถามนั้น คำถามข้าม router มา เพราะ client คือ 10.10.1.10 และ DNS เซิร์ฟเวอร์อยู่ในเครือข่ายสำนักงาน

clientclientDebian 13 · Docker lab
$ dig +short api.internal; dig +short TXT hello.internal; dig +short MX internal
web.internal.
10.10.2.20
"Hello from the lab DNS server"
10 mail.internal.

$ dig nosuch.internal | grep status
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 36021

$ getent hosts web.internal
10.10.2.20      web.internal
RecordRecordHoldsเก็บอะไรIn the labในแล็บ
Aan IPv4 addressIPv4 addressweb.internal → 10.10.2.20
AAAAan IPv6 addressIPv6 address–
CNAMEanother name to look up insteadชื่ออื่นที่ต้องไปค้นแทนapi.internal → web.internal
MXthe mail server for a domainเมลเซิร์ฟเวอร์ของโดเมนinternal → mail.internal
TXTfree text, often used to prove you own a domainข้อความอิสระ มักใช้พิสูจน์ความเป็นเจ้าของโดเมนhello.internal
NXDOMAINnot a record: the answer "this name doesn't exist"ไม่ใช่ record แต่เป็นคำตอบว่า "ไม่มีชื่อนี้"nosuch.internal

Before asking DNS, most programs check /etc/hosts, a plain file of address-name pairs. getent hosts follows the same order as your programs. dig doesn't: it always asks the DNS server directly. ก่อนถาม DNS โปรแกรมส่วนใหญ่จะตรวจ /etc/hosts ก่อน ซึ่งเป็นไฟล์ธรรมดาที่เก็บคู่ address กับชื่อ getent hosts ทำตามลำดับเดียวกับโปรแกรมของเรา แต่ dig ไม่ทำ มันถาม DNS เซิร์ฟเวอร์โดยตรงเสมอ

clientclientDebian 13 · Docker lab
$ echo "10.10.2.99 web.internal" >> /etc/hosts; getent hosts web.internal; dig +short web.internal
10.10.2.99      web.internal
10.10.2.20

Two different answers for the same name: curl and your browser would go to 10.10.2.99, but dig still says 10.10.2.20.ชื่อเดียวกันได้คำตอบสองแบบ curl และเบราว์เซอร์จะไปที่ 10.10.2.99 แต่ dig ยังบอกว่า 10.10.2.20

Lesson 7 · Servicesบทที่ 7 · บริการ

Ports, TCP and UDPพอร์ต TCP และ UDP

An IP address finds the machine; a port (a number from 1 to 65535) finds the program on it. A server program listens on a port; ss lists them. Two protocols carry most traffic: TCP sets up a connection and makes sure everything arrives in order; UDP just sends separate messages, faster but with no guarantees. IP address หาเครื่อง ส่วน พอร์ต (ตัวเลขตั้งแต่ 1 ถึง 65535) หาโปรแกรมบนเครื่องนั้น โปรแกรมเซิร์ฟเวอร์จะ listen บนพอร์ต และ ss แสดงรายการเหล่านั้น การรับส่งข้อมูลส่วนใหญ่ใช้สองโปรโตคอล TCP สร้างการเชื่อมต่อและทำให้ทุกอย่างมาถึงครบและตามลำดับ UDP แค่ส่งข้อความแยกกันไป เร็วกว่าแต่ไม่มีการรับประกัน

webwebDebian 13 · Docker lab
$ ss -tlnp
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess                      
LISTEN 0      4096      127.0.0.11:36511      0.0.0.0:*                                
LISTEN 0      511          0.0.0.0:80         0.0.0.0:*    users:(("nginx",pid=8,fd=5))
LISTEN 0      511          0.0.0.0:443        0.0.0.0:*    users:(("nginx",pid=8,fd=6))
dnsdnsDebian 13 · Docker lab
$ ss -ulnp
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
UNCONN 0      0            0.0.0.0:53         0.0.0.0:*          
UNCONN 0      0         127.0.0.11:38036      0.0.0.0:*          
UNCONN 0      0               [::]:53            [::]:*          

-t TCP, -u UDP, -l listening, -n numbers instead of names, -p the program. nginx listens on TCP 80 and 443 on 0.0.0.0, meaning every address of the machine. The DNS server listens on UDP 53. Ignore 127.0.0.11: that's Docker's own helper inside each container.-t คือ TCP, -u คือ UDP, -l คือที่กำลัง listen, -n แสดงเป็นตัวเลขแทนชื่อ และ -p แสดงโปรแกรม nginx listen บน TCP 80 และ 443 ที่ 0.0.0.0 ซึ่งหมายถึงทุก address ของเครื่อง DNS เซิร์ฟเวอร์ listen บน UDP 53 ไม่ต้องสนใจ 127.0.0.11 เพราะเป็นตัวช่วยของ Docker เองในทุก container

clientclientDebian 13 · Docker lab
$ nc -zv web.internal 80; nc -zv web.internal 443; nc -zv web.internal 8080
Connection to web.internal (10.10.2.20) 80 port [tcp/http] succeeded!
Connection to web.internal (10.10.2.20) 443 port [tcp/https] succeeded!
nc: connect to web.internal (10.10.2.20) port 8080 (tcp) failed: Connection refused

nc -z only checks whether a TCP connection can be made. Port 8080: Connection refused. The machine answered at once, but nothing listens there.nc -z ตรวจเพียงว่าเชื่อมต่อ TCP ได้หรือไม่ พอร์ต 8080 ได้ Connection refused แปลว่าเครื่องตอบกลับทันที แต่ไม่มีโปรแกรมใด listen อยู่

nc (netcat) can also be the server. I made web listen on TCP port 9000 and UDP port 9999, then sent a line to each from the client: nc (netcat) เป็นเซิร์ฟเวอร์ได้ด้วย ผมให้ web listen บน TCP พอร์ต 9000 และ UDP พอร์ต 9999 แล้วส่งข้อความหนึ่งบรรทัดไปที่แต่ละพอร์ตจาก client

clientclientDebian 13 · Docker lab
$ echo "hello over TCP" | nc -N web.internal 9000

$ echo "hello over UDP" | nc -u -w 1 web.internal 9999
web, started firstweb ซึ่งเริ่มก่อนDebian 13 · Docker lab
$ nc -l 9000
hello over TCP

$ timeout 3 nc -u -l 9999
hello over UDP
Portพอร์ตServiceบริการPortพอร์ตServiceบริการ
22/tcpSSH3306/tcpMySQL
53/udp, 53/tcpDNS5432/tcpPostgreSQL
80/tcpHTTP6379/tcpRedis
443/tcpHTTPS3000, 8080/tcpcommon for development serversใช้บ่อยกับเซิร์ฟเวอร์สำหรับพัฒนา

Lesson 8 · Servicesบทที่ 8 · บริการ

Inside a TCP connectionภายในการเชื่อมต่อ TCP

Let's watch one complete web request. I ran tcpdump on the router's office side while the client fetched the home page with curl -s -o /dev/null web.internal. timeout -s INT 3 stops tcpdump after 3 seconds, as if pressing Ctrl+C. มาดูคำขอเว็บหนึ่งครั้งตั้งแต่ต้นจนจบ ผมรัน tcpdump บน router ฝั่งเครือข่ายสำนักงาน ขณะที่ client ดึงหน้าแรกด้วย curl -s -o /dev/null web.internal ส่วน timeout -s INT 3 หยุด tcpdump หลัง 3 วินาที เหมือนกด Ctrl+C

routerrouterDebian 13 · Docker lab
$ timeout -s INT 3 tcpdump -n -i eth1 tcp port 80
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:09:56.787732 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [S], seq 3387188901, win 64240, options [mss 1460,sackOK,TS val 3895902054 ecr 0,nop,wscale 7], length 0
18:09:56.787765 IP 10.10.2.20.80 > 10.10.1.10.43436: Flags [S.], seq 1048383717, ack 3387188902, win 65160, options [mss 1460,sackOK,TS val 633903972 ecr 3895902054,nop,wscale 7], length 0
18:09:56.787813 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [.], ack 1, win 502, options [nop,nop,TS val 3895902054 ecr 633903972], length 0
18:09:56.787892 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [P.], seq 1:77, ack 1, win 502, options [nop,nop,TS val 3895902054 ecr 633903972], length 76: HTTP: GET / HTTP/1.1
18:09:56.787896 IP 10.10.2.20.80 > 10.10.1.10.43436: Flags [.], ack 77, win 509, options [nop,nop,TS val 633903972 ecr 3895902054], length 0
18:09:56.788535 IP 10.10.2.20.80 > 10.10.1.10.43436: Flags [P.], seq 1:307, ack 77, win 509, options [nop,nop,TS val 633903973 ecr 3895902054], length 306: HTTP: HTTP/1.1 200 OK
18:09:56.788544 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [.], ack 307, win 501, options [nop,nop,TS val 3895902055 ecr 633903973], length 0
18:09:56.788602 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [F.], seq 77, ack 307, win 501, options [nop,nop,TS val 3895902055 ecr 633903973], length 0
18:09:56.788628 IP 10.10.2.20.80 > 10.10.1.10.43436: Flags [F.], seq 307, ack 78, win 509, options [nop,nop,TS val 633903973 ecr 3895902055], length 0
18:09:56.788638 IP 10.10.1.10.43436 > 10.10.2.20.80: Flags [.], ack 308, win 501, options [nop,nop,TS val 3895902055 ecr 633903973], length 0

10 packets captured
10 packets received by filter
0 packets dropped by kernel
Packetsแพ็กเก็ตFlagsWhat happensเกิดอะไรขึ้น
1–3[S] [S.] [.]the three-way handshake: SYN, SYN-ACK, ACK. Both sides agree on starting sequence numbers.three-way handshake: SYN, SYN-ACK, ACK ทั้งสองฝ่ายตกลงเลขลำดับเริ่มต้นร่วมกัน
4–5[P.] [.]the client sends 76 bytes, GET / HTTP/1.1; the server acknowledges themclient ส่ง 76 ไบต์ คือ GET / HTTP/1.1 และเซิร์ฟเวอร์ยืนยันว่าได้รับแล้ว
6–7[P.] [.]the server sends 306 bytes, HTTP/1.1 200 OK and the page; the client acknowledgesเซิร์ฟเวอร์ส่ง 306 ไบต์ คือ HTTP/1.1 200 OK พร้อมหน้าเว็บ และ client ยืนยันว่าได้รับ
8–10[F.] [F.] [.]both sides close with FIN, and the last ACK confirms itทั้งสองฝ่ายปิดการเชื่อมต่อด้วย FIN และ ACK สุดท้ายยืนยัน

Note the client's side of the connection: 10.10.1.10.43436. The client picked a random ephemeral port for itself, so a connection is really four numbers: source address and port, destination address and port. That's how one machine can hold thousands of connections to the same server. สังเกตฝั่ง client ของการเชื่อมต่อ 10.10.1.10.43436 client สุ่ม ephemeral port ให้ตัวเอง การเชื่อมต่อหนึ่งจึงประกอบด้วยตัวเลขสี่ค่า คือ address และพอร์ตต้นทาง กับ address และพอร์ตปลายทาง นี่คือเหตุที่เครื่องเดียวเชื่อมต่อกับเซิร์ฟเวอร์เดียวกันได้หลายพันการเชื่อมต่อ

Layersเลเยอร์

You have now seen every layer that matters in practice: Ethernet frames with MAC addresses (link), IP packets with addresses and TTL (network), TCP segments with ports and flags (transport), and HTTP inside them (application). The OSI model explainer puts names on all seven.ตอนนี้เราได้เห็นทุกเลเยอร์ที่สำคัญในการใช้งานจริงแล้ว ได้แก่ เฟรม Ethernet ที่มี MAC address (link), แพ็กเก็ต IP ที่มี address และ TTL (network), segment ของ TCP ที่มีพอร์ตและ flag (transport) และ HTTP ที่อยู่ข้างใน (application) ดู คำอธิบายโมเดล OSI สำหรับชื่อของทั้งเจ็ดเลเยอร์

Lesson 9 · Servicesบทที่ 9 · บริการ

HTTP and HTTPSHTTP และ HTTPS

HTTP is plain text over a TCP connection: a request line and headers from the client, a status line, headers and a body from the server. curl -v shows all of it. Lines with * are curl's notes, > is what it sent, < is what came back. HTTP คือข้อความธรรมดาบนการเชื่อมต่อ TCP ฝั่ง client ส่งบรรทัดคำขอและ header ฝั่งเซิร์ฟเวอร์ส่งบรรทัดสถานะ header และ body กลับมา curl -v แสดงทั้งหมด บรรทัดที่มี * คือบันทึกของ curl, > คือสิ่งที่ส่งไป และ < คือสิ่งที่ได้กลับมา

clientclientDebian 13 · Docker lab
$ curl -v http://web.internal/
* Host web.internal:80 was resolved.
* IPv6: (none)
* IPv4: 10.10.2.20
*   Trying 10.10.2.20:80...
* Connected to web.internal (10.10.2.20) port 80
* using HTTP/1.x
> GET / HTTP/1.1
> Host: web.internal
> User-Agent: curl/8.14.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
< Server: nginx
< Date: Mon, 05 Oct 2026 18:10:11 GMT
< Content-Type: text/html
< Content-Length: 77
< Last-Modified: Mon, 05 Oct 2026 17:53:27 GMT
< Connection: keep-alive
< ETag: "6ac3e417-4d"
< Accept-Ranges: bytes
< 
<!doctype html>
<title>web.internal</title>
<h1>Hello from web.internal</h1>
* Connection #0 to host web.internal left intact
webwebDebian 13 · Docker lab
$ tail -n 1 /var/log/nginx/access.log
10.10.1.10 - - [05/Oct/2026:18:10:11 +0000] "GET / HTTP/1.1" 200 77 "-" "curl/8.14.1"

The server's log line for that request: who asked (10.10.1.10), when, what, the status code (200), the size (77 bytes) and the client program.บรรทัด log ของเซิร์ฟเวอร์สำหรับคำขอนั้น บอกว่าใครถาม (10.10.1.10) เมื่อไร ถามอะไร status code (200) ขนาด (77 ไบต์) และโปรแกรมของ client

Anyone on the path (the router, a café's Wi-Fi) can read and change plain HTTP. HTTPS is HTTP inside TLS, which encrypts the connection and proves the server's identity with a certificate. A certificate says "this key belongs to web.internal" and is signed by a certificate authority (CA) the client trusts. Browsers and operating systems ship with a list of public CAs. The lab uses its own CA, which the client doesn't trust yet: ใครก็ตามที่อยู่บนเส้นทาง (router หรือ Wi-Fi ในร้านกาแฟ) อ่านและแก้ไข HTTP ธรรมดาได้ HTTPS คือ HTTP ที่อยู่ภายใน TLS ซึ่งเข้ารหัสการเชื่อมต่อและพิสูจน์ตัวตนของเซิร์ฟเวอร์ด้วย certificate certificate บอกว่า "กุญแจนี้เป็นของ web.internal" และถูกลงนามโดย certificate authority (CA) ที่ client เชื่อถือ เบราว์เซอร์และระบบปฏิบัติการมีรายชื่อ CA สาธารณะติดมาด้วย แล็บนี้ใช้ CA ของตัวเอง ซึ่ง client ยังไม่เชื่อถือ

clientclientDebian 13 · Docker lab
$ curl https://web.internal/
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.

$ curl --cacert /root/lab-ca.crt https://web.internal/
<!doctype html>
<title>web.internal</title>
<h1>Hello from web.internal</h1>

$ curl -sv --cacert /root/lab-ca.crt https://web.internal/ -o /dev/null 2>&1 | grep -E 'hello|SSL connection|subject:|issuer:|expire|verify ok'
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / RSASSA-PSS
*  subject: CN=web.internal
*  expire date: Jan  7 17:56:05 2029 GMT
*  issuer: CN=Netlab CA
*  SSL certificate verify ok.

With the lab's CA certificate, curl could check the chain: the certificate is for web.internal, issued by Netlab CA, not expired, and the signature is valid. Only then did it send the request, encrypted with TLS 1.3.เมื่อมี certificate ของ CA ในแล็บ curl ตรวจสายการรับรองได้ว่า certificate เป็นของ web.internal ออกโดย Netlab CA ยังไม่หมดอายุ และลายเซ็นถูกต้อง จากนั้นจึงส่งคำขอ โดยเข้ารหัสด้วย TLS 1.3

In real lifeในชีวิตจริง

Public websites get free certificates from a public CA such as Let's Encrypt, usually through the web server or a load balancer that renews them automatically. Private CAs like the lab's are common inside companies; their CA certificate is installed on company machines.เว็บไซต์สาธารณะได้ certificate ฟรีจาก CA สาธารณะ เช่น Let's Encrypt โดยมักให้เว็บเซิร์ฟเวอร์หรือ load balancer ต่ออายุให้อัตโนมัติ ส่วน CA ส่วนตัวแบบในแล็บนี้พบบ่อยในบริษัท โดยติดตั้ง certificate ของ CA ไว้ในเครื่องของบริษัท

Lesson 10 · Controlบทที่ 10 · การควบคุม

NAT and port forwardingNAT และ port forwarding

NAT (network address translation) rewrites addresses as packets pass a router. Your home router does it all day: every device inside uses a private address, and the router replaces it with its single public address on the way out, remembering which reply belongs to whom. The lab's router can do the same with Linux's firewall, iptables. First, without NAT, web sees the client's real address: NAT (network address translation) เขียน address ใหม่ขณะที่แพ็กเก็ตผ่าน router router ที่บ้านทำแบบนี้ตลอดเวลา ทุกอุปกรณ์ข้างในใช้ address ส่วนตัว และ router แทนที่ด้วย address สาธารณะเพียงอันเดียวของมันขณะส่งออกไป แล้วจำว่าคำตอบไหนเป็นของใคร router ในแล็บทำแบบเดียวกันได้ด้วยไฟร์วอลล์ของ Linux คือ iptables ก่อนอื่น เมื่อไม่มี NAT web จะเห็น address จริงของ client

web, after a request from the clientweb หลังจาก client ส่งคำขอDebian 13 · Docker lab
$ tail -n 1 /var/log/nginx/access.log
10.10.1.10 - - [05/Oct/2026:18:10:24 +0000] "GET / HTTP/1.1" 200 77 "-" "curl/8.14.1"
routerrouterDebian 13 · Docker lab
$ iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
web, after another requestweb หลังคำขออีกครั้งDebian 13 · Docker lab
$ tail -n 1 /var/log/nginx/access.log
10.10.2.1 - - [05/Oct/2026:18:10:25 +0000] "GET / HTTP/1.1" 200 77 "-" "curl/8.14.1"
routerrouterDebian 13 · Docker lab
$ iptables -t nat -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 DOCKER_POSTROUTING  all  --  *      *       0.0.0.0/0            127.0.0.11          
    2   118 MASQUERADE  all  --  *      eth1    0.0.0.0/0            0.0.0.0/0           

MASQUERADE on packets going out of eth1 (office side): the source address becomes the router's own, 10.10.2.1. Web no longer knows who the client is. The counters show 2 packets matched: NAT is decided on a connection's first packet and remembered for the rest. The DOCKER_POSTROUTING line is Docker's own rule.MASQUERADE กับแพ็กเก็ตที่ออกทาง eth1 (ฝั่งสำนักงาน) ทำให้ address ต้นทางกลายเป็นของ router เอง คือ 10.10.2.1 web จึงไม่รู้อีกต่อไปว่า client เป็นใคร ตัวนับแสดงว่ามี 2 แพ็กเก็ตที่ตรงกฎ เพราะ NAT ตัดสินที่แพ็กเก็ตแรกของการเชื่อมต่อแล้วจำไว้ใช้กับแพ็กเก็ตที่เหลือ บรรทัด DOCKER_POSTROUTING เป็นกฎของ Docker เอง

Port forwarding (destination NAT) goes the other way: connections that arrive at the router on one port are sent on to a machine behind it. Here, port 8080 on the router's home side forwards to web's port 80: Port forwarding (destination NAT) ทำงานในทิศตรงข้าม การเชื่อมต่อที่มาถึงพอร์ตหนึ่งของ router จะถูกส่งต่อไปยังเครื่องที่อยู่ด้านหลัง ในที่นี้พอร์ต 8080 ฝั่งบ้านของ router ส่งต่อไปที่พอร์ต 80 ของ web

routerrouterDebian 13 · Docker lab
$ iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.10.2.20:80
clientclientDebian 13 · Docker lab
$ curl -s 10.10.1.1:8080
<!doctype html>
<title>web.internal</title>
<h1>Hello from web.internal</h1>
Docker

This is exactly what docker run -p 8080:80 does on your computer: a DNAT rule from port 8080 on the host to port 80 of the container, which lives on a private 172.17.x.x network. Home routers' "port forwarding" page and cloud load balancers do the same job.นี่คือสิ่งที่ docker run -p 8080:80 ทำบนคอมพิวเตอร์ของเรา คือสร้างกฎ DNAT จากพอร์ต 8080 ของเครื่องไปยังพอร์ต 80 ของ container ซึ่งอยู่ในเครือข่ายส่วนตัว 172.17.x.x หน้า "port forwarding" ของ router ที่บ้านและ load balancer บนคลาวด์ก็ทำงานแบบเดียวกัน

Lesson 11 · Controlบทที่ 11 · การควบคุม

Firewallsไฟร์วอลล์

A firewall decides which packets may pass, by rules on addresses, ports and protocols. There are two ways to say no. REJECT answers "refused" straight away. DROP says nothing at all, so the sender waits until it gives up. Packets passing through the router go through the FORWARD chain. Let's block web's port 80 from the client, first with REJECT: ไฟร์วอลล์ ตัดสินว่าแพ็กเก็ตใดผ่านได้ ตามกฎที่กำหนดด้วย address พอร์ต และโปรโตคอล การปฏิเสธมีสองแบบ REJECT ตอบกลับว่า "ปฏิเสธ" ทันที DROP ไม่ตอบอะไรเลย ผู้ส่งจึงต้องรอจนยอมแพ้ แพ็กเก็ตที่ผ่าน ทะลุ router จะเข้า chain FORWARD ลองบล็อกพอร์ต 80 ของ web จาก client โดยเริ่มด้วย REJECT

routerrouterDebian 13 · Docker lab
$ iptables -A FORWARD -p tcp --dport 80 -j REJECT --reject-with tcp-reset
clientclientDebian 13 · Docker lab
$ nc -zv -w 3 web.internal 80
nc: connect to web.internal (10.10.2.20) port 80 (tcp) failed: Connection refused

$ ping -c 2 web.internal
PING web.internal (10.10.2.20) 56(84) bytes of data.
64 bytes from web.internal (10.10.2.20): icmp_seq=1 ttl=63 time=0.092 ms
64 bytes from web.internal (10.10.2.20): icmp_seq=2 ttl=63 time=0.239 ms

--- web.internal ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1019ms
rtt min/avg/max/mdev = 0.092/0.165/0.239/0.073 ms

$ curl -s --cacert /root/lab-ca.crt https://web.internal/
<!doctype html>
<title>web.internal</title>
<h1>Hello from web.internal</h1>

Only TCP port 80 is blocked: ping (ICMP) and HTTPS (port 443) still work. A working ping proves the machine is reachable, not that every service on it is.มีเพียง TCP พอร์ต 80 ที่ถูกบล็อก ping (ICMP) และ HTTPS (พอร์ต 443) ยังทำงาน การ ping ได้พิสูจน์ว่าเครื่องเข้าถึงได้ ไม่ได้พิสูจน์ว่าทุกบริการบนเครื่องนั้นเข้าถึงได้

Now the same rule with DROP (-R FORWARD 1 replaces rule 1): ต่อไปคือกฎเดียวกันแต่ใช้ DROP (-R FORWARD 1 แทนที่กฎข้อ 1)

routerrouterDebian 13 · Docker lab
$ iptables -R FORWARD 1 -p tcp --dport 80 -j DROP
clientclientDebian 13 · Docker lab
$ time nc -zv -w 3 web.internal 80
nc: connect to web.internal (10.10.2.20) port 80 (tcp) timed out: Operation now in progress

real	0m3.013s
user	0m0.001s
sys	0m0.000s
routerrouterDebian 13 · Docker lab
$ iptables -L FORWARD -n -v --line-numbers
Chain FORWARD (policy ACCEPT 39 packets, 7569 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1        3   180 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80

With DROP the client waited the full 3 seconds (-w 3) and timed out. The rule's counter shows the 3 SYN packets it swallowed: the first try and two retries. policy ACCEPT means anything that matches no rule passes. Real firewalls usually do the opposite: policy DROP, then a short list of what's allowed.เมื่อใช้ DROP client ต้องรอครบ 3 วินาที (-w 3) แล้ว หมดเวลา (timed out) ตัวนับของกฎแสดงแพ็กเก็ต SYN 3 ตัวที่ถูกกลืนไป คือการลองครั้งแรกและการลองซ้ำอีกสองครั้ง policy ACCEPT แปลว่าอะไรที่ไม่ตรงกฎใดจะผ่านได้ ไฟร์วอลล์จริงมักทำกลับกัน คือ policy DROP แล้วมีรายการสั้นๆ ของสิ่งที่อนุญาต

You seeสิ่งที่เห็นIt meansหมายความว่า
Connection refusedthe machine (or a firewall) answered: nothing listens on that port, or a REJECT ruleเครื่อง (หรือไฟร์วอลล์) ตอบกลับมา แปลว่าไม่มีอะไร listen บนพอร์ตนั้น หรือมีกฎ REJECT
Timed outno answer at all: a DROP rule, a wrong route, or the machine is offไม่มีคำตอบเลย แปลว่ามีกฎ DROP เส้นทางผิด หรือเครื่องปิดอยู่
No route to host / Host unreachablea router or your own machine knows the address can't be reachedrouter หรือเครื่องของเราเองรู้ว่าไปถึง address นั้นไม่ได้
In the cloudบนคลาวด์

Cloud firewalls (AWS security groups, Azure network security groups, Google Cloud firewall rules) work like this FORWARD chain with policy DROP: you list the ports and source addresses that are allowed in, and everything else is dropped silently, which is why a forgotten rule shows up as a timeout.ไฟร์วอลล์บนคลาวด์ (security group ของ AWS, network security group ของ Azure, firewall rule ของ Google Cloud) ทำงานเหมือน chain FORWARD ที่มี policy DROP เราระบุพอร์ตและ address ต้นทางที่อนุญาตให้เข้า ที่เหลือทั้งหมดถูกทิ้งเงียบๆ กฎที่ลืมใส่จึงแสดงผลเป็น timeout

Lesson 12 · Controlบทที่ 12 · การควบคุม

Troubleshootingการแก้ปัญหา

When "it doesn't connect", go up the layers one question at a time, and stop at the first one that fails. That's where the problem is. เมื่อ "เชื่อมต่อไม่ได้" ให้ไล่ขึ้นทีละเลเยอร์ ถามทีละคำถาม และหยุดที่คำถามแรกที่ล้มเหลว ปัญหาอยู่ตรงนั้น

  1. Does the name resolve? dig +short nameชื่อแปลงเป็น address ได้ไหม? dig +short name
  2. Is the machine reachable? ping, ip route get, traceroute (but remember ICMP may be blocked)เข้าถึงเครื่องได้ไหม? ping, ip route get, traceroute (แต่อย่าลืมว่า ICMP อาจถูกบล็อก)
  3. Is the port open? nc -zv host port, and ss -tlnp on the serverพอร์ตเปิดไหม? nc -zv host port และ ss -tlnp บนเซิร์ฟเวอร์
  4. Does the application answer correctly? curl -v, and the server's logsแอปพลิเคชันตอบถูกต้องไหม? curl -v และ log ของเซิร์ฟเวอร์

A real case from the lab: the client can't reach the database host db.internal. กรณีจริงจากแล็บ: client เข้าถึงเครื่องฐานข้อมูล db.internal ไม่ได้

clientclientDebian 13 · Docker lab
$ curl -sS --max-time 10 http://db.internal/
curl: (7) Failed to connect to db.internal port 80 after 3085 ms: Could not connect to server

$ dig +short db.internal
10.10.2.30

$ ping -c 2 db.internal
PING db.internal (10.10.2.30) 56(84) bytes of data.
From 10.10.1.1 icmp_seq=1 Destination Host Unreachable
From 10.10.1.1 icmp_seq=2 Destination Host Unreachable

--- db.internal ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1049ms
pipe 2
routerrouterDebian 13 · Docker lab
$ ip neigh show 10.10.2.30
10.10.2.30 dev eth1 FAILED 

Step 1 passes: the name resolves to 10.10.2.30. Step 2 fails, and the error comes from the router: Destination Host Unreachable. The router's ARP entry for 10.10.2.30 is FAILED: it asked "who has 10.10.2.30?" on the office network and nobody answered. No machine has that address. Either the DNS record is wrong or the database server is off. Network rules and routes are fine.ขั้นที่ 1 ผ่าน ชื่อแปลงเป็น 10.10.2.30 ได้ ขั้นที่ 2 ล้มเหลว และข้อผิดพลาดมาจาก router คือ Destination Host Unreachable รายการ ARP ของ router สำหรับ 10.10.2.30 เป็น FAILED แปลว่ามันถามว่า "ใครมี 10.10.2.30?" ในเครือข่ายสำนักงานแล้วไม่มีใครตอบ ไม่มีเครื่องใดใช้ address นี้ แปลว่า DNS record ผิด หรือเครื่องฐานข้อมูลปิดอยู่ ส่วนกฎและเส้นทางของเครือข่ายไม่มีปัญหา

For comparison, every step for web.internal passes: เพื่อเปรียบเทียบ ทุกขั้นสำหรับ web.internal ผ่านหมด

clientclientDebian 13 · Docker lab
$ ping -c 1 web.internal
PING web.internal (10.10.2.20) 56(84) bytes of data.
64 bytes from web.internal (10.10.2.20): icmp_seq=1 ttl=63 time=0.102 ms

--- web.internal ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.102/0.102/0.102/0.000 ms

$ nc -zv web.internal 80
Connection to web.internal (10.10.2.20) 80 port [tcp/http] succeeded!

$ curl -sS -o /dev/null -w "%{http_code}\n" http://web.internal/
200
TaskงานLinuxmacOSWindows
your addressesaddress ของเครื่องip addrifconfigipconfig
routing tableตารางเส้นทางip routenetstat -rnroute print
trace the pathดูเส้นทางtraceroutetraceroutetracert
ask DNSถาม DNSdigdigResolve-DnsName
listening portsพอร์ตที่ listenss -tlnplsof -iTCP -sTCP:LISTEN -nPnetstat -ano
test a portทดสอบพอร์ตnc -zv host 80nc -zv host 80Test-NetConnection host -Port 80
neighbours (ARP)เครื่องข้างเคียง (ARP)ip neigharp -aarp -a

Lesson 13 · Your turnบทที่ 13 · ถึงตาคุณแล้ว

Practiceแบบฝึกหัด

The first task runs in your browser. The other two were done in the lab; their solutions show the real output. งานแรกรันในเบราว์เซอร์ของคุณ อีกสองงานทำในแล็บ และเฉลยแสดงผลลัพธ์จริง

1. Split a network1. แบ่งเครือข่าย

Split 10.10.0.0/24 into four /26 networks. For each, print the network, the range of host addresses and the broadcast address.แบ่ง 10.10.0.0/24 เป็นเครือข่าย /26 สี่วง แต่ละวงให้พิมพ์ network ช่วงของ address สำหรับ host และ address แบบ broadcast

2. Find the DNS service2. หาบริการ DNS

On the dns machine, find which ports and protocols the DNS server listens on.บนเครื่อง dns หาว่า DNS เซิร์ฟเวอร์ listen บนพอร์ตและโปรโตคอลใดบ้าง

Show solutionดูเฉลย
dnsdnsDebian 13 · Docker lab
$ ss -ulnp; ss -tlnp
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
UNCONN 0      0         127.0.0.11:39544      0.0.0.0:*          
UNCONN 0      0            0.0.0.0:53         0.0.0.0:*          
UNCONN 0      0               [::]:53            [::]:*          
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
LISTEN 0      32           0.0.0.0:53         0.0.0.0:*          
LISTEN 0      4096      127.0.0.11:45921      0.0.0.0:*          
LISTEN 0      32              [::]:53            [::]:*          

Port 53 on both UDP and TCP. Normal questions use UDP; TCP is used for answers too big for one UDP message and for copying whole zones between servers. A firewall that only allows UDP 53 breaks those.พอร์ต 53 ทั้ง UDP และ TCP คำถามทั่วไปใช้ UDP ส่วน TCP ใช้กับคำตอบที่ใหญ่เกินกว่าข้อความ UDP เดียวจะรับได้ และใช้คัดลอกทั้งโซนระหว่างเซิร์ฟเวอร์ ไฟร์วอลล์ที่อนุญาตเฉพาะ UDP 53 จะทำให้สิ่งเหล่านี้ใช้งานไม่ได้

3. Block ping, keep the website3. บล็อก ping แต่เว็บยังใช้ได้

Add a rule on the router so the client can no longer ping web, but can still load its page.เพิ่มกฎบน router ให้ client ping web ไม่ได้อีกต่อไป แต่ยังโหลดหน้าเว็บได้

Show solutionดูเฉลย
routerrouterDebian 13 · Docker lab
$ iptables -A FORWARD -p icmp -j DROP
clientclientDebian 13 · Docker lab
$ ping -c 2 -W 1 web.internal
PING web.internal (10.10.2.20) 56(84) bytes of data.

--- web.internal ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1046ms

$ curl -s web.internal
<!doctype html>
<title>web.internal</title>
<h1>Hello from web.internal</h1>

Lesson 14 · Debuggingบทที่ 14 · การดีบัก

Common mistakesข้อผิดพลาดที่พบบ่อย

Each of these was run in the lab; the output lines are copied from it. ทุกข้อรันในแล็บ และบรรทัดผลลัพธ์ถูกคัดลอกมาจากที่นั่น

"Ping fails, so the server is down""ping ไม่ได้ แปลว่าเซิร์ฟเวอร์ล่ม"

Many firewalls drop ICMP. With the rule from Practice 3, ping loses every packet while the website works fine. Test the service itself.ไฟร์วอลล์จำนวนมากทิ้ง ICMP เมื่อมีกฎจากแบบฝึกหัดข้อ 3 ping จะเสียแพ็กเก็ตทั้งหมด ขณะที่เว็บไซต์ใช้งานได้ปกติ ให้ทดสอบที่ตัวบริการเอง

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Testing an /etc/hosts change with digทดสอบการแก้ /etc/hosts ด้วย dig

dig and nslookup ask the DNS server directly and ignore /etc/hosts. Use getent hosts, which follows the same order as your programs.dig และ nslookup ถาม DNS เซิร์ฟเวอร์โดยตรงและไม่สนใจ /etc/hosts ให้ใช้ getent hosts ซึ่งทำตามลำดับเดียวกับโปรแกรมของเรา

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Plain HTTP to the HTTPS portส่ง HTTP ธรรมดาไปที่พอร์ต HTTPS

The port doesn't choose the protocol; the URL scheme does. Port 443 expects a TLS handshake first.พอร์ตไม่ได้เป็นตัวเลือกโปรโตคอล แต่ scheme ของ URL เป็นตัวเลือก พอร์ต 443 คาดว่าจะได้ TLS handshake ก่อน

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Turning certificate checks offปิดการตรวจ certificate

curl -k (or verify=False in code) makes the error go away by accepting any certificate, including an attacker's. Trust the right CA instead.curl -k (หรือ verify=False ในโค้ด) ทำให้ข้อผิดพลาดหายไปโดยยอมรับ certificate ใดก็ได้ รวมถึงของผู้โจมตี ให้เชื่อถือ CA ที่ถูกต้องแทน

✗ Wrong✗ ผิด
✓ Right✓ ถูก

A server that listens on 127.0.0.1เซิร์ฟเวอร์ที่ listen บน 127.0.0.1

A program bound to 127.0.0.1 only accepts connections from its own machine. This is the classic "it works on the server but not from outside" and "my app in Docker can't be reached" problem. Listen on 0.0.0.0 (all addresses) and let the firewall decide who may connect.โปรแกรมที่ผูกกับ 127.0.0.1 รับการเชื่อมต่อจากเครื่องตัวเองเท่านั้น นี่คือปัญหาคลาสสิก "บนเซิร์ฟเวอร์ใช้ได้ แต่จากข้างนอกใช้ไม่ได้" และ "เข้าแอปใน Docker ไม่ได้" ให้ listen บน 0.0.0.0 (ทุก address) แล้วให้ไฟร์วอลล์ตัดสินว่าใครเชื่อมต่อได้

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Lesson 15 · Referenceบทที่ 15 · อ้างอิง

Glossaryคำศัพท์

EnglishไทยMeaningความหมาย
hostโฮสต์Any machine with an address on a networkเครื่องใดก็ได้ที่มี address ในเครือข่าย
packetแพ็กเก็ตOne piece of data with source and destination addressesข้อมูลหนึ่งชิ้นที่มี address ต้นทางและปลายทาง
interfaceอินเทอร์เฟซA network connection of a machine, like eth0จุดเชื่อมต่อเครือข่ายของเครื่อง เช่น eth0
subnet / CIDRซับเน็ต / CIDRA block of addresses, written like 10.10.1.0/24กลุ่มของ address เขียนแบบ 10.10.1.0/24
gatewayเกตเวย์The router a machine sends to for other networksrouter ที่เครื่องส่งแพ็กเก็ตไปเมื่อปลายทางอยู่ต่างเครือข่าย
routeเส้นทางA rule: packets for this network go thereกฎที่บอกว่าแพ็กเก็ตสำหรับเครือข่ายนี้ต้องไปทางไหน
TTLTTLIn IP: hops left before a packet is dropped. In DNS: seconds an answer may be cachedใน IP: จำนวน hop ที่เหลือก่อนแพ็กเก็ตถูกทิ้ง ใน DNS: จำนวนวินาทีที่เก็บคำตอบไว้ใช้ซ้ำได้
MAC addressMAC addressThe hardware address used between neighboursaddress ของฮาร์ดแวร์ที่ใช้ระหว่างเครื่องที่อยู่ติดกัน
ARPARPFinds the MAC address that owns an IP addressหาว่า MAC address ใดเป็นเจ้าของ IP address
DNS / resolverDNS / resolverTurns names into addresses / the server you askแปลงชื่อเป็น address / เซิร์ฟเวอร์ที่เราถาม
portพอร์ตA number that picks a program on a machineตัวเลขที่เลือกโปรแกรมบนเครื่อง
TCP / UDPTCP / UDPConnection with delivery checks / single messages, no checksการเชื่อมต่อที่ตรวจการส่งถึง / ข้อความเดี่ยวที่ไม่ตรวจ
handshakeแฮนด์เชกThe opening exchange of a connection (TCP: SYN, SYN-ACK, ACK)การแลกเปลี่ยนเพื่อเปิดการเชื่อมต่อ (TCP: SYN, SYN-ACK, ACK)
TLS / certificateTLS / ใบรับรองEncryption for a connection / a signed proof of a server's identityการเข้ารหัสการเชื่อมต่อ / หลักฐานที่ลงนามแล้วว่าเซิร์ฟเวอร์เป็นใคร
NATNATRewriting addresses as packets pass a routerการเขียน address ใหม่ขณะแพ็กเก็ตผ่าน router
firewallไฟร์วอลล์Rules that let packets through or notกฎที่ตัดสินว่าแพ็กเก็ตผ่านได้หรือไม่