<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Vserbu</id>
	<title>WikiLabs - Contribuții utilizator [ro]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Vserbu"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php/Special:Contribu%C8%9Bii/Vserbu"/>
	<updated>2026-04-17T00:42:16Z</updated>
	<subtitle>Contribuții utilizator</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_11_-_Linux_Kernel_Modules&amp;diff=8200</id>
		<title>OS Lab 11 - Linux Kernel Modules</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_11_-_Linux_Kernel_Modules&amp;diff=8200"/>
		<updated>2025-12-19T14:46:53Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain the kernel module lifecycle and how loadable modules extend kernel func...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the kernel module lifecycle and how loadable modules extend kernel functionality without requiring a reboot or kernel recompilation.&lt;br /&gt;
* Understand the miscdevice framework for creating character devices that appear in &amp;lt;code&amp;gt;/dev/&amp;lt;/code&amp;gt; and respond to file operations.&lt;br /&gt;
* Implement proper kernel-space buffer management with mutex-based synchronization to handle concurrent access safely.&lt;br /&gt;
* Use the ioctl() interface to pass configuration structures between userspace and kernel space, enabling device-specific operations beyond standard read/write.&lt;br /&gt;
* Apply safe data transfer techniques using copy_to_user() and copy_from_user() to respect memory protection boundaries.&lt;br /&gt;
* Implement byte transformations (case conversion, character filtering) that execute in kernel context during write operations.&lt;br /&gt;
* Debug kernel code using printk() and interpret kernel log messages to diagnose issues.&lt;br /&gt;
* Connect kernel programming concepts to previous labs: device files (Lab 2), system calls (Lab 3), IPC mechanisms (Lab 6), and kernel primitives (Labs 7-10).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-youll-build-the-superpipe-device&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What You&amp;#039;ll Build: The Superpipe Device ===&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;ll implement a loadable kernel module called &amp;#039;&amp;#039;&amp;#039;superpipe&amp;#039;&amp;#039;&amp;#039; that creates a character device at &amp;lt;code&amp;gt;/dev/superpipe&amp;lt;/code&amp;gt;. This device acts like a regular pipe with a twist: it can transform data as it passes through.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Userspace writes data to &amp;lt;code&amp;gt;/dev/superpipe&amp;lt;/code&amp;gt; (normal &amp;lt;code&amp;gt;write()&amp;lt;/code&amp;gt; syscall)&lt;br /&gt;
# Your kernel module receives the data, applies a transformation (uppercase, lowercase, filter vowels, etc.), and stores it in a kernel buffer&lt;br /&gt;
# Userspace reads data from &amp;lt;code&amp;gt;/dev/superpipe&amp;lt;/code&amp;gt; (normal &amp;lt;code&amp;gt;read()&amp;lt;/code&amp;gt; syscall)&lt;br /&gt;
# Your kernel module returns the transformed data&lt;br /&gt;
# Userspace configures the transformation mode using &amp;lt;code&amp;gt;ioctl()&amp;lt;/code&amp;gt; (passing configuration structures)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example usage:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Set transformation to uppercase via ioctl&lt;br /&gt;
./test_superpipe set_mode upper&lt;br /&gt;
&lt;br /&gt;
# Write lowercase text&lt;br /&gt;
echo &amp;quot;hello world&amp;quot; &amp;gt; /dev/superpipe&lt;br /&gt;
&lt;br /&gt;
# Read back - gets uppercase version&lt;br /&gt;
cat /dev/superpipe&lt;br /&gt;
# Output: HELLO WORLD&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This demonstrates core kernel programming concepts:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Device registration:&amp;#039;&amp;#039;&amp;#039; Creating a device file that appears in &amp;lt;code&amp;gt;/dev/&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;File operations:&amp;#039;&amp;#039;&amp;#039; Implementing the kernel side of open, read, write, close&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Memory management:&amp;#039;&amp;#039;&amp;#039; Allocating kernel buffers with &amp;lt;code&amp;gt;kmalloc()&amp;lt;/code&amp;gt;, using &amp;lt;code&amp;gt;kfree()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data transfer:&amp;#039;&amp;#039;&amp;#039; Safely moving data between user and kernel space&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Synchronization:&amp;#039;&amp;#039;&amp;#039; Using mutexes to protect shared data from concurrent access&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ioctl interface:&amp;#039;&amp;#039;&amp;#039; Passing configuration structures for device-specific operations&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Transform-on-write:&amp;#039;&amp;#039;&amp;#039; Applying byte transformations as data enters the kernel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Operating System:&amp;#039;&amp;#039;&amp;#039; Linux-based system with kernel 5.x or 6.x&lt;br /&gt;
&lt;br /&gt;
Check your kernel version:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;uname -r&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If you&amp;#039;re running kernel 4.x or older, you may encounter compatibility issues with some kernel APIs used in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Kernel Headers:&amp;#039;&amp;#039;&amp;#039; You must have kernel headers installed that match your running kernel. The headers provide the necessary definitions and build infrastructure for compiling kernel modules.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Build Tools:&amp;#039;&amp;#039;&amp;#039; GCC compiler and make utility for building the module.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Privileges:&amp;#039;&amp;#039;&amp;#039; Root access via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; is required for loading/unloading modules and accessing device files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Disk Space:&amp;#039;&amp;#039;&amp;#039; At least 500MB free for kernel headers and build artifacts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;IMPORTANT: Work in a Virtual Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Kernel programming is different from userspace programming. Bugs in kernel code can cause kernel panics, system freezes, or data corruption. A simple null pointer dereference that would segfault in userspace can crash your entire system in kernel space.&lt;br /&gt;
&lt;br /&gt;
Therefore, it is &amp;#039;&amp;#039;&amp;#039;strongly recommended&amp;#039;&amp;#039;&amp;#039; to work in a virtual machine (VirtualBox, VMware, QEMU, or similar). This way, if your module crashes the kernel, you can simply restart the VM without affecting your host system.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
Install the necessary packages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    build-essential \&lt;br /&gt;
    linux-headers-$(uname -r) \&lt;br /&gt;
    vim&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Package descriptions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;build-essential&amp;lt;/code&amp;gt;: Meta-package that installs GCC, make, and other compilation tools&lt;br /&gt;
* &amp;lt;code&amp;gt;linux-headers-$(uname -r)&amp;lt;/code&amp;gt;: Kernel header files matching your running kernel version&lt;br /&gt;
* &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt;: Text editor (or use your preferred editor)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify kernel headers installation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -l /lib/modules/$(uname -r)/build&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This should show a symbolic link pointing to the kernel build directory (usually in &amp;lt;code&amp;gt;/usr/src/&amp;lt;/code&amp;gt;). If this directory doesn&amp;#039;t exist, your kernel headers are not properly installed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-modules-extending-the-kernel-dynamically&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel Modules: Extending the Kernel Dynamically ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-a-kernel-module&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== What is a Kernel Module? ====&lt;br /&gt;
&lt;br /&gt;
The Linux kernel is the core of the operating system—it manages hardware, enforces security, schedules processes, and provides the abstractions (files, processes, sockets) that userspace programs rely on. But the kernel faces a design challenge: it must support thousands of different hardware devices, filesystems, network protocols, and features. If every possible driver and feature were compiled directly into the kernel, the kernel image would be enormous, and systems would waste memory loading features they never use.&lt;br /&gt;
&lt;br /&gt;
Linux solves this with &amp;#039;&amp;#039;&amp;#039;loadable kernel modules&amp;#039;&amp;#039;&amp;#039;—pieces of code that can be dynamically loaded into the running kernel and unloaded when no longer needed. Think of modules as plugins for the kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits of kernel modules:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flexibility:&amp;#039;&amp;#039;&amp;#039; Load only the drivers and features you actually need&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Development speed:&amp;#039;&amp;#039;&amp;#039; Test kernel code without rebooting&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modularity:&amp;#039;&amp;#039;&amp;#039; Separate concerns (network drivers, filesystem support, etc.) into independent modules&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Distribution:&amp;#039;&amp;#039;&amp;#039; Ship proprietary drivers separately from the GPL-licensed kernel&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Memory efficiency:&amp;#039;&amp;#039;&amp;#039; Don&amp;#039;t waste RAM on unused features&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Examples of kernel modules:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;lsmod&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command lists currently loaded modules. You&amp;#039;ll see entries like:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nvidia&amp;lt;/code&amp;gt; - NVIDIA graphics driver&lt;br /&gt;
* &amp;lt;code&amp;gt;ext4&amp;lt;/code&amp;gt; - ext4 filesystem support&lt;br /&gt;
* &amp;lt;code&amp;gt;iwlwifi&amp;lt;/code&amp;gt; - Intel wireless driver&lt;br /&gt;
* &amp;lt;code&amp;gt;bluetooth&amp;lt;/code&amp;gt; - Bluetooth protocol support&lt;br /&gt;
&lt;br /&gt;
Each of these started as a &amp;lt;code&amp;gt;.ko&amp;lt;/code&amp;gt; file (kernel object) on disk and was loaded into the running kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;module-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Module Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
A kernel module progresses through a well-defined lifecycle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────────────┐&lt;br /&gt;
│   Module Source     │  module.c (your code)&lt;br /&gt;
│      (.c file)      │&lt;br /&gt;
└──────────┬──────────┘&lt;br /&gt;
           │ Compilation (make)&lt;br /&gt;
           ↓&lt;br /&gt;
┌─────────────────────┐&lt;br /&gt;
│   Module Binary     │  module.ko (kernel object)&lt;br /&gt;
│      (.ko file)     │  Lives on disk&lt;br /&gt;
└──────────┬──────────┘&lt;br /&gt;
           │ insmod / modprobe&lt;br /&gt;
           ↓&lt;br /&gt;
┌─────────────────────┐&lt;br /&gt;
│   Init Function     │  module_init() runs&lt;br /&gt;
│      Runs Once      │  Allocates resources&lt;br /&gt;
│                     │  Registers device&lt;br /&gt;
└──────────┬──────────┘&lt;br /&gt;
           │ Success&lt;br /&gt;
           ↓&lt;br /&gt;
┌─────────────────────┐&lt;br /&gt;
│   Module Loaded     │  Module is active&lt;br /&gt;
│   Functions Callable│  Handlers installed&lt;br /&gt;
│                     │  Device available&lt;br /&gt;
└──────────┬──────────┘&lt;br /&gt;
           │ rmmod&lt;br /&gt;
           ↓&lt;br /&gt;
┌─────────────────────┐&lt;br /&gt;
│   Exit Function     │  module_exit() runs&lt;br /&gt;
│      Runs Once      │  Frees resources&lt;br /&gt;
│                     │  Unregisters device&lt;br /&gt;
└─────────────────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key commands:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;insmod&amp;#039;&amp;#039;&amp;#039;: Insert module (basic loader, requires full path)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;rmmod&amp;#039;&amp;#039;&amp;#039;: Remove module (must not be in use)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;lsmod&amp;#039;&amp;#039;&amp;#039;: List loaded modules&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;modprobe&amp;#039;&amp;#039;&amp;#039;: Smart loader (resolves dependencies, searches standard paths)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;modinfo&amp;#039;&amp;#039;&amp;#039;: Display module information&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Compile module&lt;br /&gt;
make&lt;br /&gt;
&lt;br /&gt;
# Load module&lt;br /&gt;
sudo insmod superpipe.ko&lt;br /&gt;
&lt;br /&gt;
# Verify loaded&lt;br /&gt;
lsmod | grep superpipe&lt;br /&gt;
&lt;br /&gt;
# View module info&lt;br /&gt;
modinfo superpipe.ko&lt;br /&gt;
&lt;br /&gt;
# Unload module&lt;br /&gt;
sudo rmmod superpipe&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-space-vs-user-space&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Kernel Space vs. User Space ====&lt;br /&gt;
&lt;br /&gt;
Modern operating systems divide memory into two distinct regions with different privilege levels:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;User Space:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Where normal applications run&lt;br /&gt;
* Limited privileges&lt;br /&gt;
* Cannot access hardware directly&lt;br /&gt;
* Cannot access other processes&amp;#039; memory&lt;br /&gt;
* Errors cause process to crash (segmentation fault), not system crash&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Kernel Space:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Where kernel code runs (including your module!)&lt;br /&gt;
* Full privileges (can access all memory, all hardware)&lt;br /&gt;
* Can execute privileged CPU instructions&lt;br /&gt;
* Errors can crash the entire system (kernel panic)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────────────────────────────────────┐&lt;br /&gt;
│           User Space                        │&lt;br /&gt;
│                                             │&lt;br /&gt;
│  [Process 1] [Process 2] [Process 3] ...   │&lt;br /&gt;
│                                             │&lt;br /&gt;
│  Limited privileges                         │&lt;br /&gt;
│  Protected memory                           │&lt;br /&gt;
│  Crash = segfault (only that process dies)  │&lt;br /&gt;
│                                             │&lt;br /&gt;
└─────────────────┬───────────────────────────┘&lt;br /&gt;
                  │ System Call Interface&lt;br /&gt;
                  │ (open, read, write, ioctl, ...)&lt;br /&gt;
┌─────────────────▼───────────────────────────┐&lt;br /&gt;
│           Kernel Space                      │&lt;br /&gt;
│                                             │&lt;br /&gt;
│  [Kernel Core] [Your Module] [Drivers] ... │&lt;br /&gt;
│                                             │&lt;br /&gt;
│  Full privileges                            │&lt;br /&gt;
│  Access to all memory                       │&lt;br /&gt;
│  Crash = kernel panic (entire system down)  │&lt;br /&gt;
│                                             │&lt;br /&gt;
└─────────────────────────────────────────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical differences for kernel programming:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Aspect&lt;br /&gt;
! User Space&lt;br /&gt;
! Kernel Space&lt;br /&gt;
|-&lt;br /&gt;
| Memory allocation&lt;br /&gt;
| &amp;lt;code&amp;gt;malloc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;free()&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;kmalloc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kfree()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Printing&lt;br /&gt;
| &amp;lt;code&amp;gt;printf()&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;printk()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Standard library&lt;br /&gt;
| Full libc available&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;No libc!&amp;#039;&amp;#039;&amp;#039; Only kernel functions&lt;br /&gt;
|-&lt;br /&gt;
| Floating point&lt;br /&gt;
| Allowed&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Forbidden&amp;#039;&amp;#039;&amp;#039; (saves/restores FPU state manually)&lt;br /&gt;
|-&lt;br /&gt;
| Sleep/wait&lt;br /&gt;
| Can block freely&lt;br /&gt;
| Must use special functions&lt;br /&gt;
|-&lt;br /&gt;
| Error handling&lt;br /&gt;
| Program crashes&lt;br /&gt;
| System crashes&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why these restrictions?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The kernel is responsible for managing all system resources and mediating between competing processes. It cannot rely on any userspace code or libraries because it IS the foundation that everything else relies on. Additionally, the kernel must be extremely efficient—it gets invoked millions of times per second.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;character-devices-and-the-miscdevice-framework&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Character Devices and the Miscdevice Framework ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;device-files-in-dev&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Device Files in /dev/ ====&lt;br /&gt;
&lt;br /&gt;
In Unix philosophy, &amp;amp;quot;everything is a file.&amp;amp;quot; This includes hardware devices. The &amp;lt;code&amp;gt;/dev/&amp;lt;/code&amp;gt; directory contains special files that represent devices—when you read from or write to these files, you&amp;#039;re actually communicating with hardware or kernel drivers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Examine your system:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -l /dev/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You&amp;#039;ll see entries like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;crw-rw-rw- 1 root root    1,   3 Dec 19 10:00 null&lt;br /&gt;
crw-rw-rw- 1 root root    1,   5 Dec 19 10:00 zero&lt;br /&gt;
crw-rw-rw- 1 root root    1,   8 Dec 19 10:00 random&lt;br /&gt;
crw-rw-rw- 1 root root    1,   9 Dec 19 10:00 urandom&lt;br /&gt;
brw-rw---- 1 root disk    8,   0 Dec 19 10:00 sda&lt;br /&gt;
crw------- 1 root root   10,  58 Dec 19 10:00 hw_random&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Decoding the first character:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; = character device&lt;br /&gt;
* &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; = block device&lt;br /&gt;
* &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; = symbolic link&lt;br /&gt;
* &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; = directory&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;character-devices-vs-block-devices&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Character Devices vs. Block Devices ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Character devices&amp;#039;&amp;#039;&amp;#039; (c):&lt;br /&gt;
&lt;br /&gt;
* Data accessed as a stream of bytes&lt;br /&gt;
* No random access (typically)&lt;br /&gt;
* Examples: serial ports (&amp;lt;code&amp;gt;/dev/ttyS0&amp;lt;/code&amp;gt;), terminals (&amp;lt;code&amp;gt;/dev/tty&amp;lt;/code&amp;gt;), random number generator (&amp;lt;code&amp;gt;/dev/random&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Used for devices where data flows sequentially&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Block devices&amp;#039;&amp;#039;&amp;#039; (b):&lt;br /&gt;
&lt;br /&gt;
* Data accessed in fixed-size blocks (typically 512 bytes, 1kB or 4kB)&lt;br /&gt;
* Random access supported&lt;br /&gt;
* Examples: solid-state drives (&amp;lt;code&amp;gt;/dev/nvme0n1&amp;lt;/code&amp;gt;), hard drives (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;), USB drives (&amp;lt;code&amp;gt;/dev/sdb&amp;lt;/code&amp;gt;), loop devices&lt;br /&gt;
* Used for storage devices with addressable blocks&lt;br /&gt;
&lt;br /&gt;
Our superpipe device is a &amp;#039;&amp;#039;&amp;#039;character device&amp;#039;&amp;#039;&amp;#039; because it operates on byte streams without random access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;major-and-minor-numbers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Major and Minor Numbers ====&lt;br /&gt;
&lt;br /&gt;
Every device file has two numbers associated with it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;crw-rw-rw- 1 root root    1,   3 Dec 19 10:00 null&lt;br /&gt;
                          ↑    ↑&lt;br /&gt;
                       Major  Minor&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Major number:&amp;#039;&amp;#039;&amp;#039; Identifies the driver&lt;br /&gt;
&lt;br /&gt;
* The kernel uses this to route operations to the correct driver&lt;br /&gt;
* Example: Major 1 = memory devices (&amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/zero&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/random&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Example: Major 8 = SCSI disk devices (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/sdb&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Minor number:&amp;#039;&amp;#039;&amp;#039; Identifies specific device instance within that driver&lt;br /&gt;
&lt;br /&gt;
* Interpreted by the driver, not the kernel&lt;br /&gt;
* Example: &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; (8,0), &amp;lt;code&amp;gt;/dev/sda1&amp;lt;/code&amp;gt; (8,1), &amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt; (8,2)&lt;br /&gt;
* The SCSI driver uses minor number to distinguish between disks and partitions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-miscdevice-framework&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Miscdevice Framework ====&lt;br /&gt;
&lt;br /&gt;
Creating a character device traditionally requires:&lt;br /&gt;
&lt;br /&gt;
# Registering a major number with &amp;lt;code&amp;gt;register_chrdev()&amp;lt;/code&amp;gt;&lt;br /&gt;
# Implementing a full character device structure&lt;br /&gt;
# Handling device creation in &amp;lt;code&amp;gt;/dev/&amp;lt;/code&amp;gt; (or relying on udev)&lt;br /&gt;
&lt;br /&gt;
This is tedious for simple devices. The &amp;#039;&amp;#039;&amp;#039;miscdevice&amp;#039;&amp;#039;&amp;#039; framework simplifies this:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automatic major number:&amp;#039;&amp;#039;&amp;#039; All miscdevices share major number 10&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic minor allocation:&amp;#039;&amp;#039;&amp;#039; Kernel assigns minor numbers automatically&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Simplified registration:&amp;#039;&amp;#039;&amp;#039; Single function call to register&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automatic /dev/ creation:&amp;#039;&amp;#039;&amp;#039; Device appears automatically when registered&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Perfect for:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Simple character devices&lt;br /&gt;
* Pseudo-devices (devices not tied to hardware)&lt;br /&gt;
* Control interfaces&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The miscdevice structure:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/miscdevice.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct miscdevice {&lt;br /&gt;
    int minor;                              /* Minor number (or MISC_DYNAMIC_MINOR) */&lt;br /&gt;
    const char *name;                       /* Device name (creates /dev/&amp;lt;name&amp;gt;) */&lt;br /&gt;
    const struct file_operations *fops;     /* File operations */&lt;br /&gt;
    /* ... other fields ... */&lt;br /&gt;
};&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using miscdevice:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static struct miscdevice my_device = {&lt;br /&gt;
    .minor = MISC_DYNAMIC_MINOR,    /* Kernel assigns minor number */&lt;br /&gt;
    .name = &amp;quot;mydevice&amp;quot;,             /* Creates /dev/mydevice */&lt;br /&gt;
    .fops = &amp;amp;my_fops,               /* Your file operations */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* In module init */&lt;br /&gt;
misc_register(&amp;amp;my_device);          /* Register device */&lt;br /&gt;
&lt;br /&gt;
/* In module exit */&lt;br /&gt;
misc_deregister(&amp;amp;my_device);        /* Unregister device */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
That&amp;#039;s it! The device appears in &amp;lt;code&amp;gt;/dev/mydevice&amp;lt;/code&amp;gt; and is ready to use.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;file-operations-the-kernel-side-of-system-calls&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== File Operations: The Kernel Side of System Calls ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-file_operations-structure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The file_operations Structure ====&lt;br /&gt;
&lt;br /&gt;
When userspace makes system calls like &amp;lt;code&amp;gt;open()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;read()&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;write()&amp;lt;/code&amp;gt; on your device file, the kernel needs to know which functions in your module to call. This mapping is provided by the &amp;lt;code&amp;gt;file_operations&amp;lt;/code&amp;gt; structure.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The structure (simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/fs.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct file_operations {&lt;br /&gt;
    struct module *owner;       /* Pointer to module (prevents unload while in use) */&lt;br /&gt;
    &lt;br /&gt;
    /* Open/close */&lt;br /&gt;
    int (*open)(struct inode *, struct file *);&lt;br /&gt;
    int (*release)(struct inode *, struct file *);&lt;br /&gt;
    &lt;br /&gt;
    /* Read/write */&lt;br /&gt;
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);&lt;br /&gt;
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);&lt;br /&gt;
    &lt;br /&gt;
    /* ioctl */&lt;br /&gt;
    long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);&lt;br /&gt;
    &lt;br /&gt;
    /* ... many more operations (lseek, poll, mmap, etc.) ... */&lt;br /&gt;
};&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static const struct file_operations my_fops = {&lt;br /&gt;
    .owner = THIS_MODULE,&lt;br /&gt;
    .open = my_open,&lt;br /&gt;
    .release = my_release,&lt;br /&gt;
    .read = my_read,&lt;br /&gt;
    .write = my_write,&lt;br /&gt;
    .unlocked_ioctl = my_ioctl,&lt;br /&gt;
};&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You implement only the operations your device supports. Operations you don&amp;#039;t implement default to appropriate behavior (typically returning -EINVAL or -ENOSYS).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-handlers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Handlers ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;open handler:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Called when userspace opens your device file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static int my_open(struct inode *inode, struct file *file)&lt;br /&gt;
{&lt;br /&gt;
    /* Allocate per-file state, increment usage counters, etc. */&lt;br /&gt;
    printk(KERN_INFO &amp;quot;Device opened\n&amp;quot;);&lt;br /&gt;
    return 0;  /* Success */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;inode&amp;lt;/code&amp;gt;: Represents the device file on disk (contains major/minor numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;: Represents an open file descriptor (contains file offset, flags, private_data)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;release handler:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Called when userspace closes your device file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static int my_release(struct inode *inode, struct file *file)&lt;br /&gt;
{&lt;br /&gt;
    /* Free resources, decrement counters, etc. */&lt;br /&gt;
    printk(KERN_INFO &amp;quot;Device closed\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Called &amp;amp;quot;release&amp;amp;quot; not &amp;amp;quot;close&amp;amp;quot; because multiple file descriptors can point to the same open file (via &amp;lt;code&amp;gt;dup()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;fork()&amp;lt;/code&amp;gt;). Release is called when the last reference is closed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;read handler:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Called when userspace reads from your device.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static ssize_t my_read(struct file *file, char __user *buffer,&lt;br /&gt;
                       size_t count, loff_t *offset)&lt;br /&gt;
{&lt;br /&gt;
    /* Copy data from kernel space to userspace buffer */&lt;br /&gt;
    /* Return number of bytes read, 0 for EOF, negative for error */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;: The open file&lt;br /&gt;
* &amp;lt;code&amp;gt;buffer&amp;lt;/code&amp;gt;: &amp;#039;&amp;#039;&amp;#039;Userspace&amp;#039;&amp;#039;&amp;#039; buffer (marked with &amp;lt;code&amp;gt;__user&amp;lt;/code&amp;gt; annotation)&lt;br /&gt;
* &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: Number of bytes requested&lt;br /&gt;
* &amp;lt;code&amp;gt;offset&amp;lt;/code&amp;gt;: File offset (can be updated)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return value:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Positive: Number of bytes successfully read&lt;br /&gt;
* 0: End of file&lt;br /&gt;
* Negative: Error code&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;write handler:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Called when userspace writes to your device.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static ssize_t my_write(struct file *file, const char __user *buffer,&lt;br /&gt;
                        size_t count, loff_t *offset)&lt;br /&gt;
{&lt;br /&gt;
    /* Copy data from userspace buffer to kernel space */&lt;br /&gt;
    /* Return number of bytes written, negative for error */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;unlocked_ioctl handler:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Called when userspace invokes ioctl() for device-specific operations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg)&lt;br /&gt;
{&lt;br /&gt;
    /* Handle device-specific commands */&lt;br /&gt;
    /* &amp;#039;arg&amp;#039; can be a value or a pointer to userspace memory */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
We&amp;#039;ll explore ioctl in detail in section 4.6.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;moving-data-between-kernel-and-user-space&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Moving Data Between Kernel and User Space ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;why-we-cant-access-user-memory-directly&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Why We Can&amp;#039;t Access User Memory Directly ====&lt;br /&gt;
&lt;br /&gt;
This is one of the most critical concepts in kernel programming and a common source of bugs for beginners.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In userspace, when you have a pointer, you can just dereference it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;char *str = &amp;quot;hello&amp;quot;;&lt;br /&gt;
printf(&amp;quot;%c&amp;quot;, str[0]);  /* Works fine in userspace */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In kernel space, you &amp;#039;&amp;#039;&amp;#039;cannot&amp;#039;&amp;#039;&amp;#039; do this with pointers to userspace memory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* WRONG - will crash the kernel! */&lt;br /&gt;
static ssize_t my_read(struct file *file, char __user *user_buffer, ...)&lt;br /&gt;
{&lt;br /&gt;
    user_buffer[0] = &amp;#039;A&amp;#039;;  /* NEVER DO THIS! */&lt;br /&gt;
    strcpy(user_buffer, kernel_data);  /* NEVER DO THIS! */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why not?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Memory protection:&amp;#039;&amp;#039;&amp;#039; User memory might not be mapped, might be swapped to disk, or might not exist at all (bad pointer)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Page faults:&amp;#039;&amp;#039;&amp;#039; Accessing user memory can cause page faults, which need special handling in kernel context&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Security:&amp;#039;&amp;#039;&amp;#039; The kernel must validate that the user actually has permission to access that memory&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;MMU (Memory Management Unit):&amp;#039;&amp;#039;&amp;#039; The CPU&amp;#039;s MMU enforces that kernel code cannot directly access user address space&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens if you try?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In the best case, you get a kernel oops (non-fatal kernel error). In the worst case, you corrupt memory and cause a kernel panic (crash).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-safe-way-copy_to_user-and-copy_from_user&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Safe Way: copy_to_user and copy_from_user ====&lt;br /&gt;
&lt;br /&gt;
The kernel provides safe functions for transferring data between kernel and user space:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;copy_to_user:&amp;#039;&amp;#039;&amp;#039; Copy from kernel to userspace&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/uaccess.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt;: Destination in userspace (marked with &amp;lt;code&amp;gt;__user&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;from&amp;lt;/code&amp;gt;: Source in kernel space&lt;br /&gt;
* &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;: Number of bytes to copy&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return value:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 0: Success (all bytes copied)&lt;br /&gt;
* Non-zero: Number of bytes that could NOT be copied (failure)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;char kernel_buffer[100] = &amp;quot;Hello from kernel&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if (copy_to_user(user_buffer, kernel_buffer, strlen(kernel_buffer))) {&lt;br /&gt;
    return -EFAULT;  /* Bad address - tell userspace */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;copy_from_user:&amp;#039;&amp;#039;&amp;#039; Copy from userspace to kernel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt;: Destination in kernel space&lt;br /&gt;
* &amp;lt;code&amp;gt;from&amp;lt;/code&amp;gt;: Source in userspace (marked with &amp;lt;code&amp;gt;__user&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;: Number of bytes to copy&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return value:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 0: Success&lt;br /&gt;
* Non-zero: Number of bytes that could NOT be copied&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;char kernel_buffer[100];&lt;br /&gt;
&lt;br /&gt;
if (copy_from_user(kernel_buffer, user_buffer, count)) {&lt;br /&gt;
    return -EFAULT;  /* Bad address */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What these functions do:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Check that the user address is valid&lt;br /&gt;
# Check that the user has permission to access that memory&lt;br /&gt;
# Handle page faults if the memory is swapped out&lt;br /&gt;
# Actually perform the copy&lt;br /&gt;
# Return success/failure&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The __user annotation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;__user&amp;lt;/code&amp;gt; marker is a sparse (static analysis tool) annotation that helps catch bugs at compile time. It doesn&amp;#039;t affect the generated code, but it helps ensure you&amp;#039;re using the right functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;error-codes-communicating-failures&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Error Codes: Communicating Failures ====&lt;br /&gt;
&lt;br /&gt;
When something goes wrong in your kernel module, you communicate this to userspace by returning negative error codes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common error codes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Error Code&lt;br /&gt;
! Value&lt;br /&gt;
! Meaning&lt;br /&gt;
! When to use&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-EFAULT&amp;lt;/code&amp;gt;&lt;br /&gt;
| -14&lt;br /&gt;
| Bad address&lt;br /&gt;
| copy_to_user/copy_from_user failed&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;&lt;br /&gt;
| -22&lt;br /&gt;
| Invalid argument&lt;br /&gt;
| Invalid ioctl command, bad parameter&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-ENOMEM&amp;lt;/code&amp;gt;&lt;br /&gt;
| -12&lt;br /&gt;
| Out of memory&lt;br /&gt;
| kmalloc() failed&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-ENOSPC&amp;lt;/code&amp;gt;&lt;br /&gt;
| -28&lt;br /&gt;
| No space left&lt;br /&gt;
| Device buffer full&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-ENODEV&amp;lt;/code&amp;gt;&lt;br /&gt;
| -19&lt;br /&gt;
| No such device&lt;br /&gt;
| Device not found/initialized&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-EBUSY&amp;lt;/code&amp;gt;&lt;br /&gt;
| -16&lt;br /&gt;
| Device busy&lt;br /&gt;
| Resource in use&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-EIO&amp;lt;/code&amp;gt;&lt;br /&gt;
| -5&lt;br /&gt;
| I/O error&lt;br /&gt;
| Hardware error&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How to use:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;if (!buffer) {&lt;br /&gt;
    return -ENOMEM;  /* Allocation failed */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (copy_from_user(buffer, user_ptr, len)) {&lt;br /&gt;
    return -EFAULT;  /* Bad pointer from userspace */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if (cmd != VALID_COMMAND) {&lt;br /&gt;
    return -EINVAL;  /* Invalid command */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Userspace sees these as errno values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Userspace code */&lt;br /&gt;
if (write(fd, data, len) &amp;lt; 0) {&lt;br /&gt;
    perror(&amp;quot;write&amp;quot;);  /* Prints &amp;quot;write: Bad address&amp;quot; for -EFAULT */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;synchronization-protecting-shared-data&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Synchronization: Protecting Shared Data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-concurrency-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Concurrency Problem ====&lt;br /&gt;
&lt;br /&gt;
Your kernel module can be accessed by multiple processes simultaneously. Consider this scenario:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Time  Process A              Process B&lt;br /&gt;
----  ---------------------  ---------------------&lt;br /&gt;
  1   open(&amp;amp;quot;/dev/superpipe&amp;amp;quot;)&lt;br /&gt;
  2   write(&amp;amp;quot;hello&amp;amp;quot;)         open(&amp;amp;quot;/dev/superpipe&amp;amp;quot;)&lt;br /&gt;
  3   [writing to buffer]    write(&amp;amp;quot;world&amp;amp;quot;)&lt;br /&gt;
  4   [writing to buffer]    [writing to buffer]  ← RACE!&lt;br /&gt;
  5                          [writing to buffer]  ← DATA CORRUPTION!&amp;lt;/pre&amp;gt;&lt;br /&gt;
Without synchronization, both processes could write to the buffer simultaneously, resulting in garbled data, corrupted state, or kernel crashes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Types of concurrency:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multiple processes:&amp;#039;&amp;#039;&amp;#039; Different processes calling your module&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multiple threads:&amp;#039;&amp;#039;&amp;#039; Threads in the same process&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Interrupts:&amp;#039;&amp;#039;&amp;#039; Interrupt handlers can preempt your code&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multiple CPUs:&amp;#039;&amp;#039;&amp;#039; Code running simultaneously on different CPU cores&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The shared state problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Global device state - shared by all processes */&lt;br /&gt;
static struct {&lt;br /&gt;
    char buffer[4096];&lt;br /&gt;
    size_t len;&lt;br /&gt;
} dev_state;&lt;br /&gt;
&lt;br /&gt;
/* UNSAFE - race condition! */&lt;br /&gt;
static ssize_t unsafe_write(...) {&lt;br /&gt;
    /* What if another process modifies dev_state.len right here? */&lt;br /&gt;
    memcpy(&amp;amp;dev_state.buffer[dev_state.len], data, count);&lt;br /&gt;
    dev_state.len += count;  /* Race! Another write could have happened */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;mutexes-in-the-kernel&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Mutexes in the Kernel ====&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;mutex&amp;#039;&amp;#039;&amp;#039; (mutual exclusion lock) ensures that only one thread can access a critical section at a time.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Declaring a mutex:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/mutex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
static DEFINE_MUTEX(my_mutex);  /* Declares and initializes */&lt;br /&gt;
&lt;br /&gt;
/* Or for dynamic initialization: */&lt;br /&gt;
struct mutex my_mutex;&lt;br /&gt;
mutex_init(&amp;amp;my_mutex);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using a mutex:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Acquire lock - will sleep if already held */&lt;br /&gt;
mutex_lock(&amp;amp;my_mutex);&lt;br /&gt;
&lt;br /&gt;
/* Critical section - only one thread at a time */&lt;br /&gt;
/* Access shared data here */&lt;br /&gt;
&lt;br /&gt;
/* Release lock */&lt;br /&gt;
mutex_unlock(&amp;amp;my_mutex);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Interruptible locking:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In syscall handlers (read, write, ioctl), use the interruptible version so users can interrupt with Ctrl+C:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Try to acquire lock, but allow signals to interrupt */&lt;br /&gt;
if (mutex_lock_interruptible(&amp;amp;my_mutex)) {&lt;br /&gt;
    return -ERESTARTSYS;  /* System call was interrupted */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Critical section */&lt;br /&gt;
&lt;br /&gt;
mutex_unlock(&amp;amp;my_mutex);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Safe write example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static DEFINE_MUTEX(device_mutex);&lt;br /&gt;
&lt;br /&gt;
static ssize_t safe_write(struct file *file, const char __user *user_buffer,&lt;br /&gt;
                         size_t count, loff_t *offset)&lt;br /&gt;
{&lt;br /&gt;
    /* Acquire lock */&lt;br /&gt;
    if (mutex_lock_interruptible(&amp;amp;device_mutex))&lt;br /&gt;
        return -ERESTARTSYS;&lt;br /&gt;
    &lt;br /&gt;
    /* Critical section - safe from concurrent access */&lt;br /&gt;
    if (dev_state.len + count &amp;gt; BUFFER_SIZE) {&lt;br /&gt;
        mutex_unlock(&amp;amp;device_mutex);&lt;br /&gt;
        return -ENOSPC;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (copy_from_user(&amp;amp;dev_state.buffer[dev_state.len], user_buffer, count)) {&lt;br /&gt;
        mutex_unlock(&amp;amp;device_mutex);&lt;br /&gt;
        return -EFAULT;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dev_state.len += count;&lt;br /&gt;
    &lt;br /&gt;
    /* Release lock */&lt;br /&gt;
    mutex_unlock(&amp;amp;device_mutex);&lt;br /&gt;
    return count;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Rules for mutex usage:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Always release:&amp;#039;&amp;#039;&amp;#039; Every lock must have a corresponding unlock (even on error paths)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Short critical sections:&amp;#039;&amp;#039;&amp;#039; Hold locks for the minimum time necessary&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;No sleeping with spinlocks:&amp;#039;&amp;#039;&amp;#039; Don&amp;#039;t use mutexes in interrupt context (use spinlocks instead)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Avoid deadlocks:&amp;#039;&amp;#039;&amp;#039; Don&amp;#039;t acquire multiple locks, or always acquire in the same order&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-ioctl-interface-device-specific-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The ioctl Interface: Device-Specific Commands ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-ioctl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== What is ioctl? ====&lt;br /&gt;
&lt;br /&gt;
The standard file operations (&amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;write&amp;lt;/code&amp;gt;) are generic—they work the same way across all devices. But sometimes you need device-specific operations that don&amp;#039;t fit the read/write model:&lt;br /&gt;
&lt;br /&gt;
* Configure a serial port&amp;#039;s baud rate&lt;br /&gt;
* Set video resolution on a graphics device&lt;br /&gt;
* Enable a network interface&lt;br /&gt;
* Set transformation mode on our superpipe device&lt;br /&gt;
&lt;br /&gt;
This is what &amp;lt;code&amp;gt;ioctl()&amp;lt;/code&amp;gt; (input/output control) is for: device-specific commands that go beyond simple data transfer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Userspace signature:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;sys/ioctl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int ioctl(int fd, unsigned long request, ...);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;fd&amp;lt;/code&amp;gt;: File descriptor&lt;br /&gt;
* &amp;lt;code&amp;gt;request&amp;lt;/code&amp;gt;: Command code (defined by driver)&lt;br /&gt;
* &amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;: Optional argument (pointer or value)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example usage:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;int fd = open(&amp;quot;/dev/superpipe&amp;quot;, O_RDWR);&lt;br /&gt;
&lt;br /&gt;
/* Simple command with no data */&lt;br /&gt;
ioctl(fd, SUPERPIPE_IOC_RESET);&lt;br /&gt;
&lt;br /&gt;
/* Command that passes a value */&lt;br /&gt;
int mode = TRANSFORM_UPPER;&lt;br /&gt;
ioctl(fd, SUPERPIPE_IOC_SET_MODE, mode);&lt;br /&gt;
&lt;br /&gt;
/* Command that passes a structure */&lt;br /&gt;
struct superpipe_config cfg = { .mode = TRANSFORM_UPPER };&lt;br /&gt;
ioctl(fd, SUPERPIPE_IOC_SET_CONFIG, &amp;amp;cfg);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;defining-ioctl-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Defining ioctl Commands ====&lt;br /&gt;
&lt;br /&gt;
ioctl commands are unsigned integers, but they&amp;#039;re not arbitrary—they&amp;#039;re constructed using macros that encode information about the command.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command encoding:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;     Direction | Size of data | Magic | Sequence&lt;br /&gt;
   ____________|______________|_______|__________&lt;br /&gt;
  |    2 bits  |   14 bits    | 8 bits | 8 bits  |&lt;br /&gt;
   ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The macros:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/ioctl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
_IO(magic, number)              /* No data transfer */&lt;br /&gt;
_IOR(magic, number, datatype)   /* Read from device (copy_to_user) */&lt;br /&gt;
_IOW(magic, number, datatype)   /* Write to device (copy_from_user) */&lt;br /&gt;
_IOWR(magic, number, datatype)  /* Both read and write */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parameters:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;magic&amp;lt;/code&amp;gt;: 8-bit magic number (identifies your driver, typically a character)&lt;br /&gt;
* &amp;lt;code&amp;gt;number&amp;lt;/code&amp;gt;: Command number (sequential numbering)&lt;br /&gt;
* &amp;lt;code&amp;gt;datatype&amp;lt;/code&amp;gt;: Type of data being transferred (for size encoding)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#define SUPERPIPE_IOC_MAGIC &amp;#039;k&amp;#039;&lt;br /&gt;
&lt;br /&gt;
/* No data transfer */&lt;br /&gt;
#define SUPERPIPE_IOC_RESET  _IO(SUPERPIPE_IOC_MAGIC, 0)&lt;br /&gt;
&lt;br /&gt;
/* Write an integer to device */&lt;br /&gt;
#define SUPERPIPE_IOC_SET_MODE  _IOW(SUPERPIPE_IOC_MAGIC, 1, int)&lt;br /&gt;
&lt;br /&gt;
/* Read an integer from device */&lt;br /&gt;
#define SUPERPIPE_IOC_GET_MODE  _IOR(SUPERPIPE_IOC_MAGIC, 2, int)&lt;br /&gt;
&lt;br /&gt;
/* Read and write a structure */&lt;br /&gt;
#define SUPERPIPE_IOC_CONFIG  _IOWR(SUPERPIPE_IOC_MAGIC, 3, struct superpipe_config)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why use macros instead of simple numbers?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Type safety:&amp;#039;&amp;#039;&amp;#039; Encodes the size of the data being transferred&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Direction:&amp;#039;&amp;#039;&amp;#039; Indicates whether data flows to/from device&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Collision avoidance:&amp;#039;&amp;#039;&amp;#039; Magic number helps avoid conflicts with other drivers&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Self-documenting:&amp;#039;&amp;#039;&amp;#039; The macro tells you what the command does&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;passing-structures-with-ioctl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Passing Structures with ioctl ====&lt;br /&gt;
&lt;br /&gt;
For complex configuration, passing structures is more flexible than passing simple values.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Define the structure (in UAPI header, shared between kernel and userspace):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* superpipe_uapi.h */&lt;br /&gt;
struct superpipe_ioctl_config {&lt;br /&gt;
    __u32 mode;      /* Transformation mode */&lt;br /&gt;
    __u32 flags;     /* Additional flags (for future expansion) */&lt;br /&gt;
};&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Use &amp;lt;code&amp;gt;__u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;__u64&amp;lt;/code&amp;gt;, etc. (double underscore types) in UAPI headers. These are guaranteed to have the same size in both 32-bit and 64-bit architectures.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Define the ioctl command:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#define SUPERPIPE_IOC_SET_CONFIG  _IOW(SUPERPIPE_IOC_MAGIC, 1, struct superpipe_ioctl_config)&lt;br /&gt;
#define SUPERPIPE_IOC_GET_CONFIG  _IOR(SUPERPIPE_IOC_MAGIC, 2, struct superpipe_ioctl_config)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Userspace usage:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;quot;superpipe_uapi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int fd = open(&amp;quot;/dev/superpipe&amp;quot;, O_RDWR);&lt;br /&gt;
&lt;br /&gt;
struct superpipe_ioctl_config cfg = {&lt;br /&gt;
    .mode = TRANSFORM_UPPER,&lt;br /&gt;
    .flags = 0,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
if (ioctl(fd, SUPERPIPE_IOC_SET_CONFIG, &amp;amp;cfg) &amp;lt; 0) {&lt;br /&gt;
    perror(&amp;quot;ioctl&amp;quot;);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Kernel implementation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static long superpipe_ioctl(struct file *file, unsigned int cmd, unsigned long arg)&lt;br /&gt;
{&lt;br /&gt;
    struct superpipe_ioctl_config cfg;&lt;br /&gt;
    &lt;br /&gt;
    switch (cmd) {&lt;br /&gt;
    case SUPERPIPE_IOC_SET_CONFIG:&lt;br /&gt;
        /* Copy structure from userspace */&lt;br /&gt;
        if (copy_from_user(&amp;amp;cfg, (void __user *)arg, sizeof(cfg)))&lt;br /&gt;
            return -EFAULT;&lt;br /&gt;
        &lt;br /&gt;
        /* Validate */&lt;br /&gt;
        if (cfg.mode &amp;gt;= TRANSFORM_MAX)&lt;br /&gt;
            return -EINVAL;&lt;br /&gt;
        &lt;br /&gt;
        /* Apply configuration */&lt;br /&gt;
        device_state.mode = cfg.mode;&lt;br /&gt;
        return 0;&lt;br /&gt;
        &lt;br /&gt;
    case SUPERPIPE_IOC_GET_CONFIG:&lt;br /&gt;
        /* Prepare structure */&lt;br /&gt;
        cfg.mode = device_state.mode;&lt;br /&gt;
        cfg.flags = 0;&lt;br /&gt;
        &lt;br /&gt;
        /* Copy to userspace */&lt;br /&gt;
        if (copy_to_user((void __user *)arg, &amp;amp;cfg, sizeof(cfg)))&lt;br /&gt;
            return -EFAULT;&lt;br /&gt;
        &lt;br /&gt;
        return 0;&lt;br /&gt;
        &lt;br /&gt;
    default:&lt;br /&gt;
        return -EINVAL;  /* Unknown command */&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key points:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;arg is a userspace pointer:&amp;#039;&amp;#039;&amp;#039; Must use copy_from_user/copy_to_user&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Validate inputs:&amp;#039;&amp;#039;&amp;#039; Check that values are in valid ranges&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Return 0 on success:&amp;#039;&amp;#039;&amp;#039; Negative error code on failure&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handle unknown commands:&amp;#039;&amp;#039;&amp;#039; Return -EINVAL for commands you don&amp;#039;t recognize&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-uapi-header-sharing-definitions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The UAPI Header: Sharing Definitions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-uapi&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== What is UAPI? ====&lt;br /&gt;
&lt;br /&gt;
UAPI stands for &amp;amp;quot;User API&amp;amp;quot;—definitions that are shared between kernel code and userspace code. In the Linux kernel source tree, these headers live in &amp;lt;code&amp;gt;include/uapi/linux/&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why separate UAPI headers?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Single source of truth:&amp;#039;&amp;#039;&amp;#039; Define ioctl commands, structures, constants once&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Consistency:&amp;#039;&amp;#039;&amp;#039; Kernel and userspace always agree on command values and structure layouts&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Stability:&amp;#039;&amp;#039;&amp;#039; UAPI is part of the kernel&amp;#039;s stable ABI—once defined, it shouldn&amp;#039;t change (backward compatibility)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Clear boundary:&amp;#039;&amp;#039;&amp;#039; Clearly separates internal kernel definitions from external interface&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What goes in a UAPI header?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* ioctl command definitions&lt;br /&gt;
* Structure definitions for data passed between kernel and userspace&lt;br /&gt;
* Constant definitions (modes, flags, limits)&lt;br /&gt;
* Enum definitions used in the interface&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What does NOT go in a UAPI header?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Internal kernel structures&lt;br /&gt;
* Function declarations&lt;br /&gt;
* Kernel-only constants&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Our UAPI header structure:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* superpipe_uapi.h - Shared between kernel and userspace */&lt;br /&gt;
#ifndef _SUPERPIPE_UAPI_H&lt;br /&gt;
#define _SUPERPIPE_UAPI_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;linux/ioctl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/types.h&amp;gt;  /* For __u32, __u64, etc. */&lt;br /&gt;
&lt;br /&gt;
/* Transform modes */&lt;br /&gt;
#define SUPERPIPE_MODE_NONE         0&lt;br /&gt;
#define SUPERPIPE_MODE_UPPER        1&lt;br /&gt;
#define SUPERPIPE_MODE_LOWER        2&lt;br /&gt;
#define SUPERPIPE_MODE_DROP_VOWELS  3&lt;br /&gt;
#define SUPERPIPE_MODE_DROP_SPACES  4&lt;br /&gt;
&lt;br /&gt;
/* Configuration structure */&lt;br /&gt;
struct superpipe_ioctl_config {&lt;br /&gt;
    __u32 mode;      /* One of SUPERPIPE_MODE_* */&lt;br /&gt;
    __u32 flags;     /* Reserved for future use */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* ioctl commands */&lt;br /&gt;
#define SUPERPIPE_IOC_MAGIC &amp;#039;k&amp;#039;&lt;br /&gt;
&lt;br /&gt;
#define SUPERPIPE_IOC_SET_CONFIG  _IOW(SUPERPIPE_IOC_MAGIC, 1, struct superpipe_ioctl_config)&lt;br /&gt;
#define SUPERPIPE_IOC_GET_CONFIG  _IOR(SUPERPIPE_IOC_MAGIC, 2, struct superpipe_ioctl_config)&lt;br /&gt;
#define SUPERPIPE_IOC_RESET       _IO(SUPERPIPE_IOC_MAGIC, 3)&lt;br /&gt;
&lt;br /&gt;
#endif /* _SUPERPIPE_UAPI_H */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using in kernel code:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* superpipe.c */&lt;br /&gt;
#include &amp;quot;superpipe_uapi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
static long superpipe_ioctl(...)&lt;br /&gt;
{&lt;br /&gt;
    struct superpipe_ioctl_config cfg;&lt;br /&gt;
    &lt;br /&gt;
    switch (cmd) {&lt;br /&gt;
    case SUPERPIPE_IOC_SET_CONFIG:&lt;br /&gt;
        /* Use the definitions from UAPI header */&lt;br /&gt;
        if (copy_from_user(&amp;amp;cfg, ...))&lt;br /&gt;
            return -EFAULT;&lt;br /&gt;
        &lt;br /&gt;
        if (cfg.mode == SUPERPIPE_MODE_UPPER) {&lt;br /&gt;
            /* ... */&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using in userspace code:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* test_superpipe.c */&lt;br /&gt;
#include &amp;quot;superpipe_uapi.h&amp;quot;  /* Same header! */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    struct superpipe_ioctl_config cfg = {&lt;br /&gt;
        .mode = SUPERPIPE_MODE_UPPER,&lt;br /&gt;
        .flags = 0,&lt;br /&gt;
    };&lt;br /&gt;
    &lt;br /&gt;
    ioctl(fd, SUPERPIPE_IOC_SET_CONFIG, &amp;amp;cfg);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Change command codes in one place&lt;br /&gt;
* Add new modes without editing two files&lt;br /&gt;
* Compiler catches mismatches automatically&lt;br /&gt;
* Standard Linux kernel practice&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;design-the-superpipe-device&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Design: The Superpipe Device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;device-behavior&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Device Behavior ===&lt;br /&gt;
&lt;br /&gt;
The superpipe device implements a buffered pipe with configurable byte transformations. Here&amp;#039;s the complete behavior specification:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Basic operations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Write:&amp;#039;&amp;#039;&amp;#039; Userspace writes data → Kernel applies transformation → Stores in buffer&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Read:&amp;#039;&amp;#039;&amp;#039; Kernel copies data from buffer → Returns to userspace → Removes from buffer (consuming read)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ioctl:&amp;#039;&amp;#039;&amp;#039; Userspace sends configuration → Kernel updates transformation mode&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Buffer properties:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Size:&amp;#039;&amp;#039;&amp;#039; 4096 bytes (4KB)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Type:&amp;#039;&amp;#039;&amp;#039; Circular/linear buffer (your choice)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior when full:&amp;#039;&amp;#039;&amp;#039; write() returns -ENOSPC&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior when empty:&amp;#039;&amp;#039;&amp;#039; read() returns 0 (EOF)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transformation timing:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Data is transformed &amp;#039;&amp;#039;&amp;#039;during write&amp;#039;&amp;#039;&amp;#039;, not during read.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example flow:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Time  Action                           Buffer State        Mode&lt;br /&gt;
----  ------------------------------   -----------------   -----------&lt;br /&gt;
  1   ioctl(SET_CONFIG, NONE)          []                  NONE&lt;br /&gt;
  2   write(&amp;amp;quot;Hello&amp;amp;quot;)                   [&amp;amp;quot;Hello&amp;amp;quot;]           NONE&lt;br /&gt;
  3   read() → &amp;amp;quot;Hello&amp;amp;quot;                 []                  NONE&lt;br /&gt;
  4   ioctl(SET_CONFIG, UPPER)         []                  UPPER&lt;br /&gt;
  5   write(&amp;amp;quot;world&amp;amp;quot;)                   [&amp;amp;quot;WORLD&amp;amp;quot;]           UPPER&lt;br /&gt;
  6   read() → &amp;amp;quot;WORLD&amp;amp;quot;                 []                  UPPER&lt;br /&gt;
  7   ioctl(SET_CONFIG, NONE)          []                  NONE&lt;br /&gt;
  8   write(&amp;amp;quot;test&amp;amp;quot;)                    [&amp;amp;quot;test&amp;amp;quot;]            NONE&lt;br /&gt;
  9   ioctl(SET_CONFIG, UPPER)         [&amp;amp;quot;test&amp;amp;quot;]            UPPER (mode change doesn&amp;#039;t affect existing data!)&lt;br /&gt;
 10   write(&amp;amp;quot;NEW&amp;amp;quot;)                     [&amp;amp;quot;testNEW&amp;amp;quot;]         UPPER&lt;br /&gt;
 11   read() → &amp;amp;quot;testNEW&amp;amp;quot;               []                  UPPER&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key insight from example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
At time 9, we change mode to UPPER, but the existing data &amp;amp;quot;test&amp;amp;quot; stays lowercase. Only NEW data (written after the mode change) gets transformed. This is a consequence of transform-on-write: once data is in the buffer, its transformation is &amp;amp;quot;baked in.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Implementation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Pseudo-code for transform-on-write */&lt;br /&gt;
static ssize_t superpipe_write(...)&lt;br /&gt;
{&lt;br /&gt;
    char *write_position;&lt;br /&gt;
    size_t final_len;&lt;br /&gt;
    &lt;br /&gt;
    /* Copy from userspace to buffer */&lt;br /&gt;
    write_position = buffer + buffer_len;&lt;br /&gt;
    copy_from_user(write_position, user_data, count);&lt;br /&gt;
    &lt;br /&gt;
    /* Transform in-place immediately */&lt;br /&gt;
    final_len = count;  /* Start with bytes copied */&lt;br /&gt;
    switch (mode) {&lt;br /&gt;
    case TRANSFORM_UPPER:&lt;br /&gt;
        transform_upper(write_position, count);&lt;br /&gt;
        /* final_len stays count - no compaction */&lt;br /&gt;
        break;&lt;br /&gt;
    case TRANSFORM_DROP_VOWELS:&lt;br /&gt;
        final_len = transform_drop_vowels(write_position, count);&lt;br /&gt;
        /* final_len may be less than count - data compacted */&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Update buffer length by actual stored bytes */&lt;br /&gt;
    buffer_len += final_len;&lt;br /&gt;
    &lt;br /&gt;
    /* Return bytes consumed from user, not bytes stored */&lt;br /&gt;
    return count;  /* Standard UNIX semantics */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note for filtering transforms:&amp;#039;&amp;#039;&amp;#039; When dropping characters (vowels, spaces), the amount of data stored may be less than the amount written. The write() call returns the actual number of bytes stored after filtering.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;device-state-structure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Device State Structure ===&lt;br /&gt;
&lt;br /&gt;
All device state is stored in a single global structure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Transform modes - internal to kernel */&lt;br /&gt;
enum transform_mode {&lt;br /&gt;
    TRANSFORM_NONE = 0,&lt;br /&gt;
    TRANSFORM_UPPER,&lt;br /&gt;
    TRANSFORM_LOWER,&lt;br /&gt;
    TRANSFORM_DROP_VOWELS,&lt;br /&gt;
    TRANSFORM_DROP_SPACES,&lt;br /&gt;
    TRANSFORM_MAX  /* Sentinel for validation */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* Device state */&lt;br /&gt;
struct superpipe_device {&lt;br /&gt;
    char *buffer;              /* Kernel buffer (allocated with kmalloc) */&lt;br /&gt;
    size_t buffer_len;         /* Current amount of data in buffer */&lt;br /&gt;
    struct mutex lock;         /* Protects buffer and mode from concurrent access */&lt;br /&gt;
    enum transform_mode mode;  /* Current transformation mode */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static struct superpipe_device dev_state;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Design notes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Global state:&amp;#039;&amp;#039;&amp;#039; We use a single global structure because we have exactly one device instance (&amp;lt;code&amp;gt;/dev/superpipe&amp;lt;/code&amp;gt;). All opens share the same buffer and transformation mode.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Alternative designs:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Per-file state:&amp;#039;&amp;#039;&amp;#039; Each open() could have independent buffer and mode (use file-&amp;amp;gt;private_data)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Embedded miscdevice:&amp;#039;&amp;#039;&amp;#039; Embed miscdevice in device structure, use container_of() to access&lt;br /&gt;
&lt;br /&gt;
For this lab, global state is simpler and clearer for learning.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Initialization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static int __init superpipe_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Allocate buffer */&lt;br /&gt;
    dev_state.buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);&lt;br /&gt;
    if (!dev_state.buffer)&lt;br /&gt;
        return -ENOMEM;&lt;br /&gt;
    &lt;br /&gt;
    /* Initialize state */&lt;br /&gt;
    dev_state.buffer_len = 0;&lt;br /&gt;
    dev_state.mode = TRANSFORM_NONE;&lt;br /&gt;
    mutex_init(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    /* Register device */&lt;br /&gt;
    return misc_register(&amp;amp;superpipe_miscdev);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cleanup:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static void __exit superpipe_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    misc_deregister(&amp;amp;superpipe_miscdev);&lt;br /&gt;
    kfree(dev_state.buffer);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-implementation-superpipe-with-uppercase-transform&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference Implementation: Superpipe with Uppercase Transform ==&lt;br /&gt;
&lt;br /&gt;
In this section, we present a complete, working kernel module that implements the superpipe device with support for two transformation modes: NONE (pass-through) and UPPER (convert to uppercase). This reference implementation demonstrates all the concepts covered in the theoretical background.&lt;br /&gt;
&lt;br /&gt;
Study this code carefully. The exercise at the end asks you to extend it to support additional transformations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;uapi-header-superpipe_uapih&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== UAPI Header: superpipe_uapi.h ===&lt;br /&gt;
&lt;br /&gt;
This header is shared between kernel code and userspace programs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* superpipe_uapi.h - User API definitions for superpipe device */&lt;br /&gt;
#ifndef _SUPERPIPE_UAPI_H&lt;br /&gt;
#define _SUPERPIPE_UAPI_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;linux/ioctl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Transform modes&lt;br /&gt;
 * These constants identify which transformation to apply&lt;br /&gt;
 */&lt;br /&gt;
#define SUPERPIPE_MODE_NONE   0  /* Pass through unchanged */&lt;br /&gt;
#define SUPERPIPE_MODE_UPPER  1  /* Convert to uppercase */&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Configuration structure&lt;br /&gt;
 * Passed between userspace and kernel via ioctl&lt;br /&gt;
 */&lt;br /&gt;
struct superpipe_ioctl_config {&lt;br /&gt;
    __u32 mode;      /* One of SUPERPIPE_MODE_* constants */&lt;br /&gt;
    __u32 flags;     /* Reserved for future use (set to 0) */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * ioctl commands&lt;br /&gt;
 * Magic number &amp;#039;k&amp;#039; chosen arbitrarily (could be any character)&lt;br /&gt;
 */&lt;br /&gt;
#define SUPERPIPE_IOC_MAGIC &amp;#039;k&amp;#039;&lt;br /&gt;
&lt;br /&gt;
/* Set transformation configuration (write config to device) */&lt;br /&gt;
#define SUPERPIPE_IOC_SET_CONFIG  _IOW(SUPERPIPE_IOC_MAGIC, 1, struct superpipe_ioctl_config)&lt;br /&gt;
&lt;br /&gt;
/* Get current configuration (read config from device) */&lt;br /&gt;
#define SUPERPIPE_IOC_GET_CONFIG  _IOR(SUPERPIPE_IOC_MAGIC, 2, struct superpipe_ioctl_config)&lt;br /&gt;
&lt;br /&gt;
/* Reset device (clear buffer) */&lt;br /&gt;
#define SUPERPIPE_IOC_RESET       _IO(SUPERPIPE_IOC_MAGIC, 3)&lt;br /&gt;
&lt;br /&gt;
#endif /* _SUPERPIPE_UAPI_H */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-module-superpipe_refc&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel Module: superpipe_ref.c ===&lt;br /&gt;
&lt;br /&gt;
This is the complete kernel module implementation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* superpipe_ref.c - Reference implementation of superpipe device */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;linux/module.h&amp;gt;      /* Core module functionality */&lt;br /&gt;
#include &amp;lt;linux/kernel.h&amp;gt;      /* Kernel types and macros */&lt;br /&gt;
#include &amp;lt;linux/init.h&amp;gt;        /* module_init, module_exit */&lt;br /&gt;
#include &amp;lt;linux/miscdevice.h&amp;gt;  /* Miscdevice framework */&lt;br /&gt;
#include &amp;lt;linux/fs.h&amp;gt;          /* File operations structure */&lt;br /&gt;
#include &amp;lt;linux/uaccess.h&amp;gt;     /* copy_to_user, copy_from_user */&lt;br /&gt;
#include &amp;lt;linux/mutex.h&amp;gt;       /* Mutex synchronization */&lt;br /&gt;
#include &amp;lt;linux/slab.h&amp;gt;        /* kmalloc, kfree */&lt;br /&gt;
#include &amp;lt;linux/ctype.h&amp;gt;       /* toupper, tolower */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;superpipe_uapi.h&amp;quot;    /* Shared UAPI definitions */&lt;br /&gt;
&lt;br /&gt;
#define DEVICE_NAME &amp;quot;superpipe&amp;quot;&lt;br /&gt;
#define BUFFER_SIZE 4096&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Internal transform modes&lt;br /&gt;
 * Maps to UAPI constants but kept internal to module&lt;br /&gt;
 */&lt;br /&gt;
enum transform_mode {&lt;br /&gt;
    TRANSFORM_NONE = SUPERPIPE_MODE_NONE,&lt;br /&gt;
    TRANSFORM_UPPER = SUPERPIPE_MODE_UPPER,&lt;br /&gt;
    TRANSFORM_MAX   /* Sentinel for validation */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Device state structure&lt;br /&gt;
 * All state is stored here (single global instance)&lt;br /&gt;
 */&lt;br /&gt;
struct superpipe_device {&lt;br /&gt;
    char *buffer;              /* Dynamically allocated kernel buffer */&lt;br /&gt;
    size_t buffer_len;         /* Current amount of data in buffer */&lt;br /&gt;
    struct mutex lock;         /* Protects all fields from concurrent access */&lt;br /&gt;
    enum transform_mode mode;  /* Current transformation mode */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/* Global device state (single instance for one device) */&lt;br /&gt;
static struct superpipe_device dev_state;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * transform_upper - Apply uppercase transformation in-place&lt;br /&gt;
 * @data: Pointer to data to transform&lt;br /&gt;
 * @len: Length of data in bytes&lt;br /&gt;
 *&lt;br /&gt;
 * Converts all ASCII lowercase letters (a-z) to uppercase (A-Z).&lt;br /&gt;
 * Other characters pass through unchanged.&lt;br /&gt;
 */&lt;br /&gt;
static void transform_upper(char *data, size_t len)&lt;br /&gt;
{&lt;br /&gt;
    size_t i;&lt;br /&gt;
    &lt;br /&gt;
    for (i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
        if (islower(data[i])) {&lt;br /&gt;
            data[i] = toupper(data[i]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_open - Handle device open&lt;br /&gt;
 * @inode: Inode representing the device file&lt;br /&gt;
 * @file: File structure for this open&lt;br /&gt;
 *&lt;br /&gt;
 * Called when userspace opens /dev/superpipe.&lt;br /&gt;
 * We don&amp;#039;t need to do anything special, just log the event.&lt;br /&gt;
 *&lt;br /&gt;
 * Return: 0 on success&lt;br /&gt;
 */&lt;br /&gt;
static int superpipe_open(struct inode *inode, struct file *file)&lt;br /&gt;
{&lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Device opened by process %d (%s)\n&amp;quot;,&lt;br /&gt;
           current-&amp;gt;pid, current-&amp;gt;comm);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_release - Handle device close&lt;br /&gt;
 * @inode: Inode representing the device file&lt;br /&gt;
 * @file: File structure being closed&lt;br /&gt;
 *&lt;br /&gt;
 * Called when userspace closes /dev/superpipe.&lt;br /&gt;
 * Cleanup would go here if we allocated per-file resources.&lt;br /&gt;
 *&lt;br /&gt;
 * Return: 0 on success&lt;br /&gt;
 */&lt;br /&gt;
static int superpipe_release(struct inode *inode, struct file *file)&lt;br /&gt;
{&lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Device closed by process %d (%s)\n&amp;quot;,&lt;br /&gt;
           current-&amp;gt;pid, current-&amp;gt;comm);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_read - Handle read operation&lt;br /&gt;
 * @file: File structure&lt;br /&gt;
 * @user_buffer: Userspace buffer to copy data into&lt;br /&gt;
 * @count: Number of bytes requested&lt;br /&gt;
 * @offset: File offset (unused for character devices)&lt;br /&gt;
 *&lt;br /&gt;
 * Copies data from kernel buffer to userspace.&lt;br /&gt;
 * Data is removed from buffer after reading (consuming read).&lt;br /&gt;
 * With transform-on-write, data is already transformed, so we just copy.&lt;br /&gt;
 *&lt;br /&gt;
 * Return: Number of bytes read, 0 for EOF, negative error code on failure&lt;br /&gt;
 */&lt;br /&gt;
static ssize_t superpipe_read(struct file *file, char __user *user_buffer,&lt;br /&gt;
                              size_t count, loff_t *offset)&lt;br /&gt;
{&lt;br /&gt;
    size_t to_copy;&lt;br /&gt;
    &lt;br /&gt;
    /* Acquire lock - interruptible so Ctrl+C works */&lt;br /&gt;
    if (mutex_lock_interruptible(&amp;amp;dev_state.lock))&lt;br /&gt;
        return -ERESTARTSYS;  /* Interrupted by signal */&lt;br /&gt;
    &lt;br /&gt;
    /* Check if there&amp;#039;s data available */&lt;br /&gt;
    if (dev_state.buffer_len == 0) {&lt;br /&gt;
        mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
        return 0;  /* EOF - no data available */&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Determine how much to copy: minimum of requested and available */&lt;br /&gt;
    to_copy = (count &amp;lt; dev_state.buffer_len) ? count : dev_state.buffer_len;&lt;br /&gt;
    &lt;br /&gt;
    /* Copy data to userspace - data is already transformed */&lt;br /&gt;
    if (copy_to_user(user_buffer, dev_state.buffer, to_copy)) {&lt;br /&gt;
        mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
        return -EFAULT;  /* Bad userspace address */&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     * Remove copied data from buffer by shifting remaining data forward&lt;br /&gt;
     * There are more efficient ways to do this (eg. ring buffers)&lt;br /&gt;
     */&lt;br /&gt;
    dev_state.buffer_len -= to_copy;&lt;br /&gt;
    if (dev_state.buffer_len &amp;gt; 0) {&lt;br /&gt;
        memmove(dev_state.buffer, dev_state.buffer + to_copy,&lt;br /&gt;
                dev_state.buffer_len);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Read %zu bytes\n&amp;quot;, to_copy);&lt;br /&gt;
    return to_copy;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_write - Handle write operation&lt;br /&gt;
 * @file: File structure&lt;br /&gt;
 * @user_buffer: Userspace buffer containing data to write&lt;br /&gt;
 * @count: Number of bytes to write&lt;br /&gt;
 * @offset: File offset (unused for character devices)&lt;br /&gt;
 *&lt;br /&gt;
 * Copies data from userspace to kernel buffer, applying transformation.&lt;br /&gt;
 * This is where transform-on-write happens: data is transformed as it&lt;br /&gt;
 * enters the buffer, not when it&amp;#039;s read out.&lt;br /&gt;
 *&lt;br /&gt;
 * Return: Number of bytes written, negative error code on failure&lt;br /&gt;
 */&lt;br /&gt;
static ssize_t superpipe_write(struct file *file,&lt;br /&gt;
                               const char __user *user_buffer,&lt;br /&gt;
                               size_t count, loff_t *offset)&lt;br /&gt;
{&lt;br /&gt;
    size_t space_available;&lt;br /&gt;
    size_t to_write;&lt;br /&gt;
    char *write_position;&lt;br /&gt;
    &lt;br /&gt;
    /* Acquire lock */&lt;br /&gt;
    if (mutex_lock_interruptible(&amp;amp;dev_state.lock))&lt;br /&gt;
        return -ERESTARTSYS;&lt;br /&gt;
    &lt;br /&gt;
    /* Check available space in buffer */&lt;br /&gt;
    space_available = BUFFER_SIZE - dev_state.buffer_len;&lt;br /&gt;
    if (space_available == 0) {&lt;br /&gt;
        mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
        return -ENOSPC;  /* Buffer full - no space left */&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Determine how much we can actually write */&lt;br /&gt;
    to_write = (count &amp;lt; space_available) ? count : space_available;&lt;br /&gt;
    &lt;br /&gt;
    /* Calculate where to write in buffer */&lt;br /&gt;
    write_position = dev_state.buffer + dev_state.buffer_len;&lt;br /&gt;
    &lt;br /&gt;
    /* Copy from userspace to kernel buffer */&lt;br /&gt;
    if (copy_from_user(write_position, user_buffer, to_write)) {&lt;br /&gt;
        mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
        return -EFAULT;  /* Bad userspace address */&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /*&lt;br /&gt;
     * TRANSFORM ON WRITE&lt;br /&gt;
     * Apply transformation to the newly written data in-place&lt;br /&gt;
     * Data in buffer is always in its final transformed state&lt;br /&gt;
     */&lt;br /&gt;
    switch (dev_state.mode) {&lt;br /&gt;
    case TRANSFORM_UPPER:&lt;br /&gt;
        transform_upper(write_position, to_write);&lt;br /&gt;
        break;&lt;br /&gt;
    case TRANSFORM_NONE:&lt;br /&gt;
        /* No transformation - data already copied as-is */&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        /* Should never happen if validation is correct */&lt;br /&gt;
        printk(KERN_WARNING &amp;quot;superpipe: Invalid mode %d\n&amp;quot;, dev_state.mode);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Update buffer length - for this reference, same as to_write */&lt;br /&gt;
    dev_state.buffer_len += to_write;&lt;br /&gt;
    &lt;br /&gt;
    mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Wrote %zu bytes (mode=%d)\n&amp;quot;,&lt;br /&gt;
           to_write, dev_state.mode);&lt;br /&gt;
    &lt;br /&gt;
    /* Return bytes consumed from user buffer */&lt;br /&gt;
    return to_write;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_ioctl - Handle ioctl commands&lt;br /&gt;
 * @file: File structure&lt;br /&gt;
 * @cmd: ioctl command code&lt;br /&gt;
 * @arg: ioctl argument (pointer or value, depends on command)&lt;br /&gt;
 *&lt;br /&gt;
 * Handles device-specific configuration commands.&lt;br /&gt;
 * Commands are defined in superpipe_uapi.h&lt;br /&gt;
 *&lt;br /&gt;
 * Return: 0 on success, negative error code on failure&lt;br /&gt;
 */&lt;br /&gt;
static long superpipe_ioctl(struct file *file, unsigned int cmd,&lt;br /&gt;
                            unsigned long arg)&lt;br /&gt;
{&lt;br /&gt;
    struct superpipe_ioctl_config cfg;&lt;br /&gt;
    int ret = 0;&lt;br /&gt;
    &lt;br /&gt;
    /* Acquire lock */&lt;br /&gt;
    if (mutex_lock_interruptible(&amp;amp;dev_state.lock))&lt;br /&gt;
        return -ERESTARTSYS;&lt;br /&gt;
    &lt;br /&gt;
    switch (cmd) {&lt;br /&gt;
    case SUPERPIPE_IOC_SET_CONFIG:&lt;br /&gt;
        /* Copy configuration structure from userspace */&lt;br /&gt;
        if (copy_from_user(&amp;amp;cfg, (void __user *)arg, sizeof(cfg))) {&lt;br /&gt;
            ret = -EFAULT;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        /* Validate mode */&lt;br /&gt;
        if (cfg.mode &amp;gt;= TRANSFORM_MAX) {&lt;br /&gt;
            printk(KERN_WARNING &amp;quot;superpipe: Invalid mode %u\n&amp;quot;, cfg.mode);&lt;br /&gt;
            ret = -EINVAL;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        /* Apply configuration */&lt;br /&gt;
        dev_state.mode = cfg.mode;&lt;br /&gt;
        printk(KERN_INFO &amp;quot;superpipe: Mode set to %u\n&amp;quot;, cfg.mode);&lt;br /&gt;
        break;&lt;br /&gt;
        &lt;br /&gt;
    case SUPERPIPE_IOC_GET_CONFIG:&lt;br /&gt;
        /* Prepare configuration structure */&lt;br /&gt;
        cfg.mode = dev_state.mode;&lt;br /&gt;
        cfg.flags = 0;&lt;br /&gt;
        &lt;br /&gt;
        /* Copy to userspace */&lt;br /&gt;
        if (copy_to_user((void __user *)arg, &amp;amp;cfg, sizeof(cfg))) {&lt;br /&gt;
            ret = -EFAULT;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        printk(KERN_INFO &amp;quot;superpipe: Returned config (mode=%u)\n&amp;quot;, cfg.mode);&lt;br /&gt;
        break;&lt;br /&gt;
        &lt;br /&gt;
    case SUPERPIPE_IOC_RESET:&lt;br /&gt;
        /* Clear buffer */&lt;br /&gt;
        dev_state.buffer_len = 0;&lt;br /&gt;
        printk(KERN_INFO &amp;quot;superpipe: Buffer reset\n&amp;quot;);&lt;br /&gt;
        break;&lt;br /&gt;
        &lt;br /&gt;
    default:&lt;br /&gt;
        /* Unknown command */&lt;br /&gt;
        printk(KERN_WARNING &amp;quot;superpipe: Unknown ioctl command: 0x%x\n&amp;quot;, cmd);&lt;br /&gt;
        ret = -EINVAL;&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
    return ret;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * File operations structure&lt;br /&gt;
 * Maps system calls to our handler functions&lt;br /&gt;
 */&lt;br /&gt;
static const struct file_operations superpipe_fops = {&lt;br /&gt;
    .owner = THIS_MODULE,&lt;br /&gt;
    .open = superpipe_open,&lt;br /&gt;
    .release = superpipe_release,&lt;br /&gt;
    .read = superpipe_read,&lt;br /&gt;
    .write = superpipe_write,&lt;br /&gt;
    .unlocked_ioctl = superpipe_ioctl,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Miscdevice structure&lt;br /&gt;
 * Defines our character device&lt;br /&gt;
 */&lt;br /&gt;
static struct miscdevice superpipe_miscdev = {&lt;br /&gt;
    .minor = MISC_DYNAMIC_MINOR,  /* Kernel assigns minor number automatically */&lt;br /&gt;
    .name = DEVICE_NAME,           /* Creates /dev/superpipe */&lt;br /&gt;
    .fops = &amp;amp;superpipe_fops,       /* Our file operations */&lt;br /&gt;
    .mode = 0666,                  /* Device permissions (rw-rw-rw-) */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_init - Module initialization&lt;br /&gt;
 *&lt;br /&gt;
 * Called when module is loaded with insmod.&lt;br /&gt;
 * Allocates resources and registers the device.&lt;br /&gt;
 *&lt;br /&gt;
 * Return: 0 on success, negative error code on failure&lt;br /&gt;
 */&lt;br /&gt;
static int __init superpipe_init(void)&lt;br /&gt;
{&lt;br /&gt;
    int ret;&lt;br /&gt;
    &lt;br /&gt;
    /* Allocate kernel buffer (4KB) */&lt;br /&gt;
    dev_state.buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);&lt;br /&gt;
    if (!dev_state.buffer) {&lt;br /&gt;
        printk(KERN_ERR &amp;quot;superpipe: Failed to allocate buffer\n&amp;quot;);&lt;br /&gt;
        return -ENOMEM;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Initialize device state */&lt;br /&gt;
    dev_state.buffer_len = 0;&lt;br /&gt;
    dev_state.mode = TRANSFORM_NONE;  /* Start in pass-through mode */&lt;br /&gt;
    mutex_init(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    /* Register miscdevice - creates /dev/superpipe */&lt;br /&gt;
    ret = misc_register(&amp;amp;superpipe_miscdev);&lt;br /&gt;
    if (ret) {&lt;br /&gt;
        printk(KERN_ERR &amp;quot;superpipe: Failed to register device (error %d)\n&amp;quot;, ret);&lt;br /&gt;
        kfree(dev_state.buffer);&lt;br /&gt;
        return ret;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Module loaded successfully\n&amp;quot;);&lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Device created at /dev/%s\n&amp;quot;, DEVICE_NAME);&lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Buffer size: %d bytes\n&amp;quot;, BUFFER_SIZE);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * superpipe_exit - Module cleanup&lt;br /&gt;
 *&lt;br /&gt;
 * Called when module is unloaded with rmmod.&lt;br /&gt;
 * Unregisters device and frees resources.&lt;br /&gt;
 */&lt;br /&gt;
static void __exit superpipe_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Unregister device - removes /dev/superpipe */&lt;br /&gt;
    misc_deregister(&amp;amp;superpipe_miscdev);&lt;br /&gt;
    &lt;br /&gt;
    /* Free buffer */&lt;br /&gt;
    kfree(dev_state.buffer);&lt;br /&gt;
    &lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Module unloaded\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Register initialization and cleanup functions */&lt;br /&gt;
module_init(superpipe_init);&lt;br /&gt;
module_exit(superpipe_exit);&lt;br /&gt;
&lt;br /&gt;
/* Module metadata */&lt;br /&gt;
MODULE_LICENSE(&amp;quot;GPL&amp;quot;);&lt;br /&gt;
MODULE_AUTHOR(&amp;quot;Your Name &amp;lt;your.email@example.com&amp;gt;&amp;quot;);&lt;br /&gt;
MODULE_DESCRIPTION(&amp;quot;Superpipe character device with configurable transformations&amp;quot;);&lt;br /&gt;
MODULE_VERSION(&amp;quot;1.0&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-reference-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Understanding the Reference Implementation ===&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s walk through the key components of this implementation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;module-initialization&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Module Initialization ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static int __init superpipe_init(void)&lt;br /&gt;
{&lt;br /&gt;
    /* 1. Allocate buffer */&lt;br /&gt;
    dev_state.buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);&lt;br /&gt;
    &lt;br /&gt;
    /* 2. Initialize state */&lt;br /&gt;
    dev_state.buffer_len = 0;&lt;br /&gt;
    dev_state.mode = TRANSFORM_NONE;&lt;br /&gt;
    mutex_init(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    /* 3. Register device */&lt;br /&gt;
    ret = misc_register(&amp;amp;superpipe_miscdev);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step-by-step:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Allocate buffer:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;kmalloc()&amp;lt;/code&amp;gt; is the kernel equivalent of &amp;lt;code&amp;gt;malloc()&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;GFP_KERNEL&amp;lt;/code&amp;gt; means &amp;amp;quot;can sleep, normal priority allocation.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Initialize state:&amp;#039;&amp;#039;&amp;#039; Set buffer empty, mode to pass-through, initialize mutex&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Register device:&amp;#039;&amp;#039;&amp;#039; This creates &amp;lt;code&amp;gt;/dev/superpipe&amp;lt;/code&amp;gt; and makes it available&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;If anything fails:&amp;#039;&amp;#039;&amp;#039; Clean up already-allocated resources before returning error code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-open-handler&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Open Handler ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static int superpipe_open(struct inode *inode, struct file *file)&lt;br /&gt;
{&lt;br /&gt;
    printk(KERN_INFO &amp;quot;superpipe: Device opened by process %d (%s)\n&amp;quot;,&lt;br /&gt;
           current-&amp;gt;pid, current-&amp;gt;comm);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens here:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; is a kernel global pointing to the current process&amp;#039;s task_struct&lt;br /&gt;
* &amp;lt;code&amp;gt;current-&amp;amp;gt;pid&amp;lt;/code&amp;gt; is the process ID&lt;br /&gt;
* &amp;lt;code&amp;gt;current-&amp;amp;gt;comm&amp;lt;/code&amp;gt; is the process name (from argv[0])&lt;br /&gt;
* We don&amp;#039;t need to allocate per-file resources, so just log and return success&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-write-handler-transform-on-write&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Write Handler: Transform-on-Write ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static ssize_t superpipe_write(...)&lt;br /&gt;
{&lt;br /&gt;
    /* 1. Acquire lock */&lt;br /&gt;
    mutex_lock_interruptible(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    /* 2. Check space */&lt;br /&gt;
    if (space_available == 0)&lt;br /&gt;
        return -ENOSPC;&lt;br /&gt;
    &lt;br /&gt;
    /* 3. Copy from user */&lt;br /&gt;
    write_position = dev_state.buffer + dev_state.buffer_len;&lt;br /&gt;
    copy_from_user(write_position, user_buffer, to_write);&lt;br /&gt;
    &lt;br /&gt;
    /* 4. TRANSFORM IN-PLACE */&lt;br /&gt;
    switch (dev_state.mode) {&lt;br /&gt;
    case TRANSFORM_UPPER:&lt;br /&gt;
        transform_upper(write_position, to_write);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* 5. Update length */&lt;br /&gt;
    dev_state.buffer_len += to_write;&lt;br /&gt;
    &lt;br /&gt;
    /* 6. Release lock */&lt;br /&gt;
    mutex_unlock(&amp;amp;dev_state.lock);&lt;br /&gt;
    &lt;br /&gt;
    return to_write;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key points:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Data is transformed immediately after being copied from userspace&lt;br /&gt;
* The transformation happens on &amp;lt;code&amp;gt;write_position&amp;lt;/code&amp;gt; (the buffer location), not a temporary copy&lt;br /&gt;
* Once stored, data is in its final transformed state&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-read-handler-simple-copy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Read Handler: Simple Copy ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static ssize_t superpipe_read(...)&lt;br /&gt;
{&lt;br /&gt;
    /* 1. Check for data */&lt;br /&gt;
    if (dev_state.buffer_len == 0)&lt;br /&gt;
        return 0;  /* EOF */&lt;br /&gt;
    &lt;br /&gt;
    /* 2. Copy to user - data already transformed */&lt;br /&gt;
    copy_to_user(user_buffer, dev_state.buffer, to_copy);&lt;br /&gt;
    &lt;br /&gt;
    /* 3. Remove consumed data */&lt;br /&gt;
    dev_state.buffer_len -= to_copy;&lt;br /&gt;
    memmove(dev_state.buffer, dev_state.buffer + to_copy,&lt;br /&gt;
            dev_state.buffer_len);&lt;br /&gt;
    &lt;br /&gt;
    return to_copy;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why it&amp;#039;s simple:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* No transformation needed—data is already in final form&lt;br /&gt;
* Just copy to userspace and remove from buffer&lt;br /&gt;
* &amp;lt;code&amp;gt;memmove()&amp;lt;/code&amp;gt; shifts remaining data to the beginning&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-ioctl-handler-configuration-with-structs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The ioctl Handler: Configuration with Structs ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static long superpipe_ioctl(...)&lt;br /&gt;
{&lt;br /&gt;
    struct superpipe_ioctl_config cfg;&lt;br /&gt;
    &lt;br /&gt;
    switch (cmd) {&lt;br /&gt;
    case SUPERPIPE_IOC_SET_CONFIG:&lt;br /&gt;
        /* 1. Copy struct from userspace */&lt;br /&gt;
        if (copy_from_user(&amp;amp;cfg, (void __user *)arg, sizeof(cfg)))&lt;br /&gt;
            return -EFAULT;&lt;br /&gt;
        &lt;br /&gt;
        /* 2. Validate */&lt;br /&gt;
        if (cfg.mode &amp;gt;= TRANSFORM_MAX)&lt;br /&gt;
            return -EINVAL;&lt;br /&gt;
        &lt;br /&gt;
        /* 3. Apply */&lt;br /&gt;
        dev_state.mode = cfg.mode;&lt;br /&gt;
        break;&lt;br /&gt;
        &lt;br /&gt;
    case SUPERPIPE_IOC_GET_CONFIG:&lt;br /&gt;
        /* 1. Prepare struct */&lt;br /&gt;
        cfg.mode = dev_state.mode;&lt;br /&gt;
        cfg.flags = 0;&lt;br /&gt;
        &lt;br /&gt;
        /* 2. Copy to userspace */&lt;br /&gt;
        if (copy_to_user((void __user *)arg, &amp;amp;cfg, sizeof(cfg)))&lt;br /&gt;
            return -EFAULT;&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Pattern for struct-based ioctl:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# For SET (write to device): &amp;lt;code&amp;gt;copy_from_user&amp;lt;/code&amp;gt; → validate → apply&lt;br /&gt;
# For GET (read from device): prepare → &amp;lt;code&amp;gt;copy_to_user&amp;lt;/code&amp;gt;&lt;br /&gt;
# Always validate inputs before applying&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;module-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Module Cleanup ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static void __exit superpipe_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    misc_deregister(&amp;amp;superpipe_miscdev);  /* Remove device */&lt;br /&gt;
    kfree(dev_state.buffer);              /* Free buffer */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cleanup must:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Undo everything done in init&lt;br /&gt;
* Be safe to call even if init partially failed (though our init handles that)&lt;br /&gt;
* Not leak resources&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;compiling-and-testing-the-reference-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Compiling and Testing the Reference Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-compile-the-module&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Compile the Module ===&lt;br /&gt;
&lt;br /&gt;
Create a &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;makefile&amp;quot;&amp;gt;obj-m += superpipe_ref.o&lt;br /&gt;
&lt;br /&gt;
KDIR := /lib/modules/$(shell uname -r)/build&lt;br /&gt;
PWD := $(shell pwd)&lt;br /&gt;
&lt;br /&gt;
all:&lt;br /&gt;
    $(MAKE) -C $(KDIR) M=$(PWD) modules&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
    $(MAKE) -C $(KDIR) M=$(PWD) clean&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Compile:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;make&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;make -C /lib/modules/6.5.0-generic/build M=/home/user/lab11 modules&lt;br /&gt;
make[1]: Entering directory &amp;#039;/usr/src/linux-headers-6.5.0-generic&amp;#039;&lt;br /&gt;
  CC [M]  /home/user/lab11/superpipe_ref.o&lt;br /&gt;
  MODPOST /home/user/lab11/Module.symvers&lt;br /&gt;
  CC [M]  /home/user/lab11/superpipe_ref.mod.o&lt;br /&gt;
  LD [M]  /home/user/lab11/superpipe_ref.ko&lt;br /&gt;
make[1]: Leaving directory &amp;#039;/usr/src/linux-headers-6.5.0-generic&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You now have &amp;lt;code&amp;gt;superpipe_ref.ko&amp;lt;/code&amp;gt; (kernel object file).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-load-the-module&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Load the Module ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Load the module:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo insmod superpipe_ref.ko&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify it loaded:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;lsmod | grep superpipe&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;superpipe_ref          16384  0&amp;lt;/pre&amp;gt;&lt;br /&gt;
The &amp;amp;quot;0&amp;amp;quot; means no one is currently using it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check kernel messages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dmesg | tail -5&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[12345.678] superpipe: Module loaded successfully&lt;br /&gt;
[12345.678] superpipe: Device created at /dev/superpipe&lt;br /&gt;
[12345.678] superpipe: Buffer size: 4096 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify device file:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -l /dev/superpipe&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;crw-rw-rw- 1 root root 10, 58 Dec 19 12:00 /dev/superpipe&amp;lt;/pre&amp;gt;&lt;br /&gt;
The device is ready to use!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-build-the-test-program&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Build the Test Program ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;test_superpipe.c&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* test_superpipe.c - Userspace test program for superpipe */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/ioctl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;superpipe_uapi.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void usage(const char *progname)&lt;br /&gt;
{&lt;br /&gt;
    fprintf(stderr, &amp;quot;Usage: %s &amp;lt;command&amp;gt; [args]\n&amp;quot;, progname);&lt;br /&gt;
    fprintf(stderr, &amp;quot;Commands:\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  set_mode &amp;lt;none|upper&amp;gt;   - Set transformation mode\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  get_mode                - Get current mode\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  write &amp;lt;text&amp;gt;            - Write text to device\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  read                    - Read from device\n&amp;quot;);&lt;br /&gt;
    fprintf(stderr, &amp;quot;  reset                   - Reset device (clear buffer)\n&amp;quot;);&lt;br /&gt;
    exit(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
    int fd;&lt;br /&gt;
    struct superpipe_ioctl_config cfg;&lt;br /&gt;
    char buffer[256];&lt;br /&gt;
    ssize_t n;&lt;br /&gt;
    &lt;br /&gt;
    if (argc &amp;lt; 2) {&lt;br /&gt;
        usage(argv[0]);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Open device */&lt;br /&gt;
    fd = open(&amp;quot;/dev/superpipe&amp;quot;, O_RDWR);&lt;br /&gt;
    if (fd &amp;lt; 0) {&lt;br /&gt;
        perror(&amp;quot;Failed to open /dev/superpipe&amp;quot;);&lt;br /&gt;
        fprintf(stderr, &amp;quot;Make sure the module is loaded\n&amp;quot;);&lt;br /&gt;
        return 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* Process command */&lt;br /&gt;
    if (strcmp(argv[1], &amp;quot;set_mode&amp;quot;) == 0) {&lt;br /&gt;
        if (argc &amp;lt; 3) {&lt;br /&gt;
            fprintf(stderr, &amp;quot;Error: set_mode requires mode argument\n&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if (strcmp(argv[2], &amp;quot;none&amp;quot;) == 0) {&lt;br /&gt;
            cfg.mode = SUPERPIPE_MODE_NONE;&lt;br /&gt;
        } else if (strcmp(argv[2], &amp;quot;upper&amp;quot;) == 0) {&lt;br /&gt;
            cfg.mode = SUPERPIPE_MODE_UPPER;&lt;br /&gt;
        } else {&lt;br /&gt;
            fprintf(stderr, &amp;quot;Error: Unknown mode &amp;#039;%s&amp;#039;\n&amp;quot;, argv[2]);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        cfg.flags = 0;&lt;br /&gt;
        &lt;br /&gt;
        if (ioctl(fd, SUPERPIPE_IOC_SET_CONFIG, &amp;amp;cfg) &amp;lt; 0) {&lt;br /&gt;
            perror(&amp;quot;ioctl SET_CONFIG&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        printf(&amp;quot;✓ Mode set to %s\n&amp;quot;, argv[2]);&lt;br /&gt;
        &lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;get_mode&amp;quot;) == 0) {&lt;br /&gt;
        if (ioctl(fd, SUPERPIPE_IOC_GET_CONFIG, &amp;amp;cfg) &amp;lt; 0) {&lt;br /&gt;
            perror(&amp;quot;ioctl GET_CONFIG&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        printf(&amp;quot;Current mode: &amp;quot;);&lt;br /&gt;
        switch (cfg.mode) {&lt;br /&gt;
        case SUPERPIPE_MODE_NONE:&lt;br /&gt;
            printf(&amp;quot;NONE\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case SUPERPIPE_MODE_UPPER:&lt;br /&gt;
            printf(&amp;quot;UPPER\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            printf(&amp;quot;UNKNOWN (%u)\n&amp;quot;, cfg.mode);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;write&amp;quot;) == 0) {&lt;br /&gt;
        if (argc &amp;lt; 3) {&lt;br /&gt;
            fprintf(stderr, &amp;quot;Error: write requires text argument\n&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        n = write(fd, argv[2], strlen(argv[2]));&lt;br /&gt;
        if (n &amp;lt; 0) {&lt;br /&gt;
            perror(&amp;quot;write&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        printf(&amp;quot;✓ Wrote %zd bytes\n&amp;quot;, n);&lt;br /&gt;
        &lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;read&amp;quot;) == 0) {&lt;br /&gt;
        n = read(fd, buffer, sizeof(buffer) - 1);&lt;br /&gt;
        if (n &amp;lt; 0) {&lt;br /&gt;
            perror(&amp;quot;read&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if (n == 0) {&lt;br /&gt;
            printf(&amp;quot;(no data available)\n&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
            buffer[n] = &amp;#039;\0&amp;#039;;&lt;br /&gt;
            printf(&amp;quot;%s\n&amp;quot;, buffer);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
    } else if (strcmp(argv[1], &amp;quot;reset&amp;quot;) == 0) {&lt;br /&gt;
        if (ioctl(fd, SUPERPIPE_IOC_RESET) &amp;lt; 0) {&lt;br /&gt;
            perror(&amp;quot;ioctl RESET&amp;quot;);&lt;br /&gt;
            close(fd);&lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        printf(&amp;quot;✓ Device reset\n&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
    } else {&lt;br /&gt;
        fprintf(stderr, &amp;quot;Error: Unknown command &amp;#039;%s&amp;#039;\n&amp;quot;, argv[1]);&lt;br /&gt;
        close(fd);&lt;br /&gt;
        usage(argv[0]);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    close(fd);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Compile:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;gcc -o test_superpipe test_superpipe.c&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-test-basic-write-and-read&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 4: Test Basic Write and Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test pass-through mode:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode none&lt;br /&gt;
./test_superpipe write &amp;quot;Hello World 123&amp;quot;&lt;br /&gt;
./test_superpipe read&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;✓ Mode set to none&lt;br /&gt;
✓ Wrote 15 bytes&lt;br /&gt;
Hello World 123&amp;lt;/pre&amp;gt;&lt;br /&gt;
The data passes through unchanged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-test-uppercase-transformation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 5: Test Uppercase Transformation ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Enable uppercase mode and test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode upper&lt;br /&gt;
./test_superpipe write &amp;quot;hello world 123&amp;quot;&lt;br /&gt;
./test_superpipe read&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;✓ Mode set to upper&lt;br /&gt;
✓ Wrote 15 bytes&lt;br /&gt;
HELLO WORLD 123&amp;lt;/pre&amp;gt;&lt;br /&gt;
The data was transformed to uppercase during the write!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-6-test-mixed-mode-writes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 6: Test Mixed Mode Writes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test that mode changes only affect new writes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode none&lt;br /&gt;
./test_superpipe write &amp;quot;plain &amp;quot;&lt;br /&gt;
./test_superpipe set_mode upper&lt;br /&gt;
./test_superpipe write &amp;quot;caps &amp;quot;&lt;br /&gt;
./test_superpipe set_mode none&lt;br /&gt;
./test_superpipe write &amp;quot;normal&amp;quot;&lt;br /&gt;
./test_superpipe read&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;✓ Mode set to none&lt;br /&gt;
✓ Wrote 6 bytes&lt;br /&gt;
✓ Mode set to upper&lt;br /&gt;
✓ Wrote 5 bytes&lt;br /&gt;
✓ Mode set to none&lt;br /&gt;
✓ Wrote 6 bytes&lt;br /&gt;
plain CAPS normal&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each write was transformed according to the mode at the time of writing!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-7-view-kernel-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 7: View Kernel Messages ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check what the kernel logged:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dmesg | tail -20&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[12345.678] superpipe: Module loaded successfully&lt;br /&gt;
[12345.678] superpipe: Device created at /dev/superpipe&lt;br /&gt;
[12346.123] superpipe: Device opened by process 1234 (test_superpipe)&lt;br /&gt;
[12346.124] superpipe: Mode set to 0&lt;br /&gt;
[12346.125] superpipe: Device closed by process 1234 (test_superpipe)&lt;br /&gt;
[12346.200] superpipe: Device opened by process 1235 (test_superpipe)&lt;br /&gt;
[12346.201] superpipe: Wrote 15 bytes (mode=0)&lt;br /&gt;
[12346.202] superpipe: Device closed by process 1235 (test_superpipe)&lt;br /&gt;
[12346.300] superpipe: Device opened by process 1236 (test_superpipe)&lt;br /&gt;
[12346.301] superpipe: Read 15 bytes&lt;br /&gt;
[12346.302] superpipe: Device closed by process 1236 (test_superpipe)&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can see every open, close, read, write, and ioctl operation logged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-8-unload-the-module&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 8: Unload the Module ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Unload:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo rmmod superpipe_ref&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify device is gone:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -l /dev/superpipe&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ls: cannot access &amp;#039;/dev/superpipe&amp;#039;: No such file or directory&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check kernel log:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dmesg | tail -1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[12350.456] superpipe: Module unloaded&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-full-superpipe-implementation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Exercise: Full Superpipe Implementation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;objective&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Objective ===&lt;br /&gt;
&lt;br /&gt;
Extend the reference implementation to support five transformation modes:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;NONE&amp;#039;&amp;#039;&amp;#039; - Pass through unchanged (already implemented)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;UPPER&amp;#039;&amp;#039;&amp;#039; - Convert to uppercase (already implemented)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LOWER&amp;#039;&amp;#039;&amp;#039; - Convert to lowercase (NEW)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DROP_VOWELS&amp;#039;&amp;#039;&amp;#039; - Remove vowels (a, e, i, o, u, A, E, I, O, U) (NEW)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DROP_SPACES&amp;#039;&amp;#039;&amp;#039; - Remove space characters (0x20) (NEW)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Requirements ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Update the UAPI Header&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Add new mode constants to &amp;lt;code&amp;gt;superpipe_uapi.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#define SUPERPIPE_MODE_NONE         0&lt;br /&gt;
#define SUPERPIPE_MODE_UPPER        1&lt;br /&gt;
#define SUPERPIPE_MODE_LOWER        2  /* NEW */&lt;br /&gt;
#define SUPERPIPE_MODE_DROP_VOWELS  3  /* NEW */&lt;br /&gt;
#define SUPERPIPE_MODE_DROP_SPACES  4  /* NEW */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Implement Transformation Functions&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Add three new transformation functions to your kernel module:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Convert to lowercase */&lt;br /&gt;
static void transform_lower(char *data, size_t len);&lt;br /&gt;
&lt;br /&gt;
/* Remove vowels (returns new length after filtering) */&lt;br /&gt;
static size_t transform_drop_vowels(char *data, size_t len);&lt;br /&gt;
&lt;br /&gt;
/* Remove spaces (returns new length after filtering) */&lt;br /&gt;
static size_t transform_drop_spaces(char *data, size_t len);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Update the Write Handler&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Modify &amp;lt;code&amp;gt;superpipe_write()&amp;lt;/code&amp;gt; to handle all transformation modes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Apply transformation - filtering may reduce length */&lt;br /&gt;
size_t final_len = to_write;&lt;br /&gt;
&lt;br /&gt;
switch (dev_state.mode) {&lt;br /&gt;
case TRANSFORM_NONE:&lt;br /&gt;
    /* No transformation */&lt;br /&gt;
    break;&lt;br /&gt;
case TRANSFORM_UPPER:&lt;br /&gt;
    transform_upper(write_position, to_write);&lt;br /&gt;
    break;&lt;br /&gt;
case TRANSFORM_LOWER:&lt;br /&gt;
    transform_lower(write_position, to_write);&lt;br /&gt;
    break;&lt;br /&gt;
case TRANSFORM_DROP_VOWELS:&lt;br /&gt;
    final_len = transform_drop_vowels(write_position, to_write);&lt;br /&gt;
    break;&lt;br /&gt;
case TRANSFORM_DROP_SPACES:&lt;br /&gt;
    final_len = transform_drop_spaces(write_position, to_write);&lt;br /&gt;
    break;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Update buffer length by actual stored data */&lt;br /&gt;
dev_state.buffer_len += final_len;&lt;br /&gt;
&lt;br /&gt;
return final_len;  /* Return bytes actually stored */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Update the Test Program&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Add support for new modes in &amp;lt;code&amp;gt;test_superpipe.c&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;} else if (strcmp(argv[2], &amp;quot;lower&amp;quot;) == 0) {&lt;br /&gt;
    cfg.mode = SUPERPIPE_MODE_LOWER;&lt;br /&gt;
} else if (strcmp(argv[2], &amp;quot;drop_vowels&amp;quot;) == 0) {&lt;br /&gt;
    cfg.mode = SUPERPIPE_MODE_DROP_VOWELS;&lt;br /&gt;
} else if (strcmp(argv[2], &amp;quot;drop_spaces&amp;quot;) == 0) {&lt;br /&gt;
    cfg.mode = SUPERPIPE_MODE_DROP_SPACES;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;transformation-specifications&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Transformation Specifications ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TRANSFORM_LOWER:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Convert all ASCII uppercase letters (A-Z) to lowercase (a-z). Other characters unchanged.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Input:  &amp;amp;quot;Hello WORLD 123&amp;amp;quot;&lt;br /&gt;
Output: &amp;amp;quot;hello world 123&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TRANSFORM_DROP_VOWELS:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Remove all vowels (both upper and lowercase): a, e, i, o, u, A, E, I, O, U&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Input:  &amp;amp;quot;beautiful day&amp;amp;quot;&lt;br /&gt;
Output: &amp;amp;quot;btfl dy&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Input:  &amp;amp;quot;AEIOU&amp;amp;quot;&lt;br /&gt;
Output: &amp;amp;quot;&amp;amp;quot; (empty)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TRANSFORM_DROP_SPACES:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Remove all space characters (ASCII 0x20)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Input:  &amp;amp;quot;hello world test&amp;amp;quot;&lt;br /&gt;
Output: &amp;amp;quot;helloworldtest&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Input:  &amp;amp;quot;   spaces   &amp;amp;quot;&lt;br /&gt;
Output: &amp;amp;quot;spaces&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important for filtering transforms:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When characters are removed, the data must be compacted in the buffer. However, the write() system call still returns the number of bytes consumed from the user&amp;#039;s buffer (standard UNIX behavior).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode drop_vowels&lt;br /&gt;
./test_superpipe write &amp;quot;hello&amp;quot;    # Writes 5 bytes&lt;br /&gt;
# Output: ✓ Wrote 5 bytes         # Returns 5 (consumed all user data)&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Output: hll                      # But only 3 bytes stored in buffer&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;implementation-guidance&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Implementation Guidance ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Helper function for vowel detection:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static inline bool is_vowel(char c)&lt;br /&gt;
{&lt;br /&gt;
    return (c == &amp;#039;a&amp;#039; || c == &amp;#039;e&amp;#039; || c == &amp;#039;i&amp;#039; || c == &amp;#039;o&amp;#039; || c == &amp;#039;u&amp;#039; ||&lt;br /&gt;
            c == &amp;#039;A&amp;#039; || c == &amp;#039;E&amp;#039; || c == &amp;#039;I&amp;#039; || c == &amp;#039;O&amp;#039; || c == &amp;#039;U&amp;#039;);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Pattern for filtering (compacting) transformations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static size_t transform_drop_vowels(char *data, size_t len)&lt;br /&gt;
{&lt;br /&gt;
    size_t i, j = 0;&lt;br /&gt;
    &lt;br /&gt;
    /* Iterate through input, copy only non-vowels */&lt;br /&gt;
    for (i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
        if (!is_vowel(data[i])) {&lt;br /&gt;
            data[j++] = data[i];  /* Keep this character */&lt;br /&gt;
        }&lt;br /&gt;
        /* Vowels are skipped - not copied to output position */&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /* j now contains the number of characters kept */&lt;br /&gt;
    return j;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why this works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* We iterate with &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; through all input characters&lt;br /&gt;
* We write kept characters to position &amp;lt;code&amp;gt;j&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;j&amp;lt;/code&amp;gt; only advances when we keep a character&lt;br /&gt;
* At the end, &amp;lt;code&amp;gt;j&amp;lt;/code&amp;gt; is the new length&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Pattern for case conversion:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;static void transform_lower(char *data, size_t len)&lt;br /&gt;
{&lt;br /&gt;
    size_t i;&lt;br /&gt;
    &lt;br /&gt;
    for (i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
        if (isupper(data[i])) {&lt;br /&gt;
            data[i] = tolower(data[i]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;testing-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Testing Requirements ===&lt;br /&gt;
&lt;br /&gt;
Create a comprehensive test script &amp;lt;code&amp;gt;test.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 1: NONE mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode none&lt;br /&gt;
./test_superpipe write &amp;quot;Hello World 123&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: Hello World 123&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 2: UPPER mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode upper&lt;br /&gt;
./test_superpipe write &amp;quot;hello world 123&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: HELLO WORLD 123&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 3: LOWER mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode lower&lt;br /&gt;
./test_superpipe write &amp;quot;HELLO WORLD 123&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: hello world 123&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 4: DROP_VOWELS mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode drop_vowels&lt;br /&gt;
./test_superpipe write &amp;quot;beautiful&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: btfl&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 5: DROP_SPACES mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode drop_spaces&lt;br /&gt;
./test_superpipe write &amp;quot;hello world test&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: helloworldtest&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 6: Mixed modes (different modes for different writes)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./test_superpipe set_mode none&lt;br /&gt;
./test_superpipe write &amp;quot;plain &amp;quot;&lt;br /&gt;
./test_superpipe set_mode upper&lt;br /&gt;
./test_superpipe write &amp;quot;CAPS &amp;quot;&lt;br /&gt;
./test_superpipe set_mode lower&lt;br /&gt;
./test_superpipe write &amp;quot;lower&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: plain CAPS lower&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test 7: Edge cases&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# All vowels&lt;br /&gt;
./test_superpipe set_mode drop_vowels&lt;br /&gt;
./test_superpipe write &amp;quot;aeiou&amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: (empty)&lt;br /&gt;
&lt;br /&gt;
# All spaces&lt;br /&gt;
./test_superpipe set_mode drop_spaces&lt;br /&gt;
./test_superpipe write &amp;quot;     &amp;quot;&lt;br /&gt;
./test_superpipe read&lt;br /&gt;
# Expected: (empty)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverables ===&lt;br /&gt;
&lt;br /&gt;
Submit a PDF with your code and screenshots of:&lt;br /&gt;
&lt;br /&gt;
* All transform modes tested and working&lt;br /&gt;
* Detailed kernel logs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-kernel-programming-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Kernel Programming Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;module-macros-and-metadata&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Module Macros and Metadata ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/module.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/kernel.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/init.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Declare init and exit functions */&lt;br /&gt;
module_init(my_init_function);&lt;br /&gt;
module_exit(my_exit_function);&lt;br /&gt;
&lt;br /&gt;
/* Module metadata (required) */&lt;br /&gt;
MODULE_LICENSE(&amp;quot;GPL&amp;quot;);                    /* Must be GPL-compatible */&lt;br /&gt;
MODULE_AUTHOR(&amp;quot;Your Name &amp;lt;email&amp;gt;&amp;quot;);&lt;br /&gt;
MODULE_DESCRIPTION(&amp;quot;Brief description&amp;quot;);&lt;br /&gt;
MODULE_VERSION(&amp;quot;1.0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* Init function signature */&lt;br /&gt;
static int __init my_init_function(void)&lt;br /&gt;
{&lt;br /&gt;
    /* Return 0 on success, negative error code on failure */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Exit function signature */&lt;br /&gt;
static void __exit my_exit_function(void)&lt;br /&gt;
{&lt;br /&gt;
    /* No return value */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;memory-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Memory Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/slab.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Allocate memory */&lt;br /&gt;
void *ptr = kmalloc(size, GFP_KERNEL);&lt;br /&gt;
if (!ptr)&lt;br /&gt;
    return -ENOMEM;&lt;br /&gt;
&lt;br /&gt;
/* Allocate and zero memory */&lt;br /&gt;
void *ptr = kzalloc(size, GFP_KERNEL);&lt;br /&gt;
&lt;br /&gt;
/* Free memory */&lt;br /&gt;
kfree(ptr);&lt;br /&gt;
&lt;br /&gt;
/* GFP flags:&lt;br /&gt;
 * GFP_KERNEL  - Normal allocation, can sleep&lt;br /&gt;
 * GFP_ATOMIC  - Cannot sleep (use in interrupt context)&lt;br /&gt;
 * GFP_USER    - Allocate for userspace&lt;br /&gt;
 */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;data-transfer-functions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Data Transfer Functions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/uaccess.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Copy to userspace */&lt;br /&gt;
if (copy_to_user(user_ptr, kernel_ptr, size))&lt;br /&gt;
    return -EFAULT;&lt;br /&gt;
&lt;br /&gt;
/* Copy from userspace */&lt;br /&gt;
if (copy_from_user(kernel_ptr, user_ptr, size))&lt;br /&gt;
    return -EFAULT;&lt;br /&gt;
&lt;br /&gt;
/* Get a single value from userspace */&lt;br /&gt;
int val;&lt;br /&gt;
if (get_user(val, (int __user *)user_ptr))&lt;br /&gt;
    return -EFAULT;&lt;br /&gt;
&lt;br /&gt;
/* Put a single value to userspace */&lt;br /&gt;
if (put_user(val, (int __user *)user_ptr))&lt;br /&gt;
    return -EFAULT;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;synchronization&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Synchronization ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/mutex.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Declare mutex */&lt;br /&gt;
static DEFINE_MUTEX(my_mutex);&lt;br /&gt;
&lt;br /&gt;
/* Or dynamic initialization */&lt;br /&gt;
struct mutex my_mutex;&lt;br /&gt;
mutex_init(&amp;amp;my_mutex);&lt;br /&gt;
&lt;br /&gt;
/* Lock (sleeps if unavailable) */&lt;br /&gt;
mutex_lock(&amp;amp;my_mutex);&lt;br /&gt;
/* ... critical section ... */&lt;br /&gt;
mutex_unlock(&amp;amp;my_mutex);&lt;br /&gt;
&lt;br /&gt;
/* Lock (interruptible by signals) */&lt;br /&gt;
if (mutex_lock_interruptible(&amp;amp;my_mutex))&lt;br /&gt;
    return -ERESTARTSYS;&lt;br /&gt;
/* ... critical section ... */&lt;br /&gt;
mutex_unlock(&amp;amp;my_mutex);&lt;br /&gt;
&lt;br /&gt;
/* Try lock (non-blocking) */&lt;br /&gt;
if (mutex_trylock(&amp;amp;my_mutex)) {&lt;br /&gt;
    /* Got lock */&lt;br /&gt;
    /* ... critical section ... */&lt;br /&gt;
    mutex_unlock(&amp;amp;my_mutex);&lt;br /&gt;
} else {&lt;br /&gt;
    /* Lock held by someone else */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;logging-and-debugging&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Logging and Debugging ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/kernel.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Log levels */&lt;br /&gt;
printk(KERN_EMERG   &amp;quot;Emergency\n&amp;quot;);    /* System unusable */&lt;br /&gt;
printk(KERN_ALERT   &amp;quot;Alert\n&amp;quot;);        /* Action must be taken */&lt;br /&gt;
printk(KERN_CRIT    &amp;quot;Critical\n&amp;quot;);     /* Critical condition */&lt;br /&gt;
printk(KERN_ERR     &amp;quot;Error\n&amp;quot;);        /* Error condition */&lt;br /&gt;
printk(KERN_WARNING &amp;quot;Warning\n&amp;quot;);      /* Warning */&lt;br /&gt;
printk(KERN_NOTICE  &amp;quot;Notice\n&amp;quot;);       /* Normal but significant */&lt;br /&gt;
printk(KERN_INFO    &amp;quot;Info\n&amp;quot;);         /* Informational */&lt;br /&gt;
printk(KERN_DEBUG   &amp;quot;Debug\n&amp;quot;);        /* Debug messages */&lt;br /&gt;
&lt;br /&gt;
/* Formatted output (like printf) */&lt;br /&gt;
printk(KERN_INFO &amp;quot;Value: %d, String: %s\n&amp;quot;, value, string);&lt;br /&gt;
&lt;br /&gt;
/* Access current process info */&lt;br /&gt;
printk(KERN_INFO &amp;quot;PID: %d, Name: %s\n&amp;quot;, current-&amp;gt;pid, current-&amp;gt;comm);&lt;br /&gt;
&lt;br /&gt;
/* View logs */&lt;br /&gt;
/* dmesg */&lt;br /&gt;
/* tail -f /var/log/kern.log */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-error-codes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Error Codes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#include &amp;lt;linux/errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
return 0;          /* Success */&lt;br /&gt;
return -ENOMEM;    /* Out of memory */&lt;br /&gt;
return -EFAULT;    /* Bad address (copy_to/from_user failed) */&lt;br /&gt;
return -EINVAL;    /* Invalid argument */&lt;br /&gt;
return -ENOSPC;    /* No space left on device */&lt;br /&gt;
return -EBUSY;     /* Device or resource busy */&lt;br /&gt;
return -EIO;       /* I/O error */&lt;br /&gt;
return -ENODEV;    /* No such device */&lt;br /&gt;
return -EPERM;     /* Operation not permitted */&lt;br /&gt;
return -EACCES;    /* Permission denied */&lt;br /&gt;
return -ERESTARTSYS; /* Interrupted system call */&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-issues-and-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Issues and Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;compilation-errors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Compilation Errors ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;No rule to make target&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;make: *** No rule to make target &amp;#039;modules&amp;#039;.  Stop.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Wrong KDIR path in Makefile&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Verify kernel headers are installed and path is correct:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -l /lib/modules/$(uname -r)/build&lt;br /&gt;
# Should exist and link to kernel source&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;missing MODULE_LICENSE&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;WARNING: modpost: missing MODULE_LICENSE() in ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Forgot to add MODULE_LICENSE macro&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Add to your module:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;MODULE_LICENSE(&amp;quot;GPL&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;implicit declaration of function&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Missing &amp;lt;code&amp;gt;#include&amp;lt;/code&amp;gt; directive&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Add appropriate header. Common headers:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;lt;linux/kernel.h&amp;amp;gt;&amp;lt;/code&amp;gt; - Core kernel functions&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;lt;linux/uaccess.h&amp;amp;gt;&amp;lt;/code&amp;gt; - copy_to_user, copy_from_user&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;lt;linux/slab.h&amp;amp;gt;&amp;lt;/code&amp;gt; - kmalloc, kfree&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;lt;linux/mutex.h&amp;amp;gt;&amp;lt;/code&amp;gt; - Mutex functions&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;module-loading-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Module Loading Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;Invalid module format&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;insmod: ERROR: could not insert module: Invalid module format&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Module compiled for different kernel version&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Clean and rebuild:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;make clean&lt;br /&gt;
make&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensure &amp;lt;code&amp;gt;uname -r&amp;lt;/code&amp;gt; matches the kernel you&amp;#039;re running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;Operation not permitted&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Need root privileges&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Use sudo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo insmod module.ko&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;Device or resource busy&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;rmmod: ERROR: Module is in use&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Device is still open or module is in use&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Close all programs using the device:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;lsof /dev/superpipe  # Find processes using device&lt;br /&gt;
kill &amp;lt;PID&amp;gt;           # Kill processes if necessary&lt;br /&gt;
sudo rmmod module&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;runtime-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Runtime Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: &amp;amp;quot;Bad address&amp;amp;quot; (-EFAULT) from read/write&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Forgot copy_to_user/copy_from_user&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Never directly access user pointers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* WRONG */&lt;br /&gt;
strcpy(user_buffer, kernel_data);&lt;br /&gt;
&lt;br /&gt;
/* CORRECT */&lt;br /&gt;
if (copy_to_user(user_buffer, kernel_data, len))&lt;br /&gt;
    return -EFAULT;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: Kernel panic on module load&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Accessing NULL pointer or uninitialized memory&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Always check return values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;buffer = kmalloc(size, GFP_KERNEL);&lt;br /&gt;
if (!buffer) {&lt;br /&gt;
    printk(KERN_ERR &amp;quot;Failed to allocate\n&amp;quot;);&lt;br /&gt;
    return -ENOMEM;  /* Don&amp;#039;t continue with NULL pointer! */&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: Data corruption with concurrent access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Missing mutex locks&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039; Protect all shared data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Acquire lock before accessing shared state */&lt;br /&gt;
mutex_lock_interruptible(&amp;amp;dev_state.lock);&lt;br /&gt;
&lt;br /&gt;
/* Access shared data */&lt;br /&gt;
&lt;br /&gt;
/* Release lock */&lt;br /&gt;
mutex_unlock(&amp;amp;dev_state.lock);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Issue: Module stuck, can&amp;#039;t unload&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Process stuck in kernel code (infinite loop or deadlock)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fix:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Kill the hung process&lt;br /&gt;
* If that doesn&amp;#039;t work, reboot (this is why VMs are recommended!)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;debugging-strategies&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Debugging Strategies ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Strategy 1: Liberally use printk&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;printk(KERN_DEBUG &amp;quot;Entering function, buffer_len=%zu\n&amp;quot;, dev_state.buffer_len);&lt;br /&gt;
printk(KERN_DEBUG &amp;quot;After copy, to_write=%zu\n&amp;quot;, to_write);&lt;br /&gt;
printk(KERN_DEBUG &amp;quot;Exiting function, returning %zd\n&amp;quot;, ret);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Strategy 2: Check return values&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;ret = copy_from_user(...);&lt;br /&gt;
printk(KERN_DEBUG &amp;quot;copy_from_user returned %lu\n&amp;quot;, ret);&lt;br /&gt;
if (ret) {&lt;br /&gt;
    printk(KERN_ERR &amp;quot;Failed to copy %lu bytes\n&amp;quot;, ret);&lt;br /&gt;
    return -EFAULT;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Strategy 3: Dump buffer contents&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;/* Hexdump helper */&lt;br /&gt;
void hexdump(const char *data, size_t len)&lt;br /&gt;
{&lt;br /&gt;
    size_t i;&lt;br /&gt;
    printk(KERN_DEBUG &amp;quot;Hexdump (%zu bytes):\n&amp;quot;, len);&lt;br /&gt;
    for (i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
        printk(KERN_CONT &amp;quot;%02x &amp;quot;, (unsigned char)data[i]);&lt;br /&gt;
        if ((i + 1) % 16 == 0)&lt;br /&gt;
            printk(KERN_CONT &amp;quot;\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    printk(KERN_CONT &amp;quot;\n&amp;quot;);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Strategy 4: Monitor kernel logs in real-time&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In one terminal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo dmesg -w&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In another terminal, run your tests. You&amp;#039;ll see kernel messages appear immediately.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Strategy 5: Add assertions&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;#define ASSERT(cond) \&lt;br /&gt;
    do { \&lt;br /&gt;
        if (!(cond)) { \&lt;br /&gt;
            printk(KERN_ERR &amp;quot;Assertion failed: %s\n&amp;quot;, #cond); \&lt;br /&gt;
            BUG(); \&lt;br /&gt;
        } \&lt;br /&gt;
    } while (0)&lt;br /&gt;
&lt;br /&gt;
ASSERT(buffer_len &amp;lt;= BUFFER_SIZE);&lt;br /&gt;
ASSERT(mode &amp;lt; TRANSFORM_MAX);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;BUG()&amp;lt;/code&amp;gt; causes a kernel oops. Use only for debugging, remove before submission.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== For Further Study ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;advanced-topics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Advanced Topics ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Multiple Device Instances&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Extend superpipe to support multiple independent devices (&amp;lt;code&amp;gt;/dev/superpipe0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/superpipe1&amp;lt;/code&amp;gt;, etc.) with separate buffers and modes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hint:&amp;#039;&amp;#039;&amp;#039; Use dynamic miscdevice allocation and embed miscdevice in device structure.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Per-File-Descriptor State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Allow each open() to have independent buffer and transformation mode using &amp;lt;code&amp;gt;file-&amp;amp;gt;private_data&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Non-Blocking I/O and Poll&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Implement &amp;lt;code&amp;gt;O_NONBLOCK&amp;lt;/code&amp;gt; support and &amp;lt;code&amp;gt;poll()&amp;lt;/code&amp;gt; operation so userspace can use &amp;lt;code&amp;gt;select()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;epoll()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Sysfs Interface&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Expose device statistics via sysfs (&amp;lt;code&amp;gt;/sys/class/misc/superpipe/&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
* Total bytes read&lt;br /&gt;
* Total bytes written&lt;br /&gt;
* Current mode&lt;br /&gt;
* Buffer usage&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Advanced Transformations&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Implement more complex transformations:&lt;br /&gt;
&lt;br /&gt;
* ROT13 cipher&lt;br /&gt;
* Base64 encoding/decoding&lt;br /&gt;
* Compression (simple run-length encoding)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. Ring Buffer Implementation&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Replace linear buffer with circular ring buffer for better performance (no memmove on read).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-documentation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Documentation ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Kernel Documentation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View kernel documentation&lt;br /&gt;
cd /usr/src/linux-headers-$(uname -r)&lt;br /&gt;
ls Documentation/&lt;br /&gt;
&lt;br /&gt;
# Useful sections:&lt;br /&gt;
# - kernel-hacking/&lt;br /&gt;
# - driver-api/&lt;br /&gt;
# - core-api/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Online Resources:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Linux Kernel Documentation:&amp;#039;&amp;#039;&amp;#039; https://www.kernel.org/doc/html/latest/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Linux Device Drivers (LDD3):&amp;#039;&amp;#039;&amp;#039; Classic book, available online&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kernel Newbies:&amp;#039;&amp;#039;&amp;#039; https://kernelnewbies.org/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;KernelCI:&amp;#039;&amp;#039;&amp;#039; https://kernelci.org/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LKML Archives:&amp;#039;&amp;#039;&amp;#039; https://lkml.org/&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bootlin Linux Source View:&amp;#039;&amp;#039;&amp;#039; https://elixir.bootlin.com/linux&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Manual pages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 2 open      # open() system call&lt;br /&gt;
man 2 read      # read() system call&lt;br /&gt;
man 2 write     # write() system call&lt;br /&gt;
man 2 ioctl     # ioctl() system call&lt;br /&gt;
man 7 capabilities  # Linux capabilities&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Kernel source as reference:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Find examples of miscdevice usage&lt;br /&gt;
cd /usr/src/linux-source-*/&lt;br /&gt;
grep -r &amp;quot;misc_register&amp;quot; drivers/&lt;br /&gt;
&lt;br /&gt;
# Example drivers to study:&lt;br /&gt;
# - drivers/char/mem.c (/dev/null, /dev/zero, /dev/random)&lt;br /&gt;
# - drivers/char/misc.c (miscdevice framework itself)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;addr2line&amp;lt;/code&amp;gt;: Convert addresses in oops messages to source lines&lt;br /&gt;
* &amp;lt;code&amp;gt;objdump&amp;lt;/code&amp;gt;: Disassemble kernel modules&lt;br /&gt;
* &amp;lt;code&amp;gt;gdb&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;/proc/kcore&amp;lt;/code&amp;gt;: Debug live kernel&lt;br /&gt;
* &amp;lt;code&amp;gt;SystemTap&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;eBPF&amp;lt;/code&amp;gt;: Dynamic kernel tracing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Remember:&amp;#039;&amp;#039;&amp;#039; The Linux kernel is one of the largest open-source projects. Reading kernel code is the best way to learn kernel programming.&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8199</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8199"/>
		<updated>2025-12-19T14:41:43Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;br /&gt;
* Lab 8 - [[OS Lab 8 - Transport and Security]]&lt;br /&gt;
* Lab 9 - [[OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)]]&lt;br /&gt;
* Lab 10 - [[OS Lab 10 - Containers and Docker]]&lt;br /&gt;
* Lab 11 - [[OS Lab 11 - Linux Kernel Modules]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8198</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8198"/>
		<updated>2025-12-19T14:41:29Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;br /&gt;
* Lab 8 - [[OS Lab 8 - Transport and Security]]&lt;br /&gt;
* Lab 9 - [[OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)]]&lt;br /&gt;
* Lab 10 - [[OS Lab 10 - Containers and Docker]]&lt;br /&gt;
* Lab 10 - [[OS Lab 11 - Linux Kernel Modules]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_9_-_Application_Protocols:_DNS,_SSH,_HTTP(S)&amp;diff=8197</id>
		<title>OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_9_-_Application_Protocols:_DNS,_SSH,_HTTP(S)&amp;diff=8197"/>
		<updated>2025-12-16T17:01:41Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Part 1: Verifying SSH Installation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Understand core internet infrastructure: well-known ports, DNS hierarchy, and the roles of authoritative vs. recursive DNS servers.&lt;br /&gt;
* Use DNS tools (&amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nslookup&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;host&amp;lt;/code&amp;gt;) to query records and analyze responses, including TTLs and caching behavior.&lt;br /&gt;
* Configure secure SSH access using Ed25519 keys and explain SSH’s TOFU model compared with certificate-based PKI.&lt;br /&gt;
* Build and route HTTP(S) services using a reverse proxy, and understand why this pattern underlies modern cloud architectures.&lt;br /&gt;
* Diagnose common application-layer connectivity issues using protocol-specific debugging techniques.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-journey-through-the-network-stack&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Journey Through the Network Stack ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7 and 8, we systematically constructed the entire networking stack from the physical layer upward:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 7 (Network Infrastructure)&amp;#039;&amp;#039;&amp;#039;: We built the foundational plumbing—network interfaces, MAC addresses, IP addressing, routing tables, network bridges, and the mechanisms that allow one machine to physically reach another across networks. We demonstrated how the kernel routes packets from source to destination based on Ethernet and Layer 3 IP addressing.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 8 (Transport and Security)&amp;#039;&amp;#039;&amp;#039;: We ascended to the transport layer, introducing the critical concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to enable process-to-process communication. We explored the fundamental trade-offs between UDP&amp;#039;s connectionless simplicity and TCP&amp;#039;s reliable, connection-oriented delivery. Most importantly, we implemented Transport Layer Security (TLS) using Public Key Infrastructure (PKI) to protect data in transit from eavesdropping and tampering.&lt;br /&gt;
&lt;br /&gt;
We now reach the pinnacle of the network stack: called the Application Layer in the TCP/IP model. This is where protocols transition from answering &amp;amp;quot;how do we reliably deliver data between processes?&amp;amp;quot; to addressing &amp;amp;quot;what should we do with this data and how should applications interact?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-application-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Application Layer ===&lt;br /&gt;
&lt;br /&gt;
Consider what you actually accomplish with computers in daily practice. You do not consciously think about TCP sequence numbers, IP routing algorithms, or TLS cipher suites. Instead, you engage in high-level activities:&lt;br /&gt;
&lt;br /&gt;
* You type a domain name (e.g., &amp;amp;quot;example.com&amp;amp;quot;) and a website appears instantaneously (DNS + HTTP)&lt;br /&gt;
* You connect to a remote server to execute commands securely (SSH)&lt;br /&gt;
* You upload files, stream videos, send API requests, interact with cloud services (predominantly HTTP/HTTPS)&lt;br /&gt;
&lt;br /&gt;
The application layer is where networking infrastructure becomes visible as useful services. These are the protocols that system administrators, DevOps engineers, and software developers interact with constantly. While hundreds of application protocols exist, three protocols dominate modern networking:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DNS (Port 53)&amp;#039;&amp;#039;&amp;#039;: The distributed directory service that answers &amp;amp;quot;How do I find the IP address for this domain name?&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH (Port 22)&amp;#039;&amp;#039;&amp;#039;: The secure remote access protocol that answers &amp;amp;quot;How do I securely administer remote systems?&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS (Ports 80/443)&amp;#039;&amp;#039;&amp;#039;: The hypertext transfer protocol that has evolved far beyond web browsing to become the answer to &amp;amp;quot;How do I communicate application data?&amp;amp;quot; for nearly everything.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-complete-network-request-journey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Complete Network Request Journey ===&lt;br /&gt;
&lt;br /&gt;
By the end of this lab, you will understand the complete, end-to-end journey of a modern web request. When you type &amp;lt;code&amp;gt;https://api.example.com/users&amp;lt;/code&amp;gt; into your browser, the following sequence occurs:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DNS Resolution (This lab)&amp;#039;&amp;#039;&amp;#039;: Your system queries DNS to resolve &amp;lt;code&amp;gt;api.example.com&amp;lt;/code&amp;gt; to an IP address (e.g., 203.0.113.50)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routing (Lab 7)&amp;#039;&amp;#039;&amp;#039;: The kernel consults routing tables to determine the next hop toward that IP address&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Connection (Lab 8)&amp;#039;&amp;#039;&amp;#039;: Your system establishes a three-way handshake to create a reliable TCP connection to port 443 at that IP address&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake (Lab 8)&amp;#039;&amp;#039;&amp;#039;: The client and server perform a TLS handshake, establishing an encrypted tunnel using certificates to verify the server&amp;#039;s identity&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;HTTP Request (This lab)&amp;#039;&amp;#039;&amp;#039;: Your browser sends an HTTP GET request to the &amp;lt;code&amp;gt;/users&amp;lt;/code&amp;gt; path over the encrypted connection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reverse Proxy Routing (This lab)&amp;#039;&amp;#039;&amp;#039;: A reverse proxy on the server side examines the request path and routes it to the appropriate backend service&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Response Journey&amp;#039;&amp;#039;&amp;#039;: The response flows back through all these layers—HTTP response, TLS encryption, TCP segments, IP packets, Ethernet frames—until it reaches your browser&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Topology&amp;#039;&amp;#039;&amp;#039;: This lab builds upon the network namespace topology established in Lab 8. You should have:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Red namespace&amp;#039;&amp;#039;&amp;#039;: Simulated host with IP address 10.0.0.1/24&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blue namespace&amp;#039;&amp;#039;&amp;#039;: Simulated host with IP address 10.0.0.2/24&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host system&amp;#039;&amp;#039;&amp;#039;: Bridge interface (br0) connecting the namespaces&lt;br /&gt;
* Full bidirectional connectivity verified in Lab 8&lt;br /&gt;
&lt;br /&gt;
If you did not complete Lab 8 or your topology has been reset, you will need to recreate it following Lab 8&amp;#039;s setup instructions before proceeding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed. Run these commands on your host system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y openssh-server openssh-client dnsutils bind9-dnsutils \&lt;br /&gt;
                    caddy curl net-tools&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Package descriptions:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssh-server&amp;lt;/code&amp;gt;: OpenSSH server daemon for accepting SSH connections&lt;br /&gt;
* &amp;lt;code&amp;gt;openssh-client&amp;lt;/code&amp;gt;: OpenSSH client tools (ssh, scp, sftp) for initiating connections&lt;br /&gt;
* &amp;lt;code&amp;gt;dnsutils&amp;lt;/code&amp;gt;: DNS client utilities including &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;nslookup&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;bind9-dnsutils&amp;lt;/code&amp;gt;: Additional DNS diagnostic tools including &amp;lt;code&amp;gt;host&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: Modern web server with automatic HTTPS and simple configuration syntax&lt;br /&gt;
* &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;: Command-line HTTP client for testing and debugging&lt;br /&gt;
* &amp;lt;code&amp;gt;net-tools&amp;lt;/code&amp;gt;: Legacy networking tools (netstat, ifconfig) for compatibility&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 7 (Network Fundamentals)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, subnet masks, and CIDR notation&lt;br /&gt;
* Routing tables and default gateways&lt;br /&gt;
* Network namespaces and virtual ethernet pairs&lt;br /&gt;
* Bridge devices and how they connect network segments&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 8 (Transport and Security)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* TCP and UDP transport protocols&lt;br /&gt;
* Port numbers and socket addresses (IP:PORT)&lt;br /&gt;
* TLS encryption and certificate-based authentication&lt;br /&gt;
* The Public Key Infrastructure (PKI) trust model&lt;br /&gt;
* Basic hostname resolution using &amp;lt;code&amp;gt;/etc/hosts&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;General Linux Skills&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Command-line text manipulation (grep, awk, sed, cut)&lt;br /&gt;
* Process management (starting, stopping, viewing processes)&lt;br /&gt;
* File editing with text editors (nano, vim, or your preference)&lt;br /&gt;
* Basic Bash scripting (variables, loops, conditionals)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* The client-server model of network communication&lt;br /&gt;
* What an IP address represents and how packets are routed between networks&lt;br /&gt;
* The concept of a socket as the combination of IP address and port number&lt;br /&gt;
* Basic cryptographic concepts (public/private keys, certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-recap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer Recap ===&lt;br /&gt;
&lt;br /&gt;
Before diving into application protocols, let us briefly revisit why the transport layer exists and what problem it solves. This context is essential for understanding well-known ports and application protocol design.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Fundamental Problem&amp;#039;&amp;#039;&amp;#039;: An IP address identifies a machine on the network, but machines do not communicate with machines. &amp;#039;&amp;#039;&amp;#039;Processes&amp;#039;&amp;#039;&amp;#039; (running programs) communicate with processes. When a packet arrives at your machine&amp;#039;s IP address, the kernel must determine which of potentially hundreds of running processes should receive that packet.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution&amp;#039;&amp;#039;&amp;#039;: The transport layer introduces &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;—16-bit unsigned integers (range 0-65535) that identify specific communication endpoints. When combined with an IP address, a port creates a unique &amp;#039;&amp;#039;&amp;#039;socket address&amp;#039;&amp;#039;&amp;#039; (written as IP:PORT, e.g., 192.168.1.50:443) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
A complete connection is identified by a &amp;#039;&amp;#039;&amp;#039;five-tuple&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Source IP address&lt;br /&gt;
# Source port&lt;br /&gt;
# Destination IP address&lt;br /&gt;
# Destination port&lt;br /&gt;
# Protocol (TCP or UDP)&lt;br /&gt;
&lt;br /&gt;
This five-tuple uniquely identifies a communication channel. For example:&lt;br /&gt;
&lt;br /&gt;
* Client: 192.168.1.50:54321 → Server: 203.0.113.10:443 (HTTPS connection)&lt;br /&gt;
* Client: 10.0.0.1:35678 → Server: 10.0.0.2:22 (SSH connection)&lt;br /&gt;
&lt;br /&gt;
With this foundation established, we can now explore how standardized port numbers enable service discovery at internet scale.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;well-known-ports&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Well-Known Ports ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-problem-service-discovery-without-coordination&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Problem: Service Discovery Without Coordination ====&lt;br /&gt;
&lt;br /&gt;
Consider a scenario where you want to connect to a web server running at IP address &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;. You know the machine&amp;#039;s network address, but you face a critical problem: &amp;#039;&amp;#039;&amp;#039;Which port is the web server listening on?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server could be bound to any of 65,535 possible port numbers:&lt;br /&gt;
&lt;br /&gt;
* Port 80? (Traditional HTTP)&lt;br /&gt;
* Port 8080? (Alternative HTTP)&lt;br /&gt;
* Port 3000? (Common development port)&lt;br /&gt;
* Port 9000? (Arbitrary choice)&lt;br /&gt;
* Some completely random port like 47281?&lt;br /&gt;
&lt;br /&gt;
Without knowing the correct port, you cannot establish a connection. It is analogous to knowing someone&amp;#039;s street address but not their apartment number in a massive building with 65,535 apartments.&lt;br /&gt;
&lt;br /&gt;
You could attempt to connect to all 65,535 ports sequentially (this is precisely what port scanning tools like &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; do), but this approach is:&lt;br /&gt;
&lt;br /&gt;
* Extremely slow (trying all ports could take minutes or hours)&lt;br /&gt;
* Inefficient (wastes network bandwidth and computational resources)&lt;br /&gt;
* Potentially suspicious (may trigger intrusion detection systems)&lt;br /&gt;
* Impractical for everyday use (users expect instant connections)&lt;br /&gt;
&lt;br /&gt;
There must be a superior solution.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-solution-standardized-port-assignments&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Solution: Standardized Port Assignments ====&lt;br /&gt;
&lt;br /&gt;
The early architects of the internet solved this problem through an elegantly simple mechanism: &amp;#039;&amp;#039;&amp;#039;social convention&amp;#039;&amp;#039;&amp;#039;. The Internet Assigned Numbers Authority (IANA) maintains an official registry of port number assignments, and the community agrees to follow these standards.&lt;br /&gt;
&lt;br /&gt;
This is not a technical enforcement mechanism—there is no kernel code that prevents you from running an SSH server on port 8080 or an HTTP server on port 22. Instead, it is a &amp;#039;&amp;#039;&amp;#039;social contract&amp;#039;&amp;#039;&amp;#039;: we collectively agree that certain services will use certain ports, making the internet predictable and functional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-number-space-division&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Port Number Space Division ====&lt;br /&gt;
&lt;br /&gt;
The IANA divides the port number space into three categories:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports are reserved for standard, widely-used services. Key characteristics:&lt;br /&gt;
&lt;br /&gt;
* Officially registered with IANA for specific protocols&lt;br /&gt;
* Binding to these ports requires elevated privileges (root/administrator) on Unix-like systems&lt;br /&gt;
* This privilege requirement is a security feature: it prevents normal users from impersonating critical services&lt;br /&gt;
* Examples: HTTP (80), HTTPS (443), SSH (22), DNS (53), SMTP (25)&lt;br /&gt;
&lt;br /&gt;
The privilege requirement exists because if any user could bind to port 80, they could impersonate a web server and potentially trick other programs or users into trusting them.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports can be registered with IANA for specific services, but enforcement is less strict. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* No special privileges required to bind&lt;br /&gt;
* Software vendors often register ports for their applications&lt;br /&gt;
* Examples: MySQL (3306), PostgreSQL (5432), MongoDB (27017), Redis (6379)&lt;br /&gt;
* Applications can use these ports without requiring root access&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Dynamic/Private/Ephemeral Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports are used for temporary client-side connections. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* Never registered for permanent services&lt;br /&gt;
* Assigned automatically by the operating system when a client initiates an outbound connection&lt;br /&gt;
* The client doesn&amp;#039;t choose these ports explicitly&lt;br /&gt;
* Used briefly for the duration of a connection, then released back to the pool&lt;br /&gt;
&lt;br /&gt;
When your web browser connects to a server, it might use local address &amp;lt;code&amp;gt;192.168.1.50:52341&amp;lt;/code&amp;gt; (your machine + random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server + standard HTTPS port).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-social-contract-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Social Contract in Practice ====&lt;br /&gt;
&lt;br /&gt;
When a server administrator configures a service, they &amp;#039;&amp;#039;&amp;#039;listen on the well-known port&amp;#039;&amp;#039;&amp;#039; for that service type. Clients, in turn, &amp;#039;&amp;#039;&amp;#039;assume&amp;#039;&amp;#039;&amp;#039; servers use these standard ports. This mutual agreement eliminates the need for explicit coordination.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example 1: SSH Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you execute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh admin@server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The SSH client automatically attempts to connect to port 22. You do not need to specify the port. The client assumes the server follows the convention. Under the hood, this command is equivalent to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh -p 22 admin@server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;-p 22&amp;lt;/code&amp;gt; is implicit because port 22 is the well-known port for SSH.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example 2: Web Browsing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you enter a URL in your browser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;http://example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your browser automatically connects to port 80. Similarly, for:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;https://example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your browser connects to port 443. These behaviors are hardcoded into HTTP client software based on the URL scheme.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Breaking the Convention&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You are not technically required to follow these conventions. You can run an SSH server on port 8022, a web server on port 3000, or DNS on port 5353. However, breaking conventions creates friction—clients need explicit instructions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Non-standard SSH port requires explicit specification&lt;br /&gt;
ssh -p 8022 admin@server.example.com&lt;br /&gt;
&lt;br /&gt;
# Non-standard HTTP port must be included in URL&lt;br /&gt;
curl http://example.com:3000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analogy&amp;#039;&amp;#039;&amp;#039;: Well-known ports function like standardized electrical outlets. In Europe, you expect 220V AC outlets with a specific two-prong configuration. You can plug in any device without checking each outlet—you trust the convention. Similarly, you can connect to any SSH server without checking its port—you trust it will be on port 22.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As a system administrator or developer, you will usually interact with these port numbers:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 20/21&amp;#039;&amp;#039;&amp;#039;: FTP (File Transfer Protocol) - Legacy, rarely used today&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 22&amp;#039;&amp;#039;&amp;#039;: SSH (Secure Shell) - Remote system access&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 23&amp;#039;&amp;#039;&amp;#039;: Telnet - Legacy insecure remote access (NEVER USE)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 25&amp;#039;&amp;#039;&amp;#039;: SMTP (Simple Mail Transfer Protocol) - Email sending&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 53&amp;#039;&amp;#039;&amp;#039;: DNS (Domain Name System) - Name resolution&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 80&amp;#039;&amp;#039;&amp;#039;: HTTP (Hypertext Transfer Protocol) - Web traffic unencrypted&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 110&amp;#039;&amp;#039;&amp;#039;: POP3 (Post Office Protocol) - Email retrieval (legacy)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 143&amp;#039;&amp;#039;&amp;#039;: IMAP (Internet Message Access Protocol) - Modern email retrieval&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 443&amp;#039;&amp;#039;&amp;#039;: HTTPS (HTTP Secure) - Encrypted web traffic&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 3306&amp;#039;&amp;#039;&amp;#039;: MySQL database server&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 5432&amp;#039;&amp;#039;&amp;#039;: PostgreSQL database server&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 6379&amp;#039;&amp;#039;&amp;#039;: Redis in-memory database&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 8080&amp;#039;&amp;#039;&amp;#039;: Alternative HTTP port (often used for development)&lt;br /&gt;
&lt;br /&gt;
This social contract—standardized port numbers—is a foundational element that makes the internet function at global scale. It exemplifies how simple conventions can solve complex coordination problems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;domain-name-system-dns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Domain Name System (DNS) ===&lt;br /&gt;
&lt;br /&gt;
The human-computer impedance mismatch is a fundamental challenge in computing: humans prefer memorable, meaningful names, while computers require numerical addresses. The Domain Name System (DNS) bridges this gap by providing a distributed, hierarchical database that maps human-readable domain names to machine-usable IP addresses (and other information).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;human-memory-vs-computer-addressing&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Human Memory vs. Computer Addressing ====&lt;br /&gt;
&lt;br /&gt;
IP addresses are the actual addressing mechanism for network communication. When your computer needs to send packets to a destination, it must know that destination&amp;#039;s IP address. However, IP addresses present several problems for human users:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Memorization Difficulty&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
IPv4 addresses like &amp;lt;code&amp;gt;172.217.14.206&amp;lt;/code&amp;gt; are difficult for humans to remember and type accurately. IPv6 addresses like &amp;lt;code&amp;gt;2607:f8b0:4004:c07::64&amp;lt;/code&amp;gt; are even worse. While you might remember your own phone number, remembering hundreds of IP addresses for services you use is impractical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Instability and Change&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
IP addresses can change due to:&lt;br /&gt;
&lt;br /&gt;
* Infrastructure migrations (moving servers between data centers)&lt;br /&gt;
* Load balancing requirements (distributing traffic across multiple servers)&lt;br /&gt;
* Failover scenarios (switching to backup servers during outages)&lt;br /&gt;
* Dynamic allocation (DHCP assigns different IPs over time)&lt;br /&gt;
&lt;br /&gt;
If users memorized IP addresses, every infrastructure change would break their connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Lack of Semantic Meaning&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
An IP address like &amp;lt;code&amp;gt;198.51.100.42&amp;lt;/code&amp;gt; conveys no information about what service it provides or which organization operates it. The name &amp;lt;code&amp;gt;store.example.com&amp;lt;/code&amp;gt; immediately suggests it&amp;#039;s a commercial store operated by Example Corporation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Service Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A single IP address can host multiple services (using different ports) or multiple websites (using HTTP virtual hosting). The domain name helps identify which specific service the user wants.&lt;br /&gt;
&lt;br /&gt;
The Solution: Domain Name System&lt;br /&gt;
&lt;br /&gt;
DNS solves these problems by creating a globally distributed, hierarchical naming system that maps domain names to IP addresses and other resource records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Historical Context&amp;#039;&amp;#039;&amp;#039;: Before DNS, the internet used a centralized hosts file (&amp;lt;code&amp;gt;/etc/hosts&amp;lt;/code&amp;gt; on Unix systems) maintained by the Network Information Center (NIC) at Stanford Research Institute. Administrators would request IP-to-hostname mappings, and NIC would periodically distribute an updated hosts file to all connected systems. This approach did not scale beyond a few thousand hosts.&lt;br /&gt;
&lt;br /&gt;
In 1983, Paul Mockapetris designed the Domain Name System (RFC 882 and RFC 883, later superseded by RFC 1034 and RFC 1035), which has remained the foundation of internet naming for over 40 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-hierarchy-organizing-the-global-namespace&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Hierarchy: Organizing the Global Namespace ====&lt;br /&gt;
&lt;br /&gt;
DNS organizes domain names in a hierarchical tree structure, reading from right to left:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.engineering.example.com.&lt;br /&gt;
 |       |          |      |&lt;br /&gt;
 |       |          |      └── Root (empty label)&lt;br /&gt;
 |       |          └────────── Top-Level Domain (TLD)&lt;br /&gt;
 |       └───────────────────── Second-Level Domain (SLD)&lt;br /&gt;
 └───────────────────────────── Subdomain(s)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Root Domain (&amp;amp;quot;.&amp;amp;quot;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
At the top of the hierarchy is the root domain, represented by an empty label. Fully qualified domain names (FQDNs) technically end with a dot: &amp;lt;code&amp;gt;example.com.&amp;lt;/code&amp;gt; The trailing dot is usually omitted in practice, but it represents the root.&lt;br /&gt;
&lt;br /&gt;
There are 13 root name server systems (labeled A through M: a.root-servers.net through m.root-servers.net) operated by different organizations worldwide. These are the authoritative source for information about top-level domains.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Top-Level Domains (TLDs)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TLDs are the highest level of the DNS hierarchy below the root. Categories include:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Generic TLDs (gTLDs)&amp;#039;&amp;#039;&amp;#039;: .com, .org, .net, .edu, .gov, .mil, .int&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Country-Code TLDs (ccTLDs)&amp;#039;&amp;#039;&amp;#039;: .us (United States), .uk (United Kingdom), .jp (Japan), .de (Germany)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;New gTLDs&amp;#039;&amp;#039;&amp;#039;: .app, .dev, .tech, .cloud, .blog, etc. (thousands added since 2013)&lt;br /&gt;
&lt;br /&gt;
Each TLD is managed by a registry organization. For example, VeriSign operates the .com and .net TLDs, while Educause operates .edu.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Second-Level Domains (SLDs)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These are the domains that organizations register: &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;github.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mit.edu&amp;lt;/code&amp;gt;. You register these through domain registrars, who interface with the TLD registries.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Subdomains&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Domain owners can create arbitrary subdomains: &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mail.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;api.v2.example.com&amp;lt;/code&amp;gt;. Subdomains do not require separate registration—the owner of the parent domain controls all subdomains.&lt;br /&gt;
&lt;br /&gt;
DNS Servers: Authoritative vs. Recursive&lt;br /&gt;
&lt;br /&gt;
Two fundamentally different types of DNS servers perform distinct roles in the DNS infrastructure:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Authoritative DNS Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Authoritative servers are the definitive source of truth for a specific zone (portion of the DNS namespace). Characteristics:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Responsibility&amp;#039;&amp;#039;&amp;#039;: Provide answers for domains they have explicit authority over&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data Source&amp;#039;&amp;#039;&amp;#039;: Return information directly from their configured zone files&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior&amp;#039;&amp;#039;&amp;#039;: Only answer queries for domains in their zones; refer queries elsewhere for other domains&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operation&amp;#039;&amp;#039;&amp;#039;: Typically operated by domain owners or their DNS providers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Caching&amp;#039;&amp;#039;&amp;#039;: Do not cache responses for other domains&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Example&amp;#039;&amp;#039;&amp;#039;: If you own &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;, your authoritative servers know the IP addresses for &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mail.example.com&amp;lt;/code&amp;gt;, etc.&lt;br /&gt;
&lt;br /&gt;
When you configure DNS records for your domain through your registrar or DNS provider, you are updating authoritative servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Recursive DNS Resolvers (Recursive Resolvers)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Recursive resolvers perform the DNS resolution process on behalf of clients. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Responsibility&amp;#039;&amp;#039;&amp;#039;: Answer any DNS query by recursively querying authoritative servers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data Source&amp;#039;&amp;#039;&amp;#039;: Cache responses and query authoritative servers when cache misses occur&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior&amp;#039;&amp;#039;&amp;#039;: Accept any query and traverse the DNS hierarchy to find answers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operation&amp;#039;&amp;#039;&amp;#039;: Typically provided by ISPs, corporations, or public DNS services (Google Public DNS 8.8.8.8, Cloudflare 1.1.1.1)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Caching&amp;#039;&amp;#039;&amp;#039;: Extensively cache responses to improve performance and reduce load&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Client-Facing&amp;#039;&amp;#039;&amp;#039;: This is the type of server that end-user devices query&lt;br /&gt;
&lt;br /&gt;
When your computer performs a DNS lookup, it queries a recursive resolver, which then does the work of finding the answer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Distinction&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This distinction is fundamental:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Authoritative servers&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;I have authority over example.com, and I can definitively tell you that [http://www.example.com www.example.com] is 203.0.113.50&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recursive resolvers&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;I don&amp;#039;t know the answer to your query, but I&amp;#039;ll traverse the DNS hierarchy to find an authoritative server that does know, then give you the answer&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analogy&amp;#039;&amp;#039;&amp;#039;: An authoritative DNS server is like a property owner who knows everything about their own house. A recursive resolver is like a GPS navigation system that can find directions to any address by consulting various maps and data sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-resolution-process-following-the-hierarchy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Resolution Process: Following the Hierarchy ====&lt;br /&gt;
&lt;br /&gt;
When you type &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; in your browser and press Enter, a complex resolution process occurs behind the scenes. Let us trace this process step by step.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scenario&amp;#039;&amp;#039;&amp;#039;: Your computer needs to resolve &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; to an IP address. Your system is configured to use the recursive resolver at 1.1.1.1 (Cloudflare&amp;#039;s public DNS).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Check Local Cache&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Your operating system maintains a local DNS cache. If &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; was recently resolved and the TTL hasn&amp;#039;t expired, the cached answer is returned immediately (typically in microseconds). No network query is needed.&lt;br /&gt;
&lt;br /&gt;
If the cache contains no valid entry, proceed to Step 2.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Query Recursive Resolver&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Your computer sends a DNS query to its configured recursive resolver (1.1.1.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Query: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The recursive resolver checks its own cache. If it has a recent answer, it returns immediately. If not, it begins the recursive resolution process.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Query Root Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The recursive resolver queries one of the 13 root name servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → Root Server: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The root server does not know the specific answer but knows which servers are authoritative for the .com TLD:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root → Resolver: I don&amp;#039;t know about www.example.com specifically, &lt;br /&gt;
                 but you can ask the .com TLD servers. Here are their addresses:&lt;br /&gt;
                 - a.gtld-servers.net (192.5.6.30)&lt;br /&gt;
                 - b.gtld-servers.net (192.33.14.30)&lt;br /&gt;
                 [and others...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is called a &amp;#039;&amp;#039;&amp;#039;referral&amp;#039;&amp;#039;&amp;#039;—the root server refers the resolver to servers that are more specific.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Query TLD Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The resolver queries one of the .com TLD servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → .com TLD Server: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The TLD server knows which name servers are authoritative for example.com:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;.com TLD → Resolver: I don&amp;#039;t know about www.example.com specifically,&lt;br /&gt;
                     but the authoritative servers for example.com are:&lt;br /&gt;
                     - ns1.example.com (198.51.100.1)&lt;br /&gt;
                     - ns2.example.com (198.51.100.2)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Another referral, this time to the authoritative servers for the domain.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Query Authoritative Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The resolver queries example.com&amp;#039;s authoritative name server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → ns1.example.com: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
This server has definitive authority over example.com and provides the answer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ns1.example.com → Resolver: www.example.com is 203.0.113.50&lt;br /&gt;
                           (TTL: 3600 seconds)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Return Answer to Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The recursive resolver returns the answer to your computer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → Your Computer: www.example.com is 203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cache at Multiple Levels&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The answer is cached:&lt;br /&gt;
&lt;br /&gt;
* Your operating system caches the result (OS-level cache)&lt;br /&gt;
* Your browser may have its own cache (application-level cache)&lt;br /&gt;
* The recursive resolver caches the result (resolver cache)&lt;br /&gt;
&lt;br /&gt;
Future queries for &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; within the TTL period (3600 seconds = 1 hour in this example) will be answered from cache without repeating the full resolution process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-record-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Record Types ====&lt;br /&gt;
&lt;br /&gt;
DNS stores more than just IP addresses. Different record types serve different purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;A Record (Address Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Maps a domain name to an IPv4 address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.    IN  A   203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the most common record type. When you browse to a website, your browser requests the A record to find the server&amp;#039;s IPv4 address.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;AAAA Record (IPv6 Address Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Maps a domain name to an IPv6 address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.    IN  AAAA    2001:db8::1&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pronounced &amp;amp;quot;quad-A record.&amp;amp;quot; As IPv6 adoption increases, these records are becoming more common.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;CNAME Record (Canonical Name Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Creates an alias from one domain name to another. The resolver follows the chain to find the final IP address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.        IN  CNAME   webserver.example.com.&lt;br /&gt;
webserver.example.com.  IN  A       203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use cases:&lt;br /&gt;
&lt;br /&gt;
* Simplifying infrastructure changes (update one A record instead of many)&lt;br /&gt;
* Content delivery networks (CDNs): alias your domain to the CDN&amp;#039;s domain&lt;br /&gt;
* Load balancing and failover scenarios&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MX Record (Mail Exchanger Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Specifies the mail servers responsible for receiving email for a domain. Includes a priority number (lower numbers have higher priority).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  MX  10  mail1.example.com.&lt;br /&gt;
example.com.    IN  MX  20  mail2.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
If mail1 is unavailable, the sending server tries mail2. Email delivery depends critically on properly configured MX records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NS Record (Name Server Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Specifies the authoritative name servers for a domain or zone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  NS  ns1.example.com.&lt;br /&gt;
example.com.    IN  NS  ns2.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
These records delegate authority. When you register a domain, you specify NS records pointing to your DNS provider&amp;#039;s servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TXT Record (Text Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Stores arbitrary text data. Modern uses include:&lt;br /&gt;
&lt;br /&gt;
* SPF records for email authentication: &amp;lt;code&amp;gt;&amp;amp;quot;v=spf1 include:_spf.example.com ~all&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* DKIM keys for email signing&lt;br /&gt;
* Domain verification for services (Google, Microsoft, etc.): &amp;lt;code&amp;gt;&amp;amp;quot;google-site-verification=abc123xyz789&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* DMARC policies for email protection&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  TXT &amp;amp;quot;v=spf1 include:_spf.google.com ~all&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PTR Record (Pointer Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Provides reverse DNS lookup—mapping an IP address to a domain name. Used primarily for email server validation and logging.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;50.113.0.203.in-addr.arpa.  IN  PTR mail.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Mail servers often check PTR records to verify sender legitimacy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SOA Record (Start of Authority Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Defines authoritative information about a DNS zone, including the primary name server, administrator&amp;#039;s email, serial number, and timing parameters for zone transfers and caching.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.  IN  SOA ns1.example.com. admin.example.com. (&lt;br /&gt;
                      2024010101  ; Serial&lt;br /&gt;
                      3600        ; Refresh (1 hour)&lt;br /&gt;
                      1800        ; Retry (30 minutes)&lt;br /&gt;
                      1209600     ; Expire (2 weeks)&lt;br /&gt;
                      86400       ; Minimum TTL (1 day)&lt;br /&gt;
                      )&amp;lt;/pre&amp;gt;&lt;br /&gt;
Time To Live (TTL)&lt;br /&gt;
&lt;br /&gt;
Every DNS record includes a TTL (Time To Live) value specified in seconds. This value tells caching servers how long they can store the record before re-querying the authoritative server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Short TTL (e.g., 60-300 seconds)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Advantages:&lt;br /&gt;
&lt;br /&gt;
* Changes propagate quickly&lt;br /&gt;
* Useful before planned infrastructure changes&lt;br /&gt;
* Allows rapid failover&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
&lt;br /&gt;
* Increases DNS query load&lt;br /&gt;
* Slightly slower for end users (more frequent resolution)&lt;br /&gt;
* Higher bandwidth consumption&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Long TTL (e.g., 3600-86400 seconds)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Advantages:&lt;br /&gt;
&lt;br /&gt;
* Reduces DNS query load significantly&lt;br /&gt;
* Faster for end users (more cache hits)&lt;br /&gt;
* Lower bandwidth consumption&lt;br /&gt;
* Better resilience if authoritative servers become unavailable&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
&lt;br /&gt;
* Changes propagate slowly (users may see old data until TTL expires)&lt;br /&gt;
* Failover is slower&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;secure-shell-ssh-encrypted-remote-access&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Secure Shell (SSH): Encrypted Remote Access ===&lt;br /&gt;
&lt;br /&gt;
SSH (Secure Shell) is the standard protocol for secure remote access to systems. It replaced the insecure Telnet protocol in the 1990s and has become ubiquitous in system administration, DevOps, and software development.&lt;br /&gt;
&lt;br /&gt;
SSH provides three critical security properties:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: All data (including credentials) is encrypted using symmetric encryption (AES, ChaCha20)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Cryptographic MACs (Message Authentication Codes) prevent tampering&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Public key cryptography verifies server and optionally client identity&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-architecture&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Architecture ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH is not a single protocol but a protocol suite:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-TRANS (Transport Layer Protocol, RFC 4253)&amp;#039;&amp;#039;&amp;#039;: Establishes encrypted channel, handles server authentication&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-AUTH (Authentication Protocol, RFC 4252)&amp;#039;&amp;#039;&amp;#039;: Handles client authentication (password, public key, etc.)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-CONN (Connection Protocol, RFC 4254)&amp;#039;&amp;#039;&amp;#039;: Multiplexes multiple channels (terminal session, port forwarding, etc.) over one TCP connection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Standard Port&amp;#039;&amp;#039;&amp;#039;: TCP port 22 (well-known port)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Establishment Sequence&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. TCP three-way handshake establishes connection&lt;br /&gt;
2. SSH version exchange (both sides advertise SSH protocol version)&lt;br /&gt;
3. Algorithm negotiation (agree on encryption, MAC, compression algorithms)&lt;br /&gt;
4. Diffie-Hellman key exchange generates session keys&lt;br /&gt;
5. Server authenticates itself using its host key&lt;br /&gt;
6. Client authenticates itself (password or public key)&lt;br /&gt;
7. Encrypted session established, commands can be executed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-authentication-methods&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Authentication Methods ====&lt;br /&gt;
&lt;br /&gt;
SSH supports multiple authentication methods, which can be combined or used exclusively:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Password Authentication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The client sends the password over the encrypted SSH connection (not plaintext like Telnet). While the password is protected in transit, password authentication has weaknesses:&lt;br /&gt;
&lt;br /&gt;
* Vulnerable to brute-force attacks if weak passwords are used&lt;br /&gt;
* Requires users to memorize or manage passwords&lt;br /&gt;
* Passwords can be stolen through phishing, keyloggers, or social engineering&lt;br /&gt;
* No way to distinguish different clients (all use the same password)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Public Key Authentication (Recommended)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Uses asymmetric cryptography:&lt;br /&gt;
&lt;br /&gt;
* Client generates a public/private key pair&lt;br /&gt;
* Public key is placed on the server in &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;&lt;br /&gt;
* Private key remains on the client and never leaves the system&lt;br /&gt;
* Server challenges client to prove possession of the private key without transmitting it&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Strong cryptographic security (typically 2048-4096 bit RSA or 256 bit Ed25519)&lt;br /&gt;
* No password to steal or forget&lt;br /&gt;
* Can use different keys for different purposes&lt;br /&gt;
* Keys can be protected with passphrases&lt;br /&gt;
* Supports automation (keys without passphrases for scripts)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Modern Best Practice&amp;#039;&amp;#039;&amp;#039;: Disable password authentication entirely, allowing only public key authentication.&lt;br /&gt;
&lt;br /&gt;
Public Key Cryptography Review&lt;br /&gt;
&lt;br /&gt;
Since public key authentication is critical to SSH security, let us review the cryptographic foundation:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Cryptography Properties&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Two mathematically related keys: public key and private key&lt;br /&gt;
# Public key can be shared openly&lt;br /&gt;
# Private key must be kept secret&lt;br /&gt;
# Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
# Data signed with private key can be verified with public key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Authentication Protocol&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Client sends username and public key to server&lt;br /&gt;
# Server checks if public key is in &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; for that user&lt;br /&gt;
# Server generates random challenge, encrypts it with client&amp;#039;s public key, sends to client&lt;br /&gt;
# Client decrypts challenge with private key, computes hash, sends hash back&lt;br /&gt;
# Server verifies hash matches expected value&lt;br /&gt;
# If match: authentication succeeds (client proved possession of private key)&lt;br /&gt;
&lt;br /&gt;
The private key never travels across the network. The server cannot impersonate the client because it only has the public key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-key-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Key Types ====&lt;br /&gt;
&lt;br /&gt;
Modern SSH supports several key types with different security and performance characteristics:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;RSA (Rivest-Shamir-Adleman)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Traditional and widely supported&lt;br /&gt;
* Key size: 2048 or 4096 bits (3072 bits is middle ground)&lt;br /&gt;
* Slower than newer algorithms&lt;br /&gt;
* Still secure if key size is adequate (≥2048 bits)&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t rsa -b 4096&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Ed25519 (Edwards-curve Digital Signature Algorithm)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Modern elliptic curve algorithm (introduced ~2013)&lt;br /&gt;
* Key size: 256 bits (much smaller than RSA)&lt;br /&gt;
* Faster than RSA&lt;br /&gt;
* Strong security properties&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;ECDSA (Elliptic Curve Digital Signature Algorithm)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Older elliptic curve algorithm&lt;br /&gt;
* Key sizes: 256, 384, or 521 bits&lt;br /&gt;
* Concerns about NSA influence on NIST curves&lt;br /&gt;
* Ed25519 generally preferred over ECDSA for new keys&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t ecdsa -b 256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practice&amp;#039;&amp;#039;&amp;#039;: Use Ed25519 for new SSH keys. It offers excellent security with small key sizes and fast performance. Fall back to RSA 4096 only if connecting to older systems that don&amp;#039;t support Ed25519.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-host-key-verification-trust-on-first-use-tofu&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Host Key Verification: Trust On First Use (TOFU) ====&lt;br /&gt;
&lt;br /&gt;
One of SSH&amp;#039;s critical security features is host key verification, which protects against man-in-the-middle (MITM) attacks. This mechanism uses a &amp;amp;quot;Trust On First Use&amp;amp;quot; (TOFU) model.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The MITM Threat&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Your Computer] ←→ [Attacker&amp;#039;s Computer] ←→ [Real Server]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The attacker intercepts your connection, relays your commands to the real server, and relays responses back to you. From your perspective, everything appears normal, but the attacker can:&lt;br /&gt;
&lt;br /&gt;
* Log all your commands and responses&lt;br /&gt;
* Steal credentials if you authenticate with passwords&lt;br /&gt;
* Modify commands before forwarding them&lt;br /&gt;
* Inject malicious commands&lt;br /&gt;
&lt;br /&gt;
This is called a man-in-the-middle (MITM) attack.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH&amp;#039;s Defense: Host Keys&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Every SSH server has a unique host key (a public/private key pair) that identifies the server. The server&amp;#039;s public host key is its cryptographic identity.&lt;br /&gt;
&lt;br /&gt;
When you first connect to a server, SSH shows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The authenticity of host &amp;#039;server.example.com (203.0.113.50)&amp;#039; can&amp;#039;t be established.&lt;br /&gt;
ED25519 key fingerprint is SHA256:abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx.&lt;br /&gt;
Are you sure you want to continue connecting (yes/no/[fingerprint])?&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the TOFU moment. SSH is asking: &amp;amp;quot;I&amp;#039;ve never seen this server before. Do you trust this host key?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;If you type &amp;amp;quot;yes&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
SSH stores the server&amp;#039;s host key in &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;server.example.com,203.0.113.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
On subsequent connections, SSH verifies the server presents the same host key. If the host key changes, SSH displays an alarming warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @&lt;br /&gt;
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!&lt;br /&gt;
Someone could be eavesdropping on you right now (man-in-the-middle attack)!&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why the Warning?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A changed host key indicates one of several scenarios:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Legitimate&amp;#039;&amp;#039;&amp;#039;: Server was reinstalled, host key regenerated&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Legitimate&amp;#039;&amp;#039;&amp;#039;: Server moved to new hardware&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Malicious&amp;#039;&amp;#039;&amp;#039;: MITM attack in progress&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Configuration Error&amp;#039;&amp;#039;&amp;#039;: Connecting to wrong server&lt;br /&gt;
&lt;br /&gt;
SSH conservatively assumes the worst-case scenario and refuses the connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The TOFU Model&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;quot;Trust On First Use&amp;amp;quot; means:&lt;br /&gt;
&lt;br /&gt;
* First connection: You must manually verify the host key (typically by checking a fingerprint through an out-of-band channel)&lt;br /&gt;
* Subsequent connections: SSH automatically verifies the host key matches the stored value&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Comparison to PKI (Lab 8)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 8 that TLS uses a Public Key Infrastructure (PKI) with Certificate Authorities (CAs). Let us contrast the two models:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI/CA Model (HTTPS/TLS)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Server presents a certificate signed by a trusted CA&lt;br /&gt;
* Your browser has a list of trusted CA public keys&lt;br /&gt;
* Browser verifies certificate signature cryptographically&lt;br /&gt;
* No manual verification needed on first connection&lt;br /&gt;
* Scales well: one CA can sign millions of certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TOFU Model (SSH)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Server presents its public host key&lt;br /&gt;
* No centrally trusted authority&lt;br /&gt;
* You must manually verify on first connection (or accept risk)&lt;br /&gt;
* Subsequent connections are automatically verified&lt;br /&gt;
* Simpler infrastructure, but first-use verification is user&amp;#039;s responsibility&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why SSH Uses TOFU Instead of PKI&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# SSH predates widespread PKI adoption&lt;br /&gt;
# No consensus on which CAs to trust for SSH&lt;br /&gt;
# PKI adds complexity and cost (certificate purchase/management)&lt;br /&gt;
# TOFU works well for SSH&amp;#039;s primary use case (administrators connecting to their own servers)&lt;br /&gt;
# SSH certificates exist but are less common than HTTPS certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practice&amp;#039;&amp;#039;&amp;#039;: On first connection, verify the host key fingerprint through a trusted channel:&lt;br /&gt;
&lt;br /&gt;
* Check the fingerprint displayed in server documentation&lt;br /&gt;
* View the fingerprint on the server directly: &amp;lt;code&amp;gt;ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
* Compare with fingerprint shown by SSH client&lt;br /&gt;
&lt;br /&gt;
For production systems, always verify host keys. For test/lab environments, the risk is lower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-known-hosts-format&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Known Hosts Format ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt; file stores server host keys. Format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;hostname,ip algorithm public_key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;server.example.com,203.0.113.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hashed Format&amp;#039;&amp;#039;&amp;#039;: Some systems hash the hostname for privacy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;|1|base64hash|base64hash ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
This prevents someone with access to your known_hosts file from learning which servers you connect to.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wildcard Entries&amp;#039;&amp;#039;&amp;#039;: You can use wildcards:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;*.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Management&amp;#039;&amp;#039;&amp;#039;: Remove entries when servers are legitimately reinstalled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -R server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-authorized-keys&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Authorized Keys ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; file on the server determines which public keys can authenticate as a specific user.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Format&amp;#039;&amp;#039;&amp;#039;: One public key per line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIdef456... user@laptop&lt;br /&gt;
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCghi789... user@desktop&amp;lt;/pre&amp;gt;&lt;br /&gt;
The trailing comment (&amp;lt;code&amp;gt;user@laptop&amp;lt;/code&amp;gt;) is optional but helps identify keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Permissions&amp;#039;&amp;#039;&amp;#039;: SSH is strict about file permissions for security:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod 700 ~/.ssh              # Directory: rwx------&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys  # File: rw-------&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If permissions are too permissive, SSH may refuse to use the keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Restricting Keys&amp;#039;&amp;#039;&amp;#039;: You can add restrictions before keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;command=&amp;amp;quot;backup.sh&amp;amp;quot; ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIdef456... backup-key&lt;br /&gt;
from=&amp;amp;quot;203.0.113.0/24&amp;amp;quot; ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIghi789... admin-key&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;command=&amp;amp;quot;backup.sh&amp;amp;quot;&amp;lt;/code&amp;gt;: This key can only run backup.sh (useful for automation)&lt;br /&gt;
* &amp;lt;code&amp;gt;from=&amp;amp;quot;203.0.113.0/24&amp;amp;quot;&amp;lt;/code&amp;gt;: This key only works from specified IP range&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Agent&amp;#039;&amp;#039;&amp;#039;: For convenience, use ssh-agent to cache decrypted private keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;eval $(ssh-agent)&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The agent holds the unlocked private key in memory, allowing multiple connections without re-entering the passphrase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hypertext-transfer-protocol-http-the-universal-application-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== HyperText Transfer Protocol (HTTP): The Universal Application Protocol ===&lt;br /&gt;
&lt;br /&gt;
HTTP (HyperText Transfer Protocol) was initially designed in 1991 by Tim Berners-Lee to transfer hypertext documents (HTML web pages). Over three decades, HTTP has evolved far beyond its original purpose to become the universal protocol for nearly all application-level communication on the internet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;why-http-is-everywhere&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Why HTTP is Everywhere ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Simplicity&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP uses human-readable text commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;GET /api/users HTTP/1.1&lt;br /&gt;
Host: api.example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
This simplicity makes HTTP easy to:&lt;br /&gt;
&lt;br /&gt;
* Debug (you can read the protocol)&lt;br /&gt;
* Implement (libraries exist for every language)&lt;br /&gt;
* Extend (add custom headers easily)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Statelessness&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each HTTP request is independent—the server doesn&amp;#039;t need to maintain state between requests. This design decision has profound implications:&lt;br /&gt;
&lt;br /&gt;
* Servers can handle millions of clients without per-client memory&lt;br /&gt;
* Horizontal scaling is trivial (add more servers, any server can handle any request)&lt;br /&gt;
* Failures don&amp;#039;t cascade (if a request fails, retry without state recovery)&lt;br /&gt;
* Caching is straightforward (each request is independent)&lt;br /&gt;
&lt;br /&gt;
State management, when needed, is handled at the application layer (cookies, sessions, tokens).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Flexible Content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP can transfer any type of data:&lt;br /&gt;
&lt;br /&gt;
* HTML documents&lt;br /&gt;
* JSON API responses&lt;br /&gt;
* XML data&lt;br /&gt;
* Binary files (images, videos, executables)&lt;br /&gt;
* Streaming data&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;Content-Type&amp;lt;/code&amp;gt; header specifies what the body contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Content-Type: application/json&lt;br /&gt;
Content-Type: text/html&lt;br /&gt;
Content-Type: image/png&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Firewall Friendliness&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP (port 80) and HTTPS (port 443) are almost universally allowed through firewalls. Companies may block many ports, but they cannot block web traffic without breaking internet access. Applications using HTTP/HTTPS thus avoid firewall issues that plague custom protocols.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Tooling and Infrastructure&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Decades of investment have created rich ecosystems:&lt;br /&gt;
&lt;br /&gt;
* Web servers: Apache, Nginx, Caddy, IIS&lt;br /&gt;
* Reverse proxies and load balancers&lt;br /&gt;
* CDNs (Content Delivery Networks)&lt;br /&gt;
* Caching proxies&lt;br /&gt;
* API gateways&lt;br /&gt;
* Monitoring and debugging tools&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Result&amp;#039;&amp;#039;&amp;#039;: HTTP has become the default choice for application communication. APIs that might have used custom TCP protocols now use HTTP-based REST or GraphQL. Real-time protocols that might have used custom UDP protocols now use WebSockets (which upgrade HTTP connections). Even protocols that don&amp;#039;t naturally map to HTTP often use it anyway for operational simplicity.&lt;br /&gt;
&lt;br /&gt;
HTTP Request-Response Model&lt;br /&gt;
&lt;br /&gt;
HTTP uses a simple request-response pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Client sends HTTP Request&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;METHOD PATH VERSION&lt;br /&gt;
Headers&lt;br /&gt;
(blank line)&lt;br /&gt;
Body (optional)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Server sends HTTP Response&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;VERSION STATUS_CODE STATUS_MESSAGE&lt;br /&gt;
Headers&lt;br /&gt;
(blank line)&lt;br /&gt;
Body (optional)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example Exchange&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Request:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET /api/users/123 HTTP/1.1&lt;br /&gt;
Host: api.example.com&lt;br /&gt;
User-Agent: curl/7.68.0&lt;br /&gt;
Accept: application/json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Response:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;HTTP/1.1 200 OK&lt;br /&gt;
Content-Type: application/json&lt;br /&gt;
Content-Length: 58&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;id&amp;quot;:123,&amp;quot;name&amp;quot;:&amp;quot;Alice&amp;quot;,&amp;quot;email&amp;quot;:&amp;quot;alice@example.com&amp;quot;}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-methods-verbs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Methods (Verbs) ====&lt;br /&gt;
&lt;br /&gt;
HTTP defines several methods that indicate the desired action:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;GET&amp;#039;&amp;#039;&amp;#039;: Retrieve a resource&lt;br /&gt;
&lt;br /&gt;
* Should not modify server state (idempotent and safe)&lt;br /&gt;
* Can be cached&lt;br /&gt;
* Examples: Load web page, fetch API data, download file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;POST&amp;#039;&amp;#039;&amp;#039;: Submit data to create a new resource&lt;br /&gt;
&lt;br /&gt;
* May modify server state&lt;br /&gt;
* Not idempotent (repeating creates multiple resources)&lt;br /&gt;
* Examples: Submit form, create new user via API&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PUT&amp;#039;&amp;#039;&amp;#039;: Update an existing resource (or create if doesn&amp;#039;t exist)&lt;br /&gt;
&lt;br /&gt;
* Idempotent (repeating has same effect as doing once)&lt;br /&gt;
* Replaces entire resource&lt;br /&gt;
* Examples: Update user profile, upload file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PATCH&amp;#039;&amp;#039;&amp;#039;: Partially update a resource&lt;br /&gt;
&lt;br /&gt;
* Apply partial modifications&lt;br /&gt;
* Not necessarily idempotent (depends on implementation)&lt;br /&gt;
* Examples: Update single field of a record&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DELETE&amp;#039;&amp;#039;&amp;#039;: Remove a resource&lt;br /&gt;
&lt;br /&gt;
* Idempotent (deleting twice has same effect as deleting once)&lt;br /&gt;
* Examples: Delete user account, remove file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HEAD&amp;#039;&amp;#039;&amp;#039;: Same as GET but returns only headers (no body)&lt;br /&gt;
&lt;br /&gt;
* Check if resource exists&lt;br /&gt;
* Check content size before downloading&lt;br /&gt;
* Verify cache freshness&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;OPTIONS&amp;#039;&amp;#039;&amp;#039;: Query supported methods for a resource&lt;br /&gt;
&lt;br /&gt;
* Used for CORS (Cross-Origin Resource Sharing) preflight requests&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-status-codes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Status Codes ====&lt;br /&gt;
&lt;br /&gt;
The status code indicates the result of the request. Codes are grouped:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1xx: Informational&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 100 Continue: Client should continue with request&lt;br /&gt;
* 101 Switching Protocols: Server switching protocols (e.g., WebSocket upgrade)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2xx: Success&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 200 OK: Request succeeded&lt;br /&gt;
* 201 Created: Resource created successfully&lt;br /&gt;
* 204 No Content: Success but no response body&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3xx: Redirection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 301 Moved Permanently: Resource moved, update bookmarks&lt;br /&gt;
* 302 Found: Temporary redirect&lt;br /&gt;
* 304 Not Modified: Cached version is still valid&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4xx: Client Error&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 400 Bad Request: Malformed request&lt;br /&gt;
* 401 Unauthorized: Authentication required&lt;br /&gt;
* 403 Forbidden: Authenticated but not authorized&lt;br /&gt;
* 404 Not Found: Resource doesn&amp;#039;t exist&lt;br /&gt;
* 429 Too Many Requests: Rate limit exceeded&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5xx: Server Error&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 500 Internal Server Error: Server encountered error&lt;br /&gt;
* 502 Bad Gateway: Proxy received invalid response from upstream&lt;br /&gt;
* 503 Service Unavailable: Server temporarily overloaded or down&lt;br /&gt;
* 504 Gateway Timeout: Proxy timeout waiting for upstream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-headers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Headers ====&lt;br /&gt;
&lt;br /&gt;
Headers provide metadata about the request or response. Some critical headers:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request Headers&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Host: api.example.com&amp;lt;/code&amp;gt; — Required in HTTP/1.1, specifies target hostname&lt;br /&gt;
* &amp;lt;code&amp;gt;User-Agent: curl/7.68.0&amp;lt;/code&amp;gt; — Identifies client software&lt;br /&gt;
* &amp;lt;code&amp;gt;Accept: application/json&amp;lt;/code&amp;gt; — Content types client understands&lt;br /&gt;
* &amp;lt;code&amp;gt;Authorization: Bearer token123&amp;lt;/code&amp;gt; — Authentication credentials&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Type: application/json&amp;lt;/code&amp;gt; — Type of request body&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Length: 58&amp;lt;/code&amp;gt; — Size of request body in bytes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Response Headers&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Type: application/json&amp;lt;/code&amp;gt; — Type of response body&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Length: 1234&amp;lt;/code&amp;gt; — Size of response body&lt;br /&gt;
* &amp;lt;code&amp;gt;Cache-Control: max-age=3600&amp;lt;/code&amp;gt; — Caching directives&lt;br /&gt;
* &amp;lt;code&amp;gt;Set-Cookie: session=abc123; HttpOnly&amp;lt;/code&amp;gt; — Set cookie in browser&lt;br /&gt;
* &amp;lt;code&amp;gt;Location: /api/users/456&amp;lt;/code&amp;gt; — Redirect target (with 3xx status)&lt;br /&gt;
* &amp;lt;code&amp;gt;Server: nginx/1.18.0&amp;lt;/code&amp;gt; — Server software (often hidden for security)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security Headers&amp;#039;&amp;#039;&amp;#039; (modern best practices):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Strict-Transport-Security: max-age=31536000&amp;lt;/code&amp;gt; — Force HTTPS&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Security-Policy: default-src &amp;#039;self&amp;#039;&amp;lt;/code&amp;gt; — Mitigate XSS attacks&lt;br /&gt;
* &amp;lt;code&amp;gt;X-Frame-Options: DENY&amp;lt;/code&amp;gt; — Prevent clickjacking&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;virtual-hosting-multiple-sites-on-one-ip&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Virtual Hosting: Multiple Sites on One IP ====&lt;br /&gt;
&lt;br /&gt;
HTTP&amp;#039;s &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header enables virtual hosting—running multiple websites on a single IP address. The web server examines the &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header to determine which site the request targets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET / HTTP/1.1&lt;br /&gt;
Host: site1.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET / HTTP/1.1&lt;br /&gt;
Host: site2.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both requests go to the same IP address (e.g., 203.0.113.50) and same port (80 or 443), but the &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header tells the server which site to serve. This is how shared hosting providers can host thousands of websites on relatively few servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTPS Complication&amp;#039;&amp;#039;&amp;#039;: Virtual hosting works seamlessly for HTTP, but HTTPS initially had issues because the TLS handshake occurs before the HTTP request (so the server doesn&amp;#039;t know which certificate to present). Server Name Indication (SNI), added to TLS in 2003, solved this by including the hostname in the TLS handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http11-vs-http2-vs-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP/1.1 vs. HTTP/2 vs. HTTP/3 ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/1.1&amp;#039;&amp;#039;&amp;#039; (1997, RFC 2616, updated RFC 7230-7235):&lt;br /&gt;
&lt;br /&gt;
* Text-based protocol&lt;br /&gt;
* One request per connection (or serial requests with keep-alive)&lt;br /&gt;
* Head-of-line blocking (one slow request delays all subsequent requests)&lt;br /&gt;
* Still widely used&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/2&amp;#039;&amp;#039;&amp;#039; (2015, RFC 7540):&lt;br /&gt;
&lt;br /&gt;
* Binary protocol (more efficient parsing)&lt;br /&gt;
* Multiplexing (multiple concurrent requests on one connection)&lt;br /&gt;
* Server push (server can send resources before client requests them)&lt;br /&gt;
* Header compression (reduces overhead)&lt;br /&gt;
* Requires HTTPS in practice (browsers only support HTTP/2 over TLS)&lt;br /&gt;
* Growing adoption&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/3&amp;#039;&amp;#039;&amp;#039; (2022, RFC 9114):&lt;br /&gt;
&lt;br /&gt;
* Uses QUIC instead of TCP (UDP-based transport with built-in TLS)&lt;br /&gt;
* Reduces connection establishment latency&lt;br /&gt;
* Better handling of packet loss&lt;br /&gt;
* Connection migration (survive IP address changes)&lt;br /&gt;
* Newest, increasing adoption&lt;br /&gt;
&lt;br /&gt;
For this lab, we use HTTP/1.1 for simplicity, but modern production systems increasingly deploy HTTP/2 and HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Reverse Proxies: The Modern Web Architecture Pattern&lt;br /&gt;
&lt;br /&gt;
A reverse proxy is a server that sits in front of backend servers and forwards client requests to them. The client believes it is talking directly to the origin server, unaware of the proxy&amp;#039;s existence.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Terminology Clarification&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Forward Proxy&amp;#039;&amp;#039;&amp;#039;: Client knows about proxy, uses it to access external servers (e.g., corporate proxy for internet access)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reverse Proxy&amp;#039;&amp;#039;&amp;#039;: Client unaware of proxy, thinks it&amp;#039;s talking to the origin server directly&lt;br /&gt;
&lt;br /&gt;
Reverse Proxy Benefits&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Load Balancing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Distribute requests across multiple backend servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client → Reverse Proxy → [Backend 1]&lt;br /&gt;
                      → [Backend 2]&lt;br /&gt;
                      → [Backend 3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Strategies:&lt;br /&gt;
&lt;br /&gt;
* Round-robin: Cycle through backends in order&lt;br /&gt;
* Least connections: Send to backend with fewest active connections&lt;br /&gt;
* IP hash: Always send same client to same backend (session affinity)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. TLS Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The reverse proxy handles TLS encryption/decryption:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client ←(HTTPS)→ Reverse Proxy ←(HTTP)→ Backend Servers&amp;lt;/pre&amp;gt;&lt;br /&gt;
Benefits:&lt;br /&gt;
&lt;br /&gt;
* Backend servers don&amp;#039;t need TLS configuration or certificates&lt;br /&gt;
* Centralized certificate management&lt;br /&gt;
* Reduces computational load on backends (TLS crypto is expensive)&lt;br /&gt;
* Simplifies backend server configuration&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Caching&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Cache responses to reduce backend load:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client → Reverse Proxy (check cache) → Backend (if cache miss)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Subsequent requests for the same resource are served from cache without hitting backends. Can dramatically improve performance and reduce costs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Compression&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The proxy can compress responses (gzip, brotli) before sending to clients, reducing bandwidth:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Backend → Proxy (compress) → Client&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Security&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Hide backend server details (IP addresses, software versions)&lt;br /&gt;
* Web Application Firewall (WAF) functionality&lt;br /&gt;
* Rate limiting and DDoS protection&lt;br /&gt;
* Centralized logging and monitoring&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. Path-Based Routing&amp;#039;&amp;#039;&amp;#039; (Critical for Microservices)&lt;br /&gt;
&lt;br /&gt;
Route requests to different backends based on URL path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;client request /api/users    → Reverse Proxy → User Service&lt;br /&gt;
client request /api/orders   → Reverse Proxy → Order Service&lt;br /&gt;
client request /api/products → Reverse Proxy → Product Service&amp;lt;/pre&amp;gt;&lt;br /&gt;
From the client&amp;#039;s perspective, everything is at one domain (e.g., &amp;lt;code&amp;gt;api.example.com&amp;lt;/code&amp;gt;). The reverse proxy routes to appropriate microservices based on path. This is the architectural pattern that enables modern microservices.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;path-based-routing-in-depth&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Path-Based Routing in Depth ====&lt;br /&gt;
&lt;br /&gt;
Path-based routing is the killer feature that made HTTP the universal protocol. Let us examine why:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem Without Path-Based Routing&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Suppose you have three backend services:&lt;br /&gt;
&lt;br /&gt;
* User service (manages user accounts)&lt;br /&gt;
* Order service (manages orders)&lt;br /&gt;
* Product service (manages product catalog)&lt;br /&gt;
&lt;br /&gt;
Without path-based routing, clients must know the specific addresses of each service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;user-service.example.com/users&lt;br /&gt;
order-service.example.com/orders&lt;br /&gt;
product-service.example.com/products&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problems&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Clients must maintain knowledge of all services&lt;br /&gt;
# Adding/removing/renaming services breaks clients&lt;br /&gt;
# Difficult to manage CORS (Cross-Origin Resource Sharing) with multiple domains&lt;br /&gt;
# More complex DNS management&lt;br /&gt;
# More complex TLS certificate management (separate cert for each service)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution With Path-Based Routing&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
One unified API endpoint:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;api.example.com/users     → routes to User Service&lt;br /&gt;
api.example.com/orders    → routes to Order Service&lt;br /&gt;
api.example.com/products  → routes to Product Service&amp;lt;/pre&amp;gt;&lt;br /&gt;
Reverse proxy configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if path starts with /users    → forward to user-service:3001&lt;br /&gt;
if path starts with /orders   → forward to order-service:3002&lt;br /&gt;
if path starts with /products → forward to product-service:3003&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Clients see one consistent API domain&lt;br /&gt;
# Backend services can change without client awareness&lt;br /&gt;
# One TLS certificate for all services&lt;br /&gt;
# Simplified CORS configuration&lt;br /&gt;
# Centralized authentication and rate limiting&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is How Modern Systems Work&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kubernetes&amp;#039;&amp;#039;&amp;#039;: Ingress controllers route paths to services&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AWS&amp;#039;&amp;#039;&amp;#039;: Application Load Balancers route paths to target groups&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Microservices&amp;#039;&amp;#039;&amp;#039;: API Gateway routes paths to microservices&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Netflix, Uber, Airbnb&amp;#039;&amp;#039;&amp;#039;: All use path-based routing at scale&lt;br /&gt;
&lt;br /&gt;
You will implement this pattern in the exercises.&lt;br /&gt;
&lt;br /&gt;
Evolution from Monoliths to Microservices&lt;br /&gt;
&lt;br /&gt;
Understanding path-based routing requires understanding the architectural evolution:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Monolithic Architecture&amp;#039;&amp;#039;&amp;#039; (Traditional):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Single Application&lt;br /&gt;
├── User Management&lt;br /&gt;
├── Order Processing&lt;br /&gt;
├── Product Catalog&lt;br /&gt;
├── Payment Processing&lt;br /&gt;
└── Notification System&amp;lt;/pre&amp;gt;&lt;br /&gt;
All functionality in one codebase, runs as one process. Advantages: simple deployment, shared database, no network calls between components. Disadvantages: hard to scale specific components, one bug can crash entire system, difficult to update independently.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Microservices Architecture&amp;#039;&amp;#039;&amp;#039; (Modern):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;User Service      ──┐&lt;br /&gt;
Order Service     ──┤&lt;br /&gt;
Product Service   ──├── Reverse Proxy ── Clients&lt;br /&gt;
Payment Service   ──┤&lt;br /&gt;
Notification Svc  ──┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each service is independent: separate codebase, separate process, separate deployment, separate scaling. Advantages: scale components independently, update services without affecting others, use different technologies per service, isolated failures. Disadvantages: increased operational complexity, network latency between services, distributed system challenges.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Reverse Proxy&amp;#039;s Role&amp;#039;&amp;#039;&amp;#039;: Makes microservices look like a monolith to clients. Clients don&amp;#039;t know or care that backend is distributed—they just make requests to one API endpoint.&lt;br /&gt;
&lt;br /&gt;
Observability and Debugging&lt;br /&gt;
&lt;br /&gt;
Modern reverse proxies provide rich observability:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access Logs&amp;#039;&amp;#039;&amp;#039;: Record every request:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;203.0.113.42 - - [05/Dec/2024:14:23:45 +0000] &amp;amp;quot;GET /api/users HTTP/1.1&amp;amp;quot; 200 1234 &amp;amp;quot;-&amp;amp;quot; &amp;amp;quot;curl/7.68.0&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields: client IP, timestamp, method, path, protocol version, status code, response size, referer, user-agent&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Error Logs&amp;#039;&amp;#039;&amp;#039;: Record issues:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[error] 2024/12/05 14:23:50 upstream timed out (110: Connection timed out) while connecting to upstream&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Metrics&amp;#039;&amp;#039;&amp;#039;: Request rate, error rate, latency percentiles (p50, p95, p99), upstream health&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Distributed Tracing&amp;#039;&amp;#039;&amp;#039;: Track requests across services using trace IDs in headers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;X-Request-ID: 7f2a3b4c-8d9e-4f1a-b2c3-d4e5f6a7b8c9&amp;lt;/pre&amp;gt;&lt;br /&gt;
When troubleshooting issues, these logs and metrics are invaluable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;laboratory-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Laboratory Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-dns-resolution-and-analysis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: DNS Resolution and Analysis ===&lt;br /&gt;
&lt;br /&gt;
Objective: Use DNS client tools to query various record types and observe the DNS resolution process. Understand the difference between authoritative and recursive queries, analyze TTL values, and measure resolution performance.&lt;br /&gt;
&lt;br /&gt;
Required Topology: You must have the Red and Blue namespaces from Lab 8 with IP addresses 10.0.0.1 and 10.0.0.2 respectively, connected via bridge br0. Verify connectivity before proceeding:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If this fails, recreate your Lab 8 topology before continuing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-dns-queries-with-dig&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic DNS Queries with dig ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; (Domain Information Groper) tool is the primary DNS debugging utility. It provides detailed information about DNS queries and responses.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Verify dig is installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig -v&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DiG 9.18.1-1ubuntu1.3-Ubuntu&amp;lt;/pre&amp;gt;&lt;br /&gt;
If not installed: &amp;lt;code&amp;gt;sudo apt install dnsutils&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Perform a basic A record query for google.com:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;; &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; DiG 9.18.1-1ubuntu1.3-Ubuntu &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; google.com&lt;br /&gt;
;; global options: +cmd&lt;br /&gt;
;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 12345&lt;br /&gt;
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags:; udp: 1232&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;google.com.                    IN      A&lt;br /&gt;
&lt;br /&gt;
;; ANSWER SECTION:&lt;br /&gt;
google.com.             163     IN      A       142.250.185.46&lt;br /&gt;
&lt;br /&gt;
;; Query time: 23 msec&lt;br /&gt;
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)&lt;br /&gt;
;; WHEN: Fri Dec 05 14:30:22 UTC 2024&lt;br /&gt;
;; MSG SIZE  rcvd: 55&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Let us dissect this output:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Header Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status: NOERROR&amp;lt;/code&amp;gt; — Query succeeded&lt;br /&gt;
* &amp;lt;code&amp;gt;id: 12345&amp;lt;/code&amp;gt; — Random query ID for matching requests/responses&lt;br /&gt;
* &amp;lt;code&amp;gt;flags: qr rd ra&amp;lt;/code&amp;gt; —&lt;br /&gt;
** &amp;lt;code&amp;gt;qr&amp;lt;/code&amp;gt;: Query Response (this is a response, not a query)&lt;br /&gt;
** &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt;: Recursion Desired (client requested recursive resolution)&lt;br /&gt;
** &amp;lt;code&amp;gt;ra&amp;lt;/code&amp;gt;: Recursion Available (server supports recursive queries)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Question Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;google.com.                    IN      A&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows what we asked for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;google.com.&amp;lt;/code&amp;gt; — Domain name (with trailing dot, the FQDN)&lt;br /&gt;
* &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; — Internet class (as opposed to other historical DNS classes)&lt;br /&gt;
* &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; — Record type (IPv4 address)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Answer Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;google.com.             163     IN      A       142.250.185.46&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the answer:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;google.com.&amp;lt;/code&amp;gt; — Domain queried&lt;br /&gt;
* &amp;lt;code&amp;gt;163&amp;lt;/code&amp;gt; — TTL (Time To Live) in seconds, countdown starts when received&lt;br /&gt;
* &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; — Internet class&lt;br /&gt;
* &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; — Record type&lt;br /&gt;
* &amp;lt;code&amp;gt;142.250.185.46&amp;lt;/code&amp;gt; — The IPv4 address&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Metadata&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Query time: 23 msec&amp;lt;/code&amp;gt; — Time to get response (includes network latency and resolution time)&lt;br /&gt;
* &amp;lt;code&amp;gt;SERVER: 127.0.0.53#53&amp;lt;/code&amp;gt; — DNS server queried (systemd-resolved on Ubuntu)&lt;br /&gt;
* &amp;lt;code&amp;gt;MSG SIZE  rcvd: 55&amp;lt;/code&amp;gt; — Response packet size in bytes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Query a specific DNS server directly (bypass systemd-resolved):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig @8.8.8.8 google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;@8.8.8.8&amp;lt;/code&amp;gt; specifies Google Public DNS. Compare the query time to the previous result.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Query an IPv6 address (AAAA record):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com AAAA&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             299     IN      AAAA    2607:f8b0:4004:c07::66&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: You might see multiple AAAA records if Google has multiple IPv6 addresses for redundancy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Query mail server records (MX records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com MX&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             3599    IN      MX      10 smtp.google.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
The number (10) is the priority. Lower numbers have higher priority. If multiple MX records exist, mail servers try the lowest priority number first.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6&amp;#039;&amp;#039;&amp;#039;: Query name server records (NS records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com NS&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             21599   IN      NS      ns1.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns2.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns3.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns4.google.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Google&amp;#039;s authoritative name servers. These servers have definitive authority over google.com records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7&amp;#039;&amp;#039;&amp;#039;: Query text records (TXT records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com TXT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;v=spf1 include:_spf.google.com ~all&amp;amp;quot;&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95&amp;amp;quot;&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
TXT records store arbitrary text. Common uses: SPF records for email authentication, domain verification for services, DKIM public keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8&amp;#039;&amp;#039;&amp;#039;: Request short output format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +short google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;142.250.185.46&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just the IP address, no verbose information. Useful for scripts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9&amp;#039;&amp;#039;&amp;#039;: Trace the DNS resolution path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +trace google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;.                       518400  IN      NS      a.root-servers.net.&lt;br /&gt;
.                       518400  IN      NS      b.root-servers.net.&lt;br /&gt;
[... other root servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 239 bytes from 8.8.8.8#53 in 23 ms&lt;br /&gt;
&lt;br /&gt;
com.                    172800  IN      NS      a.gtld-servers.net.&lt;br /&gt;
com.                    172800  IN      NS      b.gtld-servers.net.&lt;br /&gt;
[... other .com TLD servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 1173 bytes from 192.5.5.241#53(a.root-servers.net) in 87 ms&lt;br /&gt;
&lt;br /&gt;
google.com.             172800  IN      NS      ns1.google.com.&lt;br /&gt;
google.com.             172800  IN      NS      ns2.google.com.&lt;br /&gt;
[... other google.com name servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 836 bytes from 192.12.94.30#53(e.gtld-servers.net) in 103 ms&lt;br /&gt;
&lt;br /&gt;
google.com.             300     IN      A       142.250.185.46&lt;br /&gt;
&lt;br /&gt;
;; Received 55 bytes from 216.239.32.10#53(ns1.google.com) in 11 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows the complete resolution process:&lt;br /&gt;
&lt;br /&gt;
# Query root servers → get .com TLD servers&lt;br /&gt;
# Query .com TLD servers → get google.com authoritative servers&lt;br /&gt;
# Query google.com authoritative servers → get final answer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable A ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; commands for A, AAAA, MX, NS, and TXT records for two different domains (suggestion: use a domain like station01.internal.arh.pub.ro, or stud.etti.pub.ro for MX)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;dig +trace&amp;lt;/code&amp;gt; for any domain&lt;br /&gt;
# Output of two consecutive &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; queries showing TTL countdown&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-ssh-configuration-and-key-based-authentication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: SSH Configuration and Key-Based Authentication ===&lt;br /&gt;
&lt;br /&gt;
Objective: Configure SSH server and client, generate Ed25519 key pairs, implement key-based authentication, understand the Trust On First Use (TOFU) security model, and observe host key verification&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-verifying-ssh-installation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Verifying SSH Installation ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Check if SSH server is installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dpkg -l | grep openssh-server&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ii  openssh-server  1:8.9p1-3ubuntu0.4  amd64  secure shell (SSH) server&amp;lt;/pre&amp;gt;&lt;br /&gt;
If not installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install openssh-server openssh-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Check SSH service status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl status ssh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not running:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl start ssh&lt;br /&gt;
sudo systemctl enable ssh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-generating-ssh-key-pairs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Part 2: Generating SSH Key Pairs ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll generate Ed25519 keys because they offer strong security with small key sizes and fast performance.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Generate a key pair as your current user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -t ed25519 -C &amp;quot;lab-key&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected interaction&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Generating public/private ed25519 key pair.&lt;br /&gt;
Enter file in which to save the key (/home/youruser/.ssh/id_ed25519): [press Enter]&lt;br /&gt;
Enter passphrase (empty for no passphrase): [press Enter for no passphrase]&lt;br /&gt;
Enter same passphrase again: [press Enter]&lt;br /&gt;
Your identification has been saved in /home/youruser/.ssh/id_ed25519&lt;br /&gt;
Your public key has been saved in /home/youruser/.ssh/id_ed25519.pub&lt;br /&gt;
The key fingerprint is:&lt;br /&gt;
SHA256:xyz789abc123def456ghi789jkl012mno345pqr678 lab-key&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key Decisions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;File location&amp;#039;&amp;#039;&amp;#039;: Accept default (&amp;lt;code&amp;gt;~/.ssh/id_ed25519&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Passphrase&amp;#039;&amp;#039;&amp;#039;: For this lab, skip the passphrase for simplicity. In production, always use a strong passphrase to protect the private key. A passphrase encrypts the private key file—even if an attacker steals the file, they cannot use it without the passphrase.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Examine the generated keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/.ssh/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwx------ 2 youruser youruser 4096 Dec  5 14:30 .&lt;br /&gt;
drwxr-x--- 8 youruser youruser 4096 Dec  5 14:30 ..&lt;br /&gt;
-rw------- 1 youruser youruser  411 Dec  5 14:30 id_ed25519&lt;br /&gt;
-rw-r--r-- 1 youruser youruser   99 Dec  5 14:30 id_ed25519.pub&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical&amp;#039;&amp;#039;&amp;#039;: Private key (&amp;lt;code&amp;gt;id_ed25519&amp;lt;/code&amp;gt;) has mode 600 (readable/writable only by owner). Public key (&amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;) has mode 644 (world-readable).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: View your public key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/id_ed25519.pub&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This public key can be freely shared. You&amp;#039;ll copy this to accounts you want to access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-setting-up-key-based-authentication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Setting Up Key-Based Authentication ====&lt;br /&gt;
&lt;br /&gt;
Configure SSH so you can connect to the &amp;lt;code&amp;gt;sshtest&amp;lt;/code&amp;gt; account using your key.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Copy your public key to the test user&amp;#039;s authorized_keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/id_ed25519.pub ~/.ssh/authorized_keys&lt;br /&gt;
sudo chmod 600 /home/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Verify the setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /home/sshtest/.ssh/&lt;br /&gt;
sudo cat ~/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Permissions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;~/.ssh/&amp;lt;/code&amp;gt;: 700 (drwx------)&lt;br /&gt;
* &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt;: 600 (-rw-------)&lt;br /&gt;
* Owner must be the target user&lt;br /&gt;
&lt;br /&gt;
If permissions are wrong, SSH will silently ignore the keys and fall back to password authentication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-first-connection---trust-on-first-use-tofu&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: First Connection - Trust On First Use (TOFU) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Connect to localhost as the test user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (first connection)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The authenticity of host &amp;#039;localhost (127.0.0.1)&amp;#039; can&amp;#039;t be established.&lt;br /&gt;
ED25519 key fingerprint is SHA256:abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx.&lt;br /&gt;
This key is not known by any other names.&lt;br /&gt;
Are you sure you want to continue connecting (yes/no/[fingerprint])?&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is the TOFU (Trust On First Use) moment.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
SSH is asking you to verify the server&amp;#039;s identity. In production, you would:&lt;br /&gt;
&lt;br /&gt;
# Check the server&amp;#039;s host key fingerprint: &amp;lt;code&amp;gt;ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
# Compare the fingerprint displayed by SSH with the server&amp;#039;s actual fingerprint&lt;br /&gt;
# If they match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;. If they don&amp;#039;t match, &amp;#039;&amp;#039;&amp;#039;do not proceed&amp;#039;&amp;#039;&amp;#039; (possible MITM attack)&lt;br /&gt;
&lt;br /&gt;
For this lab, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Are you sure you want to continue connecting (yes/no/[fingerprint])? yes&lt;br /&gt;
Warning: Permanently added &amp;#039;localhost&amp;#039; (ED25519) to the list of known hosts.&lt;br /&gt;
sshtest@hostname:~$&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;You are now connected!&amp;#039;&amp;#039;&amp;#039; The connection succeeded using key-based authentication (no password prompt).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Explore the connection:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;whoami&lt;br /&gt;
pwd&lt;br /&gt;
hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Exit the SSH session:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-understanding-known_hosts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Understanding known_hosts ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Examine the known_hosts file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/known_hosts&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123def456ghi789jkl012mno345pqr678stu901vwx&amp;lt;/pre&amp;gt;&lt;br /&gt;
This file stores trusted host keys. Format: &amp;lt;code&amp;gt;hostname/ip algorithm public_key&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On subsequent connections, SSH verifies the server presents the same host key. If the key changes, SSH displays a warning.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Connect again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected behavior&amp;#039;&amp;#039;&amp;#039;: No host key prompt this time—immediate connection. SSH verified the host key matches the one in known_hosts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-simulating-a-host-key-change&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Simulating a Host Key Change ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll deliberately change the server&amp;#039;s host key to see SSH&amp;#039;s security warning.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Regenerate the host key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo rm /etc/ssh/ssh_host_ed25519_key*&lt;br /&gt;
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N &amp;quot;&amp;quot;&lt;br /&gt;
sudo systemctl restart sshd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Attempt to connect:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @&lt;br /&gt;
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!&lt;br /&gt;
Someone could be eavesdropping on you right now (man-in-the-middle attack)!&lt;br /&gt;
It is also possible that a host key has just been changed.&lt;br /&gt;
The fingerprint for the ED25519 key sent by the remote host is&lt;br /&gt;
SHA256:new_fingerprint_here.&lt;br /&gt;
Please contact your system administrator.&lt;br /&gt;
Add correct host key in /home/youruser/.ssh/known_hosts to get rid of this message.&lt;br /&gt;
Offending ED25519 key in /home/youruser/.ssh/known_hosts:1&lt;br /&gt;
  remove with:&lt;br /&gt;
  ssh-keygen -f &amp;amp;quot;/home/youruser/.ssh/known_hosts&amp;amp;quot; -R &amp;amp;quot;localhost&amp;amp;quot;&lt;br /&gt;
Host key for localhost has changed and you have requested strict checking.&lt;br /&gt;
Host key verification failed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH refuses to connect!&amp;#039;&amp;#039;&amp;#039; This is the security mechanism in action. SSH detected that the server&amp;#039;s host key changed, which could indicate:&lt;br /&gt;
&lt;br /&gt;
# Legitimate server reinstallation&lt;br /&gt;
# Man-in-the-middle attack&lt;br /&gt;
&lt;br /&gt;
SSH conservatively assumes the worst case and blocks the connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Remove the old host key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -f ~/.ssh/known_hosts -R &amp;quot;localhost&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# Host localhost found: line 1&lt;br /&gt;
/home/youruser/.ssh/known_hosts updated.&lt;br /&gt;
Original contents retained as /home/youruser/.ssh/known_hosts.old&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Reconnect (you&amp;#039;ll see the TOFU prompt again):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to trust the new host key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable B ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# The TOFU prompt on first SSH connection&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/id_ed25519.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;amp;quot;WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!&amp;amp;quot; message&lt;br /&gt;
# Successful SSH connection after resolving the host key change&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-http-services-and-reverse-proxy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: HTTP Services and Reverse Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective&amp;#039;&amp;#039;&amp;#039;: Build simple HTTP backend services in network namespaces and configure Caddy as a reverse proxy with path-based routing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-installing-caddy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Installing Caddy ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Install Caddy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy curl&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Verify installation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;caddy version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;v2.6.2&amp;lt;/code&amp;gt; or similar&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-creating-simple-backend-services&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Creating Simple Backend Services ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll create basic file-serving HTTP servers in the Red and Blue namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Create content directories and files for Red service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create directory for Red service&lt;br /&gt;
mkdir -p ~/red-service&lt;br /&gt;
cd ~/red-service&lt;br /&gt;
&lt;br /&gt;
# Create an index file&lt;br /&gt;
cat &amp;gt; index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Red Service&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Red Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Hello from Red namespace!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;IP: 10.0.0.1 | Port: 8001&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
# Create an API response file&lt;br /&gt;
mkdir -p api&lt;br /&gt;
cat &amp;gt; api/data.json &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Red Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.1&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Start Red service in its namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start HTTP server in Red namespace (run in background with &amp;amp;)&lt;br /&gt;
sudo ip netns exec red python3 -m http.server 8001 --bind 10.0.0.1 --directory ~/red-service &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;Serving HTTP on 10.0.0.1 port 8001 (http://10.0.0.1:8001/) ...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Create content for Blue service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create directory for Blue service&lt;br /&gt;
mkdir -p ~/blue-service&lt;br /&gt;
cd ~/blue-service&lt;br /&gt;
&lt;br /&gt;
# Create an index file&lt;br /&gt;
cat &amp;gt; index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Blue Service&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Blue Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Hello from Blue namespace!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;IP: 10.0.0.2 | Port: 8002&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
# Create an API response file&lt;br /&gt;
mkdir -p api&lt;br /&gt;
cat &amp;gt; api/data.json &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Blue Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.2&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Start Blue service in its namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start HTTP server in Blue namespace&lt;br /&gt;
sudo ip netns exec blue python3 -m http.server 8002 --bind 10.0.0.2 --directory ~/blue-service &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Test the backend services directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test Red service&lt;br /&gt;
curl http://10.0.0.1:8001/&lt;br /&gt;
&lt;br /&gt;
# Test Blue service&lt;br /&gt;
curl http://10.0.0.2:8002/&lt;br /&gt;
&lt;br /&gt;
# Test JSON endpoints&lt;br /&gt;
curl http://10.0.0.1:8001/api/data.json&lt;br /&gt;
curl http://10.0.0.2:8002/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the HTML and JSON content from each service.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-configuring-caddy-reverse-proxy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Configuring Caddy Reverse Proxy ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Create a Caddyfile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd ~&lt;br /&gt;
cat &amp;gt; Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
# Caddy Reverse Proxy Configuration&lt;br /&gt;
&lt;br /&gt;
:8080 {&lt;br /&gt;
    # Health check endpoint&lt;br /&gt;
    handle /health {&lt;br /&gt;
        respond &amp;quot;OK - API Gateway Running&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Root path&lt;br /&gt;
    handle / {&lt;br /&gt;
        respond &amp;quot;API Gateway - Use /red/ or /blue/ paths&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /red/* to Red service (strips /red prefix)&lt;br /&gt;
    handle_path /red/* {&lt;br /&gt;
        reverse_proxy 10.0.0.1:8001&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /blue/* to Blue service (strips /blue prefix)&lt;br /&gt;
    handle_path /blue/* {&lt;br /&gt;
        reverse_proxy 10.0.0.2:8002&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Enable logging&lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the Configuration&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;:8080&amp;lt;/code&amp;gt; - Listen on port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;handle /health&amp;lt;/code&amp;gt; - Health check endpoint (doesn&amp;#039;t go to backends)&lt;br /&gt;
* &amp;lt;code&amp;gt;handle_path /red/*&amp;lt;/code&amp;gt; - Strips &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt; prefix before forwarding&lt;br /&gt;
** Client requests &amp;lt;code&amp;gt;/red/api/data.json&amp;lt;/code&amp;gt; → Backend receives &amp;lt;code&amp;gt;/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reverse_proxy 10.0.0.1:8001&amp;lt;/code&amp;gt; - Forward to Red service&lt;br /&gt;
* Similar logic for Blue service&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Start Caddy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;caddy run --config ~/Caddyfile&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;INFO    using config from file&lt;br /&gt;
INFO    serving initial configuration&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this terminal open to see request logs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-testing-path-based-routing&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: Testing Path-Based Routing ====&lt;br /&gt;
&lt;br /&gt;
Open a &amp;#039;&amp;#039;&amp;#039;new terminal&amp;#039;&amp;#039;&amp;#039; for testing.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Test the root path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;API Gateway - Use /red/ or /blue/ paths&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Test health check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/health&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;OK - API Gateway Running&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Test Red service through proxy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the Red service HTML page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Red Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.1&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Test Blue service through proxy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the Blue service HTML page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Blue Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.2&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Test with verbose output to see headers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -v http://localhost:8080/red/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output (key parts):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;gt; GET /red/api/data.json HTTP/1.1&lt;br /&gt;
&amp;amp;gt; Host: localhost:8080&lt;br /&gt;
...&lt;br /&gt;
&amp;amp;lt; HTTP/1.1 200 OK&lt;br /&gt;
&amp;amp;lt; Content-Type: application/json&lt;br /&gt;
&amp;amp;lt; Server: Caddy&lt;br /&gt;
...&lt;br /&gt;
{JSON response}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observation&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Client requested &amp;lt;code&amp;gt;/red/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
* Caddy stripped &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt; and forwarded &amp;lt;code&amp;gt;/api/data.json&amp;lt;/code&amp;gt; to backend&lt;br /&gt;
* Response comes back through Caddy (notice &amp;lt;code&amp;gt;Server: Caddy&amp;lt;/code&amp;gt; header)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-understanding-the-flow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Understanding the Flow ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request Flow Diagram&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Request: http://localhost:8080/red/api/data.json&lt;br /&gt;
       |&lt;br /&gt;
       v&lt;br /&gt;
   Caddy (port 8080)&lt;br /&gt;
       |&lt;br /&gt;
       | Strips /red prefix&lt;br /&gt;
       | Path becomes: /api/data.json&lt;br /&gt;
       v&lt;br /&gt;
   Red Service (10.0.0.1:8001)&lt;br /&gt;
       |&lt;br /&gt;
       | Serves file: ~/red-service/api/data.json&lt;br /&gt;
       v&lt;br /&gt;
   Response back through Caddy&lt;br /&gt;
       |&lt;br /&gt;
       v&lt;br /&gt;
   Client receives JSON&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Cleanup ====&lt;br /&gt;
&lt;br /&gt;
When finished testing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Stop Caddy (Ctrl+C in Caddy terminal)&lt;br /&gt;
&lt;br /&gt;
# Kill Python servers&lt;br /&gt;
pkill -f &amp;quot;python3 -m http.server 8001&amp;quot;&lt;br /&gt;
pkill -f &amp;quot;python3 -m http.server 8002&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Or kill all Python HTTP servers&lt;br /&gt;
sudo pkill -f &amp;quot;http.server&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable C ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/&amp;lt;/code&amp;gt; (root path)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/health&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/red/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/blue/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl -v http://localhost:8080/red/&amp;lt;/code&amp;gt; showing response headers&lt;br /&gt;
# Your complete Caddyfile contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
This section covers common issues you may encounter during the lab exercises.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== DNS Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: DNS queries fail or time out&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check DNS resolver configuration&lt;br /&gt;
cat /etc/resolv.conf&lt;br /&gt;
&lt;br /&gt;
# Test with different DNS servers&lt;br /&gt;
dig @8.8.8.8 google.com&lt;br /&gt;
dig @1.1.1.1 google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Verify network connectivity: &amp;lt;code&amp;gt;ping 8.8.8.8&amp;lt;/code&amp;gt;&lt;br /&gt;
* Check firewall rules: &amp;lt;code&amp;gt;sudo iptables -L -n | grep 53&amp;lt;/code&amp;gt;&lt;br /&gt;
* Try alternative DNS servers&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;connection timed out; no servers could be reached&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: No DNS resolver configured or resolver unreachable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Add Google DNS to resolv.conf&lt;br /&gt;
echo &amp;quot;nameserver 8.8.8.8&amp;quot; | sudo tee -a /etc/resolv.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== SSH Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;Permission denied (publickey)&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check SSH with verbose output&lt;br /&gt;
ssh -v testuser@10.0.0.2&lt;br /&gt;
&lt;br /&gt;
# Verify key permissions&lt;br /&gt;
ls -la ~/.ssh/id_ed25519&lt;br /&gt;
&lt;br /&gt;
# Check authorized_keys on server&lt;br /&gt;
sudo ls -la /home/testuser/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Causes&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Wrong permissions on private key (must be 600)&lt;br /&gt;
# Wrong permissions on authorized_keys (must be 600)&lt;br /&gt;
# Wrong ownership of .ssh directory&lt;br /&gt;
# Public key not in authorized_keys&lt;br /&gt;
# Private key not loaded&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Fix permissions&lt;br /&gt;
chmod 600 ~/.ssh/id_ed25519&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
chmod 700 ~/.ssh&lt;br /&gt;
&lt;br /&gt;
# Verify key is loaded&lt;br /&gt;
ssh-add -l&lt;br /&gt;
&lt;br /&gt;
# Add key if not loaded&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: Server&amp;#039;s host key changed (legitimate reinstall or potential MITM attack)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039; (if change is legitimate):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Remove old host key&lt;br /&gt;
ssh-keygen -R 10.0.0.2&lt;br /&gt;
&lt;br /&gt;
# Reconnect and verify new fingerprint&lt;br /&gt;
ssh testuser@10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: SSH connection hangs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check if SSH server is running&lt;br /&gt;
sudo ss -tlnp | grep :22&lt;br /&gt;
&lt;br /&gt;
# Test connectivity&lt;br /&gt;
nc -zv 10.0.0.2 22&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Restart SSH server: &amp;lt;code&amp;gt;sudo systemctl restart sshd&amp;lt;/code&amp;gt;&lt;br /&gt;
* Check firewall rules&lt;br /&gt;
* Verify network namespace connectivity&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;httpcaddy-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== HTTP/Caddy Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Caddy fails to start&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Validate configuration&lt;br /&gt;
caddy validate --config /etc/caddy/Caddyfile&lt;br /&gt;
&lt;br /&gt;
# Check if port is already in use&lt;br /&gt;
sudo ss -tlnp | grep :8080&lt;br /&gt;
&lt;br /&gt;
# View Caddy logs&lt;br /&gt;
sudo journalctl -u caddy -n 50&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Causes&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Port already in use&lt;br /&gt;
# Syntax error in Caddyfile&lt;br /&gt;
# Permission issues&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Kill process using port 8080&lt;br /&gt;
sudo fuser -k 8080/tcp&lt;br /&gt;
&lt;br /&gt;
# Fix Caddyfile syntax&lt;br /&gt;
caddy fmt --overwrite /etc/caddy/Caddyfile&lt;br /&gt;
&lt;br /&gt;
# Run Caddy with elevated privileges if needed&lt;br /&gt;
sudo caddy run --config /etc/caddy/Caddyfile&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Reverse proxy returns &amp;amp;quot;502 Bad Gateway&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: Backend service not running or unreachable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test backend directly&lt;br /&gt;
curl http://10.0.0.1:8001&lt;br /&gt;
curl http://10.0.0.2:8002&lt;br /&gt;
&lt;br /&gt;
# Check if backend processes are running&lt;br /&gt;
sudo ip netns exec red ps aux | grep python&lt;br /&gt;
sudo ip netns exec blue ps aux | grep python&lt;br /&gt;
&lt;br /&gt;
# Check connectivity from host to namespace&lt;br /&gt;
ping -c 2 10.0.0.1&lt;br /&gt;
ping -c 2 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Restart backend services&lt;br /&gt;
* Verify namespace network configuration&lt;br /&gt;
* Check backend logs for errors&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Path routing not working correctly&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test with verbose curl&lt;br /&gt;
curl -v http://localhost:8080/red/&lt;br /&gt;
&lt;br /&gt;
# Check Caddy access logs&lt;br /&gt;
sudo tail -f /var/log/caddy/access.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;: Verify Caddyfile syntax, especially &amp;lt;code&amp;gt;handle_path&amp;lt;/code&amp;gt; directives. Ensure path prefixes match exactly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-namespace-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Namespace Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Cannot ping between namespaces&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check interface status&lt;br /&gt;
sudo ip netns exec red ip link show&lt;br /&gt;
sudo ip netns exec blue ip link show&lt;br /&gt;
&lt;br /&gt;
# Check IP addresses&lt;br /&gt;
sudo ip netns exec red ip addr show&lt;br /&gt;
sudo ip netns exec blue ip addr show&lt;br /&gt;
&lt;br /&gt;
# Check routing&lt;br /&gt;
sudo ip netns exec red ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Recreate namespace configuration from Lab 8&lt;br /&gt;
* Verify veth pairs are connected&lt;br /&gt;
* Check bridge configuration&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Services in namespace cannot access internet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: No default gateway or NAT configured&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;: Configure NAT on host (if needed for external access):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enable IP forwarding&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Add NAT rule&lt;br /&gt;
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-protocol-information&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Protocol Information ==&lt;br /&gt;
&lt;br /&gt;
This appendix provides information about other application protocols you may encounter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;email-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Email Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SMTP (Simple Mail Transfer Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 25 (unencrypted), 587 (submission with STARTTLS), 465 (SMTPS)&lt;br /&gt;
* Purpose: Sending email between servers&lt;br /&gt;
* Client-to-server: Port 587&lt;br /&gt;
* Server-to-server: Port 25&lt;br /&gt;
* Note: Port 25 often blocked by ISPs to prevent spam&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;IMAP (Internet Message Access Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 143 (unencrypted), 993 (IMAPS)&lt;br /&gt;
* Purpose: Reading email with server synchronization&lt;br /&gt;
* Advantages: Emails stay on server, accessible from multiple devices&lt;br /&gt;
* Modern replacement for POP3&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;POP3 (Post Office Protocol v3)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 110 (unencrypted), 995 (POP3S)&lt;br /&gt;
* Purpose: Downloading email to client&lt;br /&gt;
* Downloads emails and typically deletes from server&lt;br /&gt;
* Legacy protocol, IMAP preferred today&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;database-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Database Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MySQL&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 3306&lt;br /&gt;
* Protocol: MySQL wire protocol (proprietary)&lt;br /&gt;
* Common tools: mysql client, MySQL Workbench&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PostgreSQL&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 5432&lt;br /&gt;
* Protocol: PostgreSQL wire protocol&lt;br /&gt;
* Common tools: psql client, pgAdmin&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MongoDB&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 27017&lt;br /&gt;
* Protocol: MongoDB wire protocol&lt;br /&gt;
* NoSQL document database&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Redis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 6379&lt;br /&gt;
* Protocol: RESP (Redis Serialization Protocol)&lt;br /&gt;
* In-memory data structure store&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;other-common-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Other Common Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;FTP (File Transfer Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 21 (control), 20 (data)&lt;br /&gt;
* Legacy file transfer protocol&lt;br /&gt;
* Security issues: Transmits credentials in cleartext&lt;br /&gt;
* Modern alternatives: SFTP (SSH File Transfer Protocol), FTPS (FTP over SSL/TLS)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;LDAP (Lightweight Directory Access Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 389 (unencrypted), 636 (LDAPS)&lt;br /&gt;
* Purpose: Directory services (user authentication, organizational data)&lt;br /&gt;
* Common in enterprises for centralized authentication&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;RDP (Remote Desktop Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 3389&lt;br /&gt;
* Purpose: Remote desktop access (Windows)&lt;br /&gt;
* Proprietary Microsoft protocol&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;VNC (Virtual Network Computing)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 5900+ (5900, 5901, etc.)&lt;br /&gt;
* Purpose: Remote desktop access (cross-platform)&lt;br /&gt;
* Open protocol, multiple implementations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NTP (Network Time Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 123&lt;br /&gt;
* Purpose: Clock synchronization&lt;br /&gt;
* Critical for distributed systems, logging, security&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Syslog&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 514 (UDP/TCP)&lt;br /&gt;
* Purpose: Logging infrastructure&lt;br /&gt;
* Centralized log collection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ports-to-avoid&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Ports to Avoid ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Insecure Legacy Protocols&amp;#039;&amp;#039;&amp;#039; (never use in production):&lt;br /&gt;
&lt;br /&gt;
* Port 21: FTP (use SFTP or FTPS instead)&lt;br /&gt;
* Port 23: Telnet (use SSH instead)&lt;br /&gt;
* Port 69: TFTP (use SFTP or SCP instead)&lt;br /&gt;
* Port 110: POP3 unencrypted (use POP3S or IMAP)&lt;br /&gt;
* Port 143: IMAP unencrypted (use IMAPS)&lt;br /&gt;
* Port 389: LDAP unencrypted (use LDAPS)&lt;br /&gt;
* Port 445: SMB (often exploited, use VPN if needed)&lt;br /&gt;
* Port 512-514: rlogin, rsh, rexec (use SSH instead)&lt;br /&gt;
&lt;br /&gt;
These protocols transmit data (including credentials) in cleartext and/or have known security vulnerabilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all required elements. Organize clearly with section headers matching exercise labels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the dominant application layer protocols that form the foundation of modern internet services. You now understand how DNS enables service discovery, SSH provides secure remote access, and HTTP(S) serves as the universal protocol for APIs and web services.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS Deep Dives&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Setting up authoritative DNS with BIND9 or PowerDNS&lt;br /&gt;
* DNS security: DNSSEC for cryptographic verification of DNS responses&lt;br /&gt;
* DNS over HTTPS (DoH) and DNS over TLS (DoT) for encrypted DNS queries&lt;br /&gt;
* Dynamic DNS (DDNS) and service discovery patterns&lt;br /&gt;
* Split-horizon DNS for internal vs. external name resolution&lt;br /&gt;
* DNS-based load balancing and geographic routing (GeoDNS)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Advanced Topics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* SSH tunneling: Local forwarding (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;), remote forwarding (&amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;), dynamic forwarding (&amp;lt;code&amp;gt;-D&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ProxyJump (&amp;lt;code&amp;gt;-J&amp;lt;/code&amp;gt;) for accessing hosts through bastion servers&lt;br /&gt;
* SSH certificates (different from host keys) for scalable authentication&lt;br /&gt;
* SSH agent forwarding security considerations&lt;br /&gt;
* SSH config files (&amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;) for managing multiple hosts&lt;br /&gt;
* SSH as SOCKS proxy for secure browsing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS Evolution&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* HTTP/2 improvements: multiplexing, server push, header compression, binary protocol&lt;br /&gt;
* HTTP/3 and QUIC: UDP-based transport with built-in encryption&lt;br /&gt;
* WebSockets for real-time bidirectional communication&lt;br /&gt;
* Server-Sent Events (SSE) for server-push updates&lt;br /&gt;
* gRPC for efficient RPC over HTTP/2&lt;br /&gt;
* GraphQL as alternative to REST for flexible API queries&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Web Server Configuration&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Nginx advanced reverse proxy patterns&lt;br /&gt;
* HAProxy for high-performance load balancing&lt;br /&gt;
* Load balancing algorithms: round-robin, least connections, IP hash, consistent hashing&lt;br /&gt;
* SSL/TLS termination best practices&lt;br /&gt;
* Certificate management with Let&amp;#039;s Encrypt&lt;br /&gt;
* HTTP caching: proxy caching, CDN integration, cache invalidation strategies&lt;br /&gt;
* Security headers: HSTS, Content-Security-Policy, X-Frame-Options, CORS&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Modern Architecture Patterns&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Microservices architecture: benefits and challenges&lt;br /&gt;
* Service mesh: Istio, Linkerd, Consul Connect&lt;br /&gt;
* API gateways: Kong, Ambassador, Traefik, AWS API Gateway&lt;br /&gt;
* Kubernetes Ingress controllers and service routing&lt;br /&gt;
* Serverless architecture and function-as-a-service (FaaS)&lt;br /&gt;
* Event-driven architectures: message queues, pub/sub patterns&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Other Application Protocols&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Email protocols: SMTP for sending, IMAP for retrieval, SPF/DKIM/DMARC for authentication&lt;br /&gt;
* Database wire protocols: MySQL, PostgreSQL, MongoDB, Redis&lt;br /&gt;
* Message queue protocols: AMQP (RabbitMQ), Kafka protocol&lt;br /&gt;
* Monitoring and observability: Prometheus metrics, StatsD, OpenTelemetry, syslog&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man dig              # DNS query tool&lt;br /&gt;
man nslookup         # DNS lookup utility&lt;br /&gt;
man host             # DNS lookup utility&lt;br /&gt;
man ssh              # SSH client&lt;br /&gt;
man sshd             # SSH server daemon&lt;br /&gt;
man sshd_config      # SSH server configuration&lt;br /&gt;
man ssh_config       # SSH client configuration&lt;br /&gt;
man ssh-keygen       # SSH key generation and management&lt;br /&gt;
man ssh-add          # SSH agent key management&lt;br /&gt;
man curl             # HTTP client&lt;br /&gt;
man wget             # HTTP client alternative&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc1034 DNS RFC 1034] - Domain Names: Concepts and Facilities&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc1035 DNS RFC 1035] - Domain Names: Implementation and Specification&lt;br /&gt;
* [http://www.zytrax.com/books/dns/ DNS Zone File Guide] - Comprehensive DNS resource&lt;br /&gt;
* [https://developers.google.com/speed/public-dns Google Public DNS] - Documentation and best practices&lt;br /&gt;
* [https://www.cloudflare.com/learning/dns/what-is-dns/ Cloudflare 1.1.1.1] - DNS learning center&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc4251 SSH Protocol RFC 4251] - SSH Architecture&lt;br /&gt;
* [https://www.ssh.com/academy/ssh SSH.com Academy] - Comprehensive SSH tutorials&lt;br /&gt;
* [https://infosec.mozilla.org/guidelines/openssh Mozilla SSH Guidelines] - Security best practices&lt;br /&gt;
* [https://ed25519.cr.yp.to/ Ed25519 Benefits] - Information about Ed25519 cryptography&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc7230 HTTP/1.1 RFC 7230-7235] - HTTP specification series&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc7540 HTTP/2 RFC 7540] - HTTP/2 specification&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9114 HTTP/3 RFC 9114] - HTTP/3 specification&lt;br /&gt;
* [https://developer.mozilla.org/en-US/docs/Web/HTTP MDN HTTP Documentation] - Comprehensive HTTP reference&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free book by Ilya Grigorik&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Web Servers and Reverse Proxies&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://caddyserver.com/docs/ Caddy Documentation] - Modern web server with automatic HTTPS&lt;br /&gt;
* [https://nginx.org/en/docs/ Nginx Documentation] - High-performance web server and reverse proxy&lt;br /&gt;
* [https://www.haproxy.org/documentation.html HAProxy Documentation] - Load balancer and proxy&lt;br /&gt;
* [https://doc.traefik.io/traefik/ Traefik Documentation] - Modern HTTP reverse proxy and load balancer&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://owasp.org/www-project-top-ten/ OWASP Top 10] - Web application security risks&lt;br /&gt;
* [https://ssl-config.mozilla.org/ Mozilla SSL Configuration Generator] - TLS best practices&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt] - Free TLS certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of websites&lt;br /&gt;
&lt;br /&gt;
Practice Environments&lt;br /&gt;
&lt;br /&gt;
* [https://overthewire.org/wargames/bandit/ OverTheWire Bandit] - Linux command-line challenges including networking&lt;br /&gt;
* [https://www.hackthebox.com/ HackTheBox] - Penetration testing practice with networking components&lt;br /&gt;
* [https://tryhackme.com/ TryHackMe] - Guided cybersecurity learning including networking modules&lt;br /&gt;
* [https://www.katacoda.com/courses/kubernetes Kubernetes Playground] - Practice with modern orchestration&lt;br /&gt;
* [https://labs.play-with-docker.com/ Docker Playground] - Free Docker environment for testing&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8196</id>
		<title>OS Lab 10 - Containers and Docker</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8196"/>
		<updated>2025-12-16T16:32:09Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Part 3: Test Persistence */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how containers use Linux kernel namespaces (PID, mount, network, UTS, IPC, user, cgroup) to provide process isolation without requiring separate operating systems or hypervisors.&lt;br /&gt;
* Differentiate between container images (immutable filesystem templates) and running containers (ephemeral process instances in isolated namespaces).&lt;br /&gt;
* Understand how OverlayFS provides efficient layered filesystems that allow multiple containers to share common base layers while maintaining separate writable layers.&lt;br /&gt;
* Apply cgroup resource limits to constrain container CPU, memory, and I/O usage, preventing resource monopolization.&lt;br /&gt;
* Configure Docker networking using custom bridges, port mapping (NAT), and container-to-container communication with automatic DNS resolution.&lt;br /&gt;
* Use volumes and bind mounts to persist data beyond container lifecycles and share data between containers and the host.&lt;br /&gt;
* Build multi-container applications with coordinated networking and resource management, demonstrating modern microservices architecture patterns.&lt;br /&gt;
* Connect container concepts to previous labs: relate network namespaces to Lab 7, mount namespaces to Lab 2, PID namespaces to Lab 3, and Docker networking to Labs 7-9.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;from-manual-isolation-to-automated-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== From Manual Isolation to Automated Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you manually constructed network isolation using Linux kernel primitives. You created virtual network interfaces with &amp;lt;code&amp;gt;ip link add type veth&amp;lt;/code&amp;gt;, configured bridges with &amp;lt;code&amp;gt;ip link add type bridge&amp;lt;/code&amp;gt;, established isolated network stacks with &amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, and set up routing and NAT rules. Through this hands-on work, you gained deep insight into how the Linux kernel provides network isolation at the namespace level.&lt;br /&gt;
&lt;br /&gt;
Every time you executed &amp;lt;code&amp;gt;sudo ip netns exec red ping 10.0.0.3&amp;lt;/code&amp;gt;, you were demonstrating a fundamental concept: the kernel can create completely isolated environments where processes see only a subset of system resources. The &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace had its own network interfaces, its own routing table, and its own firewall rules—completely invisible to processes running in other namespaces or on the host.&lt;br /&gt;
&lt;br /&gt;
This isolation is powerful, but network namespaces are just one of seven namespace types that the Linux kernel provides. To fully isolate an application and create what we call a &amp;amp;quot;container,&amp;amp;quot; you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;): Isolated network stack—you&amp;#039;ve mastered this in Labs 7-9&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PID namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;): Isolated process tree—each namespace has its own PID 1&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mount namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;): Isolated filesystem view—different root directory from the host&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UTS namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;): Isolated hostname—each container can have its own hostname&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IPC namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;): Isolated inter-process communication—shared memory, semaphores, message queues&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;): Isolated UIDs/GIDs—security boundary for privilege separation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Cgroup namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;): Isolated view of control group hierarchy&lt;br /&gt;
&lt;br /&gt;
Additionally, you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Control groups (cgroups)&amp;#039;&amp;#039;&amp;#039; to limit CPU, memory, and I/O resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy-on-write filesystems&amp;#039;&amp;#039;&amp;#039; (OverlayFS) for efficient storage&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Image management&amp;#039;&amp;#039;&amp;#039; for distributing application packages&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Orchestration&amp;#039;&amp;#039;&amp;#039; for managing the lifecycle of multiple isolated environments&lt;br /&gt;
&lt;br /&gt;
Manually setting up all of these components for every application would require hundreds of commands and deep kernel knowledge. This is the problem that containerization technology solves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-are-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What are Containers? ===&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;container&amp;#039;&amp;#039;&amp;#039; is an isolated process (or process tree) that uses Linux kernel features—namespaces, cgroups, and layered filesystems—to provide the illusion of running in a separate system. Containers package an application with its dependencies, libraries, and configuration into a single unit that can run consistently across different environments.&lt;br /&gt;
&lt;br /&gt;
Containers are not a specific product or tool—they are a pattern for using kernel isolation features. Multiple container runtimes exist, each implementing this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Runtimes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039;: The most widely adopted container platform, providing a complete ecosystem (daemon, CLI, image format, registry)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Podman&amp;#039;&amp;#039;&amp;#039;: Daemonless container engine, compatible with Docker images and commands, can run rootless&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Industry-standard container runtime, used by Kubernetes and Docker (as of Docker 1.11+)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CRI-O&amp;#039;&amp;#039;&amp;#039;: Lightweight container runtime built for Kubernetes, implementing the Container Runtime Interface (CRI)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LXC/LXD&amp;#039;&amp;#039;&amp;#039;: System containers that more closely resemble traditional VMs, providing full init systems&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What Container Runtimes Provide:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Namespace Management&amp;#039;&amp;#039;&amp;#039;: Automatically create and configure all necessary namespace types&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Filesystem Layers&amp;#039;&amp;#039;&amp;#039;: Use OverlayFS or similar copy-on-write filesystems for efficient storage&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Configuration&amp;#039;&amp;#039;&amp;#039;: Set up bridges, veth pairs, and NAT rules automatically&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Resource Control&amp;#039;&amp;#039;&amp;#039;: Configure cgroups to enforce CPU and memory limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Image Distribution&amp;#039;&amp;#039;&amp;#039;: Provide standard formats (OCI) for packaging and distributing applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Lifecycle Management&amp;#039;&amp;#039;&amp;#039;: Offer commands to create, start, stop, and remove containers&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-as-a-container-runtime&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker as a Container Runtime ===&lt;br /&gt;
&lt;br /&gt;
In this lab, we use &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039; because it is the most widely deployed and well-documented container runtime. Docker&amp;#039;s architecture consists of:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Engine (dockerd)&amp;#039;&amp;#039;&amp;#039;: Daemon that manages containers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker CLI (docker)&amp;#039;&amp;#039;&amp;#039;: Command-line interface for interacting with the daemon&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Lower-level runtime that Docker uses internally&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;runc&amp;#039;&amp;#039;&amp;#039;: OCI-compliant runtime that actually creates and runs containers&lt;br /&gt;
&lt;br /&gt;
When you execute &amp;lt;code&amp;gt;docker run nginx&amp;lt;/code&amp;gt;, Docker performs approximately 50-100 system calls to configure namespaces, mount filesystems, set up networking, and start the process—all operations you could do manually but would require significant time and expertise.&lt;br /&gt;
&lt;br /&gt;
The concepts you learn with Docker apply to all container runtimes, as they all use the same underlying kernel features. The commands may differ (e.g., &amp;lt;code&amp;gt;podman run&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt;), but the fundamental mechanisms remain the same.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shift-in-software-deployment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Shift in Software Deployment ===&lt;br /&gt;
&lt;br /&gt;
Containers represent a fundamental paradigm shift in how we think about software deployment and infrastructure management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Traditional Deployment Model (Pre-Container Era):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Provision a server (physical or virtual machine)&lt;br /&gt;
2. Install operating system (Ubuntu, RHEL, etc.)&lt;br /&gt;
3. Install runtime dependencies (Python 3.9, Node.js 16, specific library versions)&lt;br /&gt;
4. Configure environment variables, users, permissions&lt;br /&gt;
5. Deploy application code&lt;br /&gt;
6. Configure monitoring, logging, security&lt;br /&gt;
7. Hope everything works the same as on your development machine&lt;br /&gt;
8. Troubleshoot when it doesn&amp;#039;t (&amp;amp;quot;works on my machine&amp;amp;quot; problem)&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model suffers from several critical issues:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dependency Hell&amp;#039;&amp;#039;&amp;#039;: Different applications require different, potentially conflicting library versions&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration Drift&amp;#039;&amp;#039;&amp;#039;: Development, staging, and production environments gradually diverge&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Snowflake Servers&amp;#039;&amp;#039;&amp;#039;: Each server becomes unique and unreproducible&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Slow Deployment&amp;#039;&amp;#039;&amp;#039;: Setting up a new environment can take hours or days&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Poor Resource Utilization&amp;#039;&amp;#039;&amp;#039;: Applications can&amp;#039;t share servers due to dependency conflicts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-Based Deployment Model:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Developer creates Dockerfile specifying exact environment&lt;br /&gt;
2. Build process creates immutable container image with all dependencies&lt;br /&gt;
3. Image tested in CI/CD pipeline (identical to production)&lt;br /&gt;
4. Image deployed to any server with Docker installed&lt;br /&gt;
5. Container starts in seconds with guaranteed-identical environment&lt;br /&gt;
6. Multiple isolated applications run on same host without conflicts&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model provides:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Images never change after building; deploy new versions rather than modifying running systems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Development environment = testing environment = production environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Portability&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;Build once, run anywhere&amp;amp;quot; (laptop, data center, cloud)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: Run 10-100 containers on a single host (vs. 5-10 VMs)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rapid Deployment&amp;#039;&amp;#039;&amp;#039;: Start containers in milliseconds vs. minutes for VMs&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Microservices Architecture&amp;#039;&amp;#039;&amp;#039;: Enables decomposing monoliths into independently deployable services&lt;br /&gt;
&lt;br /&gt;
This shift has revolutionized software engineering, enabling modern DevOps practices, continuous deployment, and cloud-native architectures. Companies like Netflix, Uber, and Airbnb run millions of containers to serve billions of requests daily.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;containers-vs-virtual-machines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Containers vs Virtual Machines ===&lt;br /&gt;
&lt;br /&gt;
Understanding the architectural difference between containers and virtual machines is crucial for appreciating why containers have become the dominant deployment model.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Virtual Machine Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│ Guest OS    │ │ Guest OS    │ │ Guest OS    │&lt;br /&gt;
│ (Kernel)    │ │ (Kernel)    │ │ (Kernel)    │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Hypervisor (VMware, KVM, Xen)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
        Host Operating System &amp;amp;amp; Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Each VM runs a complete guest operating system with its own kernel&lt;br /&gt;
* Hypervisor emulates hardware, providing virtual CPUs, RAM, disks, NICs&lt;br /&gt;
* Strong isolation (separate kernels mean vulnerabilities in one VM don&amp;#039;t affect others)&lt;br /&gt;
* Heavy resource consumption (each OS kernel needs 1-2GB RAM)&lt;br /&gt;
* Slow startup (boot entire OS: 30-60 seconds)&lt;br /&gt;
* Large disk footprint (each VM stores complete OS: 1-10GB)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Container Runtime (Docker Engine)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
    Host Operating System &amp;amp;amp; Shared Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* All containers share the host&amp;#039;s kernel (no guest OS needed)&lt;br /&gt;
* Container runtime manages namespace and cgroup isolation&lt;br /&gt;
* Lightweight isolation (namespaces separate processes, but they&amp;#039;re still just processes)&lt;br /&gt;
* Minimal resource overhead (containers use only incremental memory beyond their application)&lt;br /&gt;
* Fast startup (start process in isolated namespace: milliseconds)&lt;br /&gt;
* Small disk footprint (layered filesystem shares common base images: 10-100MB incremental)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Trade-off:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Virtual machines provide &amp;#039;&amp;#039;&amp;#039;stronger security isolation&amp;#039;&amp;#039;&amp;#039; at the cost of &amp;#039;&amp;#039;&amp;#039;resource efficiency&amp;#039;&amp;#039;&amp;#039;. If your threat model requires complete kernel isolation (e.g., multi-tenant cloud providers hosting untrusted code), VMs are appropriate.&lt;br /&gt;
&lt;br /&gt;
Containers provide &amp;#039;&amp;#039;&amp;#039;lighter-weight isolation&amp;#039;&amp;#039;&amp;#039; with &amp;#039;&amp;#039;&amp;#039;much better resource efficiency&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re running your own applications on your own infrastructure, containers are usually the right choice. You can run 10-100 containers on hardware that would support only 5-10 VMs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;An Important Insight:&amp;#039;&amp;#039;&amp;#039; When you run &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; on the host, you see container processes. They&amp;#039;re not hidden inside separate kernels like VM processes would be. This demonstrates that containers are just isolated processes on the host—the kernel makes them appear isolated through namespaces, but they&amp;#039;re fundamentally just processes.&lt;br /&gt;
&lt;br /&gt;
This is both a feature (efficiency, observability) and a constraint (shared kernel means a kernel vulnerability could affect all containers). Modern container security practices use defense-in-depth: namespaces + cgroups + seccomp + AppArmor/SELinux + user namespaces to create multiple security layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operating System&amp;#039;&amp;#039;&amp;#039;: Linux-based system (Ubuntu 20.04+ recommended, but Debian, Fedora, CentOS also supported)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RAM&amp;#039;&amp;#039;&amp;#039;: Minimum 2GB, 4GB recommended for comfortable operation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Disk Space&amp;#039;&amp;#039;&amp;#039;: At least 20GB free (Docker images and container layers consume significant space)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CPU&amp;#039;&amp;#039;&amp;#039;: Any modern x86_64 or ARM64 processor&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Privileges&amp;#039;&amp;#039;&amp;#039;: Root access via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (required for Docker installation and initial setup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kernel&amp;#039;&amp;#039;&amp;#039;: Minimum Linux kernel 3.10 (kernel 4.0+ recommended for full feature support)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check your kernel version:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;uname -r&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If below 3.10, you&amp;#039;ll need to update your kernel before proceeding.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check available disk space:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker stores images and containers in &amp;lt;code&amp;gt;/var/lib/docker&amp;lt;/code&amp;gt; by default. Ensure you have sufficient space.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
Before beginning, ensure the following packages are installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release \&lt;br /&gt;
    bridge-utils \&lt;br /&gt;
    net-tools&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Package descriptions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;apt-transport-https&amp;lt;/code&amp;gt;: Enables apt to retrieve packages over HTTPS&lt;br /&gt;
* &amp;lt;code&amp;gt;ca-certificates&amp;lt;/code&amp;gt;: Common CA certificates for SSL verification&lt;br /&gt;
* &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;: Command-line tool for transferring data (used to download Docker&amp;#039;s GPG key)&lt;br /&gt;
* &amp;lt;code&amp;gt;gnupg&amp;lt;/code&amp;gt;: GNU Privacy Guard for verifying package signatures&lt;br /&gt;
* &amp;lt;code&amp;gt;lsb-release&amp;lt;/code&amp;gt;: Provides Linux Standard Base version information (used to detect Ubuntu version)&lt;br /&gt;
* &amp;lt;code&amp;gt;bridge-utils&amp;lt;/code&amp;gt;: Tools for managing bridge devices (&amp;lt;code&amp;gt;brctl&amp;lt;/code&amp;gt; command)&lt;br /&gt;
* &amp;lt;code&amp;gt;net-tools&amp;lt;/code&amp;gt;: Legacy networking tools (&amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt;) for compatibility&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
This lab builds directly on concepts from previous labs. You should be comfortable with:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 2 (Filesystems):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Filesystem hierarchy and directory structure&lt;br /&gt;
* Mount points and the &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; command&lt;br /&gt;
* Understanding of &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; purposes&lt;br /&gt;
* File permissions and ownership (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Symbolic links and filesystem navigation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 3 (Processes and Jobs):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs (PIDs) and process hierarchy&lt;br /&gt;
* Parent-child process relationships&lt;br /&gt;
* &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; command and process listing&lt;br /&gt;
* Foreground vs. background processes&lt;br /&gt;
* Process signals (SIGTERM, SIGKILL)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 4 (Users, Groups, and Permissions):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs (UIDs) and group IDs (GIDs)&lt;br /&gt;
* Root vs. unprivileged users&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; for privilege escalation&lt;br /&gt;
* File and directory permissions (read, write, execute)&lt;br /&gt;
* Security implications of running processes as root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 7 (Network Fundamentals):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces and IP addresses&lt;br /&gt;
* Bridges and virtual ethernet (veth) pairs&lt;br /&gt;
* Subnets and CIDR notation&lt;br /&gt;
* Routing tables and default gateways&lt;br /&gt;
* Network Address Translation (NAT)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespaces&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip netns exec&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
This is crucial—Docker networking uses the exact same mechanisms you built manually.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 8 (Transport and Security):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP and UDP protocols&lt;br /&gt;
* Port numbers and socket addresses (IP:PORT)&lt;br /&gt;
* Client-server communication model&lt;br /&gt;
* The concept of listening vs. connecting&lt;br /&gt;
* TLS/SSL (Docker can use HTTPS for secure image distribution)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 9 (Application Protocols):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* HTTP/HTTPS protocols&lt;br /&gt;
* Reverse proxies and path-based routing&lt;br /&gt;
* DNS and hostname resolution&lt;br /&gt;
* Caddy web server configuration&lt;br /&gt;
* Multi-tier application architecture&lt;br /&gt;
&lt;br /&gt;
You should also be comfortable with:&lt;br /&gt;
&lt;br /&gt;
* Command-line text manipulation (&amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sed&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Basic bash scripting (variables, loops, conditionals)&lt;br /&gt;
* Using multiple terminal windows simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;linux-namespaces-the-foundation-of-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Linux Namespaces: The Foundation of Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you worked extensively with network namespaces—one of seven namespace types provided by the Linux kernel. Every time you executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red&lt;br /&gt;
sudo ip netns exec red ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You were creating an isolated network environment and executing commands within that isolated environment. The processes running in the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace could not see network interfaces, routes, or connections in other namespaces. This isolation is the fundamental mechanism that makes containers possible.&lt;br /&gt;
&lt;br /&gt;
Docker extends this concept to six additional namespace types, providing complete process isolation. Understanding namespaces is essential to understanding containers—they are not optional background knowledge, but rather the core technology that defines what a container is.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-seven-namespace-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Seven Namespace Types ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Network Namespace (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You know this namespace intimately from Labs 7-9. When you created the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; namespaces, you were creating isolated network stacks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces (lo, eth0, wlan0, etc.)&lt;br /&gt;
* IP addresses (each namespace has its own IPs)&lt;br /&gt;
* Routing tables (&amp;lt;code&amp;gt;ip route show&amp;lt;/code&amp;gt; output differs per namespace)&lt;br /&gt;
* Firewall rules (iptables/nftables rules are namespace-specific)&lt;br /&gt;
* Network sockets and ports (multiple processes in different namespaces can bind to the same port number)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 7, you manually created network namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red                    # Create isolated network stack&lt;br /&gt;
sudo ip netns exec red ip link show      # View interfaces in that namespace&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does exactly this when you run a container, but also creates six other namespace types simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example implications:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container A can listen on port 80, Container B can listen on port 80—no conflict&lt;br /&gt;
* Container A cannot see Container B&amp;#039;s network connections (&amp;lt;code&amp;gt;ss -tuna&amp;lt;/code&amp;gt; shows only its own)&lt;br /&gt;
* Each container has its own localhost (127.0.0.1) that&amp;#039;s separate from the host&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. PID Namespace (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The PID namespace isolates the process ID number space. This is related to what you learned in Lab 3 about process management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs—processes in different PID namespaces can have the same PID&lt;br /&gt;
* Process visibility—processes can only see other processes in the same PID namespace&lt;br /&gt;
* PID 1 (init process)—each PID namespace has its own PID 1&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The kernel maintains a separate process tree for each PID namespace. When you create a PID namespace and start a process in it:&lt;br /&gt;
&lt;br /&gt;
* Inside the namespace, that process is PID 1 (like an init system)&lt;br /&gt;
* Outside the namespace, that same process has a different PID (e.g., 12345)&lt;br /&gt;
* Processes inside cannot see processes outside their namespace tree&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On the host&lt;br /&gt;
ps aux | wc -l&lt;br /&gt;
# Output: 237 processes&lt;br /&gt;
&lt;br /&gt;
# Inside a container&lt;br /&gt;
docker exec container ps aux | wc -l&lt;br /&gt;
# Output: 5 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s processes think they&amp;#039;re the only processes on the system. They cannot see the host&amp;#039;s other 232 processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Mount Namespace (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The mount namespace isolates the filesystem mount table. This relates directly to Lab 2 where you learned about filesystems and mounting.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Mount points—what is mounted at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, etc.&lt;br /&gt;
* Root filesystem—each namespace can have a completely different root directory&lt;br /&gt;
* Mount propagation—mounts in one namespace don&amp;#039;t affect others (by default)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you create a mount namespace, the new namespace inherits a copy of the parent&amp;#039;s mount table. But subsequent mounts/unmounts in the child don&amp;#039;t affect the parent.&lt;br /&gt;
&lt;br /&gt;
Containers use this to provide a completely different filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host (Ubuntu)&lt;br /&gt;
ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...&lt;br /&gt;
&lt;br /&gt;
# In container (Fedora)&lt;br /&gt;
docker exec fedora ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...  # Different files!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both see &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, but they&amp;#039;re seeing different directories. The container&amp;#039;s &amp;lt;code&amp;gt;/etc/os-release&amp;lt;/code&amp;gt; shows Fedora, while the host&amp;#039;s shows Ubuntu.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Technical implementation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker uses OverlayFS (covered later) to construct the container&amp;#039;s root filesystem from image layers, then uses pivot_root or chroot to make that directory appear as &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to the container&amp;#039;s processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. UTS Namespace (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
UTS stands for &amp;amp;quot;Unix Timesharing System&amp;amp;quot;—a historical name. The UTS namespace isolates hostname and domain name.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System hostname (&amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; command output)&lt;br /&gt;
* Domain name (NIS domain name)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each UTS namespace can set its own hostname independently of other namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host&lt;br /&gt;
hostname&lt;br /&gt;
# Output: myserver.example.com&lt;br /&gt;
&lt;br /&gt;
# In container&lt;br /&gt;
docker exec container hostname&lt;br /&gt;
# Output: a1b2c3d4e5f6  (container ID)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Containers typically use the container ID as hostname by default, but you can override with &amp;lt;code&amp;gt;--hostname&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --hostname=webserver nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. IPC Namespace (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The IPC namespace isolates System V Inter-Process Communication resources. This relates to Lab 6 where you learned about IPC mechanisms.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System V message queues&lt;br /&gt;
* System V semaphore sets&lt;br /&gt;
* System V shared memory segments&lt;br /&gt;
* POSIX message queues (in &amp;lt;code&amp;gt;/dev/mqueue&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 6:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 6, you learned that processes can communicate via shared memory, message queues, and semaphores. The IPC namespace ensures that processes in different namespaces cannot access each other&amp;#039;s IPC objects, even if they use the same IPC keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A creates shared memory segment with key 1234&lt;br /&gt;
docker exec containerA ipcmk -M 1024 -p 1234&lt;br /&gt;
&lt;br /&gt;
# Container B tries to access it&lt;br /&gt;
docker exec containerB ipcs -m -k 1234&lt;br /&gt;
# Output: no segment found (different IPC namespace)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. User Namespace (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The user namespace isolates user IDs (UIDs) and group IDs (GIDs). This is the most complex namespace and provides significant security benefits.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs—UID 0 inside namespace can map to UID 100000 outside&lt;br /&gt;
* Group IDs—similar mapping for GIDs&lt;br /&gt;
* Capabilities—process can have capabilities inside namespace but not outside&lt;br /&gt;
* Security attributes—AppArmor/SELinux contexts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
User namespaces allow UID/GID mapping. A process can be root (UID 0) inside the namespace but unprivileged (e.g., UID 100000) outside.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Inside Container: UID 0 (root)&lt;br /&gt;
        ↓ mapping&lt;br /&gt;
Outside Container: UID 100000 (unprivileged)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security benefit:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even if an attacker compromises a container and gains root privileges inside the container, they&amp;#039;re still unprivileged on the host. If they escape the container, they cannot access files owned by actual root.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s approach:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, many Docker configurations share the host&amp;#039;s user namespace (for simplicity and compatibility). Rootless Docker and user-remapped Docker use separate user namespaces for enhanced security.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Cgroup Namespace (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The cgroup namespace isolates the view of the cgroup hierarchy. Note this is different from cgroups themselves (which we&amp;#039;ll cover separately).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/sys/fs/cgroup&amp;lt;/code&amp;gt; hierarchy&lt;br /&gt;
* Ability to see other containers&amp;#039; resource constraints&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without cgroup namespaces, a process can read &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt; and see the full path to its cgroup, revealing information about the container orchestration system.&lt;br /&gt;
&lt;br /&gt;
With cgroup namespaces, the process sees itself at the root of the cgroup tree, hiding the real hierarchy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; This namespace isolates the &amp;#039;&amp;#039;view&amp;#039;&amp;#039; of cgroups, not the enforcement of resource limits. Resource limits are enforced by cgroups themselves (covered in section 4.5).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;namespace-identifiers-understanding-procpidns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Namespace Identifiers: Understanding /proc/PID/ns/ ====&lt;br /&gt;
&lt;br /&gt;
Every process has a directory at &amp;lt;code&amp;gt;/proc/PID/ns/&amp;lt;/code&amp;gt; containing symbolic links to namespace identifiers. These links reveal which namespaces the process belongs to.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Examining namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 root root 0 Dec 12 10:30 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026531835]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 ipc -&amp;amp;gt; &amp;#039;ipc:[4026531839]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 mnt -&amp;amp;gt; &amp;#039;mnt:[4026531840]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 pid -&amp;amp;gt; &amp;#039;pid:[4026531836]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 uts -&amp;amp;gt; &amp;#039;uts:[4026531838]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the format:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each symlink follows the pattern: &amp;lt;code&amp;gt;namespace_type:[inode_number]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;inode number&amp;#039;&amp;#039;&amp;#039; is the critical piece of information. It uniquely identifies that specific namespace instance. Think of it as a &amp;amp;quot;namespace ID.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key principle: Same inode = Shared namespace, Different inode = Isolated namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example from Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you created network namespaces in Lab 7, each had a unique network namespace inode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host process&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Host&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
# Process in red namespace&lt;br /&gt;
sudo ip netns exec red ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532145]&amp;#039;  # Different inode = isolated!&lt;br /&gt;
&lt;br /&gt;
# Process in blue namespace&lt;br /&gt;
sudo ip netns exec blue ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532147]&amp;#039;  # Also different = also isolated!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Comparing container to host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID on host&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example output: 12345&lt;br /&gt;
&lt;br /&gt;
# View container&amp;#039;s namespaces&lt;br /&gt;
sudo ls -la /proc/12345/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# View host&amp;#039;s namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Different inodes! Container is isolated.&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Namespace sharing:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Sometimes containers intentionally share namespaces. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container shares the host&amp;#039;s network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These symlinks aren&amp;#039;t just informational—you can actually use them to enter namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --net=/proc/12345/ns/net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command enters the network namespace of PID 12345 and runs &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside that namespace. This is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works under the hood—it uses &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; to join the container&amp;#039;s namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary table:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Namespace Type&lt;br /&gt;
! What It Isolates&lt;br /&gt;
! Lab Connection&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;&lt;br /&gt;
| Network stack (interfaces, IPs, routes, ports)&lt;br /&gt;
| Lab 7: You built this manually!&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;&lt;br /&gt;
| Process IDs and process tree&lt;br /&gt;
| Lab 3: Process management&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;&lt;br /&gt;
| Filesystem mounts and root directory&lt;br /&gt;
| Lab 2: Mounting and filesystems&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;&lt;br /&gt;
| Hostname and domain name&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;&lt;br /&gt;
| Shared memory, message queues, semaphores&lt;br /&gt;
| Lab 6: IPC mechanisms&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&lt;br /&gt;
| User and group IDs, capabilities&lt;br /&gt;
| Lab 4: Users and permissions&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
| View of cgroup hierarchy&lt;br /&gt;
| -&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-images-vs-running-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Images vs Running Containers ===&lt;br /&gt;
&lt;br /&gt;
This distinction is fundamental to understanding Docker and is often a source of confusion.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A container image is a &amp;#039;&amp;#039;&amp;#039;read-only template&amp;#039;&amp;#039;&amp;#039; consisting of:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;A root filesystem&amp;#039;&amp;#039;&amp;#039;: All files and directories that will appear in the container (applications, libraries, configuration files)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Metadata&amp;#039;&amp;#039;&amp;#039;: Information about how to run the container (default command, environment variables, exposed ports, volumes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Layers&amp;#039;&amp;#039;&amp;#039;: The filesystem is composed of multiple read-only layers (explained in section 4.4)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable&amp;#039;&amp;#039;&amp;#039;: Once built, an image never changes&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Shareable&amp;#039;&amp;#039;&amp;#039;: Multiple containers can use the same image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Versionable&amp;#039;&amp;#039;&amp;#039;: Images can have tags (e.g., &amp;lt;code&amp;gt;nginx:1.21&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nginx:latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Distributable&amp;#039;&amp;#039;&amp;#039;: Images can be pushed to/pulled from registries (Docker Hub, private registries)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stored on disk&amp;#039;&amp;#039;&amp;#039;: Images consume storage even when not running&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Running Container:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A running container is an &amp;#039;&amp;#039;&amp;#039;instance&amp;#039;&amp;#039;&amp;#039; of an image—a process (or process tree) running in isolated namespaces with its own writable filesystem layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Ephemeral&amp;#039;&amp;#039;&amp;#039;: State is lost when the container is removed (unless using volumes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mutable&amp;#039;&amp;#039;&amp;#039;: Can make changes inside the container (install packages, create files)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process-based&amp;#039;&amp;#039;&amp;#039;: A container is fundamentally just a Linux process in isolated namespaces&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived&amp;#039;&amp;#039;&amp;#039;: Containers are typically created, used, and destroyed frequently&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful during runtime&amp;#039;&amp;#039;&amp;#039;: Maintains state while running, but that state disappears on removal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example to illustrate:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Pull an image (download the template)&lt;br /&gt;
docker pull nginx&lt;br /&gt;
&lt;br /&gt;
# The image now exists on disk&lt;br /&gt;
docker images&lt;br /&gt;
# Output shows: nginx  latest  a1b2c3d4  100MB&lt;br /&gt;
&lt;br /&gt;
# Start first container from this image&lt;br /&gt;
docker run -d --name web1 nginx&lt;br /&gt;
&lt;br /&gt;
# Start second container from the same image  &lt;br /&gt;
docker run -d --name web2 nginx&lt;br /&gt;
&lt;br /&gt;
# Both containers share the same base image filesystem&lt;br /&gt;
# But each has its own writable layer and separate namespaces&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now you have:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;One image&amp;#039;&amp;#039;&amp;#039; (nginx:latest) on disk&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Two containers&amp;#039;&amp;#039;&amp;#039; (web1 and web2) running as separate processes&lt;br /&gt;
* Each container has its own PID namespace, network namespace, etc.&lt;br /&gt;
* Changes in web1 don&amp;#039;t affect web2 (isolated writable layers)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verification:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Modify web1&lt;br /&gt;
docker exec web1 bash -c &amp;quot;echo &amp;#039;Hello from web1&amp;#039; &amp;gt; /usr/share/nginx/html/test.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Check web1&lt;br /&gt;
docker exec web1 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: Hello from web1&lt;br /&gt;
&lt;br /&gt;
# Check web2&lt;br /&gt;
docker exec web2 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: cat: /usr/share/nginx/html/test.txt: No such file or directory&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The file exists in web1 but not in web2, even though they&amp;#039;re from the same image. Each container has its own writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Image to Container Relationship Diagram:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;          [nginx Image]&lt;br /&gt;
          (Read-only)&lt;br /&gt;
               |&lt;br /&gt;
     ┌─────────┴─────────┐&lt;br /&gt;
     ↓                   ↓&lt;br /&gt;
[Container 1]       [Container 2]&lt;br /&gt;
(Writable layer)    (Writable layer)&lt;br /&gt;
(PID namespace)     (PID namespace)&lt;br /&gt;
(Net namespace)     (Net namespace)&lt;br /&gt;
(Isolated)          (Isolated)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filesystem perspective:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Image Layers (read-only, shared):&lt;br /&gt;
├─ Layer 3: nginx files&lt;br /&gt;
├─ Layer 2: nginx dependencies  &lt;br /&gt;
└─ Layer 1: Base OS (Debian)&lt;br /&gt;
&lt;br /&gt;
Container 1 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 1&lt;br /&gt;
&lt;br /&gt;
Container 2 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why this design?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: 100 containers from the same image share one copy of the base filesystem&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039;: Starting a container doesn&amp;#039;t require copying files—just create a new writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Consistency&amp;#039;&amp;#039;&amp;#039;: All containers from an image start with identical state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutability&amp;#039;&amp;#039;&amp;#039;: Encourages treating containers as disposable—don&amp;#039;t modify running containers, rebuild images instead&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle-and-ephemeral-nature&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle and Ephemeral Nature ===&lt;br /&gt;
&lt;br /&gt;
Understanding the container lifecycle is essential for proper container usage. Containers are designed to be ephemeral—short-lived and replaceable.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────┐&lt;br /&gt;
│ Created │ (Container exists but not running)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │ (Processes executing in isolated namespaces)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker stop (SIGTERM, then SIGKILL after timeout)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Stopped │ (Processes terminated, filesystem layer persists)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start (restart with same writable layer)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker rm (delete container)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Removed │ (Container and writable layer deleted forever)&lt;br /&gt;
└─────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key lifecycle commands:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create and start in one step (most common)&lt;br /&gt;
docker run nginx&lt;br /&gt;
&lt;br /&gt;
# Create without starting&lt;br /&gt;
docker create --name test nginx&lt;br /&gt;
&lt;br /&gt;
# Start existing stopped container&lt;br /&gt;
docker start test&lt;br /&gt;
&lt;br /&gt;
# Stop running container (SIGTERM to main process)&lt;br /&gt;
docker stop test&lt;br /&gt;
&lt;br /&gt;
# Force stop (SIGKILL)&lt;br /&gt;
docker kill test&lt;br /&gt;
&lt;br /&gt;
# Remove stopped container&lt;br /&gt;
docker rm test&lt;br /&gt;
&lt;br /&gt;
# Remove running container (force)&lt;br /&gt;
docker rm -f test&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Ephemeral Nature:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, all changes made inside a container are lost when the container is removed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container&lt;br /&gt;
docker run -d --name demo nginx&lt;br /&gt;
&lt;br /&gt;
# Make changes inside&lt;br /&gt;
docker exec demo bash -c &amp;quot;echo &amp;#039;My data&amp;#039; &amp;gt; /tmp/important.txt&amp;quot;&lt;br /&gt;
docker exec demo cat /tmp/important.txt&lt;br /&gt;
# Output: My data&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop demo&lt;br /&gt;
docker rm demo&lt;br /&gt;
&lt;br /&gt;
# Try to access the data&lt;br /&gt;
docker run --name demo2 nginx&lt;br /&gt;
docker exec demo2 cat /tmp/important.txt&lt;br /&gt;
# Output: cat: /tmp/important.txt: No such file or directory&lt;br /&gt;
# THE DATA IS GONE!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why ephemeral?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This might seem like a limitation, but it&amp;#039;s actually a feature that enables important practices:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t patch running systems; deploy new versions&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Every deployment starts from a known state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Testing&amp;#039;&amp;#039;&amp;#039;: Test environments are identical to production&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Rollback&amp;#039;&amp;#039;&amp;#039;: Easy to roll back to previous image version&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Scaling&amp;#039;&amp;#039;&amp;#039;: Identical containers can be created/destroyed dynamically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When you need persistence:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For data that must survive container restarts, use &amp;#039;&amp;#039;&amp;#039;volumes&amp;#039;&amp;#039;&amp;#039; (covered in section 4.7):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create named volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use volume in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data in /data survives container removal&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container lifecycle best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Treat containers as cattle, not pets&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t name them, don&amp;#039;t SSH into them to debug, don&amp;#039;t manually configure them&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Logs go to stdout/stderr&amp;#039;&amp;#039;&amp;#039;: Not to files inside the container (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration via environment variables&amp;#039;&amp;#039;&amp;#039;: Not by editing files inside the container&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data goes in volumes&amp;#039;&amp;#039;&amp;#039;: Not in the container&amp;#039;s writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived processes&amp;#039;&amp;#039;&amp;#039;: Containers should start quickly and shut down gracefully&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about process lifecycle (start, run, terminate). Containers follow a similar lifecycle, but operate at a higher level of abstraction—each container lifecycle event actually involves creating/destroying multiple processes in isolated namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;control-groups-cgroups-resource-limiting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Control Groups (cgroups): Resource Limiting ===&lt;br /&gt;
&lt;br /&gt;
Control groups (cgroups) are a Linux kernel feature that limits, accounts for, and isolates resource usage (CPU, memory, disk I/O, network) of process groups. Without cgroups, a runaway container could consume all CPU or memory, starving other containers and crashing the host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without resource limits:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Malicious or buggy container&lt;br /&gt;
docker run -d evil-container&lt;br /&gt;
&lt;br /&gt;
# This container&amp;#039;s process could:&lt;br /&gt;
# - Consume 100% CPU (slow down everything else)&lt;br /&gt;
# - Allocate all available RAM (trigger OOM killer on host)&lt;br /&gt;
# - Fill up disk space (crash other containers)&lt;br /&gt;
# - Monopolize network bandwidth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable in multi-tenant environments. You need resource isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: cgroups&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Cgroups organize processes into hierarchical groups with configurable resource limits. The kernel enforces these limits, preventing processes from exceeding their allocation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cgroup Controllers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The Linux kernel provides several cgroup controllers, each managing a different resource type:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpu&amp;#039;&amp;#039;&amp;#039;: Limits CPU time&lt;br /&gt;
#* CPU shares (relative priority)&lt;br /&gt;
#* CPU quotas (hard limits)&lt;br /&gt;
#* CPU affinity (pin to specific cores)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;memory&amp;#039;&amp;#039;&amp;#039;: Limits RAM usage&lt;br /&gt;
#* Hard limits (container killed if exceeded)&lt;br /&gt;
#* Soft limits (reclaim memory under pressure)&lt;br /&gt;
#* Swap limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;blkio&amp;#039;&amp;#039;&amp;#039;: Limits disk I/O&lt;br /&gt;
#* Read/write bandwidth limits&lt;br /&gt;
#* I/O operation rate limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;net_cls/net_prio&amp;#039;&amp;#039;&amp;#039;: Network bandwidth control&lt;br /&gt;
#* Traffic classification&lt;br /&gt;
#* Priority settings&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;pids&amp;#039;&amp;#039;&amp;#039;: Limits number of processes&lt;br /&gt;
#* Prevents fork bombs&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpuset&amp;#039;&amp;#039;&amp;#039;: Assigns specific CPUs and memory nodes&lt;br /&gt;
#* NUMA awareness&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s cgroup integration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you start a container with resource limits, Docker configures the appropriate cgroups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --memory=512m --cpus=1.5 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a cgroup hierarchy at &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; and configures:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;memory.limit_in_bytes = 536870912&amp;lt;/code&amp;gt; (512MB)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt; to enforce 1.5 CPUs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Viewing cgroup settings:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example: 12345&lt;br /&gt;
&lt;br /&gt;
# View memory limit&lt;br /&gt;
cat /sys/fs/cgroup/memory/docker/12345/memory.limit_in_bytes&lt;br /&gt;
# Output: 536870912&lt;br /&gt;
&lt;br /&gt;
# View CPU quota&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/12345/cpu.cfs_quota_us&lt;br /&gt;
# Output: 150000 (1.5 CPUs)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common resource limit flags:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Memory limits&lt;br /&gt;
--memory=512m              # Hard limit: 512MB RAM&lt;br /&gt;
--memory-reservation=256m  # Soft limit: try to stay under 256MB&lt;br /&gt;
--memory-swap=512m         # Total memory+swap limit&lt;br /&gt;
&lt;br /&gt;
# CPU limits&lt;br /&gt;
--cpus=1.5                 # Use at most 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512           # Relative CPU priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1          # Pin to CPU cores 0 and 1&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb    # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb   # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100           # Max 100 processes in container&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Testing memory limits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container with 256MB memory limit&lt;br /&gt;
docker run -it --memory=256m ubuntu bash&lt;br /&gt;
&lt;br /&gt;
# Inside container, try to allocate 512MB&lt;br /&gt;
# (requires &amp;#039;stress&amp;#039; tool)&lt;br /&gt;
apt-get update &amp;amp;&amp;amp; apt-get install -y stress&lt;br /&gt;
stress --vm 1 --vm-bytes 512M&lt;br /&gt;
&lt;br /&gt;
# Container is killed when exceeding limit&lt;br /&gt;
# Output: stress: FAIL: [1] (415) &amp;lt;-- worker 7 got signal 9&lt;br /&gt;
# Signal 9 = SIGKILL (OOM killer)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why cgroups are essential:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-tenancy&amp;#039;&amp;#039;&amp;#039;: Run untrusted workloads safely&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Quality of Service&amp;#039;&amp;#039;&amp;#039;: Guarantee resources for critical applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Fair sharing&amp;#039;&amp;#039;&amp;#039;: Prevent one container from monopolizing resources&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Predictability&amp;#039;&amp;#039;&amp;#039;: Know exactly how much resources each container can use&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Cost control&amp;#039;&amp;#039;&amp;#039;: In cloud environments, map cgroups to billing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;cgroup vs namespace distinction:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;isolation&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;resource limits&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Both are necessary for containers. Namespaces prevent containers from seeing each other; cgroups prevent containers from starving each other.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-networking-bridges-veth-pairs-and-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Networking: Bridges, veth Pairs, and NAT ===&lt;br /&gt;
&lt;br /&gt;
Docker networking should feel familiar—it uses the exact same mechanisms you built manually in Lab 7. The primary difference is automation: Docker sets up bridges, veth pairs, routes, and NAT rules automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Default Docker Network Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you install Docker, it creates a default bridge network:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐&lt;br /&gt;
│  Container A    │  │  Container B    │  │  Container C    │&lt;br /&gt;
│  172.17.0.2     │  │  172.17.0.3     │  │  172.17.0.4     │&lt;br /&gt;
└────────┬────────┘  └────────┬────────┘  └────────┬────────┘&lt;br /&gt;
         │eth0               │eth0               │eth0&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
      (veth pair)         (veth pair)         (veth pair)&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
    ┌────┴───────────────────┴───────────────────┴────┐&lt;br /&gt;
    │              docker0 Bridge                      │&lt;br /&gt;
    │              172.17.0.1/16                       │&lt;br /&gt;
    └────────────────────┬─────────────────────────────┘&lt;br /&gt;
                         │&lt;br /&gt;
                    [Host eth0]&lt;br /&gt;
                         │&lt;br /&gt;
                    [Internet]&lt;br /&gt;
                   (via NAT/MASQUERADE)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Component breakdown (all from Lab 7!):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Bridge Interface (&amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically creates a bridge interface when installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show docker0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;3: docker0: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    link/ether 02:42:8f:a3:f1:2a brd ff:ff:ff:ff:ff:ff&lt;br /&gt;
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; from Lab 7:&lt;br /&gt;
&lt;br /&gt;
* Bridge interface acting as virtual switch&lt;br /&gt;
* Assigned IP address 172.17.0.1&lt;br /&gt;
* Subnet 172.17.0.0/16 (65,536 addresses available)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. veth Pairs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For each container, Docker creates a veth pair:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep veth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;8: veth7a3f2b1@if7: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&lt;br /&gt;
10: veth9d4e8c2@if9: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Decoding this:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;veth7a3f2b1@if7&amp;lt;/code&amp;gt;: One end of veth pair, connected to bridge (&amp;lt;code&amp;gt;master docker0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;@if7&amp;lt;/code&amp;gt;: Paired with interface index 7 (inside container)&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly what you did in Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-host type veth peer name v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does the same thing, automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Container Network Namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each container has its own network namespace (you built these manually in Lab 7!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View container&amp;#039;s network interfaces&lt;br /&gt;
docker exec container ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
&lt;br /&gt;
7: eth0@if8: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container sees:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Its own loopback interface&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if8&amp;lt;/code&amp;gt;: Its end of the veth pair (paired with host&amp;#039;s interface index 8)&lt;br /&gt;
* IP address from docker0 subnet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. IP Address Assignment&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker acts as a simple DHCP-like service, assigning IPs sequentially:&lt;br /&gt;
&lt;br /&gt;
* First container: 172.17.0.2&lt;br /&gt;
* Second container: 172.17.0.3&lt;br /&gt;
* Third container: 172.17.0.4&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Routing in Container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the container&amp;#039;s routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec container ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 172.17.0.1 dev eth0&lt;br /&gt;
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Default route: Send all traffic to 172.17.0.1 (the bridge) for routing to internet&lt;br /&gt;
* Local route: 172.17.0.0/16 is directly reachable via eth0&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the routing you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. NAT for Internet Access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically configures iptables/nftables NAT rules (MASQUERADE) so containers can reach the internet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;MASQUERADE  all  --  172.17.0.0/16  0.0.0.0/0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the NAT you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Port Mapping (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you use &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;, Docker sets up Destination NAT (DNAT):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8080:80 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker adds an iptables DNAT rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep DNAT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.2:80&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039; Traffic arriving at host port 8080 is redirected to 172.17.0.2:80&lt;br /&gt;
&lt;br /&gt;
This is also NAT, specifically DNAT (Destination NAT). You encountered source NAT (SNAT/MASQUERADE) in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-to-Container Communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Containers on the same bridge can communicate directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A pings Container B&lt;br /&gt;
docker exec containerA ping 172.17.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The packet flow:&lt;br /&gt;
&lt;br /&gt;
# Container A sends to 172.17.0.2 (Container B&amp;#039;s IP)&lt;br /&gt;
# Packet goes out Container A&amp;#039;s eth0 (through veth pair)&lt;br /&gt;
# Arrives on docker0 bridge&lt;br /&gt;
# Bridge forwards to veth pair for Container B&lt;br /&gt;
# Packet arrives at Container B&amp;#039;s eth0&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;No routing through host networking needed&amp;#039;&amp;#039;&amp;#039;—the bridge forwards directly (Layer 2 switching).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Custom Networks&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can create custom bridge networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.20.0.0/24 mynet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a new bridge interface (e.g., &amp;lt;code&amp;gt;br-abc123def456&amp;lt;/code&amp;gt;) with subnet 10.20.0.0/24.&lt;br /&gt;
&lt;br /&gt;
Containers on different networks are isolated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --network=mynet --name=isolated nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container is on &amp;lt;code&amp;gt;mynet&amp;lt;/code&amp;gt;, not &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;, so it cannot communicate with containers on the default bridge (different Layer 2 segment).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS Resolution Between Containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker provides automatic DNS resolution for container names within custom networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create mynet&lt;br /&gt;
docker run -d --network=mynet --name=web nginx&lt;br /&gt;
docker run -d --network=mynet --name=app alpine&lt;br /&gt;
&lt;br /&gt;
# From app container&lt;br /&gt;
docker exec app ping web&lt;br /&gt;
# Resolves &amp;#039;web&amp;#039; to web container&amp;#039;s IP address!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker runs an embedded DNS server (listening on 127.0.0.11 inside containers) that resolves container names to IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Modes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker supports several network modes:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;bridge&amp;#039;&amp;#039;&amp;#039; (default): Container on docker0 bridge (or custom bridge)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039;: Container shares host&amp;#039;s network namespace (no isolation)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;none&amp;#039;&amp;#039;&amp;#039;: No networking (container has only loopback)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;container:name&amp;#039;&amp;#039;&amp;#039;: Share another container&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example: host mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s network namespace inode is the same as the host&amp;#039;s:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container sees all host&amp;#039;s network interfaces and can bind to any port on any interface.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker networking uses:&lt;br /&gt;
&lt;br /&gt;
* Bridges (like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* veth pairs (like &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; ↔ &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Network namespaces (like &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Routing tables and default routes&lt;br /&gt;
* NAT/MASQUERADE for internet access&lt;br /&gt;
* DNAT for port mapping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;volumes-persistent-and-shared-storage&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volumes: Persistent and Shared Storage ===&lt;br /&gt;
&lt;br /&gt;
By default, container filesystems are ephemeral—all changes are lost when the container is removed. For data that must persist (databases, user uploads, logs), Docker provides volumes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start database container&lt;br /&gt;
docker run -d --name db postgres&lt;br /&gt;
&lt;br /&gt;
# Database writes data&lt;br /&gt;
docker exec db psql -c &amp;quot;CREATE DATABASE myapp;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker rm -f db&lt;br /&gt;
&lt;br /&gt;
# Data is GONE FOREVER!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable for stateful applications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: Volumes&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Volumes are directories on the host that are mounted into containers. Data written to volumes persists beyond container lifecycle.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two Volume Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Named Volumes (Docker-managed):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker manages the volume storage location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data written to /data inside container is stored in:&lt;br /&gt;
# /var/lib/docker/volumes/mydata/_data (on host)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Docker manages storage location&lt;br /&gt;
* Portable across hosts (can be backed up, restored)&lt;br /&gt;
* Works with volume plugins (NFS, cloud storage)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Use named volumes for production databases, critical data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Bind Mounts (Host directory):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Mount a host directory directly into the container.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Mount host directory into container&lt;br /&gt;
docker run -v /home/user/html:/usr/share/nginx/html nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Any changes in &amp;lt;code&amp;gt;/home/user/html&amp;lt;/code&amp;gt; on the host are immediately visible in &amp;lt;code&amp;gt;/usr/share/nginx/html&amp;lt;/code&amp;gt; inside the container, and vice versa.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Direct access to files from host&lt;br /&gt;
* Useful for development (edit code on host, see changes in container immediately)&lt;br /&gt;
* No Docker management needed&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Disadvantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Tied to specific host filesystem paths&lt;br /&gt;
* Less portable&lt;br /&gt;
* Permissions can be tricky (host UID vs. container UID)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Docker essentially does:&lt;br /&gt;
mount --bind /var/lib/docker/volumes/mydata/_data /data&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Sharing Between Containers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Multiple containers can share the same volume:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create shared&lt;br /&gt;
&lt;br /&gt;
# Container 1 writes&lt;br /&gt;
docker run -v shared:/data --name writer alpine sh -c &amp;quot;echo &amp;#039;Hello&amp;#039; &amp;gt; /data/file.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Container 2 reads&lt;br /&gt;
docker run -v shared:/data --name reader alpine cat /data/file.txt&lt;br /&gt;
# Output: Hello&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Use cases:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Shared configuration between containers&lt;br /&gt;
* Log aggregation (multiple containers write logs to shared volume)&lt;br /&gt;
* Data processing pipelines (one container produces, another consumes)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Lifecycle:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect mydata&lt;br /&gt;
&lt;br /&gt;
# Remove volume (only if no containers using it)&lt;br /&gt;
docker volume rm mydata&lt;br /&gt;
&lt;br /&gt;
# Remove all unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Inspecting Volume Mounts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect container --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;volume&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/var/lib/docker/volumes/mydata/_data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;z&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;volume&amp;amp;quot; or &amp;amp;quot;bind&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Container-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: Read-write (true) or read-only (false)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read-Only Volumes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For security, you can mount volumes read-only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -v mydata:/data:ro nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container can read &amp;lt;code&amp;gt;/data&amp;lt;/code&amp;gt; but cannot write to it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;tmpfs Mounts (In-Memory Temporary Storage):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For sensitive data that should never touch disk:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --tmpfs /tmp:size=100m nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; directory is stored in RAM and disappears when the container stops.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Named volumes for databases&amp;#039;&amp;#039;&amp;#039;: Postgres, MySQL, MongoDB&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bind mounts for development&amp;#039;&amp;#039;&amp;#039;: Code that you&amp;#039;re actively editing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;tmpfs for secrets&amp;#039;&amp;#039;&amp;#039;: Temporary credentials, keys&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volume plugins for cloud&amp;#039;&amp;#039;&amp;#039;: AWS EBS, Azure Disk, GCP Persistent Disk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;laboratory-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Laboratory Exercises ==&lt;br /&gt;
&lt;br /&gt;
The following exercises build progressively, demonstrating how Docker automates the kernel-level primitives you mastered in previous labs. You will install Docker, inspect namespace isolation, explore interactive containers, configure persistent storage, and build a multi-container application with networking and resource limits.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-installing-docker&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Installing Docker ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Install Docker Engine from the official Docker repository and configure it for non-root access.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why the official repository?&amp;#039;&amp;#039;&amp;#039; Ubuntu&amp;#039;s default repositories often contain outdated Docker versions. The official Docker repository provides the latest stable releases with security updates and new features.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Remove old Docker versions (if any)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
If you previously installed Docker from Ubuntu&amp;#039;s repositories or older Docker installations exist, remove them to avoid conflicts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt remove docker docker-engine docker.io containerd runc&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It&amp;#039;s safe to run this even if these packages aren&amp;#039;t installed—apt will simply report they&amp;#039;re not present.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Install prerequisites&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Install packages needed for adding Docker&amp;#039;s repository:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Add Docker&amp;#039;s GPG key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker signs its packages with a GPG key to ensure authenticity. Add this key to your system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo install -m 0755 -d /etc/apt/keyrings&lt;br /&gt;
&lt;br /&gt;
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \&lt;br /&gt;
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg&lt;br /&gt;
&lt;br /&gt;
sudo chmod a+r /etc/apt/keyrings/docker.gpg&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Creates &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; directory for storing repository keys&lt;br /&gt;
* Downloads Docker&amp;#039;s GPG public key&lt;br /&gt;
* Converts it to binary format (&amp;lt;code&amp;gt;.gpg&amp;lt;/code&amp;gt; file)&lt;br /&gt;
* Makes it readable by all users&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Add Docker repository to apt sources&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tee /etc/apt/sources.list.d/docker.sources &amp;lt;&amp;lt;EOF&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://download.docker.com/linux/ubuntu&lt;br /&gt;
Suites: $(. /etc/os-release &amp;amp;&amp;amp; echo &amp;quot;${UBUNTU_CODENAME:-$VERSION_CODENAME}&amp;quot;)&lt;br /&gt;
Components: stable&lt;br /&gt;
Signed-By: /etc/apt/keyrings/docker.gpg&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Detects your CPU architecture (amd64, arm64, etc.)&lt;br /&gt;
* Detects your Ubuntu version codename (focal, jammy, etc.)&lt;br /&gt;
* Adds Docker&amp;#039;s repository to apt&amp;#039;s source list&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Install Docker Engine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Update package index and install Docker:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    docker-ce \&lt;br /&gt;
    docker-ce-cli \&lt;br /&gt;
    containerd.io \&lt;br /&gt;
    docker-buildx-plugin \&lt;br /&gt;
    docker-compose-plugin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packages installed:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce&amp;lt;/code&amp;gt;: Docker Community Edition engine (the main Docker daemon)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce-cli&amp;lt;/code&amp;gt;: Docker command-line interface&lt;br /&gt;
* &amp;lt;code&amp;gt;containerd.io&amp;lt;/code&amp;gt;: Container runtime that Docker uses under the hood&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-buildx-plugin&amp;lt;/code&amp;gt;: Extended build capabilities (multi-platform images)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-compose-plugin&amp;lt;/code&amp;gt;: Docker Compose for multi-container applications&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Verify Docker installation&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check Docker version:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo docker --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Docker version 24.0.7, build afdd53b&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your version number may be different (newer), which is fine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Start and enable Docker service&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Ensure Docker daemon starts on boot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check service status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl status docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;● docker.service - Docker Application Container Engine&lt;br /&gt;
     Loaded: loaded (/lib/systemd/system/docker.service; enabled)&lt;br /&gt;
     Active: active (running) since Thu 2024-12-12 10:30:00 UTC; 5min ago&amp;lt;/pre&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;Active: active (running)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Configure non-root Docker access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, only root can run Docker commands. To run Docker without &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, add your user to the &amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important:&amp;#039;&amp;#039;&amp;#039; Log out and log back in for this change to take effect. Alternatively, you can run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;newgrp docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This starts a new shell with updated group membership.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Verify non-root access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Test that you can run Docker without sudo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If this works without errors, you&amp;#039;ve successfully installed Docker!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;hello-world:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/hello-world&lt;br /&gt;
c1ec31eb5944: Pull complete&lt;br /&gt;
Digest: sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6&lt;br /&gt;
Status: Downloaded newer image for hello-world:latest&lt;br /&gt;
&lt;br /&gt;
Hello from Docker!&lt;br /&gt;
This message shows that your installation appears to be working correctly.&lt;br /&gt;
&lt;br /&gt;
To generate this message, Docker took the following steps:&lt;br /&gt;
 1. The Docker client contacted the Docker daemon.&lt;br /&gt;
 2. The Docker daemon pulled the &amp;amp;quot;hello-world&amp;amp;quot; image from the Docker Hub.&lt;br /&gt;
 3. The Docker daemon created a new container from that image which runs the&lt;br /&gt;
    executable that produces the output you are currently reading.&lt;br /&gt;
 4. The Docker daemon streamed that output to the Docker client, which sent it&lt;br /&gt;
    to your terminal.&lt;br /&gt;
&lt;br /&gt;
To try something more ambitious, you can run an Ubuntu container with:&lt;br /&gt;
 $ docker run -it ubuntu bash&lt;br /&gt;
&lt;br /&gt;
Share images, automate workflows, and more with a free Docker ID:&lt;br /&gt;
 https://hub.docker.com/&lt;br /&gt;
&lt;br /&gt;
For more examples and ideas, visit:&lt;br /&gt;
 https://docs.docker.com/get-started/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker client (&amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; command) connected to Docker daemon (dockerd)&lt;br /&gt;
# Daemon checked local images—didn&amp;#039;t find &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt;&lt;br /&gt;
# Daemon pulled &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt; image from Docker Hub (public registry)&lt;br /&gt;
# Daemon created container from image&lt;br /&gt;
# Container executed its program (printed the message)&lt;br /&gt;
# Container exited&lt;br /&gt;
# Output sent back to your terminal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up test container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the hello-world container with status &amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm $(docker ps -aq --filter &amp;quot;ancestor=hello-world&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify it&amp;#039;s gone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo systemctl status docker&amp;lt;/code&amp;gt; (showing Active: active (running))&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the entire message)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-hello-world-and-namespace-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercise B: Hello World and Namespace Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run your first container, understand the container lifecycle, and inspect the Linux kernel namespaces that provide container isolation—connecting directly to your work in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-hello-world&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Hello World ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run the hello-world container (if not done in Exercise A)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
We covered this in Exercise A, but let&amp;#039;s examine what actually happened in more detail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: List all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Nothing (empty list)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why?&amp;#039;&amp;#039;&amp;#039; The hello-world container ran, printed its message, and exited immediately. &amp;lt;code&amp;gt;docker ps&amp;lt;/code&amp;gt; only shows running containers by default.&lt;br /&gt;
&lt;br /&gt;
To see all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES&lt;br /&gt;
a1b2c3d4e5f6   hello-world   &amp;amp;quot;/hello&amp;amp;quot;   10 seconds ago   Exited (0) 8 seconds ago              eager_tesla&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CONTAINER ID&amp;#039;&amp;#039;&amp;#039;: Short hex identifier (first 12 chars of full 64-char ID)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IMAGE&amp;#039;&amp;#039;&amp;#039;: Which image this container was created from&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;COMMAND&amp;#039;&amp;#039;&amp;#039;: The process that ran inside the container (&amp;lt;code&amp;gt;/hello&amp;lt;/code&amp;gt; executable)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CREATED&amp;#039;&amp;#039;&amp;#039;: When the container was created&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;STATUS&amp;#039;&amp;#039;&amp;#039;: Current state—&amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt; means process exited with code 0 (success)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PORTS&amp;#039;&amp;#039;&amp;#039;: Port mappings (none for hello-world)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NAMES&amp;#039;&amp;#039;&amp;#039;: Random name if you don&amp;#039;t specify one (Docker generates names like &amp;amp;quot;eager_tesla&amp;amp;quot;, &amp;amp;quot;hopeful_darwin&amp;amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get detailed information about the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect eager_tesla  # Use your actual container name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This outputs a large JSON document with all container metadata. Let&amp;#039;s extract specific fields:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Just the State section&lt;br /&gt;
docker inspect eager_tesla --format=&amp;#039;{{json .State}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (formatted):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;Status&amp;quot;: &amp;quot;exited&amp;quot;,&lt;br /&gt;
    &amp;quot;Running&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Paused&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Restarting&amp;quot;: false,&lt;br /&gt;
    &amp;quot;OOMKilled&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Dead&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Pid&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;ExitCode&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;StartedAt&amp;quot;: &amp;quot;2024-12-12T10:35:00Z&amp;quot;,&lt;br /&gt;
    &amp;quot;FinishedAt&amp;quot;: &amp;quot;2024-12-12T10:35:01Z&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container ran for about 1 second (started at :00, finished at :01)&lt;br /&gt;
* Exit code 0 (successful completion)&lt;br /&gt;
* PID is 0 (process has terminated; while running it had a real PID)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: View container logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even though the container exited, Docker saved its output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows the hello-world message again. This demonstrates that Docker captures stdout/stderr from containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Stopped containers still consume disk space (their writable layer persists). Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify removal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container should be gone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-namespaces-connect-to-lab-7&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Namespaces (Connect to Lab 7!) ====&lt;br /&gt;
&lt;br /&gt;
Now let&amp;#039;s run a longer-lived container and examine its namespace isolation—this directly connects to your hands-on work with network namespaces in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run a persistent container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode—run in background&lt;br /&gt;
* &amp;lt;code&amp;gt;--name inspector&amp;lt;/code&amp;gt;: Give it a memorable name instead of random name&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;nginx:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/nginx&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for nginx:latest&lt;br /&gt;
b7f9a8e6c4d3a1b2c5e8f9d2a7c4b6e8f3d9a2c1b4e7f8d3a5c2b1&amp;lt;/pre&amp;gt;&lt;br /&gt;
The long hex string is the full 64-character container ID. Docker returns this after creating the container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify the container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND                  CREATED          STATUS          PORTS     NAMES&lt;br /&gt;
b7f9a8e6c4d3   nginx   &amp;amp;quot;/docker-entrypoint.…&amp;amp;quot;   10 seconds ago   Up 8 seconds    80/tcp    inspector&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container is running nginx web server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Get the container&amp;#039;s PID on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Remember: containers are just processes. Let&amp;#039;s find the PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; A number like &amp;lt;code&amp;gt;12345&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the process ID on the &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039; system. Let&amp;#039;s verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep 12345&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see nginx processes! The container is just a process with a fancy namespace wrapper.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Examine the container&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial step connecting to Lab 7. Replace 12345 with your actual PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/12345/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 0&lt;br /&gt;
dr-x--x--x 2 root root 0 Dec 12 10:40 .&lt;br /&gt;
dr-xr-xr-x 9 root root 0 Dec 12 10:40 ..&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026533671]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 ipc -&amp;amp;gt; &amp;#039;ipc:[4026533669]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 mnt -&amp;amp;gt; &amp;#039;mnt:[4026533667]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid_for_children -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 uts -&amp;amp;gt; &amp;#039;uts:[4026533668]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis of namespace inodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Note the inode numbers (the numbers in brackets). Each represents a namespace instance.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Compare with host&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 youruser youruser 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical observation:&amp;#039;&amp;#039;&amp;#039; The container&amp;#039;s network namespace inode (&amp;lt;code&amp;gt;4026533672&amp;lt;/code&amp;gt;) is &amp;#039;&amp;#039;&amp;#039;different&amp;#039;&amp;#039;&amp;#039; from the host&amp;#039;s (&amp;lt;code&amp;gt;4026531992&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Compare two containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Start a second container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector2 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Get its PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector2 --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check its network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/NEW_PID/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; A different inode number from both the host and &amp;lt;code&amp;gt;inspector&amp;lt;/code&amp;gt; container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion:&amp;#039;&amp;#039;&amp;#039; Each container has its own isolated network namespace, just like the red and blue namespaces you created manually in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Examine other namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s check PID namespace isolation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host PID namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/pid&lt;br /&gt;
&lt;br /&gt;
# Container PID namespace  &lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/pid&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Different inodes = isolated process trees!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: User namespace (often shared)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host user namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/user&lt;br /&gt;
&lt;br /&gt;
# Container user namespace&lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/user&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Often the &amp;#039;&amp;#039;&amp;#039;same&amp;#039;&amp;#039;&amp;#039; inode number.&lt;br /&gt;
&lt;br /&gt;
Many Docker configurations share the host&amp;#039;s user namespace for simplicity. This means UID 0 in the container is UID 0 on the host (less secure, but more compatible).&lt;br /&gt;
&lt;br /&gt;
For enhanced security, Docker can be configured to use separate user namespaces (rootless Docker), but that&amp;#039;s beyond this lab&amp;#039;s scope.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Enter the container&amp;#039;s namespace with nsenter&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can actually enter a container&amp;#039;s namespaces using the &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; command (this is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --target CONTAINER_PID --net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This executes &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside the container&amp;#039;s network namespace, showing the container&amp;#039;s view of network interfaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop inspector inspector2&lt;br /&gt;
docker rm inspector inspector2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable B ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the full hello message)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker ps -a&amp;lt;/code&amp;gt; showing the exited hello-world container with its random name&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/code&amp;gt; (showing the PID)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo ls -la /proc/PID/ns/&amp;lt;/code&amp;gt; for the inspector container (showing all namespaces)&lt;br /&gt;
# Side-by-side comparison:&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/code&amp;gt; (host&amp;#039;s network namespace inode)&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&amp;lt;/code&amp;gt; (container&amp;#039;s network namespace inode)&lt;br /&gt;
#* Highlight that the inode numbers are different&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-interactive-exploration-with-fedora&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Interactive Exploration with Fedora ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run an interactive container with a different Linux distribution (Fedora instead of Ubuntu), demonstrating mount namespace isolation (different root filesystems), PID namespace isolation (isolated process tree), and UTS namespace isolation (different hostname).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important context:&amp;#039;&amp;#039;&amp;#039; Your host might be running Ubuntu, but the container will run Fedora. Both will be using the same Linux kernel, but they&amp;#039;ll have completely different filesystems and will appear as different &amp;amp;quot;machines&amp;amp;quot; from inside.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run Fedora container interactively&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -it --name fedora-explore fedora bash&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Interactive—keep STDIN open&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Allocate pseudo-TTY (terminal)&lt;br /&gt;
* &amp;lt;code&amp;gt;fedora&amp;lt;/code&amp;gt;: Pull Fedora base image from Docker Hub&lt;br /&gt;
* &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;: Command to run inside container (start bash shell)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker downloads Fedora base image (if not cached)&lt;br /&gt;
# Creates container from image&lt;br /&gt;
# Starts bash inside the container&lt;br /&gt;
# Attaches your terminal to that bash session&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;fedora:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/fedora&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for fedora:latest&lt;br /&gt;
[root@a1b2c3d4e5f6 /]#&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe the prompt change:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Before: &amp;lt;code&amp;gt;user@hostname:~$&amp;lt;/code&amp;gt; (your normal shell)&lt;br /&gt;
* After: &amp;lt;code&amp;gt;[root@a1b2c3d4e5f6 /]#&amp;lt;/code&amp;gt; (inside container)&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the Fedora container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Explore the filesystem (Mount Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the operating system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Fedora Linux&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;39 (Container Image)&amp;amp;quot;&lt;br /&gt;
ID=fedora&lt;br /&gt;
VERSION_ID=39&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Open a &amp;#039;&amp;#039;&amp;#039;new terminal&amp;#039;&amp;#039;&amp;#039; on your host (don&amp;#039;t close the container terminal) and run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output on host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Ubuntu&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;22.04.3 LTS (Jammy Jellyfish)&amp;amp;quot;&lt;br /&gt;
ID=ubuntu&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two different operating systems on the same machine!&amp;#039;&amp;#039;&amp;#039; This is mount namespace isolation—the container has its own root filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Back in the container&amp;#039;&amp;#039;&amp;#039;, explore the filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Fedora&amp;#039;s files, not your host&amp;#039;s Ubuntu files. They&amp;#039;re completely different directory trees.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try to use Ubuntu&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;apt update&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bash: apt: command not found&amp;lt;/pre&amp;gt;&lt;br /&gt;
apt doesn&amp;#039;t exist in Fedora! Fedora uses a different package manager.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try Fedora&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4.18.2&lt;br /&gt;
  Installed: dnf-0:4.18.2-1.fc39.noarch&lt;br /&gt;
  ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
dnf exists because we&amp;#039;re in a Fedora environment.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Install a package:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf install -y nano&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This works! We can install packages just like on a real Fedora system.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 2, you learned about the filesystem hierarchy (&amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt;, etc.). Mount namespaces let the container have completely different contents at these paths. The container&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; is different from the host&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Examine process isolation (PID Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container, check running processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND&lt;br /&gt;
root           1  0.0  0.0  12345  2345 pts/0    Ss   10:45   0:00 bash&lt;br /&gt;
root          67  0.0  0.0  44567  3456 pts/0    R+   10:47   0:00 ps aux&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Only two processes visible!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* PID 1: bash (the container&amp;#039;s init process)&lt;br /&gt;
* PID 67: ps command we just ran&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host&amp;#039;&amp;#039;&amp;#039; (in your other terminal):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; 200+ processes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The container cannot see the host&amp;#039;s processes!&amp;#039;&amp;#039;&amp;#039; This is PID namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the container&amp;#039;s perspective, bash is PID 1&amp;#039;&amp;#039;&amp;#039; (like systemd is PID 1 on a normal Linux system).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the host&amp;#039;s perspective, that same bash process has a different PID&amp;#039;&amp;#039;&amp;#039; (e.g., 12345).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about PIDs and the process hierarchy. PID namespaces create separate process hierarchies—the container has its own process tree starting from PID 1.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Check hostname (UTS Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the container ID (first 12 characters of the full container ID).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;your-hostname.example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Different hostnames! This is UTS namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Examine network configuration (Network Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&lt;br /&gt;
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
       valid_lft forever preferred_lft forever&lt;br /&gt;
       &lt;br /&gt;
17: eth0@if18: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP group default &lt;br /&gt;
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&lt;br /&gt;
       valid_lft forever preferred_lft forever&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Container&amp;#039;s loopback interface (127.0.0.1)&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if18&amp;lt;/code&amp;gt;: Container&amp;#039;s network interface (part of veth pair)&lt;br /&gt;
** &amp;lt;code&amp;gt;@if18&amp;lt;/code&amp;gt;: Indicates this interface is paired with interface index 18 (on the host)&lt;br /&gt;
** IP address: 172.17.0.2 (from docker0 bridge subnet)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show | grep &amp;quot;172.17&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the docker0 bridge has IP 172.17.0.1, and you might see veth interfaces for containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you built manually!&lt;br /&gt;
&lt;br /&gt;
* docker0 bridge ≈ br-lab from Lab 7&lt;br /&gt;
* Container&amp;#039;s eth0 ≈ v-client from Lab 7&lt;br /&gt;
* veth pair connects container to bridge ≈ Lab 7&amp;#039;s veth pair architecture&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test internet connectivity from container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Ping succeeds!&lt;br /&gt;
&lt;br /&gt;
This works because:&lt;br /&gt;
&lt;br /&gt;
# Container has default route to 172.17.0.1 (docker0 bridge)&lt;br /&gt;
# Host has IP forwarding enabled&lt;br /&gt;
# Host has NAT rule (MASQUERADE) for 172.17.0.0/16&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is identical to what you configured in Lab 7!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Try to see host&amp;#039;s processes (will fail)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep systemd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; No systemd processes visible.&lt;br /&gt;
&lt;br /&gt;
You cannot see the host&amp;#039;s processes from inside the container (PID namespace isolation).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Exit the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you exit bash (PID 1 in the container), the container stops automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify the container stopped:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE    COMMAND   CREATED         STATUS                     NAMES&lt;br /&gt;
a1b2c3d4e5f6   fedora   &amp;amp;quot;bash&amp;amp;quot;    5 minutes ago   Exited (0) 10 seconds ago  fedora-explore&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The container still exists (STATUS: Exited), but it&amp;#039;s not running. You can restart it with &amp;lt;code&amp;gt;docker start fedora-explore&amp;lt;/code&amp;gt; if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm fedora-explore&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable C ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Fedora)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Ubuntu or your host OS)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; (showing minimal processes, bash as PID 1)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux | wc -l&amp;lt;/code&amp;gt; (showing many more processes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing container ID)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing host&amp;#039;s hostname)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; (showing eth0 with 172.17.0.x address)&lt;br /&gt;
# Brief explanation (4-5 sentences): What do these differences demonstrate about namespace isolation? How does this relate to what you learned in Labs 2, 3, and 7?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-persistent-storage-with-caddy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Persistent Storage with Caddy ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run Caddy web server with persistent configuration and content using bind mounts, demonstrating that data can survive container removal and be shared between host and container.&lt;br /&gt;
&lt;br /&gt;
Caddy is the web server you&amp;#039;ve been using throughout Lab 9. We&amp;#039;ll run it in a container and configure it using files from the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-basic-caddy-container&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Basic Caddy Container ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create directory structure on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-caddy/{site,data,config}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Directory purposes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;site&amp;lt;/code&amp;gt;: Website content (HTML files)&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data directory (certificates, storage)&lt;br /&gt;
* &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt;: Caddy configuration (Caddyfile)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Create a simple website&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;This is running in a Docker container.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The file you&amp;#039;re viewing is mounted from the host filesystem.&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;Changes made on the host appear instantly in the container!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Create Caddyfile configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/config/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Caddyfile explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;:80&amp;lt;/code&amp;gt;: Listen on port 80 (inside container)&lt;br /&gt;
* &amp;lt;code&amp;gt;root * /usr/share/caddy&amp;lt;/code&amp;gt;: Serve files from this directory&lt;br /&gt;
* &amp;lt;code&amp;gt;file_server&amp;lt;/code&amp;gt;: Enable static file serving&lt;br /&gt;
* &amp;lt;code&amp;gt;log&amp;lt;/code&amp;gt;: Send access logs to stdout (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Run Caddy container with volume mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-persistent \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Breaking down the command:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode (run in background)&lt;br /&gt;
* &amp;lt;code&amp;gt;--name caddy-persistent&amp;lt;/code&amp;gt;: Give container a memorable name&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: Port mapping&lt;br /&gt;
** Host port 8080 → Container port 80&lt;br /&gt;
** This is NAT (DNAT specifically) from Lab 7!&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/site:/usr/share/caddy&amp;lt;/code&amp;gt;: Bind mount&lt;br /&gt;
** Host directory &amp;lt;code&amp;gt;~/lab10-caddy/site&amp;lt;/code&amp;gt; appears at &amp;lt;code&amp;gt;/usr/share/caddy&amp;lt;/code&amp;gt; inside container&lt;br /&gt;
** Bidirectional: changes on either side are visible on both sides&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/data:/data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data storage&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/config:/etc/caddy&amp;lt;/code&amp;gt;: Caddy&amp;#039;s configuration&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: Image to use&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;caddy:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/caddy&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for caddy:latest&lt;br /&gt;
f8e9c7b6d5a4e3b2c1f7d8a9e6b5c4d3a2f1e8d7c6b5a4e3d2c1b9f8&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Verify container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS                  NAMES&lt;br /&gt;
f8e9c7b6d5a4   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    0.0.0.0:8080-&amp;amp;gt;80/tcp   caddy-persistent&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note the PORTS column:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;0.0.0.0:8080-&amp;amp;gt;80/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This means:&lt;br /&gt;
&lt;br /&gt;
* Listen on all host interfaces (&amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Host port 8080 maps to container port 80&lt;br /&gt;
* Protocol: TCP&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test the web server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Your HTML page!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/html&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also open &amp;lt;code&amp;gt;http://localhost:8080&amp;lt;/code&amp;gt; in a web browser on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: View Caddy logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.456,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;using provided configuration&amp;amp;quot;,...}&lt;br /&gt;
{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.789,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;serving initial configuration&amp;amp;quot;}&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Caddy&amp;#039;s startup logs. When you access the website, you&amp;#039;ll see access logs here too.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-mounts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Mounts ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Inspect the container&amp;#039;s mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/site&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/usr/share/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/config&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/etc/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Field explanations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;bind&amp;amp;quot; (bind mount, not Docker-managed volume)&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host filesystem path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Path inside container&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: true (read-write), false would be read-only&lt;br /&gt;
* &amp;lt;code&amp;gt;Propagation&amp;lt;/code&amp;gt;: How mount events propagate (rprivate = don&amp;#039;t propagate to other mounts)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: View from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent df -h | grep caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows mounted filesystems inside the container containing &amp;amp;quot;caddy&amp;amp;quot; in the path.&lt;br /&gt;
&lt;br /&gt;
You can also list the files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent ls -la /usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 12&lt;br /&gt;
drwxr-xr-x 2 root root 4096 Dec 12 10:50 .&lt;br /&gt;
drwxr-xr-x 1 root root 4096 Dec 12 10:50 ..&lt;br /&gt;
-rw-r--r-- 1 root root  687 Dec 12 10:50 index.html&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the same &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; you created on the host!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-test-persistence&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Test Persistence ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Modify the file on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without stopping the container&amp;#039;&amp;#039;&amp;#039;, edit the HTML file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt;&amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;🎉 This was added from the host!&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The container is still running, and changes appear instantly.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Verify the change appears in the container immediately&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; The new section appears!&lt;br /&gt;
&lt;br /&gt;
You didn&amp;#039;t restart the container, yet the content changed. This demonstrates &amp;#039;&amp;#039;&amp;#039;bidirectional bind mount synchronization&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Create a new file from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent /bin/sh -c &amp;quot;echo &amp;#039;&amp;lt;h2&amp;gt;Created from container&amp;lt;/h2&amp;gt;&amp;#039; &amp;gt; /usr/share/caddy/test.html&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify the file appears on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwxr-xr-x 2 youruser youruser 4096 Dec 12 11:00 .&lt;br /&gt;
drwxr-xr-x 5 youruser youruser 4096 Dec 12 10:45 ..&lt;br /&gt;
-rw-r--r-- 1 youruser youruser  687 Dec 12 10:50 index.html&lt;br /&gt;
-rw-r--r-- 1 root     root       34 Dec 12 11:00 test.html  ← New file!&amp;lt;/pre&amp;gt;&lt;br /&gt;
The file exists on the host! Changes flow both directions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The file is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; because the process inside the container runs as root. This is one of the complexities of bind mounts—UID/GID mapping between host and container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Stop and remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-persistent&lt;br /&gt;
docker rm caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Verify files still exist on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All files still there!&lt;br /&gt;
&lt;br /&gt;
The files survive because they&amp;#039;re stored on the host filesystem, not in the container&amp;#039;s ephemeral writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start a NEW container with the same bind mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-new \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Verify persistence&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All your previous content is still there!&lt;br /&gt;
&lt;br /&gt;
The website works immediately because all the content and configuration was stored on the host, not inside the old container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Compare to ephemeral storage&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s demonstrate what happens without volumes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container without volumes&lt;br /&gt;
docker run -d --name caddy-temp caddy&lt;br /&gt;
&lt;br /&gt;
# Create file inside container&lt;br /&gt;
docker exec caddy-temp /bin/sh -c &amp;quot;echo &amp;#039;temporary&amp;#039; &amp;gt; /tmp/temp.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Verify file exists&lt;br /&gt;
docker exec caddy-temp cat /tmp/temp.txt&lt;br /&gt;
# Output: temporary&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop caddy-temp&lt;br /&gt;
docker rm caddy-temp&lt;br /&gt;
&lt;br /&gt;
# Try to access the file in a new container&lt;br /&gt;
docker run -d --name caddy-temp2 caddy&lt;br /&gt;
docker exec caddy-temp2 cat /tmp/temp.txt&lt;br /&gt;
# Output: cat: /tmp/temp.txt: No such file or directory&lt;br /&gt;
&lt;br /&gt;
# The file is GONE because it was in the ephemeral layer&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-new&lt;br /&gt;
docker rm caddy-new&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The files in &amp;lt;code&amp;gt;~/lab10-caddy/&amp;lt;/code&amp;gt; remain intact on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# What Docker essentially does:&lt;br /&gt;
mount --bind ~/lab10-caddy/site /var/lib/docker/overlay2/.../merged/usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deliverable D ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing your custom HTML (initial version)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039;&amp;lt;/code&amp;gt; (formatted with python3 -m json.tool)&lt;br /&gt;
# After modifying &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; on the host, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing the new content (without restarting container)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/code&amp;gt; showing both &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;test.html&amp;lt;/code&amp;gt; created from inside the container&lt;br /&gt;
# After removing the original container and starting &amp;lt;code&amp;gt;caddy-new&amp;lt;/code&amp;gt;, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; demonstrating that content persisted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-multi-container-infrastructure-with-networking-and-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Multi-Container Infrastructure with Networking and Resource Limits ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Build a complete three-tier architecture with:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Purple&amp;#039;&amp;#039;&amp;#039; container acting as reverse proxy (routes requests based on URL path)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Red&amp;#039;&amp;#039;&amp;#039; container serving backend content (memory-limited)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blue&amp;#039;&amp;#039;&amp;#039; container serving backend content (CPU-limited)&lt;br /&gt;
&lt;br /&gt;
This exercise synthesizes everything from Labs 7-9:&lt;br /&gt;
&lt;br /&gt;
* Custom networks (Lab 7 bridges)&lt;br /&gt;
* Container-to-container communication (Lab 7 veth pairs and routing)&lt;br /&gt;
* Reverse proxy with path-based routing (Lab 9 Caddy configuration)&lt;br /&gt;
* Resource limits (cgroups)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-create-a-custom-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Create a Custom Network ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a custom bridge network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.10.0.0/24 labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a new bridge (just like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7) with subnet 10.10.0.0/24.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the network ID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Inspect the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (abbreviated):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;labnet&amp;quot;,&lt;br /&gt;
        &amp;quot;Id&amp;quot;: &amp;quot;a1b2c3d4e5f6...&amp;quot;,&lt;br /&gt;
        &amp;quot;Driver&amp;quot;: &amp;quot;bridge&amp;quot;,&lt;br /&gt;
        &amp;quot;IPAM&amp;quot;: {&lt;br /&gt;
            &amp;quot;Config&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;Subnet&amp;quot;: &amp;quot;10.10.0.0/24&amp;quot;,&lt;br /&gt;
                    &amp;quot;Gateway&amp;quot;: &amp;quot;10.10.0.1&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;Containers&amp;quot;: {},&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key observations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Driver: &amp;amp;quot;bridge&amp;amp;quot;&amp;lt;/code&amp;gt;: Uses bridge networking (Layer 2 switching)&lt;br /&gt;
* &amp;lt;code&amp;gt;Subnet: &amp;amp;quot;10.10.0.0/24&amp;amp;quot;&amp;lt;/code&amp;gt;: Our specified subnet&lt;br /&gt;
* &amp;lt;code&amp;gt;Gateway: &amp;amp;quot;10.10.0.1&amp;amp;quot;&amp;lt;/code&amp;gt;: Docker automatically assigns .1 as gateway&lt;br /&gt;
* &amp;lt;code&amp;gt;Containers: {}&amp;lt;/code&amp;gt;: No containers connected yet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Verify bridge creation on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;5: br-a1b2c3d4e5f6: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default&amp;lt;/pre&amp;gt;&lt;br /&gt;
Docker created a new bridge interface! The name includes the network ID prefix.&lt;br /&gt;
&lt;br /&gt;
You can also use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;brctl show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bridge name          bridge id          STP enabled    interfaces&lt;br /&gt;
br-a1b2c3d4e5f6      8000.0242a1b2c3d4  no&lt;br /&gt;
docker0              8000.0242f7a8b9c0  no&amp;lt;/pre&amp;gt;&lt;br /&gt;
Two bridges:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;: Default Docker bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;br-a1b2c3d4e5f6&amp;lt;/code&amp;gt;: Our custom labnet bridge&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you did with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add br-lab type bridge&lt;br /&gt;
sudo ip link set br-lab up&lt;br /&gt;
sudo ip addr add 10.0.0.1/24 dev br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker automated it!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-create-backend-services-red-and-blue&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Create Backend Services (Red and Blue) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Create content directories&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-multicontainer/{red,blue,purple}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Create Red service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Red Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; Memory capped at 256MB&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is memory-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Create Red Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Create Blue service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Blue Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Blue Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Blue&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.3&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; CPU capped at 0.5 cores&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is CPU-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Create Blue Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Start Red container with memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name red \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.2 \&lt;br /&gt;
  --memory=256m \&lt;br /&gt;
  --memory-swap=256m \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--network labnet&amp;lt;/code&amp;gt;: Connect to our custom network (not default docker0)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.2&amp;lt;/code&amp;gt;: Assign static IP address (just like &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt; in Lab 7!)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory=256m&amp;lt;/code&amp;gt;: cgroup memory limit (hard cap: 256MB RAM)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory-swap=256m&amp;lt;/code&amp;gt;: Total memory+swap limit (setting equal to memory means no swap)&lt;br /&gt;
** If swap was 512m, container could use 256MB RAM + 256MB swap&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/etc/caddy&amp;lt;/code&amp;gt;: Mount Caddyfile&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/usr/share/caddy&amp;lt;/code&amp;gt;: Mount website content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Container ID&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Verify Red is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps --filter &amp;quot;name=red&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Test Red directly&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Since Red has IP 10.10.0.2, let&amp;#039;s verify we can reach it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From host (won&amp;#039;t work directly because we&amp;#039;re not in labnet)&lt;br /&gt;
# But we can use docker exec from another container&lt;br /&gt;
&lt;br /&gt;
# Quick test: use docker exec to reach Red from Red itself&lt;br /&gt;
docker exec red curl -s http://localhost | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Red Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Start Blue container with CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name blue \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.3 \&lt;br /&gt;
  --cpus=0.5 \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--cpus=0.5&amp;lt;/code&amp;gt;: cgroup CPU limit (maximum 50% of one CPU core)&lt;br /&gt;
** If CPU-intensive work is attempted, kernel will throttle it&lt;br /&gt;
* Other flags same as Red&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify both containers are running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS   NAMES&lt;br /&gt;
a1b2c3d4e5f6   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 20 seconds ago   Up 18 seconds   80/tcp  red&lt;br /&gt;
b7c8d9e0f1a2   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    80/tcp  blue&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Test container-to-container communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, ping Blue&lt;br /&gt;
docker exec red ping -c 2 10.10.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, curl Blue&amp;#039;s website&lt;br /&gt;
docker exec red curl -s http://10.10.0.3 | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Containers on the same custom network can communicate directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-create-reverse-proxy-purple&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Create Reverse Proxy (Purple) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Create Purple&amp;#039;s Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial piece—routing based on URL path (from Lab 9!)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/purple/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    # Health check endpoint&lt;br /&gt;
    handle /health {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - OK&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Root path&lt;br /&gt;
    handle / {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /red/* to red container (strip /red prefix)&lt;br /&gt;
    handle_path /red/* {&lt;br /&gt;
        reverse_proxy red:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /blue/* to blue container (strip /blue prefix)&lt;br /&gt;
    handle_path /blue/* {&lt;br /&gt;
        reverse_proxy blue:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Logging&lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key points:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;handle_path /red/*&amp;lt;/code&amp;gt;: Matches any path starting with &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;handle_path&amp;lt;/code&amp;gt; strips the prefix before forwarding&lt;br /&gt;
** Client requests &amp;lt;code&amp;gt;/red/index.html&amp;lt;/code&amp;gt; → Backend receives &amp;lt;code&amp;gt;/index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reverse_proxy red:80&amp;lt;/code&amp;gt;: Forward to container named &amp;amp;quot;red&amp;amp;quot; on port 80&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Using hostname &amp;amp;quot;red&amp;amp;quot;, not IP!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** Docker&amp;#039;s embedded DNS resolves &amp;amp;quot;red&amp;amp;quot; to 10.10.0.2&lt;br /&gt;
* Same for blue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start Purple reverse proxy&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name purple \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.10 \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-multicontainer/purple:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.10&amp;lt;/code&amp;gt;: Purple gets IP 10.10.0.10 (different from Red/Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: &amp;#039;&amp;#039;&amp;#039;Port mapping&amp;#039;&amp;#039;&amp;#039; (host port 8080 → container port 80)&lt;br /&gt;
** This is NAT (DNAT) from Lab 7!&lt;br /&gt;
** Traffic to &amp;lt;code&amp;gt;localhost:8080&amp;lt;/code&amp;gt; on host gets forwarded to &amp;lt;code&amp;gt;10.10.0.10:80&amp;lt;/code&amp;gt; in container&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-testing-the-infrastructure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: Testing the Infrastructure ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Test health check&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/health&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This request:&lt;br /&gt;
&lt;br /&gt;
# Reaches host&amp;#039;s port 8080&lt;br /&gt;
# Docker&amp;#039;s DNAT rule forwards to Purple (10.10.0.10:80)&lt;br /&gt;
# Purple&amp;#039;s Caddy responds directly (no backend needed for /health)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Test root path&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Test routing to Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Red service HTML (with red background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Red Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Your curl → Host port 8080&lt;br /&gt;
# DNAT → Purple (10.10.0.10:80)&lt;br /&gt;
# Purple sees path &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;, strips &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt;, forwards &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;red:80&amp;lt;/code&amp;gt;&lt;br /&gt;
# Purple&amp;#039;s DNS resolves &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; to 10.10.0.2&lt;br /&gt;
# Request goes to Red (10.10.0.2:80)&lt;br /&gt;
# Red&amp;#039;s Caddy serves &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
# Response travels back through Purple to your curl&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 20: Test routing to Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Blue service HTML (with blue background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 21: Test with verbose output to see headers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -v http://localhost:8080/red/ 2&amp;gt;&amp;amp;1 | grep -A10 &amp;quot;&amp;lt; HTTP&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt; HTTP/1.1 200 OK&lt;br /&gt;
&amp;amp;lt; Content-Type: text/html; charset=utf-8&lt;br /&gt;
&amp;amp;lt; Server: Caddy&lt;br /&gt;
&amp;amp;lt; Date: Thu, 12 Dec 2024 11:30:00 GMT&lt;br /&gt;
&amp;amp;lt; Content-Length: 687&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;Server: Caddy&amp;lt;/code&amp;gt; header (from Purple, acting as proxy)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 22: Test in browser&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Open in your web browser:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/red/&amp;lt;/code&amp;gt; → Should see red-styled page&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/blue/&amp;lt;/code&amp;gt; → Should see blue-styled page&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-inspect-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Inspect Resource Limits ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 23: Check Red&amp;#039;s memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 256 MB in bytes (256 × 1024 × 1024 = 268435456).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 24: Check Blue&amp;#039;s CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;500000000&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 0.5 CPU cores in nanocpus (0.5 × 10^9 = 500,000,000).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 25: View cgroup settings from the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get Red&amp;#039;s main process PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;RED_PID=$(docker inspect red --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
echo &amp;quot;Red&amp;#039;s PID: $RED_PID&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
View memory limit in cgroup filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /sys/fs/cgroup/memory/docker/$RED_PID/memory.limit_in_bytes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
View Blue&amp;#039;s CPU quota:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;BLUE_PID=$(docker inspect blue --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/$BLUE_PID/cpu.cfs_quota_us&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;50000&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt;: 100000 (default, 100ms period)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt;: 50000 (50ms out of 100ms = 50% = 0.5 CPUs)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to kernel concepts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These cgroup files in &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; are how the kernel enforces resource limits. Docker configures these files, and the kernel does the actual enforcement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-network-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Network Inspection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 26: Inspect labnet network showing all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet --format=&amp;#039;{{json .Containers}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;a1b2c3d4e5f6...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:02&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.2/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;b7c8d9e0f1a2...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:03&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.3/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;f8e9c7b6d5a4...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;purple&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:0a&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.10/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All three containers are on the labnet network with their assigned IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 27: Test DNS resolution between containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Purple, resolve &amp;quot;red&amp;quot; hostname&lt;br /&gt;
docker exec purple nslookup red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Server:         127.0.0.11&lt;br /&gt;
Address:        127.0.0.11#53&lt;br /&gt;
&lt;br /&gt;
Non-authoritative answer:&lt;br /&gt;
Name:   red&lt;br /&gt;
Address: 10.10.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;127.0.0.11&amp;lt;/code&amp;gt;: Docker&amp;#039;s embedded DNS server (listening inside each container)&lt;br /&gt;
* DNS resolves container name &amp;amp;quot;red&amp;amp;quot; to IP 10.10.0.2&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 28: Test connectivity using hostnames&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple pings Red by hostname&lt;br /&gt;
docker exec purple ping -c 2 red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple curls Blue by hostname&lt;br /&gt;
docker exec purple curl -s http://blue | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-7-architecture-visualization&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 7: Architecture Visualization ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The architecture you built:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                    ┌─────────────────┐&lt;br /&gt;
                    │   Your Host     │&lt;br /&gt;
                    │  (Port 8080)    │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                     Port Mapping (NAT)&lt;br /&gt;
                      8080 → 80&lt;br /&gt;
                             │&lt;br /&gt;
                    ┌────────▼────────┐&lt;br /&gt;
                    │  Purple Proxy   │&lt;br /&gt;
                    │  10.10.0.10:80  │&lt;br /&gt;
                    │  (Caddy)        │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                   Docker Network: labnet&lt;br /&gt;
                   (Bridge: br-a1b2c3d4e5f6)&lt;br /&gt;
                    10.10.0.0/24&lt;br /&gt;
                             │&lt;br /&gt;
                   ┌─────────┴──────────┐&lt;br /&gt;
                   │                    │&lt;br /&gt;
          ┌────────▼────────┐  ┌───────▼─────────┐&lt;br /&gt;
          │   Red Service   │  │  Blue Service   │&lt;br /&gt;
          │   10.10.0.2:80  │  │  10.10.0.3:80   │&lt;br /&gt;
          │   (Caddy)       │  │  (Caddy)        │&lt;br /&gt;
          │  [mem: 256MB]   │  │  [cpu: 0.5]     │&lt;br /&gt;
          └─────────────────┘  └─────────────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request flow for &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Curl → localhost:8080 (your host)&lt;br /&gt;
2. Host&amp;#039;s iptables DNAT rule → 10.10.0.10:80 (Purple)&lt;br /&gt;
3. Purple receives request to /red/&lt;br /&gt;
4. Purple&amp;#039;s Caddy config: handle_path /red/* → reverse_proxy red:80&lt;br /&gt;
5. Purple strips /red prefix → request becomes /&lt;br /&gt;
6. Purple&amp;#039;s DNS resolves &amp;amp;quot;red&amp;amp;quot; → 10.10.0.2&lt;br /&gt;
7. Purple → Red (10.10.0.2:80) GET /&lt;br /&gt;
8. Red&amp;#039;s Caddy serves /usr/share/caddy/index.html&lt;br /&gt;
9. Response: Red → Purple → Host → Curl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Compare to Lab 7 and Lab 9:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 7&amp;#039;&amp;#039;&amp;#039;: You manually created bridges, veth pairs, assigned IPs, configured routes, set up NAT&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 9&amp;#039;&amp;#039;&amp;#039;: You manually configured Caddy reverse proxy with path-based routing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;This exercise&amp;#039;&amp;#039;&amp;#039;: Docker automated all the networking, you just specified what you wanted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-8-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 8: Cleanup ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 29: Stop all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 30: Remove containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 31: Remove the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network rm labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 32: Verify cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&lt;br /&gt;
docker network ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Only default networks (bridge, host, none) should remain.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 33: Verify host bridge removed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The custom bridge (br-a1b2c3d4e5f6) should be gone. Only docker0 remains.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-e&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable E ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker network inspect labnet&amp;lt;/code&amp;gt; showing all three containers with their IPs (before running curl tests)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/health&amp;lt;/code&amp;gt; (health check response)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt; (Red service HTML) with visible red-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/blue/&amp;lt;/code&amp;gt; (Blue service HTML) with visible blue-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/code&amp;gt; showing memory limit in bytes&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/code&amp;gt; showing CPU limit in nanocpus&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker exec purple nslookup red&amp;lt;/code&amp;gt; showing DNS resolution&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-docker-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Docker Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for Docker commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container (create + start)&lt;br /&gt;
docker run [OPTIONS] IMAGE [COMMAND]&lt;br /&gt;
docker run -d nginx                          # Detached (background)&lt;br /&gt;
docker run -it ubuntu bash                   # Interactive with terminal&lt;br /&gt;
docker run --name mycontainer nginx          # Assign name&lt;br /&gt;
&lt;br /&gt;
# List containers&lt;br /&gt;
docker ps                                    # Running only&lt;br /&gt;
docker ps -a                                 # All (including stopped)&lt;br /&gt;
docker ps -q                                 # Show only IDs (quiet)&lt;br /&gt;
&lt;br /&gt;
# Start stopped container&lt;br /&gt;
docker start CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Stop running container&lt;br /&gt;
docker stop CONTAINER                        # Graceful (SIGTERM)&lt;br /&gt;
docker kill CONTAINER                        # Forceful (SIGKILL)&lt;br /&gt;
&lt;br /&gt;
# Restart container&lt;br /&gt;
docker restart CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove container&lt;br /&gt;
docker rm CONTAINER                          # Must be stopped first&lt;br /&gt;
docker rm -f CONTAINER                       # Force remove (stop + remove)&lt;br /&gt;
&lt;br /&gt;
# Execute command in running container&lt;br /&gt;
docker exec CONTAINER COMMAND&lt;br /&gt;
docker exec -it CONTAINER bash               # Interactive shell&lt;br /&gt;
&lt;br /&gt;
# View container logs&lt;br /&gt;
docker logs CONTAINER&lt;br /&gt;
docker logs -f CONTAINER                     # Follow (tail -f style)&lt;br /&gt;
&lt;br /&gt;
# Inspect container (detailed JSON info)&lt;br /&gt;
docker inspect CONTAINER&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.Status}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;image-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Image Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List images&lt;br /&gt;
docker images&lt;br /&gt;
docker image ls&lt;br /&gt;
&lt;br /&gt;
# Pull image from registry&lt;br /&gt;
docker pull IMAGE[:TAG]&lt;br /&gt;
docker pull nginx                            # Latest tag (default)&lt;br /&gt;
docker pull nginx:1.21                       # Specific tag&lt;br /&gt;
&lt;br /&gt;
# Remove image&lt;br /&gt;
docker rmi IMAGE&lt;br /&gt;
docker image rm IMAGE&lt;br /&gt;
&lt;br /&gt;
# View image layers&lt;br /&gt;
docker history IMAGE&lt;br /&gt;
&lt;br /&gt;
# Remove unused images&lt;br /&gt;
docker image prune                           # Dangling images&lt;br /&gt;
docker image prune -a                        # All unused images&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List networks&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Create network&lt;br /&gt;
docker network create NETWORK&lt;br /&gt;
docker network create --subnet=10.20.0.0/24 mynet&lt;br /&gt;
&lt;br /&gt;
# Inspect network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Connect container to network&lt;br /&gt;
docker network connect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Disconnect container from network&lt;br /&gt;
docker network disconnect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove network&lt;br /&gt;
docker network rm NETWORK&lt;br /&gt;
&lt;br /&gt;
# Remove unused networks&lt;br /&gt;
docker network prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;volume-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volume Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Create volume&lt;br /&gt;
docker volume create VOLUME&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove volume&lt;br /&gt;
docker volume rm VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# CPU limits&lt;br /&gt;
--cpus=1.5                                   # Limit to 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512                             # Relative priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1                            # Pin to specific cores&lt;br /&gt;
&lt;br /&gt;
# Memory limits&lt;br /&gt;
--memory=512m                                # Hard limit: 512 MB RAM&lt;br /&gt;
--memory=2g                                  # Hard limit: 2 GB RAM&lt;br /&gt;
--memory-swap=1g                             # Total memory+swap&lt;br /&gt;
--memory-reservation=256m                    # Soft limit&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb             # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb            # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100                             # Max 100 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;inspection-and-debugging&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Inspection and Debugging ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View resource usage stats&lt;br /&gt;
docker stats&lt;br /&gt;
docker stats CONTAINER                       # Specific container&lt;br /&gt;
&lt;br /&gt;
# View processes in container&lt;br /&gt;
docker top CONTAINER&lt;br /&gt;
&lt;br /&gt;
# View port mappings&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Copy files between host and container&lt;br /&gt;
docker cp CONTAINER:/path/to/file ./file    # Container → Host&lt;br /&gt;
docker cp ./file CONTAINER:/path/to/file    # Host → Container&lt;br /&gt;
&lt;br /&gt;
# Stream events from Docker daemon&lt;br /&gt;
docker events&lt;br /&gt;
&lt;br /&gt;
# Show disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# System-wide cleanup&lt;br /&gt;
docker system prune                          # Remove unused objects&lt;br /&gt;
docker system prune -a                       # Remove all unused objects&lt;br /&gt;
docker system prune -a --volumes            # Include volumes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;installation-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Installation Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt; fails after installation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check if Docker daemon is running&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# If not running, start it&lt;br /&gt;
sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;Cannot connect to the Docker daemon&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Docker daemon not running or permission issue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check daemon status&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# Check if your user is in docker group&lt;br /&gt;
groups | grep docker&lt;br /&gt;
&lt;br /&gt;
# If not, add user and log out/in&lt;br /&gt;
sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;permission-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Permission Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;permission denied&amp;amp;quot; when running docker commands&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Option 1: Add user to docker group (permanent)&lt;br /&gt;
sudo usermod -aG docker $USER&lt;br /&gt;
# Then log out and log back in&lt;br /&gt;
&lt;br /&gt;
# Option 2: Use sudo (temporary)&lt;br /&gt;
sudo docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Files created by container are owned by root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Container runs as root (UID 0) by default&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container as specific user&lt;br /&gt;
docker run --user $(id -u):$(id -g) IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use user namespaces (advanced)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container can&amp;#039;t access internet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test from container&lt;br /&gt;
docker exec CONTAINER ping -c 2 8.8.8.8&lt;br /&gt;
&lt;br /&gt;
# Check Docker&amp;#039;s NAT rules&lt;br /&gt;
sudo iptables -t nat -L -n | grep docker&lt;br /&gt;
&lt;br /&gt;
# Check IP forwarding&lt;br /&gt;
sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enable IP forwarding if disabled&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Restart Docker daemon&lt;br /&gt;
sudo systemctl restart docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Containers on custom network can&amp;#039;t communicate&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check network exists&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Check containers are on same network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Test connectivity&lt;br /&gt;
docker exec CONTAINER1 ping CONTAINER2_IP&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Ensure both containers on same network&lt;br /&gt;
docker network connect NETWORK CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Port mapping not working (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check port is actually mapped&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Check if host port is already in use&lt;br /&gt;
sudo ss -tulpn | grep :8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# If port in use, use different host port&lt;br /&gt;
docker run -p 8081:80 nginx&lt;br /&gt;
&lt;br /&gt;
# Or stop the conflicting process&lt;br /&gt;
sudo fuser -k 8080/tcp&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;storage-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Storage Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;No space left on device&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check Docker disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# Check host disk space&lt;br /&gt;
df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Remove unused objects&lt;br /&gt;
docker system prune -a&lt;br /&gt;
&lt;br /&gt;
# Remove specific old images&lt;br /&gt;
docker images&lt;br /&gt;
docker rmi IMAGE_ID&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Changes in bind mount not visible in container&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Path doesn&amp;#039;t exist or wrong path specified&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Verify path exists on host&lt;br /&gt;
ls -la /path/to/host/directory&lt;br /&gt;
&lt;br /&gt;
# Use absolute paths&lt;br /&gt;
docker run -v /absolute/path:/container/path IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use $PWD for current directory&lt;br /&gt;
docker run -v $PWD/relative/path:/container/path IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-limit-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Limit Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container killed unexpectedly&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check container exit code&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.ExitCode}}&amp;#039;&lt;br /&gt;
# Exit code 137 = killed (often OOM)&lt;br /&gt;
&lt;br /&gt;
# Check logs&lt;br /&gt;
docker logs CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Increase memory limit&lt;br /&gt;
docker run --memory=1g IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or run without memory limit (default)&lt;br /&gt;
docker run IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container using too much CPU&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Limit CPU usage&lt;br /&gt;
docker run --cpus=0.5 IMAGE&lt;br /&gt;
&lt;br /&gt;
# Check what&amp;#039;s consuming CPU&lt;br /&gt;
docker exec CONTAINER top&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;next-steps-dockerfile-and-docker-compose&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Next Steps: Dockerfile and Docker Compose ==&lt;br /&gt;
&lt;br /&gt;
Congratulations! You&amp;#039;ve mastered the fundamental OS concepts behind containers:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; provide isolation (network, PID, mount, UTS, IPC, user, cgroup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039; enforce resource limits (CPU, memory, I/O)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;OverlayFS&amp;#039;&amp;#039;&amp;#039; provides efficient layered filesystems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bridges and veth pairs&amp;#039;&amp;#039;&amp;#039; connect containers (same as Lab 7!)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volumes&amp;#039;&amp;#039;&amp;#039; persist data beyond container lifecycles&lt;br /&gt;
&lt;br /&gt;
However, you&amp;#039;ve been using &amp;#039;&amp;#039;&amp;#039;pre-built images&amp;#039;&amp;#039;&amp;#039; and running containers with long &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands. In production environments, you need:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom images&amp;#039;&amp;#039;&amp;#039; tailored to your applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducible builds&amp;#039;&amp;#039;&amp;#039; that can be version-controlled&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-container orchestration&amp;#039;&amp;#039;&amp;#039; with coordinated startup and networking&lt;br /&gt;
&lt;br /&gt;
This is where &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039; come in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dockerfile-building-custom-images&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Dockerfile: Building Custom Images ===&lt;br /&gt;
&lt;br /&gt;
Instead of starting from a base image and manually installing packages, you define your application&amp;#039;s environment in a &amp;lt;code&amp;gt;Dockerfile&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dockerfile&amp;quot;&amp;gt;# Start from Ubuntu base image&lt;br /&gt;
FROM ubuntu:22.04&lt;br /&gt;
&lt;br /&gt;
# Install dependencies&lt;br /&gt;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;br /&gt;
    python3 \&lt;br /&gt;
    python3-pip \&lt;br /&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br /&gt;
&lt;br /&gt;
# Copy application code&lt;br /&gt;
COPY app.py /app/&lt;br /&gt;
COPY requirements.txt /app/&lt;br /&gt;
&lt;br /&gt;
# Install Python dependencies&lt;br /&gt;
WORKDIR /app&lt;br /&gt;
RUN pip3 install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
# Expose port&lt;br /&gt;
EXPOSE 8000&lt;br /&gt;
&lt;br /&gt;
# Define startup command&lt;br /&gt;
CMD [&amp;quot;python3&amp;quot;, &amp;quot;app.py&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Build the image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker build -t myapp:1.0 .&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Run containers from your custom image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8000:8000 myapp:1.0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Same Dockerfile always builds identical image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Version control&amp;#039;&amp;#039;&amp;#039;: Dockerfile lives in git alongside code&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Documentation&amp;#039;&amp;#039;&amp;#039;: Dockerfile explicitly documents dependencies and setup&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automation&amp;#039;&amp;#039;&amp;#039;: CI/CD pipelines can build images automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Dockerfile instructions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;FROM&amp;lt;/code&amp;gt;: Base image to start from&lt;br /&gt;
* &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt;: Execute command during build (install packages, etc.)&lt;br /&gt;
* &amp;lt;code&amp;gt;COPY&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;ADD&amp;lt;/code&amp;gt;: Copy files from host into image&lt;br /&gt;
* &amp;lt;code&amp;gt;WORKDIR&amp;lt;/code&amp;gt;: Set working directory&lt;br /&gt;
* &amp;lt;code&amp;gt;ENV&amp;lt;/code&amp;gt;: Set environment variables&lt;br /&gt;
* &amp;lt;code&amp;gt;EXPOSE&amp;lt;/code&amp;gt;: Document which ports the application uses&lt;br /&gt;
* &amp;lt;code&amp;gt;CMD&amp;lt;/code&amp;gt;: Default command to run when container starts&lt;br /&gt;
* &amp;lt;code&amp;gt;ENTRYPOINT&amp;lt;/code&amp;gt;: Configure container as executable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Use specific image tags (not &amp;lt;code&amp;gt;latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Minimize layers (combine &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt; commands)&lt;br /&gt;
* Leverage build cache (order instructions from least to most frequently changing)&lt;br /&gt;
* Use &amp;lt;code&amp;gt;.dockerignore&amp;lt;/code&amp;gt; (like &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; for Docker builds)&lt;br /&gt;
* Don&amp;#039;t run as root (use &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; instruction)&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-compose-multi-container-orchestration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Compose: Multi-Container Orchestration ===&lt;br /&gt;
&lt;br /&gt;
Instead of running multiple &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands with complex options, define your entire infrastructure in a &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;version: &amp;#039;3.8&amp;#039;&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
  # Purple reverse proxy&lt;br /&gt;
  purple:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;8080:80&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./purple:/etc/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.10&lt;br /&gt;
    depends_on:&lt;br /&gt;
      - red&lt;br /&gt;
      - blue&lt;br /&gt;
&lt;br /&gt;
  # Red backend&lt;br /&gt;
  red:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./red:/etc/caddy&lt;br /&gt;
      - ./red:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.2&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          memory: 256M&lt;br /&gt;
&lt;br /&gt;
  # Blue backend&lt;br /&gt;
  blue:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./blue:/etc/caddy&lt;br /&gt;
      - ./blue:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.3&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          cpus: &amp;#039;0.5&amp;#039;&lt;br /&gt;
&lt;br /&gt;
networks:&lt;br /&gt;
  labnet:&lt;br /&gt;
    driver: bridge&lt;br /&gt;
    ipam:&lt;br /&gt;
      config:&lt;br /&gt;
        - subnet: 10.10.0.0/24&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Start entire infrastructure:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or in detached mode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up -d&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Stop everything:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose down&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;View logs:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose logs -f&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Declarative&amp;#039;&amp;#039;&amp;#039;: Describe desired state, not imperative commands&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Single source of truth&amp;#039;&amp;#039;&amp;#039;: One file defines entire application&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Easy to share&amp;#039;&amp;#039;&amp;#039;: Colleagues can run &amp;lt;code&amp;gt;docker compose up&amp;lt;/code&amp;gt; and get identical environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Development/production parity&amp;#039;&amp;#039;&amp;#039;: Same compose file works everywhere&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automatic networking&amp;#039;&amp;#039;&amp;#039;: Compose creates network and DNS automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is the production-ready way to deploy multi-container applications.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Further learning:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039;: Learn advanced instructions, multi-stage builds, build arguments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039;: Learn service dependencies, health checks, scaling&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Swarm&amp;#039;&amp;#039;&amp;#039;: Docker&amp;#039;s built-in orchestration for multi-host deployments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kubernetes&amp;#039;&amp;#039;&amp;#039;: Industry-standard container orchestration platform&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Container registries&amp;#039;&amp;#039;&amp;#039;: Push images to Docker Hub, GitHub Container Registry, or private registries&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;: Image scanning, rootless Docker, seccomp profiles, AppArmor&lt;br /&gt;
&lt;br /&gt;
These topics build on the solid foundation you&amp;#039;ve established in this lab. You now understand what containers actually are at the OS level—the rest is learning tools that make containers easier to build and deploy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all deliverables from Exercises A through E, organized with clear section headers matching the exercise labels.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Required deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable A&amp;#039;&amp;#039;&amp;#039;: Installation verification (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable B&amp;#039;&amp;#039;&amp;#039;: Hello World and namespace inspection (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable C&amp;#039;&amp;#039;&amp;#039;: Fedora exploration (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable D&amp;#039;&amp;#039;&amp;#039;: Persistent storage (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable E&amp;#039;&amp;#039;&amp;#039;: Multi-container infrastructure (screenshots)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced containerization using Docker, demonstrating how Linux kernel features (namespaces, cgroups, OverlayFS) are composed to create isolated application environments. You&amp;#039;ve seen that Docker is not magic—it&amp;#039;s sophisticated automation of kernel primitives you already understand from previous labs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Internals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Deep dive into runc (the OCI runtime that Docker uses)&lt;br /&gt;
* Understanding containerd (container runtime layer)&lt;br /&gt;
* Linux capabilities and security contexts&lt;br /&gt;
* AppArmor and SELinux profiles for containers&lt;br /&gt;
* User namespaces and rootless containers&lt;br /&gt;
* Seccomp profiles for system call filtering&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dockerfile Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
* Build cache optimization strategies&lt;br /&gt;
* Layer ordering for efficient rebuilds&lt;br /&gt;
* .dockerignore for excluding files&lt;br /&gt;
* Security scanning with Trivy or Clair&lt;br /&gt;
* Distroless and minimal base images&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker Compose Advanced:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Service dependencies with health checks&lt;br /&gt;
* Environment-specific overrides&lt;br /&gt;
* Secrets management&lt;br /&gt;
* Multiple compose files (base + overrides)&lt;br /&gt;
* Named volumes vs bind mounts&lt;br /&gt;
* Integration testing with compose&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Orchestration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Kubernetes architecture and concepts&lt;br /&gt;
* kubectl basics&lt;br /&gt;
* Pods, Services, Deployments, ConfigMaps&lt;br /&gt;
* Kubernetes networking (CNI plugins)&lt;br /&gt;
* Helm charts for package management&lt;br /&gt;
* Service mesh (Istio, Linkerd)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Networking Deep Dive:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Bridge vs host vs macvlan networking&lt;br /&gt;
* Overlay networks for multi-host communication&lt;br /&gt;
* Network plugins and CNI specification&lt;br /&gt;
* Load balancing strategies&lt;br /&gt;
* Service discovery patterns&lt;br /&gt;
* Network policies and segmentation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container escape techniques and mitigations&lt;br /&gt;
* Image vulnerability scanning&lt;br /&gt;
* Runtime security monitoring (Falco)&lt;br /&gt;
* Least privilege principles&lt;br /&gt;
* Supply chain security&lt;br /&gt;
* Harbor for secure image registry&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container resource tuning&lt;br /&gt;
* Storage drivers (overlay2, btrfs, zfs)&lt;br /&gt;
* Network performance optimization&lt;br /&gt;
* Monitoring with Prometheus and Grafana&lt;br /&gt;
* Distributed tracing with Jaeger&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Production Operations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Logging strategies (centralized logging)&lt;br /&gt;
* Health checks and readiness probes&lt;br /&gt;
* Rolling updates and blue-green deployments&lt;br /&gt;
* Backup and disaster recovery&lt;br /&gt;
* CI/CD integration (Jenkins, GitLab CI)&lt;br /&gt;
* Container registries (private, public)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 namespaces                             # Linux namespaces overview&lt;br /&gt;
man 7 cgroups                                # Control groups overview&lt;br /&gt;
man 2 unshare                                # Create namespaces&lt;br /&gt;
man 2 setns                                  # Enter existing namespace&lt;br /&gt;
man 8 nsenter                                # Run program in namespace&lt;br /&gt;
man 1 docker                                 # Docker CLI reference&lt;br /&gt;
man 1 docker-run                             # docker run reference&lt;br /&gt;
man 1 docker-compose                         # Docker Compose reference&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Official Documentation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docs.docker.com/ Docker Documentation] - Comprehensive official docs&lt;br /&gt;
* [https://hub.docker.com/ Docker Hub] - Public image registry&lt;br /&gt;
* [https://opencontainers.org/ OCI Specification] - Open Container Initiative standards&lt;br /&gt;
* [https://containerd.io/docs/ containerd Documentation] - Container runtime&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Tutorials and Guides:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docker-curriculum.com/ Docker Curriculum] - Beginner-friendly tutorial&lt;br /&gt;
* [https://labs.play-with-docker.com/ Play with Docker] - Free interactive playground&lt;br /&gt;
* [https://kubernetes.io/docs/ Kubernetes Documentation] - K8s official docs&lt;br /&gt;
* [https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ Dockerfile Best Practices]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deep Dives:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://lwn.net/Articles/531114/ Understanding Namespaces (LWN)] - Series on Linux namespaces&lt;br /&gt;
* [https://jvns.ca/blog/2016/10/10/what-even-is-a-container/ How Containers Work] - Excellent blog post by Julia Evans&lt;br /&gt;
* [https://sysdig.com/blog/dockerfile-best-practices/ Container Security Best Practices] - Security-focused guide&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Community:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://forums.docker.com/ Docker Forums] - Community support&lt;br /&gt;
* [https://stackoverflow.com/questions/tagged/docker Stack Overflow - Docker Tag]&lt;br /&gt;
* [https://reddit.com/r/docker Reddit r/docker]&lt;br /&gt;
* [https://slack.cncf.io/ CNCF Slack] - Cloud Native Computing Foundation community&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Practice Environments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://www.katacoda.com/ Katacoda] - Interactive Docker and Kubernetes scenarios&lt;br /&gt;
* [https://labs.play-with-k8s.com/ Play with Kubernetes] - K8s playground&lt;br /&gt;
* [https://killercoda.com/ KillerCoda] - Interactive learning scenarios&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8195</id>
		<title>OS Lab 10 - Containers and Docker</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8195"/>
		<updated>2025-12-16T16:05:50Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Exercise A: Installing Docker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how containers use Linux kernel namespaces (PID, mount, network, UTS, IPC, user, cgroup) to provide process isolation without requiring separate operating systems or hypervisors.&lt;br /&gt;
* Differentiate between container images (immutable filesystem templates) and running containers (ephemeral process instances in isolated namespaces).&lt;br /&gt;
* Understand how OverlayFS provides efficient layered filesystems that allow multiple containers to share common base layers while maintaining separate writable layers.&lt;br /&gt;
* Apply cgroup resource limits to constrain container CPU, memory, and I/O usage, preventing resource monopolization.&lt;br /&gt;
* Configure Docker networking using custom bridges, port mapping (NAT), and container-to-container communication with automatic DNS resolution.&lt;br /&gt;
* Use volumes and bind mounts to persist data beyond container lifecycles and share data between containers and the host.&lt;br /&gt;
* Build multi-container applications with coordinated networking and resource management, demonstrating modern microservices architecture patterns.&lt;br /&gt;
* Connect container concepts to previous labs: relate network namespaces to Lab 7, mount namespaces to Lab 2, PID namespaces to Lab 3, and Docker networking to Labs 7-9.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;from-manual-isolation-to-automated-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== From Manual Isolation to Automated Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you manually constructed network isolation using Linux kernel primitives. You created virtual network interfaces with &amp;lt;code&amp;gt;ip link add type veth&amp;lt;/code&amp;gt;, configured bridges with &amp;lt;code&amp;gt;ip link add type bridge&amp;lt;/code&amp;gt;, established isolated network stacks with &amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, and set up routing and NAT rules. Through this hands-on work, you gained deep insight into how the Linux kernel provides network isolation at the namespace level.&lt;br /&gt;
&lt;br /&gt;
Every time you executed &amp;lt;code&amp;gt;sudo ip netns exec red ping 10.0.0.3&amp;lt;/code&amp;gt;, you were demonstrating a fundamental concept: the kernel can create completely isolated environments where processes see only a subset of system resources. The &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace had its own network interfaces, its own routing table, and its own firewall rules—completely invisible to processes running in other namespaces or on the host.&lt;br /&gt;
&lt;br /&gt;
This isolation is powerful, but network namespaces are just one of seven namespace types that the Linux kernel provides. To fully isolate an application and create what we call a &amp;amp;quot;container,&amp;amp;quot; you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;): Isolated network stack—you&amp;#039;ve mastered this in Labs 7-9&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PID namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;): Isolated process tree—each namespace has its own PID 1&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mount namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;): Isolated filesystem view—different root directory from the host&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UTS namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;): Isolated hostname—each container can have its own hostname&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IPC namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;): Isolated inter-process communication—shared memory, semaphores, message queues&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;): Isolated UIDs/GIDs—security boundary for privilege separation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Cgroup namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;): Isolated view of control group hierarchy&lt;br /&gt;
&lt;br /&gt;
Additionally, you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Control groups (cgroups)&amp;#039;&amp;#039;&amp;#039; to limit CPU, memory, and I/O resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy-on-write filesystems&amp;#039;&amp;#039;&amp;#039; (OverlayFS) for efficient storage&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Image management&amp;#039;&amp;#039;&amp;#039; for distributing application packages&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Orchestration&amp;#039;&amp;#039;&amp;#039; for managing the lifecycle of multiple isolated environments&lt;br /&gt;
&lt;br /&gt;
Manually setting up all of these components for every application would require hundreds of commands and deep kernel knowledge. This is the problem that containerization technology solves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-are-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What are Containers? ===&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;container&amp;#039;&amp;#039;&amp;#039; is an isolated process (or process tree) that uses Linux kernel features—namespaces, cgroups, and layered filesystems—to provide the illusion of running in a separate system. Containers package an application with its dependencies, libraries, and configuration into a single unit that can run consistently across different environments.&lt;br /&gt;
&lt;br /&gt;
Containers are not a specific product or tool—they are a pattern for using kernel isolation features. Multiple container runtimes exist, each implementing this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Runtimes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039;: The most widely adopted container platform, providing a complete ecosystem (daemon, CLI, image format, registry)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Podman&amp;#039;&amp;#039;&amp;#039;: Daemonless container engine, compatible with Docker images and commands, can run rootless&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Industry-standard container runtime, used by Kubernetes and Docker (as of Docker 1.11+)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CRI-O&amp;#039;&amp;#039;&amp;#039;: Lightweight container runtime built for Kubernetes, implementing the Container Runtime Interface (CRI)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LXC/LXD&amp;#039;&amp;#039;&amp;#039;: System containers that more closely resemble traditional VMs, providing full init systems&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What Container Runtimes Provide:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Namespace Management&amp;#039;&amp;#039;&amp;#039;: Automatically create and configure all necessary namespace types&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Filesystem Layers&amp;#039;&amp;#039;&amp;#039;: Use OverlayFS or similar copy-on-write filesystems for efficient storage&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Configuration&amp;#039;&amp;#039;&amp;#039;: Set up bridges, veth pairs, and NAT rules automatically&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Resource Control&amp;#039;&amp;#039;&amp;#039;: Configure cgroups to enforce CPU and memory limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Image Distribution&amp;#039;&amp;#039;&amp;#039;: Provide standard formats (OCI) for packaging and distributing applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Lifecycle Management&amp;#039;&amp;#039;&amp;#039;: Offer commands to create, start, stop, and remove containers&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-as-a-container-runtime&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker as a Container Runtime ===&lt;br /&gt;
&lt;br /&gt;
In this lab, we use &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039; because it is the most widely deployed and well-documented container runtime. Docker&amp;#039;s architecture consists of:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Engine (dockerd)&amp;#039;&amp;#039;&amp;#039;: Daemon that manages containers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker CLI (docker)&amp;#039;&amp;#039;&amp;#039;: Command-line interface for interacting with the daemon&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Lower-level runtime that Docker uses internally&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;runc&amp;#039;&amp;#039;&amp;#039;: OCI-compliant runtime that actually creates and runs containers&lt;br /&gt;
&lt;br /&gt;
When you execute &amp;lt;code&amp;gt;docker run nginx&amp;lt;/code&amp;gt;, Docker performs approximately 50-100 system calls to configure namespaces, mount filesystems, set up networking, and start the process—all operations you could do manually but would require significant time and expertise.&lt;br /&gt;
&lt;br /&gt;
The concepts you learn with Docker apply to all container runtimes, as they all use the same underlying kernel features. The commands may differ (e.g., &amp;lt;code&amp;gt;podman run&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt;), but the fundamental mechanisms remain the same.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shift-in-software-deployment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Shift in Software Deployment ===&lt;br /&gt;
&lt;br /&gt;
Containers represent a fundamental paradigm shift in how we think about software deployment and infrastructure management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Traditional Deployment Model (Pre-Container Era):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Provision a server (physical or virtual machine)&lt;br /&gt;
2. Install operating system (Ubuntu, RHEL, etc.)&lt;br /&gt;
3. Install runtime dependencies (Python 3.9, Node.js 16, specific library versions)&lt;br /&gt;
4. Configure environment variables, users, permissions&lt;br /&gt;
5. Deploy application code&lt;br /&gt;
6. Configure monitoring, logging, security&lt;br /&gt;
7. Hope everything works the same as on your development machine&lt;br /&gt;
8. Troubleshoot when it doesn&amp;#039;t (&amp;amp;quot;works on my machine&amp;amp;quot; problem)&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model suffers from several critical issues:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dependency Hell&amp;#039;&amp;#039;&amp;#039;: Different applications require different, potentially conflicting library versions&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration Drift&amp;#039;&amp;#039;&amp;#039;: Development, staging, and production environments gradually diverge&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Snowflake Servers&amp;#039;&amp;#039;&amp;#039;: Each server becomes unique and unreproducible&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Slow Deployment&amp;#039;&amp;#039;&amp;#039;: Setting up a new environment can take hours or days&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Poor Resource Utilization&amp;#039;&amp;#039;&amp;#039;: Applications can&amp;#039;t share servers due to dependency conflicts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-Based Deployment Model:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Developer creates Dockerfile specifying exact environment&lt;br /&gt;
2. Build process creates immutable container image with all dependencies&lt;br /&gt;
3. Image tested in CI/CD pipeline (identical to production)&lt;br /&gt;
4. Image deployed to any server with Docker installed&lt;br /&gt;
5. Container starts in seconds with guaranteed-identical environment&lt;br /&gt;
6. Multiple isolated applications run on same host without conflicts&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model provides:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Images never change after building; deploy new versions rather than modifying running systems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Development environment = testing environment = production environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Portability&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;Build once, run anywhere&amp;amp;quot; (laptop, data center, cloud)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: Run 10-100 containers on a single host (vs. 5-10 VMs)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rapid Deployment&amp;#039;&amp;#039;&amp;#039;: Start containers in milliseconds vs. minutes for VMs&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Microservices Architecture&amp;#039;&amp;#039;&amp;#039;: Enables decomposing monoliths into independently deployable services&lt;br /&gt;
&lt;br /&gt;
This shift has revolutionized software engineering, enabling modern DevOps practices, continuous deployment, and cloud-native architectures. Companies like Netflix, Uber, and Airbnb run millions of containers to serve billions of requests daily.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;containers-vs-virtual-machines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Containers vs Virtual Machines ===&lt;br /&gt;
&lt;br /&gt;
Understanding the architectural difference between containers and virtual machines is crucial for appreciating why containers have become the dominant deployment model.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Virtual Machine Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│ Guest OS    │ │ Guest OS    │ │ Guest OS    │&lt;br /&gt;
│ (Kernel)    │ │ (Kernel)    │ │ (Kernel)    │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Hypervisor (VMware, KVM, Xen)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
        Host Operating System &amp;amp;amp; Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Each VM runs a complete guest operating system with its own kernel&lt;br /&gt;
* Hypervisor emulates hardware, providing virtual CPUs, RAM, disks, NICs&lt;br /&gt;
* Strong isolation (separate kernels mean vulnerabilities in one VM don&amp;#039;t affect others)&lt;br /&gt;
* Heavy resource consumption (each OS kernel needs 1-2GB RAM)&lt;br /&gt;
* Slow startup (boot entire OS: 30-60 seconds)&lt;br /&gt;
* Large disk footprint (each VM stores complete OS: 1-10GB)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Container Runtime (Docker Engine)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
    Host Operating System &amp;amp;amp; Shared Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* All containers share the host&amp;#039;s kernel (no guest OS needed)&lt;br /&gt;
* Container runtime manages namespace and cgroup isolation&lt;br /&gt;
* Lightweight isolation (namespaces separate processes, but they&amp;#039;re still just processes)&lt;br /&gt;
* Minimal resource overhead (containers use only incremental memory beyond their application)&lt;br /&gt;
* Fast startup (start process in isolated namespace: milliseconds)&lt;br /&gt;
* Small disk footprint (layered filesystem shares common base images: 10-100MB incremental)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Trade-off:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Virtual machines provide &amp;#039;&amp;#039;&amp;#039;stronger security isolation&amp;#039;&amp;#039;&amp;#039; at the cost of &amp;#039;&amp;#039;&amp;#039;resource efficiency&amp;#039;&amp;#039;&amp;#039;. If your threat model requires complete kernel isolation (e.g., multi-tenant cloud providers hosting untrusted code), VMs are appropriate.&lt;br /&gt;
&lt;br /&gt;
Containers provide &amp;#039;&amp;#039;&amp;#039;lighter-weight isolation&amp;#039;&amp;#039;&amp;#039; with &amp;#039;&amp;#039;&amp;#039;much better resource efficiency&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re running your own applications on your own infrastructure, containers are usually the right choice. You can run 10-100 containers on hardware that would support only 5-10 VMs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;An Important Insight:&amp;#039;&amp;#039;&amp;#039; When you run &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; on the host, you see container processes. They&amp;#039;re not hidden inside separate kernels like VM processes would be. This demonstrates that containers are just isolated processes on the host—the kernel makes them appear isolated through namespaces, but they&amp;#039;re fundamentally just processes.&lt;br /&gt;
&lt;br /&gt;
This is both a feature (efficiency, observability) and a constraint (shared kernel means a kernel vulnerability could affect all containers). Modern container security practices use defense-in-depth: namespaces + cgroups + seccomp + AppArmor/SELinux + user namespaces to create multiple security layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operating System&amp;#039;&amp;#039;&amp;#039;: Linux-based system (Ubuntu 20.04+ recommended, but Debian, Fedora, CentOS also supported)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RAM&amp;#039;&amp;#039;&amp;#039;: Minimum 2GB, 4GB recommended for comfortable operation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Disk Space&amp;#039;&amp;#039;&amp;#039;: At least 20GB free (Docker images and container layers consume significant space)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CPU&amp;#039;&amp;#039;&amp;#039;: Any modern x86_64 or ARM64 processor&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Privileges&amp;#039;&amp;#039;&amp;#039;: Root access via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (required for Docker installation and initial setup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kernel&amp;#039;&amp;#039;&amp;#039;: Minimum Linux kernel 3.10 (kernel 4.0+ recommended for full feature support)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check your kernel version:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;uname -r&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If below 3.10, you&amp;#039;ll need to update your kernel before proceeding.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check available disk space:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker stores images and containers in &amp;lt;code&amp;gt;/var/lib/docker&amp;lt;/code&amp;gt; by default. Ensure you have sufficient space.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
Before beginning, ensure the following packages are installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release \&lt;br /&gt;
    bridge-utils \&lt;br /&gt;
    net-tools&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Package descriptions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;apt-transport-https&amp;lt;/code&amp;gt;: Enables apt to retrieve packages over HTTPS&lt;br /&gt;
* &amp;lt;code&amp;gt;ca-certificates&amp;lt;/code&amp;gt;: Common CA certificates for SSL verification&lt;br /&gt;
* &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;: Command-line tool for transferring data (used to download Docker&amp;#039;s GPG key)&lt;br /&gt;
* &amp;lt;code&amp;gt;gnupg&amp;lt;/code&amp;gt;: GNU Privacy Guard for verifying package signatures&lt;br /&gt;
* &amp;lt;code&amp;gt;lsb-release&amp;lt;/code&amp;gt;: Provides Linux Standard Base version information (used to detect Ubuntu version)&lt;br /&gt;
* &amp;lt;code&amp;gt;bridge-utils&amp;lt;/code&amp;gt;: Tools for managing bridge devices (&amp;lt;code&amp;gt;brctl&amp;lt;/code&amp;gt; command)&lt;br /&gt;
* &amp;lt;code&amp;gt;net-tools&amp;lt;/code&amp;gt;: Legacy networking tools (&amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt;) for compatibility&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
This lab builds directly on concepts from previous labs. You should be comfortable with:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 2 (Filesystems):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Filesystem hierarchy and directory structure&lt;br /&gt;
* Mount points and the &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; command&lt;br /&gt;
* Understanding of &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; purposes&lt;br /&gt;
* File permissions and ownership (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Symbolic links and filesystem navigation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 3 (Processes and Jobs):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs (PIDs) and process hierarchy&lt;br /&gt;
* Parent-child process relationships&lt;br /&gt;
* &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; command and process listing&lt;br /&gt;
* Foreground vs. background processes&lt;br /&gt;
* Process signals (SIGTERM, SIGKILL)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 4 (Users, Groups, and Permissions):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs (UIDs) and group IDs (GIDs)&lt;br /&gt;
* Root vs. unprivileged users&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; for privilege escalation&lt;br /&gt;
* File and directory permissions (read, write, execute)&lt;br /&gt;
* Security implications of running processes as root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 7 (Network Fundamentals):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces and IP addresses&lt;br /&gt;
* Bridges and virtual ethernet (veth) pairs&lt;br /&gt;
* Subnets and CIDR notation&lt;br /&gt;
* Routing tables and default gateways&lt;br /&gt;
* Network Address Translation (NAT)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespaces&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip netns exec&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
This is crucial—Docker networking uses the exact same mechanisms you built manually.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 8 (Transport and Security):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP and UDP protocols&lt;br /&gt;
* Port numbers and socket addresses (IP:PORT)&lt;br /&gt;
* Client-server communication model&lt;br /&gt;
* The concept of listening vs. connecting&lt;br /&gt;
* TLS/SSL (Docker can use HTTPS for secure image distribution)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 9 (Application Protocols):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* HTTP/HTTPS protocols&lt;br /&gt;
* Reverse proxies and path-based routing&lt;br /&gt;
* DNS and hostname resolution&lt;br /&gt;
* Caddy web server configuration&lt;br /&gt;
* Multi-tier application architecture&lt;br /&gt;
&lt;br /&gt;
You should also be comfortable with:&lt;br /&gt;
&lt;br /&gt;
* Command-line text manipulation (&amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sed&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Basic bash scripting (variables, loops, conditionals)&lt;br /&gt;
* Using multiple terminal windows simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;linux-namespaces-the-foundation-of-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Linux Namespaces: The Foundation of Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you worked extensively with network namespaces—one of seven namespace types provided by the Linux kernel. Every time you executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red&lt;br /&gt;
sudo ip netns exec red ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You were creating an isolated network environment and executing commands within that isolated environment. The processes running in the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace could not see network interfaces, routes, or connections in other namespaces. This isolation is the fundamental mechanism that makes containers possible.&lt;br /&gt;
&lt;br /&gt;
Docker extends this concept to six additional namespace types, providing complete process isolation. Understanding namespaces is essential to understanding containers—they are not optional background knowledge, but rather the core technology that defines what a container is.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-seven-namespace-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Seven Namespace Types ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Network Namespace (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You know this namespace intimately from Labs 7-9. When you created the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; namespaces, you were creating isolated network stacks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces (lo, eth0, wlan0, etc.)&lt;br /&gt;
* IP addresses (each namespace has its own IPs)&lt;br /&gt;
* Routing tables (&amp;lt;code&amp;gt;ip route show&amp;lt;/code&amp;gt; output differs per namespace)&lt;br /&gt;
* Firewall rules (iptables/nftables rules are namespace-specific)&lt;br /&gt;
* Network sockets and ports (multiple processes in different namespaces can bind to the same port number)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 7, you manually created network namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red                    # Create isolated network stack&lt;br /&gt;
sudo ip netns exec red ip link show      # View interfaces in that namespace&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does exactly this when you run a container, but also creates six other namespace types simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example implications:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container A can listen on port 80, Container B can listen on port 80—no conflict&lt;br /&gt;
* Container A cannot see Container B&amp;#039;s network connections (&amp;lt;code&amp;gt;ss -tuna&amp;lt;/code&amp;gt; shows only its own)&lt;br /&gt;
* Each container has its own localhost (127.0.0.1) that&amp;#039;s separate from the host&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. PID Namespace (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The PID namespace isolates the process ID number space. This is related to what you learned in Lab 3 about process management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs—processes in different PID namespaces can have the same PID&lt;br /&gt;
* Process visibility—processes can only see other processes in the same PID namespace&lt;br /&gt;
* PID 1 (init process)—each PID namespace has its own PID 1&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The kernel maintains a separate process tree for each PID namespace. When you create a PID namespace and start a process in it:&lt;br /&gt;
&lt;br /&gt;
* Inside the namespace, that process is PID 1 (like an init system)&lt;br /&gt;
* Outside the namespace, that same process has a different PID (e.g., 12345)&lt;br /&gt;
* Processes inside cannot see processes outside their namespace tree&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On the host&lt;br /&gt;
ps aux | wc -l&lt;br /&gt;
# Output: 237 processes&lt;br /&gt;
&lt;br /&gt;
# Inside a container&lt;br /&gt;
docker exec container ps aux | wc -l&lt;br /&gt;
# Output: 5 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s processes think they&amp;#039;re the only processes on the system. They cannot see the host&amp;#039;s other 232 processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Mount Namespace (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The mount namespace isolates the filesystem mount table. This relates directly to Lab 2 where you learned about filesystems and mounting.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Mount points—what is mounted at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, etc.&lt;br /&gt;
* Root filesystem—each namespace can have a completely different root directory&lt;br /&gt;
* Mount propagation—mounts in one namespace don&amp;#039;t affect others (by default)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you create a mount namespace, the new namespace inherits a copy of the parent&amp;#039;s mount table. But subsequent mounts/unmounts in the child don&amp;#039;t affect the parent.&lt;br /&gt;
&lt;br /&gt;
Containers use this to provide a completely different filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host (Ubuntu)&lt;br /&gt;
ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...&lt;br /&gt;
&lt;br /&gt;
# In container (Fedora)&lt;br /&gt;
docker exec fedora ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...  # Different files!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both see &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, but they&amp;#039;re seeing different directories. The container&amp;#039;s &amp;lt;code&amp;gt;/etc/os-release&amp;lt;/code&amp;gt; shows Fedora, while the host&amp;#039;s shows Ubuntu.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Technical implementation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker uses OverlayFS (covered later) to construct the container&amp;#039;s root filesystem from image layers, then uses pivot_root or chroot to make that directory appear as &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to the container&amp;#039;s processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. UTS Namespace (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
UTS stands for &amp;amp;quot;Unix Timesharing System&amp;amp;quot;—a historical name. The UTS namespace isolates hostname and domain name.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System hostname (&amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; command output)&lt;br /&gt;
* Domain name (NIS domain name)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each UTS namespace can set its own hostname independently of other namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host&lt;br /&gt;
hostname&lt;br /&gt;
# Output: myserver.example.com&lt;br /&gt;
&lt;br /&gt;
# In container&lt;br /&gt;
docker exec container hostname&lt;br /&gt;
# Output: a1b2c3d4e5f6  (container ID)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Containers typically use the container ID as hostname by default, but you can override with &amp;lt;code&amp;gt;--hostname&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --hostname=webserver nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. IPC Namespace (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The IPC namespace isolates System V Inter-Process Communication resources. This relates to Lab 6 where you learned about IPC mechanisms.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System V message queues&lt;br /&gt;
* System V semaphore sets&lt;br /&gt;
* System V shared memory segments&lt;br /&gt;
* POSIX message queues (in &amp;lt;code&amp;gt;/dev/mqueue&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 6:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 6, you learned that processes can communicate via shared memory, message queues, and semaphores. The IPC namespace ensures that processes in different namespaces cannot access each other&amp;#039;s IPC objects, even if they use the same IPC keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A creates shared memory segment with key 1234&lt;br /&gt;
docker exec containerA ipcmk -M 1024 -p 1234&lt;br /&gt;
&lt;br /&gt;
# Container B tries to access it&lt;br /&gt;
docker exec containerB ipcs -m -k 1234&lt;br /&gt;
# Output: no segment found (different IPC namespace)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. User Namespace (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The user namespace isolates user IDs (UIDs) and group IDs (GIDs). This is the most complex namespace and provides significant security benefits.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs—UID 0 inside namespace can map to UID 100000 outside&lt;br /&gt;
* Group IDs—similar mapping for GIDs&lt;br /&gt;
* Capabilities—process can have capabilities inside namespace but not outside&lt;br /&gt;
* Security attributes—AppArmor/SELinux contexts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
User namespaces allow UID/GID mapping. A process can be root (UID 0) inside the namespace but unprivileged (e.g., UID 100000) outside.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Inside Container: UID 0 (root)&lt;br /&gt;
        ↓ mapping&lt;br /&gt;
Outside Container: UID 100000 (unprivileged)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security benefit:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even if an attacker compromises a container and gains root privileges inside the container, they&amp;#039;re still unprivileged on the host. If they escape the container, they cannot access files owned by actual root.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s approach:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, many Docker configurations share the host&amp;#039;s user namespace (for simplicity and compatibility). Rootless Docker and user-remapped Docker use separate user namespaces for enhanced security.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Cgroup Namespace (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The cgroup namespace isolates the view of the cgroup hierarchy. Note this is different from cgroups themselves (which we&amp;#039;ll cover separately).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/sys/fs/cgroup&amp;lt;/code&amp;gt; hierarchy&lt;br /&gt;
* Ability to see other containers&amp;#039; resource constraints&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without cgroup namespaces, a process can read &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt; and see the full path to its cgroup, revealing information about the container orchestration system.&lt;br /&gt;
&lt;br /&gt;
With cgroup namespaces, the process sees itself at the root of the cgroup tree, hiding the real hierarchy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; This namespace isolates the &amp;#039;&amp;#039;view&amp;#039;&amp;#039; of cgroups, not the enforcement of resource limits. Resource limits are enforced by cgroups themselves (covered in section 4.5).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;namespace-identifiers-understanding-procpidns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Namespace Identifiers: Understanding /proc/PID/ns/ ====&lt;br /&gt;
&lt;br /&gt;
Every process has a directory at &amp;lt;code&amp;gt;/proc/PID/ns/&amp;lt;/code&amp;gt; containing symbolic links to namespace identifiers. These links reveal which namespaces the process belongs to.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Examining namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 root root 0 Dec 12 10:30 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026531835]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 ipc -&amp;amp;gt; &amp;#039;ipc:[4026531839]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 mnt -&amp;amp;gt; &amp;#039;mnt:[4026531840]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 pid -&amp;amp;gt; &amp;#039;pid:[4026531836]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 uts -&amp;amp;gt; &amp;#039;uts:[4026531838]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the format:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each symlink follows the pattern: &amp;lt;code&amp;gt;namespace_type:[inode_number]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;inode number&amp;#039;&amp;#039;&amp;#039; is the critical piece of information. It uniquely identifies that specific namespace instance. Think of it as a &amp;amp;quot;namespace ID.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key principle: Same inode = Shared namespace, Different inode = Isolated namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example from Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you created network namespaces in Lab 7, each had a unique network namespace inode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host process&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Host&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
# Process in red namespace&lt;br /&gt;
sudo ip netns exec red ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532145]&amp;#039;  # Different inode = isolated!&lt;br /&gt;
&lt;br /&gt;
# Process in blue namespace&lt;br /&gt;
sudo ip netns exec blue ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532147]&amp;#039;  # Also different = also isolated!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Comparing container to host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID on host&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example output: 12345&lt;br /&gt;
&lt;br /&gt;
# View container&amp;#039;s namespaces&lt;br /&gt;
sudo ls -la /proc/12345/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# View host&amp;#039;s namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Different inodes! Container is isolated.&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Namespace sharing:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Sometimes containers intentionally share namespaces. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container shares the host&amp;#039;s network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These symlinks aren&amp;#039;t just informational—you can actually use them to enter namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --net=/proc/12345/ns/net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command enters the network namespace of PID 12345 and runs &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside that namespace. This is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works under the hood—it uses &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; to join the container&amp;#039;s namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary table:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Namespace Type&lt;br /&gt;
! What It Isolates&lt;br /&gt;
! Lab Connection&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;&lt;br /&gt;
| Network stack (interfaces, IPs, routes, ports)&lt;br /&gt;
| Lab 7: You built this manually!&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;&lt;br /&gt;
| Process IDs and process tree&lt;br /&gt;
| Lab 3: Process management&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;&lt;br /&gt;
| Filesystem mounts and root directory&lt;br /&gt;
| Lab 2: Mounting and filesystems&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;&lt;br /&gt;
| Hostname and domain name&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;&lt;br /&gt;
| Shared memory, message queues, semaphores&lt;br /&gt;
| Lab 6: IPC mechanisms&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&lt;br /&gt;
| User and group IDs, capabilities&lt;br /&gt;
| Lab 4: Users and permissions&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
| View of cgroup hierarchy&lt;br /&gt;
| -&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-images-vs-running-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Images vs Running Containers ===&lt;br /&gt;
&lt;br /&gt;
This distinction is fundamental to understanding Docker and is often a source of confusion.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A container image is a &amp;#039;&amp;#039;&amp;#039;read-only template&amp;#039;&amp;#039;&amp;#039; consisting of:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;A root filesystem&amp;#039;&amp;#039;&amp;#039;: All files and directories that will appear in the container (applications, libraries, configuration files)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Metadata&amp;#039;&amp;#039;&amp;#039;: Information about how to run the container (default command, environment variables, exposed ports, volumes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Layers&amp;#039;&amp;#039;&amp;#039;: The filesystem is composed of multiple read-only layers (explained in section 4.4)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable&amp;#039;&amp;#039;&amp;#039;: Once built, an image never changes&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Shareable&amp;#039;&amp;#039;&amp;#039;: Multiple containers can use the same image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Versionable&amp;#039;&amp;#039;&amp;#039;: Images can have tags (e.g., &amp;lt;code&amp;gt;nginx:1.21&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nginx:latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Distributable&amp;#039;&amp;#039;&amp;#039;: Images can be pushed to/pulled from registries (Docker Hub, private registries)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stored on disk&amp;#039;&amp;#039;&amp;#039;: Images consume storage even when not running&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Running Container:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A running container is an &amp;#039;&amp;#039;&amp;#039;instance&amp;#039;&amp;#039;&amp;#039; of an image—a process (or process tree) running in isolated namespaces with its own writable filesystem layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Ephemeral&amp;#039;&amp;#039;&amp;#039;: State is lost when the container is removed (unless using volumes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mutable&amp;#039;&amp;#039;&amp;#039;: Can make changes inside the container (install packages, create files)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process-based&amp;#039;&amp;#039;&amp;#039;: A container is fundamentally just a Linux process in isolated namespaces&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived&amp;#039;&amp;#039;&amp;#039;: Containers are typically created, used, and destroyed frequently&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful during runtime&amp;#039;&amp;#039;&amp;#039;: Maintains state while running, but that state disappears on removal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example to illustrate:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Pull an image (download the template)&lt;br /&gt;
docker pull nginx&lt;br /&gt;
&lt;br /&gt;
# The image now exists on disk&lt;br /&gt;
docker images&lt;br /&gt;
# Output shows: nginx  latest  a1b2c3d4  100MB&lt;br /&gt;
&lt;br /&gt;
# Start first container from this image&lt;br /&gt;
docker run -d --name web1 nginx&lt;br /&gt;
&lt;br /&gt;
# Start second container from the same image  &lt;br /&gt;
docker run -d --name web2 nginx&lt;br /&gt;
&lt;br /&gt;
# Both containers share the same base image filesystem&lt;br /&gt;
# But each has its own writable layer and separate namespaces&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now you have:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;One image&amp;#039;&amp;#039;&amp;#039; (nginx:latest) on disk&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Two containers&amp;#039;&amp;#039;&amp;#039; (web1 and web2) running as separate processes&lt;br /&gt;
* Each container has its own PID namespace, network namespace, etc.&lt;br /&gt;
* Changes in web1 don&amp;#039;t affect web2 (isolated writable layers)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verification:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Modify web1&lt;br /&gt;
docker exec web1 bash -c &amp;quot;echo &amp;#039;Hello from web1&amp;#039; &amp;gt; /usr/share/nginx/html/test.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Check web1&lt;br /&gt;
docker exec web1 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: Hello from web1&lt;br /&gt;
&lt;br /&gt;
# Check web2&lt;br /&gt;
docker exec web2 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: cat: /usr/share/nginx/html/test.txt: No such file or directory&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The file exists in web1 but not in web2, even though they&amp;#039;re from the same image. Each container has its own writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Image to Container Relationship Diagram:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;          [nginx Image]&lt;br /&gt;
          (Read-only)&lt;br /&gt;
               |&lt;br /&gt;
     ┌─────────┴─────────┐&lt;br /&gt;
     ↓                   ↓&lt;br /&gt;
[Container 1]       [Container 2]&lt;br /&gt;
(Writable layer)    (Writable layer)&lt;br /&gt;
(PID namespace)     (PID namespace)&lt;br /&gt;
(Net namespace)     (Net namespace)&lt;br /&gt;
(Isolated)          (Isolated)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filesystem perspective:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Image Layers (read-only, shared):&lt;br /&gt;
├─ Layer 3: nginx files&lt;br /&gt;
├─ Layer 2: nginx dependencies  &lt;br /&gt;
└─ Layer 1: Base OS (Debian)&lt;br /&gt;
&lt;br /&gt;
Container 1 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 1&lt;br /&gt;
&lt;br /&gt;
Container 2 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why this design?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: 100 containers from the same image share one copy of the base filesystem&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039;: Starting a container doesn&amp;#039;t require copying files—just create a new writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Consistency&amp;#039;&amp;#039;&amp;#039;: All containers from an image start with identical state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutability&amp;#039;&amp;#039;&amp;#039;: Encourages treating containers as disposable—don&amp;#039;t modify running containers, rebuild images instead&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle-and-ephemeral-nature&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle and Ephemeral Nature ===&lt;br /&gt;
&lt;br /&gt;
Understanding the container lifecycle is essential for proper container usage. Containers are designed to be ephemeral—short-lived and replaceable.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────┐&lt;br /&gt;
│ Created │ (Container exists but not running)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │ (Processes executing in isolated namespaces)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker stop (SIGTERM, then SIGKILL after timeout)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Stopped │ (Processes terminated, filesystem layer persists)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start (restart with same writable layer)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker rm (delete container)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Removed │ (Container and writable layer deleted forever)&lt;br /&gt;
└─────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key lifecycle commands:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create and start in one step (most common)&lt;br /&gt;
docker run nginx&lt;br /&gt;
&lt;br /&gt;
# Create without starting&lt;br /&gt;
docker create --name test nginx&lt;br /&gt;
&lt;br /&gt;
# Start existing stopped container&lt;br /&gt;
docker start test&lt;br /&gt;
&lt;br /&gt;
# Stop running container (SIGTERM to main process)&lt;br /&gt;
docker stop test&lt;br /&gt;
&lt;br /&gt;
# Force stop (SIGKILL)&lt;br /&gt;
docker kill test&lt;br /&gt;
&lt;br /&gt;
# Remove stopped container&lt;br /&gt;
docker rm test&lt;br /&gt;
&lt;br /&gt;
# Remove running container (force)&lt;br /&gt;
docker rm -f test&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Ephemeral Nature:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, all changes made inside a container are lost when the container is removed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container&lt;br /&gt;
docker run -d --name demo nginx&lt;br /&gt;
&lt;br /&gt;
# Make changes inside&lt;br /&gt;
docker exec demo bash -c &amp;quot;echo &amp;#039;My data&amp;#039; &amp;gt; /tmp/important.txt&amp;quot;&lt;br /&gt;
docker exec demo cat /tmp/important.txt&lt;br /&gt;
# Output: My data&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop demo&lt;br /&gt;
docker rm demo&lt;br /&gt;
&lt;br /&gt;
# Try to access the data&lt;br /&gt;
docker run --name demo2 nginx&lt;br /&gt;
docker exec demo2 cat /tmp/important.txt&lt;br /&gt;
# Output: cat: /tmp/important.txt: No such file or directory&lt;br /&gt;
# THE DATA IS GONE!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why ephemeral?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This might seem like a limitation, but it&amp;#039;s actually a feature that enables important practices:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t patch running systems; deploy new versions&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Every deployment starts from a known state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Testing&amp;#039;&amp;#039;&amp;#039;: Test environments are identical to production&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Rollback&amp;#039;&amp;#039;&amp;#039;: Easy to roll back to previous image version&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Scaling&amp;#039;&amp;#039;&amp;#039;: Identical containers can be created/destroyed dynamically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When you need persistence:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For data that must survive container restarts, use &amp;#039;&amp;#039;&amp;#039;volumes&amp;#039;&amp;#039;&amp;#039; (covered in section 4.7):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create named volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use volume in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data in /data survives container removal&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container lifecycle best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Treat containers as cattle, not pets&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t name them, don&amp;#039;t SSH into them to debug, don&amp;#039;t manually configure them&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Logs go to stdout/stderr&amp;#039;&amp;#039;&amp;#039;: Not to files inside the container (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration via environment variables&amp;#039;&amp;#039;&amp;#039;: Not by editing files inside the container&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data goes in volumes&amp;#039;&amp;#039;&amp;#039;: Not in the container&amp;#039;s writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived processes&amp;#039;&amp;#039;&amp;#039;: Containers should start quickly and shut down gracefully&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about process lifecycle (start, run, terminate). Containers follow a similar lifecycle, but operate at a higher level of abstraction—each container lifecycle event actually involves creating/destroying multiple processes in isolated namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;control-groups-cgroups-resource-limiting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Control Groups (cgroups): Resource Limiting ===&lt;br /&gt;
&lt;br /&gt;
Control groups (cgroups) are a Linux kernel feature that limits, accounts for, and isolates resource usage (CPU, memory, disk I/O, network) of process groups. Without cgroups, a runaway container could consume all CPU or memory, starving other containers and crashing the host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without resource limits:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Malicious or buggy container&lt;br /&gt;
docker run -d evil-container&lt;br /&gt;
&lt;br /&gt;
# This container&amp;#039;s process could:&lt;br /&gt;
# - Consume 100% CPU (slow down everything else)&lt;br /&gt;
# - Allocate all available RAM (trigger OOM killer on host)&lt;br /&gt;
# - Fill up disk space (crash other containers)&lt;br /&gt;
# - Monopolize network bandwidth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable in multi-tenant environments. You need resource isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: cgroups&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Cgroups organize processes into hierarchical groups with configurable resource limits. The kernel enforces these limits, preventing processes from exceeding their allocation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cgroup Controllers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The Linux kernel provides several cgroup controllers, each managing a different resource type:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpu&amp;#039;&amp;#039;&amp;#039;: Limits CPU time&lt;br /&gt;
#* CPU shares (relative priority)&lt;br /&gt;
#* CPU quotas (hard limits)&lt;br /&gt;
#* CPU affinity (pin to specific cores)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;memory&amp;#039;&amp;#039;&amp;#039;: Limits RAM usage&lt;br /&gt;
#* Hard limits (container killed if exceeded)&lt;br /&gt;
#* Soft limits (reclaim memory under pressure)&lt;br /&gt;
#* Swap limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;blkio&amp;#039;&amp;#039;&amp;#039;: Limits disk I/O&lt;br /&gt;
#* Read/write bandwidth limits&lt;br /&gt;
#* I/O operation rate limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;net_cls/net_prio&amp;#039;&amp;#039;&amp;#039;: Network bandwidth control&lt;br /&gt;
#* Traffic classification&lt;br /&gt;
#* Priority settings&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;pids&amp;#039;&amp;#039;&amp;#039;: Limits number of processes&lt;br /&gt;
#* Prevents fork bombs&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpuset&amp;#039;&amp;#039;&amp;#039;: Assigns specific CPUs and memory nodes&lt;br /&gt;
#* NUMA awareness&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s cgroup integration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you start a container with resource limits, Docker configures the appropriate cgroups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --memory=512m --cpus=1.5 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a cgroup hierarchy at &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; and configures:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;memory.limit_in_bytes = 536870912&amp;lt;/code&amp;gt; (512MB)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt; to enforce 1.5 CPUs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Viewing cgroup settings:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example: 12345&lt;br /&gt;
&lt;br /&gt;
# View memory limit&lt;br /&gt;
cat /sys/fs/cgroup/memory/docker/12345/memory.limit_in_bytes&lt;br /&gt;
# Output: 536870912&lt;br /&gt;
&lt;br /&gt;
# View CPU quota&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/12345/cpu.cfs_quota_us&lt;br /&gt;
# Output: 150000 (1.5 CPUs)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common resource limit flags:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Memory limits&lt;br /&gt;
--memory=512m              # Hard limit: 512MB RAM&lt;br /&gt;
--memory-reservation=256m  # Soft limit: try to stay under 256MB&lt;br /&gt;
--memory-swap=512m         # Total memory+swap limit&lt;br /&gt;
&lt;br /&gt;
# CPU limits&lt;br /&gt;
--cpus=1.5                 # Use at most 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512           # Relative CPU priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1          # Pin to CPU cores 0 and 1&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb    # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb   # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100           # Max 100 processes in container&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Testing memory limits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container with 256MB memory limit&lt;br /&gt;
docker run -it --memory=256m ubuntu bash&lt;br /&gt;
&lt;br /&gt;
# Inside container, try to allocate 512MB&lt;br /&gt;
# (requires &amp;#039;stress&amp;#039; tool)&lt;br /&gt;
apt-get update &amp;amp;&amp;amp; apt-get install -y stress&lt;br /&gt;
stress --vm 1 --vm-bytes 512M&lt;br /&gt;
&lt;br /&gt;
# Container is killed when exceeding limit&lt;br /&gt;
# Output: stress: FAIL: [1] (415) &amp;lt;-- worker 7 got signal 9&lt;br /&gt;
# Signal 9 = SIGKILL (OOM killer)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why cgroups are essential:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-tenancy&amp;#039;&amp;#039;&amp;#039;: Run untrusted workloads safely&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Quality of Service&amp;#039;&amp;#039;&amp;#039;: Guarantee resources for critical applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Fair sharing&amp;#039;&amp;#039;&amp;#039;: Prevent one container from monopolizing resources&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Predictability&amp;#039;&amp;#039;&amp;#039;: Know exactly how much resources each container can use&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Cost control&amp;#039;&amp;#039;&amp;#039;: In cloud environments, map cgroups to billing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;cgroup vs namespace distinction:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;isolation&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;resource limits&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Both are necessary for containers. Namespaces prevent containers from seeing each other; cgroups prevent containers from starving each other.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-networking-bridges-veth-pairs-and-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Networking: Bridges, veth Pairs, and NAT ===&lt;br /&gt;
&lt;br /&gt;
Docker networking should feel familiar—it uses the exact same mechanisms you built manually in Lab 7. The primary difference is automation: Docker sets up bridges, veth pairs, routes, and NAT rules automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Default Docker Network Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you install Docker, it creates a default bridge network:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐&lt;br /&gt;
│  Container A    │  │  Container B    │  │  Container C    │&lt;br /&gt;
│  172.17.0.2     │  │  172.17.0.3     │  │  172.17.0.4     │&lt;br /&gt;
└────────┬────────┘  └────────┬────────┘  └────────┬────────┘&lt;br /&gt;
         │eth0               │eth0               │eth0&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
      (veth pair)         (veth pair)         (veth pair)&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
    ┌────┴───────────────────┴───────────────────┴────┐&lt;br /&gt;
    │              docker0 Bridge                      │&lt;br /&gt;
    │              172.17.0.1/16                       │&lt;br /&gt;
    └────────────────────┬─────────────────────────────┘&lt;br /&gt;
                         │&lt;br /&gt;
                    [Host eth0]&lt;br /&gt;
                         │&lt;br /&gt;
                    [Internet]&lt;br /&gt;
                   (via NAT/MASQUERADE)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Component breakdown (all from Lab 7!):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Bridge Interface (&amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically creates a bridge interface when installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show docker0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;3: docker0: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    link/ether 02:42:8f:a3:f1:2a brd ff:ff:ff:ff:ff:ff&lt;br /&gt;
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; from Lab 7:&lt;br /&gt;
&lt;br /&gt;
* Bridge interface acting as virtual switch&lt;br /&gt;
* Assigned IP address 172.17.0.1&lt;br /&gt;
* Subnet 172.17.0.0/16 (65,536 addresses available)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. veth Pairs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For each container, Docker creates a veth pair:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep veth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;8: veth7a3f2b1@if7: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&lt;br /&gt;
10: veth9d4e8c2@if9: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Decoding this:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;veth7a3f2b1@if7&amp;lt;/code&amp;gt;: One end of veth pair, connected to bridge (&amp;lt;code&amp;gt;master docker0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;@if7&amp;lt;/code&amp;gt;: Paired with interface index 7 (inside container)&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly what you did in Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-host type veth peer name v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does the same thing, automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Container Network Namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each container has its own network namespace (you built these manually in Lab 7!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View container&amp;#039;s network interfaces&lt;br /&gt;
docker exec container ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
&lt;br /&gt;
7: eth0@if8: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container sees:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Its own loopback interface&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if8&amp;lt;/code&amp;gt;: Its end of the veth pair (paired with host&amp;#039;s interface index 8)&lt;br /&gt;
* IP address from docker0 subnet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. IP Address Assignment&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker acts as a simple DHCP-like service, assigning IPs sequentially:&lt;br /&gt;
&lt;br /&gt;
* First container: 172.17.0.2&lt;br /&gt;
* Second container: 172.17.0.3&lt;br /&gt;
* Third container: 172.17.0.4&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Routing in Container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the container&amp;#039;s routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec container ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 172.17.0.1 dev eth0&lt;br /&gt;
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Default route: Send all traffic to 172.17.0.1 (the bridge) for routing to internet&lt;br /&gt;
* Local route: 172.17.0.0/16 is directly reachable via eth0&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the routing you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. NAT for Internet Access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically configures iptables/nftables NAT rules (MASQUERADE) so containers can reach the internet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;MASQUERADE  all  --  172.17.0.0/16  0.0.0.0/0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the NAT you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Port Mapping (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you use &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;, Docker sets up Destination NAT (DNAT):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8080:80 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker adds an iptables DNAT rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep DNAT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.2:80&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039; Traffic arriving at host port 8080 is redirected to 172.17.0.2:80&lt;br /&gt;
&lt;br /&gt;
This is also NAT, specifically DNAT (Destination NAT). You encountered source NAT (SNAT/MASQUERADE) in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-to-Container Communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Containers on the same bridge can communicate directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A pings Container B&lt;br /&gt;
docker exec containerA ping 172.17.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The packet flow:&lt;br /&gt;
&lt;br /&gt;
# Container A sends to 172.17.0.2 (Container B&amp;#039;s IP)&lt;br /&gt;
# Packet goes out Container A&amp;#039;s eth0 (through veth pair)&lt;br /&gt;
# Arrives on docker0 bridge&lt;br /&gt;
# Bridge forwards to veth pair for Container B&lt;br /&gt;
# Packet arrives at Container B&amp;#039;s eth0&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;No routing through host networking needed&amp;#039;&amp;#039;&amp;#039;—the bridge forwards directly (Layer 2 switching).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Custom Networks&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can create custom bridge networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.20.0.0/24 mynet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a new bridge interface (e.g., &amp;lt;code&amp;gt;br-abc123def456&amp;lt;/code&amp;gt;) with subnet 10.20.0.0/24.&lt;br /&gt;
&lt;br /&gt;
Containers on different networks are isolated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --network=mynet --name=isolated nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container is on &amp;lt;code&amp;gt;mynet&amp;lt;/code&amp;gt;, not &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;, so it cannot communicate with containers on the default bridge (different Layer 2 segment).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS Resolution Between Containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker provides automatic DNS resolution for container names within custom networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create mynet&lt;br /&gt;
docker run -d --network=mynet --name=web nginx&lt;br /&gt;
docker run -d --network=mynet --name=app alpine&lt;br /&gt;
&lt;br /&gt;
# From app container&lt;br /&gt;
docker exec app ping web&lt;br /&gt;
# Resolves &amp;#039;web&amp;#039; to web container&amp;#039;s IP address!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker runs an embedded DNS server (listening on 127.0.0.11 inside containers) that resolves container names to IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Modes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker supports several network modes:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;bridge&amp;#039;&amp;#039;&amp;#039; (default): Container on docker0 bridge (or custom bridge)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039;: Container shares host&amp;#039;s network namespace (no isolation)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;none&amp;#039;&amp;#039;&amp;#039;: No networking (container has only loopback)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;container:name&amp;#039;&amp;#039;&amp;#039;: Share another container&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example: host mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s network namespace inode is the same as the host&amp;#039;s:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container sees all host&amp;#039;s network interfaces and can bind to any port on any interface.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker networking uses:&lt;br /&gt;
&lt;br /&gt;
* Bridges (like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* veth pairs (like &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; ↔ &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Network namespaces (like &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Routing tables and default routes&lt;br /&gt;
* NAT/MASQUERADE for internet access&lt;br /&gt;
* DNAT for port mapping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;volumes-persistent-and-shared-storage&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volumes: Persistent and Shared Storage ===&lt;br /&gt;
&lt;br /&gt;
By default, container filesystems are ephemeral—all changes are lost when the container is removed. For data that must persist (databases, user uploads, logs), Docker provides volumes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start database container&lt;br /&gt;
docker run -d --name db postgres&lt;br /&gt;
&lt;br /&gt;
# Database writes data&lt;br /&gt;
docker exec db psql -c &amp;quot;CREATE DATABASE myapp;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker rm -f db&lt;br /&gt;
&lt;br /&gt;
# Data is GONE FOREVER!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable for stateful applications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: Volumes&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Volumes are directories on the host that are mounted into containers. Data written to volumes persists beyond container lifecycle.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two Volume Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Named Volumes (Docker-managed):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker manages the volume storage location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data written to /data inside container is stored in:&lt;br /&gt;
# /var/lib/docker/volumes/mydata/_data (on host)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Docker manages storage location&lt;br /&gt;
* Portable across hosts (can be backed up, restored)&lt;br /&gt;
* Works with volume plugins (NFS, cloud storage)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Use named volumes for production databases, critical data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Bind Mounts (Host directory):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Mount a host directory directly into the container.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Mount host directory into container&lt;br /&gt;
docker run -v /home/user/html:/usr/share/nginx/html nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Any changes in &amp;lt;code&amp;gt;/home/user/html&amp;lt;/code&amp;gt; on the host are immediately visible in &amp;lt;code&amp;gt;/usr/share/nginx/html&amp;lt;/code&amp;gt; inside the container, and vice versa.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Direct access to files from host&lt;br /&gt;
* Useful for development (edit code on host, see changes in container immediately)&lt;br /&gt;
* No Docker management needed&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Disadvantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Tied to specific host filesystem paths&lt;br /&gt;
* Less portable&lt;br /&gt;
* Permissions can be tricky (host UID vs. container UID)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Docker essentially does:&lt;br /&gt;
mount --bind /var/lib/docker/volumes/mydata/_data /data&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Sharing Between Containers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Multiple containers can share the same volume:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create shared&lt;br /&gt;
&lt;br /&gt;
# Container 1 writes&lt;br /&gt;
docker run -v shared:/data --name writer alpine sh -c &amp;quot;echo &amp;#039;Hello&amp;#039; &amp;gt; /data/file.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Container 2 reads&lt;br /&gt;
docker run -v shared:/data --name reader alpine cat /data/file.txt&lt;br /&gt;
# Output: Hello&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Use cases:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Shared configuration between containers&lt;br /&gt;
* Log aggregation (multiple containers write logs to shared volume)&lt;br /&gt;
* Data processing pipelines (one container produces, another consumes)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Lifecycle:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect mydata&lt;br /&gt;
&lt;br /&gt;
# Remove volume (only if no containers using it)&lt;br /&gt;
docker volume rm mydata&lt;br /&gt;
&lt;br /&gt;
# Remove all unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Inspecting Volume Mounts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect container --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;volume&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/var/lib/docker/volumes/mydata/_data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;z&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;volume&amp;amp;quot; or &amp;amp;quot;bind&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Container-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: Read-write (true) or read-only (false)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read-Only Volumes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For security, you can mount volumes read-only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -v mydata:/data:ro nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container can read &amp;lt;code&amp;gt;/data&amp;lt;/code&amp;gt; but cannot write to it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;tmpfs Mounts (In-Memory Temporary Storage):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For sensitive data that should never touch disk:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --tmpfs /tmp:size=100m nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; directory is stored in RAM and disappears when the container stops.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Named volumes for databases&amp;#039;&amp;#039;&amp;#039;: Postgres, MySQL, MongoDB&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bind mounts for development&amp;#039;&amp;#039;&amp;#039;: Code that you&amp;#039;re actively editing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;tmpfs for secrets&amp;#039;&amp;#039;&amp;#039;: Temporary credentials, keys&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volume plugins for cloud&amp;#039;&amp;#039;&amp;#039;: AWS EBS, Azure Disk, GCP Persistent Disk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;laboratory-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Laboratory Exercises ==&lt;br /&gt;
&lt;br /&gt;
The following exercises build progressively, demonstrating how Docker automates the kernel-level primitives you mastered in previous labs. You will install Docker, inspect namespace isolation, explore interactive containers, configure persistent storage, and build a multi-container application with networking and resource limits.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-installing-docker&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Installing Docker ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Install Docker Engine from the official Docker repository and configure it for non-root access.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why the official repository?&amp;#039;&amp;#039;&amp;#039; Ubuntu&amp;#039;s default repositories often contain outdated Docker versions. The official Docker repository provides the latest stable releases with security updates and new features.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Remove old Docker versions (if any)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
If you previously installed Docker from Ubuntu&amp;#039;s repositories or older Docker installations exist, remove them to avoid conflicts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt remove docker docker-engine docker.io containerd runc&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It&amp;#039;s safe to run this even if these packages aren&amp;#039;t installed—apt will simply report they&amp;#039;re not present.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Install prerequisites&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Install packages needed for adding Docker&amp;#039;s repository:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Add Docker&amp;#039;s GPG key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker signs its packages with a GPG key to ensure authenticity. Add this key to your system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo install -m 0755 -d /etc/apt/keyrings&lt;br /&gt;
&lt;br /&gt;
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \&lt;br /&gt;
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg&lt;br /&gt;
&lt;br /&gt;
sudo chmod a+r /etc/apt/keyrings/docker.gpg&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Creates &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; directory for storing repository keys&lt;br /&gt;
* Downloads Docker&amp;#039;s GPG public key&lt;br /&gt;
* Converts it to binary format (&amp;lt;code&amp;gt;.gpg&amp;lt;/code&amp;gt; file)&lt;br /&gt;
* Makes it readable by all users&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Add Docker repository to apt sources&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tee /etc/apt/sources.list.d/docker.sources &amp;lt;&amp;lt;EOF&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://download.docker.com/linux/ubuntu&lt;br /&gt;
Suites: $(. /etc/os-release &amp;amp;&amp;amp; echo &amp;quot;${UBUNTU_CODENAME:-$VERSION_CODENAME}&amp;quot;)&lt;br /&gt;
Components: stable&lt;br /&gt;
Signed-By: /etc/apt/keyrings/docker.gpg&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Detects your CPU architecture (amd64, arm64, etc.)&lt;br /&gt;
* Detects your Ubuntu version codename (focal, jammy, etc.)&lt;br /&gt;
* Adds Docker&amp;#039;s repository to apt&amp;#039;s source list&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Install Docker Engine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Update package index and install Docker:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    docker-ce \&lt;br /&gt;
    docker-ce-cli \&lt;br /&gt;
    containerd.io \&lt;br /&gt;
    docker-buildx-plugin \&lt;br /&gt;
    docker-compose-plugin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packages installed:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce&amp;lt;/code&amp;gt;: Docker Community Edition engine (the main Docker daemon)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce-cli&amp;lt;/code&amp;gt;: Docker command-line interface&lt;br /&gt;
* &amp;lt;code&amp;gt;containerd.io&amp;lt;/code&amp;gt;: Container runtime that Docker uses under the hood&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-buildx-plugin&amp;lt;/code&amp;gt;: Extended build capabilities (multi-platform images)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-compose-plugin&amp;lt;/code&amp;gt;: Docker Compose for multi-container applications&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Verify Docker installation&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check Docker version:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo docker --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Docker version 24.0.7, build afdd53b&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your version number may be different (newer), which is fine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Start and enable Docker service&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Ensure Docker daemon starts on boot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check service status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl status docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;● docker.service - Docker Application Container Engine&lt;br /&gt;
     Loaded: loaded (/lib/systemd/system/docker.service; enabled)&lt;br /&gt;
     Active: active (running) since Thu 2024-12-12 10:30:00 UTC; 5min ago&amp;lt;/pre&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;Active: active (running)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Configure non-root Docker access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, only root can run Docker commands. To run Docker without &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, add your user to the &amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important:&amp;#039;&amp;#039;&amp;#039; Log out and log back in for this change to take effect. Alternatively, you can run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;newgrp docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This starts a new shell with updated group membership.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Verify non-root access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Test that you can run Docker without sudo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If this works without errors, you&amp;#039;ve successfully installed Docker!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;hello-world:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/hello-world&lt;br /&gt;
c1ec31eb5944: Pull complete&lt;br /&gt;
Digest: sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6&lt;br /&gt;
Status: Downloaded newer image for hello-world:latest&lt;br /&gt;
&lt;br /&gt;
Hello from Docker!&lt;br /&gt;
This message shows that your installation appears to be working correctly.&lt;br /&gt;
&lt;br /&gt;
To generate this message, Docker took the following steps:&lt;br /&gt;
 1. The Docker client contacted the Docker daemon.&lt;br /&gt;
 2. The Docker daemon pulled the &amp;amp;quot;hello-world&amp;amp;quot; image from the Docker Hub.&lt;br /&gt;
 3. The Docker daemon created a new container from that image which runs the&lt;br /&gt;
    executable that produces the output you are currently reading.&lt;br /&gt;
 4. The Docker daemon streamed that output to the Docker client, which sent it&lt;br /&gt;
    to your terminal.&lt;br /&gt;
&lt;br /&gt;
To try something more ambitious, you can run an Ubuntu container with:&lt;br /&gt;
 $ docker run -it ubuntu bash&lt;br /&gt;
&lt;br /&gt;
Share images, automate workflows, and more with a free Docker ID:&lt;br /&gt;
 https://hub.docker.com/&lt;br /&gt;
&lt;br /&gt;
For more examples and ideas, visit:&lt;br /&gt;
 https://docs.docker.com/get-started/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker client (&amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; command) connected to Docker daemon (dockerd)&lt;br /&gt;
# Daemon checked local images—didn&amp;#039;t find &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt;&lt;br /&gt;
# Daemon pulled &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt; image from Docker Hub (public registry)&lt;br /&gt;
# Daemon created container from image&lt;br /&gt;
# Container executed its program (printed the message)&lt;br /&gt;
# Container exited&lt;br /&gt;
# Output sent back to your terminal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up test container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the hello-world container with status &amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm $(docker ps -aq --filter &amp;quot;ancestor=hello-world&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify it&amp;#039;s gone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo systemctl status docker&amp;lt;/code&amp;gt; (showing Active: active (running))&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the entire message)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-hello-world-and-namespace-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercise B: Hello World and Namespace Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run your first container, understand the container lifecycle, and inspect the Linux kernel namespaces that provide container isolation—connecting directly to your work in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-hello-world&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Hello World ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run the hello-world container (if not done in Exercise A)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
We covered this in Exercise A, but let&amp;#039;s examine what actually happened in more detail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: List all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Nothing (empty list)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why?&amp;#039;&amp;#039;&amp;#039; The hello-world container ran, printed its message, and exited immediately. &amp;lt;code&amp;gt;docker ps&amp;lt;/code&amp;gt; only shows running containers by default.&lt;br /&gt;
&lt;br /&gt;
To see all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES&lt;br /&gt;
a1b2c3d4e5f6   hello-world   &amp;amp;quot;/hello&amp;amp;quot;   10 seconds ago   Exited (0) 8 seconds ago              eager_tesla&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CONTAINER ID&amp;#039;&amp;#039;&amp;#039;: Short hex identifier (first 12 chars of full 64-char ID)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IMAGE&amp;#039;&amp;#039;&amp;#039;: Which image this container was created from&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;COMMAND&amp;#039;&amp;#039;&amp;#039;: The process that ran inside the container (&amp;lt;code&amp;gt;/hello&amp;lt;/code&amp;gt; executable)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CREATED&amp;#039;&amp;#039;&amp;#039;: When the container was created&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;STATUS&amp;#039;&amp;#039;&amp;#039;: Current state—&amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt; means process exited with code 0 (success)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PORTS&amp;#039;&amp;#039;&amp;#039;: Port mappings (none for hello-world)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NAMES&amp;#039;&amp;#039;&amp;#039;: Random name if you don&amp;#039;t specify one (Docker generates names like &amp;amp;quot;eager_tesla&amp;amp;quot;, &amp;amp;quot;hopeful_darwin&amp;amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get detailed information about the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect eager_tesla  # Use your actual container name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This outputs a large JSON document with all container metadata. Let&amp;#039;s extract specific fields:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Just the State section&lt;br /&gt;
docker inspect eager_tesla --format=&amp;#039;{{json .State}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (formatted):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;Status&amp;quot;: &amp;quot;exited&amp;quot;,&lt;br /&gt;
    &amp;quot;Running&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Paused&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Restarting&amp;quot;: false,&lt;br /&gt;
    &amp;quot;OOMKilled&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Dead&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Pid&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;ExitCode&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;StartedAt&amp;quot;: &amp;quot;2024-12-12T10:35:00Z&amp;quot;,&lt;br /&gt;
    &amp;quot;FinishedAt&amp;quot;: &amp;quot;2024-12-12T10:35:01Z&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container ran for about 1 second (started at :00, finished at :01)&lt;br /&gt;
* Exit code 0 (successful completion)&lt;br /&gt;
* PID is 0 (process has terminated; while running it had a real PID)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: View container logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even though the container exited, Docker saved its output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows the hello-world message again. This demonstrates that Docker captures stdout/stderr from containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Stopped containers still consume disk space (their writable layer persists). Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify removal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container should be gone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-namespaces-connect-to-lab-7&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Namespaces (Connect to Lab 7!) ====&lt;br /&gt;
&lt;br /&gt;
Now let&amp;#039;s run a longer-lived container and examine its namespace isolation—this directly connects to your hands-on work with network namespaces in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run a persistent container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode—run in background&lt;br /&gt;
* &amp;lt;code&amp;gt;--name inspector&amp;lt;/code&amp;gt;: Give it a memorable name instead of random name&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;nginx:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/nginx&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for nginx:latest&lt;br /&gt;
b7f9a8e6c4d3a1b2c5e8f9d2a7c4b6e8f3d9a2c1b4e7f8d3a5c2b1&amp;lt;/pre&amp;gt;&lt;br /&gt;
The long hex string is the full 64-character container ID. Docker returns this after creating the container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify the container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND                  CREATED          STATUS          PORTS     NAMES&lt;br /&gt;
b7f9a8e6c4d3   nginx   &amp;amp;quot;/docker-entrypoint.…&amp;amp;quot;   10 seconds ago   Up 8 seconds    80/tcp    inspector&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container is running nginx web server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Get the container&amp;#039;s PID on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Remember: containers are just processes. Let&amp;#039;s find the PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; A number like &amp;lt;code&amp;gt;12345&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the process ID on the &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039; system. Let&amp;#039;s verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep 12345&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see nginx processes! The container is just a process with a fancy namespace wrapper.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Examine the container&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial step connecting to Lab 7. Replace 12345 with your actual PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/12345/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 0&lt;br /&gt;
dr-x--x--x 2 root root 0 Dec 12 10:40 .&lt;br /&gt;
dr-xr-xr-x 9 root root 0 Dec 12 10:40 ..&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026533671]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 ipc -&amp;amp;gt; &amp;#039;ipc:[4026533669]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 mnt -&amp;amp;gt; &amp;#039;mnt:[4026533667]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid_for_children -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 uts -&amp;amp;gt; &amp;#039;uts:[4026533668]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis of namespace inodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Note the inode numbers (the numbers in brackets). Each represents a namespace instance.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Compare with host&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 youruser youruser 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical observation:&amp;#039;&amp;#039;&amp;#039; The container&amp;#039;s network namespace inode (&amp;lt;code&amp;gt;4026533672&amp;lt;/code&amp;gt;) is &amp;#039;&amp;#039;&amp;#039;different&amp;#039;&amp;#039;&amp;#039; from the host&amp;#039;s (&amp;lt;code&amp;gt;4026531992&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Compare two containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Start a second container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector2 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Get its PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector2 --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check its network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/NEW_PID/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; A different inode number from both the host and &amp;lt;code&amp;gt;inspector&amp;lt;/code&amp;gt; container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion:&amp;#039;&amp;#039;&amp;#039; Each container has its own isolated network namespace, just like the red and blue namespaces you created manually in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Examine other namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s check PID namespace isolation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host PID namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/pid&lt;br /&gt;
&lt;br /&gt;
# Container PID namespace  &lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/pid&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Different inodes = isolated process trees!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: User namespace (often shared)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host user namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/user&lt;br /&gt;
&lt;br /&gt;
# Container user namespace&lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/user&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Often the &amp;#039;&amp;#039;&amp;#039;same&amp;#039;&amp;#039;&amp;#039; inode number.&lt;br /&gt;
&lt;br /&gt;
Many Docker configurations share the host&amp;#039;s user namespace for simplicity. This means UID 0 in the container is UID 0 on the host (less secure, but more compatible).&lt;br /&gt;
&lt;br /&gt;
For enhanced security, Docker can be configured to use separate user namespaces (rootless Docker), but that&amp;#039;s beyond this lab&amp;#039;s scope.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Enter the container&amp;#039;s namespace with nsenter&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can actually enter a container&amp;#039;s namespaces using the &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; command (this is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --target CONTAINER_PID --net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This executes &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside the container&amp;#039;s network namespace, showing the container&amp;#039;s view of network interfaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop inspector inspector2&lt;br /&gt;
docker rm inspector inspector2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable B ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the full hello message)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker ps -a&amp;lt;/code&amp;gt; showing the exited hello-world container with its random name&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/code&amp;gt; (showing the PID)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo ls -la /proc/PID/ns/&amp;lt;/code&amp;gt; for the inspector container (showing all namespaces)&lt;br /&gt;
# Side-by-side comparison:&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/code&amp;gt; (host&amp;#039;s network namespace inode)&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&amp;lt;/code&amp;gt; (container&amp;#039;s network namespace inode)&lt;br /&gt;
#* Highlight that the inode numbers are different&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-interactive-exploration-with-fedora&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Interactive Exploration with Fedora ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run an interactive container with a different Linux distribution (Fedora instead of Ubuntu), demonstrating mount namespace isolation (different root filesystems), PID namespace isolation (isolated process tree), and UTS namespace isolation (different hostname).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important context:&amp;#039;&amp;#039;&amp;#039; Your host might be running Ubuntu, but the container will run Fedora. Both will be using the same Linux kernel, but they&amp;#039;ll have completely different filesystems and will appear as different &amp;amp;quot;machines&amp;amp;quot; from inside.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run Fedora container interactively&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -it --name fedora-explore fedora bash&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Interactive—keep STDIN open&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Allocate pseudo-TTY (terminal)&lt;br /&gt;
* &amp;lt;code&amp;gt;fedora&amp;lt;/code&amp;gt;: Pull Fedora base image from Docker Hub&lt;br /&gt;
* &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;: Command to run inside container (start bash shell)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker downloads Fedora base image (if not cached)&lt;br /&gt;
# Creates container from image&lt;br /&gt;
# Starts bash inside the container&lt;br /&gt;
# Attaches your terminal to that bash session&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;fedora:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/fedora&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for fedora:latest&lt;br /&gt;
[root@a1b2c3d4e5f6 /]#&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe the prompt change:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Before: &amp;lt;code&amp;gt;user@hostname:~$&amp;lt;/code&amp;gt; (your normal shell)&lt;br /&gt;
* After: &amp;lt;code&amp;gt;[root@a1b2c3d4e5f6 /]#&amp;lt;/code&amp;gt; (inside container)&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the Fedora container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Explore the filesystem (Mount Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the operating system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Fedora Linux&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;39 (Container Image)&amp;amp;quot;&lt;br /&gt;
ID=fedora&lt;br /&gt;
VERSION_ID=39&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Open a &amp;#039;&amp;#039;&amp;#039;new terminal&amp;#039;&amp;#039;&amp;#039; on your host (don&amp;#039;t close the container terminal) and run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output on host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Ubuntu&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;22.04.3 LTS (Jammy Jellyfish)&amp;amp;quot;&lt;br /&gt;
ID=ubuntu&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two different operating systems on the same machine!&amp;#039;&amp;#039;&amp;#039; This is mount namespace isolation—the container has its own root filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Back in the container&amp;#039;&amp;#039;&amp;#039;, explore the filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Fedora&amp;#039;s files, not your host&amp;#039;s Ubuntu files. They&amp;#039;re completely different directory trees.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try to use Ubuntu&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;apt update&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bash: apt: command not found&amp;lt;/pre&amp;gt;&lt;br /&gt;
apt doesn&amp;#039;t exist in Fedora! Fedora uses a different package manager.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try Fedora&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4.18.2&lt;br /&gt;
  Installed: dnf-0:4.18.2-1.fc39.noarch&lt;br /&gt;
  ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
dnf exists because we&amp;#039;re in a Fedora environment.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Install a package:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf install -y nano&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This works! We can install packages just like on a real Fedora system.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 2, you learned about the filesystem hierarchy (&amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt;, etc.). Mount namespaces let the container have completely different contents at these paths. The container&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; is different from the host&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Examine process isolation (PID Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container, check running processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND&lt;br /&gt;
root           1  0.0  0.0  12345  2345 pts/0    Ss   10:45   0:00 bash&lt;br /&gt;
root          67  0.0  0.0  44567  3456 pts/0    R+   10:47   0:00 ps aux&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Only two processes visible!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* PID 1: bash (the container&amp;#039;s init process)&lt;br /&gt;
* PID 67: ps command we just ran&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host&amp;#039;&amp;#039;&amp;#039; (in your other terminal):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; 200+ processes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The container cannot see the host&amp;#039;s processes!&amp;#039;&amp;#039;&amp;#039; This is PID namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the container&amp;#039;s perspective, bash is PID 1&amp;#039;&amp;#039;&amp;#039; (like systemd is PID 1 on a normal Linux system).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the host&amp;#039;s perspective, that same bash process has a different PID&amp;#039;&amp;#039;&amp;#039; (e.g., 12345).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about PIDs and the process hierarchy. PID namespaces create separate process hierarchies—the container has its own process tree starting from PID 1.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Check hostname (UTS Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the container ID (first 12 characters of the full container ID).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;your-hostname.example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Different hostnames! This is UTS namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Examine network configuration (Network Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&lt;br /&gt;
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
       valid_lft forever preferred_lft forever&lt;br /&gt;
       &lt;br /&gt;
17: eth0@if18: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP group default &lt;br /&gt;
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&lt;br /&gt;
       valid_lft forever preferred_lft forever&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Container&amp;#039;s loopback interface (127.0.0.1)&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if18&amp;lt;/code&amp;gt;: Container&amp;#039;s network interface (part of veth pair)&lt;br /&gt;
** &amp;lt;code&amp;gt;@if18&amp;lt;/code&amp;gt;: Indicates this interface is paired with interface index 18 (on the host)&lt;br /&gt;
** IP address: 172.17.0.2 (from docker0 bridge subnet)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show | grep &amp;quot;172.17&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the docker0 bridge has IP 172.17.0.1, and you might see veth interfaces for containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you built manually!&lt;br /&gt;
&lt;br /&gt;
* docker0 bridge ≈ br-lab from Lab 7&lt;br /&gt;
* Container&amp;#039;s eth0 ≈ v-client from Lab 7&lt;br /&gt;
* veth pair connects container to bridge ≈ Lab 7&amp;#039;s veth pair architecture&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test internet connectivity from container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Ping succeeds!&lt;br /&gt;
&lt;br /&gt;
This works because:&lt;br /&gt;
&lt;br /&gt;
# Container has default route to 172.17.0.1 (docker0 bridge)&lt;br /&gt;
# Host has IP forwarding enabled&lt;br /&gt;
# Host has NAT rule (MASQUERADE) for 172.17.0.0/16&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is identical to what you configured in Lab 7!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Try to see host&amp;#039;s processes (will fail)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep systemd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; No systemd processes visible.&lt;br /&gt;
&lt;br /&gt;
You cannot see the host&amp;#039;s processes from inside the container (PID namespace isolation).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Exit the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you exit bash (PID 1 in the container), the container stops automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify the container stopped:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE    COMMAND   CREATED         STATUS                     NAMES&lt;br /&gt;
a1b2c3d4e5f6   fedora   &amp;amp;quot;bash&amp;amp;quot;    5 minutes ago   Exited (0) 10 seconds ago  fedora-explore&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The container still exists (STATUS: Exited), but it&amp;#039;s not running. You can restart it with &amp;lt;code&amp;gt;docker start fedora-explore&amp;lt;/code&amp;gt; if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm fedora-explore&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable C ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Fedora)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Ubuntu or your host OS)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; (showing minimal processes, bash as PID 1)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux | wc -l&amp;lt;/code&amp;gt; (showing many more processes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing container ID)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing host&amp;#039;s hostname)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; (showing eth0 with 172.17.0.x address)&lt;br /&gt;
# Brief explanation (4-5 sentences): What do these differences demonstrate about namespace isolation? How does this relate to what you learned in Labs 2, 3, and 7?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-persistent-storage-with-caddy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Persistent Storage with Caddy ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run Caddy web server with persistent configuration and content using bind mounts, demonstrating that data can survive container removal and be shared between host and container.&lt;br /&gt;
&lt;br /&gt;
Caddy is the web server you&amp;#039;ve been using throughout Lab 9. We&amp;#039;ll run it in a container and configure it using files from the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-basic-caddy-container&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Basic Caddy Container ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create directory structure on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-caddy/{site,data,config}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Directory purposes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;site&amp;lt;/code&amp;gt;: Website content (HTML files)&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data directory (certificates, storage)&lt;br /&gt;
* &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt;: Caddy configuration (Caddyfile)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Create a simple website&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;This is running in a Docker container.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The file you&amp;#039;re viewing is mounted from the host filesystem.&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;Changes made on the host appear instantly in the container!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Create Caddyfile configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/config/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Caddyfile explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;:80&amp;lt;/code&amp;gt;: Listen on port 80 (inside container)&lt;br /&gt;
* &amp;lt;code&amp;gt;root * /usr/share/caddy&amp;lt;/code&amp;gt;: Serve files from this directory&lt;br /&gt;
* &amp;lt;code&amp;gt;file_server&amp;lt;/code&amp;gt;: Enable static file serving&lt;br /&gt;
* &amp;lt;code&amp;gt;log&amp;lt;/code&amp;gt;: Send access logs to stdout (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Run Caddy container with volume mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-persistent \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Breaking down the command:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode (run in background)&lt;br /&gt;
* &amp;lt;code&amp;gt;--name caddy-persistent&amp;lt;/code&amp;gt;: Give container a memorable name&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: Port mapping&lt;br /&gt;
** Host port 8080 → Container port 80&lt;br /&gt;
** This is NAT (DNAT specifically) from Lab 7!&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/site:/usr/share/caddy&amp;lt;/code&amp;gt;: Bind mount&lt;br /&gt;
** Host directory &amp;lt;code&amp;gt;~/lab10-caddy/site&amp;lt;/code&amp;gt; appears at &amp;lt;code&amp;gt;/usr/share/caddy&amp;lt;/code&amp;gt; inside container&lt;br /&gt;
** Bidirectional: changes on either side are visible on both sides&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/data:/data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data storage&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/config:/etc/caddy&amp;lt;/code&amp;gt;: Caddy&amp;#039;s configuration&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: Image to use&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;caddy:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/caddy&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for caddy:latest&lt;br /&gt;
f8e9c7b6d5a4e3b2c1f7d8a9e6b5c4d3a2f1e8d7c6b5a4e3d2c1b9f8&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Verify container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS                  NAMES&lt;br /&gt;
f8e9c7b6d5a4   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    0.0.0.0:8080-&amp;amp;gt;80/tcp   caddy-persistent&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note the PORTS column:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;0.0.0.0:8080-&amp;amp;gt;80/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This means:&lt;br /&gt;
&lt;br /&gt;
* Listen on all host interfaces (&amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Host port 8080 maps to container port 80&lt;br /&gt;
* Protocol: TCP&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test the web server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Your HTML page!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/html&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also open &amp;lt;code&amp;gt;http://localhost:8080&amp;lt;/code&amp;gt; in a web browser on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: View Caddy logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.456,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;using provided configuration&amp;amp;quot;,...}&lt;br /&gt;
{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.789,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;serving initial configuration&amp;amp;quot;}&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Caddy&amp;#039;s startup logs. When you access the website, you&amp;#039;ll see access logs here too.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-mounts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Mounts ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Inspect the container&amp;#039;s mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/site&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/usr/share/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/config&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/etc/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Field explanations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;bind&amp;amp;quot; (bind mount, not Docker-managed volume)&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host filesystem path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Path inside container&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: true (read-write), false would be read-only&lt;br /&gt;
* &amp;lt;code&amp;gt;Propagation&amp;lt;/code&amp;gt;: How mount events propagate (rprivate = don&amp;#039;t propagate to other mounts)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: View from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent df -h | grep caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows mounted filesystems inside the container containing &amp;amp;quot;caddy&amp;amp;quot; in the path.&lt;br /&gt;
&lt;br /&gt;
You can also list the files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent ls -la /usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 12&lt;br /&gt;
drwxr-xr-x 2 root root 4096 Dec 12 10:50 .&lt;br /&gt;
drwxr-xr-x 1 root root 4096 Dec 12 10:50 ..&lt;br /&gt;
-rw-r--r-- 1 root root  687 Dec 12 10:50 index.html&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the same &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; you created on the host!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-test-persistence&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Test Persistence ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Modify the file on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without stopping the container&amp;#039;&amp;#039;&amp;#039;, edit the HTML file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt;&amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;🎉 This was added from the host!&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The container is still running, and changes appear instantly.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Verify the change appears in the container immediately&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; The new section appears!&lt;br /&gt;
&lt;br /&gt;
You didn&amp;#039;t restart the container, yet the content changed. This demonstrates &amp;#039;&amp;#039;&amp;#039;bidirectional bind mount synchronization&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Create a new file from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent bash -c &amp;quot;echo &amp;#039;&amp;lt;h2&amp;gt;Created from container&amp;lt;/h2&amp;gt;&amp;#039; &amp;gt; /usr/share/caddy/test.html&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify the file appears on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwxr-xr-x 2 youruser youruser 4096 Dec 12 11:00 .&lt;br /&gt;
drwxr-xr-x 5 youruser youruser 4096 Dec 12 10:45 ..&lt;br /&gt;
-rw-r--r-- 1 youruser youruser  687 Dec 12 10:50 index.html&lt;br /&gt;
-rw-r--r-- 1 root     root       34 Dec 12 11:00 test.html  ← New file!&amp;lt;/pre&amp;gt;&lt;br /&gt;
The file exists on the host! Changes flow both directions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The file is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; because the process inside the container runs as root. This is one of the complexities of bind mounts—UID/GID mapping between host and container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Stop and remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-persistent&lt;br /&gt;
docker rm caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Verify files still exist on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All files still there!&lt;br /&gt;
&lt;br /&gt;
The files survive because they&amp;#039;re stored on the host filesystem, not in the container&amp;#039;s ephemeral writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start a NEW container with the same bind mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-new \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Verify persistence&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All your previous content is still there!&lt;br /&gt;
&lt;br /&gt;
The website works immediately because all the content and configuration was stored on the host, not inside the old container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Compare to ephemeral storage&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s demonstrate what happens without volumes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container without volumes&lt;br /&gt;
docker run -d --name caddy-temp caddy&lt;br /&gt;
&lt;br /&gt;
# Create file inside container&lt;br /&gt;
docker exec caddy-temp bash -c &amp;quot;echo &amp;#039;temporary&amp;#039; &amp;gt; /tmp/temp.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Verify file exists&lt;br /&gt;
docker exec caddy-temp cat /tmp/temp.txt&lt;br /&gt;
# Output: temporary&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop caddy-temp&lt;br /&gt;
docker rm caddy-temp&lt;br /&gt;
&lt;br /&gt;
# Try to access the file in a new container&lt;br /&gt;
docker run -d --name caddy-temp2 caddy&lt;br /&gt;
docker exec caddy-temp2 cat /tmp/temp.txt&lt;br /&gt;
# Output: cat: /tmp/temp.txt: No such file or directory&lt;br /&gt;
&lt;br /&gt;
# The file is GONE because it was in the ephemeral layer&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-new&lt;br /&gt;
docker rm caddy-new&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The files in &amp;lt;code&amp;gt;~/lab10-caddy/&amp;lt;/code&amp;gt; remain intact on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# What Docker essentially does:&lt;br /&gt;
mount --bind ~/lab10-caddy/site /var/lib/docker/overlay2/.../merged/usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable D ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing your custom HTML (initial version)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039;&amp;lt;/code&amp;gt; (formatted with python3 -m json.tool)&lt;br /&gt;
# After modifying &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; on the host, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing the new content (without restarting container)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/code&amp;gt; showing both &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;test.html&amp;lt;/code&amp;gt; created from inside the container&lt;br /&gt;
# After removing the original container and starting &amp;lt;code&amp;gt;caddy-new&amp;lt;/code&amp;gt;, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; demonstrating that content persisted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-multi-container-infrastructure-with-networking-and-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Multi-Container Infrastructure with Networking and Resource Limits ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Build a complete three-tier architecture with:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Purple&amp;#039;&amp;#039;&amp;#039; container acting as reverse proxy (routes requests based on URL path)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Red&amp;#039;&amp;#039;&amp;#039; container serving backend content (memory-limited)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blue&amp;#039;&amp;#039;&amp;#039; container serving backend content (CPU-limited)&lt;br /&gt;
&lt;br /&gt;
This exercise synthesizes everything from Labs 7-9:&lt;br /&gt;
&lt;br /&gt;
* Custom networks (Lab 7 bridges)&lt;br /&gt;
* Container-to-container communication (Lab 7 veth pairs and routing)&lt;br /&gt;
* Reverse proxy with path-based routing (Lab 9 Caddy configuration)&lt;br /&gt;
* Resource limits (cgroups)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-create-a-custom-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Create a Custom Network ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a custom bridge network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.10.0.0/24 labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a new bridge (just like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7) with subnet 10.10.0.0/24.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the network ID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Inspect the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (abbreviated):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;labnet&amp;quot;,&lt;br /&gt;
        &amp;quot;Id&amp;quot;: &amp;quot;a1b2c3d4e5f6...&amp;quot;,&lt;br /&gt;
        &amp;quot;Driver&amp;quot;: &amp;quot;bridge&amp;quot;,&lt;br /&gt;
        &amp;quot;IPAM&amp;quot;: {&lt;br /&gt;
            &amp;quot;Config&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;Subnet&amp;quot;: &amp;quot;10.10.0.0/24&amp;quot;,&lt;br /&gt;
                    &amp;quot;Gateway&amp;quot;: &amp;quot;10.10.0.1&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;Containers&amp;quot;: {},&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key observations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Driver: &amp;amp;quot;bridge&amp;amp;quot;&amp;lt;/code&amp;gt;: Uses bridge networking (Layer 2 switching)&lt;br /&gt;
* &amp;lt;code&amp;gt;Subnet: &amp;amp;quot;10.10.0.0/24&amp;amp;quot;&amp;lt;/code&amp;gt;: Our specified subnet&lt;br /&gt;
* &amp;lt;code&amp;gt;Gateway: &amp;amp;quot;10.10.0.1&amp;amp;quot;&amp;lt;/code&amp;gt;: Docker automatically assigns .1 as gateway&lt;br /&gt;
* &amp;lt;code&amp;gt;Containers: {}&amp;lt;/code&amp;gt;: No containers connected yet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Verify bridge creation on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;5: br-a1b2c3d4e5f6: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default&amp;lt;/pre&amp;gt;&lt;br /&gt;
Docker created a new bridge interface! The name includes the network ID prefix.&lt;br /&gt;
&lt;br /&gt;
You can also use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;brctl show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bridge name          bridge id          STP enabled    interfaces&lt;br /&gt;
br-a1b2c3d4e5f6      8000.0242a1b2c3d4  no&lt;br /&gt;
docker0              8000.0242f7a8b9c0  no&amp;lt;/pre&amp;gt;&lt;br /&gt;
Two bridges:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;: Default Docker bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;br-a1b2c3d4e5f6&amp;lt;/code&amp;gt;: Our custom labnet bridge&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you did with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add br-lab type bridge&lt;br /&gt;
sudo ip link set br-lab up&lt;br /&gt;
sudo ip addr add 10.0.0.1/24 dev br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker automated it!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-create-backend-services-red-and-blue&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Create Backend Services (Red and Blue) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Create content directories&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-multicontainer/{red,blue,purple}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Create Red service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Red Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; Memory capped at 256MB&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is memory-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Create Red Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Create Blue service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Blue Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Blue Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Blue&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.3&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; CPU capped at 0.5 cores&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is CPU-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Create Blue Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Start Red container with memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name red \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.2 \&lt;br /&gt;
  --memory=256m \&lt;br /&gt;
  --memory-swap=256m \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--network labnet&amp;lt;/code&amp;gt;: Connect to our custom network (not default docker0)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.2&amp;lt;/code&amp;gt;: Assign static IP address (just like &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt; in Lab 7!)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory=256m&amp;lt;/code&amp;gt;: cgroup memory limit (hard cap: 256MB RAM)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory-swap=256m&amp;lt;/code&amp;gt;: Total memory+swap limit (setting equal to memory means no swap)&lt;br /&gt;
** If swap was 512m, container could use 256MB RAM + 256MB swap&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/etc/caddy&amp;lt;/code&amp;gt;: Mount Caddyfile&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/usr/share/caddy&amp;lt;/code&amp;gt;: Mount website content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Container ID&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Verify Red is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps --filter &amp;quot;name=red&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Test Red directly&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Since Red has IP 10.10.0.2, let&amp;#039;s verify we can reach it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From host (won&amp;#039;t work directly because we&amp;#039;re not in labnet)&lt;br /&gt;
# But we can use docker exec from another container&lt;br /&gt;
&lt;br /&gt;
# Quick test: use docker exec to reach Red from Red itself&lt;br /&gt;
docker exec red curl -s http://localhost | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Red Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Start Blue container with CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name blue \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.3 \&lt;br /&gt;
  --cpus=0.5 \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--cpus=0.5&amp;lt;/code&amp;gt;: cgroup CPU limit (maximum 50% of one CPU core)&lt;br /&gt;
** If CPU-intensive work is attempted, kernel will throttle it&lt;br /&gt;
* Other flags same as Red&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify both containers are running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS   NAMES&lt;br /&gt;
a1b2c3d4e5f6   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 20 seconds ago   Up 18 seconds   80/tcp  red&lt;br /&gt;
b7c8d9e0f1a2   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    80/tcp  blue&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Test container-to-container communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, ping Blue&lt;br /&gt;
docker exec red ping -c 2 10.10.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, curl Blue&amp;#039;s website&lt;br /&gt;
docker exec red curl -s http://10.10.0.3 | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Containers on the same custom network can communicate directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-create-reverse-proxy-purple&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Create Reverse Proxy (Purple) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Create Purple&amp;#039;s Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial piece—routing based on URL path (from Lab 9!)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/purple/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    # Health check endpoint&lt;br /&gt;
    handle /health {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - OK&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Root path&lt;br /&gt;
    handle / {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /red/* to red container (strip /red prefix)&lt;br /&gt;
    handle_path /red/* {&lt;br /&gt;
        reverse_proxy red:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /blue/* to blue container (strip /blue prefix)&lt;br /&gt;
    handle_path /blue/* {&lt;br /&gt;
        reverse_proxy blue:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Logging&lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key points:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;handle_path /red/*&amp;lt;/code&amp;gt;: Matches any path starting with &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;handle_path&amp;lt;/code&amp;gt; strips the prefix before forwarding&lt;br /&gt;
** Client requests &amp;lt;code&amp;gt;/red/index.html&amp;lt;/code&amp;gt; → Backend receives &amp;lt;code&amp;gt;/index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reverse_proxy red:80&amp;lt;/code&amp;gt;: Forward to container named &amp;amp;quot;red&amp;amp;quot; on port 80&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Using hostname &amp;amp;quot;red&amp;amp;quot;, not IP!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** Docker&amp;#039;s embedded DNS resolves &amp;amp;quot;red&amp;amp;quot; to 10.10.0.2&lt;br /&gt;
* Same for blue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start Purple reverse proxy&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name purple \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.10 \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-multicontainer/purple:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.10&amp;lt;/code&amp;gt;: Purple gets IP 10.10.0.10 (different from Red/Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: &amp;#039;&amp;#039;&amp;#039;Port mapping&amp;#039;&amp;#039;&amp;#039; (host port 8080 → container port 80)&lt;br /&gt;
** This is NAT (DNAT) from Lab 7!&lt;br /&gt;
** Traffic to &amp;lt;code&amp;gt;localhost:8080&amp;lt;/code&amp;gt; on host gets forwarded to &amp;lt;code&amp;gt;10.10.0.10:80&amp;lt;/code&amp;gt; in container&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-testing-the-infrastructure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: Testing the Infrastructure ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Test health check&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/health&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This request:&lt;br /&gt;
&lt;br /&gt;
# Reaches host&amp;#039;s port 8080&lt;br /&gt;
# Docker&amp;#039;s DNAT rule forwards to Purple (10.10.0.10:80)&lt;br /&gt;
# Purple&amp;#039;s Caddy responds directly (no backend needed for /health)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Test root path&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Test routing to Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Red service HTML (with red background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Red Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Your curl → Host port 8080&lt;br /&gt;
# DNAT → Purple (10.10.0.10:80)&lt;br /&gt;
# Purple sees path &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;, strips &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt;, forwards &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;red:80&amp;lt;/code&amp;gt;&lt;br /&gt;
# Purple&amp;#039;s DNS resolves &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; to 10.10.0.2&lt;br /&gt;
# Request goes to Red (10.10.0.2:80)&lt;br /&gt;
# Red&amp;#039;s Caddy serves &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
# Response travels back through Purple to your curl&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 20: Test routing to Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Blue service HTML (with blue background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 21: Test with verbose output to see headers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -v http://localhost:8080/red/ 2&amp;gt;&amp;amp;1 | grep -A10 &amp;quot;&amp;lt; HTTP&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt; HTTP/1.1 200 OK&lt;br /&gt;
&amp;amp;lt; Content-Type: text/html; charset=utf-8&lt;br /&gt;
&amp;amp;lt; Server: Caddy&lt;br /&gt;
&amp;amp;lt; Date: Thu, 12 Dec 2024 11:30:00 GMT&lt;br /&gt;
&amp;amp;lt; Content-Length: 687&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;Server: Caddy&amp;lt;/code&amp;gt; header (from Purple, acting as proxy)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 22: Test in browser&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Open in your web browser:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/red/&amp;lt;/code&amp;gt; → Should see red-styled page&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/blue/&amp;lt;/code&amp;gt; → Should see blue-styled page&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-inspect-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Inspect Resource Limits ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 23: Check Red&amp;#039;s memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 256 MB in bytes (256 × 1024 × 1024 = 268435456).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 24: Check Blue&amp;#039;s CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;500000000&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 0.5 CPU cores in nanocpus (0.5 × 10^9 = 500,000,000).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 25: View cgroup settings from the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get Red&amp;#039;s main process PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;RED_PID=$(docker inspect red --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
echo &amp;quot;Red&amp;#039;s PID: $RED_PID&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
View memory limit in cgroup filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /sys/fs/cgroup/memory/docker/$RED_PID/memory.limit_in_bytes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
View Blue&amp;#039;s CPU quota:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;BLUE_PID=$(docker inspect blue --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/$BLUE_PID/cpu.cfs_quota_us&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;50000&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt;: 100000 (default, 100ms period)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt;: 50000 (50ms out of 100ms = 50% = 0.5 CPUs)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to kernel concepts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These cgroup files in &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; are how the kernel enforces resource limits. Docker configures these files, and the kernel does the actual enforcement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-network-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Network Inspection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 26: Inspect labnet network showing all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet --format=&amp;#039;{{json .Containers}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;a1b2c3d4e5f6...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:02&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.2/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;b7c8d9e0f1a2...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:03&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.3/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;f8e9c7b6d5a4...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;purple&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:0a&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.10/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All three containers are on the labnet network with their assigned IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 27: Test DNS resolution between containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Purple, resolve &amp;quot;red&amp;quot; hostname&lt;br /&gt;
docker exec purple nslookup red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Server:         127.0.0.11&lt;br /&gt;
Address:        127.0.0.11#53&lt;br /&gt;
&lt;br /&gt;
Non-authoritative answer:&lt;br /&gt;
Name:   red&lt;br /&gt;
Address: 10.10.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;127.0.0.11&amp;lt;/code&amp;gt;: Docker&amp;#039;s embedded DNS server (listening inside each container)&lt;br /&gt;
* DNS resolves container name &amp;amp;quot;red&amp;amp;quot; to IP 10.10.0.2&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 28: Test connectivity using hostnames&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple pings Red by hostname&lt;br /&gt;
docker exec purple ping -c 2 red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple curls Blue by hostname&lt;br /&gt;
docker exec purple curl -s http://blue | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-7-architecture-visualization&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 7: Architecture Visualization ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The architecture you built:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                    ┌─────────────────┐&lt;br /&gt;
                    │   Your Host     │&lt;br /&gt;
                    │  (Port 8080)    │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                     Port Mapping (NAT)&lt;br /&gt;
                      8080 → 80&lt;br /&gt;
                             │&lt;br /&gt;
                    ┌────────▼────────┐&lt;br /&gt;
                    │  Purple Proxy   │&lt;br /&gt;
                    │  10.10.0.10:80  │&lt;br /&gt;
                    │  (Caddy)        │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                   Docker Network: labnet&lt;br /&gt;
                   (Bridge: br-a1b2c3d4e5f6)&lt;br /&gt;
                    10.10.0.0/24&lt;br /&gt;
                             │&lt;br /&gt;
                   ┌─────────┴──────────┐&lt;br /&gt;
                   │                    │&lt;br /&gt;
          ┌────────▼────────┐  ┌───────▼─────────┐&lt;br /&gt;
          │   Red Service   │  │  Blue Service   │&lt;br /&gt;
          │   10.10.0.2:80  │  │  10.10.0.3:80   │&lt;br /&gt;
          │   (Caddy)       │  │  (Caddy)        │&lt;br /&gt;
          │  [mem: 256MB]   │  │  [cpu: 0.5]     │&lt;br /&gt;
          └─────────────────┘  └─────────────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request flow for &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Curl → localhost:8080 (your host)&lt;br /&gt;
2. Host&amp;#039;s iptables DNAT rule → 10.10.0.10:80 (Purple)&lt;br /&gt;
3. Purple receives request to /red/&lt;br /&gt;
4. Purple&amp;#039;s Caddy config: handle_path /red/* → reverse_proxy red:80&lt;br /&gt;
5. Purple strips /red prefix → request becomes /&lt;br /&gt;
6. Purple&amp;#039;s DNS resolves &amp;amp;quot;red&amp;amp;quot; → 10.10.0.2&lt;br /&gt;
7. Purple → Red (10.10.0.2:80) GET /&lt;br /&gt;
8. Red&amp;#039;s Caddy serves /usr/share/caddy/index.html&lt;br /&gt;
9. Response: Red → Purple → Host → Curl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Compare to Lab 7 and Lab 9:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 7&amp;#039;&amp;#039;&amp;#039;: You manually created bridges, veth pairs, assigned IPs, configured routes, set up NAT&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 9&amp;#039;&amp;#039;&amp;#039;: You manually configured Caddy reverse proxy with path-based routing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;This exercise&amp;#039;&amp;#039;&amp;#039;: Docker automated all the networking, you just specified what you wanted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-8-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 8: Cleanup ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 29: Stop all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 30: Remove containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 31: Remove the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network rm labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 32: Verify cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&lt;br /&gt;
docker network ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Only default networks (bridge, host, none) should remain.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 33: Verify host bridge removed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The custom bridge (br-a1b2c3d4e5f6) should be gone. Only docker0 remains.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-e&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable E ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker network inspect labnet&amp;lt;/code&amp;gt; showing all three containers with their IPs (before running curl tests)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/health&amp;lt;/code&amp;gt; (health check response)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt; (Red service HTML) with visible red-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/blue/&amp;lt;/code&amp;gt; (Blue service HTML) with visible blue-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/code&amp;gt; showing memory limit in bytes&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/code&amp;gt; showing CPU limit in nanocpus&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker exec purple nslookup red&amp;lt;/code&amp;gt; showing DNS resolution&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-docker-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Docker Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for Docker commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container (create + start)&lt;br /&gt;
docker run [OPTIONS] IMAGE [COMMAND]&lt;br /&gt;
docker run -d nginx                          # Detached (background)&lt;br /&gt;
docker run -it ubuntu bash                   # Interactive with terminal&lt;br /&gt;
docker run --name mycontainer nginx          # Assign name&lt;br /&gt;
&lt;br /&gt;
# List containers&lt;br /&gt;
docker ps                                    # Running only&lt;br /&gt;
docker ps -a                                 # All (including stopped)&lt;br /&gt;
docker ps -q                                 # Show only IDs (quiet)&lt;br /&gt;
&lt;br /&gt;
# Start stopped container&lt;br /&gt;
docker start CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Stop running container&lt;br /&gt;
docker stop CONTAINER                        # Graceful (SIGTERM)&lt;br /&gt;
docker kill CONTAINER                        # Forceful (SIGKILL)&lt;br /&gt;
&lt;br /&gt;
# Restart container&lt;br /&gt;
docker restart CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove container&lt;br /&gt;
docker rm CONTAINER                          # Must be stopped first&lt;br /&gt;
docker rm -f CONTAINER                       # Force remove (stop + remove)&lt;br /&gt;
&lt;br /&gt;
# Execute command in running container&lt;br /&gt;
docker exec CONTAINER COMMAND&lt;br /&gt;
docker exec -it CONTAINER bash               # Interactive shell&lt;br /&gt;
&lt;br /&gt;
# View container logs&lt;br /&gt;
docker logs CONTAINER&lt;br /&gt;
docker logs -f CONTAINER                     # Follow (tail -f style)&lt;br /&gt;
&lt;br /&gt;
# Inspect container (detailed JSON info)&lt;br /&gt;
docker inspect CONTAINER&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.Status}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;image-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Image Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List images&lt;br /&gt;
docker images&lt;br /&gt;
docker image ls&lt;br /&gt;
&lt;br /&gt;
# Pull image from registry&lt;br /&gt;
docker pull IMAGE[:TAG]&lt;br /&gt;
docker pull nginx                            # Latest tag (default)&lt;br /&gt;
docker pull nginx:1.21                       # Specific tag&lt;br /&gt;
&lt;br /&gt;
# Remove image&lt;br /&gt;
docker rmi IMAGE&lt;br /&gt;
docker image rm IMAGE&lt;br /&gt;
&lt;br /&gt;
# View image layers&lt;br /&gt;
docker history IMAGE&lt;br /&gt;
&lt;br /&gt;
# Remove unused images&lt;br /&gt;
docker image prune                           # Dangling images&lt;br /&gt;
docker image prune -a                        # All unused images&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List networks&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Create network&lt;br /&gt;
docker network create NETWORK&lt;br /&gt;
docker network create --subnet=10.20.0.0/24 mynet&lt;br /&gt;
&lt;br /&gt;
# Inspect network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Connect container to network&lt;br /&gt;
docker network connect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Disconnect container from network&lt;br /&gt;
docker network disconnect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove network&lt;br /&gt;
docker network rm NETWORK&lt;br /&gt;
&lt;br /&gt;
# Remove unused networks&lt;br /&gt;
docker network prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;volume-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volume Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Create volume&lt;br /&gt;
docker volume create VOLUME&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove volume&lt;br /&gt;
docker volume rm VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# CPU limits&lt;br /&gt;
--cpus=1.5                                   # Limit to 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512                             # Relative priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1                            # Pin to specific cores&lt;br /&gt;
&lt;br /&gt;
# Memory limits&lt;br /&gt;
--memory=512m                                # Hard limit: 512 MB RAM&lt;br /&gt;
--memory=2g                                  # Hard limit: 2 GB RAM&lt;br /&gt;
--memory-swap=1g                             # Total memory+swap&lt;br /&gt;
--memory-reservation=256m                    # Soft limit&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb             # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb            # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100                             # Max 100 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;inspection-and-debugging&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Inspection and Debugging ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View resource usage stats&lt;br /&gt;
docker stats&lt;br /&gt;
docker stats CONTAINER                       # Specific container&lt;br /&gt;
&lt;br /&gt;
# View processes in container&lt;br /&gt;
docker top CONTAINER&lt;br /&gt;
&lt;br /&gt;
# View port mappings&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Copy files between host and container&lt;br /&gt;
docker cp CONTAINER:/path/to/file ./file    # Container → Host&lt;br /&gt;
docker cp ./file CONTAINER:/path/to/file    # Host → Container&lt;br /&gt;
&lt;br /&gt;
# Stream events from Docker daemon&lt;br /&gt;
docker events&lt;br /&gt;
&lt;br /&gt;
# Show disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# System-wide cleanup&lt;br /&gt;
docker system prune                          # Remove unused objects&lt;br /&gt;
docker system prune -a                       # Remove all unused objects&lt;br /&gt;
docker system prune -a --volumes            # Include volumes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;installation-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Installation Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt; fails after installation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check if Docker daemon is running&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# If not running, start it&lt;br /&gt;
sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;Cannot connect to the Docker daemon&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Docker daemon not running or permission issue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check daemon status&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# Check if your user is in docker group&lt;br /&gt;
groups | grep docker&lt;br /&gt;
&lt;br /&gt;
# If not, add user and log out/in&lt;br /&gt;
sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;permission-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Permission Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;permission denied&amp;amp;quot; when running docker commands&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Option 1: Add user to docker group (permanent)&lt;br /&gt;
sudo usermod -aG docker $USER&lt;br /&gt;
# Then log out and log back in&lt;br /&gt;
&lt;br /&gt;
# Option 2: Use sudo (temporary)&lt;br /&gt;
sudo docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Files created by container are owned by root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Container runs as root (UID 0) by default&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container as specific user&lt;br /&gt;
docker run --user $(id -u):$(id -g) IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use user namespaces (advanced)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container can&amp;#039;t access internet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test from container&lt;br /&gt;
docker exec CONTAINER ping -c 2 8.8.8.8&lt;br /&gt;
&lt;br /&gt;
# Check Docker&amp;#039;s NAT rules&lt;br /&gt;
sudo iptables -t nat -L -n | grep docker&lt;br /&gt;
&lt;br /&gt;
# Check IP forwarding&lt;br /&gt;
sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enable IP forwarding if disabled&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Restart Docker daemon&lt;br /&gt;
sudo systemctl restart docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Containers on custom network can&amp;#039;t communicate&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check network exists&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Check containers are on same network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Test connectivity&lt;br /&gt;
docker exec CONTAINER1 ping CONTAINER2_IP&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Ensure both containers on same network&lt;br /&gt;
docker network connect NETWORK CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Port mapping not working (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check port is actually mapped&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Check if host port is already in use&lt;br /&gt;
sudo ss -tulpn | grep :8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# If port in use, use different host port&lt;br /&gt;
docker run -p 8081:80 nginx&lt;br /&gt;
&lt;br /&gt;
# Or stop the conflicting process&lt;br /&gt;
sudo fuser -k 8080/tcp&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;storage-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Storage Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;No space left on device&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check Docker disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# Check host disk space&lt;br /&gt;
df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Remove unused objects&lt;br /&gt;
docker system prune -a&lt;br /&gt;
&lt;br /&gt;
# Remove specific old images&lt;br /&gt;
docker images&lt;br /&gt;
docker rmi IMAGE_ID&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Changes in bind mount not visible in container&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Path doesn&amp;#039;t exist or wrong path specified&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Verify path exists on host&lt;br /&gt;
ls -la /path/to/host/directory&lt;br /&gt;
&lt;br /&gt;
# Use absolute paths&lt;br /&gt;
docker run -v /absolute/path:/container/path IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use $PWD for current directory&lt;br /&gt;
docker run -v $PWD/relative/path:/container/path IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-limit-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Limit Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container killed unexpectedly&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check container exit code&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.ExitCode}}&amp;#039;&lt;br /&gt;
# Exit code 137 = killed (often OOM)&lt;br /&gt;
&lt;br /&gt;
# Check logs&lt;br /&gt;
docker logs CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Increase memory limit&lt;br /&gt;
docker run --memory=1g IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or run without memory limit (default)&lt;br /&gt;
docker run IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container using too much CPU&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Limit CPU usage&lt;br /&gt;
docker run --cpus=0.5 IMAGE&lt;br /&gt;
&lt;br /&gt;
# Check what&amp;#039;s consuming CPU&lt;br /&gt;
docker exec CONTAINER top&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;next-steps-dockerfile-and-docker-compose&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Next Steps: Dockerfile and Docker Compose ==&lt;br /&gt;
&lt;br /&gt;
Congratulations! You&amp;#039;ve mastered the fundamental OS concepts behind containers:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; provide isolation (network, PID, mount, UTS, IPC, user, cgroup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039; enforce resource limits (CPU, memory, I/O)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;OverlayFS&amp;#039;&amp;#039;&amp;#039; provides efficient layered filesystems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bridges and veth pairs&amp;#039;&amp;#039;&amp;#039; connect containers (same as Lab 7!)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volumes&amp;#039;&amp;#039;&amp;#039; persist data beyond container lifecycles&lt;br /&gt;
&lt;br /&gt;
However, you&amp;#039;ve been using &amp;#039;&amp;#039;&amp;#039;pre-built images&amp;#039;&amp;#039;&amp;#039; and running containers with long &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands. In production environments, you need:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom images&amp;#039;&amp;#039;&amp;#039; tailored to your applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducible builds&amp;#039;&amp;#039;&amp;#039; that can be version-controlled&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-container orchestration&amp;#039;&amp;#039;&amp;#039; with coordinated startup and networking&lt;br /&gt;
&lt;br /&gt;
This is where &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039; come in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dockerfile-building-custom-images&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Dockerfile: Building Custom Images ===&lt;br /&gt;
&lt;br /&gt;
Instead of starting from a base image and manually installing packages, you define your application&amp;#039;s environment in a &amp;lt;code&amp;gt;Dockerfile&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dockerfile&amp;quot;&amp;gt;# Start from Ubuntu base image&lt;br /&gt;
FROM ubuntu:22.04&lt;br /&gt;
&lt;br /&gt;
# Install dependencies&lt;br /&gt;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;br /&gt;
    python3 \&lt;br /&gt;
    python3-pip \&lt;br /&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br /&gt;
&lt;br /&gt;
# Copy application code&lt;br /&gt;
COPY app.py /app/&lt;br /&gt;
COPY requirements.txt /app/&lt;br /&gt;
&lt;br /&gt;
# Install Python dependencies&lt;br /&gt;
WORKDIR /app&lt;br /&gt;
RUN pip3 install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
# Expose port&lt;br /&gt;
EXPOSE 8000&lt;br /&gt;
&lt;br /&gt;
# Define startup command&lt;br /&gt;
CMD [&amp;quot;python3&amp;quot;, &amp;quot;app.py&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Build the image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker build -t myapp:1.0 .&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Run containers from your custom image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8000:8000 myapp:1.0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Same Dockerfile always builds identical image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Version control&amp;#039;&amp;#039;&amp;#039;: Dockerfile lives in git alongside code&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Documentation&amp;#039;&amp;#039;&amp;#039;: Dockerfile explicitly documents dependencies and setup&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automation&amp;#039;&amp;#039;&amp;#039;: CI/CD pipelines can build images automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Dockerfile instructions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;FROM&amp;lt;/code&amp;gt;: Base image to start from&lt;br /&gt;
* &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt;: Execute command during build (install packages, etc.)&lt;br /&gt;
* &amp;lt;code&amp;gt;COPY&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;ADD&amp;lt;/code&amp;gt;: Copy files from host into image&lt;br /&gt;
* &amp;lt;code&amp;gt;WORKDIR&amp;lt;/code&amp;gt;: Set working directory&lt;br /&gt;
* &amp;lt;code&amp;gt;ENV&amp;lt;/code&amp;gt;: Set environment variables&lt;br /&gt;
* &amp;lt;code&amp;gt;EXPOSE&amp;lt;/code&amp;gt;: Document which ports the application uses&lt;br /&gt;
* &amp;lt;code&amp;gt;CMD&amp;lt;/code&amp;gt;: Default command to run when container starts&lt;br /&gt;
* &amp;lt;code&amp;gt;ENTRYPOINT&amp;lt;/code&amp;gt;: Configure container as executable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Use specific image tags (not &amp;lt;code&amp;gt;latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Minimize layers (combine &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt; commands)&lt;br /&gt;
* Leverage build cache (order instructions from least to most frequently changing)&lt;br /&gt;
* Use &amp;lt;code&amp;gt;.dockerignore&amp;lt;/code&amp;gt; (like &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; for Docker builds)&lt;br /&gt;
* Don&amp;#039;t run as root (use &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; instruction)&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-compose-multi-container-orchestration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Compose: Multi-Container Orchestration ===&lt;br /&gt;
&lt;br /&gt;
Instead of running multiple &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands with complex options, define your entire infrastructure in a &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;version: &amp;#039;3.8&amp;#039;&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
  # Purple reverse proxy&lt;br /&gt;
  purple:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;8080:80&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./purple:/etc/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.10&lt;br /&gt;
    depends_on:&lt;br /&gt;
      - red&lt;br /&gt;
      - blue&lt;br /&gt;
&lt;br /&gt;
  # Red backend&lt;br /&gt;
  red:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./red:/etc/caddy&lt;br /&gt;
      - ./red:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.2&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          memory: 256M&lt;br /&gt;
&lt;br /&gt;
  # Blue backend&lt;br /&gt;
  blue:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./blue:/etc/caddy&lt;br /&gt;
      - ./blue:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.3&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          cpus: &amp;#039;0.5&amp;#039;&lt;br /&gt;
&lt;br /&gt;
networks:&lt;br /&gt;
  labnet:&lt;br /&gt;
    driver: bridge&lt;br /&gt;
    ipam:&lt;br /&gt;
      config:&lt;br /&gt;
        - subnet: 10.10.0.0/24&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Start entire infrastructure:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or in detached mode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up -d&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Stop everything:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose down&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;View logs:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose logs -f&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Declarative&amp;#039;&amp;#039;&amp;#039;: Describe desired state, not imperative commands&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Single source of truth&amp;#039;&amp;#039;&amp;#039;: One file defines entire application&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Easy to share&amp;#039;&amp;#039;&amp;#039;: Colleagues can run &amp;lt;code&amp;gt;docker compose up&amp;lt;/code&amp;gt; and get identical environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Development/production parity&amp;#039;&amp;#039;&amp;#039;: Same compose file works everywhere&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automatic networking&amp;#039;&amp;#039;&amp;#039;: Compose creates network and DNS automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is the production-ready way to deploy multi-container applications.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Further learning:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039;: Learn advanced instructions, multi-stage builds, build arguments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039;: Learn service dependencies, health checks, scaling&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Swarm&amp;#039;&amp;#039;&amp;#039;: Docker&amp;#039;s built-in orchestration for multi-host deployments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kubernetes&amp;#039;&amp;#039;&amp;#039;: Industry-standard container orchestration platform&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Container registries&amp;#039;&amp;#039;&amp;#039;: Push images to Docker Hub, GitHub Container Registry, or private registries&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;: Image scanning, rootless Docker, seccomp profiles, AppArmor&lt;br /&gt;
&lt;br /&gt;
These topics build on the solid foundation you&amp;#039;ve established in this lab. You now understand what containers actually are at the OS level—the rest is learning tools that make containers easier to build and deploy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all deliverables from Exercises A through E, organized with clear section headers matching the exercise labels.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Required deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable A&amp;#039;&amp;#039;&amp;#039;: Installation verification (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable B&amp;#039;&amp;#039;&amp;#039;: Hello World and namespace inspection (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable C&amp;#039;&amp;#039;&amp;#039;: Fedora exploration (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable D&amp;#039;&amp;#039;&amp;#039;: Persistent storage (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable E&amp;#039;&amp;#039;&amp;#039;: Multi-container infrastructure (screenshots)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced containerization using Docker, demonstrating how Linux kernel features (namespaces, cgroups, OverlayFS) are composed to create isolated application environments. You&amp;#039;ve seen that Docker is not magic—it&amp;#039;s sophisticated automation of kernel primitives you already understand from previous labs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Internals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Deep dive into runc (the OCI runtime that Docker uses)&lt;br /&gt;
* Understanding containerd (container runtime layer)&lt;br /&gt;
* Linux capabilities and security contexts&lt;br /&gt;
* AppArmor and SELinux profiles for containers&lt;br /&gt;
* User namespaces and rootless containers&lt;br /&gt;
* Seccomp profiles for system call filtering&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dockerfile Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
* Build cache optimization strategies&lt;br /&gt;
* Layer ordering for efficient rebuilds&lt;br /&gt;
* .dockerignore for excluding files&lt;br /&gt;
* Security scanning with Trivy or Clair&lt;br /&gt;
* Distroless and minimal base images&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker Compose Advanced:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Service dependencies with health checks&lt;br /&gt;
* Environment-specific overrides&lt;br /&gt;
* Secrets management&lt;br /&gt;
* Multiple compose files (base + overrides)&lt;br /&gt;
* Named volumes vs bind mounts&lt;br /&gt;
* Integration testing with compose&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Orchestration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Kubernetes architecture and concepts&lt;br /&gt;
* kubectl basics&lt;br /&gt;
* Pods, Services, Deployments, ConfigMaps&lt;br /&gt;
* Kubernetes networking (CNI plugins)&lt;br /&gt;
* Helm charts for package management&lt;br /&gt;
* Service mesh (Istio, Linkerd)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Networking Deep Dive:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Bridge vs host vs macvlan networking&lt;br /&gt;
* Overlay networks for multi-host communication&lt;br /&gt;
* Network plugins and CNI specification&lt;br /&gt;
* Load balancing strategies&lt;br /&gt;
* Service discovery patterns&lt;br /&gt;
* Network policies and segmentation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container escape techniques and mitigations&lt;br /&gt;
* Image vulnerability scanning&lt;br /&gt;
* Runtime security monitoring (Falco)&lt;br /&gt;
* Least privilege principles&lt;br /&gt;
* Supply chain security&lt;br /&gt;
* Harbor for secure image registry&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container resource tuning&lt;br /&gt;
* Storage drivers (overlay2, btrfs, zfs)&lt;br /&gt;
* Network performance optimization&lt;br /&gt;
* Monitoring with Prometheus and Grafana&lt;br /&gt;
* Distributed tracing with Jaeger&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Production Operations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Logging strategies (centralized logging)&lt;br /&gt;
* Health checks and readiness probes&lt;br /&gt;
* Rolling updates and blue-green deployments&lt;br /&gt;
* Backup and disaster recovery&lt;br /&gt;
* CI/CD integration (Jenkins, GitLab CI)&lt;br /&gt;
* Container registries (private, public)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 namespaces                             # Linux namespaces overview&lt;br /&gt;
man 7 cgroups                                # Control groups overview&lt;br /&gt;
man 2 unshare                                # Create namespaces&lt;br /&gt;
man 2 setns                                  # Enter existing namespace&lt;br /&gt;
man 8 nsenter                                # Run program in namespace&lt;br /&gt;
man 1 docker                                 # Docker CLI reference&lt;br /&gt;
man 1 docker-run                             # docker run reference&lt;br /&gt;
man 1 docker-compose                         # Docker Compose reference&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Official Documentation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docs.docker.com/ Docker Documentation] - Comprehensive official docs&lt;br /&gt;
* [https://hub.docker.com/ Docker Hub] - Public image registry&lt;br /&gt;
* [https://opencontainers.org/ OCI Specification] - Open Container Initiative standards&lt;br /&gt;
* [https://containerd.io/docs/ containerd Documentation] - Container runtime&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Tutorials and Guides:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docker-curriculum.com/ Docker Curriculum] - Beginner-friendly tutorial&lt;br /&gt;
* [https://labs.play-with-docker.com/ Play with Docker] - Free interactive playground&lt;br /&gt;
* [https://kubernetes.io/docs/ Kubernetes Documentation] - K8s official docs&lt;br /&gt;
* [https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ Dockerfile Best Practices]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deep Dives:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://lwn.net/Articles/531114/ Understanding Namespaces (LWN)] - Series on Linux namespaces&lt;br /&gt;
* [https://jvns.ca/blog/2016/10/10/what-even-is-a-container/ How Containers Work] - Excellent blog post by Julia Evans&lt;br /&gt;
* [https://sysdig.com/blog/dockerfile-best-practices/ Container Security Best Practices] - Security-focused guide&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Community:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://forums.docker.com/ Docker Forums] - Community support&lt;br /&gt;
* [https://stackoverflow.com/questions/tagged/docker Stack Overflow - Docker Tag]&lt;br /&gt;
* [https://reddit.com/r/docker Reddit r/docker]&lt;br /&gt;
* [https://slack.cncf.io/ CNCF Slack] - Cloud Native Computing Foundation community&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Practice Environments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://www.katacoda.com/ Katacoda] - Interactive Docker and Kubernetes scenarios&lt;br /&gt;
* [https://labs.play-with-k8s.com/ Play with Kubernetes] - K8s playground&lt;br /&gt;
* [https://killercoda.com/ KillerCoda] - Interactive learning scenarios&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8194</id>
		<title>OS Lab 10 - Containers and Docker</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_10_-_Containers_and_Docker&amp;diff=8194"/>
		<updated>2025-12-12T14:52:44Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain how containers use Linux kernel namespaces (PID, mount, network, UTS, I...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how containers use Linux kernel namespaces (PID, mount, network, UTS, IPC, user, cgroup) to provide process isolation without requiring separate operating systems or hypervisors.&lt;br /&gt;
* Differentiate between container images (immutable filesystem templates) and running containers (ephemeral process instances in isolated namespaces).&lt;br /&gt;
* Understand how OverlayFS provides efficient layered filesystems that allow multiple containers to share common base layers while maintaining separate writable layers.&lt;br /&gt;
* Apply cgroup resource limits to constrain container CPU, memory, and I/O usage, preventing resource monopolization.&lt;br /&gt;
* Configure Docker networking using custom bridges, port mapping (NAT), and container-to-container communication with automatic DNS resolution.&lt;br /&gt;
* Use volumes and bind mounts to persist data beyond container lifecycles and share data between containers and the host.&lt;br /&gt;
* Build multi-container applications with coordinated networking and resource management, demonstrating modern microservices architecture patterns.&lt;br /&gt;
* Connect container concepts to previous labs: relate network namespaces to Lab 7, mount namespaces to Lab 2, PID namespaces to Lab 3, and Docker networking to Labs 7-9.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;from-manual-isolation-to-automated-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== From Manual Isolation to Automated Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you manually constructed network isolation using Linux kernel primitives. You created virtual network interfaces with &amp;lt;code&amp;gt;ip link add type veth&amp;lt;/code&amp;gt;, configured bridges with &amp;lt;code&amp;gt;ip link add type bridge&amp;lt;/code&amp;gt;, established isolated network stacks with &amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, and set up routing and NAT rules. Through this hands-on work, you gained deep insight into how the Linux kernel provides network isolation at the namespace level.&lt;br /&gt;
&lt;br /&gt;
Every time you executed &amp;lt;code&amp;gt;sudo ip netns exec red ping 10.0.0.3&amp;lt;/code&amp;gt;, you were demonstrating a fundamental concept: the kernel can create completely isolated environments where processes see only a subset of system resources. The &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace had its own network interfaces, its own routing table, and its own firewall rules—completely invisible to processes running in other namespaces or on the host.&lt;br /&gt;
&lt;br /&gt;
This isolation is powerful, but network namespaces are just one of seven namespace types that the Linux kernel provides. To fully isolate an application and create what we call a &amp;amp;quot;container,&amp;amp;quot; you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;): Isolated network stack—you&amp;#039;ve mastered this in Labs 7-9&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PID namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;): Isolated process tree—each namespace has its own PID 1&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mount namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;): Isolated filesystem view—different root directory from the host&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UTS namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;): Isolated hostname—each container can have its own hostname&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IPC namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;): Isolated inter-process communication—shared memory, semaphores, message queues&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;): Isolated UIDs/GIDs—security boundary for privilege separation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Cgroup namespace&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;): Isolated view of control group hierarchy&lt;br /&gt;
&lt;br /&gt;
Additionally, you need:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Control groups (cgroups)&amp;#039;&amp;#039;&amp;#039; to limit CPU, memory, and I/O resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy-on-write filesystems&amp;#039;&amp;#039;&amp;#039; (OverlayFS) for efficient storage&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Image management&amp;#039;&amp;#039;&amp;#039; for distributing application packages&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Orchestration&amp;#039;&amp;#039;&amp;#039; for managing the lifecycle of multiple isolated environments&lt;br /&gt;
&lt;br /&gt;
Manually setting up all of these components for every application would require hundreds of commands and deep kernel knowledge. This is the problem that containerization technology solves.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-are-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What are Containers? ===&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;container&amp;#039;&amp;#039;&amp;#039; is an isolated process (or process tree) that uses Linux kernel features—namespaces, cgroups, and layered filesystems—to provide the illusion of running in a separate system. Containers package an application with its dependencies, libraries, and configuration into a single unit that can run consistently across different environments.&lt;br /&gt;
&lt;br /&gt;
Containers are not a specific product or tool—they are a pattern for using kernel isolation features. Multiple container runtimes exist, each implementing this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Runtimes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039;: The most widely adopted container platform, providing a complete ecosystem (daemon, CLI, image format, registry)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Podman&amp;#039;&amp;#039;&amp;#039;: Daemonless container engine, compatible with Docker images and commands, can run rootless&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Industry-standard container runtime, used by Kubernetes and Docker (as of Docker 1.11+)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CRI-O&amp;#039;&amp;#039;&amp;#039;: Lightweight container runtime built for Kubernetes, implementing the Container Runtime Interface (CRI)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;LXC/LXD&amp;#039;&amp;#039;&amp;#039;: System containers that more closely resemble traditional VMs, providing full init systems&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What Container Runtimes Provide:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Namespace Management&amp;#039;&amp;#039;&amp;#039;: Automatically create and configure all necessary namespace types&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Filesystem Layers&amp;#039;&amp;#039;&amp;#039;: Use OverlayFS or similar copy-on-write filesystems for efficient storage&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Configuration&amp;#039;&amp;#039;&amp;#039;: Set up bridges, veth pairs, and NAT rules automatically&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Resource Control&amp;#039;&amp;#039;&amp;#039;: Configure cgroups to enforce CPU and memory limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Image Distribution&amp;#039;&amp;#039;&amp;#039;: Provide standard formats (OCI) for packaging and distributing applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Lifecycle Management&amp;#039;&amp;#039;&amp;#039;: Offer commands to create, start, stop, and remove containers&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-as-a-container-runtime&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker as a Container Runtime ===&lt;br /&gt;
&lt;br /&gt;
In this lab, we use &amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;&amp;#039;&amp;#039; because it is the most widely deployed and well-documented container runtime. Docker&amp;#039;s architecture consists of:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Engine (dockerd)&amp;#039;&amp;#039;&amp;#039;: Daemon that manages containers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker CLI (docker)&amp;#039;&amp;#039;&amp;#039;: Command-line interface for interacting with the daemon&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;containerd&amp;#039;&amp;#039;&amp;#039;: Lower-level runtime that Docker uses internally&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;runc&amp;#039;&amp;#039;&amp;#039;: OCI-compliant runtime that actually creates and runs containers&lt;br /&gt;
&lt;br /&gt;
When you execute &amp;lt;code&amp;gt;docker run nginx&amp;lt;/code&amp;gt;, Docker performs approximately 50-100 system calls to configure namespaces, mount filesystems, set up networking, and start the process—all operations you could do manually but would require significant time and expertise.&lt;br /&gt;
&lt;br /&gt;
The concepts you learn with Docker apply to all container runtimes, as they all use the same underlying kernel features. The commands may differ (e.g., &amp;lt;code&amp;gt;podman run&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt;), but the fundamental mechanisms remain the same.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shift-in-software-deployment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Shift in Software Deployment ===&lt;br /&gt;
&lt;br /&gt;
Containers represent a fundamental paradigm shift in how we think about software deployment and infrastructure management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Traditional Deployment Model (Pre-Container Era):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Provision a server (physical or virtual machine)&lt;br /&gt;
2. Install operating system (Ubuntu, RHEL, etc.)&lt;br /&gt;
3. Install runtime dependencies (Python 3.9, Node.js 16, specific library versions)&lt;br /&gt;
4. Configure environment variables, users, permissions&lt;br /&gt;
5. Deploy application code&lt;br /&gt;
6. Configure monitoring, logging, security&lt;br /&gt;
7. Hope everything works the same as on your development machine&lt;br /&gt;
8. Troubleshoot when it doesn&amp;#039;t (&amp;amp;quot;works on my machine&amp;amp;quot; problem)&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model suffers from several critical issues:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dependency Hell&amp;#039;&amp;#039;&amp;#039;: Different applications require different, potentially conflicting library versions&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration Drift&amp;#039;&amp;#039;&amp;#039;: Development, staging, and production environments gradually diverge&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Snowflake Servers&amp;#039;&amp;#039;&amp;#039;: Each server becomes unique and unreproducible&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Slow Deployment&amp;#039;&amp;#039;&amp;#039;: Setting up a new environment can take hours or days&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Poor Resource Utilization&amp;#039;&amp;#039;&amp;#039;: Applications can&amp;#039;t share servers due to dependency conflicts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-Based Deployment Model:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Developer creates Dockerfile specifying exact environment&lt;br /&gt;
2. Build process creates immutable container image with all dependencies&lt;br /&gt;
3. Image tested in CI/CD pipeline (identical to production)&lt;br /&gt;
4. Image deployed to any server with Docker installed&lt;br /&gt;
5. Container starts in seconds with guaranteed-identical environment&lt;br /&gt;
6. Multiple isolated applications run on same host without conflicts&amp;lt;/pre&amp;gt;&lt;br /&gt;
This model provides:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Images never change after building; deploy new versions rather than modifying running systems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Development environment = testing environment = production environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Portability&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;Build once, run anywhere&amp;amp;quot; (laptop, data center, cloud)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: Run 10-100 containers on a single host (vs. 5-10 VMs)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rapid Deployment&amp;#039;&amp;#039;&amp;#039;: Start containers in milliseconds vs. minutes for VMs&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Microservices Architecture&amp;#039;&amp;#039;&amp;#039;: Enables decomposing monoliths into independently deployable services&lt;br /&gt;
&lt;br /&gt;
This shift has revolutionized software engineering, enabling modern DevOps practices, continuous deployment, and cloud-native architectures. Companies like Netflix, Uber, and Airbnb run millions of containers to serve billions of requests daily.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;containers-vs-virtual-machines&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Containers vs Virtual Machines ===&lt;br /&gt;
&lt;br /&gt;
Understanding the architectural difference between containers and virtual machines is crucial for appreciating why containers have become the dominant deployment model.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Virtual Machine Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│ Guest OS    │ │ Guest OS    │ │ Guest OS    │&lt;br /&gt;
│ (Kernel)    │ │ (Kernel)    │ │ (Kernel)    │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Hypervisor (VMware, KVM, Xen)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
        Host Operating System &amp;amp;amp; Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Each VM runs a complete guest operating system with its own kernel&lt;br /&gt;
* Hypervisor emulates hardware, providing virtual CPUs, RAM, disks, NICs&lt;br /&gt;
* Strong isolation (separate kernels mean vulnerabilities in one VM don&amp;#039;t affect others)&lt;br /&gt;
* Heavy resource consumption (each OS kernel needs 1-2GB RAM)&lt;br /&gt;
* Slow startup (boot entire OS: 30-60 seconds)&lt;br /&gt;
* Large disk footprint (each VM stores complete OS: 1-10GB)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────┐ ┌─────────────┐ ┌─────────────┐&lt;br /&gt;
│   App A     │ │   App B     │ │   App C     │&lt;br /&gt;
├─────────────┤ ├─────────────┤ ├─────────────┤&lt;br /&gt;
│  Libraries  │ │  Libraries  │ │  Libraries  │&lt;br /&gt;
└─────────────┘ └─────────────┘ └─────────────┘&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
       Container Runtime (Docker Engine)&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
    Host Operating System &amp;amp;amp; Shared Kernel&lt;br /&gt;
─────────────────────────────────────────────────&lt;br /&gt;
                 Hardware&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* All containers share the host&amp;#039;s kernel (no guest OS needed)&lt;br /&gt;
* Container runtime manages namespace and cgroup isolation&lt;br /&gt;
* Lightweight isolation (namespaces separate processes, but they&amp;#039;re still just processes)&lt;br /&gt;
* Minimal resource overhead (containers use only incremental memory beyond their application)&lt;br /&gt;
* Fast startup (start process in isolated namespace: milliseconds)&lt;br /&gt;
* Small disk footprint (layered filesystem shares common base images: 10-100MB incremental)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Trade-off:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Virtual machines provide &amp;#039;&amp;#039;&amp;#039;stronger security isolation&amp;#039;&amp;#039;&amp;#039; at the cost of &amp;#039;&amp;#039;&amp;#039;resource efficiency&amp;#039;&amp;#039;&amp;#039;. If your threat model requires complete kernel isolation (e.g., multi-tenant cloud providers hosting untrusted code), VMs are appropriate.&lt;br /&gt;
&lt;br /&gt;
Containers provide &amp;#039;&amp;#039;&amp;#039;lighter-weight isolation&amp;#039;&amp;#039;&amp;#039; with &amp;#039;&amp;#039;&amp;#039;much better resource efficiency&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re running your own applications on your own infrastructure, containers are usually the right choice. You can run 10-100 containers on hardware that would support only 5-10 VMs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;An Important Insight:&amp;#039;&amp;#039;&amp;#039; When you run &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; on the host, you see container processes. They&amp;#039;re not hidden inside separate kernels like VM processes would be. This demonstrates that containers are just isolated processes on the host—the kernel makes them appear isolated through namespaces, but they&amp;#039;re fundamentally just processes.&lt;br /&gt;
&lt;br /&gt;
This is both a feature (efficiency, observability) and a constraint (shared kernel means a kernel vulnerability could affect all containers). Modern container security practices use defense-in-depth: namespaces + cgroups + seccomp + AppArmor/SELinux + user namespaces to create multiple security layers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operating System&amp;#039;&amp;#039;&amp;#039;: Linux-based system (Ubuntu 20.04+ recommended, but Debian, Fedora, CentOS also supported)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;RAM&amp;#039;&amp;#039;&amp;#039;: Minimum 2GB, 4GB recommended for comfortable operation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Disk Space&amp;#039;&amp;#039;&amp;#039;: At least 20GB free (Docker images and container layers consume significant space)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CPU&amp;#039;&amp;#039;&amp;#039;: Any modern x86_64 or ARM64 processor&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Privileges&amp;#039;&amp;#039;&amp;#039;: Root access via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (required for Docker installation and initial setup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kernel&amp;#039;&amp;#039;&amp;#039;: Minimum Linux kernel 3.10 (kernel 4.0+ recommended for full feature support)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check your kernel version:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;uname -r&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If below 3.10, you&amp;#039;ll need to update your kernel before proceeding.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check available disk space:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker stores images and containers in &amp;lt;code&amp;gt;/var/lib/docker&amp;lt;/code&amp;gt; by default. Ensure you have sufficient space.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
Before beginning, ensure the following packages are installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release \&lt;br /&gt;
    bridge-utils \&lt;br /&gt;
    net-tools&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Package descriptions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;apt-transport-https&amp;lt;/code&amp;gt;: Enables apt to retrieve packages over HTTPS&lt;br /&gt;
* &amp;lt;code&amp;gt;ca-certificates&amp;lt;/code&amp;gt;: Common CA certificates for SSL verification&lt;br /&gt;
* &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;: Command-line tool for transferring data (used to download Docker&amp;#039;s GPG key)&lt;br /&gt;
* &amp;lt;code&amp;gt;gnupg&amp;lt;/code&amp;gt;: GNU Privacy Guard for verifying package signatures&lt;br /&gt;
* &amp;lt;code&amp;gt;lsb-release&amp;lt;/code&amp;gt;: Provides Linux Standard Base version information (used to detect Ubuntu version)&lt;br /&gt;
* &amp;lt;code&amp;gt;bridge-utils&amp;lt;/code&amp;gt;: Tools for managing bridge devices (&amp;lt;code&amp;gt;brctl&amp;lt;/code&amp;gt; command)&lt;br /&gt;
* &amp;lt;code&amp;gt;net-tools&amp;lt;/code&amp;gt;: Legacy networking tools (&amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt;) for compatibility&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
This lab builds directly on concepts from previous labs. You should be comfortable with:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 2 (Filesystems):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Filesystem hierarchy and directory structure&lt;br /&gt;
* Mount points and the &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; command&lt;br /&gt;
* Understanding of &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; purposes&lt;br /&gt;
* File permissions and ownership (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Symbolic links and filesystem navigation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 3 (Processes and Jobs):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs (PIDs) and process hierarchy&lt;br /&gt;
* Parent-child process relationships&lt;br /&gt;
* &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; command and process listing&lt;br /&gt;
* Foreground vs. background processes&lt;br /&gt;
* Process signals (SIGTERM, SIGKILL)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 4 (Users, Groups, and Permissions):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs (UIDs) and group IDs (GIDs)&lt;br /&gt;
* Root vs. unprivileged users&lt;br /&gt;
* &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; for privilege escalation&lt;br /&gt;
* File and directory permissions (read, write, execute)&lt;br /&gt;
* Security implications of running processes as root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 7 (Network Fundamentals):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces and IP addresses&lt;br /&gt;
* Bridges and virtual ethernet (veth) pairs&lt;br /&gt;
* Subnets and CIDR notation&lt;br /&gt;
* Routing tables and default gateways&lt;br /&gt;
* Network Address Translation (NAT)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network namespaces&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip netns exec&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
This is crucial—Docker networking uses the exact same mechanisms you built manually.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 8 (Transport and Security):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP and UDP protocols&lt;br /&gt;
* Port numbers and socket addresses (IP:PORT)&lt;br /&gt;
* Client-server communication model&lt;br /&gt;
* The concept of listening vs. connecting&lt;br /&gt;
* TLS/SSL (Docker can use HTTPS for secure image distribution)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 9 (Application Protocols):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* HTTP/HTTPS protocols&lt;br /&gt;
* Reverse proxies and path-based routing&lt;br /&gt;
* DNS and hostname resolution&lt;br /&gt;
* Caddy web server configuration&lt;br /&gt;
* Multi-tier application architecture&lt;br /&gt;
&lt;br /&gt;
You should also be comfortable with:&lt;br /&gt;
&lt;br /&gt;
* Command-line text manipulation (&amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sed&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Basic bash scripting (variables, loops, conditionals)&lt;br /&gt;
* Using multiple terminal windows simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;linux-namespaces-the-foundation-of-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Linux Namespaces: The Foundation of Containers ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7-9, you worked extensively with network namespaces—one of seven namespace types provided by the Linux kernel. Every time you executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red&lt;br /&gt;
sudo ip netns exec red ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You were creating an isolated network environment and executing commands within that isolated environment. The processes running in the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace could not see network interfaces, routes, or connections in other namespaces. This isolation is the fundamental mechanism that makes containers possible.&lt;br /&gt;
&lt;br /&gt;
Docker extends this concept to six additional namespace types, providing complete process isolation. Understanding namespaces is essential to understanding containers—they are not optional background knowledge, but rather the core technology that defines what a container is.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-seven-namespace-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Seven Namespace Types ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Network Namespace (&amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You know this namespace intimately from Labs 7-9. When you created the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; namespaces, you were creating isolated network stacks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Network interfaces (lo, eth0, wlan0, etc.)&lt;br /&gt;
* IP addresses (each namespace has its own IPs)&lt;br /&gt;
* Routing tables (&amp;lt;code&amp;gt;ip route show&amp;lt;/code&amp;gt; output differs per namespace)&lt;br /&gt;
* Firewall rules (iptables/nftables rules are namespace-specific)&lt;br /&gt;
* Network sockets and ports (multiple processes in different namespaces can bind to the same port number)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 7, you manually created network namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red                    # Create isolated network stack&lt;br /&gt;
sudo ip netns exec red ip link show      # View interfaces in that namespace&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does exactly this when you run a container, but also creates six other namespace types simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example implications:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container A can listen on port 80, Container B can listen on port 80—no conflict&lt;br /&gt;
* Container A cannot see Container B&amp;#039;s network connections (&amp;lt;code&amp;gt;ss -tuna&amp;lt;/code&amp;gt; shows only its own)&lt;br /&gt;
* Each container has its own localhost (127.0.0.1) that&amp;#039;s separate from the host&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. PID Namespace (&amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The PID namespace isolates the process ID number space. This is related to what you learned in Lab 3 about process management.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Process IDs—processes in different PID namespaces can have the same PID&lt;br /&gt;
* Process visibility—processes can only see other processes in the same PID namespace&lt;br /&gt;
* PID 1 (init process)—each PID namespace has its own PID 1&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The kernel maintains a separate process tree for each PID namespace. When you create a PID namespace and start a process in it:&lt;br /&gt;
&lt;br /&gt;
* Inside the namespace, that process is PID 1 (like an init system)&lt;br /&gt;
* Outside the namespace, that same process has a different PID (e.g., 12345)&lt;br /&gt;
* Processes inside cannot see processes outside their namespace tree&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On the host&lt;br /&gt;
ps aux | wc -l&lt;br /&gt;
# Output: 237 processes&lt;br /&gt;
&lt;br /&gt;
# Inside a container&lt;br /&gt;
docker exec container ps aux | wc -l&lt;br /&gt;
# Output: 5 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s processes think they&amp;#039;re the only processes on the system. They cannot see the host&amp;#039;s other 232 processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Mount Namespace (&amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The mount namespace isolates the filesystem mount table. This relates directly to Lab 2 where you learned about filesystems and mounting.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Mount points—what is mounted at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, etc.&lt;br /&gt;
* Root filesystem—each namespace can have a completely different root directory&lt;br /&gt;
* Mount propagation—mounts in one namespace don&amp;#039;t affect others (by default)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you create a mount namespace, the new namespace inherits a copy of the parent&amp;#039;s mount table. But subsequent mounts/unmounts in the child don&amp;#039;t affect the parent.&lt;br /&gt;
&lt;br /&gt;
Containers use this to provide a completely different filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host (Ubuntu)&lt;br /&gt;
ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...&lt;br /&gt;
&lt;br /&gt;
# In container (Fedora)&lt;br /&gt;
docker exec fedora ls /&lt;br /&gt;
bin  boot  dev  etc  home  lib  ...  # Different files!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both see &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, but they&amp;#039;re seeing different directories. The container&amp;#039;s &amp;lt;code&amp;gt;/etc/os-release&amp;lt;/code&amp;gt; shows Fedora, while the host&amp;#039;s shows Ubuntu.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Technical implementation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker uses OverlayFS (covered later) to construct the container&amp;#039;s root filesystem from image layers, then uses pivot_root or chroot to make that directory appear as &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to the container&amp;#039;s processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. UTS Namespace (&amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
UTS stands for &amp;amp;quot;Unix Timesharing System&amp;amp;quot;—a historical name. The UTS namespace isolates hostname and domain name.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System hostname (&amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; command output)&lt;br /&gt;
* Domain name (NIS domain name)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each UTS namespace can set its own hostname independently of other namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# On host&lt;br /&gt;
hostname&lt;br /&gt;
# Output: myserver.example.com&lt;br /&gt;
&lt;br /&gt;
# In container&lt;br /&gt;
docker exec container hostname&lt;br /&gt;
# Output: a1b2c3d4e5f6  (container ID)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Containers typically use the container ID as hostname by default, but you can override with &amp;lt;code&amp;gt;--hostname&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --hostname=webserver nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. IPC Namespace (&amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The IPC namespace isolates System V Inter-Process Communication resources. This relates to Lab 6 where you learned about IPC mechanisms.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* System V message queues&lt;br /&gt;
* System V semaphore sets&lt;br /&gt;
* System V shared memory segments&lt;br /&gt;
* POSIX message queues (in &amp;lt;code&amp;gt;/dev/mqueue&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 6:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 6, you learned that processes can communicate via shared memory, message queues, and semaphores. The IPC namespace ensures that processes in different namespaces cannot access each other&amp;#039;s IPC objects, even if they use the same IPC keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A creates shared memory segment with key 1234&lt;br /&gt;
docker exec containerA ipcmk -M 1024 -p 1234&lt;br /&gt;
&lt;br /&gt;
# Container B tries to access it&lt;br /&gt;
docker exec containerB ipcs -m -k 1234&lt;br /&gt;
# Output: no segment found (different IPC namespace)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. User Namespace (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The user namespace isolates user IDs (UIDs) and group IDs (GIDs). This is the most complex namespace and provides significant security benefits.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* User IDs—UID 0 inside namespace can map to UID 100000 outside&lt;br /&gt;
* Group IDs—similar mapping for GIDs&lt;br /&gt;
* Capabilities—process can have capabilities inside namespace but not outside&lt;br /&gt;
* Security attributes—AppArmor/SELinux contexts&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
User namespaces allow UID/GID mapping. A process can be root (UID 0) inside the namespace but unprivileged (e.g., UID 100000) outside.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Inside Container: UID 0 (root)&lt;br /&gt;
        ↓ mapping&lt;br /&gt;
Outside Container: UID 100000 (unprivileged)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security benefit:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even if an attacker compromises a container and gains root privileges inside the container, they&amp;#039;re still unprivileged on the host. If they escape the container, they cannot access files owned by actual root.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s approach:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, many Docker configurations share the host&amp;#039;s user namespace (for simplicity and compatibility). Rootless Docker and user-remapped Docker use separate user namespaces for enhanced security.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Cgroup Namespace (&amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The cgroup namespace isolates the view of the cgroup hierarchy. Note this is different from cgroups themselves (which we&amp;#039;ll cover separately).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What it isolates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
* View of &amp;lt;code&amp;gt;/sys/fs/cgroup&amp;lt;/code&amp;gt; hierarchy&lt;br /&gt;
* Ability to see other containers&amp;#039; resource constraints&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How it works:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without cgroup namespaces, a process can read &amp;lt;code&amp;gt;/proc/self/cgroup&amp;lt;/code&amp;gt; and see the full path to its cgroup, revealing information about the container orchestration system.&lt;br /&gt;
&lt;br /&gt;
With cgroup namespaces, the process sees itself at the root of the cgroup tree, hiding the real hierarchy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; This namespace isolates the &amp;#039;&amp;#039;view&amp;#039;&amp;#039; of cgroups, not the enforcement of resource limits. Resource limits are enforced by cgroups themselves (covered in section 4.5).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;namespace-identifiers-understanding-procpidns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Namespace Identifiers: Understanding /proc/PID/ns/ ====&lt;br /&gt;
&lt;br /&gt;
Every process has a directory at &amp;lt;code&amp;gt;/proc/PID/ns/&amp;lt;/code&amp;gt; containing symbolic links to namespace identifiers. These links reveal which namespaces the process belongs to.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Examining namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 root root 0 Dec 12 10:30 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026531835]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 ipc -&amp;amp;gt; &amp;#039;ipc:[4026531839]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 mnt -&amp;amp;gt; &amp;#039;mnt:[4026531840]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 pid -&amp;amp;gt; &amp;#039;pid:[4026531836]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:30 uts -&amp;amp;gt; &amp;#039;uts:[4026531838]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the format:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each symlink follows the pattern: &amp;lt;code&amp;gt;namespace_type:[inode_number]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;inode number&amp;#039;&amp;#039;&amp;#039; is the critical piece of information. It uniquely identifies that specific namespace instance. Think of it as a &amp;amp;quot;namespace ID.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key principle: Same inode = Shared namespace, Different inode = Isolated namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example from Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you created network namespaces in Lab 7, each had a unique network namespace inode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host process&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Host&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
# Process in red namespace&lt;br /&gt;
sudo ip netns exec red ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532145]&amp;#039;  # Different inode = isolated!&lt;br /&gt;
&lt;br /&gt;
# Process in blue namespace&lt;br /&gt;
sudo ip netns exec blue ls -la /proc/$$/ns/net&lt;br /&gt;
net -&amp;gt; &amp;#039;net:[4026532147]&amp;#039;  # Also different = also isolated!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Comparing container to host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID on host&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example output: 12345&lt;br /&gt;
&lt;br /&gt;
# View container&amp;#039;s namespaces&lt;br /&gt;
sudo ls -la /proc/12345/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# View host&amp;#039;s namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Different inodes! Container is isolated.&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Namespace sharing:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Sometimes containers intentionally share namespaces. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container shares the host&amp;#039;s network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Using namespace identifiers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These symlinks aren&amp;#039;t just informational—you can actually use them to enter namespaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --net=/proc/12345/ns/net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command enters the network namespace of PID 12345 and runs &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside that namespace. This is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works under the hood—it uses &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; to join the container&amp;#039;s namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary table:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Namespace Type&lt;br /&gt;
! What It Isolates&lt;br /&gt;
! Lab Connection&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;net&amp;lt;/code&amp;gt;&lt;br /&gt;
| Network stack (interfaces, IPs, routes, ports)&lt;br /&gt;
| Lab 7: You built this manually!&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pid&amp;lt;/code&amp;gt;&lt;br /&gt;
| Process IDs and process tree&lt;br /&gt;
| Lab 3: Process management&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mnt&amp;lt;/code&amp;gt;&lt;br /&gt;
| Filesystem mounts and root directory&lt;br /&gt;
| Lab 2: Mounting and filesystems&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;uts&amp;lt;/code&amp;gt;&lt;br /&gt;
| Hostname and domain name&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ipc&amp;lt;/code&amp;gt;&lt;br /&gt;
| Shared memory, message queues, semaphores&lt;br /&gt;
| Lab 6: IPC mechanisms&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&lt;br /&gt;
| User and group IDs, capabilities&lt;br /&gt;
| Lab 4: Users and permissions&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cgroup&amp;lt;/code&amp;gt;&lt;br /&gt;
| View of cgroup hierarchy&lt;br /&gt;
| -&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-images-vs-running-containers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Images vs Running Containers ===&lt;br /&gt;
&lt;br /&gt;
This distinction is fundamental to understanding Docker and is often a source of confusion.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A container image is a &amp;#039;&amp;#039;&amp;#039;read-only template&amp;#039;&amp;#039;&amp;#039; consisting of:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;A root filesystem&amp;#039;&amp;#039;&amp;#039;: All files and directories that will appear in the container (applications, libraries, configuration files)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Metadata&amp;#039;&amp;#039;&amp;#039;: Information about how to run the container (default command, environment variables, exposed ports, volumes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Layers&amp;#039;&amp;#039;&amp;#039;: The filesystem is composed of multiple read-only layers (explained in section 4.4)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutable&amp;#039;&amp;#039;&amp;#039;: Once built, an image never changes&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Shareable&amp;#039;&amp;#039;&amp;#039;: Multiple containers can use the same image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Versionable&amp;#039;&amp;#039;&amp;#039;: Images can have tags (e.g., &amp;lt;code&amp;gt;nginx:1.21&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nginx:latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Distributable&amp;#039;&amp;#039;&amp;#039;: Images can be pushed to/pulled from registries (Docker Hub, private registries)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stored on disk&amp;#039;&amp;#039;&amp;#039;: Images consume storage even when not running&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Running Container:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A running container is an &amp;#039;&amp;#039;&amp;#039;instance&amp;#039;&amp;#039;&amp;#039; of an image—a process (or process tree) running in isolated namespaces with its own writable filesystem layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Ephemeral&amp;#039;&amp;#039;&amp;#039;: State is lost when the container is removed (unless using volumes)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mutable&amp;#039;&amp;#039;&amp;#039;: Can make changes inside the container (install packages, create files)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Process-based&amp;#039;&amp;#039;&amp;#039;: A container is fundamentally just a Linux process in isolated namespaces&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived&amp;#039;&amp;#039;&amp;#039;: Containers are typically created, used, and destroyed frequently&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful during runtime&amp;#039;&amp;#039;&amp;#039;: Maintains state while running, but that state disappears on removal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example to illustrate:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Pull an image (download the template)&lt;br /&gt;
docker pull nginx&lt;br /&gt;
&lt;br /&gt;
# The image now exists on disk&lt;br /&gt;
docker images&lt;br /&gt;
# Output shows: nginx  latest  a1b2c3d4  100MB&lt;br /&gt;
&lt;br /&gt;
# Start first container from this image&lt;br /&gt;
docker run -d --name web1 nginx&lt;br /&gt;
&lt;br /&gt;
# Start second container from the same image  &lt;br /&gt;
docker run -d --name web2 nginx&lt;br /&gt;
&lt;br /&gt;
# Both containers share the same base image filesystem&lt;br /&gt;
# But each has its own writable layer and separate namespaces&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now you have:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;One image&amp;#039;&amp;#039;&amp;#039; (nginx:latest) on disk&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Two containers&amp;#039;&amp;#039;&amp;#039; (web1 and web2) running as separate processes&lt;br /&gt;
* Each container has its own PID namespace, network namespace, etc.&lt;br /&gt;
* Changes in web1 don&amp;#039;t affect web2 (isolated writable layers)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verification:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Modify web1&lt;br /&gt;
docker exec web1 bash -c &amp;quot;echo &amp;#039;Hello from web1&amp;#039; &amp;gt; /usr/share/nginx/html/test.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Check web1&lt;br /&gt;
docker exec web1 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: Hello from web1&lt;br /&gt;
&lt;br /&gt;
# Check web2&lt;br /&gt;
docker exec web2 cat /usr/share/nginx/html/test.txt&lt;br /&gt;
# Output: cat: /usr/share/nginx/html/test.txt: No such file or directory&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The file exists in web1 but not in web2, even though they&amp;#039;re from the same image. Each container has its own writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Image to Container Relationship Diagram:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;          [nginx Image]&lt;br /&gt;
          (Read-only)&lt;br /&gt;
               |&lt;br /&gt;
     ┌─────────┴─────────┐&lt;br /&gt;
     ↓                   ↓&lt;br /&gt;
[Container 1]       [Container 2]&lt;br /&gt;
(Writable layer)    (Writable layer)&lt;br /&gt;
(PID namespace)     (PID namespace)&lt;br /&gt;
(Net namespace)     (Net namespace)&lt;br /&gt;
(Isolated)          (Isolated)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filesystem perspective:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Image Layers (read-only, shared):&lt;br /&gt;
├─ Layer 3: nginx files&lt;br /&gt;
├─ Layer 2: nginx dependencies  &lt;br /&gt;
└─ Layer 1: Base OS (Debian)&lt;br /&gt;
&lt;br /&gt;
Container 1 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 1&lt;br /&gt;
&lt;br /&gt;
Container 2 (writable, unique):&lt;br /&gt;
└─ Writable layer: Changes made in container 2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why this design?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Efficiency&amp;#039;&amp;#039;&amp;#039;: 100 containers from the same image share one copy of the base filesystem&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Speed&amp;#039;&amp;#039;&amp;#039;: Starting a container doesn&amp;#039;t require copying files—just create a new writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Consistency&amp;#039;&amp;#039;&amp;#039;: All containers from an image start with identical state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Immutability&amp;#039;&amp;#039;&amp;#039;: Encourages treating containers as disposable—don&amp;#039;t modify running containers, rebuild images instead&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle-and-ephemeral-nature&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle and Ephemeral Nature ===&lt;br /&gt;
&lt;br /&gt;
Understanding the container lifecycle is essential for proper container usage. Containers are designed to be ephemeral—short-lived and replaceable.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────┐&lt;br /&gt;
│ Created │ (Container exists but not running)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │ (Processes executing in isolated namespaces)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker stop (SIGTERM, then SIGKILL after timeout)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Stopped │ (Processes terminated, filesystem layer persists)&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker start (restart with same writable layer)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Running │&lt;br /&gt;
└────┬────┘&lt;br /&gt;
     │ docker rm (delete container)&lt;br /&gt;
     ↓&lt;br /&gt;
┌─────────┐&lt;br /&gt;
│ Removed │ (Container and writable layer deleted forever)&lt;br /&gt;
└─────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key lifecycle commands:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create and start in one step (most common)&lt;br /&gt;
docker run nginx&lt;br /&gt;
&lt;br /&gt;
# Create without starting&lt;br /&gt;
docker create --name test nginx&lt;br /&gt;
&lt;br /&gt;
# Start existing stopped container&lt;br /&gt;
docker start test&lt;br /&gt;
&lt;br /&gt;
# Stop running container (SIGTERM to main process)&lt;br /&gt;
docker stop test&lt;br /&gt;
&lt;br /&gt;
# Force stop (SIGKILL)&lt;br /&gt;
docker kill test&lt;br /&gt;
&lt;br /&gt;
# Remove stopped container&lt;br /&gt;
docker rm test&lt;br /&gt;
&lt;br /&gt;
# Remove running container (force)&lt;br /&gt;
docker rm -f test&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Ephemeral Nature:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, all changes made inside a container are lost when the container is removed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container&lt;br /&gt;
docker run -d --name demo nginx&lt;br /&gt;
&lt;br /&gt;
# Make changes inside&lt;br /&gt;
docker exec demo bash -c &amp;quot;echo &amp;#039;My data&amp;#039; &amp;gt; /tmp/important.txt&amp;quot;&lt;br /&gt;
docker exec demo cat /tmp/important.txt&lt;br /&gt;
# Output: My data&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop demo&lt;br /&gt;
docker rm demo&lt;br /&gt;
&lt;br /&gt;
# Try to access the data&lt;br /&gt;
docker run --name demo2 nginx&lt;br /&gt;
docker exec demo2 cat /tmp/important.txt&lt;br /&gt;
# Output: cat: /tmp/important.txt: No such file or directory&lt;br /&gt;
# THE DATA IS GONE!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why ephemeral?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This might seem like a limitation, but it&amp;#039;s actually a feature that enables important practices:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Immutable Infrastructure&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t patch running systems; deploy new versions&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Every deployment starts from a known state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Testing&amp;#039;&amp;#039;&amp;#039;: Test environments are identical to production&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Rollback&amp;#039;&amp;#039;&amp;#039;: Easy to roll back to previous image version&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Scaling&amp;#039;&amp;#039;&amp;#039;: Identical containers can be created/destroyed dynamically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When you need persistence:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For data that must survive container restarts, use &amp;#039;&amp;#039;&amp;#039;volumes&amp;#039;&amp;#039;&amp;#039; (covered in section 4.7):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create named volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use volume in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data in /data survives container removal&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container lifecycle best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Treat containers as cattle, not pets&amp;#039;&amp;#039;&amp;#039;: Don&amp;#039;t name them, don&amp;#039;t SSH into them to debug, don&amp;#039;t manually configure them&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Logs go to stdout/stderr&amp;#039;&amp;#039;&amp;#039;: Not to files inside the container (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Configuration via environment variables&amp;#039;&amp;#039;&amp;#039;: Not by editing files inside the container&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data goes in volumes&amp;#039;&amp;#039;&amp;#039;: Not in the container&amp;#039;s writable layer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Short-lived processes&amp;#039;&amp;#039;&amp;#039;: Containers should start quickly and shut down gracefully&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about process lifecycle (start, run, terminate). Containers follow a similar lifecycle, but operate at a higher level of abstraction—each container lifecycle event actually involves creating/destroying multiple processes in isolated namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;control-groups-cgroups-resource-limiting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Control Groups (cgroups): Resource Limiting ===&lt;br /&gt;
&lt;br /&gt;
Control groups (cgroups) are a Linux kernel feature that limits, accounts for, and isolates resource usage (CPU, memory, disk I/O, network) of process groups. Without cgroups, a runaway container could consume all CPU or memory, starving other containers and crashing the host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Without resource limits:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Malicious or buggy container&lt;br /&gt;
docker run -d evil-container&lt;br /&gt;
&lt;br /&gt;
# This container&amp;#039;s process could:&lt;br /&gt;
# - Consume 100% CPU (slow down everything else)&lt;br /&gt;
# - Allocate all available RAM (trigger OOM killer on host)&lt;br /&gt;
# - Fill up disk space (crash other containers)&lt;br /&gt;
# - Monopolize network bandwidth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable in multi-tenant environments. You need resource isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: cgroups&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Cgroups organize processes into hierarchical groups with configurable resource limits. The kernel enforces these limits, preventing processes from exceeding their allocation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cgroup Controllers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The Linux kernel provides several cgroup controllers, each managing a different resource type:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpu&amp;#039;&amp;#039;&amp;#039;: Limits CPU time&lt;br /&gt;
#* CPU shares (relative priority)&lt;br /&gt;
#* CPU quotas (hard limits)&lt;br /&gt;
#* CPU affinity (pin to specific cores)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;memory&amp;#039;&amp;#039;&amp;#039;: Limits RAM usage&lt;br /&gt;
#* Hard limits (container killed if exceeded)&lt;br /&gt;
#* Soft limits (reclaim memory under pressure)&lt;br /&gt;
#* Swap limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;blkio&amp;#039;&amp;#039;&amp;#039;: Limits disk I/O&lt;br /&gt;
#* Read/write bandwidth limits&lt;br /&gt;
#* I/O operation rate limits&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;net_cls/net_prio&amp;#039;&amp;#039;&amp;#039;: Network bandwidth control&lt;br /&gt;
#* Traffic classification&lt;br /&gt;
#* Priority settings&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;pids&amp;#039;&amp;#039;&amp;#039;: Limits number of processes&lt;br /&gt;
#* Prevents fork bombs&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;cpuset&amp;#039;&amp;#039;&amp;#039;: Assigns specific CPUs and memory nodes&lt;br /&gt;
#* NUMA awareness&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker&amp;#039;s cgroup integration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you start a container with resource limits, Docker configures the appropriate cgroups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --memory=512m --cpus=1.5 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a cgroup hierarchy at &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; and configures:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;memory.limit_in_bytes = 536870912&amp;lt;/code&amp;gt; (512MB)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt; to enforce 1.5 CPUs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Viewing cgroup settings:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Get container&amp;#039;s PID&lt;br /&gt;
docker inspect container --format &amp;#039;{{.State.Pid}}&amp;#039;&lt;br /&gt;
# Example: 12345&lt;br /&gt;
&lt;br /&gt;
# View memory limit&lt;br /&gt;
cat /sys/fs/cgroup/memory/docker/12345/memory.limit_in_bytes&lt;br /&gt;
# Output: 536870912&lt;br /&gt;
&lt;br /&gt;
# View CPU quota&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/12345/cpu.cfs_quota_us&lt;br /&gt;
# Output: 150000 (1.5 CPUs)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common resource limit flags:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Memory limits&lt;br /&gt;
--memory=512m              # Hard limit: 512MB RAM&lt;br /&gt;
--memory-reservation=256m  # Soft limit: try to stay under 256MB&lt;br /&gt;
--memory-swap=512m         # Total memory+swap limit&lt;br /&gt;
&lt;br /&gt;
# CPU limits&lt;br /&gt;
--cpus=1.5                 # Use at most 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512           # Relative CPU priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1          # Pin to CPU cores 0 and 1&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb    # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb   # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100           # Max 100 processes in container&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Testing memory limits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container with 256MB memory limit&lt;br /&gt;
docker run -it --memory=256m ubuntu bash&lt;br /&gt;
&lt;br /&gt;
# Inside container, try to allocate 512MB&lt;br /&gt;
# (requires &amp;#039;stress&amp;#039; tool)&lt;br /&gt;
apt-get update &amp;amp;&amp;amp; apt-get install -y stress&lt;br /&gt;
stress --vm 1 --vm-bytes 512M&lt;br /&gt;
&lt;br /&gt;
# Container is killed when exceeding limit&lt;br /&gt;
# Output: stress: FAIL: [1] (415) &amp;lt;-- worker 7 got signal 9&lt;br /&gt;
# Signal 9 = SIGKILL (OOM killer)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why cgroups are essential:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-tenancy&amp;#039;&amp;#039;&amp;#039;: Run untrusted workloads safely&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Quality of Service&amp;#039;&amp;#039;&amp;#039;: Guarantee resources for critical applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Fair sharing&amp;#039;&amp;#039;&amp;#039;: Prevent one container from monopolizing resources&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Predictability&amp;#039;&amp;#039;&amp;#039;: Know exactly how much resources each container can use&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Cost control&amp;#039;&amp;#039;&amp;#039;: In cloud environments, map cgroups to billing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;cgroup vs namespace distinction:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;isolation&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039;: Provide &amp;#039;&amp;#039;resource limits&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Both are necessary for containers. Namespaces prevent containers from seeing each other; cgroups prevent containers from starving each other.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-networking-bridges-veth-pairs-and-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Networking: Bridges, veth Pairs, and NAT ===&lt;br /&gt;
&lt;br /&gt;
Docker networking should feel familiar—it uses the exact same mechanisms you built manually in Lab 7. The primary difference is automation: Docker sets up bridges, veth pairs, routes, and NAT rules automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Default Docker Network Architecture:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you install Docker, it creates a default bridge network:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐&lt;br /&gt;
│  Container A    │  │  Container B    │  │  Container C    │&lt;br /&gt;
│  172.17.0.2     │  │  172.17.0.3     │  │  172.17.0.4     │&lt;br /&gt;
└────────┬────────┘  └────────┬────────┘  └────────┬────────┘&lt;br /&gt;
         │eth0               │eth0               │eth0&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
      (veth pair)         (veth pair)         (veth pair)&lt;br /&gt;
         │                   │                   │&lt;br /&gt;
    ┌────┴───────────────────┴───────────────────┴────┐&lt;br /&gt;
    │              docker0 Bridge                      │&lt;br /&gt;
    │              172.17.0.1/16                       │&lt;br /&gt;
    └────────────────────┬─────────────────────────────┘&lt;br /&gt;
                         │&lt;br /&gt;
                    [Host eth0]&lt;br /&gt;
                         │&lt;br /&gt;
                    [Internet]&lt;br /&gt;
                   (via NAT/MASQUERADE)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Component breakdown (all from Lab 7!):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Bridge Interface (&amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically creates a bridge interface when installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show docker0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;3: docker0: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    link/ether 02:42:8f:a3:f1:2a brd ff:ff:ff:ff:ff:ff&lt;br /&gt;
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; from Lab 7:&lt;br /&gt;
&lt;br /&gt;
* Bridge interface acting as virtual switch&lt;br /&gt;
* Assigned IP address 172.17.0.1&lt;br /&gt;
* Subnet 172.17.0.0/16 (65,536 addresses available)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. veth Pairs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For each container, Docker creates a veth pair:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep veth&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;8: veth7a3f2b1@if7: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&lt;br /&gt;
10: veth9d4e8c2@if9: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 master docker0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Decoding this:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;veth7a3f2b1@if7&amp;lt;/code&amp;gt;: One end of veth pair, connected to bridge (&amp;lt;code&amp;gt;master docker0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;@if7&amp;lt;/code&amp;gt;: Paired with interface index 7 (inside container)&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly what you did in Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-host type veth peer name v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker does the same thing, automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Container Network Namespace&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each container has its own network namespace (you built these manually in Lab 7!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View container&amp;#039;s network interfaces&lt;br /&gt;
docker exec container ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
&lt;br /&gt;
7: eth0@if8: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container sees:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Its own loopback interface&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if8&amp;lt;/code&amp;gt;: Its end of the veth pair (paired with host&amp;#039;s interface index 8)&lt;br /&gt;
* IP address from docker0 subnet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. IP Address Assignment&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker acts as a simple DHCP-like service, assigning IPs sequentially:&lt;br /&gt;
&lt;br /&gt;
* First container: 172.17.0.2&lt;br /&gt;
* Second container: 172.17.0.3&lt;br /&gt;
* Third container: 172.17.0.4&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Routing in Container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the container&amp;#039;s routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec container ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 172.17.0.1 dev eth0&lt;br /&gt;
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Default route: Send all traffic to 172.17.0.1 (the bridge) for routing to internet&lt;br /&gt;
* Local route: 172.17.0.0/16 is directly reachable via eth0&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the routing you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. NAT for Internet Access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker automatically configures iptables/nftables NAT rules (MASQUERADE) so containers can reach the internet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;MASQUERADE  all  --  172.17.0.0/16  0.0.0.0/0&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly the NAT you configured in Lab 7&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;7. Port Mapping (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you use &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;, Docker sets up Destination NAT (DNAT):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8080:80 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker adds an iptables DNAT rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo iptables -t nat -L -n | grep DNAT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:8080 to:172.17.0.2:80&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Translation:&amp;#039;&amp;#039;&amp;#039; Traffic arriving at host port 8080 is redirected to 172.17.0.2:80&lt;br /&gt;
&lt;br /&gt;
This is also NAT, specifically DNAT (Destination NAT). You encountered source NAT (SNAT/MASQUERADE) in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container-to-Container Communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Containers on the same bridge can communicate directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Container A pings Container B&lt;br /&gt;
docker exec containerA ping 172.17.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The packet flow:&lt;br /&gt;
&lt;br /&gt;
# Container A sends to 172.17.0.2 (Container B&amp;#039;s IP)&lt;br /&gt;
# Packet goes out Container A&amp;#039;s eth0 (through veth pair)&lt;br /&gt;
# Arrives on docker0 bridge&lt;br /&gt;
# Bridge forwards to veth pair for Container B&lt;br /&gt;
# Packet arrives at Container B&amp;#039;s eth0&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;No routing through host networking needed&amp;#039;&amp;#039;&amp;#039;—the bridge forwards directly (Layer 2 switching).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Custom Networks&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can create custom bridge networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.20.0.0/24 mynet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker creates a new bridge interface (e.g., &amp;lt;code&amp;gt;br-abc123def456&amp;lt;/code&amp;gt;) with subnet 10.20.0.0/24.&lt;br /&gt;
&lt;br /&gt;
Containers on different networks are isolated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --network=mynet --name=isolated nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This container is on &amp;lt;code&amp;gt;mynet&amp;lt;/code&amp;gt;, not &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;, so it cannot communicate with containers on the default bridge (different Layer 2 segment).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS Resolution Between Containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker provides automatic DNS resolution for container names within custom networks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create mynet&lt;br /&gt;
docker run -d --network=mynet --name=web nginx&lt;br /&gt;
docker run -d --network=mynet --name=app alpine&lt;br /&gt;
&lt;br /&gt;
# From app container&lt;br /&gt;
docker exec app ping web&lt;br /&gt;
# Resolves &amp;#039;web&amp;#039; to web container&amp;#039;s IP address!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Docker runs an embedded DNS server (listening on 127.0.0.11 inside containers) that resolves container names to IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Modes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker supports several network modes:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;bridge&amp;#039;&amp;#039;&amp;#039; (default): Container on docker0 bridge (or custom bridge)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039;: Container shares host&amp;#039;s network namespace (no isolation)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;none&amp;#039;&amp;#039;&amp;#039;: No networking (container has only loopback)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;container:name&amp;#039;&amp;#039;&amp;#039;: Share another container&amp;#039;s network namespace&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example: host mode&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --network=host nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container&amp;#039;s network namespace inode is the same as the host&amp;#039;s:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&lt;br /&gt;
# Output: net -&amp;gt; &amp;#039;net:[4026531992]&amp;#039;  # Same as host!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container sees all host&amp;#039;s network interfaces and can bind to any port on any interface.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Summary:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker networking uses:&lt;br /&gt;
&lt;br /&gt;
* Bridges (like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* veth pairs (like &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; ↔ &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Network namespaces (like &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt; from Lab 7)&lt;br /&gt;
* Routing tables and default routes&lt;br /&gt;
* NAT/MASQUERADE for internet access&lt;br /&gt;
* DNAT for port mapping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;volumes-persistent-and-shared-storage&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volumes: Persistent and Shared Storage ===&lt;br /&gt;
&lt;br /&gt;
By default, container filesystems are ephemeral—all changes are lost when the container is removed. For data that must persist (databases, user uploads, logs), Docker provides volumes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start database container&lt;br /&gt;
docker run -d --name db postgres&lt;br /&gt;
&lt;br /&gt;
# Database writes data&lt;br /&gt;
docker exec db psql -c &amp;quot;CREATE DATABASE myapp;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker rm -f db&lt;br /&gt;
&lt;br /&gt;
# Data is GONE FOREVER!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is unacceptable for stateful applications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution: Volumes&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Volumes are directories on the host that are mounted into containers. Data written to volumes persists beyond container lifecycle.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two Volume Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Named Volumes (Docker-managed):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker manages the volume storage location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# Use in container&lt;br /&gt;
docker run -v mydata:/data nginx&lt;br /&gt;
&lt;br /&gt;
# Data written to /data inside container is stored in:&lt;br /&gt;
# /var/lib/docker/volumes/mydata/_data (on host)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Docker manages storage location&lt;br /&gt;
* Portable across hosts (can be backed up, restored)&lt;br /&gt;
* Works with volume plugins (NFS, cloud storage)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Use named volumes for production databases, critical data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Bind Mounts (Host directory):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Mount a host directory directly into the container.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Mount host directory into container&lt;br /&gt;
docker run -v /home/user/html:/usr/share/nginx/html nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Any changes in &amp;lt;code&amp;gt;/home/user/html&amp;lt;/code&amp;gt; on the host are immediately visible in &amp;lt;code&amp;gt;/usr/share/nginx/html&amp;lt;/code&amp;gt; inside the container, and vice versa.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Direct access to files from host&lt;br /&gt;
* Useful for development (edit code on host, see changes in container immediately)&lt;br /&gt;
* No Docker management needed&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Disadvantages:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Tied to specific host filesystem paths&lt;br /&gt;
* Less portable&lt;br /&gt;
* Permissions can be tricky (host UID vs. container UID)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Docker essentially does:&lt;br /&gt;
mount --bind /var/lib/docker/volumes/mydata/_data /data&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Sharing Between Containers:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Multiple containers can share the same volume:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create shared&lt;br /&gt;
&lt;br /&gt;
# Container 1 writes&lt;br /&gt;
docker run -v shared:/data --name writer alpine sh -c &amp;quot;echo &amp;#039;Hello&amp;#039; &amp;gt; /data/file.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Container 2 reads&lt;br /&gt;
docker run -v shared:/data --name reader alpine cat /data/file.txt&lt;br /&gt;
# Output: Hello&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Use cases:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Shared configuration between containers&lt;br /&gt;
* Log aggregation (multiple containers write logs to shared volume)&lt;br /&gt;
* Data processing pipelines (one container produces, another consumes)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Volume Lifecycle:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create volume&lt;br /&gt;
docker volume create mydata&lt;br /&gt;
&lt;br /&gt;
# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect mydata&lt;br /&gt;
&lt;br /&gt;
# Remove volume (only if no containers using it)&lt;br /&gt;
docker volume rm mydata&lt;br /&gt;
&lt;br /&gt;
# Remove all unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Inspecting Volume Mounts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect container --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;volume&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/var/lib/docker/volumes/mydata/_data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;z&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;volume&amp;amp;quot; or &amp;amp;quot;bind&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Container-side path&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: Read-write (true) or read-only (false)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read-Only Volumes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For security, you can mount volumes read-only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -v mydata:/data:ro nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Container can read &amp;lt;code&amp;gt;/data&amp;lt;/code&amp;gt; but cannot write to it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;tmpfs Mounts (In-Memory Temporary Storage):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For sensitive data that should never touch disk:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run --tmpfs /tmp:size=100m nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; directory is stored in RAM and disappears when the container stops.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Named volumes for databases&amp;#039;&amp;#039;&amp;#039;: Postgres, MySQL, MongoDB&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bind mounts for development&amp;#039;&amp;#039;&amp;#039;: Code that you&amp;#039;re actively editing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;tmpfs for secrets&amp;#039;&amp;#039;&amp;#039;: Temporary credentials, keys&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volume plugins for cloud&amp;#039;&amp;#039;&amp;#039;: AWS EBS, Azure Disk, GCP Persistent Disk&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;laboratory-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Laboratory Exercises ==&lt;br /&gt;
&lt;br /&gt;
The following exercises build progressively, demonstrating how Docker automates the kernel-level primitives you mastered in previous labs. You will install Docker, inspect namespace isolation, explore interactive containers, configure persistent storage, and build a multi-container application with networking and resource limits.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-installing-docker&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Installing Docker ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Install Docker Engine from the official Docker repository and configure it for non-root access.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why the official repository?&amp;#039;&amp;#039;&amp;#039; Ubuntu&amp;#039;s default repositories often contain outdated Docker versions. The official Docker repository provides the latest stable releases with security updates and new features.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Remove old Docker versions (if any)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
If you previously installed Docker from Ubuntu&amp;#039;s repositories or older Docker installations exist, remove them to avoid conflicts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt remove docker docker-engine docker.io containerd runc&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It&amp;#039;s safe to run this even if these packages aren&amp;#039;t installed—apt will simply report they&amp;#039;re not present.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Install prerequisites&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Install packages needed for adding Docker&amp;#039;s repository:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    apt-transport-https \&lt;br /&gt;
    ca-certificates \&lt;br /&gt;
    curl \&lt;br /&gt;
    gnupg \&lt;br /&gt;
    lsb-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Add Docker&amp;#039;s GPG key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Docker signs its packages with a GPG key to ensure authenticity. Add this key to your system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo install -m 0755 -d /etc/apt/keyrings&lt;br /&gt;
&lt;br /&gt;
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \&lt;br /&gt;
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg&lt;br /&gt;
&lt;br /&gt;
sudo chmod a+r /etc/apt/keyrings/docker.gpg&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Creates &amp;lt;code&amp;gt;/etc/apt/keyrings/&amp;lt;/code&amp;gt; directory for storing repository keys&lt;br /&gt;
* Downloads Docker&amp;#039;s GPG public key&lt;br /&gt;
* Converts it to binary format (&amp;lt;code&amp;gt;.gpg&amp;lt;/code&amp;gt; file)&lt;br /&gt;
* Makes it readable by all users&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Add Docker repository to apt sources&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tee /etc/apt/sources.list.d/docker.sources &amp;lt;&amp;lt;EOF&lt;br /&gt;
Types: deb&lt;br /&gt;
URIs: https://download.docker.com/linux/ubuntu&lt;br /&gt;
Suites: $(. /etc/os-release &amp;amp;&amp;amp; echo &amp;quot;${UBUNTU_CODENAME:-$VERSION_CODENAME}&amp;quot;)&lt;br /&gt;
Components: stable&lt;br /&gt;
Signed-By: /etc/apt/keyrings/docker.asc&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What this does:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Detects your CPU architecture (amd64, arm64, etc.)&lt;br /&gt;
* Detects your Ubuntu version codename (focal, jammy, etc.)&lt;br /&gt;
* Adds Docker&amp;#039;s repository to apt&amp;#039;s source list&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Install Docker Engine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Update package index and install Docker:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y \&lt;br /&gt;
    docker-ce \&lt;br /&gt;
    docker-ce-cli \&lt;br /&gt;
    containerd.io \&lt;br /&gt;
    docker-buildx-plugin \&lt;br /&gt;
    docker-compose-plugin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packages installed:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce&amp;lt;/code&amp;gt;: Docker Community Edition engine (the main Docker daemon)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-ce-cli&amp;lt;/code&amp;gt;: Docker command-line interface&lt;br /&gt;
* &amp;lt;code&amp;gt;containerd.io&amp;lt;/code&amp;gt;: Container runtime that Docker uses under the hood&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-buildx-plugin&amp;lt;/code&amp;gt;: Extended build capabilities (multi-platform images)&lt;br /&gt;
* &amp;lt;code&amp;gt;docker-compose-plugin&amp;lt;/code&amp;gt;: Docker Compose for multi-container applications&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Verify Docker installation&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check Docker version:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo docker --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Docker version 24.0.7, build afdd53b&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your version number may be different (newer), which is fine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Start and enable Docker service&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Ensure Docker daemon starts on boot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check service status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl status docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;● docker.service - Docker Application Container Engine&lt;br /&gt;
     Loaded: loaded (/lib/systemd/system/docker.service; enabled)&lt;br /&gt;
     Active: active (running) since Thu 2024-12-12 10:30:00 UTC; 5min ago&amp;lt;/pre&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;Active: active (running)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Configure non-root Docker access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
By default, only root can run Docker commands. To run Docker without &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, add your user to the &amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; group:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important:&amp;#039;&amp;#039;&amp;#039; Log out and log back in for this change to take effect. Alternatively, you can run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;newgrp docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This starts a new shell with updated group membership.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Verify non-root access&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Test that you can run Docker without sudo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If this works without errors, you&amp;#039;ve successfully installed Docker!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;hello-world:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/hello-world&lt;br /&gt;
c1ec31eb5944: Pull complete&lt;br /&gt;
Digest: sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6&lt;br /&gt;
Status: Downloaded newer image for hello-world:latest&lt;br /&gt;
&lt;br /&gt;
Hello from Docker!&lt;br /&gt;
This message shows that your installation appears to be working correctly.&lt;br /&gt;
&lt;br /&gt;
To generate this message, Docker took the following steps:&lt;br /&gt;
 1. The Docker client contacted the Docker daemon.&lt;br /&gt;
 2. The Docker daemon pulled the &amp;amp;quot;hello-world&amp;amp;quot; image from the Docker Hub.&lt;br /&gt;
 3. The Docker daemon created a new container from that image which runs the&lt;br /&gt;
    executable that produces the output you are currently reading.&lt;br /&gt;
 4. The Docker daemon streamed that output to the Docker client, which sent it&lt;br /&gt;
    to your terminal.&lt;br /&gt;
&lt;br /&gt;
To try something more ambitious, you can run an Ubuntu container with:&lt;br /&gt;
 $ docker run -it ubuntu bash&lt;br /&gt;
&lt;br /&gt;
Share images, automate workflows, and more with a free Docker ID:&lt;br /&gt;
 https://hub.docker.com/&lt;br /&gt;
&lt;br /&gt;
For more examples and ideas, visit:&lt;br /&gt;
 https://docs.docker.com/get-started/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker client (&amp;lt;code&amp;gt;docker&amp;lt;/code&amp;gt; command) connected to Docker daemon (dockerd)&lt;br /&gt;
# Daemon checked local images—didn&amp;#039;t find &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt;&lt;br /&gt;
# Daemon pulled &amp;lt;code&amp;gt;hello-world&amp;lt;/code&amp;gt; image from Docker Hub (public registry)&lt;br /&gt;
# Daemon created container from image&lt;br /&gt;
# Container executed its program (printed the message)&lt;br /&gt;
# Container exited&lt;br /&gt;
# Output sent back to your terminal&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up test container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the hello-world container with status &amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm $(docker ps -aq --filter &amp;quot;ancestor=hello-world&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify it&amp;#039;s gone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo systemctl status docker&amp;lt;/code&amp;gt; (showing Active: active (running))&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the entire message)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-hello-world-and-namespace-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Hello World and Namespace Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run your first container, understand the container lifecycle, and inspect the Linux kernel namespaces that provide container isolation—connecting directly to your work in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-hello-world&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Hello World ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run the hello-world container (if not done in Exercise A)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
We covered this in Exercise A, but let&amp;#039;s examine what actually happened in more detail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: List all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Nothing (empty list)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why?&amp;#039;&amp;#039;&amp;#039; The hello-world container ran, printed its message, and exited immediately. &amp;lt;code&amp;gt;docker ps&amp;lt;/code&amp;gt; only shows running containers by default.&lt;br /&gt;
&lt;br /&gt;
To see all containers (including stopped):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES&lt;br /&gt;
a1b2c3d4e5f6   hello-world   &amp;amp;quot;/hello&amp;amp;quot;   10 seconds ago   Exited (0) 8 seconds ago              eager_tesla&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the fields:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CONTAINER ID&amp;#039;&amp;#039;&amp;#039;: Short hex identifier (first 12 chars of full 64-char ID)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IMAGE&amp;#039;&amp;#039;&amp;#039;: Which image this container was created from&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;COMMAND&amp;#039;&amp;#039;&amp;#039;: The process that ran inside the container (&amp;lt;code&amp;gt;/hello&amp;lt;/code&amp;gt; executable)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CREATED&amp;#039;&amp;#039;&amp;#039;: When the container was created&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;STATUS&amp;#039;&amp;#039;&amp;#039;: Current state—&amp;lt;code&amp;gt;Exited (0)&amp;lt;/code&amp;gt; means process exited with code 0 (success)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PORTS&amp;#039;&amp;#039;&amp;#039;: Port mappings (none for hello-world)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;NAMES&amp;#039;&amp;#039;&amp;#039;: Random name if you don&amp;#039;t specify one (Docker generates names like &amp;amp;quot;eager_tesla&amp;amp;quot;, &amp;amp;quot;hopeful_darwin&amp;amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get detailed information about the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect eager_tesla  # Use your actual container name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This outputs a large JSON document with all container metadata. Let&amp;#039;s extract specific fields:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Just the State section&lt;br /&gt;
docker inspect eager_tesla --format=&amp;#039;{{json .State}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (formatted):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;Status&amp;quot;: &amp;quot;exited&amp;quot;,&lt;br /&gt;
    &amp;quot;Running&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Paused&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Restarting&amp;quot;: false,&lt;br /&gt;
    &amp;quot;OOMKilled&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Dead&amp;quot;: false,&lt;br /&gt;
    &amp;quot;Pid&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;ExitCode&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;StartedAt&amp;quot;: &amp;quot;2024-12-12T10:35:00Z&amp;quot;,&lt;br /&gt;
    &amp;quot;FinishedAt&amp;quot;: &amp;quot;2024-12-12T10:35:01Z&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container ran for about 1 second (started at :00, finished at :01)&lt;br /&gt;
* Exit code 0 (successful completion)&lt;br /&gt;
* PID is 0 (process has terminated; while running it had a real PID)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: View container logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Even though the container exited, Docker saved its output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows the hello-world message again. This demonstrates that Docker captures stdout/stderr from containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Stopped containers still consume disk space (their writable layer persists). Remove it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm eager_tesla&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify removal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The container should be gone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-namespaces-connect-to-lab-7&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Namespaces (Connect to Lab 7!) ====&lt;br /&gt;
&lt;br /&gt;
Now let&amp;#039;s run a longer-lived container and examine its namespace isolation—this directly connects to your hands-on work with network namespaces in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run a persistent container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode—run in background&lt;br /&gt;
* &amp;lt;code&amp;gt;--name inspector&amp;lt;/code&amp;gt;: Give it a memorable name instead of random name&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;nginx:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/nginx&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for nginx:latest&lt;br /&gt;
b7f9a8e6c4d3a1b2c5e8f9d2a7c4b6e8f3d9a2c1b4e7f8d3a5c2b1&amp;lt;/pre&amp;gt;&lt;br /&gt;
The long hex string is the full 64-character container ID. Docker returns this after creating the container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify the container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND                  CREATED          STATUS          PORTS     NAMES&lt;br /&gt;
b7f9a8e6c4d3   nginx   &amp;amp;quot;/docker-entrypoint.…&amp;amp;quot;   10 seconds ago   Up 8 seconds    80/tcp    inspector&amp;lt;/pre&amp;gt;&lt;br /&gt;
The container is running nginx web server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Get the container&amp;#039;s PID on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Remember: containers are just processes. Let&amp;#039;s find the PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; A number like &amp;lt;code&amp;gt;12345&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the process ID on the &amp;#039;&amp;#039;&amp;#039;host&amp;#039;&amp;#039;&amp;#039; system. Let&amp;#039;s verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep 12345&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see nginx processes! The container is just a process with a fancy namespace wrapper.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Examine the container&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial step connecting to Lab 7. Replace 12345 with your actual PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/12345/ns/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 0&lt;br /&gt;
dr-x--x--x 2 root root 0 Dec 12 10:40 .&lt;br /&gt;
dr-xr-xr-x 9 root root 0 Dec 12 10:40 ..&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 cgroup -&amp;amp;gt; &amp;#039;cgroup:[4026533671]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 ipc -&amp;amp;gt; &amp;#039;ipc:[4026533669]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 mnt -&amp;amp;gt; &amp;#039;mnt:[4026533667]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026533672]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 pid_for_children -&amp;amp;gt; &amp;#039;pid:[4026533670]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 user -&amp;amp;gt; &amp;#039;user:[4026531837]&amp;#039;&lt;br /&gt;
lrwxrwxrwx 1 root root 0 Dec 12 10:40 uts -&amp;amp;gt; &amp;#039;uts:[4026533668]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis of namespace inodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Note the inode numbers (the numbers in brackets). Each represents a namespace instance.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Compare with host&amp;#039;s namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;lrwxrwxrwx 1 youruser youruser 0 Dec 12 10:40 net -&amp;amp;gt; &amp;#039;net:[4026531992]&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical observation:&amp;#039;&amp;#039;&amp;#039; The container&amp;#039;s network namespace inode (&amp;lt;code&amp;gt;4026533672&amp;lt;/code&amp;gt;) is &amp;#039;&amp;#039;&amp;#039;different&amp;#039;&amp;#039;&amp;#039; from the host&amp;#039;s (&amp;lt;code&amp;gt;4026531992&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Compare two containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Start a second container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d --name inspector2 nginx&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Get its PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect inspector2 --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check its network namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /proc/NEW_PID/ns/net&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; A different inode number from both the host and &amp;lt;code&amp;gt;inspector&amp;lt;/code&amp;gt; container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion:&amp;#039;&amp;#039;&amp;#039; Each container has its own isolated network namespace, just like the red and blue namespaces you created manually in Lab 7.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Examine other namespaces&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s check PID namespace isolation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host PID namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/pid&lt;br /&gt;
&lt;br /&gt;
# Container PID namespace  &lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/pid&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Different inodes = isolated process trees!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: User namespace (often shared)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Host user namespace&lt;br /&gt;
sudo ls -la /proc/$$/ns/user&lt;br /&gt;
&lt;br /&gt;
# Container user namespace&lt;br /&gt;
sudo ls -la /proc/CONTAINER_PID/ns/user&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Often the &amp;#039;&amp;#039;&amp;#039;same&amp;#039;&amp;#039;&amp;#039; inode number.&lt;br /&gt;
&lt;br /&gt;
Many Docker configurations share the host&amp;#039;s user namespace for simplicity. This means UID 0 in the container is UID 0 on the host (less secure, but more compatible).&lt;br /&gt;
&lt;br /&gt;
For enhanced security, Docker can be configured to use separate user namespaces (rootless Docker), but that&amp;#039;s beyond this lab&amp;#039;s scope.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Enter the container&amp;#039;s namespace with nsenter&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You can actually enter a container&amp;#039;s namespaces using the &amp;lt;code&amp;gt;nsenter&amp;lt;/code&amp;gt; command (this is how &amp;lt;code&amp;gt;docker exec&amp;lt;/code&amp;gt; works!):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nsenter --target CONTAINER_PID --net ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This executes &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; inside the container&amp;#039;s network namespace, showing the container&amp;#039;s view of network interfaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop inspector inspector2&lt;br /&gt;
docker rm inspector inspector2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable B ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker run hello-world&amp;lt;/code&amp;gt; (the full hello message)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker ps -a&amp;lt;/code&amp;gt; showing the exited hello-world container with its random name&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect inspector --format &amp;#039;{{.State.Pid}}&amp;#039;&amp;lt;/code&amp;gt; (showing the PID)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;sudo ls -la /proc/PID/ns/&amp;lt;/code&amp;gt; for the inspector container (showing all namespaces)&lt;br /&gt;
# Side-by-side comparison:&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/$$/ns/net&amp;lt;/code&amp;gt; (host&amp;#039;s network namespace inode)&lt;br /&gt;
#* &amp;lt;code&amp;gt;sudo ls -la /proc/CONTAINER_PID/ns/net&amp;lt;/code&amp;gt; (container&amp;#039;s network namespace inode)&lt;br /&gt;
#* Highlight that the inode numbers are different&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-interactive-exploration-with-fedora&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Interactive Exploration with Fedora ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run an interactive container with a different Linux distribution (Fedora instead of Ubuntu), demonstrating mount namespace isolation (different root filesystems), PID namespace isolation (isolated process tree), and UTS namespace isolation (different hostname).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important context:&amp;#039;&amp;#039;&amp;#039; Your host might be running Ubuntu, but the container will run Fedora. Both will be using the same Linux kernel, but they&amp;#039;ll have completely different filesystems and will appear as different &amp;amp;quot;machines&amp;amp;quot; from inside.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Run Fedora container interactively&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -it --name fedora-explore fedora bash&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Interactive—keep STDIN open&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Allocate pseudo-TTY (terminal)&lt;br /&gt;
* &amp;lt;code&amp;gt;fedora&amp;lt;/code&amp;gt;: Pull Fedora base image from Docker Hub&lt;br /&gt;
* &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;: Command to run inside container (start bash shell)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Docker downloads Fedora base image (if not cached)&lt;br /&gt;
# Creates container from image&lt;br /&gt;
# Starts bash inside the container&lt;br /&gt;
# Attaches your terminal to that bash session&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;fedora:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/fedora&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for fedora:latest&lt;br /&gt;
[root@a1b2c3d4e5f6 /]#&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe the prompt change:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Before: &amp;lt;code&amp;gt;user@hostname:~$&amp;lt;/code&amp;gt; (your normal shell)&lt;br /&gt;
* After: &amp;lt;code&amp;gt;[root@a1b2c3d4e5f6 /]#&amp;lt;/code&amp;gt; (inside container)&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the Fedora container!&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Explore the filesystem (Mount Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Check the operating system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Fedora Linux&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;39 (Container Image)&amp;amp;quot;&lt;br /&gt;
ID=fedora&lt;br /&gt;
VERSION_ID=39&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Open a &amp;#039;&amp;#039;&amp;#039;new terminal&amp;#039;&amp;#039;&amp;#039; on your host (don&amp;#039;t close the container terminal) and run:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/os-release&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output on host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;NAME=&amp;amp;quot;Ubuntu&amp;amp;quot;&lt;br /&gt;
VERSION=&amp;amp;quot;22.04.3 LTS (Jammy Jellyfish)&amp;amp;quot;&lt;br /&gt;
ID=ubuntu&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Two different operating systems on the same machine!&amp;#039;&amp;#039;&amp;#039; This is mount namespace isolation—the container has its own root filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Back in the container&amp;#039;&amp;#039;&amp;#039;, explore the filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Fedora&amp;#039;s files, not your host&amp;#039;s Ubuntu files. They&amp;#039;re completely different directory trees.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try to use Ubuntu&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;apt update&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bash: apt: command not found&amp;lt;/pre&amp;gt;&lt;br /&gt;
apt doesn&amp;#039;t exist in Fedora! Fedora uses a different package manager.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try Fedora&amp;#039;s package manager:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf --version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;4.18.2&lt;br /&gt;
  Installed: dnf-0:4.18.2-1.fc39.noarch&lt;br /&gt;
  ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
dnf exists because we&amp;#039;re in a Fedora environment.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Install a package:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dnf install -y nano&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This works! We can install packages just like on a real Fedora system.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 2, you learned about the filesystem hierarchy (&amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt;, etc.). Mount namespaces let the container have completely different contents at these paths. The container&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; is different from the host&amp;#039;s &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Examine process isolation (PID Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container, check running processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND&lt;br /&gt;
root           1  0.0  0.0  12345  2345 pts/0    Ss   10:45   0:00 bash&lt;br /&gt;
root          67  0.0  0.0  44567  3456 pts/0    R+   10:47   0:00 ps aux&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Only two processes visible!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* PID 1: bash (the container&amp;#039;s init process)&lt;br /&gt;
* PID 67: ps command we just ran&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host&amp;#039;&amp;#039;&amp;#039; (in your other terminal):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; 200+ processes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The container cannot see the host&amp;#039;s processes!&amp;#039;&amp;#039;&amp;#039; This is PID namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the container&amp;#039;s perspective, bash is PID 1&amp;#039;&amp;#039;&amp;#039; (like systemd is PID 1 on a normal Linux system).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From the host&amp;#039;s perspective, that same bash process has a different PID&amp;#039;&amp;#039;&amp;#039; (e.g., 12345).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 3:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Lab 3, you learned about PIDs and the process hierarchy. PID namespaces create separate process hierarchies—the container has its own process tree starting from PID 1.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Check hostname (UTS Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the container ID (first 12 characters of the full container ID).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;your-hostname.example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Different hostnames! This is UTS namespace isolation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Examine network configuration (Network Namespace)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Inside the container:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000&lt;br /&gt;
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&lt;br /&gt;
    inet 127.0.0.1/8 scope host lo&lt;br /&gt;
       valid_lft forever preferred_lft forever&lt;br /&gt;
       &lt;br /&gt;
17: eth0@if18: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP group default &lt;br /&gt;
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0&lt;br /&gt;
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0&lt;br /&gt;
       valid_lft forever preferred_lft forever&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: Container&amp;#039;s loopback interface (127.0.0.1)&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0@if18&amp;lt;/code&amp;gt;: Container&amp;#039;s network interface (part of veth pair)&lt;br /&gt;
** &amp;lt;code&amp;gt;@if18&amp;lt;/code&amp;gt;: Indicates this interface is paired with interface index 18 (on the host)&lt;br /&gt;
** IP address: 172.17.0.2 (from docker0 bridge subnet)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;On the host:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show | grep &amp;quot;172.17&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the docker0 bridge has IP 172.17.0.1, and you might see veth interfaces for containers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you built manually!&lt;br /&gt;
&lt;br /&gt;
* docker0 bridge ≈ br-lab from Lab 7&lt;br /&gt;
* Container&amp;#039;s eth0 ≈ v-client from Lab 7&lt;br /&gt;
* veth pair connects container to bridge ≈ Lab 7&amp;#039;s veth pair architecture&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test internet connectivity from container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Ping succeeds!&lt;br /&gt;
&lt;br /&gt;
This works because:&lt;br /&gt;
&lt;br /&gt;
# Container has default route to 172.17.0.1 (docker0 bridge)&lt;br /&gt;
# Host has IP forwarding enabled&lt;br /&gt;
# Host has NAT rule (MASQUERADE) for 172.17.0.0/16&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is identical to what you configured in Lab 7!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Try to see host&amp;#039;s processes (will fail)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ps aux | grep systemd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; No systemd processes visible.&lt;br /&gt;
&lt;br /&gt;
You cannot see the host&amp;#039;s processes from inside the container (PID namespace isolation).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Exit the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you exit bash (PID 1 in the container), the container stops automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verify the container stopped:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE    COMMAND   CREATED         STATUS                     NAMES&lt;br /&gt;
a1b2c3d4e5f6   fedora   &amp;amp;quot;bash&amp;amp;quot;    5 minutes ago   Exited (0) 10 seconds ago  fedora-explore&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The container still exists (STATUS: Exited), but it&amp;#039;s not running. You can restart it with &amp;lt;code&amp;gt;docker start fedora-explore&amp;lt;/code&amp;gt; if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm fedora-explore&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable C ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Fedora)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;cat /etc/os-release&amp;lt;/code&amp;gt; (showing Ubuntu or your host OS)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux&amp;lt;/code&amp;gt; (showing minimal processes, bash as PID 1)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ps aux | wc -l&amp;lt;/code&amp;gt; (showing many more processes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing container ID)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;On host&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;hostname&amp;lt;/code&amp;gt; (showing host&amp;#039;s hostname)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Inside container&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt; (showing eth0 with 172.17.0.x address)&lt;br /&gt;
# Brief explanation (4-5 sentences): What do these differences demonstrate about namespace isolation? How does this relate to what you learned in Labs 2, 3, and 7?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-persistent-storage-with-caddy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Persistent Storage with Caddy ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Run Caddy web server with persistent configuration and content using bind mounts, demonstrating that data can survive container removal and be shared between host and container.&lt;br /&gt;
&lt;br /&gt;
Caddy is the web server you&amp;#039;ve been using throughout Lab 9. We&amp;#039;ll run it in a container and configure it using files from the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-basic-caddy-container&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Basic Caddy Container ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create directory structure on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-caddy/{site,data,config}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Directory purposes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;site&amp;lt;/code&amp;gt;: Website content (HTML files)&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data directory (certificates, storage)&lt;br /&gt;
* &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt;: Caddy configuration (Caddyfile)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Create a simple website&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;This is running in a Docker container.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The file you&amp;#039;re viewing is mounted from the host filesystem.&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;Changes made on the host appear instantly in the container!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Create Caddyfile configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-caddy/config/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Caddyfile explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;:80&amp;lt;/code&amp;gt;: Listen on port 80 (inside container)&lt;br /&gt;
* &amp;lt;code&amp;gt;root * /usr/share/caddy&amp;lt;/code&amp;gt;: Serve files from this directory&lt;br /&gt;
* &amp;lt;code&amp;gt;file_server&amp;lt;/code&amp;gt;: Enable static file serving&lt;br /&gt;
* &amp;lt;code&amp;gt;log&amp;lt;/code&amp;gt;: Send access logs to stdout (so &amp;lt;code&amp;gt;docker logs&amp;lt;/code&amp;gt; can capture them)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Run Caddy container with volume mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-persistent \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Breaking down the command:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-d&amp;lt;/code&amp;gt;: Detached mode (run in background)&lt;br /&gt;
* &amp;lt;code&amp;gt;--name caddy-persistent&amp;lt;/code&amp;gt;: Give container a memorable name&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: Port mapping&lt;br /&gt;
** Host port 8080 → Container port 80&lt;br /&gt;
** This is NAT (DNAT specifically) from Lab 7!&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/site:/usr/share/caddy&amp;lt;/code&amp;gt;: Bind mount&lt;br /&gt;
** Host directory &amp;lt;code&amp;gt;~/lab10-caddy/site&amp;lt;/code&amp;gt; appears at &amp;lt;code&amp;gt;/usr/share/caddy&amp;lt;/code&amp;gt; inside container&lt;br /&gt;
** Bidirectional: changes on either side are visible on both sides&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/data:/data&amp;lt;/code&amp;gt;: Caddy&amp;#039;s data storage&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-caddy/config:/etc/caddy&amp;lt;/code&amp;gt;: Caddy&amp;#039;s configuration&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: Image to use&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Unable to find image &amp;#039;caddy:latest&amp;#039; locally&lt;br /&gt;
latest: Pulling from library/caddy&lt;br /&gt;
...&lt;br /&gt;
Status: Downloaded newer image for caddy:latest&lt;br /&gt;
f8e9c7b6d5a4e3b2c1f7d8a9e6b5c4d3a2f1e8d7c6b5a4e3d2c1b9f8&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Verify container is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS                  NAMES&lt;br /&gt;
f8e9c7b6d5a4   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    0.0.0.0:8080-&amp;amp;gt;80/tcp   caddy-persistent&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note the PORTS column:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;0.0.0.0:8080-&amp;amp;gt;80/tcp&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This means:&lt;br /&gt;
&lt;br /&gt;
* Listen on all host interfaces (&amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Host port 8080 maps to container port 80&lt;br /&gt;
* Protocol: TCP&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Test the web server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Your HTML page!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Docker Caddy Demo&amp;lt;/title&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Hello from Dockerized Caddy!&amp;lt;/h1&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/html&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also open &amp;lt;code&amp;gt;http://localhost:8080&amp;lt;/code&amp;gt; in a web browser on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: View Caddy logs&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker logs caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.456,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;using provided configuration&amp;amp;quot;,...}&lt;br /&gt;
{&amp;amp;quot;level&amp;amp;quot;:&amp;amp;quot;info&amp;amp;quot;,&amp;amp;quot;ts&amp;amp;quot;:1702389123.789,&amp;amp;quot;msg&amp;amp;quot;:&amp;amp;quot;serving initial configuration&amp;amp;quot;}&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Caddy&amp;#039;s startup logs. When you access the website, you&amp;#039;ll see access logs here too.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-inspect-mounts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Inspect Mounts ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Inspect the container&amp;#039;s mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/site&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/usr/share/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/data&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,&lt;br /&gt;
        &amp;quot;Source&amp;quot;: &amp;quot;/home/youruser/lab10-caddy/config&amp;quot;,&lt;br /&gt;
        &amp;quot;Destination&amp;quot;: &amp;quot;/etc/caddy&amp;quot;,&lt;br /&gt;
        &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
        &amp;quot;RW&amp;quot;: true,&lt;br /&gt;
        &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Field explanations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Type&amp;lt;/code&amp;gt;: &amp;amp;quot;bind&amp;amp;quot; (bind mount, not Docker-managed volume)&lt;br /&gt;
* &amp;lt;code&amp;gt;Source&amp;lt;/code&amp;gt;: Host filesystem path&lt;br /&gt;
* &amp;lt;code&amp;gt;Destination&amp;lt;/code&amp;gt;: Path inside container&lt;br /&gt;
* &amp;lt;code&amp;gt;RW&amp;lt;/code&amp;gt;: true (read-write), false would be read-only&lt;br /&gt;
* &amp;lt;code&amp;gt;Propagation&amp;lt;/code&amp;gt;: How mount events propagate (rprivate = don&amp;#039;t propagate to other mounts)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: View from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent df -h | grep caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Shows mounted filesystems inside the container containing &amp;amp;quot;caddy&amp;amp;quot; in the path.&lt;br /&gt;
&lt;br /&gt;
You can also list the files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent ls -la /usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 12&lt;br /&gt;
drwxr-xr-x 2 root root 4096 Dec 12 10:50 .&lt;br /&gt;
drwxr-xr-x 1 root root 4096 Dec 12 10:50 ..&lt;br /&gt;
-rw-r--r-- 1 root root  687 Dec 12 10:50 index.html&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the same &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; you created on the host!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-test-persistence&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Test Persistence ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Modify the file on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without stopping the container&amp;#039;&amp;#039;&amp;#039;, edit the HTML file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt;&amp;gt; ~/lab10-caddy/site/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;🎉 This was added from the host!&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;The container is still running, and changes appear instantly.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Verify the change appears in the container immediately&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; The new section appears!&lt;br /&gt;
&lt;br /&gt;
You didn&amp;#039;t restart the container, yet the content changed. This demonstrates &amp;#039;&amp;#039;&amp;#039;bidirectional bind mount synchronization&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Create a new file from inside the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker exec caddy-persistent bash -c &amp;quot;echo &amp;#039;&amp;lt;h2&amp;gt;Created from container&amp;lt;/h2&amp;gt;&amp;#039; &amp;gt; /usr/share/caddy/test.html&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify the file appears on the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwxr-xr-x 2 youruser youruser 4096 Dec 12 11:00 .&lt;br /&gt;
drwxr-xr-x 5 youruser youruser 4096 Dec 12 10:45 ..&lt;br /&gt;
-rw-r--r-- 1 youruser youruser  687 Dec 12 10:50 index.html&lt;br /&gt;
-rw-r--r-- 1 root     root       34 Dec 12 11:00 test.html  ← New file!&amp;lt;/pre&amp;gt;&lt;br /&gt;
The file exists on the host! Changes flow both directions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The file is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; because the process inside the container runs as root. This is one of the complexities of bind mounts—UID/GID mapping between host and container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Stop and remove the container&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-persistent&lt;br /&gt;
docker rm caddy-persistent&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Verify files still exist on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All files still there!&lt;br /&gt;
&lt;br /&gt;
The files survive because they&amp;#039;re stored on the host filesystem, not in the container&amp;#039;s ephemeral writable layer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start a NEW container with the same bind mounts&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name caddy-new \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-caddy/site:/usr/share/caddy \&lt;br /&gt;
  -v ~/lab10-caddy/data:/data \&lt;br /&gt;
  -v ~/lab10-caddy/config:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Verify persistence&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; All your previous content is still there!&lt;br /&gt;
&lt;br /&gt;
The website works immediately because all the content and configuration was stored on the host, not inside the old container.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Compare to ephemeral storage&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s demonstrate what happens without volumes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start container without volumes&lt;br /&gt;
docker run -d --name caddy-temp caddy&lt;br /&gt;
&lt;br /&gt;
# Create file inside container&lt;br /&gt;
docker exec caddy-temp bash -c &amp;quot;echo &amp;#039;temporary&amp;#039; &amp;gt; /tmp/temp.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Verify file exists&lt;br /&gt;
docker exec caddy-temp cat /tmp/temp.txt&lt;br /&gt;
# Output: temporary&lt;br /&gt;
&lt;br /&gt;
# Stop and remove container&lt;br /&gt;
docker stop caddy-temp&lt;br /&gt;
docker rm caddy-temp&lt;br /&gt;
&lt;br /&gt;
# Try to access the file in a new container&lt;br /&gt;
docker run -d --name caddy-temp2 caddy&lt;br /&gt;
docker exec caddy-temp2 cat /tmp/temp.txt&lt;br /&gt;
# Output: cat: /tmp/temp.txt: No such file or directory&lt;br /&gt;
&lt;br /&gt;
# The file is GONE because it was in the ephemeral layer&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Clean up&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop caddy-new&lt;br /&gt;
docker rm caddy-new&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The files in &amp;lt;code&amp;gt;~/lab10-caddy/&amp;lt;/code&amp;gt; remain intact on your host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# What Docker essentially does:&lt;br /&gt;
mount --bind ~/lab10-caddy/site /var/lib/docker/overlay2/.../merged/usr/share/caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable D ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing your custom HTML (initial version)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect caddy-persistent --format=&amp;#039;{{json .Mounts}}&amp;#039;&amp;lt;/code&amp;gt; (formatted with python3 -m json.tool)&lt;br /&gt;
# After modifying &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; on the host, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; showing the new content (without restarting container)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;ls -la ~/lab10-caddy/site/&amp;lt;/code&amp;gt; showing both &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; and the &amp;lt;code&amp;gt;test.html&amp;lt;/code&amp;gt; created from inside the container&lt;br /&gt;
# After removing the original container and starting &amp;lt;code&amp;gt;caddy-new&amp;lt;/code&amp;gt;, output of &amp;lt;code&amp;gt;curl http://localhost:8080&amp;lt;/code&amp;gt; demonstrating that content persisted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-multi-container-infrastructure-with-networking-and-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Multi-Container Infrastructure with Networking and Resource Limits ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; Build a complete three-tier architecture with:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Purple&amp;#039;&amp;#039;&amp;#039; container acting as reverse proxy (routes requests based on URL path)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Red&amp;#039;&amp;#039;&amp;#039; container serving backend content (memory-limited)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blue&amp;#039;&amp;#039;&amp;#039; container serving backend content (CPU-limited)&lt;br /&gt;
&lt;br /&gt;
This exercise synthesizes everything from Labs 7-9:&lt;br /&gt;
&lt;br /&gt;
* Custom networks (Lab 7 bridges)&lt;br /&gt;
* Container-to-container communication (Lab 7 veth pairs and routing)&lt;br /&gt;
* Reverse proxy with path-based routing (Lab 9 Caddy configuration)&lt;br /&gt;
* Resource limits (cgroups)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-create-a-custom-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Create a Custom Network ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a custom bridge network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network create --subnet=10.10.0.0/24 labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a new bridge (just like &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt; from Lab 7) with subnet 10.10.0.0/24.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the network ID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Inspect the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (abbreviated):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;labnet&amp;quot;,&lt;br /&gt;
        &amp;quot;Id&amp;quot;: &amp;quot;a1b2c3d4e5f6...&amp;quot;,&lt;br /&gt;
        &amp;quot;Driver&amp;quot;: &amp;quot;bridge&amp;quot;,&lt;br /&gt;
        &amp;quot;IPAM&amp;quot;: {&lt;br /&gt;
            &amp;quot;Config&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                    &amp;quot;Subnet&amp;quot;: &amp;quot;10.10.0.0/24&amp;quot;,&lt;br /&gt;
                    &amp;quot;Gateway&amp;quot;: &amp;quot;10.10.0.1&amp;quot;&lt;br /&gt;
                }&lt;br /&gt;
            ]&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;Containers&amp;quot;: {},&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key observations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Driver: &amp;amp;quot;bridge&amp;amp;quot;&amp;lt;/code&amp;gt;: Uses bridge networking (Layer 2 switching)&lt;br /&gt;
* &amp;lt;code&amp;gt;Subnet: &amp;amp;quot;10.10.0.0/24&amp;amp;quot;&amp;lt;/code&amp;gt;: Our specified subnet&lt;br /&gt;
* &amp;lt;code&amp;gt;Gateway: &amp;amp;quot;10.10.0.1&amp;amp;quot;&amp;lt;/code&amp;gt;: Docker automatically assigns .1 as gateway&lt;br /&gt;
* &amp;lt;code&amp;gt;Containers: {}&amp;lt;/code&amp;gt;: No containers connected yet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Verify bridge creation on host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;5: br-a1b2c3d4e5f6: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default&amp;lt;/pre&amp;gt;&lt;br /&gt;
Docker created a new bridge interface! The name includes the network ID prefix.&lt;br /&gt;
&lt;br /&gt;
You can also use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;brctl show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bridge name          bridge id          STP enabled    interfaces&lt;br /&gt;
br-a1b2c3d4e5f6      8000.0242a1b2c3d4  no&lt;br /&gt;
docker0              8000.0242f7a8b9c0  no&amp;lt;/pre&amp;gt;&lt;br /&gt;
Two bridges:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;docker0&amp;lt;/code&amp;gt;: Default Docker bridge&lt;br /&gt;
* &amp;lt;code&amp;gt;br-a1b2c3d4e5f6&amp;lt;/code&amp;gt;: Our custom labnet bridge&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to Lab 7:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is &amp;#039;&amp;#039;&amp;#039;exactly&amp;#039;&amp;#039;&amp;#039; what you did with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add br-lab type bridge&lt;br /&gt;
sudo ip link set br-lab up&lt;br /&gt;
sudo ip addr add 10.0.0.1/24 dev br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker automated it!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-create-backend-services-red-and-blue&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Create Backend Services (Red and Blue) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Create content directories&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p ~/lab10-multicontainer/{red,blue,purple}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Create Red service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Red Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; Memory capped at 256MB&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is memory-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Create Red Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/red/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Create Blue service content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Blue Service&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Blue Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;info&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Blue&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.3&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Resource Limit:&amp;lt;/strong&amp;gt; CPU capped at 0.5 cores&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;This backend is CPU-constrained by cgroups.&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Create Blue Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/blue/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    root * /usr/share/caddy&lt;br /&gt;
    file_server&lt;br /&gt;
    &lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Start Red container with memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name red \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.2 \&lt;br /&gt;
  --memory=256m \&lt;br /&gt;
  --memory-swap=256m \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/red:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--network labnet&amp;lt;/code&amp;gt;: Connect to our custom network (not default docker0)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.2&amp;lt;/code&amp;gt;: Assign static IP address (just like &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt; in Lab 7!)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory=256m&amp;lt;/code&amp;gt;: cgroup memory limit (hard cap: 256MB RAM)&lt;br /&gt;
* &amp;lt;code&amp;gt;--memory-swap=256m&amp;lt;/code&amp;gt;: Total memory+swap limit (setting equal to memory means no swap)&lt;br /&gt;
** If swap was 512m, container could use 256MB RAM + 256MB swap&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/etc/caddy&amp;lt;/code&amp;gt;: Mount Caddyfile&lt;br /&gt;
* &amp;lt;code&amp;gt;-v ~/lab10-multicontainer/red:/usr/share/caddy&amp;lt;/code&amp;gt;: Mount website content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Container ID&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Verify Red is running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps --filter &amp;quot;name=red&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 11: Test Red directly&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Since Red has IP 10.10.0.2, let&amp;#039;s verify we can reach it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From host (won&amp;#039;t work directly because we&amp;#039;re not in labnet)&lt;br /&gt;
# But we can use docker exec from another container&lt;br /&gt;
&lt;br /&gt;
# Quick test: use docker exec to reach Red from Red itself&lt;br /&gt;
docker exec red curl -s http://localhost | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Red Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 12: Start Blue container with CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name blue \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.3 \&lt;br /&gt;
  --cpus=0.5 \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/etc/caddy \&lt;br /&gt;
  -v ~/lab10-multicontainer/blue:/usr/share/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--cpus=0.5&amp;lt;/code&amp;gt;: cgroup CPU limit (maximum 50% of one CPU core)&lt;br /&gt;
** If CPU-intensive work is attempted, kernel will throttle it&lt;br /&gt;
* Other flags same as Red&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 13: Verify both containers are running&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS   NAMES&lt;br /&gt;
a1b2c3d4e5f6   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 20 seconds ago   Up 18 seconds   80/tcp  red&lt;br /&gt;
b7c8d9e0f1a2   caddy   &amp;amp;quot;caddy run...&amp;amp;quot; 10 seconds ago   Up 8 seconds    80/tcp  blue&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 14: Test container-to-container communication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, ping Blue&lt;br /&gt;
docker exec red ping -c 2 10.10.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Red, curl Blue&amp;#039;s website&lt;br /&gt;
docker exec red curl -s http://10.10.0.3 | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Containers on the same custom network can communicate directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-create-reverse-proxy-purple&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Create Reverse Proxy (Purple) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 15: Create Purple&amp;#039;s Caddyfile&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This is the crucial piece—routing based on URL path (from Lab 9!)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; ~/lab10-multicontainer/purple/Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
:80 {&lt;br /&gt;
    # Health check endpoint&lt;br /&gt;
    handle /health {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - OK&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Root path&lt;br /&gt;
    handle / {&lt;br /&gt;
        respond &amp;quot;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /red/* to red container (strip /red prefix)&lt;br /&gt;
    handle_path /red/* {&lt;br /&gt;
        reverse_proxy red:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /blue/* to blue container (strip /blue prefix)&lt;br /&gt;
    handle_path /blue/* {&lt;br /&gt;
        reverse_proxy blue:80&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Logging&lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key points:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;handle_path /red/*&amp;lt;/code&amp;gt;: Matches any path starting with &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;handle_path&amp;lt;/code&amp;gt; strips the prefix before forwarding&lt;br /&gt;
** Client requests &amp;lt;code&amp;gt;/red/index.html&amp;lt;/code&amp;gt; → Backend receives &amp;lt;code&amp;gt;/index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reverse_proxy red:80&amp;lt;/code&amp;gt;: Forward to container named &amp;amp;quot;red&amp;amp;quot; on port 80&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Using hostname &amp;amp;quot;red&amp;amp;quot;, not IP!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** Docker&amp;#039;s embedded DNS resolves &amp;amp;quot;red&amp;amp;quot; to 10.10.0.2&lt;br /&gt;
* Same for blue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 16: Start Purple reverse proxy&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d \&lt;br /&gt;
  --name purple \&lt;br /&gt;
  --network labnet \&lt;br /&gt;
  --ip 10.10.0.10 \&lt;br /&gt;
  -p 8080:80 \&lt;br /&gt;
  -v ~/lab10-multicontainer/purple:/etc/caddy \&lt;br /&gt;
  caddy&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flags explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ip 10.10.0.10&amp;lt;/code&amp;gt;: Purple gets IP 10.10.0.10 (different from Red/Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080:80&amp;lt;/code&amp;gt;: &amp;#039;&amp;#039;&amp;#039;Port mapping&amp;#039;&amp;#039;&amp;#039; (host port 8080 → container port 80)&lt;br /&gt;
** This is NAT (DNAT) from Lab 7!&lt;br /&gt;
** Traffic to &amp;lt;code&amp;gt;localhost:8080&amp;lt;/code&amp;gt; on host gets forwarded to &amp;lt;code&amp;gt;10.10.0.10:80&amp;lt;/code&amp;gt; in container&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-testing-the-infrastructure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: Testing the Infrastructure ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 17: Test health check&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/health&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This request:&lt;br /&gt;
&lt;br /&gt;
# Reaches host&amp;#039;s port 8080&lt;br /&gt;
# Docker&amp;#039;s DNAT rule forwards to Purple (10.10.0.10:80)&lt;br /&gt;
# Purple&amp;#039;s Caddy responds directly (no backend needed for /health)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 18: Test root path&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Purple Reverse Proxy - Use /red/ or /blue/ paths&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 19: Test routing to Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Red service HTML (with red background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;h1&amp;gt;Red Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Container:&amp;lt;/strong&amp;gt; Red&amp;lt;/p&amp;gt;&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;IP:&amp;lt;/strong&amp;gt; 10.10.0.2&amp;lt;/p&amp;gt;&lt;br /&gt;
...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happened:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Your curl → Host port 8080&lt;br /&gt;
# DNAT → Purple (10.10.0.10:80)&lt;br /&gt;
# Purple sees path &amp;lt;code&amp;gt;/red/&amp;lt;/code&amp;gt;, strips &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt;, forwards &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;red:80&amp;lt;/code&amp;gt;&lt;br /&gt;
# Purple&amp;#039;s DNS resolves &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; to 10.10.0.2&lt;br /&gt;
# Request goes to Red (10.10.0.2:80)&lt;br /&gt;
# Red&amp;#039;s Caddy serves &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;&lt;br /&gt;
# Response travels back through Purple to your curl&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 20: Test routing to Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039; Blue service HTML (with blue background styling)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 21: Test with verbose output to see headers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -v http://localhost:8080/red/ 2&amp;gt;&amp;amp;1 | grep -A10 &amp;quot;&amp;lt; HTTP&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;lt; HTTP/1.1 200 OK&lt;br /&gt;
&amp;amp;lt; Content-Type: text/html; charset=utf-8&lt;br /&gt;
&amp;amp;lt; Server: Caddy&lt;br /&gt;
&amp;amp;lt; Date: Thu, 12 Dec 2024 11:30:00 GMT&lt;br /&gt;
&amp;amp;lt; Content-Length: 687&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observe:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;Server: Caddy&amp;lt;/code&amp;gt; header (from Purple, acting as proxy)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 22: Test in browser&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Open in your web browser:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/red/&amp;lt;/code&amp;gt; → Should see red-styled page&lt;br /&gt;
* &amp;lt;code&amp;gt;http://localhost:8080/blue/&amp;lt;/code&amp;gt; → Should see blue-styled page&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-inspect-resource-limits&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Inspect Resource Limits ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 23: Check Red&amp;#039;s memory limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 256 MB in bytes (256 × 1024 × 1024 = 268435456).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 24: Check Blue&amp;#039;s CPU limit&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;500000000&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is 0.5 CPU cores in nanocpus (0.5 × 10^9 = 500,000,000).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 25: View cgroup settings from the host&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Get Red&amp;#039;s main process PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;RED_PID=$(docker inspect red --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
echo &amp;quot;Red&amp;#039;s PID: $RED_PID&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
View memory limit in cgroup filesystem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /sys/fs/cgroup/memory/docker/$RED_PID/memory.limit_in_bytes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;268435456&amp;lt;/pre&amp;gt;&lt;br /&gt;
View Blue&amp;#039;s CPU quota:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;BLUE_PID=$(docker inspect blue --format &amp;#039;{{.State.Pid}}&amp;#039;)&lt;br /&gt;
cat /sys/fs/cgroup/cpu/docker/$BLUE_PID/cpu.cfs_quota_us&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;50000&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_period_us&amp;lt;/code&amp;gt;: 100000 (default, 100ms period)&lt;br /&gt;
* &amp;lt;code&amp;gt;cpu.cfs_quota_us&amp;lt;/code&amp;gt;: 50000 (50ms out of 100ms = 50% = 0.5 CPUs)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection to kernel concepts:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These cgroup files in &amp;lt;code&amp;gt;/sys/fs/cgroup/&amp;lt;/code&amp;gt; are how the kernel enforces resource limits. Docker configures these files, and the kernel does the actual enforcement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-network-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Network Inspection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 26: Inspect labnet network showing all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network inspect labnet --format=&amp;#039;{{json .Containers}}&amp;#039; | python3 -m json.tool&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
    &amp;quot;a1b2c3d4e5f6...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:02&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.2/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;b7c8d9e0f1a2...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:03&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.3/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;f8e9c7b6d5a4...&amp;quot;: {&lt;br /&gt;
        &amp;quot;Name&amp;quot;: &amp;quot;purple&amp;quot;,&lt;br /&gt;
        &amp;quot;EndpointID&amp;quot;: &amp;quot;...&amp;quot;,&lt;br /&gt;
        &amp;quot;MacAddress&amp;quot;: &amp;quot;02:42:0a:0a:00:0a&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv4Address&amp;quot;: &amp;quot;10.10.0.10/24&amp;quot;,&lt;br /&gt;
        &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All three containers are on the labnet network with their assigned IPs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 27: Test DNS resolution between containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# From Purple, resolve &amp;quot;red&amp;quot; hostname&lt;br /&gt;
docker exec purple nslookup red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Server:         127.0.0.11&lt;br /&gt;
Address:        127.0.0.11#53&lt;br /&gt;
&lt;br /&gt;
Non-authoritative answer:&lt;br /&gt;
Name:   red&lt;br /&gt;
Address: 10.10.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;127.0.0.11&amp;lt;/code&amp;gt;: Docker&amp;#039;s embedded DNS server (listening inside each container)&lt;br /&gt;
* DNS resolves container name &amp;amp;quot;red&amp;amp;quot; to IP 10.10.0.2&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 28: Test connectivity using hostnames&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple pings Red by hostname&lt;br /&gt;
docker exec purple ping -c 2 red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected:&amp;#039;&amp;#039;&amp;#039; Success!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Purple curls Blue by hostname&lt;br /&gt;
docker exec purple curl -s http://blue | grep &amp;quot;&amp;lt;h1&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    &amp;amp;lt;h1&amp;amp;gt;Blue Service&amp;amp;lt;/h1&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-7-architecture-visualization&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 7: Architecture Visualization ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The architecture you built:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                    ┌─────────────────┐&lt;br /&gt;
                    │   Your Host     │&lt;br /&gt;
                    │  (Port 8080)    │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                     Port Mapping (NAT)&lt;br /&gt;
                      8080 → 80&lt;br /&gt;
                             │&lt;br /&gt;
                    ┌────────▼────────┐&lt;br /&gt;
                    │  Purple Proxy   │&lt;br /&gt;
                    │  10.10.0.10:80  │&lt;br /&gt;
                    │  (Caddy)        │&lt;br /&gt;
                    └────────┬────────┘&lt;br /&gt;
                             │&lt;br /&gt;
                   Docker Network: labnet&lt;br /&gt;
                   (Bridge: br-a1b2c3d4e5f6)&lt;br /&gt;
                    10.10.0.0/24&lt;br /&gt;
                             │&lt;br /&gt;
                   ┌─────────┴──────────┐&lt;br /&gt;
                   │                    │&lt;br /&gt;
          ┌────────▼────────┐  ┌───────▼─────────┐&lt;br /&gt;
          │   Red Service   │  │  Blue Service   │&lt;br /&gt;
          │   10.10.0.2:80  │  │  10.10.0.3:80   │&lt;br /&gt;
          │   (Caddy)       │  │  (Caddy)        │&lt;br /&gt;
          │  [mem: 256MB]   │  │  [cpu: 0.5]     │&lt;br /&gt;
          └─────────────────┘  └─────────────────┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request flow for &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. Curl → localhost:8080 (your host)&lt;br /&gt;
2. Host&amp;#039;s iptables DNAT rule → 10.10.0.10:80 (Purple)&lt;br /&gt;
3. Purple receives request to /red/&lt;br /&gt;
4. Purple&amp;#039;s Caddy config: handle_path /red/* → reverse_proxy red:80&lt;br /&gt;
5. Purple strips /red prefix → request becomes /&lt;br /&gt;
6. Purple&amp;#039;s DNS resolves &amp;amp;quot;red&amp;amp;quot; → 10.10.0.2&lt;br /&gt;
7. Purple → Red (10.10.0.2:80) GET /&lt;br /&gt;
8. Red&amp;#039;s Caddy serves /usr/share/caddy/index.html&lt;br /&gt;
9. Response: Red → Purple → Host → Curl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Compare to Lab 7 and Lab 9:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 7&amp;#039;&amp;#039;&amp;#039;: You manually created bridges, veth pairs, assigned IPs, configured routes, set up NAT&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 9&amp;#039;&amp;#039;&amp;#039;: You manually configured Caddy reverse proxy with path-based routing&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;This exercise&amp;#039;&amp;#039;&amp;#039;: Docker automated all the networking, you just specified what you wanted&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-8-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 8: Cleanup ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 29: Stop all containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker stop purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 30: Remove containers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker rm purple red blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 31: Remove the network&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker network rm labnet&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 32: Verify cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker ps -a&lt;br /&gt;
docker network ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Only default networks (bridge, host, none) should remain.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 33: Verify host bridge removed&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep br-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The custom bridge (br-a1b2c3d4e5f6) should be gone. Only docker0 remains.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-e&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable E ===&lt;br /&gt;
&lt;br /&gt;
Provide screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker network inspect labnet&amp;lt;/code&amp;gt; showing all three containers with their IPs (before running curl tests)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/health&amp;lt;/code&amp;gt; (health check response)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/red/&amp;lt;/code&amp;gt; (Red service HTML) with visible red-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/blue/&amp;lt;/code&amp;gt; (Blue service HTML) with visible blue-styled content&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect red --format=&amp;#039;{{.HostConfig.Memory}}&amp;#039;&amp;lt;/code&amp;gt; showing memory limit in bytes&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker inspect blue --format=&amp;#039;{{.HostConfig.NanoCpus}}&amp;#039;&amp;lt;/code&amp;gt; showing CPU limit in nanocpus&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;docker exec purple nslookup red&amp;lt;/code&amp;gt; showing DNS resolution&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-docker-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Docker Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for Docker commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;container-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Container Lifecycle ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container (create + start)&lt;br /&gt;
docker run [OPTIONS] IMAGE [COMMAND]&lt;br /&gt;
docker run -d nginx                          # Detached (background)&lt;br /&gt;
docker run -it ubuntu bash                   # Interactive with terminal&lt;br /&gt;
docker run --name mycontainer nginx          # Assign name&lt;br /&gt;
&lt;br /&gt;
# List containers&lt;br /&gt;
docker ps                                    # Running only&lt;br /&gt;
docker ps -a                                 # All (including stopped)&lt;br /&gt;
docker ps -q                                 # Show only IDs (quiet)&lt;br /&gt;
&lt;br /&gt;
# Start stopped container&lt;br /&gt;
docker start CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Stop running container&lt;br /&gt;
docker stop CONTAINER                        # Graceful (SIGTERM)&lt;br /&gt;
docker kill CONTAINER                        # Forceful (SIGKILL)&lt;br /&gt;
&lt;br /&gt;
# Restart container&lt;br /&gt;
docker restart CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove container&lt;br /&gt;
docker rm CONTAINER                          # Must be stopped first&lt;br /&gt;
docker rm -f CONTAINER                       # Force remove (stop + remove)&lt;br /&gt;
&lt;br /&gt;
# Execute command in running container&lt;br /&gt;
docker exec CONTAINER COMMAND&lt;br /&gt;
docker exec -it CONTAINER bash               # Interactive shell&lt;br /&gt;
&lt;br /&gt;
# View container logs&lt;br /&gt;
docker logs CONTAINER&lt;br /&gt;
docker logs -f CONTAINER                     # Follow (tail -f style)&lt;br /&gt;
&lt;br /&gt;
# Inspect container (detailed JSON info)&lt;br /&gt;
docker inspect CONTAINER&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.Status}}&amp;#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;image-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Image Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List images&lt;br /&gt;
docker images&lt;br /&gt;
docker image ls&lt;br /&gt;
&lt;br /&gt;
# Pull image from registry&lt;br /&gt;
docker pull IMAGE[:TAG]&lt;br /&gt;
docker pull nginx                            # Latest tag (default)&lt;br /&gt;
docker pull nginx:1.21                       # Specific tag&lt;br /&gt;
&lt;br /&gt;
# Remove image&lt;br /&gt;
docker rmi IMAGE&lt;br /&gt;
docker image rm IMAGE&lt;br /&gt;
&lt;br /&gt;
# View image layers&lt;br /&gt;
docker history IMAGE&lt;br /&gt;
&lt;br /&gt;
# Remove unused images&lt;br /&gt;
docker image prune                           # Dangling images&lt;br /&gt;
docker image prune -a                        # All unused images&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List networks&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Create network&lt;br /&gt;
docker network create NETWORK&lt;br /&gt;
docker network create --subnet=10.20.0.0/24 mynet&lt;br /&gt;
&lt;br /&gt;
# Inspect network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Connect container to network&lt;br /&gt;
docker network connect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Disconnect container from network&lt;br /&gt;
docker network disconnect NETWORK CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Remove network&lt;br /&gt;
docker network rm NETWORK&lt;br /&gt;
&lt;br /&gt;
# Remove unused networks&lt;br /&gt;
docker network prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;volume-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Volume Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List volumes&lt;br /&gt;
docker volume ls&lt;br /&gt;
&lt;br /&gt;
# Create volume&lt;br /&gt;
docker volume create VOLUME&lt;br /&gt;
&lt;br /&gt;
# Inspect volume&lt;br /&gt;
docker volume inspect VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove volume&lt;br /&gt;
docker volume rm VOLUME&lt;br /&gt;
&lt;br /&gt;
# Remove unused volumes&lt;br /&gt;
docker volume prune&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# CPU limits&lt;br /&gt;
--cpus=1.5                                   # Limit to 1.5 CPU cores&lt;br /&gt;
--cpu-shares=512                             # Relative priority (default 1024)&lt;br /&gt;
--cpuset-cpus=0,1                            # Pin to specific cores&lt;br /&gt;
&lt;br /&gt;
# Memory limits&lt;br /&gt;
--memory=512m                                # Hard limit: 512 MB RAM&lt;br /&gt;
--memory=2g                                  # Hard limit: 2 GB RAM&lt;br /&gt;
--memory-swap=1g                             # Total memory+swap&lt;br /&gt;
--memory-reservation=256m                    # Soft limit&lt;br /&gt;
&lt;br /&gt;
# I/O limits&lt;br /&gt;
--device-read-bps=/dev/sda:10mb             # Limit read bandwidth&lt;br /&gt;
--device-write-bps=/dev/sda:10mb            # Limit write bandwidth&lt;br /&gt;
&lt;br /&gt;
# Process limits&lt;br /&gt;
--pids-limit=100                             # Max 100 processes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;inspection-and-debugging&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Inspection and Debugging ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# View resource usage stats&lt;br /&gt;
docker stats&lt;br /&gt;
docker stats CONTAINER                       # Specific container&lt;br /&gt;
&lt;br /&gt;
# View processes in container&lt;br /&gt;
docker top CONTAINER&lt;br /&gt;
&lt;br /&gt;
# View port mappings&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Copy files between host and container&lt;br /&gt;
docker cp CONTAINER:/path/to/file ./file    # Container → Host&lt;br /&gt;
docker cp ./file CONTAINER:/path/to/file    # Host → Container&lt;br /&gt;
&lt;br /&gt;
# Stream events from Docker daemon&lt;br /&gt;
docker events&lt;br /&gt;
&lt;br /&gt;
# Show disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# System-wide cleanup&lt;br /&gt;
docker system prune                          # Remove unused objects&lt;br /&gt;
docker system prune -a                       # Remove all unused objects&lt;br /&gt;
docker system prune -a --volumes            # Include volumes&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;installation-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Installation Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;docker --version&amp;lt;/code&amp;gt; fails after installation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check if Docker daemon is running&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# If not running, start it&lt;br /&gt;
sudo systemctl start docker&lt;br /&gt;
sudo systemctl enable docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;Cannot connect to the Docker daemon&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Docker daemon not running or permission issue&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check daemon status&lt;br /&gt;
sudo systemctl status docker&lt;br /&gt;
&lt;br /&gt;
# Check if your user is in docker group&lt;br /&gt;
groups | grep docker&lt;br /&gt;
&lt;br /&gt;
# If not, add user and log out/in&lt;br /&gt;
sudo usermod -aG docker $USER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;permission-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Permission Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;permission denied&amp;amp;quot; when running docker commands&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Option 1: Add user to docker group (permanent)&lt;br /&gt;
sudo usermod -aG docker $USER&lt;br /&gt;
# Then log out and log back in&lt;br /&gt;
&lt;br /&gt;
# Option 2: Use sudo (temporary)&lt;br /&gt;
sudo docker run hello-world&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Files created by container are owned by root&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Container runs as root (UID 0) by default&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Run container as specific user&lt;br /&gt;
docker run --user $(id -u):$(id -g) IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use user namespaces (advanced)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container can&amp;#039;t access internet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test from container&lt;br /&gt;
docker exec CONTAINER ping -c 2 8.8.8.8&lt;br /&gt;
&lt;br /&gt;
# Check Docker&amp;#039;s NAT rules&lt;br /&gt;
sudo iptables -t nat -L -n | grep docker&lt;br /&gt;
&lt;br /&gt;
# Check IP forwarding&lt;br /&gt;
sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enable IP forwarding if disabled&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Restart Docker daemon&lt;br /&gt;
sudo systemctl restart docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Containers on custom network can&amp;#039;t communicate&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check network exists&lt;br /&gt;
docker network ls&lt;br /&gt;
&lt;br /&gt;
# Check containers are on same network&lt;br /&gt;
docker network inspect NETWORK&lt;br /&gt;
&lt;br /&gt;
# Test connectivity&lt;br /&gt;
docker exec CONTAINER1 ping CONTAINER2_IP&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Ensure both containers on same network&lt;br /&gt;
docker network connect NETWORK CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Port mapping not working (&amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt; flag)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check port is actually mapped&lt;br /&gt;
docker port CONTAINER&lt;br /&gt;
&lt;br /&gt;
# Check if host port is already in use&lt;br /&gt;
sudo ss -tulpn | grep :8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# If port in use, use different host port&lt;br /&gt;
docker run -p 8081:80 nginx&lt;br /&gt;
&lt;br /&gt;
# Or stop the conflicting process&lt;br /&gt;
sudo fuser -k 8080/tcp&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;storage-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Storage Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; &amp;amp;quot;No space left on device&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check Docker disk usage&lt;br /&gt;
docker system df&lt;br /&gt;
&lt;br /&gt;
# Check host disk space&lt;br /&gt;
df -h /var/lib/docker&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Remove unused objects&lt;br /&gt;
docker system prune -a&lt;br /&gt;
&lt;br /&gt;
# Remove specific old images&lt;br /&gt;
docker images&lt;br /&gt;
docker rmi IMAGE_ID&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Changes in bind mount not visible in container&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause:&amp;#039;&amp;#039;&amp;#039; Path doesn&amp;#039;t exist or wrong path specified&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Verify path exists on host&lt;br /&gt;
ls -la /path/to/host/directory&lt;br /&gt;
&lt;br /&gt;
# Use absolute paths&lt;br /&gt;
docker run -v /absolute/path:/container/path IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or use $PWD for current directory&lt;br /&gt;
docker run -v $PWD/relative/path:/container/path IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;resource-limit-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Resource Limit Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container killed unexpectedly&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check container exit code&lt;br /&gt;
docker inspect CONTAINER --format=&amp;#039;{{.State.ExitCode}}&amp;#039;&lt;br /&gt;
# Exit code 137 = killed (often OOM)&lt;br /&gt;
&lt;br /&gt;
# Check logs&lt;br /&gt;
docker logs CONTAINER&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Increase memory limit&lt;br /&gt;
docker run --memory=1g IMAGE&lt;br /&gt;
&lt;br /&gt;
# Or run without memory limit (default)&lt;br /&gt;
docker run IMAGE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem:&amp;#039;&amp;#039;&amp;#039; Container using too much CPU&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Limit CPU usage&lt;br /&gt;
docker run --cpus=0.5 IMAGE&lt;br /&gt;
&lt;br /&gt;
# Check what&amp;#039;s consuming CPU&lt;br /&gt;
docker exec CONTAINER top&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;next-steps-dockerfile-and-docker-compose&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Next Steps: Dockerfile and Docker Compose ==&lt;br /&gt;
&lt;br /&gt;
Congratulations! You&amp;#039;ve mastered the fundamental OS concepts behind containers:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Namespaces&amp;#039;&amp;#039;&amp;#039; provide isolation (network, PID, mount, UTS, IPC, user, cgroup)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;cgroups&amp;#039;&amp;#039;&amp;#039; enforce resource limits (CPU, memory, I/O)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;OverlayFS&amp;#039;&amp;#039;&amp;#039; provides efficient layered filesystems&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bridges and veth pairs&amp;#039;&amp;#039;&amp;#039; connect containers (same as Lab 7!)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Volumes&amp;#039;&amp;#039;&amp;#039; persist data beyond container lifecycles&lt;br /&gt;
&lt;br /&gt;
However, you&amp;#039;ve been using &amp;#039;&amp;#039;&amp;#039;pre-built images&amp;#039;&amp;#039;&amp;#039; and running containers with long &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands. In production environments, you need:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom images&amp;#039;&amp;#039;&amp;#039; tailored to your applications&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reproducible builds&amp;#039;&amp;#039;&amp;#039; that can be version-controlled&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Multi-container orchestration&amp;#039;&amp;#039;&amp;#039; with coordinated startup and networking&lt;br /&gt;
&lt;br /&gt;
This is where &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039; come in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dockerfile-building-custom-images&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Dockerfile: Building Custom Images ===&lt;br /&gt;
&lt;br /&gt;
Instead of starting from a base image and manually installing packages, you define your application&amp;#039;s environment in a &amp;lt;code&amp;gt;Dockerfile&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;dockerfile&amp;quot;&amp;gt;# Start from Ubuntu base image&lt;br /&gt;
FROM ubuntu:22.04&lt;br /&gt;
&lt;br /&gt;
# Install dependencies&lt;br /&gt;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;br /&gt;
    python3 \&lt;br /&gt;
    python3-pip \&lt;br /&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br /&gt;
&lt;br /&gt;
# Copy application code&lt;br /&gt;
COPY app.py /app/&lt;br /&gt;
COPY requirements.txt /app/&lt;br /&gt;
&lt;br /&gt;
# Install Python dependencies&lt;br /&gt;
WORKDIR /app&lt;br /&gt;
RUN pip3 install -r requirements.txt&lt;br /&gt;
&lt;br /&gt;
# Expose port&lt;br /&gt;
EXPOSE 8000&lt;br /&gt;
&lt;br /&gt;
# Define startup command&lt;br /&gt;
CMD [&amp;quot;python3&amp;quot;, &amp;quot;app.py&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Build the image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker build -t myapp:1.0 .&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Run containers from your custom image:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker run -d -p 8000:8000 myapp:1.0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reproducibility&amp;#039;&amp;#039;&amp;#039;: Same Dockerfile always builds identical image&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Version control&amp;#039;&amp;#039;&amp;#039;: Dockerfile lives in git alongside code&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Documentation&amp;#039;&amp;#039;&amp;#039;: Dockerfile explicitly documents dependencies and setup&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automation&amp;#039;&amp;#039;&amp;#039;: CI/CD pipelines can build images automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Dockerfile instructions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;FROM&amp;lt;/code&amp;gt;: Base image to start from&lt;br /&gt;
* &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt;: Execute command during build (install packages, etc.)&lt;br /&gt;
* &amp;lt;code&amp;gt;COPY&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;ADD&amp;lt;/code&amp;gt;: Copy files from host into image&lt;br /&gt;
* &amp;lt;code&amp;gt;WORKDIR&amp;lt;/code&amp;gt;: Set working directory&lt;br /&gt;
* &amp;lt;code&amp;gt;ENV&amp;lt;/code&amp;gt;: Set environment variables&lt;br /&gt;
* &amp;lt;code&amp;gt;EXPOSE&amp;lt;/code&amp;gt;: Document which ports the application uses&lt;br /&gt;
* &amp;lt;code&amp;gt;CMD&amp;lt;/code&amp;gt;: Default command to run when container starts&lt;br /&gt;
* &amp;lt;code&amp;gt;ENTRYPOINT&amp;lt;/code&amp;gt;: Configure container as executable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Use specific image tags (not &amp;lt;code&amp;gt;latest&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Minimize layers (combine &amp;lt;code&amp;gt;RUN&amp;lt;/code&amp;gt; commands)&lt;br /&gt;
* Leverage build cache (order instructions from least to most frequently changing)&lt;br /&gt;
* Use &amp;lt;code&amp;gt;.dockerignore&amp;lt;/code&amp;gt; (like &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; for Docker builds)&lt;br /&gt;
* Don&amp;#039;t run as root (use &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; instruction)&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;docker-compose-multi-container-orchestration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Docker Compose: Multi-Container Orchestration ===&lt;br /&gt;
&lt;br /&gt;
Instead of running multiple &amp;lt;code&amp;gt;docker run&amp;lt;/code&amp;gt; commands with complex options, define your entire infrastructure in a &amp;lt;code&amp;gt;docker-compose.yml&amp;lt;/code&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;version: &amp;#039;3.8&amp;#039;&lt;br /&gt;
&lt;br /&gt;
services:&lt;br /&gt;
  # Purple reverse proxy&lt;br /&gt;
  purple:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    ports:&lt;br /&gt;
      - &amp;quot;8080:80&amp;quot;&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./purple:/etc/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.10&lt;br /&gt;
    depends_on:&lt;br /&gt;
      - red&lt;br /&gt;
      - blue&lt;br /&gt;
&lt;br /&gt;
  # Red backend&lt;br /&gt;
  red:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./red:/etc/caddy&lt;br /&gt;
      - ./red:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.2&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          memory: 256M&lt;br /&gt;
&lt;br /&gt;
  # Blue backend&lt;br /&gt;
  blue:&lt;br /&gt;
    image: caddy&lt;br /&gt;
    volumes:&lt;br /&gt;
      - ./blue:/etc/caddy&lt;br /&gt;
      - ./blue:/usr/share/caddy&lt;br /&gt;
    networks:&lt;br /&gt;
      labnet:&lt;br /&gt;
        ipv4_address: 10.10.0.3&lt;br /&gt;
    deploy:&lt;br /&gt;
      resources:&lt;br /&gt;
        limits:&lt;br /&gt;
          cpus: &amp;#039;0.5&amp;#039;&lt;br /&gt;
&lt;br /&gt;
networks:&lt;br /&gt;
  labnet:&lt;br /&gt;
    driver: bridge&lt;br /&gt;
    ipam:&lt;br /&gt;
      config:&lt;br /&gt;
        - subnet: 10.10.0.0/24&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Start entire infrastructure:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or in detached mode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose up -d&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Stop everything:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose down&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;View logs:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;docker compose logs -f&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Declarative&amp;#039;&amp;#039;&amp;#039;: Describe desired state, not imperative commands&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Single source of truth&amp;#039;&amp;#039;&amp;#039;: One file defines entire application&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Easy to share&amp;#039;&amp;#039;&amp;#039;: Colleagues can run &amp;lt;code&amp;gt;docker compose up&amp;lt;/code&amp;gt; and get identical environment&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Development/production parity&amp;#039;&amp;#039;&amp;#039;: Same compose file works everywhere&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Automatic networking&amp;#039;&amp;#039;&amp;#039;: Compose creates network and DNS automatically&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is the production-ready way to deploy multi-container applications.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Further learning:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dockerfile&amp;#039;&amp;#039;&amp;#039;: Learn advanced instructions, multi-stage builds, build arguments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Compose&amp;#039;&amp;#039;&amp;#039;: Learn service dependencies, health checks, scaling&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Docker Swarm&amp;#039;&amp;#039;&amp;#039;: Docker&amp;#039;s built-in orchestration for multi-host deployments&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kubernetes&amp;#039;&amp;#039;&amp;#039;: Industry-standard container orchestration platform&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Container registries&amp;#039;&amp;#039;&amp;#039;: Push images to Docker Hub, GitHub Container Registry, or private registries&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;: Image scanning, rootless Docker, seccomp profiles, AppArmor&lt;br /&gt;
&lt;br /&gt;
These topics build on the solid foundation you&amp;#039;ve established in this lab. You now understand what containers actually are at the OS level—the rest is learning tools that make containers easier to build and deploy.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all deliverables from Exercises A through E, organized with clear section headers matching the exercise labels.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Required deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable A&amp;#039;&amp;#039;&amp;#039;: Installation verification (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable B&amp;#039;&amp;#039;&amp;#039;: Hello World and namespace inspection (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable C&amp;#039;&amp;#039;&amp;#039;: Fedora exploration (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable D&amp;#039;&amp;#039;&amp;#039;: Persistent storage (screenshots)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Deliverable E&amp;#039;&amp;#039;&amp;#039;: Multi-container infrastructure (screenshots)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced containerization using Docker, demonstrating how Linux kernel features (namespaces, cgroups, OverlayFS) are composed to create isolated application environments. You&amp;#039;ve seen that Docker is not magic—it&amp;#039;s sophisticated automation of kernel primitives you already understand from previous labs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Internals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Deep dive into runc (the OCI runtime that Docker uses)&lt;br /&gt;
* Understanding containerd (container runtime layer)&lt;br /&gt;
* Linux capabilities and security contexts&lt;br /&gt;
* AppArmor and SELinux profiles for containers&lt;br /&gt;
* User namespaces and rootless containers&lt;br /&gt;
* Seccomp profiles for system call filtering&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dockerfile Best Practices:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Multi-stage builds for smaller images&lt;br /&gt;
* Build cache optimization strategies&lt;br /&gt;
* Layer ordering for efficient rebuilds&lt;br /&gt;
* .dockerignore for excluding files&lt;br /&gt;
* Security scanning with Trivy or Clair&lt;br /&gt;
* Distroless and minimal base images&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Docker Compose Advanced:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Service dependencies with health checks&lt;br /&gt;
* Environment-specific overrides&lt;br /&gt;
* Secrets management&lt;br /&gt;
* Multiple compose files (base + overrides)&lt;br /&gt;
* Named volumes vs bind mounts&lt;br /&gt;
* Integration testing with compose&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Orchestration:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Kubernetes architecture and concepts&lt;br /&gt;
* kubectl basics&lt;br /&gt;
* Pods, Services, Deployments, ConfigMaps&lt;br /&gt;
* Kubernetes networking (CNI plugins)&lt;br /&gt;
* Helm charts for package management&lt;br /&gt;
* Service mesh (Istio, Linkerd)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Networking Deep Dive:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Bridge vs host vs macvlan networking&lt;br /&gt;
* Overlay networks for multi-host communication&lt;br /&gt;
* Network plugins and CNI specification&lt;br /&gt;
* Load balancing strategies&lt;br /&gt;
* Service discovery patterns&lt;br /&gt;
* Network policies and segmentation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container escape techniques and mitigations&lt;br /&gt;
* Image vulnerability scanning&lt;br /&gt;
* Runtime security monitoring (Falco)&lt;br /&gt;
* Least privilege principles&lt;br /&gt;
* Supply chain security&lt;br /&gt;
* Harbor for secure image registry&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Container resource tuning&lt;br /&gt;
* Storage drivers (overlay2, btrfs, zfs)&lt;br /&gt;
* Network performance optimization&lt;br /&gt;
* Monitoring with Prometheus and Grafana&lt;br /&gt;
* Distributed tracing with Jaeger&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Production Operations:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Logging strategies (centralized logging)&lt;br /&gt;
* Health checks and readiness probes&lt;br /&gt;
* Rolling updates and blue-green deployments&lt;br /&gt;
* Backup and disaster recovery&lt;br /&gt;
* CI/CD integration (Jenkins, GitLab CI)&lt;br /&gt;
* Container registries (private, public)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 namespaces                             # Linux namespaces overview&lt;br /&gt;
man 7 cgroups                                # Control groups overview&lt;br /&gt;
man 2 unshare                                # Create namespaces&lt;br /&gt;
man 2 setns                                  # Enter existing namespace&lt;br /&gt;
man 8 nsenter                                # Run program in namespace&lt;br /&gt;
man 1 docker                                 # Docker CLI reference&lt;br /&gt;
man 1 docker-run                             # docker run reference&lt;br /&gt;
man 1 docker-compose                         # Docker Compose reference&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Official Documentation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docs.docker.com/ Docker Documentation] - Comprehensive official docs&lt;br /&gt;
* [https://hub.docker.com/ Docker Hub] - Public image registry&lt;br /&gt;
* [https://opencontainers.org/ OCI Specification] - Open Container Initiative standards&lt;br /&gt;
* [https://containerd.io/docs/ containerd Documentation] - Container runtime&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Tutorials and Guides:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://docker-curriculum.com/ Docker Curriculum] - Beginner-friendly tutorial&lt;br /&gt;
* [https://labs.play-with-docker.com/ Play with Docker] - Free interactive playground&lt;br /&gt;
* [https://kubernetes.io/docs/ Kubernetes Documentation] - K8s official docs&lt;br /&gt;
* [https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ Dockerfile Best Practices]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deep Dives:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://lwn.net/Articles/531114/ Understanding Namespaces (LWN)] - Series on Linux namespaces&lt;br /&gt;
* [https://jvns.ca/blog/2016/10/10/what-even-is-a-container/ How Containers Work] - Excellent blog post by Julia Evans&lt;br /&gt;
* [https://sysdig.com/blog/dockerfile-best-practices/ Container Security Best Practices] - Security-focused guide&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Community:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://forums.docker.com/ Docker Forums] - Community support&lt;br /&gt;
* [https://stackoverflow.com/questions/tagged/docker Stack Overflow - Docker Tag]&lt;br /&gt;
* [https://reddit.com/r/docker Reddit r/docker]&lt;br /&gt;
* [https://slack.cncf.io/ CNCF Slack] - Cloud Native Computing Foundation community&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Practice Environments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* [https://www.katacoda.com/ Katacoda] - Interactive Docker and Kubernetes scenarios&lt;br /&gt;
* [https://labs.play-with-k8s.com/ Play with Kubernetes] - K8s playground&lt;br /&gt;
* [https://killercoda.com/ KillerCoda] - Interactive learning scenarios&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8193</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8193"/>
		<updated>2025-12-12T14:50:28Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;br /&gt;
* Lab 8 - [[OS Lab 8 - Transport and Security]]&lt;br /&gt;
* Lab 9 - [[OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)]]&lt;br /&gt;
* Lab 10 - [[OS Lab 10 - Containers and Docker]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_9_-_Application_Protocols:_DNS,_SSH,_HTTP(S)&amp;diff=8192</id>
		<title>OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_9_-_Application_Protocols:_DNS,_SSH,_HTTP(S)&amp;diff=8192"/>
		<updated>2025-12-05T15:10:10Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Understand core internet infrastructure: well-known ports, DNS hierarchy, and t...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Understand core internet infrastructure: well-known ports, DNS hierarchy, and the roles of authoritative vs. recursive DNS servers.&lt;br /&gt;
* Use DNS tools (&amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nslookup&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;host&amp;lt;/code&amp;gt;) to query records and analyze responses, including TTLs and caching behavior.&lt;br /&gt;
* Configure secure SSH access using Ed25519 keys and explain SSH’s TOFU model compared with certificate-based PKI.&lt;br /&gt;
* Build and route HTTP(S) services using a reverse proxy, and understand why this pattern underlies modern cloud architectures.&lt;br /&gt;
* Diagnose common application-layer connectivity issues using protocol-specific debugging techniques.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-journey-through-the-network-stack&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Journey Through the Network Stack ===&lt;br /&gt;
&lt;br /&gt;
In Labs 7 and 8, we systematically constructed the entire networking stack from the physical layer upward:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 7 (Network Infrastructure)&amp;#039;&amp;#039;&amp;#039;: We built the foundational plumbing—network interfaces, MAC addresses, IP addressing, routing tables, network bridges, and the mechanisms that allow one machine to physically reach another across networks. We demonstrated how the kernel routes packets from source to destination based on Ethernet and Layer 3 IP addressing.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Lab 8 (Transport and Security)&amp;#039;&amp;#039;&amp;#039;: We ascended to the transport layer, introducing the critical concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to enable process-to-process communication. We explored the fundamental trade-offs between UDP&amp;#039;s connectionless simplicity and TCP&amp;#039;s reliable, connection-oriented delivery. Most importantly, we implemented Transport Layer Security (TLS) using Public Key Infrastructure (PKI) to protect data in transit from eavesdropping and tampering.&lt;br /&gt;
&lt;br /&gt;
We now reach the pinnacle of the network stack: called the Application Layer in the TCP/IP model. This is where protocols transition from answering &amp;amp;quot;how do we reliably deliver data between processes?&amp;amp;quot; to addressing &amp;amp;quot;what should we do with this data and how should applications interact?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-application-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Application Layer ===&lt;br /&gt;
&lt;br /&gt;
Consider what you actually accomplish with computers in daily practice. You do not consciously think about TCP sequence numbers, IP routing algorithms, or TLS cipher suites. Instead, you engage in high-level activities:&lt;br /&gt;
&lt;br /&gt;
* You type a domain name (e.g., &amp;amp;quot;example.com&amp;amp;quot;) and a website appears instantaneously (DNS + HTTP)&lt;br /&gt;
* You connect to a remote server to execute commands securely (SSH)&lt;br /&gt;
* You upload files, stream videos, send API requests, interact with cloud services (predominantly HTTP/HTTPS)&lt;br /&gt;
&lt;br /&gt;
The application layer is where networking infrastructure becomes visible as useful services. These are the protocols that system administrators, DevOps engineers, and software developers interact with constantly. While hundreds of application protocols exist, three protocols dominate modern networking:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DNS (Port 53)&amp;#039;&amp;#039;&amp;#039;: The distributed directory service that answers &amp;amp;quot;How do I find the IP address for this domain name?&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH (Port 22)&amp;#039;&amp;#039;&amp;#039;: The secure remote access protocol that answers &amp;amp;quot;How do I securely administer remote systems?&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS (Ports 80/443)&amp;#039;&amp;#039;&amp;#039;: The hypertext transfer protocol that has evolved far beyond web browsing to become the answer to &amp;amp;quot;How do I communicate application data?&amp;amp;quot; for nearly everything.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-complete-network-request-journey&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Complete Network Request Journey ===&lt;br /&gt;
&lt;br /&gt;
By the end of this lab, you will understand the complete, end-to-end journey of a modern web request. When you type &amp;lt;code&amp;gt;https://api.example.com/users&amp;lt;/code&amp;gt; into your browser, the following sequence occurs:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;DNS Resolution (This lab)&amp;#039;&amp;#039;&amp;#039;: Your system queries DNS to resolve &amp;lt;code&amp;gt;api.example.com&amp;lt;/code&amp;gt; to an IP address (e.g., 203.0.113.50)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routing (Lab 7)&amp;#039;&amp;#039;&amp;#039;: The kernel consults routing tables to determine the next hop toward that IP address&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Connection (Lab 8)&amp;#039;&amp;#039;&amp;#039;: Your system establishes a three-way handshake to create a reliable TCP connection to port 443 at that IP address&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake (Lab 8)&amp;#039;&amp;#039;&amp;#039;: The client and server perform a TLS handshake, establishing an encrypted tunnel using certificates to verify the server&amp;#039;s identity&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;HTTP Request (This lab)&amp;#039;&amp;#039;&amp;#039;: Your browser sends an HTTP GET request to the &amp;lt;code&amp;gt;/users&amp;lt;/code&amp;gt; path over the encrypted connection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reverse Proxy Routing (This lab)&amp;#039;&amp;#039;&amp;#039;: A reverse proxy on the server side examines the request path and routes it to the appropriate backend service&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Response Journey&amp;#039;&amp;#039;&amp;#039;: The response flows back through all these layers—HTTP response, TLS encryption, TCP segments, IP packets, Ethernet frames—until it reaches your browser&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Topology&amp;#039;&amp;#039;&amp;#039;: This lab builds upon the network namespace topology established in Lab 8. You should have:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Red namespace&amp;#039;&amp;#039;&amp;#039;: Simulated host with IP address 10.0.0.1/24&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Blue namespace&amp;#039;&amp;#039;&amp;#039;: Simulated host with IP address 10.0.0.2/24&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host system&amp;#039;&amp;#039;&amp;#039;: Bridge interface (br0) connecting the namespaces&lt;br /&gt;
* Full bidirectional connectivity verified in Lab 8&lt;br /&gt;
&lt;br /&gt;
If you did not complete Lab 8 or your topology has been reset, you will need to recreate it following Lab 8&amp;#039;s setup instructions before proceeding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed. Run these commands on your host system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y openssh-server openssh-client dnsutils bind9-dnsutils \&lt;br /&gt;
                    caddy curl net-tools&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Package descriptions:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssh-server&amp;lt;/code&amp;gt;: OpenSSH server daemon for accepting SSH connections&lt;br /&gt;
* &amp;lt;code&amp;gt;openssh-client&amp;lt;/code&amp;gt;: OpenSSH client tools (ssh, scp, sftp) for initiating connections&lt;br /&gt;
* &amp;lt;code&amp;gt;dnsutils&amp;lt;/code&amp;gt;: DNS client utilities including &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;nslookup&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;bind9-dnsutils&amp;lt;/code&amp;gt;: Additional DNS diagnostic tools including &amp;lt;code&amp;gt;host&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: Modern web server with automatic HTTPS and simple configuration syntax&lt;br /&gt;
* &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt;: Command-line HTTP client for testing and debugging&lt;br /&gt;
* &amp;lt;code&amp;gt;net-tools&amp;lt;/code&amp;gt;: Legacy networking tools (netstat, ifconfig) for compatibility&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 7 (Network Fundamentals)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, subnet masks, and CIDR notation&lt;br /&gt;
* Routing tables and default gateways&lt;br /&gt;
* Network namespaces and virtual ethernet pairs&lt;br /&gt;
* Bridge devices and how they connect network segments&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;From Lab 8 (Transport and Security)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* TCP and UDP transport protocols&lt;br /&gt;
* Port numbers and socket addresses (IP:PORT)&lt;br /&gt;
* TLS encryption and certificate-based authentication&lt;br /&gt;
* The Public Key Infrastructure (PKI) trust model&lt;br /&gt;
* Basic hostname resolution using &amp;lt;code&amp;gt;/etc/hosts&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;General Linux Skills&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Command-line text manipulation (grep, awk, sed, cut)&lt;br /&gt;
* Process management (starting, stopping, viewing processes)&lt;br /&gt;
* File editing with text editors (nano, vim, or your preference)&lt;br /&gt;
* Basic Bash scripting (variables, loops, conditionals)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* The client-server model of network communication&lt;br /&gt;
* What an IP address represents and how packets are routed between networks&lt;br /&gt;
* The concept of a socket as the combination of IP address and port number&lt;br /&gt;
* Basic cryptographic concepts (public/private keys, certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-recap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer Recap ===&lt;br /&gt;
&lt;br /&gt;
Before diving into application protocols, let us briefly revisit why the transport layer exists and what problem it solves. This context is essential for understanding well-known ports and application protocol design.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Fundamental Problem&amp;#039;&amp;#039;&amp;#039;: An IP address identifies a machine on the network, but machines do not communicate with machines. &amp;#039;&amp;#039;&amp;#039;Processes&amp;#039;&amp;#039;&amp;#039; (running programs) communicate with processes. When a packet arrives at your machine&amp;#039;s IP address, the kernel must determine which of potentially hundreds of running processes should receive that packet.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution&amp;#039;&amp;#039;&amp;#039;: The transport layer introduces &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;—16-bit unsigned integers (range 0-65535) that identify specific communication endpoints. When combined with an IP address, a port creates a unique &amp;#039;&amp;#039;&amp;#039;socket address&amp;#039;&amp;#039;&amp;#039; (written as IP:PORT, e.g., 192.168.1.50:443) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
A complete connection is identified by a &amp;#039;&amp;#039;&amp;#039;five-tuple&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Source IP address&lt;br /&gt;
# Source port&lt;br /&gt;
# Destination IP address&lt;br /&gt;
# Destination port&lt;br /&gt;
# Protocol (TCP or UDP)&lt;br /&gt;
&lt;br /&gt;
This five-tuple uniquely identifies a communication channel. For example:&lt;br /&gt;
&lt;br /&gt;
* Client: 192.168.1.50:54321 → Server: 203.0.113.10:443 (HTTPS connection)&lt;br /&gt;
* Client: 10.0.0.1:35678 → Server: 10.0.0.2:22 (SSH connection)&lt;br /&gt;
&lt;br /&gt;
With this foundation established, we can now explore how standardized port numbers enable service discovery at internet scale.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;well-known-ports&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Well-Known Ports ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-problem-service-discovery-without-coordination&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Problem: Service Discovery Without Coordination ====&lt;br /&gt;
&lt;br /&gt;
Consider a scenario where you want to connect to a web server running at IP address &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;. You know the machine&amp;#039;s network address, but you face a critical problem: &amp;#039;&amp;#039;&amp;#039;Which port is the web server listening on?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server could be bound to any of 65,535 possible port numbers:&lt;br /&gt;
&lt;br /&gt;
* Port 80? (Traditional HTTP)&lt;br /&gt;
* Port 8080? (Alternative HTTP)&lt;br /&gt;
* Port 3000? (Common development port)&lt;br /&gt;
* Port 9000? (Arbitrary choice)&lt;br /&gt;
* Some completely random port like 47281?&lt;br /&gt;
&lt;br /&gt;
Without knowing the correct port, you cannot establish a connection. It is analogous to knowing someone&amp;#039;s street address but not their apartment number in a massive building with 65,535 apartments.&lt;br /&gt;
&lt;br /&gt;
You could attempt to connect to all 65,535 ports sequentially (this is precisely what port scanning tools like &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; do), but this approach is:&lt;br /&gt;
&lt;br /&gt;
* Extremely slow (trying all ports could take minutes or hours)&lt;br /&gt;
* Inefficient (wastes network bandwidth and computational resources)&lt;br /&gt;
* Potentially suspicious (may trigger intrusion detection systems)&lt;br /&gt;
* Impractical for everyday use (users expect instant connections)&lt;br /&gt;
&lt;br /&gt;
There must be a superior solution.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-solution-standardized-port-assignments&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Solution: Standardized Port Assignments ====&lt;br /&gt;
&lt;br /&gt;
The early architects of the internet solved this problem through an elegantly simple mechanism: &amp;#039;&amp;#039;&amp;#039;social convention&amp;#039;&amp;#039;&amp;#039;. The Internet Assigned Numbers Authority (IANA) maintains an official registry of port number assignments, and the community agrees to follow these standards.&lt;br /&gt;
&lt;br /&gt;
This is not a technical enforcement mechanism—there is no kernel code that prevents you from running an SSH server on port 8080 or an HTTP server on port 22. Instead, it is a &amp;#039;&amp;#039;&amp;#039;social contract&amp;#039;&amp;#039;&amp;#039;: we collectively agree that certain services will use certain ports, making the internet predictable and functional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-number-space-division&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Port Number Space Division ====&lt;br /&gt;
&lt;br /&gt;
The IANA divides the port number space into three categories:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports are reserved for standard, widely-used services. Key characteristics:&lt;br /&gt;
&lt;br /&gt;
* Officially registered with IANA for specific protocols&lt;br /&gt;
* Binding to these ports requires elevated privileges (root/administrator) on Unix-like systems&lt;br /&gt;
* This privilege requirement is a security feature: it prevents normal users from impersonating critical services&lt;br /&gt;
* Examples: HTTP (80), HTTPS (443), SSH (22), DNS (53), SMTP (25)&lt;br /&gt;
&lt;br /&gt;
The privilege requirement exists because if any user could bind to port 80, they could impersonate a web server and potentially trick other programs or users into trusting them.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports can be registered with IANA for specific services, but enforcement is less strict. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* No special privileges required to bind&lt;br /&gt;
* Software vendors often register ports for their applications&lt;br /&gt;
* Examples: MySQL (3306), PostgreSQL (5432), MongoDB (27017), Redis (6379)&lt;br /&gt;
* Applications can use these ports without requiring root access&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Dynamic/Private/Ephemeral Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These ports are used for temporary client-side connections. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* Never registered for permanent services&lt;br /&gt;
* Assigned automatically by the operating system when a client initiates an outbound connection&lt;br /&gt;
* The client doesn&amp;#039;t choose these ports explicitly&lt;br /&gt;
* Used briefly for the duration of a connection, then released back to the pool&lt;br /&gt;
&lt;br /&gt;
When your web browser connects to a server, it might use local address &amp;lt;code&amp;gt;192.168.1.50:52341&amp;lt;/code&amp;gt; (your machine + random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server + standard HTTPS port).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-social-contract-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Social Contract in Practice ====&lt;br /&gt;
&lt;br /&gt;
When a server administrator configures a service, they &amp;#039;&amp;#039;&amp;#039;listen on the well-known port&amp;#039;&amp;#039;&amp;#039; for that service type. Clients, in turn, &amp;#039;&amp;#039;&amp;#039;assume&amp;#039;&amp;#039;&amp;#039; servers use these standard ports. This mutual agreement eliminates the need for explicit coordination.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example 1: SSH Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you execute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh admin@server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The SSH client automatically attempts to connect to port 22. You do not need to specify the port. The client assumes the server follows the convention. Under the hood, this command is equivalent to:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh -p 22 admin@server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;-p 22&amp;lt;/code&amp;gt; is implicit because port 22 is the well-known port for SSH.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example 2: Web Browsing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When you enter a URL in your browser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;http://example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your browser automatically connects to port 80. Similarly, for:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;https://example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
Your browser connects to port 443. These behaviors are hardcoded into HTTP client software based on the URL scheme.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Breaking the Convention&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
You are not technically required to follow these conventions. You can run an SSH server on port 8022, a web server on port 3000, or DNS on port 5353. However, breaking conventions creates friction—clients need explicit instructions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Non-standard SSH port requires explicit specification&lt;br /&gt;
ssh -p 8022 admin@server.example.com&lt;br /&gt;
&lt;br /&gt;
# Non-standard HTTP port must be included in URL&lt;br /&gt;
curl http://example.com:3000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analogy&amp;#039;&amp;#039;&amp;#039;: Well-known ports function like standardized electrical outlets. In Europe, you expect 220V AC outlets with a specific two-prong configuration. You can plug in any device without checking each outlet—you trust the convention. Similarly, you can connect to any SSH server without checking its port—you trust it will be on port 22.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As a system administrator or developer, you will usually interact with these port numbers:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 20/21&amp;#039;&amp;#039;&amp;#039;: FTP (File Transfer Protocol) - Legacy, rarely used today&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 22&amp;#039;&amp;#039;&amp;#039;: SSH (Secure Shell) - Remote system access&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 23&amp;#039;&amp;#039;&amp;#039;: Telnet - Legacy insecure remote access (NEVER USE)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 25&amp;#039;&amp;#039;&amp;#039;: SMTP (Simple Mail Transfer Protocol) - Email sending&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 53&amp;#039;&amp;#039;&amp;#039;: DNS (Domain Name System) - Name resolution&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 80&amp;#039;&amp;#039;&amp;#039;: HTTP (Hypertext Transfer Protocol) - Web traffic unencrypted&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 110&amp;#039;&amp;#039;&amp;#039;: POP3 (Post Office Protocol) - Email retrieval (legacy)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 143&amp;#039;&amp;#039;&amp;#039;: IMAP (Internet Message Access Protocol) - Modern email retrieval&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 443&amp;#039;&amp;#039;&amp;#039;: HTTPS (HTTP Secure) - Encrypted web traffic&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 3306&amp;#039;&amp;#039;&amp;#039;: MySQL database server&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 5432&amp;#039;&amp;#039;&amp;#039;: PostgreSQL database server&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 6379&amp;#039;&amp;#039;&amp;#039;: Redis in-memory database&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Port 8080&amp;#039;&amp;#039;&amp;#039;: Alternative HTTP port (often used for development)&lt;br /&gt;
&lt;br /&gt;
This social contract—standardized port numbers—is a foundational element that makes the internet function at global scale. It exemplifies how simple conventions can solve complex coordination problems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;domain-name-system-dns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Domain Name System (DNS) ===&lt;br /&gt;
&lt;br /&gt;
The human-computer impedance mismatch is a fundamental challenge in computing: humans prefer memorable, meaningful names, while computers require numerical addresses. The Domain Name System (DNS) bridges this gap by providing a distributed, hierarchical database that maps human-readable domain names to machine-usable IP addresses (and other information).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;human-memory-vs-computer-addressing&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Human Memory vs. Computer Addressing ====&lt;br /&gt;
&lt;br /&gt;
IP addresses are the actual addressing mechanism for network communication. When your computer needs to send packets to a destination, it must know that destination&amp;#039;s IP address. However, IP addresses present several problems for human users:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Memorization Difficulty&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
IPv4 addresses like &amp;lt;code&amp;gt;172.217.14.206&amp;lt;/code&amp;gt; are difficult for humans to remember and type accurately. IPv6 addresses like &amp;lt;code&amp;gt;2607:f8b0:4004:c07::64&amp;lt;/code&amp;gt; are even worse. While you might remember your own phone number, remembering hundreds of IP addresses for services you use is impractical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Instability and Change&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
IP addresses can change due to:&lt;br /&gt;
&lt;br /&gt;
* Infrastructure migrations (moving servers between data centers)&lt;br /&gt;
* Load balancing requirements (distributing traffic across multiple servers)&lt;br /&gt;
* Failover scenarios (switching to backup servers during outages)&lt;br /&gt;
* Dynamic allocation (DHCP assigns different IPs over time)&lt;br /&gt;
&lt;br /&gt;
If users memorized IP addresses, every infrastructure change would break their connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Lack of Semantic Meaning&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
An IP address like &amp;lt;code&amp;gt;198.51.100.42&amp;lt;/code&amp;gt; conveys no information about what service it provides or which organization operates it. The name &amp;lt;code&amp;gt;store.example.com&amp;lt;/code&amp;gt; immediately suggests it&amp;#039;s a commercial store operated by Example Corporation.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Service Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A single IP address can host multiple services (using different ports) or multiple websites (using HTTP virtual hosting). The domain name helps identify which specific service the user wants.&lt;br /&gt;
&lt;br /&gt;
The Solution: Domain Name System&lt;br /&gt;
&lt;br /&gt;
DNS solves these problems by creating a globally distributed, hierarchical naming system that maps domain names to IP addresses and other resource records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Historical Context&amp;#039;&amp;#039;&amp;#039;: Before DNS, the internet used a centralized hosts file (&amp;lt;code&amp;gt;/etc/hosts&amp;lt;/code&amp;gt; on Unix systems) maintained by the Network Information Center (NIC) at Stanford Research Institute. Administrators would request IP-to-hostname mappings, and NIC would periodically distribute an updated hosts file to all connected systems. This approach did not scale beyond a few thousand hosts.&lt;br /&gt;
&lt;br /&gt;
In 1983, Paul Mockapetris designed the Domain Name System (RFC 882 and RFC 883, later superseded by RFC 1034 and RFC 1035), which has remained the foundation of internet naming for over 40 years.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-hierarchy-organizing-the-global-namespace&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Hierarchy: Organizing the Global Namespace ====&lt;br /&gt;
&lt;br /&gt;
DNS organizes domain names in a hierarchical tree structure, reading from right to left:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.engineering.example.com.&lt;br /&gt;
 |       |          |      |&lt;br /&gt;
 |       |          |      └── Root (empty label)&lt;br /&gt;
 |       |          └────────── Top-Level Domain (TLD)&lt;br /&gt;
 |       └───────────────────── Second-Level Domain (SLD)&lt;br /&gt;
 └───────────────────────────── Subdomain(s)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Root Domain (&amp;amp;quot;.&amp;amp;quot;)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
At the top of the hierarchy is the root domain, represented by an empty label. Fully qualified domain names (FQDNs) technically end with a dot: &amp;lt;code&amp;gt;example.com.&amp;lt;/code&amp;gt; The trailing dot is usually omitted in practice, but it represents the root.&lt;br /&gt;
&lt;br /&gt;
There are 13 root name server systems (labeled A through M: a.root-servers.net through m.root-servers.net) operated by different organizations worldwide. These are the authoritative source for information about top-level domains.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Top-Level Domains (TLDs)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TLDs are the highest level of the DNS hierarchy below the root. Categories include:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Generic TLDs (gTLDs)&amp;#039;&amp;#039;&amp;#039;: .com, .org, .net, .edu, .gov, .mil, .int&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Country-Code TLDs (ccTLDs)&amp;#039;&amp;#039;&amp;#039;: .us (United States), .uk (United Kingdom), .jp (Japan), .de (Germany)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;New gTLDs&amp;#039;&amp;#039;&amp;#039;: .app, .dev, .tech, .cloud, .blog, etc. (thousands added since 2013)&lt;br /&gt;
&lt;br /&gt;
Each TLD is managed by a registry organization. For example, VeriSign operates the .com and .net TLDs, while Educause operates .edu.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Second-Level Domains (SLDs)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
These are the domains that organizations register: &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;github.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mit.edu&amp;lt;/code&amp;gt;. You register these through domain registrars, who interface with the TLD registries.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Subdomains&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Domain owners can create arbitrary subdomains: &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mail.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;api.v2.example.com&amp;lt;/code&amp;gt;. Subdomains do not require separate registration—the owner of the parent domain controls all subdomains.&lt;br /&gt;
&lt;br /&gt;
DNS Servers: Authoritative vs. Recursive&lt;br /&gt;
&lt;br /&gt;
Two fundamentally different types of DNS servers perform distinct roles in the DNS infrastructure:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Authoritative DNS Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Authoritative servers are the definitive source of truth for a specific zone (portion of the DNS namespace). Characteristics:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Responsibility&amp;#039;&amp;#039;&amp;#039;: Provide answers for domains they have explicit authority over&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data Source&amp;#039;&amp;#039;&amp;#039;: Return information directly from their configured zone files&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior&amp;#039;&amp;#039;&amp;#039;: Only answer queries for domains in their zones; refer queries elsewhere for other domains&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operation&amp;#039;&amp;#039;&amp;#039;: Typically operated by domain owners or their DNS providers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Caching&amp;#039;&amp;#039;&amp;#039;: Do not cache responses for other domains&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Example&amp;#039;&amp;#039;&amp;#039;: If you own &amp;lt;code&amp;gt;example.com&amp;lt;/code&amp;gt;, your authoritative servers know the IP addresses for &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mail.example.com&amp;lt;/code&amp;gt;, etc.&lt;br /&gt;
&lt;br /&gt;
When you configure DNS records for your domain through your registrar or DNS provider, you are updating authoritative servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Recursive DNS Resolvers (Recursive Resolvers)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Recursive resolvers perform the DNS resolution process on behalf of clients. Characteristics:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Responsibility&amp;#039;&amp;#039;&amp;#039;: Answer any DNS query by recursively querying authoritative servers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Data Source&amp;#039;&amp;#039;&amp;#039;: Cache responses and query authoritative servers when cache misses occur&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Behavior&amp;#039;&amp;#039;&amp;#039;: Accept any query and traverse the DNS hierarchy to find answers&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Operation&amp;#039;&amp;#039;&amp;#039;: Typically provided by ISPs, corporations, or public DNS services (Google Public DNS 8.8.8.8, Cloudflare 1.1.1.1)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Caching&amp;#039;&amp;#039;&amp;#039;: Extensively cache responses to improve performance and reduce load&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Client-Facing&amp;#039;&amp;#039;&amp;#039;: This is the type of server that end-user devices query&lt;br /&gt;
&lt;br /&gt;
When your computer performs a DNS lookup, it queries a recursive resolver, which then does the work of finding the answer.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Distinction&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
This distinction is fundamental:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Authoritative servers&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;I have authority over example.com, and I can definitively tell you that [http://www.example.com www.example.com] is 203.0.113.50&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recursive resolvers&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;I don&amp;#039;t know the answer to your query, but I&amp;#039;ll traverse the DNS hierarchy to find an authoritative server that does know, then give you the answer&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analogy&amp;#039;&amp;#039;&amp;#039;: An authoritative DNS server is like a property owner who knows everything about their own house. A recursive resolver is like a GPS navigation system that can find directions to any address by consulting various maps and data sources.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-resolution-process-following-the-hierarchy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Resolution Process: Following the Hierarchy ====&lt;br /&gt;
&lt;br /&gt;
When you type &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; in your browser and press Enter, a complex resolution process occurs behind the scenes. Let us trace this process step by step.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scenario&amp;#039;&amp;#039;&amp;#039;: Your computer needs to resolve &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; to an IP address. Your system is configured to use the recursive resolver at 1.1.1.1 (Cloudflare&amp;#039;s public DNS).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Check Local Cache&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Your operating system maintains a local DNS cache. If &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; was recently resolved and the TTL hasn&amp;#039;t expired, the cached answer is returned immediately (typically in microseconds). No network query is needed.&lt;br /&gt;
&lt;br /&gt;
If the cache contains no valid entry, proceed to Step 2.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Query Recursive Resolver&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Your computer sends a DNS query to its configured recursive resolver (1.1.1.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Query: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The recursive resolver checks its own cache. If it has a recent answer, it returns immediately. If not, it begins the recursive resolution process.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Query Root Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The recursive resolver queries one of the 13 root name servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → Root Server: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The root server does not know the specific answer but knows which servers are authoritative for the .com TLD:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root → Resolver: I don&amp;#039;t know about www.example.com specifically, &lt;br /&gt;
                 but you can ask the .com TLD servers. Here are their addresses:&lt;br /&gt;
                 - a.gtld-servers.net (192.5.6.30)&lt;br /&gt;
                 - b.gtld-servers.net (192.33.14.30)&lt;br /&gt;
                 [and others...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is called a &amp;#039;&amp;#039;&amp;#039;referral&amp;#039;&amp;#039;&amp;#039;—the root server refers the resolver to servers that are more specific.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Query TLD Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The resolver queries one of the .com TLD servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → .com TLD Server: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
The TLD server knows which name servers are authoritative for example.com:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;.com TLD → Resolver: I don&amp;#039;t know about www.example.com specifically,&lt;br /&gt;
                     but the authoritative servers for example.com are:&lt;br /&gt;
                     - ns1.example.com (198.51.100.1)&lt;br /&gt;
                     - ns2.example.com (198.51.100.2)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Another referral, this time to the authoritative servers for the domain.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Query Authoritative Servers&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The resolver queries example.com&amp;#039;s authoritative name server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → ns1.example.com: What is the IP address for www.example.com?&amp;lt;/pre&amp;gt;&lt;br /&gt;
This server has definitive authority over example.com and provides the answer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ns1.example.com → Resolver: www.example.com is 203.0.113.50&lt;br /&gt;
                           (TTL: 3600 seconds)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Return Answer to Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The recursive resolver returns the answer to your computer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Resolver → Your Computer: www.example.com is 203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cache at Multiple Levels&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The answer is cached:&lt;br /&gt;
&lt;br /&gt;
* Your operating system caches the result (OS-level cache)&lt;br /&gt;
* Your browser may have its own cache (application-level cache)&lt;br /&gt;
* The recursive resolver caches the result (resolver cache)&lt;br /&gt;
&lt;br /&gt;
Future queries for &amp;lt;code&amp;gt;www.example.com&amp;lt;/code&amp;gt; within the TTL period (3600 seconds = 1 hour in this example) will be answered from cache without repeating the full resolution process.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-record-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== DNS Record Types ====&lt;br /&gt;
&lt;br /&gt;
DNS stores more than just IP addresses. Different record types serve different purposes:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;A Record (Address Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Maps a domain name to an IPv4 address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.    IN  A   203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the most common record type. When you browse to a website, your browser requests the A record to find the server&amp;#039;s IPv4 address.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;AAAA Record (IPv6 Address Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Maps a domain name to an IPv6 address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.    IN  AAAA    2001:db8::1&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pronounced &amp;amp;quot;quad-A record.&amp;amp;quot; As IPv6 adoption increases, these records are becoming more common.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;CNAME Record (Canonical Name Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Creates an alias from one domain name to another. The resolver follows the chain to find the final IP address.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;www.example.com.        IN  CNAME   webserver.example.com.&lt;br /&gt;
webserver.example.com.  IN  A       203.0.113.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use cases:&lt;br /&gt;
&lt;br /&gt;
* Simplifying infrastructure changes (update one A record instead of many)&lt;br /&gt;
* Content delivery networks (CDNs): alias your domain to the CDN&amp;#039;s domain&lt;br /&gt;
* Load balancing and failover scenarios&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MX Record (Mail Exchanger Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Specifies the mail servers responsible for receiving email for a domain. Includes a priority number (lower numbers have higher priority).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  MX  10  mail1.example.com.&lt;br /&gt;
example.com.    IN  MX  20  mail2.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
If mail1 is unavailable, the sending server tries mail2. Email delivery depends critically on properly configured MX records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NS Record (Name Server Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Specifies the authoritative name servers for a domain or zone.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  NS  ns1.example.com.&lt;br /&gt;
example.com.    IN  NS  ns2.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
These records delegate authority. When you register a domain, you specify NS records pointing to your DNS provider&amp;#039;s servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TXT Record (Text Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Stores arbitrary text data. Modern uses include:&lt;br /&gt;
&lt;br /&gt;
* SPF records for email authentication: &amp;lt;code&amp;gt;&amp;amp;quot;v=spf1 include:_spf.example.com ~all&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* DKIM keys for email signing&lt;br /&gt;
* Domain verification for services (Google, Microsoft, etc.): &amp;lt;code&amp;gt;&amp;amp;quot;google-site-verification=abc123xyz789&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* DMARC policies for email protection&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.    IN  TXT &amp;amp;quot;v=spf1 include:_spf.google.com ~all&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PTR Record (Pointer Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Provides reverse DNS lookup—mapping an IP address to a domain name. Used primarily for email server validation and logging.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;50.113.0.203.in-addr.arpa.  IN  PTR mail.example.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Mail servers often check PTR records to verify sender legitimacy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SOA Record (Start of Authority Record)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Defines authoritative information about a DNS zone, including the primary name server, administrator&amp;#039;s email, serial number, and timing parameters for zone transfers and caching.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;example.com.  IN  SOA ns1.example.com. admin.example.com. (&lt;br /&gt;
                      2024010101  ; Serial&lt;br /&gt;
                      3600        ; Refresh (1 hour)&lt;br /&gt;
                      1800        ; Retry (30 minutes)&lt;br /&gt;
                      1209600     ; Expire (2 weeks)&lt;br /&gt;
                      86400       ; Minimum TTL (1 day)&lt;br /&gt;
                      )&amp;lt;/pre&amp;gt;&lt;br /&gt;
Time To Live (TTL)&lt;br /&gt;
&lt;br /&gt;
Every DNS record includes a TTL (Time To Live) value specified in seconds. This value tells caching servers how long they can store the record before re-querying the authoritative server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Short TTL (e.g., 60-300 seconds)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Advantages:&lt;br /&gt;
&lt;br /&gt;
* Changes propagate quickly&lt;br /&gt;
* Useful before planned infrastructure changes&lt;br /&gt;
* Allows rapid failover&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
&lt;br /&gt;
* Increases DNS query load&lt;br /&gt;
* Slightly slower for end users (more frequent resolution)&lt;br /&gt;
* Higher bandwidth consumption&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Long TTL (e.g., 3600-86400 seconds)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Advantages:&lt;br /&gt;
&lt;br /&gt;
* Reduces DNS query load significantly&lt;br /&gt;
* Faster for end users (more cache hits)&lt;br /&gt;
* Lower bandwidth consumption&lt;br /&gt;
* Better resilience if authoritative servers become unavailable&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
&lt;br /&gt;
* Changes propagate slowly (users may see old data until TTL expires)&lt;br /&gt;
* Failover is slower&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;secure-shell-ssh-encrypted-remote-access&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Secure Shell (SSH): Encrypted Remote Access ===&lt;br /&gt;
&lt;br /&gt;
SSH (Secure Shell) is the standard protocol for secure remote access to systems. It replaced the insecure Telnet protocol in the 1990s and has become ubiquitous in system administration, DevOps, and software development.&lt;br /&gt;
&lt;br /&gt;
SSH provides three critical security properties:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: All data (including credentials) is encrypted using symmetric encryption (AES, ChaCha20)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Cryptographic MACs (Message Authentication Codes) prevent tampering&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Public key cryptography verifies server and optionally client identity&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-architecture&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Architecture ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH is not a single protocol but a protocol suite:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-TRANS (Transport Layer Protocol, RFC 4253)&amp;#039;&amp;#039;&amp;#039;: Establishes encrypted channel, handles server authentication&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-AUTH (Authentication Protocol, RFC 4252)&amp;#039;&amp;#039;&amp;#039;: Handles client authentication (password, public key, etc.)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SSH-CONN (Connection Protocol, RFC 4254)&amp;#039;&amp;#039;&amp;#039;: Multiplexes multiple channels (terminal session, port forwarding, etc.) over one TCP connection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Standard Port&amp;#039;&amp;#039;&amp;#039;: TCP port 22 (well-known port)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Establishment Sequence&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1. TCP three-way handshake establishes connection&lt;br /&gt;
2. SSH version exchange (both sides advertise SSH protocol version)&lt;br /&gt;
3. Algorithm negotiation (agree on encryption, MAC, compression algorithms)&lt;br /&gt;
4. Diffie-Hellman key exchange generates session keys&lt;br /&gt;
5. Server authenticates itself using its host key&lt;br /&gt;
6. Client authenticates itself (password or public key)&lt;br /&gt;
7. Encrypted session established, commands can be executed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-authentication-methods&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Authentication Methods ====&lt;br /&gt;
&lt;br /&gt;
SSH supports multiple authentication methods, which can be combined or used exclusively:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Password Authentication&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The client sends the password over the encrypted SSH connection (not plaintext like Telnet). While the password is protected in transit, password authentication has weaknesses:&lt;br /&gt;
&lt;br /&gt;
* Vulnerable to brute-force attacks if weak passwords are used&lt;br /&gt;
* Requires users to memorize or manage passwords&lt;br /&gt;
* Passwords can be stolen through phishing, keyloggers, or social engineering&lt;br /&gt;
* No way to distinguish different clients (all use the same password)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Public Key Authentication (Recommended)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Uses asymmetric cryptography:&lt;br /&gt;
&lt;br /&gt;
* Client generates a public/private key pair&lt;br /&gt;
* Public key is placed on the server in &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;&lt;br /&gt;
* Private key remains on the client and never leaves the system&lt;br /&gt;
* Server challenges client to prove possession of the private key without transmitting it&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advantages&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Strong cryptographic security (typically 2048-4096 bit RSA or 256 bit Ed25519)&lt;br /&gt;
* No password to steal or forget&lt;br /&gt;
* Can use different keys for different purposes&lt;br /&gt;
* Keys can be protected with passphrases&lt;br /&gt;
* Supports automation (keys without passphrases for scripts)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Modern Best Practice&amp;#039;&amp;#039;&amp;#039;: Disable password authentication entirely, allowing only public key authentication.&lt;br /&gt;
&lt;br /&gt;
Public Key Cryptography Review&lt;br /&gt;
&lt;br /&gt;
Since public key authentication is critical to SSH security, let us review the cryptographic foundation:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Cryptography Properties&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Two mathematically related keys: public key and private key&lt;br /&gt;
# Public key can be shared openly&lt;br /&gt;
# Private key must be kept secret&lt;br /&gt;
# Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
# Data signed with private key can be verified with public key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Authentication Protocol&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Client sends username and public key to server&lt;br /&gt;
# Server checks if public key is in &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; for that user&lt;br /&gt;
# Server generates random challenge, encrypts it with client&amp;#039;s public key, sends to client&lt;br /&gt;
# Client decrypts challenge with private key, computes hash, sends hash back&lt;br /&gt;
# Server verifies hash matches expected value&lt;br /&gt;
# If match: authentication succeeds (client proved possession of private key)&lt;br /&gt;
&lt;br /&gt;
The private key never travels across the network. The server cannot impersonate the client because it only has the public key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-key-types&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Key Types ====&lt;br /&gt;
&lt;br /&gt;
Modern SSH supports several key types with different security and performance characteristics:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;RSA (Rivest-Shamir-Adleman)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Traditional and widely supported&lt;br /&gt;
* Key size: 2048 or 4096 bits (3072 bits is middle ground)&lt;br /&gt;
* Slower than newer algorithms&lt;br /&gt;
* Still secure if key size is adequate (≥2048 bits)&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t rsa -b 4096&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Ed25519 (Edwards-curve Digital Signature Algorithm)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Modern elliptic curve algorithm (introduced ~2013)&lt;br /&gt;
* Key size: 256 bits (much smaller than RSA)&lt;br /&gt;
* Faster than RSA&lt;br /&gt;
* Strong security properties&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;ECDSA (Elliptic Curve Digital Signature Algorithm)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Older elliptic curve algorithm&lt;br /&gt;
* Key sizes: 256, 384, or 521 bits&lt;br /&gt;
* Concerns about NSA influence on NIST curves&lt;br /&gt;
* Ed25519 generally preferred over ECDSA for new keys&lt;br /&gt;
* Command: &amp;lt;code&amp;gt;ssh-keygen -t ecdsa -b 256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practice&amp;#039;&amp;#039;&amp;#039;: Use Ed25519 for new SSH keys. It offers excellent security with small key sizes and fast performance. Fall back to RSA 4096 only if connecting to older systems that don&amp;#039;t support Ed25519.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-host-key-verification-trust-on-first-use-tofu&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Host Key Verification: Trust On First Use (TOFU) ====&lt;br /&gt;
&lt;br /&gt;
One of SSH&amp;#039;s critical security features is host key verification, which protects against man-in-the-middle (MITM) attacks. This mechanism uses a &amp;amp;quot;Trust On First Use&amp;amp;quot; (TOFU) model.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The MITM Threat&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Your Computer] ←→ [Attacker&amp;#039;s Computer] ←→ [Real Server]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The attacker intercepts your connection, relays your commands to the real server, and relays responses back to you. From your perspective, everything appears normal, but the attacker can:&lt;br /&gt;
&lt;br /&gt;
* Log all your commands and responses&lt;br /&gt;
* Steal credentials if you authenticate with passwords&lt;br /&gt;
* Modify commands before forwarding them&lt;br /&gt;
* Inject malicious commands&lt;br /&gt;
&lt;br /&gt;
This is called a man-in-the-middle (MITM) attack.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH&amp;#039;s Defense: Host Keys&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Every SSH server has a unique host key (a public/private key pair) that identifies the server. The server&amp;#039;s public host key is its cryptographic identity.&lt;br /&gt;
&lt;br /&gt;
When you first connect to a server, SSH shows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The authenticity of host &amp;#039;server.example.com (203.0.113.50)&amp;#039; can&amp;#039;t be established.&lt;br /&gt;
ED25519 key fingerprint is SHA256:abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx.&lt;br /&gt;
Are you sure you want to continue connecting (yes/no/[fingerprint])?&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the TOFU moment. SSH is asking: &amp;amp;quot;I&amp;#039;ve never seen this server before. Do you trust this host key?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;If you type &amp;amp;quot;yes&amp;amp;quot;&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
SSH stores the server&amp;#039;s host key in &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;server.example.com,203.0.113.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
On subsequent connections, SSH verifies the server presents the same host key. If the host key changes, SSH displays an alarming warning:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @&lt;br /&gt;
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!&lt;br /&gt;
Someone could be eavesdropping on you right now (man-in-the-middle attack)!&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why the Warning?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A changed host key indicates one of several scenarios:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Legitimate&amp;#039;&amp;#039;&amp;#039;: Server was reinstalled, host key regenerated&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Legitimate&amp;#039;&amp;#039;&amp;#039;: Server moved to new hardware&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Malicious&amp;#039;&amp;#039;&amp;#039;: MITM attack in progress&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Configuration Error&amp;#039;&amp;#039;&amp;#039;: Connecting to wrong server&lt;br /&gt;
&lt;br /&gt;
SSH conservatively assumes the worst-case scenario and refuses the connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The TOFU Model&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;quot;Trust On First Use&amp;amp;quot; means:&lt;br /&gt;
&lt;br /&gt;
* First connection: You must manually verify the host key (typically by checking a fingerprint through an out-of-band channel)&lt;br /&gt;
* Subsequent connections: SSH automatically verifies the host key matches the stored value&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Comparison to PKI (Lab 8)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 8 that TLS uses a Public Key Infrastructure (PKI) with Certificate Authorities (CAs). Let us contrast the two models:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI/CA Model (HTTPS/TLS)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Server presents a certificate signed by a trusted CA&lt;br /&gt;
* Your browser has a list of trusted CA public keys&lt;br /&gt;
* Browser verifies certificate signature cryptographically&lt;br /&gt;
* No manual verification needed on first connection&lt;br /&gt;
* Scales well: one CA can sign millions of certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TOFU Model (SSH)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Server presents its public host key&lt;br /&gt;
* No centrally trusted authority&lt;br /&gt;
* You must manually verify on first connection (or accept risk)&lt;br /&gt;
* Subsequent connections are automatically verified&lt;br /&gt;
* Simpler infrastructure, but first-use verification is user&amp;#039;s responsibility&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why SSH Uses TOFU Instead of PKI&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# SSH predates widespread PKI adoption&lt;br /&gt;
# No consensus on which CAs to trust for SSH&lt;br /&gt;
# PKI adds complexity and cost (certificate purchase/management)&lt;br /&gt;
# TOFU works well for SSH&amp;#039;s primary use case (administrators connecting to their own servers)&lt;br /&gt;
# SSH certificates exist but are less common than HTTPS certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practice&amp;#039;&amp;#039;&amp;#039;: On first connection, verify the host key fingerprint through a trusted channel:&lt;br /&gt;
&lt;br /&gt;
* Check the fingerprint displayed in server documentation&lt;br /&gt;
* View the fingerprint on the server directly: &amp;lt;code&amp;gt;ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
* Compare with fingerprint shown by SSH client&lt;br /&gt;
&lt;br /&gt;
For production systems, always verify host keys. For test/lab environments, the risk is lower.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-known-hosts-format&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Known Hosts Format ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt; file stores server host keys. Format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;hostname,ip algorithm public_key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;server.example.com,203.0.113.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hashed Format&amp;#039;&amp;#039;&amp;#039;: Some systems hash the hostname for privacy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;|1|base64hash|base64hash ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
This prevents someone with access to your known_hosts file from learning which servers you connect to.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wildcard Entries&amp;#039;&amp;#039;&amp;#039;: You can use wildcards:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;*.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Management&amp;#039;&amp;#039;&amp;#039;: Remove entries when servers are legitimately reinstalled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -R server.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-authorized-keys&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== SSH Authorized Keys ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt; file on the server determines which public keys can authenticate as a specific user.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Format&amp;#039;&amp;#039;&amp;#039;: One public key per line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIdef456... user@laptop&lt;br /&gt;
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCghi789... user@desktop&amp;lt;/pre&amp;gt;&lt;br /&gt;
The trailing comment (&amp;lt;code&amp;gt;user@laptop&amp;lt;/code&amp;gt;) is optional but helps identify keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Permissions&amp;#039;&amp;#039;&amp;#039;: SSH is strict about file permissions for security:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod 700 ~/.ssh              # Directory: rwx------&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys  # File: rw-------&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If permissions are too permissive, SSH may refuse to use the keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Restricting Keys&amp;#039;&amp;#039;&amp;#039;: You can add restrictions before keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;command=&amp;amp;quot;backup.sh&amp;amp;quot; ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIdef456... backup-key&lt;br /&gt;
from=&amp;amp;quot;203.0.113.0/24&amp;amp;quot; ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIghi789... admin-key&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;command=&amp;amp;quot;backup.sh&amp;amp;quot;&amp;lt;/code&amp;gt;: This key can only run backup.sh (useful for automation)&lt;br /&gt;
* &amp;lt;code&amp;gt;from=&amp;amp;quot;203.0.113.0/24&amp;amp;quot;&amp;lt;/code&amp;gt;: This key only works from specified IP range&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Agent&amp;#039;&amp;#039;&amp;#039;: For convenience, use ssh-agent to cache decrypted private keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;eval $(ssh-agent)&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The agent holds the unlocked private key in memory, allowing multiple connections without re-entering the passphrase.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hypertext-transfer-protocol-http-the-universal-application-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== HyperText Transfer Protocol (HTTP): The Universal Application Protocol ===&lt;br /&gt;
&lt;br /&gt;
HTTP (HyperText Transfer Protocol) was initially designed in 1991 by Tim Berners-Lee to transfer hypertext documents (HTML web pages). Over three decades, HTTP has evolved far beyond its original purpose to become the universal protocol for nearly all application-level communication on the internet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;why-http-is-everywhere&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Why HTTP is Everywhere ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Simplicity&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP uses human-readable text commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;GET /api/users HTTP/1.1&lt;br /&gt;
Host: api.example.com&amp;lt;/pre&amp;gt;&lt;br /&gt;
This simplicity makes HTTP easy to:&lt;br /&gt;
&lt;br /&gt;
* Debug (you can read the protocol)&lt;br /&gt;
* Implement (libraries exist for every language)&lt;br /&gt;
* Extend (add custom headers easily)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Statelessness&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Each HTTP request is independent—the server doesn&amp;#039;t need to maintain state between requests. This design decision has profound implications:&lt;br /&gt;
&lt;br /&gt;
* Servers can handle millions of clients without per-client memory&lt;br /&gt;
* Horizontal scaling is trivial (add more servers, any server can handle any request)&lt;br /&gt;
* Failures don&amp;#039;t cascade (if a request fails, retry without state recovery)&lt;br /&gt;
* Caching is straightforward (each request is independent)&lt;br /&gt;
&lt;br /&gt;
State management, when needed, is handled at the application layer (cookies, sessions, tokens).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Flexible Content&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP can transfer any type of data:&lt;br /&gt;
&lt;br /&gt;
* HTML documents&lt;br /&gt;
* JSON API responses&lt;br /&gt;
* XML data&lt;br /&gt;
* Binary files (images, videos, executables)&lt;br /&gt;
* Streaming data&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;Content-Type&amp;lt;/code&amp;gt; header specifies what the body contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Content-Type: application/json&lt;br /&gt;
Content-Type: text/html&lt;br /&gt;
Content-Type: image/png&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Firewall Friendliness&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
HTTP (port 80) and HTTPS (port 443) are almost universally allowed through firewalls. Companies may block many ports, but they cannot block web traffic without breaking internet access. Applications using HTTP/HTTPS thus avoid firewall issues that plague custom protocols.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Tooling and Infrastructure&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Decades of investment have created rich ecosystems:&lt;br /&gt;
&lt;br /&gt;
* Web servers: Apache, Nginx, Caddy, IIS&lt;br /&gt;
* Reverse proxies and load balancers&lt;br /&gt;
* CDNs (Content Delivery Networks)&lt;br /&gt;
* Caching proxies&lt;br /&gt;
* API gateways&lt;br /&gt;
* Monitoring and debugging tools&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Result&amp;#039;&amp;#039;&amp;#039;: HTTP has become the default choice for application communication. APIs that might have used custom TCP protocols now use HTTP-based REST or GraphQL. Real-time protocols that might have used custom UDP protocols now use WebSockets (which upgrade HTTP connections). Even protocols that don&amp;#039;t naturally map to HTTP often use it anyway for operational simplicity.&lt;br /&gt;
&lt;br /&gt;
HTTP Request-Response Model&lt;br /&gt;
&lt;br /&gt;
HTTP uses a simple request-response pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Client sends HTTP Request&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;METHOD PATH VERSION&lt;br /&gt;
Headers&lt;br /&gt;
(blank line)&lt;br /&gt;
Body (optional)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Server sends HTTP Response&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;VERSION STATUS_CODE STATUS_MESSAGE&lt;br /&gt;
Headers&lt;br /&gt;
(blank line)&lt;br /&gt;
Body (optional)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Example Exchange&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Request:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET /api/users/123 HTTP/1.1&lt;br /&gt;
Host: api.example.com&lt;br /&gt;
User-Agent: curl/7.68.0&lt;br /&gt;
Accept: application/json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Response:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;HTTP/1.1 200 OK&lt;br /&gt;
Content-Type: application/json&lt;br /&gt;
Content-Length: 58&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;id&amp;quot;:123,&amp;quot;name&amp;quot;:&amp;quot;Alice&amp;quot;,&amp;quot;email&amp;quot;:&amp;quot;alice@example.com&amp;quot;}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-methods-verbs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Methods (Verbs) ====&lt;br /&gt;
&lt;br /&gt;
HTTP defines several methods that indicate the desired action:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;GET&amp;#039;&amp;#039;&amp;#039;: Retrieve a resource&lt;br /&gt;
&lt;br /&gt;
* Should not modify server state (idempotent and safe)&lt;br /&gt;
* Can be cached&lt;br /&gt;
* Examples: Load web page, fetch API data, download file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;POST&amp;#039;&amp;#039;&amp;#039;: Submit data to create a new resource&lt;br /&gt;
&lt;br /&gt;
* May modify server state&lt;br /&gt;
* Not idempotent (repeating creates multiple resources)&lt;br /&gt;
* Examples: Submit form, create new user via API&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PUT&amp;#039;&amp;#039;&amp;#039;: Update an existing resource (or create if doesn&amp;#039;t exist)&lt;br /&gt;
&lt;br /&gt;
* Idempotent (repeating has same effect as doing once)&lt;br /&gt;
* Replaces entire resource&lt;br /&gt;
* Examples: Update user profile, upload file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PATCH&amp;#039;&amp;#039;&amp;#039;: Partially update a resource&lt;br /&gt;
&lt;br /&gt;
* Apply partial modifications&lt;br /&gt;
* Not necessarily idempotent (depends on implementation)&lt;br /&gt;
* Examples: Update single field of a record&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DELETE&amp;#039;&amp;#039;&amp;#039;: Remove a resource&lt;br /&gt;
&lt;br /&gt;
* Idempotent (deleting twice has same effect as deleting once)&lt;br /&gt;
* Examples: Delete user account, remove file&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HEAD&amp;#039;&amp;#039;&amp;#039;: Same as GET but returns only headers (no body)&lt;br /&gt;
&lt;br /&gt;
* Check if resource exists&lt;br /&gt;
* Check content size before downloading&lt;br /&gt;
* Verify cache freshness&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;OPTIONS&amp;#039;&amp;#039;&amp;#039;: Query supported methods for a resource&lt;br /&gt;
&lt;br /&gt;
* Used for CORS (Cross-Origin Resource Sharing) preflight requests&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-status-codes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Status Codes ====&lt;br /&gt;
&lt;br /&gt;
The status code indicates the result of the request. Codes are grouped:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1xx: Informational&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 100 Continue: Client should continue with request&lt;br /&gt;
* 101 Switching Protocols: Server switching protocols (e.g., WebSocket upgrade)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2xx: Success&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 200 OK: Request succeeded&lt;br /&gt;
* 201 Created: Resource created successfully&lt;br /&gt;
* 204 No Content: Success but no response body&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3xx: Redirection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 301 Moved Permanently: Resource moved, update bookmarks&lt;br /&gt;
* 302 Found: Temporary redirect&lt;br /&gt;
* 304 Not Modified: Cached version is still valid&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4xx: Client Error&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 400 Bad Request: Malformed request&lt;br /&gt;
* 401 Unauthorized: Authentication required&lt;br /&gt;
* 403 Forbidden: Authenticated but not authorized&lt;br /&gt;
* 404 Not Found: Resource doesn&amp;#039;t exist&lt;br /&gt;
* 429 Too Many Requests: Rate limit exceeded&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5xx: Server Error&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* 500 Internal Server Error: Server encountered error&lt;br /&gt;
* 502 Bad Gateway: Proxy received invalid response from upstream&lt;br /&gt;
* 503 Service Unavailable: Server temporarily overloaded or down&lt;br /&gt;
* 504 Gateway Timeout: Proxy timeout waiting for upstream&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http-headers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP Headers ====&lt;br /&gt;
&lt;br /&gt;
Headers provide metadata about the request or response. Some critical headers:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request Headers&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Host: api.example.com&amp;lt;/code&amp;gt; — Required in HTTP/1.1, specifies target hostname&lt;br /&gt;
* &amp;lt;code&amp;gt;User-Agent: curl/7.68.0&amp;lt;/code&amp;gt; — Identifies client software&lt;br /&gt;
* &amp;lt;code&amp;gt;Accept: application/json&amp;lt;/code&amp;gt; — Content types client understands&lt;br /&gt;
* &amp;lt;code&amp;gt;Authorization: Bearer token123&amp;lt;/code&amp;gt; — Authentication credentials&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Type: application/json&amp;lt;/code&amp;gt; — Type of request body&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Length: 58&amp;lt;/code&amp;gt; — Size of request body in bytes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Response Headers&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Type: application/json&amp;lt;/code&amp;gt; — Type of response body&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Length: 1234&amp;lt;/code&amp;gt; — Size of response body&lt;br /&gt;
* &amp;lt;code&amp;gt;Cache-Control: max-age=3600&amp;lt;/code&amp;gt; — Caching directives&lt;br /&gt;
* &amp;lt;code&amp;gt;Set-Cookie: session=abc123; HttpOnly&amp;lt;/code&amp;gt; — Set cookie in browser&lt;br /&gt;
* &amp;lt;code&amp;gt;Location: /api/users/456&amp;lt;/code&amp;gt; — Redirect target (with 3xx status)&lt;br /&gt;
* &amp;lt;code&amp;gt;Server: nginx/1.18.0&amp;lt;/code&amp;gt; — Server software (often hidden for security)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security Headers&amp;#039;&amp;#039;&amp;#039; (modern best practices):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Strict-Transport-Security: max-age=31536000&amp;lt;/code&amp;gt; — Force HTTPS&lt;br /&gt;
* &amp;lt;code&amp;gt;Content-Security-Policy: default-src &amp;#039;self&amp;#039;&amp;lt;/code&amp;gt; — Mitigate XSS attacks&lt;br /&gt;
* &amp;lt;code&amp;gt;X-Frame-Options: DENY&amp;lt;/code&amp;gt; — Prevent clickjacking&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;virtual-hosting-multiple-sites-on-one-ip&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Virtual Hosting: Multiple Sites on One IP ====&lt;br /&gt;
&lt;br /&gt;
HTTP&amp;#039;s &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header enables virtual hosting—running multiple websites on a single IP address. The web server examines the &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header to determine which site the request targets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET / HTTP/1.1&lt;br /&gt;
Host: site1.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
vs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;http&amp;quot;&amp;gt;GET / HTTP/1.1&lt;br /&gt;
Host: site2.example.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both requests go to the same IP address (e.g., 203.0.113.50) and same port (80 or 443), but the &amp;lt;code&amp;gt;Host&amp;lt;/code&amp;gt; header tells the server which site to serve. This is how shared hosting providers can host thousands of websites on relatively few servers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTPS Complication&amp;#039;&amp;#039;&amp;#039;: Virtual hosting works seamlessly for HTTP, but HTTPS initially had issues because the TLS handshake occurs before the HTTP request (so the server doesn&amp;#039;t know which certificate to present). Server Name Indication (SNI), added to TLS in 2003, solved this by including the hostname in the TLS handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;http11-vs-http2-vs-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== HTTP/1.1 vs. HTTP/2 vs. HTTP/3 ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/1.1&amp;#039;&amp;#039;&amp;#039; (1997, RFC 2616, updated RFC 7230-7235):&lt;br /&gt;
&lt;br /&gt;
* Text-based protocol&lt;br /&gt;
* One request per connection (or serial requests with keep-alive)&lt;br /&gt;
* Head-of-line blocking (one slow request delays all subsequent requests)&lt;br /&gt;
* Still widely used&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/2&amp;#039;&amp;#039;&amp;#039; (2015, RFC 7540):&lt;br /&gt;
&lt;br /&gt;
* Binary protocol (more efficient parsing)&lt;br /&gt;
* Multiplexing (multiple concurrent requests on one connection)&lt;br /&gt;
* Server push (server can send resources before client requests them)&lt;br /&gt;
* Header compression (reduces overhead)&lt;br /&gt;
* Requires HTTPS in practice (browsers only support HTTP/2 over TLS)&lt;br /&gt;
* Growing adoption&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/3&amp;#039;&amp;#039;&amp;#039; (2022, RFC 9114):&lt;br /&gt;
&lt;br /&gt;
* Uses QUIC instead of TCP (UDP-based transport with built-in TLS)&lt;br /&gt;
* Reduces connection establishment latency&lt;br /&gt;
* Better handling of packet loss&lt;br /&gt;
* Connection migration (survive IP address changes)&lt;br /&gt;
* Newest, increasing adoption&lt;br /&gt;
&lt;br /&gt;
For this lab, we use HTTP/1.1 for simplicity, but modern production systems increasingly deploy HTTP/2 and HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Reverse Proxies: The Modern Web Architecture Pattern&lt;br /&gt;
&lt;br /&gt;
A reverse proxy is a server that sits in front of backend servers and forwards client requests to them. The client believes it is talking directly to the origin server, unaware of the proxy&amp;#039;s existence.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Terminology Clarification&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Forward Proxy&amp;#039;&amp;#039;&amp;#039;: Client knows about proxy, uses it to access external servers (e.g., corporate proxy for internet access)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reverse Proxy&amp;#039;&amp;#039;&amp;#039;: Client unaware of proxy, thinks it&amp;#039;s talking to the origin server directly&lt;br /&gt;
&lt;br /&gt;
Reverse Proxy Benefits&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Load Balancing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Distribute requests across multiple backend servers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client → Reverse Proxy → [Backend 1]&lt;br /&gt;
                      → [Backend 2]&lt;br /&gt;
                      → [Backend 3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Strategies:&lt;br /&gt;
&lt;br /&gt;
* Round-robin: Cycle through backends in order&lt;br /&gt;
* Least connections: Send to backend with fewest active connections&lt;br /&gt;
* IP hash: Always send same client to same backend (session affinity)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. TLS Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The reverse proxy handles TLS encryption/decryption:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client ←(HTTPS)→ Reverse Proxy ←(HTTP)→ Backend Servers&amp;lt;/pre&amp;gt;&lt;br /&gt;
Benefits:&lt;br /&gt;
&lt;br /&gt;
* Backend servers don&amp;#039;t need TLS configuration or certificates&lt;br /&gt;
* Centralized certificate management&lt;br /&gt;
* Reduces computational load on backends (TLS crypto is expensive)&lt;br /&gt;
* Simplifies backend server configuration&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;3. Caching&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Cache responses to reduce backend load:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client → Reverse Proxy (check cache) → Backend (if cache miss)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Subsequent requests for the same resource are served from cache without hitting backends. Can dramatically improve performance and reduce costs.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;4. Compression&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The proxy can compress responses (gzip, brotli) before sending to clients, reducing bandwidth:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Backend → Proxy (compress) → Client&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;5. Security&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Hide backend server details (IP addresses, software versions)&lt;br /&gt;
* Web Application Firewall (WAF) functionality&lt;br /&gt;
* Rate limiting and DDoS protection&lt;br /&gt;
* Centralized logging and monitoring&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;6. Path-Based Routing&amp;#039;&amp;#039;&amp;#039; (Critical for Microservices)&lt;br /&gt;
&lt;br /&gt;
Route requests to different backends based on URL path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;client request /api/users    → Reverse Proxy → User Service&lt;br /&gt;
client request /api/orders   → Reverse Proxy → Order Service&lt;br /&gt;
client request /api/products → Reverse Proxy → Product Service&amp;lt;/pre&amp;gt;&lt;br /&gt;
From the client&amp;#039;s perspective, everything is at one domain (e.g., &amp;lt;code&amp;gt;api.example.com&amp;lt;/code&amp;gt;). The reverse proxy routes to appropriate microservices based on path. This is the architectural pattern that enables modern microservices.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;path-based-routing-in-depth&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Path-Based Routing in Depth ====&lt;br /&gt;
&lt;br /&gt;
Path-based routing is the killer feature that made HTTP the universal protocol. Let us examine why:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem Without Path-Based Routing&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Suppose you have three backend services:&lt;br /&gt;
&lt;br /&gt;
* User service (manages user accounts)&lt;br /&gt;
* Order service (manages orders)&lt;br /&gt;
* Product service (manages product catalog)&lt;br /&gt;
&lt;br /&gt;
Without path-based routing, clients must know the specific addresses of each service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;user-service.example.com/users&lt;br /&gt;
order-service.example.com/orders&lt;br /&gt;
product-service.example.com/products&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problems&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Clients must maintain knowledge of all services&lt;br /&gt;
# Adding/removing/renaming services breaks clients&lt;br /&gt;
# Difficult to manage CORS (Cross-Origin Resource Sharing) with multiple domains&lt;br /&gt;
# More complex DNS management&lt;br /&gt;
# More complex TLS certificate management (separate cert for each service)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Solution With Path-Based Routing&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
One unified API endpoint:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;api.example.com/users     → routes to User Service&lt;br /&gt;
api.example.com/orders    → routes to Order Service&lt;br /&gt;
api.example.com/products  → routes to Product Service&amp;lt;/pre&amp;gt;&lt;br /&gt;
Reverse proxy configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if path starts with /users    → forward to user-service:3001&lt;br /&gt;
if path starts with /orders   → forward to order-service:3002&lt;br /&gt;
if path starts with /products → forward to product-service:3003&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Benefits&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Clients see one consistent API domain&lt;br /&gt;
# Backend services can change without client awareness&lt;br /&gt;
# One TLS certificate for all services&lt;br /&gt;
# Simplified CORS configuration&lt;br /&gt;
# Centralized authentication and rate limiting&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is How Modern Systems Work&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kubernetes&amp;#039;&amp;#039;&amp;#039;: Ingress controllers route paths to services&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;AWS&amp;#039;&amp;#039;&amp;#039;: Application Load Balancers route paths to target groups&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Microservices&amp;#039;&amp;#039;&amp;#039;: API Gateway routes paths to microservices&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Netflix, Uber, Airbnb&amp;#039;&amp;#039;&amp;#039;: All use path-based routing at scale&lt;br /&gt;
&lt;br /&gt;
You will implement this pattern in the exercises.&lt;br /&gt;
&lt;br /&gt;
Evolution from Monoliths to Microservices&lt;br /&gt;
&lt;br /&gt;
Understanding path-based routing requires understanding the architectural evolution:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Monolithic Architecture&amp;#039;&amp;#039;&amp;#039; (Traditional):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Single Application&lt;br /&gt;
├── User Management&lt;br /&gt;
├── Order Processing&lt;br /&gt;
├── Product Catalog&lt;br /&gt;
├── Payment Processing&lt;br /&gt;
└── Notification System&amp;lt;/pre&amp;gt;&lt;br /&gt;
All functionality in one codebase, runs as one process. Advantages: simple deployment, shared database, no network calls between components. Disadvantages: hard to scale specific components, one bug can crash entire system, difficult to update independently.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Microservices Architecture&amp;#039;&amp;#039;&amp;#039; (Modern):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;User Service      ──┐&lt;br /&gt;
Order Service     ──┤&lt;br /&gt;
Product Service   ──├── Reverse Proxy ── Clients&lt;br /&gt;
Payment Service   ──┤&lt;br /&gt;
Notification Svc  ──┘&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each service is independent: separate codebase, separate process, separate deployment, separate scaling. Advantages: scale components independently, update services without affecting others, use different technologies per service, isolated failures. Disadvantages: increased operational complexity, network latency between services, distributed system challenges.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Reverse Proxy&amp;#039;s Role&amp;#039;&amp;#039;&amp;#039;: Makes microservices look like a monolith to clients. Clients don&amp;#039;t know or care that backend is distributed—they just make requests to one API endpoint.&lt;br /&gt;
&lt;br /&gt;
Observability and Debugging&lt;br /&gt;
&lt;br /&gt;
Modern reverse proxies provide rich observability:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access Logs&amp;#039;&amp;#039;&amp;#039;: Record every request:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;203.0.113.42 - - [05/Dec/2024:14:23:45 +0000] &amp;amp;quot;GET /api/users HTTP/1.1&amp;amp;quot; 200 1234 &amp;amp;quot;-&amp;amp;quot; &amp;amp;quot;curl/7.68.0&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields: client IP, timestamp, method, path, protocol version, status code, response size, referer, user-agent&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Error Logs&amp;#039;&amp;#039;&amp;#039;: Record issues:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[error] 2024/12/05 14:23:50 upstream timed out (110: Connection timed out) while connecting to upstream&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Metrics&amp;#039;&amp;#039;&amp;#039;: Request rate, error rate, latency percentiles (p50, p95, p99), upstream health&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Distributed Tracing&amp;#039;&amp;#039;&amp;#039;: Track requests across services using trace IDs in headers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;X-Request-ID: 7f2a3b4c-8d9e-4f1a-b2c3-d4e5f6a7b8c9&amp;lt;/pre&amp;gt;&lt;br /&gt;
When troubleshooting issues, these logs and metrics are invaluable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;laboratory-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Laboratory Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-dns-resolution-and-analysis&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: DNS Resolution and Analysis ===&lt;br /&gt;
&lt;br /&gt;
Objective: Use DNS client tools to query various record types and observe the DNS resolution process. Understand the difference between authoritative and recursive queries, analyze TTL values, and measure resolution performance.&lt;br /&gt;
&lt;br /&gt;
Required Topology: You must have the Red and Blue namespaces from Lab 8 with IP addresses 10.0.0.1 and 10.0.0.2 respectively, connected via bridge br0. Verify connectivity before proceeding:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If this fails, recreate your Lab 8 topology before continuing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-dns-queries-with-dig&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic DNS Queries with dig ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; (Domain Information Groper) tool is the primary DNS debugging utility. It provides detailed information about DNS queries and responses.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Verify dig is installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig -v&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DiG 9.18.1-1ubuntu1.3-Ubuntu&amp;lt;/pre&amp;gt;&lt;br /&gt;
If not installed: &amp;lt;code&amp;gt;sudo apt install dnsutils&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Perform a basic A record query for google.com:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;; &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; DiG 9.18.1-1ubuntu1.3-Ubuntu &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; google.com&lt;br /&gt;
;; global options: +cmd&lt;br /&gt;
;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 12345&lt;br /&gt;
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags:; udp: 1232&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;google.com.                    IN      A&lt;br /&gt;
&lt;br /&gt;
;; ANSWER SECTION:&lt;br /&gt;
google.com.             163     IN      A       142.250.185.46&lt;br /&gt;
&lt;br /&gt;
;; Query time: 23 msec&lt;br /&gt;
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)&lt;br /&gt;
;; WHEN: Fri Dec 05 14:30:22 UTC 2024&lt;br /&gt;
;; MSG SIZE  rcvd: 55&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
Let us dissect this output:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Header Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status: NOERROR&amp;lt;/code&amp;gt; — Query succeeded&lt;br /&gt;
* &amp;lt;code&amp;gt;id: 12345&amp;lt;/code&amp;gt; — Random query ID for matching requests/responses&lt;br /&gt;
* &amp;lt;code&amp;gt;flags: qr rd ra&amp;lt;/code&amp;gt; —&lt;br /&gt;
** &amp;lt;code&amp;gt;qr&amp;lt;/code&amp;gt;: Query Response (this is a response, not a query)&lt;br /&gt;
** &amp;lt;code&amp;gt;rd&amp;lt;/code&amp;gt;: Recursion Desired (client requested recursive resolution)&lt;br /&gt;
** &amp;lt;code&amp;gt;ra&amp;lt;/code&amp;gt;: Recursion Available (server supports recursive queries)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Question Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;google.com.                    IN      A&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows what we asked for:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;google.com.&amp;lt;/code&amp;gt; — Domain name (with trailing dot, the FQDN)&lt;br /&gt;
* &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; — Internet class (as opposed to other historical DNS classes)&lt;br /&gt;
* &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; — Record type (IPv4 address)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Answer Section&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;google.com.             163     IN      A       142.250.185.46&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is the answer:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;google.com.&amp;lt;/code&amp;gt; — Domain queried&lt;br /&gt;
* &amp;lt;code&amp;gt;163&amp;lt;/code&amp;gt; — TTL (Time To Live) in seconds, countdown starts when received&lt;br /&gt;
* &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; — Internet class&lt;br /&gt;
* &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; — Record type&lt;br /&gt;
* &amp;lt;code&amp;gt;142.250.185.46&amp;lt;/code&amp;gt; — The IPv4 address&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Metadata&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Query time: 23 msec&amp;lt;/code&amp;gt; — Time to get response (includes network latency and resolution time)&lt;br /&gt;
* &amp;lt;code&amp;gt;SERVER: 127.0.0.53#53&amp;lt;/code&amp;gt; — DNS server queried (systemd-resolved on Ubuntu)&lt;br /&gt;
* &amp;lt;code&amp;gt;MSG SIZE  rcvd: 55&amp;lt;/code&amp;gt; — Response packet size in bytes&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Query a specific DNS server directly (bypass systemd-resolved):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig @8.8.8.8 google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;@8.8.8.8&amp;lt;/code&amp;gt; specifies Google Public DNS. Compare the query time to the previous result.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Query an IPv6 address (AAAA record):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com AAAA&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             299     IN      AAAA    2607:f8b0:4004:c07::66&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: You might see multiple AAAA records if Google has multiple IPv6 addresses for redundancy.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Query mail server records (MX records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com MX&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             3599    IN      MX      10 smtp.google.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
The number (10) is the priority. Lower numbers have higher priority. If multiple MX records exist, mail servers try the lowest priority number first.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6&amp;#039;&amp;#039;&amp;#039;: Query name server records (NS records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com NS&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             21599   IN      NS      ns1.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns2.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns3.google.com.&lt;br /&gt;
google.com.             21599   IN      NS      ns4.google.com.&amp;lt;/pre&amp;gt;&lt;br /&gt;
These are Google&amp;#039;s authoritative name servers. These servers have definitive authority over google.com records.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7&amp;#039;&amp;#039;&amp;#039;: Query text records (TXT records):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig google.com TXT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (answer section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; ANSWER SECTION:&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;v=spf1 include:_spf.google.com ~all&amp;amp;quot;&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95&amp;amp;quot;&lt;br /&gt;
google.com.             3599    IN      TXT     &amp;amp;quot;google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
TXT records store arbitrary text. Common uses: SPF records for email authentication, domain verification for services, DKIM public keys.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8&amp;#039;&amp;#039;&amp;#039;: Request short output format:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +short google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;142.250.185.46&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just the IP address, no verbose information. Useful for scripts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9&amp;#039;&amp;#039;&amp;#039;: Trace the DNS resolution path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +trace google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039; (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;.                       518400  IN      NS      a.root-servers.net.&lt;br /&gt;
.                       518400  IN      NS      b.root-servers.net.&lt;br /&gt;
[... other root servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 239 bytes from 8.8.8.8#53 in 23 ms&lt;br /&gt;
&lt;br /&gt;
com.                    172800  IN      NS      a.gtld-servers.net.&lt;br /&gt;
com.                    172800  IN      NS      b.gtld-servers.net.&lt;br /&gt;
[... other .com TLD servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 1173 bytes from 192.5.5.241#53(a.root-servers.net) in 87 ms&lt;br /&gt;
&lt;br /&gt;
google.com.             172800  IN      NS      ns1.google.com.&lt;br /&gt;
google.com.             172800  IN      NS      ns2.google.com.&lt;br /&gt;
[... other google.com name servers ...]&lt;br /&gt;
&lt;br /&gt;
;; Received 836 bytes from 192.12.94.30#53(e.gtld-servers.net) in 103 ms&lt;br /&gt;
&lt;br /&gt;
google.com.             300     IN      A       142.250.185.46&lt;br /&gt;
&lt;br /&gt;
;; Received 55 bytes from 216.239.32.10#53(ns1.google.com) in 11 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows the complete resolution process:&lt;br /&gt;
&lt;br /&gt;
# Query root servers → get .com TLD servers&lt;br /&gt;
# Query .com TLD servers → get google.com authoritative servers&lt;br /&gt;
# Query google.com authoritative servers → get final answer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable A ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; commands for A, AAAA, MX, NS, and TXT records for two different domains (suggestion: use a domain like station01.internal.arh.pub.ro, or stud.etti.pub.ro for MX)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;dig +trace&amp;lt;/code&amp;gt; for any domain&lt;br /&gt;
# Output of two consecutive &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; queries showing TTL countdown&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-ssh-configuration-and-key-based-authentication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: SSH Configuration and Key-Based Authentication ===&lt;br /&gt;
&lt;br /&gt;
Objective: Configure SSH server and client, generate Ed25519 key pairs, implement key-based authentication, understand the Trust On First Use (TOFU) security model, and observe host key verification&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-verifying-ssh-installation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Verifying SSH Installation ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Check if SSH server is installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dpkg -l | grep openssh-server&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ii  openssh-server  1:8.9p1-3ubuntu0.4  amd64  secure shell (SSH) server&amp;lt;/pre&amp;gt;&lt;br /&gt;
If not installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt install openssh-server openssh-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Check SSH service status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl status sshd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not running:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo systemctl start sshd&lt;br /&gt;
sudo systemctl enable sshd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-generating-ssh-key-pairs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Generating SSH Key Pairs ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll generate Ed25519 keys because they offer strong security with small key sizes and fast performance.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Generate a key pair as your current user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -t ed25519 -C &amp;quot;lab-key&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected interaction&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Generating public/private ed25519 key pair.&lt;br /&gt;
Enter file in which to save the key (/home/youruser/.ssh/id_ed25519): [press Enter]&lt;br /&gt;
Enter passphrase (empty for no passphrase): [press Enter for no passphrase]&lt;br /&gt;
Enter same passphrase again: [press Enter]&lt;br /&gt;
Your identification has been saved in /home/youruser/.ssh/id_ed25519&lt;br /&gt;
Your public key has been saved in /home/youruser/.ssh/id_ed25519.pub&lt;br /&gt;
The key fingerprint is:&lt;br /&gt;
SHA256:xyz789abc123def456ghi789jkl012mno345pqr678 lab-key&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key Decisions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;File location&amp;#039;&amp;#039;&amp;#039;: Accept default (&amp;lt;code&amp;gt;~/.ssh/id_ed25519&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Passphrase&amp;#039;&amp;#039;&amp;#039;: For this lab, skip the passphrase for simplicity. In production, always use a strong passphrase to protect the private key. A passphrase encrypts the private key file—even if an attacker steals the file, they cannot use it without the passphrase.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Examine the generated keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la ~/.ssh/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwx------ 2 youruser youruser 4096 Dec  5 14:30 .&lt;br /&gt;
drwxr-x--- 8 youruser youruser 4096 Dec  5 14:30 ..&lt;br /&gt;
-rw------- 1 youruser youruser  411 Dec  5 14:30 id_ed25519&lt;br /&gt;
-rw-r--r-- 1 youruser youruser   99 Dec  5 14:30 id_ed25519.pub&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical&amp;#039;&amp;#039;&amp;#039;: Private key (&amp;lt;code&amp;gt;id_ed25519&amp;lt;/code&amp;gt;) has mode 600 (readable/writable only by owner). Public key (&amp;lt;code&amp;gt;.pub&amp;lt;/code&amp;gt;) has mode 644 (world-readable).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: View your public key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/id_ed25519.pub&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This public key can be freely shared. You&amp;#039;ll copy this to accounts you want to access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-setting-up-key-based-authentication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Setting Up Key-Based Authentication ====&lt;br /&gt;
&lt;br /&gt;
Configure SSH so you can connect to the &amp;lt;code&amp;gt;sshtest&amp;lt;/code&amp;gt; account using your key.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Copy your public key to the test user&amp;#039;s authorized_keys:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/id_ed25519.pub ~/.ssh/authorized_keys&lt;br /&gt;
sudo chmod 600 /home/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Verify the setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ls -la /home/sshtest/.ssh/&lt;br /&gt;
sudo cat ~/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical Permissions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;~/.ssh/&amp;lt;/code&amp;gt;: 700 (drwx------)&lt;br /&gt;
* &amp;lt;code&amp;gt;authorized_keys&amp;lt;/code&amp;gt;: 600 (-rw-------)&lt;br /&gt;
* Owner must be the target user&lt;br /&gt;
&lt;br /&gt;
If permissions are wrong, SSH will silently ignore the keys and fall back to password authentication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-first-connection---trust-on-first-use-tofu&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: First Connection - Trust On First Use (TOFU) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Connect to localhost as the test user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output (first connection)&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The authenticity of host &amp;#039;localhost (127.0.0.1)&amp;#039; can&amp;#039;t be established.&lt;br /&gt;
ED25519 key fingerprint is SHA256:abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx.&lt;br /&gt;
This key is not known by any other names.&lt;br /&gt;
Are you sure you want to continue connecting (yes/no/[fingerprint])?&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This is the TOFU (Trust On First Use) moment.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
SSH is asking you to verify the server&amp;#039;s identity. In production, you would:&lt;br /&gt;
&lt;br /&gt;
# Check the server&amp;#039;s host key fingerprint: &amp;lt;code&amp;gt;ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
# Compare the fingerprint displayed by SSH with the server&amp;#039;s actual fingerprint&lt;br /&gt;
# If they match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;. If they don&amp;#039;t match, &amp;#039;&amp;#039;&amp;#039;do not proceed&amp;#039;&amp;#039;&amp;#039; (possible MITM attack)&lt;br /&gt;
&lt;br /&gt;
For this lab, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Are you sure you want to continue connecting (yes/no/[fingerprint])? yes&lt;br /&gt;
Warning: Permanently added &amp;#039;localhost&amp;#039; (ED25519) to the list of known hosts.&lt;br /&gt;
sshtest@hostname:~$&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;You are now connected!&amp;#039;&amp;#039;&amp;#039; The connection succeeded using key-based authentication (no password prompt).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Explore the connection:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;whoami&lt;br /&gt;
pwd&lt;br /&gt;
hostname&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Exit the SSH session:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-understanding-known_hosts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Understanding known_hosts ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Examine the known_hosts file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ~/.ssh/known_hosts&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;localhost ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbc123def456ghi789jkl012mno345pqr678stu901vwx&amp;lt;/pre&amp;gt;&lt;br /&gt;
This file stores trusted host keys. Format: &amp;lt;code&amp;gt;hostname/ip algorithm public_key&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On subsequent connections, SSH verifies the server presents the same host key. If the key changes, SSH displays a warning.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Connect again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected behavior&amp;#039;&amp;#039;&amp;#039;: No host key prompt this time—immediate connection. SSH verified the host key matches the one in known_hosts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;exit&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-simulating-a-host-key-change&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Simulating a Host Key Change ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll deliberately change the server&amp;#039;s host key to see SSH&amp;#039;s security warning.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Regenerate the host key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo rm /etc/ssh/ssh_host_ed25519_key*&lt;br /&gt;
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N &amp;quot;&amp;quot;&lt;br /&gt;
sudo systemctl restart sshd&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Attempt to connect:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @&lt;br /&gt;
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&lt;br /&gt;
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!&lt;br /&gt;
Someone could be eavesdropping on you right now (man-in-the-middle attack)!&lt;br /&gt;
It is also possible that a host key has just been changed.&lt;br /&gt;
The fingerprint for the ED25519 key sent by the remote host is&lt;br /&gt;
SHA256:new_fingerprint_here.&lt;br /&gt;
Please contact your system administrator.&lt;br /&gt;
Add correct host key in /home/youruser/.ssh/known_hosts to get rid of this message.&lt;br /&gt;
Offending ED25519 key in /home/youruser/.ssh/known_hosts:1&lt;br /&gt;
  remove with:&lt;br /&gt;
  ssh-keygen -f &amp;amp;quot;/home/youruser/.ssh/known_hosts&amp;amp;quot; -R &amp;amp;quot;localhost&amp;amp;quot;&lt;br /&gt;
Host key for localhost has changed and you have requested strict checking.&lt;br /&gt;
Host key verification failed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH refuses to connect!&amp;#039;&amp;#039;&amp;#039; This is the security mechanism in action. SSH detected that the server&amp;#039;s host key changed, which could indicate:&lt;br /&gt;
&lt;br /&gt;
# Legitimate server reinstallation&lt;br /&gt;
# Man-in-the-middle attack&lt;br /&gt;
&lt;br /&gt;
SSH conservatively assumes the worst case and blocks the connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Remove the old host key:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh-keygen -f ~/.ssh/known_hosts -R &amp;quot;localhost&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# Host localhost found: line 1&lt;br /&gt;
/home/youruser/.ssh/known_hosts updated.&lt;br /&gt;
Original contents retained as /home/youruser/.ssh/known_hosts.old&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Reconnect (you&amp;#039;ll see the TOFU prompt again):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh $USER@localhost&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to trust the new host key.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable B ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# The TOFU prompt on first SSH connection&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/id_ed25519.pub&amp;lt;/code&amp;gt;&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/authorized_keys&amp;lt;/code&amp;gt;&lt;br /&gt;
# Contents of &amp;lt;code&amp;gt;~/.ssh/known_hosts&amp;lt;/code&amp;gt;&lt;br /&gt;
# The &amp;amp;quot;WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!&amp;amp;quot; message&lt;br /&gt;
# Successful SSH connection after resolving the host key change&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-http-services-and-reverse-proxy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: HTTP Services and Reverse Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective&amp;#039;&amp;#039;&amp;#039;: Build simple HTTP backend services in network namespaces and configure Caddy as a reverse proxy with path-based routing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-1-installing-caddy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 1: Installing Caddy ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Install Caddy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy curl&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Verify installation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;caddy version&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;v2.6.2&amp;lt;/code&amp;gt; or similar&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-2-creating-simple-backend-services&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 2: Creating Simple Backend Services ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll create basic file-serving HTTP servers in the Red and Blue namespaces.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Create content directories and files for Red service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create directory for Red service&lt;br /&gt;
mkdir -p ~/red-service&lt;br /&gt;
cd ~/red-service&lt;br /&gt;
&lt;br /&gt;
# Create an index file&lt;br /&gt;
cat &amp;gt; index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Red Service&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Red Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Hello from Red namespace!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;IP: 10.0.0.1 | Port: 8001&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
# Create an API response file&lt;br /&gt;
mkdir -p api&lt;br /&gt;
cat &amp;gt; api/data.json &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Red Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.1&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Start Red service in its namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start HTTP server in Red namespace (run in background with &amp;amp;)&lt;br /&gt;
sudo ip netns exec red python3 -m http.server 8001 --bind 10.0.0.1 --directory ~/red-service &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;Serving HTTP on 10.0.0.1 port 8001 (http://10.0.0.1:8001/) ...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Create content for Blue service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create directory for Blue service&lt;br /&gt;
mkdir -p ~/blue-service&lt;br /&gt;
cd ~/blue-service&lt;br /&gt;
&lt;br /&gt;
# Create an index file&lt;br /&gt;
cat &amp;gt; index.html &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Blue Service&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;Blue Service&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Hello from Blue namespace!&amp;lt;/p&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;IP: 10.0.0.2 | Port: 8002&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
# Create an API response file&lt;br /&gt;
mkdir -p api&lt;br /&gt;
cat &amp;gt; api/data.json &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Blue Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.2&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Start Blue service in its namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start HTTP server in Blue namespace&lt;br /&gt;
sudo ip netns exec blue python3 -m http.server 8002 --bind 10.0.0.2 --directory ~/blue-service &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Test the backend services directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test Red service&lt;br /&gt;
curl http://10.0.0.1:8001/&lt;br /&gt;
&lt;br /&gt;
# Test Blue service&lt;br /&gt;
curl http://10.0.0.2:8002/&lt;br /&gt;
&lt;br /&gt;
# Test JSON endpoints&lt;br /&gt;
curl http://10.0.0.1:8001/api/data.json&lt;br /&gt;
curl http://10.0.0.2:8002/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the HTML and JSON content from each service.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-3-configuring-caddy-reverse-proxy&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 3: Configuring Caddy Reverse Proxy ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Create a Caddyfile:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd ~&lt;br /&gt;
cat &amp;gt; Caddyfile &amp;lt;&amp;lt; &amp;#039;EOF&amp;#039;&lt;br /&gt;
# Caddy Reverse Proxy Configuration&lt;br /&gt;
&lt;br /&gt;
:8080 {&lt;br /&gt;
    # Health check endpoint&lt;br /&gt;
    handle /health {&lt;br /&gt;
        respond &amp;quot;OK - API Gateway Running&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Root path&lt;br /&gt;
    handle / {&lt;br /&gt;
        respond &amp;quot;API Gateway - Use /red/ or /blue/ paths&amp;quot; 200&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /red/* to Red service (strips /red prefix)&lt;br /&gt;
    handle_path /red/* {&lt;br /&gt;
        reverse_proxy 10.0.0.1:8001&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Route /blue/* to Blue service (strips /blue prefix)&lt;br /&gt;
    handle_path /blue/* {&lt;br /&gt;
        reverse_proxy 10.0.0.2:8002&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Enable logging&lt;br /&gt;
    log {&lt;br /&gt;
        output stdout&lt;br /&gt;
        format console&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Understanding the Configuration&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;:8080&amp;lt;/code&amp;gt; - Listen on port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;handle /health&amp;lt;/code&amp;gt; - Health check endpoint (doesn&amp;#039;t go to backends)&lt;br /&gt;
* &amp;lt;code&amp;gt;handle_path /red/*&amp;lt;/code&amp;gt; - Strips &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt; prefix before forwarding&lt;br /&gt;
** Client requests &amp;lt;code&amp;gt;/red/api/data.json&amp;lt;/code&amp;gt; → Backend receives &amp;lt;code&amp;gt;/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reverse_proxy 10.0.0.1:8001&amp;lt;/code&amp;gt; - Forward to Red service&lt;br /&gt;
* Similar logic for Blue service&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Start Caddy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;caddy run --config ~/Caddyfile&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;INFO    using config from file&lt;br /&gt;
INFO    serving initial configuration&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this terminal open to see request logs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-4-testing-path-based-routing&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 4: Testing Path-Based Routing ====&lt;br /&gt;
&lt;br /&gt;
Open a &amp;#039;&amp;#039;&amp;#039;new terminal&amp;#039;&amp;#039;&amp;#039; for testing.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1&amp;#039;&amp;#039;&amp;#039;: Test the root path:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;API Gateway - Use /red/ or /blue/ paths&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2&amp;#039;&amp;#039;&amp;#039;: Test health check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/health&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output: &amp;lt;code&amp;gt;OK - API Gateway Running&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3&amp;#039;&amp;#039;&amp;#039;: Test Red service through proxy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the Red service HTML page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/red/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Red Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;red&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.1&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4&amp;#039;&amp;#039;&amp;#039;: Test Blue service through proxy:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the Blue service HTML page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl http://localhost:8080/blue/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;{&lt;br /&gt;
  &amp;quot;service&amp;quot;: &amp;quot;Blue Service&amp;quot;,&lt;br /&gt;
  &amp;quot;namespace&amp;quot;: &amp;quot;blue&amp;quot;,&lt;br /&gt;
  &amp;quot;ip&amp;quot;: &amp;quot;10.0.0.2&amp;quot;,&lt;br /&gt;
  &amp;quot;status&amp;quot;: &amp;quot;running&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5&amp;#039;&amp;#039;&amp;#039;: Test with verbose output to see headers:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -v http://localhost:8080/red/api/data.json&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output (key parts):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;amp;gt; GET /red/api/data.json HTTP/1.1&lt;br /&gt;
&amp;amp;gt; Host: localhost:8080&lt;br /&gt;
...&lt;br /&gt;
&amp;amp;lt; HTTP/1.1 200 OK&lt;br /&gt;
&amp;amp;lt; Content-Type: application/json&lt;br /&gt;
&amp;amp;lt; Server: Caddy&lt;br /&gt;
...&lt;br /&gt;
{JSON response}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Observation&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Client requested &amp;lt;code&amp;gt;/red/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
* Caddy stripped &amp;lt;code&amp;gt;/red&amp;lt;/code&amp;gt; and forwarded &amp;lt;code&amp;gt;/api/data.json&amp;lt;/code&amp;gt; to backend&lt;br /&gt;
* Response comes back through Caddy (notice &amp;lt;code&amp;gt;Server: Caddy&amp;lt;/code&amp;gt; header)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-5-understanding-the-flow&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 5: Understanding the Flow ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Request Flow Diagram&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Request: http://localhost:8080/red/api/data.json&lt;br /&gt;
       |&lt;br /&gt;
       v&lt;br /&gt;
   Caddy (port 8080)&lt;br /&gt;
       |&lt;br /&gt;
       | Strips /red prefix&lt;br /&gt;
       | Path becomes: /api/data.json&lt;br /&gt;
       v&lt;br /&gt;
   Red Service (10.0.0.1:8001)&lt;br /&gt;
       |&lt;br /&gt;
       | Serves file: ~/red-service/api/data.json&lt;br /&gt;
       v&lt;br /&gt;
   Response back through Caddy&lt;br /&gt;
       |&lt;br /&gt;
       v&lt;br /&gt;
   Client receives JSON&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-6-cleanup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Part 6: Cleanup ====&lt;br /&gt;
&lt;br /&gt;
When finished testing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Stop Caddy (Ctrl+C in Caddy terminal)&lt;br /&gt;
&lt;br /&gt;
# Kill Python servers&lt;br /&gt;
pkill -f &amp;quot;python3 -m http.server 8001&amp;quot;&lt;br /&gt;
pkill -f &amp;quot;python3 -m http.server 8002&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Or kill all Python HTTP servers&lt;br /&gt;
sudo pkill -f &amp;quot;http.server&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Deliverable C ===&lt;br /&gt;
&lt;br /&gt;
Submit screenshots showing:&lt;br /&gt;
&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/&amp;lt;/code&amp;gt; (root path)&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/health&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/red/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl http://localhost:8080/blue/api/data.json&amp;lt;/code&amp;gt;&lt;br /&gt;
# Output of &amp;lt;code&amp;gt;curl -v http://localhost:8080/red/&amp;lt;/code&amp;gt; showing response headers&lt;br /&gt;
# Your complete Caddyfile contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
This section covers common issues you may encounter during the lab exercises.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== DNS Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: DNS queries fail or time out&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check DNS resolver configuration&lt;br /&gt;
cat /etc/resolv.conf&lt;br /&gt;
&lt;br /&gt;
# Test with different DNS servers&lt;br /&gt;
dig @8.8.8.8 google.com&lt;br /&gt;
dig @1.1.1.1 google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Verify network connectivity: &amp;lt;code&amp;gt;ping 8.8.8.8&amp;lt;/code&amp;gt;&lt;br /&gt;
* Check firewall rules: &amp;lt;code&amp;gt;sudo iptables -L -n | grep 53&amp;lt;/code&amp;gt;&lt;br /&gt;
* Try alternative DNS servers&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;connection timed out; no servers could be reached&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: No DNS resolver configured or resolver unreachable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Add Google DNS to resolv.conf&lt;br /&gt;
echo &amp;quot;nameserver 8.8.8.8&amp;quot; | sudo tee -a /etc/resolv.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ssh-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== SSH Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;Permission denied (publickey)&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check SSH with verbose output&lt;br /&gt;
ssh -v testuser@10.0.0.2&lt;br /&gt;
&lt;br /&gt;
# Verify key permissions&lt;br /&gt;
ls -la ~/.ssh/id_ed25519&lt;br /&gt;
&lt;br /&gt;
# Check authorized_keys on server&lt;br /&gt;
sudo ls -la /home/testuser/.ssh/authorized_keys&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Causes&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Wrong permissions on private key (must be 600)&lt;br /&gt;
# Wrong permissions on authorized_keys (must be 600)&lt;br /&gt;
# Wrong ownership of .ssh directory&lt;br /&gt;
# Public key not in authorized_keys&lt;br /&gt;
# Private key not loaded&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Fix permissions&lt;br /&gt;
chmod 600 ~/.ssh/id_ed25519&lt;br /&gt;
chmod 600 ~/.ssh/authorized_keys&lt;br /&gt;
chmod 700 ~/.ssh&lt;br /&gt;
&lt;br /&gt;
# Verify key is loaded&lt;br /&gt;
ssh-add -l&lt;br /&gt;
&lt;br /&gt;
# Add key if not loaded&lt;br /&gt;
ssh-add ~/.ssh/id_ed25519&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: &amp;amp;quot;WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: Server&amp;#039;s host key changed (legitimate reinstall or potential MITM attack)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039; (if change is legitimate):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Remove old host key&lt;br /&gt;
ssh-keygen -R 10.0.0.2&lt;br /&gt;
&lt;br /&gt;
# Reconnect and verify new fingerprint&lt;br /&gt;
ssh testuser@10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: SSH connection hangs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check if SSH server is running&lt;br /&gt;
sudo ss -tlnp | grep :22&lt;br /&gt;
&lt;br /&gt;
# Test connectivity&lt;br /&gt;
nc -zv 10.0.0.2 22&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Restart SSH server: &amp;lt;code&amp;gt;sudo systemctl restart sshd&amp;lt;/code&amp;gt;&lt;br /&gt;
* Check firewall rules&lt;br /&gt;
* Verify network namespace connectivity&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;httpcaddy-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== HTTP/Caddy Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Caddy fails to start&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Validate configuration&lt;br /&gt;
caddy validate --config /etc/caddy/Caddyfile&lt;br /&gt;
&lt;br /&gt;
# Check if port is already in use&lt;br /&gt;
sudo ss -tlnp | grep :8080&lt;br /&gt;
&lt;br /&gt;
# View Caddy logs&lt;br /&gt;
sudo journalctl -u caddy -n 50&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Common Causes&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
# Port already in use&lt;br /&gt;
# Syntax error in Caddyfile&lt;br /&gt;
# Permission issues&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Kill process using port 8080&lt;br /&gt;
sudo fuser -k 8080/tcp&lt;br /&gt;
&lt;br /&gt;
# Fix Caddyfile syntax&lt;br /&gt;
caddy fmt --overwrite /etc/caddy/Caddyfile&lt;br /&gt;
&lt;br /&gt;
# Run Caddy with elevated privileges if needed&lt;br /&gt;
sudo caddy run --config /etc/caddy/Caddyfile&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Reverse proxy returns &amp;amp;quot;502 Bad Gateway&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: Backend service not running or unreachable&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test backend directly&lt;br /&gt;
curl http://10.0.0.1:8001&lt;br /&gt;
curl http://10.0.0.2:8002&lt;br /&gt;
&lt;br /&gt;
# Check if backend processes are running&lt;br /&gt;
sudo ip netns exec red ps aux | grep python&lt;br /&gt;
sudo ip netns exec blue ps aux | grep python&lt;br /&gt;
&lt;br /&gt;
# Check connectivity from host to namespace&lt;br /&gt;
ping -c 2 10.0.0.1&lt;br /&gt;
ping -c 2 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Restart backend services&lt;br /&gt;
* Verify namespace network configuration&lt;br /&gt;
* Check backend logs for errors&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Path routing not working correctly&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test with verbose curl&lt;br /&gt;
curl -v http://localhost:8080/red/&lt;br /&gt;
&lt;br /&gt;
# Check Caddy access logs&lt;br /&gt;
sudo tail -f /var/log/caddy/access.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;: Verify Caddyfile syntax, especially &amp;lt;code&amp;gt;handle_path&amp;lt;/code&amp;gt; directives. Ensure path prefixes match exactly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-namespace-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Namespace Issues ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Cannot ping between namespaces&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Diagnostics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check interface status&lt;br /&gt;
sudo ip netns exec red ip link show&lt;br /&gt;
sudo ip netns exec blue ip link show&lt;br /&gt;
&lt;br /&gt;
# Check IP addresses&lt;br /&gt;
sudo ip netns exec red ip addr show&lt;br /&gt;
sudo ip netns exec blue ip addr show&lt;br /&gt;
&lt;br /&gt;
# Check routing&lt;br /&gt;
sudo ip netns exec red ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solutions&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Recreate namespace configuration from Lab 8&lt;br /&gt;
* Verify veth pairs are connected&lt;br /&gt;
* Check bridge configuration&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Problem&amp;#039;&amp;#039;&amp;#039;: Services in namespace cannot access internet&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Cause&amp;#039;&amp;#039;&amp;#039;: No default gateway or NAT configured&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Solution&amp;#039;&amp;#039;&amp;#039;: Configure NAT on host (if needed for external access):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enable IP forwarding&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Add NAT rule&lt;br /&gt;
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-protocol-information&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Protocol Information ==&lt;br /&gt;
&lt;br /&gt;
This appendix provides information about other application protocols you may encounter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;email-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Email Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SMTP (Simple Mail Transfer Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 25 (unencrypted), 587 (submission with STARTTLS), 465 (SMTPS)&lt;br /&gt;
* Purpose: Sending email between servers&lt;br /&gt;
* Client-to-server: Port 587&lt;br /&gt;
* Server-to-server: Port 25&lt;br /&gt;
* Note: Port 25 often blocked by ISPs to prevent spam&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;IMAP (Internet Message Access Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 143 (unencrypted), 993 (IMAPS)&lt;br /&gt;
* Purpose: Reading email with server synchronization&lt;br /&gt;
* Advantages: Emails stay on server, accessible from multiple devices&lt;br /&gt;
* Modern replacement for POP3&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;POP3 (Post Office Protocol v3)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 110 (unencrypted), 995 (POP3S)&lt;br /&gt;
* Purpose: Downloading email to client&lt;br /&gt;
* Downloads emails and typically deletes from server&lt;br /&gt;
* Legacy protocol, IMAP preferred today&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;database-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Database Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MySQL&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 3306&lt;br /&gt;
* Protocol: MySQL wire protocol (proprietary)&lt;br /&gt;
* Common tools: mysql client, MySQL Workbench&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PostgreSQL&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 5432&lt;br /&gt;
* Protocol: PostgreSQL wire protocol&lt;br /&gt;
* Common tools: psql client, pgAdmin&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;MongoDB&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 27017&lt;br /&gt;
* Protocol: MongoDB wire protocol&lt;br /&gt;
* NoSQL document database&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Redis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 6379&lt;br /&gt;
* Protocol: RESP (Redis Serialization Protocol)&lt;br /&gt;
* In-memory data structure store&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;other-common-protocols&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Other Common Protocols ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;FTP (File Transfer Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 21 (control), 20 (data)&lt;br /&gt;
* Legacy file transfer protocol&lt;br /&gt;
* Security issues: Transmits credentials in cleartext&lt;br /&gt;
* Modern alternatives: SFTP (SSH File Transfer Protocol), FTPS (FTP over SSL/TLS)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;LDAP (Lightweight Directory Access Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 389 (unencrypted), 636 (LDAPS)&lt;br /&gt;
* Purpose: Directory services (user authentication, organizational data)&lt;br /&gt;
* Common in enterprises for centralized authentication&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;RDP (Remote Desktop Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 3389&lt;br /&gt;
* Purpose: Remote desktop access (Windows)&lt;br /&gt;
* Proprietary Microsoft protocol&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;VNC (Virtual Network Computing)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 5900+ (5900, 5901, etc.)&lt;br /&gt;
* Purpose: Remote desktop access (cross-platform)&lt;br /&gt;
* Open protocol, multiple implementations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;NTP (Network Time Protocol)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 123&lt;br /&gt;
* Purpose: Clock synchronization&lt;br /&gt;
* Critical for distributed systems, logging, security&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Syslog&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Port: 514 (UDP/TCP)&lt;br /&gt;
* Purpose: Logging infrastructure&lt;br /&gt;
* Centralized log collection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ports-to-avoid&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Ports to Avoid ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Insecure Legacy Protocols&amp;#039;&amp;#039;&amp;#039; (never use in production):&lt;br /&gt;
&lt;br /&gt;
* Port 21: FTP (use SFTP or FTPS instead)&lt;br /&gt;
* Port 23: Telnet (use SSH instead)&lt;br /&gt;
* Port 69: TFTP (use SFTP or SCP instead)&lt;br /&gt;
* Port 110: POP3 unencrypted (use POP3S or IMAP)&lt;br /&gt;
* Port 143: IMAP unencrypted (use IMAPS)&lt;br /&gt;
* Port 389: LDAP unencrypted (use LDAPS)&lt;br /&gt;
* Port 445: SMB (often exploited, use VPN if needed)&lt;br /&gt;
* Port 512-514: rlogin, rsh, rexec (use SSH instead)&lt;br /&gt;
&lt;br /&gt;
These protocols transmit data (including credentials) in cleartext and/or have known security vulnerabilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all required elements. Organize clearly with section headers matching exercise labels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the dominant application layer protocols that form the foundation of modern internet services. You now understand how DNS enables service discovery, SSH provides secure remote access, and HTTP(S) serves as the universal protocol for APIs and web services.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS Deep Dives&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Setting up authoritative DNS with BIND9 or PowerDNS&lt;br /&gt;
* DNS security: DNSSEC for cryptographic verification of DNS responses&lt;br /&gt;
* DNS over HTTPS (DoH) and DNS over TLS (DoT) for encrypted DNS queries&lt;br /&gt;
* Dynamic DNS (DDNS) and service discovery patterns&lt;br /&gt;
* Split-horizon DNS for internal vs. external name resolution&lt;br /&gt;
* DNS-based load balancing and geographic routing (GeoDNS)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH Advanced Topics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* SSH tunneling: Local forwarding (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;), remote forwarding (&amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;), dynamic forwarding (&amp;lt;code&amp;gt;-D&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ProxyJump (&amp;lt;code&amp;gt;-J&amp;lt;/code&amp;gt;) for accessing hosts through bastion servers&lt;br /&gt;
* SSH certificates (different from host keys) for scalable authentication&lt;br /&gt;
* SSH agent forwarding security considerations&lt;br /&gt;
* SSH config files (&amp;lt;code&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;) for managing multiple hosts&lt;br /&gt;
* SSH as SOCKS proxy for secure browsing&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS Evolution&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* HTTP/2 improvements: multiplexing, server push, header compression, binary protocol&lt;br /&gt;
* HTTP/3 and QUIC: UDP-based transport with built-in encryption&lt;br /&gt;
* WebSockets for real-time bidirectional communication&lt;br /&gt;
* Server-Sent Events (SSE) for server-push updates&lt;br /&gt;
* gRPC for efficient RPC over HTTP/2&lt;br /&gt;
* GraphQL as alternative to REST for flexible API queries&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Web Server Configuration&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Nginx advanced reverse proxy patterns&lt;br /&gt;
* HAProxy for high-performance load balancing&lt;br /&gt;
* Load balancing algorithms: round-robin, least connections, IP hash, consistent hashing&lt;br /&gt;
* SSL/TLS termination best practices&lt;br /&gt;
* Certificate management with Let&amp;#039;s Encrypt&lt;br /&gt;
* HTTP caching: proxy caching, CDN integration, cache invalidation strategies&lt;br /&gt;
* Security headers: HSTS, Content-Security-Policy, X-Frame-Options, CORS&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Modern Architecture Patterns&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Microservices architecture: benefits and challenges&lt;br /&gt;
* Service mesh: Istio, Linkerd, Consul Connect&lt;br /&gt;
* API gateways: Kong, Ambassador, Traefik, AWS API Gateway&lt;br /&gt;
* Kubernetes Ingress controllers and service routing&lt;br /&gt;
* Serverless architecture and function-as-a-service (FaaS)&lt;br /&gt;
* Event-driven architectures: message queues, pub/sub patterns&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Other Application Protocols&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Email protocols: SMTP for sending, IMAP for retrieval, SPF/DKIM/DMARC for authentication&lt;br /&gt;
* Database wire protocols: MySQL, PostgreSQL, MongoDB, Redis&lt;br /&gt;
* Message queue protocols: AMQP (RabbitMQ), Kafka protocol&lt;br /&gt;
* Monitoring and observability: Prometheus metrics, StatsD, OpenTelemetry, syslog&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man dig              # DNS query tool&lt;br /&gt;
man nslookup         # DNS lookup utility&lt;br /&gt;
man host             # DNS lookup utility&lt;br /&gt;
man ssh              # SSH client&lt;br /&gt;
man sshd             # SSH server daemon&lt;br /&gt;
man sshd_config      # SSH server configuration&lt;br /&gt;
man ssh_config       # SSH client configuration&lt;br /&gt;
man ssh-keygen       # SSH key generation and management&lt;br /&gt;
man ssh-add          # SSH agent key management&lt;br /&gt;
man curl             # HTTP client&lt;br /&gt;
man wget             # HTTP client alternative&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;DNS&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc1034 DNS RFC 1034] - Domain Names: Concepts and Facilities&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc1035 DNS RFC 1035] - Domain Names: Implementation and Specification&lt;br /&gt;
* [http://www.zytrax.com/books/dns/ DNS Zone File Guide] - Comprehensive DNS resource&lt;br /&gt;
* [https://developers.google.com/speed/public-dns Google Public DNS] - Documentation and best practices&lt;br /&gt;
* [https://www.cloudflare.com/learning/dns/what-is-dns/ Cloudflare 1.1.1.1] - DNS learning center&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;SSH&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc4251 SSH Protocol RFC 4251] - SSH Architecture&lt;br /&gt;
* [https://www.ssh.com/academy/ssh SSH.com Academy] - Comprehensive SSH tutorials&lt;br /&gt;
* [https://infosec.mozilla.org/guidelines/openssh Mozilla SSH Guidelines] - Security best practices&lt;br /&gt;
* [https://ed25519.cr.yp.to/ Ed25519 Benefits] - Information about Ed25519 cryptography&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;HTTP/HTTPS&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc7230 HTTP/1.1 RFC 7230-7235] - HTTP specification series&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc7540 HTTP/2 RFC 7540] - HTTP/2 specification&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9114 HTTP/3 RFC 9114] - HTTP/3 specification&lt;br /&gt;
* [https://developer.mozilla.org/en-US/docs/Web/HTTP MDN HTTP Documentation] - Comprehensive HTTP reference&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free book by Ilya Grigorik&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Web Servers and Reverse Proxies&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://caddyserver.com/docs/ Caddy Documentation] - Modern web server with automatic HTTPS&lt;br /&gt;
* [https://nginx.org/en/docs/ Nginx Documentation] - High-performance web server and reverse proxy&lt;br /&gt;
* [https://www.haproxy.org/documentation.html HAProxy Documentation] - Load balancer and proxy&lt;br /&gt;
* [https://doc.traefik.io/traefik/ Traefik Documentation] - Modern HTTP reverse proxy and load balancer&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* [https://owasp.org/www-project-top-ten/ OWASP Top 10] - Web application security risks&lt;br /&gt;
* [https://ssl-config.mozilla.org/ Mozilla SSL Configuration Generator] - TLS best practices&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt] - Free TLS certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of websites&lt;br /&gt;
&lt;br /&gt;
Practice Environments&lt;br /&gt;
&lt;br /&gt;
* [https://overthewire.org/wargames/bandit/ OverTheWire Bandit] - Linux command-line challenges including networking&lt;br /&gt;
* [https://www.hackthebox.com/ HackTheBox] - Penetration testing practice with networking components&lt;br /&gt;
* [https://tryhackme.com/ TryHackMe] - Guided cybersecurity learning including networking modules&lt;br /&gt;
* [https://www.katacoda.com/courses/kubernetes Kubernetes Playground] - Practice with modern orchestration&lt;br /&gt;
* [https://labs.play-with-docker.com/ Docker Playground] - Free Docker environment for testing&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8191</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8191"/>
		<updated>2025-12-05T15:09:50Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;br /&gt;
* Lab 8 - [[OS Lab 8 - Transport and Security]]&lt;br /&gt;
* Lab 9 - [[OS Lab 9 - Application Protocols: DNS, SSH, HTTP(S)]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8190</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8190"/>
		<updated>2025-11-28T14:49:22Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Practice: TLS-Secured Connection */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
* Understand the role of DNS (Domain Name System) and hostname resolution in network communication and certificate validation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
Additionally, we&amp;#039;ll introduce a crucial component of internet infrastructure: hostname resolution. While machines communicate using IP addresses, humans prefer memorable names like &amp;quot;google.com&amp;quot; or &amp;quot;github.com&amp;quot;. More importantly, security on the internet is built on these names, not IP addresses. When you visit a website with HTTPS, the server proves it controls a specific domain name (like &amp;quot;bank.com&amp;quot;), not just an IP address. In this lab, we&amp;#039;ll use simple hostname-to-IP mappings to understand this concept before diving deeper into DNS (Domain Name System) in the next lab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
# Each machine has both an IP address (10.0.0.x) and a hostname (x.lab). This mirrors how real internet servers work—they have IP addresses for routing, but we refer to them by domain names.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
# Configure host names&lt;br /&gt;
if ! grep -q &amp;quot;# Lab8 Configuration&amp;quot; /etc/hosts; then&lt;br /&gt;
    echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;# Lab8 Configuration&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.1        host.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.2        red.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.3        blue.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* Hostname entries map red.lab → 10.0.0.2, blue.lab → 10.0.0.3&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Not test connectivity using hostnames&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 blue.lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, attempt connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt blue.lab 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;blue.lab 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you were to attempt to connect to 10.0.0.3 instead of blue.lab, it would FAIL because of hostname mismatch.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8189</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8189"/>
		<updated>2025-11-28T14:41:06Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Environment Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
* Understand the role of DNS (Domain Name System) and hostname resolution in network communication and certificate validation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
Additionally, we&amp;#039;ll introduce a crucial component of internet infrastructure: hostname resolution. While machines communicate using IP addresses, humans prefer memorable names like &amp;quot;google.com&amp;quot; or &amp;quot;github.com&amp;quot;. More importantly, security on the internet is built on these names, not IP addresses. When you visit a website with HTTPS, the server proves it controls a specific domain name (like &amp;quot;bank.com&amp;quot;), not just an IP address. In this lab, we&amp;#039;ll use simple hostname-to-IP mappings to understand this concept before diving deeper into DNS (Domain Name System) in the next lab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
# Each machine has both an IP address (10.0.0.x) and a hostname (x.lab). This mirrors how real internet servers work—they have IP addresses for routing, but we refer to them by domain names.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
# Configure host names&lt;br /&gt;
if ! grep -q &amp;quot;# Lab8 Configuration&amp;quot; /etc/hosts; then&lt;br /&gt;
    echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;# Lab8 Configuration&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.1        host.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.2        red.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.3        blue.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* Hostname entries map red.lab → 10.0.0.2, blue.lab → 10.0.0.3&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Not test connectivity using hostnames&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 blue.lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8188</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8188"/>
		<updated>2025-11-28T14:38:57Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Environment Setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
* Understand the role of DNS (Domain Name System) and hostname resolution in network communication and certificate validation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
Additionally, we&amp;#039;ll introduce a crucial component of internet infrastructure: hostname resolution. While machines communicate using IP addresses, humans prefer memorable names like &amp;quot;google.com&amp;quot; or &amp;quot;github.com&amp;quot;. More importantly, security on the internet is built on these names, not IP addresses. When you visit a website with HTTPS, the server proves it controls a specific domain name (like &amp;quot;bank.com&amp;quot;), not just an IP address. In this lab, we&amp;#039;ll use simple hostname-to-IP mappings to understand this concept before diving deeper into DNS (Domain Name System) in the next lab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
# Each machine has both an IP address (10.0.0.x) and a hostname (x.lab). This mirrors how real internet servers work—they have IP addresses for routing, but we refer to them by domain names.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
# Configure host names&lt;br /&gt;
if ! grep -q &amp;quot;# Lab8 Configuration&amp;quot; /etc/hosts; then&lt;br /&gt;
    echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;# Lab8 Configuration&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.1        host.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.2        red.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
    echo &amp;quot;10.0.0.3        blue.lab&amp;quot; &amp;gt;&amp;gt; /etc/hosts&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* Hostname entries map red.lab → 10.0.0.2, blue.lab → 10.0.0.3&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8187</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8187"/>
		<updated>2025-11-28T14:32:45Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
* Understand the role of DNS (Domain Name System) and hostname resolution in network communication and certificate validation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
Additionally, we&amp;#039;ll introduce a crucial component of internet infrastructure: hostname resolution. While machines communicate using IP addresses, humans prefer memorable names like &amp;quot;google.com&amp;quot; or &amp;quot;github.com&amp;quot;. More importantly, security on the internet is built on these names, not IP addresses. When you visit a website with HTTPS, the server proves it controls a specific domain name (like &amp;quot;bank.com&amp;quot;), not just an IP address. In this lab, we&amp;#039;ll use simple hostname-to-IP mappings to understand this concept before diving deeper into DNS (Domain Name System) in the next lab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8186</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8186"/>
		<updated>2025-11-28T14:32:02Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Objectives */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
* Understand the role of DNS (Domain Name System) and hostname resolution in network communication and certificate validation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8185</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8185"/>
		<updated>2025-11-28T14:00:03Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8184</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8184"/>
		<updated>2025-11-28T13:26:22Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* TLS: Transport Layer Security */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                                                         (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                         Certificate&lt;br /&gt;
                                                         (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                                         ServerKeyExchange&lt;br /&gt;
                                                         ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                  Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot or text of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Brief Explanation&amp;#039;&amp;#039;&amp;#039;: Write 2-3 sentences explaining why you see no packets before the message (no handshake) and why this is characteristic of UDP&amp;#039;s connectionless nature.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 3-4 sentences explaining:&lt;br /&gt;
#* What the three-way handshake accomplishes&lt;br /&gt;
#* Why TCP needs this handshake but UDP doesn&amp;#039;t&lt;br /&gt;
#* What the sequence numbers in the handshake are used for&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 3-4 sentences explaining:&lt;br /&gt;
#* What a Certificate Authority does&lt;br /&gt;
#* Why we need both a private key (blue.key) and a certificate (blue.crt)&lt;br /&gt;
#* What the signature in the certificate proves&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot or text of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 4-5 sentences explaining:&lt;br /&gt;
#* Why the tcpdump output shows gibberish instead of your actual message&lt;br /&gt;
#* What role the certificate (blue.crt) plays in establishing trust&lt;br /&gt;
#* How TLS prevents a man-in-the-middle attacker from reading your data&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8183</id>
		<title>OS Lab 8 - Transport and Security</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_8_-_Transport_and_Security&amp;diff=8183"/>
		<updated>2025-11-28T13:24:46Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain the fundamental differences between Connectionless (UDP) and Connection...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the fundamental differences between Connectionless (UDP) and Connection-Oriented (TCP) transport protocols and their appropriate use cases.&lt;br /&gt;
* Understand the TCP state machine and the lifecycle of connections (LISTEN, SYN_SENT, ESTABLISHED, TIME_WAIT, etc.).&lt;br /&gt;
* Inspect active socket states and statistics using the &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) utility to diagnose connection issues.&lt;br /&gt;
* Perform network service discovery using &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; for port scanning.&lt;br /&gt;
* Implement a complete Public Key Infrastructure (PKI) by generating Certificate Authorities (CAs), private keys, and signed certificates using &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Secure network communications using TLS (Transport Layer Security) to provide confidentiality and authenticity.&lt;br /&gt;
* Verify encryption effectiveness by inspecting packets with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to demonstrate the difference between plaintext and encrypted traffic.&lt;br /&gt;
* Understand the trust model of PKI and certificate chains used in modern HTTPS and secure communications.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 7, we built the foundational &amp;amp;quot;plumbing&amp;amp;quot; of computer networks: network interfaces, IP addresses, routing tables, and bridges. These components solve the problem of connectivity: they allow one machine to find and reach another machine on a network or across the internet. We demonstrated how the kernel routes packets from source to destination based on IP addresses.&lt;br /&gt;
&lt;br /&gt;
However, there&amp;#039;s a critical distinction we haven&amp;#039;t addressed: machines don&amp;#039;t really communicate with machines. More precisely, &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039; communicate with &amp;#039;&amp;#039;&amp;#039;processes&amp;#039;&amp;#039;&amp;#039;. When you browse a website, it&amp;#039;s not just your computer talking to a server; it&amp;#039;s your browser process talking to a web server process. When you send an email, your mail client talks to a mail server process. The question becomes: how does a packet arriving at a destination machine know which process should receive it?&lt;br /&gt;
&lt;br /&gt;
This is where the Transport Layer comes into play. The transport layer solves several fundamental problems:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Process Addressing&amp;#039;&amp;#039;&amp;#039;: It introduces the concept of &amp;#039;&amp;#039;&amp;#039;ports&amp;#039;&amp;#039;&amp;#039; to identify specific applications or services (e.g., HTTP servers typically listen on port 80, SSH on port 22, DNS on port 53).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Data Transfer Semantics&amp;#039;&amp;#039;&amp;#039;: It defines &amp;#039;&amp;#039;&amp;#039;how&amp;#039;&amp;#039;&amp;#039; data should be transferred: reliably with error checking and retransmission (TCP) or quickly with minimal overhead (UDP).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Flow Control and Congestion Management&amp;#039;&amp;#039;&amp;#039;: It prevents fast senders from overwhelming slow receivers and manages network congestion to prevent collapse.&lt;br /&gt;
&lt;br /&gt;
But there&amp;#039;s another critical problem lurking beneath the surface: &amp;#039;&amp;#039;&amp;#039;security&amp;#039;&amp;#039;&amp;#039;. The internet is fundamentally an untrusted network. Your packets traverse dozens of routers, switches, and network devices controlled by various organizations and potentially malicious actors. Any intermediate device can inspect, copy, or even modify your traffic. This is especially concerning when you&amp;#039;re transmitting sensitive data like passwords, credit card numbers, or private communications.&lt;br /&gt;
&lt;br /&gt;
In this lab, we move beyond basic connectivity to explore:&lt;br /&gt;
&lt;br /&gt;
* How processes establish communication channels using ports and sockets&lt;br /&gt;
* The trade-offs between UDP&amp;#039;s speed and TCP&amp;#039;s reliability&lt;br /&gt;
* How operating systems manage connection state&lt;br /&gt;
* Network reconnaissance techniques (port scanning) used by both administrators and attackers&lt;br /&gt;
* Cryptographic foundations of secure communications (public key infrastructure)&lt;br /&gt;
* How TLS (Transport Layer Security) protects data in transit, forming the foundation of modern secure internet protocols like HTTPS&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll not only establish connections between our virtual machines (Red and Blue namespaces) but also implement complete TLS encryption to prevent eavesdropping. By using &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to inspect packets &amp;amp;quot;on the wire,&amp;amp;quot; we&amp;#039;ll see firsthand the difference between plaintext and encrypted communications, demonstrating why TLS has become the universal standard for web traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of a Linux virtual machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;). You will need terminal access, either via SSH or directly through the VM console. Multiple terminal windows will be helpful for running servers, clients, and monitoring tools simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y nmap openssl tcpdump iproute2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool and security scanner. Includes &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;, an implementation of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; over sockets with SSL/TLS support.&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl&amp;lt;/code&amp;gt;: Cryptographic toolkit for SSL/TLS, certificate generation, and various cryptographic operations.&lt;br /&gt;
* &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;: Command-line packet analyzer for network traffic inspection.&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: Modern Linux networking utilities (provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; commands).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Network interfaces, IP addresses, and routing from Lab 7&lt;br /&gt;
* Bash scripting from Lab 5 (variables, loops, functions)&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process execution)&lt;br /&gt;
&lt;br /&gt;
You should also understand:&lt;br /&gt;
&lt;br /&gt;
* What an IP address is and how packets are routed&lt;br /&gt;
* The concept of clients and servers&lt;br /&gt;
* Basic command-line text manipulation (grep, awk, cut)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-transport-layer-bridging-machines-and-processes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Transport Layer: Bridging Machines and Processes ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Problem: Port Numbers and Multiplexing&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Imagine your computer receives a packet from the internet. The IP header tells the kernel which machine the packet is for (destination IP), but once it arrives, how does the kernel know which of the potentially hundreds of running processes should receive this packet?&lt;br /&gt;
&lt;br /&gt;
The solution is &amp;#039;&amp;#039;&amp;#039;port numbers&amp;#039;&amp;#039;&amp;#039;. A port is a 16-bit unsigned integer (range 0-65535) that identifies a specific communication endpoint on a machine. When combined with an IP address, a port creates a unique socket address (IP:PORT) that identifies a specific process on a specific machine.&lt;br /&gt;
&lt;br /&gt;
Port numbers are divided into three ranges:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Well-Known Ports (0-1023)&amp;#039;&amp;#039;&amp;#039;: Reserved for standard services (HTTP=80, HTTPS=443, SSH=22, DNS=53, SMTP=25). On Linux systems, binding to these ports typically requires root privileges.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Registered Ports (1024-49151)&amp;#039;&amp;#039;&amp;#039;: Can be registered for specific services but are less rigidly controlled.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic/Private Ports (49152-65535)&amp;#039;&amp;#039;&amp;#039;: Used for ephemeral (temporary) client-side ports when making outbound connections.&lt;br /&gt;
&lt;br /&gt;
When a web browser connects to a web server, it might create a socket with local address &amp;lt;code&amp;gt;192.168.1.50:54321&amp;lt;/code&amp;gt; (client&amp;#039;s IP and a random ephemeral port) connecting to remote address &amp;lt;code&amp;gt;203.0.113.10:443&amp;lt;/code&amp;gt; (server&amp;#039;s IP and HTTPS port). The combination of (source IP, source port, destination IP, destination port, protocol) uniquely identifies a connection.&lt;br /&gt;
&lt;br /&gt;
The transport layer performs &amp;#039;&amp;#039;&amp;#039;multiplexing&amp;#039;&amp;#039;&amp;#039; (combining multiple data streams into one network connection on the sending side) and &amp;#039;&amp;#039;&amp;#039;demultiplexing&amp;#039;&amp;#039;&amp;#039; (separating the combined stream back into individual streams on the receiving side based on port numbers).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;udp-user-datagram-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== UDP: User Datagram Protocol ====&lt;br /&gt;
&lt;br /&gt;
UDP is the simpler of the two main transport protocols. Its philosophy is &amp;amp;quot;fire and forget.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connectionless&amp;#039;&amp;#039;&amp;#039;: No handshake or connection establishment. Just send packets (called &amp;amp;quot;datagrams&amp;amp;quot;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unreliable&amp;#039;&amp;#039;&amp;#039;: No delivery guarantees. Packets may arrive out of order, be duplicated, or be lost entirely.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No State&amp;#039;&amp;#039;&amp;#039;: The kernel doesn&amp;#039;t maintain connection state. Each datagram is independent.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low Overhead&amp;#039;&amp;#039;&amp;#039;: Minimal header (8 bytes) compared to TCP&amp;#039;s 20+ bytes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fast&amp;#039;&amp;#039;&amp;#039;: No waiting for acknowledgments or retransmissions.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;UDP Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|            Length             |           Checksum            |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Just four fields: source port, destination port, length, and an optional checksum. Compare this to TCP&amp;#039;s much more complex header.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DNS Queries&amp;#039;&amp;#039;&amp;#039;: Fast lookups where a lost query can simply be retried.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real-time Streaming&amp;#039;&amp;#039;&amp;#039;: Video/audio where old data is useless (better to skip than wait).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gaming&amp;#039;&amp;#039;&amp;#039;: Low-latency updates where occasional packet loss is acceptable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IoT Sensors&amp;#039;&amp;#039;&amp;#039;: Simple periodic data updates where reliability is handled at application level.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Broadcast/Multicast&amp;#039;&amp;#039;&amp;#039;: Sending to multiple recipients simultaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;When UDP is NOT Used:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* File transfers&lt;br /&gt;
* Financial transactions&lt;br /&gt;
* Remote terminal sessions&lt;br /&gt;
* Email delivery&lt;br /&gt;
&lt;br /&gt;
UDP pushes reliability concerns to the application layer. Applications must implement their own acknowledgment, retransmission, and ordering mechanisms if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-transmission-control-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TCP: Transmission Control Protocol ====&lt;br /&gt;
&lt;br /&gt;
TCP is the workhorse of the internet. Most traffic you generate (web browsing, email, file transfers, SSH) uses TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Characteristics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection-Oriented&amp;#039;&amp;#039;&amp;#039;: Requires explicit connection establishment (handshake) and termination.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Reliable&amp;#039;&amp;#039;&amp;#039;: Guarantees that data arrives correctly and in order, using acknowledgments and retransmissions.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stateful&amp;#039;&amp;#039;&amp;#039;: The kernel maintains extensive state for each connection (sequence numbers, window sizes, timers).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Stream-Oriented&amp;#039;&amp;#039;&amp;#039;: Presents data as a continuous byte stream, not individual packets. Application-level message boundaries are not preserved.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flow Control&amp;#039;&amp;#039;&amp;#039;: Prevents fast senders from overwhelming slow receivers using sliding windows.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Congestion Control&amp;#039;&amp;#039;&amp;#039;: Detects network congestion and adjusts transmission rates to prevent network collapse.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Connection Lifecycle: The State Machine&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP connections go through a well-defined series of states. Understanding these states is crucial for troubleshooting network issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client Side:                          Server Side:&lt;br /&gt;
&lt;br /&gt;
CLOSED                                CLOSED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   (bind + listen)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                   LISTEN&lt;br /&gt;
  |                                      |&lt;br /&gt;
(connect)                                |&lt;br /&gt;
  |                                      |&lt;br /&gt;
SYN_SENT -------- SYN ----------------&amp;amp;gt; |&lt;br /&gt;
  |                                   SYN_RCVD&lt;br /&gt;
  | &amp;amp;lt;-------- SYN-ACK ----------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
ESTABLISHED ------ ACK ---------------&amp;amp;gt; ESTABLISHED&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(data transfer happens here)            |&lt;br /&gt;
  |                                      |&lt;br /&gt;
  |                                      |&lt;br /&gt;
(close)                                  |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_1 ------- FIN ---------------&amp;amp;gt; |&lt;br /&gt;
  |                                   CLOSE_WAIT&lt;br /&gt;
  | &amp;amp;lt;-------- ACK -------------------- |&lt;br /&gt;
  |                                      |&lt;br /&gt;
FIN_WAIT_2                            (close)&lt;br /&gt;
  |                                      |&lt;br /&gt;
  | &amp;amp;lt;-------- FIN -------------------- LAST_ACK&lt;br /&gt;
  |                                      |&lt;br /&gt;
TIME_WAIT ------- ACK ---------------&amp;amp;gt; CLOSED&lt;br /&gt;
  |&lt;br /&gt;
  | (wait 2*MSL)&lt;br /&gt;
  |&lt;br /&gt;
CLOSED&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Key States Explained:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSED&amp;#039;&amp;#039;&amp;#039;: No connection exists. This is the starting and ending state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039;: Server is waiting for incoming connection requests. Socket is bound to a port.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039;: Client has sent a SYN (synchronize) packet and is waiting for a response. This occurs immediately after calling &amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_RCVD&amp;#039;&amp;#039;&amp;#039;: Server has received a SYN and sent back SYN-ACK, waiting for the final ACK. Short-lived state.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039;: Connection is fully established and data can flow in both directions. This is where most time is spent.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039;: Active close initiated. Application called &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;, sent FIN, waiting for ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039;: Received ACK for our FIN, waiting for peer&amp;#039;s FIN.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;CLOSE_WAIT&amp;#039;&amp;#039;&amp;#039;: Received FIN from peer, waiting for application to call &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LAST_ACK&amp;#039;&amp;#039;&amp;#039;: Sent our FIN, waiting for final ACK.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039;: Both sides have closed, but socket remains in this state for 2*MSL (Maximum Segment Lifetime, typically 2-4 minutes) to ensure all packets have cleared the network. This prevents old duplicate packets from being interpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Three-Way Handshake:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Connection establishment (SYN → SYN-ACK → ACK) is called the &amp;amp;quot;three-way handshake&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client sends a segment with SYN flag set and an initial sequence number (ISN)&lt;br /&gt;
#* Client enters SYN_SENT state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Server → Client: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Server responds with SYN and ACK flags set&lt;br /&gt;
#* Server sends its own ISN and acknowledges client&amp;#039;s ISN+1&lt;br /&gt;
#* Server enters SYN_RCVD state&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Client → Server: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Client acknowledges server&amp;#039;s ISN+1&lt;br /&gt;
#* Both sides enter ESTABLISHED state&lt;br /&gt;
#* Data transfer can begin&lt;br /&gt;
&lt;br /&gt;
This handshake synchronizes sequence numbers on both sides, allowing reliable, ordered delivery.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Three-Way? Why Not Two?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A two-way handshake would be susceptible to old duplicate SYN packets causing false connections. The three-way handshake ensures both sides agree on current sequence numbers before data transmission begins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Connection Termination (Four-Way Handshake):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Either side can initiate closure:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: FIN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: ACK&amp;#039;&amp;#039;&amp;#039; (acknowledges FIN)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Passive Closer → Active Closer: FIN&amp;#039;&amp;#039;&amp;#039; (when application closes)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Active Closer → Passive Closer: ACK&amp;#039;&amp;#039;&amp;#039; (final acknowledgment)&lt;br /&gt;
&lt;br /&gt;
Sometimes steps 2 and 3 are combined (FIN+ACK in one packet) for a three-segment close.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP Segment Header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
TCP headers are much more complex than UDP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; 0                   1                   2                   3&lt;br /&gt;
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|          Source Port          |       Destination Port        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                        Sequence Number                        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Acknowledgment Number                      |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|  Data |           |U|A|P|R|S|F|                               |&lt;br /&gt;
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |&lt;br /&gt;
|       |           |G|K|H|T|N|N|                               |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|           Checksum            |         Urgent Pointer        |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                    Options                    |    Padding    |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
|                             Data                              |&lt;br /&gt;
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sequence Number&amp;#039;&amp;#039;&amp;#039;: Position of this segment&amp;#039;s first byte in the stream&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Acknowledgment Number&amp;#039;&amp;#039;&amp;#039;: Next expected byte from peer&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Flags&amp;#039;&amp;#039;&amp;#039;: SYN, ACK, FIN, RST, PSH, URG control connection state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Window&amp;#039;&amp;#039;&amp;#039;: Available receive buffer space (flow control)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Checksum&amp;#039;&amp;#039;&amp;#039;: Error detection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-statistics-with-ss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Statistics with ss ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt; (socket statistics) command is the modern replacement for the legacy &amp;lt;code&amp;gt;netstat&amp;lt;/code&amp;gt; command. It&amp;#039;s faster and more feature-rich.&lt;br /&gt;
&lt;br /&gt;
Common usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ss -tuna  # TCP, UDP, numeric, all states&lt;br /&gt;
ss -tln   # TCP listening sockets with numeric ports&lt;br /&gt;
ss -tapn  # TCP all states, show process names&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Options:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: UDP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All states (listening and non-listening)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Don&amp;#039;t resolve service names (show port numbers)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p&amp;lt;/code&amp;gt;: Show process using socket&lt;br /&gt;
* &amp;lt;code&amp;gt;-e&amp;lt;/code&amp;gt;: Extended information&lt;br /&gt;
* &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Memory usage&lt;br /&gt;
&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State      Recv-Q Send-Q Local Address:Port    Peer Address:Port&lt;br /&gt;
LISTEN     0      128    0.0.0.0:22            0.0.0.0:*&lt;br /&gt;
ESTAB      0      0      10.0.0.2:45678        10.0.0.3:8080&lt;br /&gt;
TIME-WAIT  0      0      10.0.0.2:45680        10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Understanding the fields:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: Current TCP state&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in receive queue (not yet read by application)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: Bytes in send queue (not yet acknowledged by peer)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: This machine&amp;#039;s socket address&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: Remote machine&amp;#039;s socket address&lt;br /&gt;
&lt;br /&gt;
Non-zero Recv-Q might indicate the application is slow to read data. Non-zero Send-Q might indicate network congestion or a slow receiver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-security-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Security Fundamentals ===&lt;br /&gt;
&lt;br /&gt;
The Threat Model: Man-in-the-Middle (MITM)&lt;br /&gt;
&lt;br /&gt;
Consider the network topology from Lab 7:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Red NS] ←→ [Bridge] ←→ [Blue NS]&lt;br /&gt;
              ↑&lt;br /&gt;
           [Host]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The host has interfaces connected to the bridge and can see all traffic between Red and Blue. In a real network, this &amp;amp;quot;middle position&amp;amp;quot; might be occupied by:&lt;br /&gt;
&lt;br /&gt;
* Your ISP&amp;#039;s routers&lt;br /&gt;
* Corporate proxy servers&lt;br /&gt;
* Public WiFi access points&lt;br /&gt;
* Government surveillance equipment&lt;br /&gt;
* Malicious actors who&amp;#039;ve compromised network infrastructure&lt;br /&gt;
&lt;br /&gt;
A Man-in-the-Middle attacker can:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Eavesdrop&amp;#039;&amp;#039;&amp;#039;: Read all plaintext data (passwords, messages, documents)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Modify&amp;#039;&amp;#039;&amp;#039;: Alter data in transit (change bank account numbers, inject malware)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Impersonate&amp;#039;&amp;#039;&amp;#039;: Pretend to be one side of the communication&lt;br /&gt;
&lt;br /&gt;
This is why encryption is essential for any sensitive communication.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cryptography-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Cryptography Basics ====&lt;br /&gt;
&lt;br /&gt;
Modern secure communications rely on a combination of &amp;#039;&amp;#039;&amp;#039;symmetric&amp;#039;&amp;#039;&amp;#039; and &amp;#039;&amp;#039;&amp;#039;asymmetric&amp;#039;&amp;#039;&amp;#039; cryptography.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Symmetric Encryption (Secret Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Same key used for encryption and decryption&lt;br /&gt;
* Fast and efficient&lt;br /&gt;
* Problem: How do you securely share the key?&lt;br /&gt;
* Examples: AES, ChaCha20&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Asymmetric Encryption (Public Key Cryptography):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Key pair: public key (can be shared) and private key (must be kept secret)&lt;br /&gt;
* Data encrypted with public key can only be decrypted with private key&lt;br /&gt;
* Slow compared to symmetric encryption&lt;br /&gt;
* Solves key distribution problem&lt;br /&gt;
* Examples: RSA, Elliptic Curve (ECDSA, Ed25519)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Digital Signatures:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Used to verify authenticity and integrity&lt;br /&gt;
* Sender creates a signature using their private key&lt;br /&gt;
* Receiver verifies using sender&amp;#039;s public key&lt;br /&gt;
* Proves the sender owns the private key and data hasn&amp;#039;t been tampered with&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hash Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* One-way functions that produce fixed-size output (digest) from arbitrary input&lt;br /&gt;
* Same input always produces same output&lt;br /&gt;
* Computationally infeasible to find two inputs with same output (collision resistance)&lt;br /&gt;
* Examples: SHA-256, SHA-3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tls-transport-layer-security&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== TLS: Transport Layer Security ====&lt;br /&gt;
&lt;br /&gt;
TLS (formerly SSL) is the protocol that secures most internet traffic today. It provides:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Confidentiality&amp;#039;&amp;#039;&amp;#039;: Data is encrypted so eavesdroppers see only gibberish&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrity&amp;#039;&amp;#039;&amp;#039;: Data cannot be modified without detection&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Verify you&amp;#039;re talking to the correct server (via certificates)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TLS Handshake (Simplified):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Client                                               Server&lt;br /&gt;
&lt;br /&gt;
ClientHello ----------------------------------------→&lt;br /&gt;
(supported ciphers, TLS versions, random nonce)&lt;br /&gt;
&lt;br /&gt;
                                 ←---------------------- ServerHello&lt;br /&gt;
                       (chosen cipher, TLS version, random nonce)&lt;br /&gt;
                                                    Certificate&lt;br /&gt;
                                          (server&amp;#039;s public key + CA signature)&lt;br /&gt;
                                              ServerKeyExchange&lt;br /&gt;
                                                  ServerHelloDone&lt;br /&gt;
&lt;br /&gt;
ClientKeyExchange ------------------------------------→&lt;br /&gt;
(pre-master secret encrypted with server&amp;#039;s public key)&lt;br /&gt;
ChangeCipherSpec&lt;br /&gt;
Finished&lt;br /&gt;
&lt;br /&gt;
                                 ←--------------- ChangeCipherSpec&lt;br /&gt;
                                                        Finished&lt;br /&gt;
&lt;br /&gt;
[Encrypted Application Data] ←----------------→ [Encrypted Application Data]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key steps:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Negotiation&amp;#039;&amp;#039;&amp;#039;: Agree on TLS version and cipher suite&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Authentication&amp;#039;&amp;#039;&amp;#039;: Server presents certificate (sometimes client does too)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Key Exchange&amp;#039;&amp;#039;&amp;#039;: Establish shared encryption keys using asymmetric crypto&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encryption&amp;#039;&amp;#039;&amp;#039;: Switch to symmetric encryption for data transfer&lt;br /&gt;
&lt;br /&gt;
In order to minimize overhead, the asymmetric crypto is only used to establish a session key. Then fast symmetric encryption is used for the actual data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why Both?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Asymmetric encryption solves the key distribution problem&lt;br /&gt;
* Symmetric encryption provides fast data encryption&lt;br /&gt;
* Together they provide security and performance&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;public-key-infrastructure-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Public Key Infrastructure (PKI) ====&lt;br /&gt;
&lt;br /&gt;
PKI is the system of trust that underlies secure internet communications.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Authority (CA)&amp;#039;&amp;#039;&amp;#039;: A trusted third party that signs certificates. Major CAs include Let&amp;#039;s Encrypt, DigiCert, GlobalSign. Your operating system and browser come with a pre-installed list of trusted root CAs.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate&amp;#039;&amp;#039;&amp;#039;: A document binding a public key to an identity (domain name, organization). Contains:&lt;br /&gt;
#* Subject (who the certificate is for)&lt;br /&gt;
#* Issuer (which CA signed it)&lt;br /&gt;
#* Public key&lt;br /&gt;
#* Validity period (not before / not after dates)&lt;br /&gt;
#* Digital signature (from CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Private Key&amp;#039;&amp;#039;&amp;#039;: Kept secret by the certificate owner. Used to prove ownership and establish secure connections.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;: A request to a CA saying &amp;amp;quot;Please sign a certificate for my public key and domain.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Trust Chain:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Root CA (self-signed, in browser&amp;#039;s trust store)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
Intermediate CA (signed by Root CA)&lt;br /&gt;
   ↓ signs&lt;br /&gt;
End-Entity Certificate (your server, signed by Intermediate CA)&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you connect to &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
# Server sends its certificate&lt;br /&gt;
# Your browser checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Browser walks the chain: End-entity ← Intermediate ← Root&lt;br /&gt;
# If Root CA is in browser&amp;#039;s trust store, certificate is trusted&lt;br /&gt;
# Browser verifies certificate is for the correct domain (example.com)&lt;br /&gt;
# Browser verifies certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, secure connection established&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Self-Signed Certificates:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In this lab, we create self-signed certificates (the CA signs its own certificate). This is fine for testing, but browsers will show warnings in production because the CA isn&amp;#039;t in their trust store. Real websites use certificates from trusted CAs.&lt;br /&gt;
&lt;br /&gt;
Port Scanning and Network Reconnaissance&lt;br /&gt;
&lt;br /&gt;
Port scanning is the process of probing a target system to discover which ports are open (have services listening). This is used by:&lt;br /&gt;
&lt;br /&gt;
* System administrators for inventory and auditing&lt;br /&gt;
* Security researchers for vulnerability assessment&lt;br /&gt;
* Attackers for reconnaissance (first step in many attacks)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;nmap Basics:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nmap -p 22 10.0.0.3          # Scan port 22 on specific IP&lt;br /&gt;
nmap -p 1-1000 10.0.0.3      # Scan ports 1-1000&lt;br /&gt;
nmap -p- 10.0.0.3            # Scan all 65535 ports&lt;br /&gt;
nmap 10.0.0.0/24             # Scan all hosts in subnet&lt;br /&gt;
nmap -sV 10.0.0.3            # Service version detection&lt;br /&gt;
nmap -O 10.0.0.3             # OS fingerprinting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Scan Types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP Connect Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sT&amp;lt;/code&amp;gt;): Completes full three-way handshake. Most reliable but also most detectable.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SYN Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sS&amp;lt;/code&amp;gt;, default for root): Sends SYN, waits for SYN-ACK, then sends RST instead of ACK. &amp;amp;quot;Half-open&amp;amp;quot; scan, stealthier.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP Scan&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;-sU&amp;lt;/code&amp;gt;): Slower, less reliable, but necessary for UDP services.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Port States:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Open&amp;#039;&amp;#039;&amp;#039;: Service actively accepting connections&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Closed&amp;#039;&amp;#039;&amp;#039;: No service, but port is reachable (responds with RST)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Filtered&amp;#039;&amp;#039;&amp;#039;: Firewall or filter blocking access (no response or ICMP unreachable)&lt;br /&gt;
&lt;br /&gt;
Only scan systems you own or have explicit permission to scan. Unauthorized scanning may violate computer crime laws.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-future-quic-and-http3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== The Future: QUIC and HTTP/3 ===&lt;br /&gt;
&lt;br /&gt;
TCP has served the internet well since the 1970s, but it has limitations that become apparent in modern, high-latency networks.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;s Problems:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Head-of-Line Blocking&amp;#039;&amp;#039;&amp;#039;: TCP provides a single ordered byte stream. If one packet is lost, all subsequent packets (even for unrelated data) must wait for retransmission. In HTTP/2, a single lost packet blocks all simultaneous downloads.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Handshake Latency&amp;#039;&amp;#039;&amp;#039;: TCP three-way handshake + TLS handshake requires 2-3 round trips before data transfer begins. On high-latency connections (satellite, mobile), this adds seconds of delay.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ossification&amp;#039;&amp;#039;&amp;#039;: TCP is implemented in operating system kernels. Deploying new features (like improved congestion control) requires OS updates on billions of devices—essentially impossible.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;QUIC (Quick UDP Internet Connections):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
QUIC is a new transport protocol developed by Google, now standardized as RFC 9000. It&amp;#039;s the foundation of HTTP/3.&lt;br /&gt;
&lt;br /&gt;
Key innovations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Built on UDP&amp;#039;&amp;#039;&amp;#039;: Implemented in userspace, not kernel. Fast iteration and deployment.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Integrated TLS 1.3&amp;#039;&amp;#039;&amp;#039;: Encryption is mandatory and built-in, not layered on top.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multiple Streams&amp;#039;&amp;#039;&amp;#039;: Supports many independent streams in one connection. Loss in one stream doesn&amp;#039;t block others.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;0-RTT Connection Resumption&amp;#039;&amp;#039;&amp;#039;: Returning clients can send data in the first packet (zero round trips).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection Migration&amp;#039;&amp;#039;&amp;#039;: Connection survives IP address changes (e.g., switching from WiFi to cellular).&lt;br /&gt;
&lt;br /&gt;
QUIC implements reliability, congestion control, and flow control in userspace, giving the protocol designers much more flexibility than kernel-based TCP.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Adoption:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
As of 2024, QUIC/HTTP/3 is used by:&lt;br /&gt;
&lt;br /&gt;
* Google services (Search, YouTube, Gmail)&lt;br /&gt;
* Facebook/Meta&lt;br /&gt;
* Cloudflare&lt;br /&gt;
* Major CDNs&lt;br /&gt;
&lt;br /&gt;
Most modern browsers support HTTP/3. It&amp;#039;s particularly beneficial for mobile users and high-latency scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Environment Setup ==&lt;br /&gt;
&lt;br /&gt;
We need the topology from Lab 7 (Red, Blue, and a Bridge connecting them). To ensure a clean, consistent environment, we&amp;#039;ll use a setup script.&lt;br /&gt;
&lt;br /&gt;
Understanding the Setup Script&lt;br /&gt;
&lt;br /&gt;
The script creates the following topology:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Host: 10.0.0.1]&lt;br /&gt;
               |&lt;br /&gt;
               | (br-lab bridge)&lt;br /&gt;
               |&lt;br /&gt;
       +-------+-------+&lt;br /&gt;
       |               |&lt;br /&gt;
  [Red: 10.0.0.2] [Blue: 10.0.0.3]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key operations:&lt;br /&gt;
&lt;br /&gt;
# Clean up any existing namespaces and bridges from previous labs&lt;br /&gt;
# Create a Linux bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acting as a virtual switch&lt;br /&gt;
# Create two network namespaces (&amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Create veth pairs connecting each namespace to the bridge&lt;br /&gt;
# Assign IP addresses and routes&lt;br /&gt;
# Enable NAT for internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-create-the-setup-script&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 1: Create the Setup Script ===&lt;br /&gt;
&lt;br /&gt;
Create &amp;lt;code&amp;gt;lab8_setup.sh&amp;lt;/code&amp;gt; with the following content:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
if [ &amp;quot;$EUID&amp;quot; -ne 0 ]; then &lt;br /&gt;
    echo &amp;quot;Please run as root (use sudo)&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Cleaning up old environment...&amp;quot;&lt;br /&gt;
ip netns delete red 2&amp;gt;/dev/null || true&lt;br /&gt;
ip netns delete blue 2&amp;gt;/dev/null || true&lt;br /&gt;
ip link delete br-lab 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Bridge...&amp;quot;&lt;br /&gt;
ip link add br-lab type bridge&lt;br /&gt;
ip link set br-lab up&lt;br /&gt;
ip addr add 10.0.0.1/24 dev br-lab&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Creating Namespaces...&amp;quot;&lt;br /&gt;
ip netns add red&lt;br /&gt;
ip netns add blue&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Red...&amp;quot;&lt;br /&gt;
ip link add veth-red type veth peer name veth-red-br&lt;br /&gt;
ip link set veth-red-br master br-lab&lt;br /&gt;
ip link set veth-red-br up&lt;br /&gt;
ip link set veth-red netns red&lt;br /&gt;
ip netns exec red ip addr add 10.0.0.2/24 dev veth-red&lt;br /&gt;
ip netns exec red ip link set veth-red up&lt;br /&gt;
ip netns exec red ip link set lo up&lt;br /&gt;
ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Wiring Blue...&amp;quot;&lt;br /&gt;
ip link add veth-blue type veth peer name veth-blue-br&lt;br /&gt;
ip link set veth-blue-br master br-lab&lt;br /&gt;
ip link set veth-blue-br up&lt;br /&gt;
ip link set veth-blue netns blue&lt;br /&gt;
ip netns exec blue ip addr add 10.0.0.3/24 dev veth-blue&lt;br /&gt;
ip netns exec blue ip link set veth-blue up&lt;br /&gt;
ip netns exec blue ip link set lo up&lt;br /&gt;
ip netns exec blue ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;[*] Enabling NAT on Host...&amp;quot;&lt;br /&gt;
sysctl -w net.ipv4.ip_forward=1 &amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Determine internet-facing interface&lt;br /&gt;
IFACE=$(ip route get 8.8.8.8 | grep -oP &amp;#039;dev \K\S+&amp;#039;)&lt;br /&gt;
echo &amp;quot;[*] Detected internet interface: $IFACE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Configure NAT (idempotent - won&amp;#039;t fail if already exists)&lt;br /&gt;
nft add table ip nat 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; } 2&amp;gt;/dev/null || true&lt;br /&gt;
nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;$IFACE&amp;quot; masquerade 2&amp;gt;/dev/null || true&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;gt;&amp;gt;&amp;gt; Setup Complete&amp;quot;&lt;br /&gt;
echo &amp;quot;    Red:  10.0.0.2&amp;quot;&lt;br /&gt;
echo &amp;quot;    Blue: 10.0.0.3&amp;quot;&lt;br /&gt;
echo &amp;quot;    Host: 10.0.0.1&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script Explanation:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;: Exit immediately if any command fails&lt;br /&gt;
* &amp;lt;code&amp;gt;[ &amp;amp;quot;$EUID&amp;amp;quot; -ne 0 ]&amp;lt;/code&amp;gt;: Check if running as root (EUID = Effective User ID)&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt;/dev/null || true&amp;lt;/code&amp;gt;: Suppress error messages and don&amp;#039;t fail if cleanup targets don&amp;#039;t exist&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route get 8.8.8.8&amp;lt;/code&amp;gt;: A way to determine which interface routes to the internet&lt;br /&gt;
* &amp;lt;code&amp;gt;grep -oP &amp;#039;dev \K\S+&amp;#039;&amp;lt;/code&amp;gt;: Extract just the interface name&lt;br /&gt;
* NAT commands use &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; to be idempotent (can run multiple times safely)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-make-the-script-executable-and-run-it&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 2: Make the Script Executable and Run It ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab8_setup.sh&lt;br /&gt;
sudo ./lab8_setup.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[*] Cleaning up old environment...&lt;br /&gt;
[*] Creating Bridge...&lt;br /&gt;
[*] Creating Namespaces...&lt;br /&gt;
[*] Wiring Red...&lt;br /&gt;
[*] Wiring Blue...&lt;br /&gt;
[*] Enabling NAT on Host...&lt;br /&gt;
[*] Detected internet interface: eth0&lt;br /&gt;
[✓] Setup Complete&lt;br /&gt;
    Red:  10.0.0.2&lt;br /&gt;
    Blue: 10.0.0.3&lt;br /&gt;
    Host: 10.0.0.1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-verify-the-setup&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Step 3: Verify the Setup ===&lt;br /&gt;
&lt;br /&gt;
Test connectivity between Red and Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should succeed. If they fail:&lt;br /&gt;
&lt;br /&gt;
* Check that the setup script completed without errors&lt;br /&gt;
* Verify interfaces are UP: &amp;lt;code&amp;gt;ip link show br-lab&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sudo ip netns exec red ip link&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify routes: &amp;lt;code&amp;gt;sudo ip netns exec red ip route show&amp;lt;/code&amp;gt;&lt;br /&gt;
* Verify NAT rules: &amp;lt;code&amp;gt;sudo nft list ruleset&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You&amp;#039;re now ready to begin the hands-on exercises!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
These exercises build progressively, demonstrating the differences between UDP and TCP, socket state management, and finally securing communications with TLS encryption.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-udp-communication-the-connectionless-message&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: UDP Communication (The Connectionless Message) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-udps-simplicity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: UDP&amp;#039;s Simplicity ====&lt;br /&gt;
&lt;br /&gt;
UDP is the &amp;amp;quot;postcards&amp;amp;quot; of the internet. You write a message, put on an address, and drop it in the mailbox. You hope it arrives, but you don&amp;#039;t get confirmation. There&amp;#039;s no handshake, no connection establishment—just send data and hope for the best.&lt;br /&gt;
&lt;br /&gt;
This simplicity has advantages:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Low latency&amp;#039;&amp;#039;&amp;#039;: No handshake delay&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No connection state&amp;#039;&amp;#039;&amp;#039;: Server can handle many clients with minimal resources&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Multicast/broadcast capable&amp;#039;&amp;#039;&amp;#039;: Can send to multiple recipients simultaneously&lt;br /&gt;
&lt;br /&gt;
For this exercise, we&amp;#039;ll send a message from Red to Blue using UDP and observe the traffic with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;. Because UDP is connectionless, you&amp;#039;ll see the data appear immediately on the wire without any preceding handshake packets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-sending-udp-messages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Sending UDP Messages ====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use three terminal windows:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 1 (Host)&amp;#039;&amp;#039;&amp;#039;: The &amp;amp;quot;wiretapper&amp;amp;quot; watching traffic on the bridge&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue)&amp;#039;&amp;#039;&amp;#039;: The UDP server listening for messages&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Terminal 3 (Red)&amp;#039;&amp;#039;&amp;#039;: The UDP client sending messages&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1 on the host, start capturing UDP traffic on port 9000:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X udp port 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-i br-lab&amp;lt;/code&amp;gt;: Capture on the bridge interface (where we can see traffic between Red and Blue)&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Display packet contents in both hex and ASCII&lt;br /&gt;
* &amp;lt;code&amp;gt;udp port 9000&amp;lt;/code&amp;gt;: BPF (Berkeley Packet Filter) expression matching UDP packets with source or destination port 9000&lt;br /&gt;
&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode&lt;br /&gt;
listening on br-lab, link-type EN10MB (Ethernet), capture size 262144 bytes&amp;lt;/pre&amp;gt;&lt;br /&gt;
Leave this running. It&amp;#039;s now waiting to capture packets.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the UDP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, start a UDP listener in the Blue namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -u -l -p 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec blue&amp;lt;/code&amp;gt;: Run command in Blue&amp;#039;s namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt;: Network cat implementation (from nmap package)&lt;br /&gt;
* &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Use UDP instead of TCP&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Listen mode (act as server)&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 9000&amp;lt;/code&amp;gt;: Listen on port 9000&lt;br /&gt;
&lt;br /&gt;
The command appears to hang: this is correct. It&amp;#039;s waiting for incoming datagrams.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the UDP Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect to the Blue server from Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat -u 10.0.0.3 9000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ncat -u 10.0.0.3 9000&amp;lt;/code&amp;gt;: Connect to 10.0.0.3 on port 9000 using UDP&lt;br /&gt;
&lt;br /&gt;
The terminal is now ready for input. You can type messages.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Send a Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Hello UDP World&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Observe the Results&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 2 (Blue server)&amp;#039;&amp;#039;&amp;#039;: Should display &amp;amp;quot;Hello UDP World&amp;amp;quot;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Terminal 1 (tcpdump)&amp;#039;&amp;#039;&amp;#039;: Should show the captured packet&lt;br /&gt;
&lt;br /&gt;
The tcpdump output will look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;15:42:18.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.9000: UDP, length 16&lt;br /&gt;
    0x0000:  4500 002c 1234 4000 4011 abcd 0a00 0002  E..,..@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 2328 0018 5678 4865 6c6c  .....n#(..VxHell&lt;br /&gt;
    0x0020:  6f20 5544 5020 576f 726c 640a            o.UDP.World.&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* Source: &amp;lt;code&amp;gt;10.0.0.2.45678&amp;lt;/code&amp;gt; (Red, ephemeral port)&lt;br /&gt;
* Destination: &amp;lt;code&amp;gt;10.0.0.3.9000&amp;lt;/code&amp;gt; (Blue, our server port)&lt;br /&gt;
* Protocol: UDP&lt;br /&gt;
* You can see &amp;amp;quot;Hello UDP World&amp;amp;quot; in the ASCII column on the right&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Critical Analysis&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look carefully at the tcpdump output. Count the packets:&lt;br /&gt;
&lt;br /&gt;
* You should see exactly ONE packet—the data packet&lt;br /&gt;
* There were NO packets before the message (no handshake)&lt;br /&gt;
* There were NO acknowledgment packets after the message&lt;br /&gt;
&lt;br /&gt;
Try sending more messages in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message two&lt;br /&gt;
Third message&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears immediately in Terminal 2 and Terminal 1 as a separate UDP datagram.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all three terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
Understanding What Happened&lt;br /&gt;
&lt;br /&gt;
When you pressed Enter in Terminal 3:&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process wrote &amp;amp;quot;Hello UDP World\n&amp;amp;quot; to a UDP socket&lt;br /&gt;
# Red&amp;#039;s kernel wrapped this in a UDP datagram (8-byte UDP header + data)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the UDP datagram in an IP packet (20-byte IP header + UDP datagram)&lt;br /&gt;
# Red&amp;#039;s kernel wrapped the IP packet in an Ethernet frame (14-byte Ethernet header + IP packet)&lt;br /&gt;
# Frame sent out veth-red interface&lt;br /&gt;
# Frame traversed veth pair to bridge&lt;br /&gt;
# Bridge forwarded frame to veth-blue-br&lt;br /&gt;
# Frame arrived at Blue&amp;#039;s veth-blue interface&lt;br /&gt;
# Blue&amp;#039;s kernel unwrapped layers and delivered payload to &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; process listening on port 9000&lt;br /&gt;
# Blue&amp;#039;s &amp;lt;code&amp;gt;ncat&amp;lt;/code&amp;gt; wrote payload to stdout&lt;br /&gt;
&lt;br /&gt;
All of this happened in microseconds, with no connection setup or teardown.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot or text of the tcpdump output showing at least one UDP packet. The output must clearly show:&lt;br /&gt;
#* Source IP and port (Red, ephemeral)&lt;br /&gt;
#* Destination IP and port (Blue, 9000)&lt;br /&gt;
#* The plaintext message in the hex/ASCII dump&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Brief Explanation&amp;#039;&amp;#039;&amp;#039;: Write 2-3 sentences explaining why you see no packets before the message (no handshake) and why this is characteristic of UDP&amp;#039;s connectionless nature.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-tcp-communication-and-socket-state-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: TCP Communication and Socket State Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-tcps-statefulness&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: TCP&amp;#039;s Statefulness ====&lt;br /&gt;
&lt;br /&gt;
Unlike UDP, TCP maintains connection state. Before any data flows, both sides must agree to communicate (handshake). The kernel tracks this state throughout the connection&amp;#039;s lifetime.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll:&lt;br /&gt;
&lt;br /&gt;
# Start a TCP server in Blue&lt;br /&gt;
# Use &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt; to discover the open port (simulating reconnaissance)&lt;br /&gt;
# Capture the three-way handshake with &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt;&lt;br /&gt;
# Establish a connection from Red&lt;br /&gt;
# Inspect the kernel&amp;#039;s socket state table to see the ESTABLISHED connection&lt;br /&gt;
# Observe the four-way handshake when closing&lt;br /&gt;
&lt;br /&gt;
This demonstrates TCP&amp;#039;s stateful nature and introduces important diagnostic tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tcp-connection-lifecycle&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TCP Connection Lifecycle ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the TCP Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start a TCP listener in Blue on port 8080:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat -l -p 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the absence of &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; flag—this defaults to TCP mode.&lt;br /&gt;
&lt;br /&gt;
The command waits for connections. In TCP, the server must be listening before clients can connect (unlike UDP where you can send to a port that isn&amp;#039;t listening).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Verify Server is Listening&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, check Blue&amp;#039;s listening sockets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tln&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ss&amp;lt;/code&amp;gt;: Socket statistics utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-t&amp;lt;/code&amp;gt;: Show TCP sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Show listening sockets only&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Numeric output (don&amp;#039;t resolve port names)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN  0       128     0.0.0.0:8080         0.0.0.0:*&amp;lt;/pre&amp;gt;&lt;br /&gt;
This shows:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: LISTEN (waiting for connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Recv-Q&amp;#039;&amp;#039;&amp;#039;: 0 (no data in receive queue)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Send-Q&amp;#039;&amp;#039;&amp;#039;: 128 (this is actually the backlog—max pending connections)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Local Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:8080 (listening on all interfaces)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Peer Address:Port&amp;#039;&amp;#039;&amp;#039;: 0.0.0.0:* (no peer yet, not connected)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;0.0.0.0&amp;lt;/code&amp;gt; address means &amp;amp;quot;any interface&amp;amp;quot;—the server will accept connections on any IP address belonging to this machine.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Port Reconnaissance with nmap&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, scan Blue from Red to discover open ports:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red nmap -p 8080 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nmap&amp;lt;/code&amp;gt;: Network exploration tool&lt;br /&gt;
* &amp;lt;code&amp;gt;-p 8080&amp;lt;/code&amp;gt;: Scan only port 8080&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3&amp;lt;/code&amp;gt;: Target IP (Blue)&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Starting Nmap 7.93 ( https://nmap.org )&lt;br /&gt;
Nmap scan report for 10.0.0.3&lt;br /&gt;
Host is up (0.000050s latency).&lt;br /&gt;
&lt;br /&gt;
PORT     STATE SERVICE&lt;br /&gt;
8080/tcp open  http-proxy&lt;br /&gt;
&lt;br /&gt;
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key information:&lt;br /&gt;
&lt;br /&gt;
* Port 8080 is &amp;#039;&amp;#039;&amp;#039;open&amp;#039;&amp;#039;&amp;#039; (accepting connections)&lt;br /&gt;
* Nmap identified it as potentially being &amp;amp;quot;http-proxy&amp;amp;quot; based on common port conventions (though it&amp;#039;s actually just our ncat listener)&lt;br /&gt;
* Latency is very low (microseconds) because everything is local&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;How did nmap determine the port is open?&amp;#039;&amp;#039;&amp;#039; nmap sent a SYN packet. Blue responded with SYN-ACK (indicating willingness to connect). nmap then sent RST to abort the connection. This &amp;amp;quot;SYN scan&amp;amp;quot; is less noisy than completing the full handshake.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Capture the Three-Way Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, start capturing TCP control packets (SYN, FIN, RST) on the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is a complex BPF filter. Let&amp;#039;s decode it:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;tcp[tcpflags]&amp;lt;/code&amp;gt;: Access the TCP flags byte in the header&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;amp;amp; (tcp-syn|tcp-fin|tcp-rst)&amp;lt;/code&amp;gt;: Bitwise AND with mask for SYN, FIN, or RST flags&lt;br /&gt;
* &amp;lt;code&amp;gt;!= 0&amp;lt;/code&amp;gt;: Match if any of these flags are set&lt;br /&gt;
&lt;br /&gt;
This captures connection establishment (SYN) and termination (FIN, RST) packets, filtering out normal data packets.&lt;br /&gt;
&lt;br /&gt;
Leave this running.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Establish a Connection&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2, connect from Red to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat 10.0.0.3 8080&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time we&amp;#039;re not using &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt; (this is the client side) and not using &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; (defaulting to TCP).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Observe the Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see three packets:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:15:32.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [S], seq 1234567890, win 65535&lt;br /&gt;
16:15:32.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [S.], seq 9876543210, ack 1234567891, win 65535&lt;br /&gt;
16:15:32.123478 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack 9876543211, win 65535&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s analyze each packet:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: SYN&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red (10.0.0.2, ephemeral port 45678)&lt;br /&gt;
* Destination: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S]&amp;lt;/code&amp;gt; (SYN only)&lt;br /&gt;
* Sequence number: Red&amp;#039;s initial sequence number (ISN)&lt;br /&gt;
* This is Red saying: &amp;amp;quot;I want to establish a connection. My starting sequence number is 1234567890.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: SYN-ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Blue (10.0.0.3, port 8080)&lt;br /&gt;
* Destination: Red (10.0.0.2, port 45678)&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[S.]&amp;lt;/code&amp;gt; (SYN + ACK)&lt;br /&gt;
* Sequence number: Blue&amp;#039;s ISN&lt;br /&gt;
* Acknowledgment: Red&amp;#039;s ISN + 1&lt;br /&gt;
* This is Blue saying: &amp;amp;quot;I accept the connection. My starting sequence number is 9876543210, and I&amp;#039;m ready to receive byte 1234567891 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: ACK&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Source: Red&lt;br /&gt;
* Destination: Blue&lt;br /&gt;
* Flags: &amp;lt;code&amp;gt;[.]&amp;lt;/code&amp;gt; (ACK only, represented as a dot)&lt;br /&gt;
* Acknowledgment: Blue&amp;#039;s ISN + 1&lt;br /&gt;
* This is Red saying: &amp;amp;quot;Acknowledged. I&amp;#039;m ready to receive byte 9876543211 from you.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The connection is now &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; on both sides.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect Socket State&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 4 (new terminal), check Blue&amp;#039;s socket table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Adding the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; flag shows all states (not just listening).&lt;br /&gt;
&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
LISTEN      0       128     0.0.0.0:8080         0.0.0.0:*&lt;br /&gt;
ESTAB       0       0       10.0.0.3:8080        10.0.0.2:45678&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we see two entries:&lt;br /&gt;
&lt;br /&gt;
# The original LISTEN socket (still waiting for additional connections)&lt;br /&gt;
# A new ESTAB (ESTABLISHED) socket representing the connected client&lt;br /&gt;
&lt;br /&gt;
The ESTABLISHED socket shows the full four-tuple:&lt;br /&gt;
&lt;br /&gt;
* Local: 10.0.0.3:8080 (Blue&amp;#039;s IP and the server port)&lt;br /&gt;
* Peer: 10.0.0.2:45678 (Red&amp;#039;s IP and Red&amp;#039;s ephemeral port)&lt;br /&gt;
&lt;br /&gt;
Also check from Red&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
ESTAB       0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
Red sees one ESTABLISHED connection. Notice the local and peer addresses are swapped from Blue&amp;#039;s perspective—same connection, different viewpoint.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Data Transfer&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The connection is established. Type a message in Terminal 2 (Red client):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Testing TCP Connection&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. The message should appear in Terminal 1 (Blue server).&lt;br /&gt;
&lt;br /&gt;
Now type a response in Terminal 1:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Message received&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter. It should appear in Terminal 2.&lt;br /&gt;
&lt;br /&gt;
TCP provides &amp;#039;&amp;#039;&amp;#039;bidirectional&amp;#039;&amp;#039;&amp;#039; communication over a single connection.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: Connection Termination&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+D (EOF, end of file) in Terminal 2 (Red client). This closes Red&amp;#039;s side of the connection.&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (tcpdump), you should see the four-way handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:20:15.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123467 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [.], ack ...&lt;br /&gt;
16:20:15.123478 IP 10.0.0.3.8080 &amp;amp;gt; 10.0.0.2.45678: Flags [F.], seq ..., ack ...&lt;br /&gt;
16:20:15.123489 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8080: Flags [.], ack ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 1: FIN from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red says: &amp;amp;quot;I&amp;#039;m done sending data. I&amp;#039;m closing my side of the connection.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 2: ACK from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue acknowledges Red&amp;#039;s FIN: &amp;amp;quot;I received your close notification.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 3: FIN from Blue&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Blue says: &amp;amp;quot;I&amp;#039;m also done. I&amp;#039;m closing my side.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Packet 4: ACK from Red&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Red acknowledges Blue&amp;#039;s FIN: &amp;amp;quot;Confirmed. Connection fully closed.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Immediately after this, check the socket state:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ss -tna&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You might see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;State       Recv-Q  Send-Q  Local Address:Port   Peer Address:Port&lt;br /&gt;
TIME-WAIT   0       0       10.0.0.2:45678       10.0.0.3:8080&amp;lt;/pre&amp;gt;&lt;br /&gt;
The connection enters TIME-WAIT state for typically 60 seconds to ensure all packets have cleared the network. This prevents old duplicate packets from being misinterpreted as part of a new connection using the same port numbers.&lt;br /&gt;
&lt;br /&gt;
After the timeout, the connection disappears entirely from the socket table.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 10: Compare with UDP&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Think about the differences:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;UDP&amp;#039;&amp;#039;&amp;#039;: No handshake, no state, no acknowledgments, just send data&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;TCP&amp;#039;&amp;#039;&amp;#039;: Three-way handshake to establish, state tracking during connection, four-way handshake to terminate&lt;br /&gt;
&lt;br /&gt;
TCP&amp;#039;s complexity provides reliability at the cost of overhead and latency.&lt;br /&gt;
&lt;br /&gt;
Understanding TCP State Transitions&lt;br /&gt;
&lt;br /&gt;
The states we observed:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;LISTEN&amp;#039;&amp;#039;&amp;#039; → Waiting for incoming connections&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;SYN_SENT&amp;#039;&amp;#039;&amp;#039; → (We didn&amp;#039;t see this as client because transition was fast)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ESTABLISHED&amp;#039;&amp;#039;&amp;#039; → Active connection, data transfer phase&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_1&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;FIN_WAIT_2&amp;#039;&amp;#039;&amp;#039; → (Brief state during close)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TIME_WAIT&amp;#039;&amp;#039;&amp;#039; → Ensuring clean shutdown&lt;br /&gt;
&lt;br /&gt;
In production environments, you might see:&lt;br /&gt;
&lt;br /&gt;
* Many TIME_WAIT connections after a load spike (normal)&lt;br /&gt;
* Connections stuck in SYN_SENT (peer not responding)&lt;br /&gt;
* Many CLOSE_WAIT (application not properly closing connections—potential resource leak)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
Provide the following:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ss Output&amp;#039;&amp;#039;&amp;#039;: The output of &amp;lt;code&amp;gt;sudo ip netns exec blue ss -tna&amp;lt;/code&amp;gt; showing both the LISTEN socket and the ESTABLISHED connection. Clearly label which line is which.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: The captured three-way handshake showing:&lt;br /&gt;
#* Packet 1: SYN (Flags [S])&lt;br /&gt;
#* Packet 2: SYN-ACK (Flags [S.])&lt;br /&gt;
#* Packet 3: ACK (Flags [.])&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 3-4 sentences explaining:&lt;br /&gt;
#* What the three-way handshake accomplishes&lt;br /&gt;
#* Why TCP needs this handshake but UDP doesn&amp;#039;t&lt;br /&gt;
#* What the sequence numbers in the handshake are used for&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-public-key-infrastructure-creating-a-certificate-authority&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Public Key Infrastructure (Creating a Certificate Authority) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-trust-problem&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Trust Problem ====&lt;br /&gt;
&lt;br /&gt;
Encryption solves the confidentiality problem. But it creates a new problem: &amp;#039;&amp;#039;&amp;#039;authentication&amp;#039;&amp;#039;&amp;#039;. How do you know you&amp;#039;re talking to the real Blue server and not an attacker pretending to be Blue?&lt;br /&gt;
&lt;br /&gt;
Consider this attack scenario:&lt;br /&gt;
&lt;br /&gt;
# Red wants to connect to Blue&lt;br /&gt;
# Attacker intercepts the connection&lt;br /&gt;
# Attacker establishes two connections: Attacker↔Red and Attacker↔Blue&lt;br /&gt;
# Attacker decrypts messages from Red, reads them, re-encrypts, and forwards to Blue&lt;br /&gt;
# Neither Red nor Blue realizes they&amp;#039;re talking through a middleman&lt;br /&gt;
&lt;br /&gt;
This is a classic Man-in-the-Middle (MITM) attack. Encryption alone doesn&amp;#039;t prevent it.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;PKI solves this with:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificates&amp;#039;&amp;#039;&amp;#039;: Digital documents binding a public key to an identity (domain name, organization)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Digital Signatures&amp;#039;&amp;#039;&amp;#039;: Certificates are signed by a trusted Certificate Authority (CA)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Trust Anchors&amp;#039;&amp;#039;&amp;#039;: Your system comes with a pre-installed list of trusted root CAs&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue:&lt;br /&gt;
&lt;br /&gt;
# Blue sends its certificate&lt;br /&gt;
# Red checks: Is this certificate signed by a CA I trust?&lt;br /&gt;
# Red verifies the certificate is for the correct domain/identity&lt;br /&gt;
# Red verifies the certificate hasn&amp;#039;t expired&lt;br /&gt;
# If all checks pass, Red uses the public key from the certificate to establish encryption&lt;br /&gt;
&lt;br /&gt;
The attacker can&amp;#039;t forge a certificate signed by a trusted CA (they don&amp;#039;t have the CA&amp;#039;s private key).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Certificate Components:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A certificate contains:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: Who the certificate is for (e.g., &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;, Common Name)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: Who signed it (e.g., &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: The subject&amp;#039;s public key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity Period&amp;#039;&amp;#039;&amp;#039;: Not Before and Not After dates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: CA&amp;#039;s digital signature over all the above&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;X.509 Standard:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Certificates use the X.509 format, an ITU-T standard. The format is binary (DER encoding) but often converted to text (PEM encoding) for easier handling. PEM format looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN CERTIFICATE-----&lt;br /&gt;
MIIDXTCCAkWgAwIBAgIJAKL0h...&lt;br /&gt;
(many lines of Base64-encoded data)&lt;br /&gt;
-----END CERTIFICATE-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-your-own-pki&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building Your Own PKI ====&lt;br /&gt;
&lt;br /&gt;
In production, you&amp;#039;d obtain certificates from a public CA like Let&amp;#039;s Encrypt, DigiCert, or GlobalSign. For this lab, we&amp;#039;ll create our own CA and sign our own certificates. This gives insight into how PKI works internally.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Create a Working Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p pki&lt;br /&gt;
cd pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This directory will contain all our keys and certificates. In production, private keys would be stored with strict access controls (chmod 600).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Generate the Certificate Authority&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
First, we create the root of our trust hierarchy—the CA itself.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=MyCA/CN=root-ca&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this complex command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req&amp;lt;/code&amp;gt;: Certificate request utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-new&amp;lt;/code&amp;gt;: Generate a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-x509&amp;lt;/code&amp;gt;: Output a self-signed certificate instead of a CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
* &amp;lt;code&amp;gt;-nodes&amp;lt;/code&amp;gt;: Don&amp;#039;t encrypt the private key (no DES, &amp;amp;quot;nodes&amp;amp;quot; = no DES). In production, you&amp;#039;d protect the CA key with a passphrase.&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject (identity):&lt;br /&gt;
** &amp;lt;code&amp;gt;C=RO&amp;lt;/code&amp;gt;: Country (Romania)&lt;br /&gt;
** &amp;lt;code&amp;gt;ST=Bucharest&amp;lt;/code&amp;gt;: State/Province&lt;br /&gt;
** &amp;lt;code&amp;gt;L=Lab&amp;lt;/code&amp;gt;: Locality&lt;br /&gt;
** &amp;lt;code&amp;gt;O=MyCA&amp;lt;/code&amp;gt;: Organization&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=root-ca&amp;lt;/code&amp;gt;: Common Name (identifies this CA)&lt;br /&gt;
* &amp;lt;code&amp;gt;-keyout ca.key&amp;lt;/code&amp;gt;: Write private key to this file&lt;br /&gt;
* &amp;lt;code&amp;gt;-out ca.crt&amp;lt;/code&amp;gt;: Write certificate to this file&lt;br /&gt;
&lt;br /&gt;
This creates two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s private key. KEEP THIS SECRET. Anyone with this key can sign certificates that your system will trust.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: The CA&amp;#039;s self-signed certificate. This is the &amp;amp;quot;trust anchor&amp;amp;quot; that clients will use to verify other certificates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Inspect the CA Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s see what we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in ca.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509&amp;lt;/code&amp;gt;: Certificate utility&lt;br /&gt;
* &amp;lt;code&amp;gt;-in ca.crt&amp;lt;/code&amp;gt;: Input file&lt;br /&gt;
* &amp;lt;code&amp;gt;-text&amp;lt;/code&amp;gt;: Output human-readable text&lt;br /&gt;
* &amp;lt;code&amp;gt;-noout&amp;lt;/code&amp;gt;: Don&amp;#039;t output the certificate itself (just the decoded text)&lt;br /&gt;
&lt;br /&gt;
Expected output (abbreviated):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 3 (0x2)&lt;br /&gt;
        Serial Number: 12345678901234567890&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: Nov  1 10:00:00 2024 GMT&lt;br /&gt;
            Not After : Nov  1 10:00:00 2025 GMT&lt;br /&gt;
        Subject: C=RO, ST=Bucharest, L=Lab, O=MyCA, CN=root-ca&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                RSA Public-Key: (2048 bit)&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    00:d4:7a:...&lt;br /&gt;
                Exponent: 65537 (0x10001)&lt;br /&gt;
        X509v3 extensions:&lt;br /&gt;
            X509v3 Subject Key Identifier: ...&lt;br /&gt;
            X509v3 Authority Key Identifier: ...&lt;br /&gt;
            X509v3 Basic Constraints: critical&lt;br /&gt;
                CA:TRUE&amp;lt;/pre&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer == Subject&amp;#039;&amp;#039;&amp;#039;: This is self-signed (the CA signed its own certificate)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Validity&amp;#039;&amp;#039;&amp;#039;: 365 days from creation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Public Key&amp;#039;&amp;#039;&amp;#039;: 2048-bit RSA key&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;CA:TRUE&amp;#039;&amp;#039;&amp;#039;: This certificate can sign other certificates&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Generate the Server&amp;#039;s Private Key&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we create a private key for the Blue server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl genrsa -out blue.key 2048&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl genrsa&amp;lt;/code&amp;gt;: Generate RSA private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.key&amp;lt;/code&amp;gt;: Output file&lt;br /&gt;
* &amp;lt;code&amp;gt;2048&amp;lt;/code&amp;gt;: Key size in bits (2048 is standard; 4096 for higher security)&lt;br /&gt;
&lt;br /&gt;
This generates blue.key, a 2048-bit RSA private key. This file must be kept secret. Anyone with this key can impersonate the Blue server.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Generate a Certificate Signing Request (CSR)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
The server creates a CSR to ask the CA to sign a certificate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl req -new -key blue.key \&lt;br /&gt;
  -subj &amp;quot;/C=RO/ST=Bucharest/L=Lab/O=BlueServer/CN=blue.lab&amp;quot; \&lt;br /&gt;
  -out blue.csr&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl req -new&amp;lt;/code&amp;gt;: Create a new certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-key blue.key&amp;lt;/code&amp;gt;: Use this private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-subj&amp;lt;/code&amp;gt;: Certificate subject:&lt;br /&gt;
** &amp;lt;code&amp;gt;CN=blue.lab&amp;lt;/code&amp;gt;: Common Name (this should match the domain name clients use to connect)&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.csr&amp;lt;/code&amp;gt;: Output CSR file&lt;br /&gt;
&lt;br /&gt;
The CSR contains:&lt;br /&gt;
&lt;br /&gt;
* The server&amp;#039;s public key (derived from blue.key)&lt;br /&gt;
* The desired subject (identity)&lt;br /&gt;
* A signature proving the requester possesses the private key&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Why a CSR?&amp;#039;&amp;#039;&amp;#039; In production, you&amp;#039;d send the CSR to a public CA. The CA verifies your identity (sometimes requiring domain ownership verification, sometimes requiring extensive documentation). Once satisfied, the CA signs your CSR, creating a certificate. You never share your private key with the CA.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Sign the Certificate (Act as CA)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Now we act as the CA and sign Blue&amp;#039;s CSR:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -req -in blue.csr -CA ca.crt -CAkey ca.key \&lt;br /&gt;
  -CAcreateserial -out blue.crt -days 365&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;openssl x509 -req&amp;lt;/code&amp;gt;: Sign a certificate request&lt;br /&gt;
* &amp;lt;code&amp;gt;-in blue.csr&amp;lt;/code&amp;gt;: Input CSR&lt;br /&gt;
* &amp;lt;code&amp;gt;-CA ca.crt&amp;lt;/code&amp;gt;: CA&amp;#039;s certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAkey ca.key&amp;lt;/code&amp;gt;: CA&amp;#039;s private key (this is why we keep it secret)&lt;br /&gt;
* &amp;lt;code&amp;gt;-CAcreateserial&amp;lt;/code&amp;gt;: Create a serial number file (ca.srl) to track issued certificates&lt;br /&gt;
* &amp;lt;code&amp;gt;-out blue.crt&amp;lt;/code&amp;gt;: Output signed certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;-days 365&amp;lt;/code&amp;gt;: Certificate valid for 365 days&lt;br /&gt;
&lt;br /&gt;
This creates blue.crt, signed by our CA. The signature proves the CA vouches for the binding between the public key and the identity &amp;amp;quot;blue.lab.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Inspect the Server Certificate&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Key observations:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer&amp;#039;&amp;#039;&amp;#039;: CN=root-ca (signed by our CA)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Subject&amp;#039;&amp;#039;&amp;#039;: CN=blue.lab (the server&amp;#039;s identity)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Issuer ≠ Subject&amp;#039;&amp;#039;&amp;#039;: This is NOT self-signed; it&amp;#039;s signed by the CA&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Signature&amp;#039;&amp;#039;&amp;#039;: Contains the CA&amp;#039;s cryptographic signature&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Verify the Certificate Chain&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Clients will verify that blue.crt is signed by ca.crt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl verify -CAfile ca.crt blue.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;blue.crt: OK&amp;lt;/pre&amp;gt;&lt;br /&gt;
This confirms the certificate chain is valid. If we modified blue.crt or used a different CA, verification would fail.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 9: File Inventory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
List the files we created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -lh pki&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Expected output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rw-r--r-- 1 user user 1.3K Nov  1 10:00 ca.crt&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 ca.key&lt;br /&gt;
-rw-r--r-- 1 user user   17 Nov  1 10:00 ca.srl&lt;br /&gt;
-rw-r--r-- 1 user user 1.1K Nov  1 10:00 blue.crt&lt;br /&gt;
-rw-r--r-- 1 user user  920 Nov  1 10:00 blue.csr&lt;br /&gt;
-rw------- 1 user user 1.7K Nov  1 10:00 blue.key&amp;lt;/pre&amp;gt;&lt;br /&gt;
Files explained:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.crt&amp;#039;&amp;#039;&amp;#039;: CA certificate (public, distribute to clients)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.key&amp;#039;&amp;#039;&amp;#039;: CA private key (KEEP SECRET)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ca.srl&amp;#039;&amp;#039;&amp;#039;: Serial number tracker (internal bookkeeping)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.crt&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s signed certificate (public)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.csr&amp;#039;&amp;#039;&amp;#039;: Certificate signing request (can be deleted after signing)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;blue.key&amp;#039;&amp;#039;&amp;#039;: Blue&amp;#039;s private key (KEEP SECRET)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-the-trust-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding the Trust Model ====&lt;br /&gt;
&lt;br /&gt;
In our lab:&lt;br /&gt;
&lt;br /&gt;
* Clients trust ca.crt (we&amp;#039;ll explicitly provide it)&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
* Therefore, clients trust blue.crt&lt;br /&gt;
&lt;br /&gt;
In the real world:&lt;br /&gt;
&lt;br /&gt;
* Your browser/OS ships with ~150 pre-trusted root CAs&lt;br /&gt;
* When you visit https://example.com, the server sends its certificate&lt;br /&gt;
* Browser verifies the certificate chain: example.com cert → Intermediate CA → Root CA (in trust store)&lt;br /&gt;
* If chain is valid and domain name matches, connection is trusted&lt;br /&gt;
&lt;br /&gt;
This is why certificate authorities are critical infrastructure. Compromise of a CA&amp;#039;s private key would allow attackers to create trusted certificates for any domain.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;File Listing&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;ls -lh pki&amp;lt;/code&amp;gt; showing all six files with their sizes.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Certificate Inspection&amp;#039;&amp;#039;&amp;#039;: Output of &amp;lt;code&amp;gt;openssl x509 -in blue.crt -text -noout&amp;lt;/code&amp;gt; showing the certificate details. Circle or highlight:&lt;br /&gt;
#* The Issuer (should be root-ca)&lt;br /&gt;
#* The Subject (should be blue.lab)&lt;br /&gt;
#* The Validity dates&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 3-4 sentences explaining:&lt;br /&gt;
#* What a Certificate Authority does&lt;br /&gt;
#* Why we need both a private key (blue.key) and a certificate (blue.crt)&lt;br /&gt;
#* What the signature in the certificate proves&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-secured-transport-with-tls-encryption&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Secured Transport with TLS Encryption ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-encryption-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Encryption in Action ====&lt;br /&gt;
&lt;br /&gt;
Now we put everything together: TCP for reliable transport + TLS for encryption using our PKI.&lt;br /&gt;
&lt;br /&gt;
When Red connects to Blue with TLS:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TCP Handshake&amp;#039;&amp;#039;&amp;#039;: Establish connection (SYN, SYN-ACK, ACK)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;TLS Handshake&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Negotiate cipher suite and TLS version&lt;br /&gt;
#* Blue sends its certificate (blue.crt)&lt;br /&gt;
#* Red verifies the certificate is signed by ca.crt (which we&amp;#039;ll provide)&lt;br /&gt;
#* Exchange keys using public key cryptography&lt;br /&gt;
#* Derive shared symmetric encryption keys&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Encrypted Data Transfer&amp;#039;&amp;#039;&amp;#039;: All application data encrypted with the shared keys&lt;br /&gt;
&lt;br /&gt;
After the handshake, all data is encrypted with fast symmetric encryption (typically AES), but the keys were securely exchanged using asymmetric encryption.&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use &amp;lt;code&amp;gt;tcpdump&amp;lt;/code&amp;gt; to show that eavesdroppers (our host acting as &amp;amp;quot;man in the middle&amp;amp;quot;) can&amp;#039;t read the encrypted traffic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-tls-secured-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: TLS-Secured Connection ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Start the Secure Server&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 1, start ncat in SSL/TLS mode in Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ncat --ssl --ssl-cert pki/blue.crt --ssl-key pki/blue.key -l -p 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-cert pki/blue.crt&amp;lt;/code&amp;gt;: Server certificate&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-key pki/blue.key&amp;lt;/code&amp;gt;: Server private key&lt;br /&gt;
* &amp;lt;code&amp;gt;-l -p 8443&amp;lt;/code&amp;gt;: Listen on port 8443 (can choose any port)&lt;br /&gt;
&lt;br /&gt;
The server is now ready to accept TLS connections.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Start the Wiretapper&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 on the host, capture traffic on port 8443:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo tcpdump -i br-lab -X -s 0 port 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-X&amp;lt;/code&amp;gt;: Show hex/ASCII payload&lt;br /&gt;
* &amp;lt;code&amp;gt;-s 0&amp;lt;/code&amp;gt;: Capture full packets (no truncation)&lt;br /&gt;
* &amp;lt;code&amp;gt;port 8443&amp;lt;/code&amp;gt;: Match source or destination port 8443&lt;br /&gt;
&lt;br /&gt;
This is our &amp;amp;quot;attacker&amp;amp;quot; position, intercepting traffic between Red and Blue.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Start the Secure Client&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3, connect from Red with TLS enabled:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ncat --ssl --ssl-verify --ssl-trustfile pki/ca.crt 10.0.0.3 8443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Command breakdown:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl&amp;lt;/code&amp;gt;: Enable SSL/TLS&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-verify&amp;lt;/code&amp;gt;: Verify server certificate (don&amp;#039;t accept self-signed or invalid certificates)&lt;br /&gt;
* &amp;lt;code&amp;gt;--ssl-trustfile pki/ca.crt&amp;lt;/code&amp;gt;: Trust anchor (our CA certificate)&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.3 8443&amp;lt;/code&amp;gt;: Connect to Blue on port 8443&lt;br /&gt;
&lt;br /&gt;
You should see the connection succeed. If you see an error like &amp;amp;quot;certificate verification failed,&amp;amp;quot; check:&lt;br /&gt;
&lt;br /&gt;
* blue.crt Common Name matches the IP/hostname you&amp;#039;re connecting to (we used blue.lab in the cert but are connecting to 10.0.0.3—this mismatch is OK for this lab since we&amp;#039;re using &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
* ca.crt is the correct CA certificate&lt;br /&gt;
* blue.crt is signed by ca.crt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 4: Observe the TLS Handshake&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 2 (tcpdump), you should see several packets immediately after connection. These are the TLS handshake:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:30:45.123456 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 1:517, ack 1, length 516&lt;br /&gt;
    0x0000:  4500 0234 1234 4000 4006 abcd 0a00 0002  E..4.4@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5678 1234 5678  .....n...4Vx.4Vx&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1603 0301 ff01 0001  P...............&lt;br /&gt;
    0x0030:  fb03 0356 4e12 3456 789a bcde f012 3456  ...VN.4Vx.....4V&lt;br /&gt;
    0x0040:  789a bcde f012 3456 789a bcde f012 3456  x...4Vx.....4V&lt;br /&gt;
    ...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice:&lt;br /&gt;
&lt;br /&gt;
* The payload starts with &amp;lt;code&amp;gt;0x16 0x03 0x03&amp;lt;/code&amp;gt; (TLS Handshake, TLS 1.2)&lt;br /&gt;
* The data looks random (it contains encrypted pre-master secrets, cipher suites, etc.)&lt;br /&gt;
* You can&amp;#039;t read any meaningful content&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 5: Send a Secret Message&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In Terminal 3 (Red client), type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;This is a Secret Password: MyP@ssw0rd123&amp;lt;/pre&amp;gt;&lt;br /&gt;
Press Enter.&lt;br /&gt;
&lt;br /&gt;
The message should appear in Terminal 1 (Blue server) in plaintext—the TLS layer decrypts it automatically before delivering to the application.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 6: Inspect the Encrypted Traffic&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Look at Terminal 2 (tcpdump). You should see packets containing the encrypted message:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;16:31:02.234567 IP 10.0.0.2.45678 &amp;amp;gt; 10.0.0.3.8443: Flags [P.], seq 517:572, length 55&lt;br /&gt;
    0x0000:  4500 005f 1235 4000 4006 abc2 0a00 0002  E.._.5@.@.......&lt;br /&gt;
    0x0010:  0a00 0003 b26e 20fb 1234 5890 1234 5890  .....n...4X..4X.&lt;br /&gt;
    0x0020:  5018 ffff abcd 0000 1703 0300 32a4 f7b3  P.........2.....&lt;br /&gt;
    0x0030:  8c44 e291 bc73 4fa8 d612 e8f3 9a45 b7c9  .D...sO......E..&lt;br /&gt;
    0x0040:  1f22 d847 b3a5 c7e9 2d48 f6a4 b812 d7c4  .&amp;amp;quot;.G....-H......&lt;br /&gt;
    0x0050:  3a95 e8f6 b2d1 c847 a5e3 9f                :......G...&amp;lt;/pre&amp;gt;&lt;br /&gt;
Critical observation: &amp;#039;&amp;#039;&amp;#039;Can you read &amp;amp;quot;This is a Secret Password&amp;amp;quot; in the hex dump?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
No! You see random-looking bytes. The actual payload is:&lt;br /&gt;
&lt;br /&gt;
* Encrypted with AES or ChaCha20 (symmetric encryption)&lt;br /&gt;
* Authenticated with HMAC or AEAD&lt;br /&gt;
* Completely unreadable without the encryption keys&lt;br /&gt;
&lt;br /&gt;
Compare this to Exercise A (UDP) where you could clearly read &amp;amp;quot;Hello UDP World&amp;amp;quot; in the packet capture.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 7: Send More Data&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Try sending additional messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Credit Card: 4532-1234-5678-9012&lt;br /&gt;
SSN: 123-45-6789&lt;br /&gt;
API Key: sk_live_1234567890abcdefghijklmnopqrstuvwxyz&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each appears in plaintext on the server (Terminal 1) but as encrypted gibberish in the packet capture (Terminal 2).&lt;br /&gt;
&lt;br /&gt;
This is exactly how HTTPS protects your sensitive data when you browse websites. Between your browser and the web server, your data is encrypted. Even if someone intercepts the packets (your ISP, a coffee shop WiFi operator, a government agency), they see only encrypted data.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 8: Cleanup&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Press Ctrl+C in all terminals to stop the processes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-what-happened&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Understanding What Happened ====&lt;br /&gt;
&lt;br /&gt;
The TLS handshake (simplified):&lt;br /&gt;
&lt;br /&gt;
# Red sends &amp;amp;quot;ClientHello&amp;amp;quot; with supported cipher suites&lt;br /&gt;
# Blue sends &amp;amp;quot;ServerHello&amp;amp;quot; with chosen cipher suite + blue.crt certificate&lt;br /&gt;
# Red verifies blue.crt is signed by ca.crt (from &amp;lt;code&amp;gt;--ssl-trustfile&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Red verifies certificate CN, validity dates, etc.&lt;br /&gt;
# Red generates a pre-master secret, encrypts it with Blue&amp;#039;s public key (from blue.crt), sends it&lt;br /&gt;
# Both sides derive the same symmetric encryption keys from the pre-master secret&lt;br /&gt;
# Both sides send &amp;amp;quot;Finished&amp;amp;quot; messages encrypted with the new keys&lt;br /&gt;
# All subsequent data is encrypted with the symmetric keys&lt;br /&gt;
&lt;br /&gt;
The symmetric keys are never transmitted—both sides independently compute them from the shared pre-master secret.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;real-world-context&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Real-World Context ====&lt;br /&gt;
&lt;br /&gt;
This is how HTTPS works:&lt;br /&gt;
&lt;br /&gt;
* Your browser has ~150 trusted root CAs built in&lt;br /&gt;
* You visit https://example.com&lt;br /&gt;
* Server sends its certificate, signed by a trusted CA&lt;br /&gt;
* Browser verifies the chain: example.com cert → Intermediate CA → Root CA&lt;br /&gt;
* If valid, browser shows padlock icon&lt;br /&gt;
* All data encrypted with TLS&lt;br /&gt;
&lt;br /&gt;
Without TLS, someone could:&lt;br /&gt;
&lt;br /&gt;
* Read your passwords&lt;br /&gt;
* Steal your session cookies&lt;br /&gt;
* Intercept your credit card numbers&lt;br /&gt;
* Modify downloads to inject malware&lt;br /&gt;
&lt;br /&gt;
This is why the web has largely moved to HTTPS-by-default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d-provide-the-following&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D: Provide the following: ====&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;tcpdump Output&amp;#039;&amp;#039;&amp;#039;: Screenshot or text of the packet capture showing encrypted data. The output must clearly show:&lt;br /&gt;
#* Source and destination (Red to Blue on port 8443)&lt;br /&gt;
#* The hex dump of the payload&lt;br /&gt;
#* The payload looks like random bytes (NOT readable text)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Comparison&amp;#039;&amp;#039;&amp;#039;: Side-by-side comparison (can be text description or screenshot):&lt;br /&gt;
#* Exercise A UDP packet capture (plaintext visible)&lt;br /&gt;
#* Exercise D TLS packet capture (encrypted)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Explanation&amp;#039;&amp;#039;&amp;#039;: Write 4-5 sentences explaining:&lt;br /&gt;
#* Why the tcpdump output shows gibberish instead of your actual message&lt;br /&gt;
#* What role the certificate (blue.crt) plays in establishing trust&lt;br /&gt;
#* How TLS prevents a man-in-the-middle attacker from reading your data&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-communication-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Network Communication Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# UDP Server&lt;br /&gt;
ncat -u -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP Client&lt;br /&gt;
ncat -u &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server&lt;br /&gt;
ncat -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client&lt;br /&gt;
ncat &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Server with TLS&lt;br /&gt;
ncat --ssl --ssl-cert &amp;lt;cert&amp;gt; --ssl-key &amp;lt;key&amp;gt; -l -p &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (verify certificate)&lt;br /&gt;
ncat --ssl --ssl-verify --ssl-trustfile &amp;lt;ca_cert&amp;gt; &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Client with TLS (no verification - insecure)&lt;br /&gt;
ncat --ssl &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-inspection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Socket Inspection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show all TCP sockets&lt;br /&gt;
ss -ta&lt;br /&gt;
&lt;br /&gt;
# Show all TCP sockets, numeric, all states&lt;br /&gt;
ss -tna&lt;br /&gt;
&lt;br /&gt;
# Show listening TCP sockets&lt;br /&gt;
ss -tln&lt;br /&gt;
&lt;br /&gt;
# Show TCP sockets with process info (requires root)&lt;br /&gt;
ss -tnap&lt;br /&gt;
&lt;br /&gt;
# Show UDP sockets&lt;br /&gt;
ss -una&lt;br /&gt;
&lt;br /&gt;
# Show socket memory usage&lt;br /&gt;
ss -tm&lt;br /&gt;
&lt;br /&gt;
# Show extended socket information&lt;br /&gt;
ss -tei&lt;br /&gt;
&lt;br /&gt;
# Filter by state&lt;br /&gt;
ss -t state established&lt;br /&gt;
ss -t state time-wait&lt;br /&gt;
&lt;br /&gt;
# Filter by port&lt;br /&gt;
ss -tn sport = :8080&lt;br /&gt;
ss -tn dport = :443&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;port-scanning-nmap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Port Scanning (nmap) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Scan specific port&lt;br /&gt;
nmap -p 22 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan port range&lt;br /&gt;
nmap -p 1-1000 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan all ports (slow)&lt;br /&gt;
nmap -p- &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan common ports (faster)&lt;br /&gt;
nmap --top-ports 100 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP SYN scan (stealthy, requires root)&lt;br /&gt;
sudo nmap -sS &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TCP Connect scan (no root required)&lt;br /&gt;
nmap -sT &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# UDP scan (slow)&lt;br /&gt;
sudo nmap -sU &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Service version detection&lt;br /&gt;
nmap -sV &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# OS detection&lt;br /&gt;
sudo nmap -O &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Aggressive scan (OS, version, scripts)&lt;br /&gt;
sudo nmap -A &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Scan subnet&lt;br /&gt;
nmap 10.0.0.0/24&lt;br /&gt;
&lt;br /&gt;
# Fast scan (no DNS resolution, no ping)&lt;br /&gt;
nmap -n -Pn &amp;lt;ip&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;packet-capture-tcpdump&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Packet Capture (tcpdump) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Capture on interface&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show hex and ASCII&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -X&lt;br /&gt;
&lt;br /&gt;
# Capture specific port&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; port 8080&lt;br /&gt;
&lt;br /&gt;
# Capture specific protocol&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; tcp&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; udp&lt;br /&gt;
&lt;br /&gt;
# Capture TCP SYN packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; tcp-syn != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Capture TCP handshakes (SYN, FIN, RST)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; &amp;quot;tcp[tcpflags] &amp;amp; (tcp-syn|tcp-fin|tcp-rst) != 0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Save to file&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -w capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Read from file&lt;br /&gt;
tcpdump -r capture.pcap&lt;br /&gt;
&lt;br /&gt;
# Capture full packets (no truncation)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -s 0&lt;br /&gt;
&lt;br /&gt;
# Show absolute sequence numbers&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -S&lt;br /&gt;
&lt;br /&gt;
# Don&amp;#039;t resolve hostnames (faster)&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n&lt;br /&gt;
&lt;br /&gt;
# Capture only N packets&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -c 10&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;certificate-management-openssl&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Certificate Management (openssl) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Generate self-signed CA&lt;br /&gt;
openssl req -new -x509 -days 365 -nodes \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=CA&amp;quot; \&lt;br /&gt;
  -keyout ca.key -out ca.crt&lt;br /&gt;
&lt;br /&gt;
# Generate private key&lt;br /&gt;
openssl genrsa -out server.key 2048&lt;br /&gt;
openssl genrsa -out server.key 4096  # More secure&lt;br /&gt;
&lt;br /&gt;
# Generate CSR&lt;br /&gt;
openssl req -new -key server.key \&lt;br /&gt;
  -subj &amp;quot;/C=XX/ST=State/L=City/O=Org/CN=domain.com&amp;quot; \&lt;br /&gt;
  -out server.csr&lt;br /&gt;
&lt;br /&gt;
# Sign CSR with CA&lt;br /&gt;
openssl x509 -req -in server.csr \&lt;br /&gt;
  -CA ca.crt -CAkey ca.key -CAcreateserial \&lt;br /&gt;
  -out server.crt -days 365&lt;br /&gt;
&lt;br /&gt;
# View certificate (human-readable)&lt;br /&gt;
openssl x509 -in cert.crt -text -noout&lt;br /&gt;
&lt;br /&gt;
# View CSR&lt;br /&gt;
openssl req -in cert.csr -text -noout&lt;br /&gt;
&lt;br /&gt;
# View private key&lt;br /&gt;
openssl rsa -in key.key -text -noout&lt;br /&gt;
&lt;br /&gt;
# Extract specific fields&lt;br /&gt;
openssl x509 -in cert.crt -noout -subject&lt;br /&gt;
openssl x509 -in cert.crt -noout -issuer&lt;br /&gt;
openssl x509 -in cert.crt -noout -dates&lt;br /&gt;
openssl x509 -in cert.crt -noout -serial&lt;br /&gt;
openssl x509 -in cert.crt -noout -fingerprint&lt;br /&gt;
&lt;br /&gt;
# Verify certificate chain&lt;br /&gt;
openssl verify -CAfile ca.crt cert.crt&lt;br /&gt;
&lt;br /&gt;
# Check certificate and key match&lt;br /&gt;
openssl x509 -noout -modulus -in cert.crt | openssl md5&lt;br /&gt;
openssl rsa -noout -modulus -in key.key | openssl md5&lt;br /&gt;
# If MD5 hashes match, certificate and key are paired&lt;br /&gt;
&lt;br /&gt;
# Convert formats&lt;br /&gt;
openssl x509 -in cert.pem -out cert.der -outform DER  # PEM to DER&lt;br /&gt;
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM  # DER to PEM&lt;br /&gt;
&lt;br /&gt;
# Test TLS connection&lt;br /&gt;
openssl s_client -connect domain.com:443 -CAfile ca.crt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-port-numbers-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Port Numbers Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Port&lt;br /&gt;
! Service&lt;br /&gt;
! Protocol&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 20&lt;br /&gt;
| FTP-DATA&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP data transfer&lt;br /&gt;
|-&lt;br /&gt;
| 21&lt;br /&gt;
| FTP&lt;br /&gt;
| TCP&lt;br /&gt;
| FTP control&lt;br /&gt;
|-&lt;br /&gt;
| 22&lt;br /&gt;
| SSH&lt;br /&gt;
| TCP&lt;br /&gt;
| Secure Shell&lt;br /&gt;
|-&lt;br /&gt;
| 23&lt;br /&gt;
| Telnet&lt;br /&gt;
| TCP&lt;br /&gt;
| Unencrypted remote access&lt;br /&gt;
|-&lt;br /&gt;
| 25&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email sending&lt;br /&gt;
|-&lt;br /&gt;
| 53&lt;br /&gt;
| DNS&lt;br /&gt;
| UDP/TCP&lt;br /&gt;
| Domain Name System&lt;br /&gt;
|-&lt;br /&gt;
| 67/68&lt;br /&gt;
| DHCP&lt;br /&gt;
| UDP&lt;br /&gt;
| Dynamic IP configuration&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| HTTP&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (unencrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 110&lt;br /&gt;
| POP3&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 143&lt;br /&gt;
| IMAP&lt;br /&gt;
| TCP&lt;br /&gt;
| Email retrieval&lt;br /&gt;
|-&lt;br /&gt;
| 443&lt;br /&gt;
| HTTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| Web traffic (encrypted)&lt;br /&gt;
|-&lt;br /&gt;
| 465&lt;br /&gt;
| SMTPS&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 587&lt;br /&gt;
| SMTP&lt;br /&gt;
| TCP&lt;br /&gt;
| SMTP (submission)&lt;br /&gt;
|-&lt;br /&gt;
| 993&lt;br /&gt;
| IMAPS&lt;br /&gt;
| TCP&lt;br /&gt;
| IMAP over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 995&lt;br /&gt;
| POP3S&lt;br /&gt;
| TCP&lt;br /&gt;
| POP3 over TLS&lt;br /&gt;
|-&lt;br /&gt;
| 3306&lt;br /&gt;
| MySQL&lt;br /&gt;
| TCP&lt;br /&gt;
| MySQL database&lt;br /&gt;
|-&lt;br /&gt;
| 3389&lt;br /&gt;
| RDP&lt;br /&gt;
| TCP&lt;br /&gt;
| Remote Desktop Protocol&lt;br /&gt;
|-&lt;br /&gt;
| 5432&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
| TCP&lt;br /&gt;
| PostgreSQL database&lt;br /&gt;
|-&lt;br /&gt;
| 6379&lt;br /&gt;
| Redis&lt;br /&gt;
| TCP&lt;br /&gt;
| Redis cache&lt;br /&gt;
|-&lt;br /&gt;
| 8080&lt;br /&gt;
| HTTP-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTP port&lt;br /&gt;
|-&lt;br /&gt;
| 8443&lt;br /&gt;
| HTTPS-Alt&lt;br /&gt;
| TCP&lt;br /&gt;
| Alternative HTTPS port&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;tcp-state-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== TCP State Reference ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! State&lt;br /&gt;
! Description&lt;br /&gt;
! Typical Duration&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| No connection&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| Server waiting for connections&lt;br /&gt;
| Indefinite&lt;br /&gt;
|-&lt;br /&gt;
| SYN_SENT&lt;br /&gt;
| Client sent SYN, waiting for SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RCVD&lt;br /&gt;
| Server received SYN, sent SYN-ACK&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Connection active, data transfer&lt;br /&gt;
| Seconds to hours&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_1&lt;br /&gt;
| Active close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| FIN_WAIT_2&lt;br /&gt;
| Active close, received ACK for FIN&lt;br /&gt;
| Seconds&lt;br /&gt;
|-&lt;br /&gt;
| CLOSE_WAIT&lt;br /&gt;
| Passive close, received FIN&lt;br /&gt;
| Variable (app-dependent)&lt;br /&gt;
|-&lt;br /&gt;
| CLOSING&lt;br /&gt;
| Both sides closing simultaneously&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| LAST_ACK&lt;br /&gt;
| Passive close, sent FIN&lt;br /&gt;
| Milliseconds&lt;br /&gt;
|-&lt;br /&gt;
| TIME_WAIT&lt;br /&gt;
| Final state after close&lt;br /&gt;
| 60-240 seconds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;cipher-suite-examples&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Cipher Suite Examples ===&lt;br /&gt;
&lt;br /&gt;
Modern cipher suites (TLS 1.3):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_256_GCM_SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_CHACHA20_POLY1305_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TLS_AES_128_GCM_SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy cipher suites (TLS 1.2):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ECDHE-RSA-AES128-GCM-SHA256&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DHE-RSA-AES256-GCM-SHA384&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Cipher suite components:&lt;br /&gt;
&lt;br /&gt;
* Key exchange: ECDHE, DHE, RSA&lt;br /&gt;
* Authentication: RSA, ECDSA, Ed25519&lt;br /&gt;
* Encryption: AES-256-GCM, AES-128-GCM, ChaCha20-Poly1305&lt;br /&gt;
* Hash: SHA256, SHA384&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing all Exercise Deliverables (A-D).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab introduced the transport layer (UDP, TCP) and applied cryptography (PKI, TLS) to secure communications. These concepts are foundational to understanding modern networked systems and security.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== For Further Study: ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Transport Protocols:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP congestion control algorithms (Reno, Cubic, BBR)&lt;br /&gt;
* TCP fast open (TFO) for reduced latency&lt;br /&gt;
* QUIC/HTTP/3 for modern applications&lt;br /&gt;
* SCTP (Stream Control Transmission Protocol)&lt;br /&gt;
* Multipath TCP (MPTCP) for using multiple interfaces simultaneously&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security and Cryptography:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TLS 1.3 improvements over TLS 1.2&lt;br /&gt;
* Certificate pinning for enhanced security&lt;br /&gt;
* Elliptic curve cryptography (ECDSA, Ed25519)&lt;br /&gt;
* Perfect forward secrecy (PFS)&lt;br /&gt;
* HSTS (HTTP Strict Transport Security)&lt;br /&gt;
* Certificate Transparency logs&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Monitoring and Debugging:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Wireshark for advanced packet analysis&lt;br /&gt;
* tshark for command-line packet analysis&lt;br /&gt;
* iptraf-ng for real-time network monitoring&lt;br /&gt;
* netstat and ss advanced features&lt;br /&gt;
* strace for tracing system calls related to networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Secure Communication Tools:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* WireGuard VPN&lt;br /&gt;
* OpenVPN&lt;br /&gt;
* SSH tunneling and port forwarding&lt;br /&gt;
* mTLS (mutual TLS) for client authentication&lt;br /&gt;
* SOCKS proxies&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Network Security:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Firewall configuration (nftables, iptables)&lt;br /&gt;
* IDS/IPS systems (Snort, Suricata)&lt;br /&gt;
* Network segmentation and VLANs&lt;br /&gt;
* DDoS mitigation techniques&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance Optimization:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* TCP tuning (window size, buffer sizes)&lt;br /&gt;
* Nagle&amp;#039;s algorithm and TCP_NODELAY&lt;br /&gt;
* TCP keepalive configuration&lt;br /&gt;
* Connection pooling&lt;br /&gt;
* Load balancing techniques&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 7 tcp          # TCP protocol overview&lt;br /&gt;
man 7 udp          # UDP protocol overview&lt;br /&gt;
man 7 ip           # IP protocol overview&lt;br /&gt;
man 8 ss           # Socket statistics utility&lt;br /&gt;
man 8 nmap         # Network exploration tool&lt;br /&gt;
man 8 tcpdump      # Packet capture tool&lt;br /&gt;
man 1 openssl      # OpenSSL command-line tool&lt;br /&gt;
man 1 ncat         # Ncat (netcat) tool&lt;br /&gt;
man 5 nftables     # nftables firewall&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://en.wikipedia.org/wiki/TCP/IP_Illustrated TCP/IP Illustrated by W. Richard Stevens] - Classic networking book&lt;br /&gt;
* [https://hpbn.co/ High Performance Browser Networking] - Free online book by Ilya Grigorik&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc8446 TLS 1.3 RFC 8446]&lt;br /&gt;
* [https://www.rfc-editor.org/rfc/rfc9000 QUIC RFC 9000]&lt;br /&gt;
* [https://letsencrypt.org/docs/ Let&amp;#039;s Encrypt Documentation] - Free CA for real certificates&lt;br /&gt;
* [https://www.ssllabs.com/ SSL Labs] - Test TLS configuration of real websites&lt;br /&gt;
* [https://www.wireshark.org/docs/ Wireshark Documentation]&lt;br /&gt;
* [https://nmap.org/docs.html Nmap Documentation]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8182</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8182"/>
		<updated>2025-11-28T13:22:54Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;br /&gt;
* Lab 8 - [[OS Lab 8 - Transport and Security]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_7_-_The_Network_Subsystem&amp;diff=8181</id>
		<title>OS Lab 7 - The Network Subsystem</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_7_-_The_Network_Subsystem&amp;diff=8181"/>
		<updated>2025-11-21T15:02:02Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel represents network abstractions: Interfaces, Addresses, Routes, and Neighbors.&lt;br /&gt;
* Use the modern iproute2 suite (&amp;lt;code&amp;gt;ip link&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip addr&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip route&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip neigh&amp;lt;/code&amp;gt;) to manage kernel network objects.&lt;br /&gt;
* Create isolated network environments using Network Namespaces to simulate multiple virtual computers.&lt;br /&gt;
* Construct a complete virtual network topology (Switch, Router, NAT) from scratch using OS primitives.&lt;br /&gt;
* Diagnose connectivity issues by inspecting the ARP cache and Routing table to understand packet delivery.&lt;br /&gt;
* Understand the relationship between Layer 2 (MAC addresses) and Layer 3 (IP addresses) networking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 6, we explored Inter-Process Communication (IPC) mechanisms that allow processes to exchange data and coordinate their actions. We touched briefly on Sockets, which extend IPC beyond a single machine by enabling communication across network boundaries. However, using sockets effectively requires understanding what happens beneath the abstraction: how does data actually travel from one machine to another?&lt;br /&gt;
&lt;br /&gt;
The answer lies in the kernel&amp;#039;s network stack, a complex subsystem responsible for managing network interfaces, addressing, routing, and protocol handling. Usually, the network stack is configured automatically by background services like NetworkManager, systemd-networkd, or DHCP clients. These tools shield users from complexity, but they also obscure the fundamental mechanisms at work.&lt;br /&gt;
&lt;br /&gt;
In this lab, we peel back those layers to interact directly with the kernel&amp;#039;s networking subsystem. We will not use physical network cables or hardware switches. Instead, we will use the operating system itself to manufacture virtual hardware—virtual network cards, virtual cables, and virtual switches. By constructing networks programmatically, we gain deep insight into how the kernel manages connectivity, routing, and address resolution.&lt;br /&gt;
&lt;br /&gt;
This lab is structured around a progression: we start with a simple point-to-point connection between two virtual machines, then evolve it into a switched network with multiple clients, routing capabilities, and internet access via Network Address Translation (NAT). Along the way, we examine the kernel&amp;#039;s internal state at each step to understand exactly how packets flow through the system.&lt;br /&gt;
&lt;br /&gt;
Understanding the network subsystem is essential for system administration, DevOps, container orchestration (Docker, Kubernetes), network troubleshooting, and security. These same primitives underlie modern cloud networking, virtual private networks, and service meshes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of Linux machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y iproute2 iputils-ping traceroute nftables&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: The modern suite for network configuration, replacing legacy tools like &amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt;. Provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command.&lt;br /&gt;
* &amp;lt;code&amp;gt;iputils-ping&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; utility for testing connectivity.&lt;br /&gt;
* &amp;lt;code&amp;gt;traceroute&amp;lt;/code&amp;gt;: Maps the path packets take through networks.&lt;br /&gt;
* &amp;lt;code&amp;gt;nftables&amp;lt;/code&amp;gt;: The modern Linux firewall/NAT framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Knowledge Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process hierarchy)&lt;br /&gt;
* File permissions from Lab 4 (execute bit, ownership)&lt;br /&gt;
* Bash scripting from Lab 5 (shebangs, variables, loops, functions)&lt;br /&gt;
* IPC concepts from Lab 6 (understanding of how processes communicate)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-network-subsystem-kernel-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== The Network Subsystem: Kernel Fundamentals ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-a-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== What is a Network? ====&lt;br /&gt;
&lt;br /&gt;
To the Linux kernel, a &amp;amp;quot;Network&amp;amp;quot; is not a physical thing—it&amp;#039;s an abstraction representing a group of computers that can communicate with each other directly, without requiring a router as an intermediary. This direct communication capability is what defines a network segment or broadcast domain.&lt;br /&gt;
&lt;br /&gt;
A single computer can participate in multiple networks simultaneously. It simply needs a separate Network Interface for each network it wants to join. Think of interfaces as &amp;amp;quot;plugs&amp;amp;quot; or &amp;amp;quot;sockets&amp;amp;quot; that connect your computer to different communication channels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-four-pillars-of-networking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Four Pillars of Networking ====&lt;br /&gt;
&lt;br /&gt;
The kernel&amp;#039;s network subsystem is built on four fundamental abstractions. Understanding these is key to mastering network configuration:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Interfaces&amp;#039;&amp;#039;&amp;#039;: The physical or virtual network adapters that can send and receive packets. Each interface represents a connection point to a network.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Addresses&amp;#039;&amp;#039;&amp;#039;: IP addresses assigned to interfaces, giving them identities on the network. Addresses define &amp;amp;quot;who&amp;amp;quot; you are.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routes&amp;#039;&amp;#039;&amp;#039;: Kernel rules that determine which interface should be used to reach a given destination. Routes define &amp;amp;quot;how to get there.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Neighbors&amp;#039;&amp;#039;&amp;#039;: The kernel&amp;#039;s cache mapping IP addresses to MAC addresses for direct communication. Neighbors define &amp;amp;quot;where exactly on this wire.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
These four abstractions work together. When you send a packet to an IP address:&lt;br /&gt;
&lt;br /&gt;
# The kernel consults the &amp;#039;&amp;#039;&amp;#039;Routing Table&amp;#039;&amp;#039;&amp;#039; to determine which &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039; to use.&lt;br /&gt;
# If the route indicates the destination is local (directly reachable), the kernel consults the &amp;#039;&amp;#039;&amp;#039;Neighbor Table&amp;#039;&amp;#039;&amp;#039; to find the MAC address.&lt;br /&gt;
# If the MAC address is unknown, the kernel uses ARP (Address Resolution Protocol) to discover it.&lt;br /&gt;
# The kernel constructs an Ethernet frame with the destination MAC address and sends it out the chosen &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-iproute2-suite-modern-network-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The iproute2 Suite: Modern Network Management ====&lt;br /&gt;
&lt;br /&gt;
Historically, Linux used separate commands for each aspect of networking: &amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt; for interfaces, &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt; for routing, &amp;lt;code&amp;gt;arp&amp;lt;/code&amp;gt; for neighbors. These tools are now considered legacy. Modern Linux uses the unified &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt; suite, centered around the &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command uses a consistent syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip OBJECT COMMAND [OPTIONS]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Where OBJECT is one of:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;link&amp;lt;/code&amp;gt;: Network interfaces (Layer 2)&lt;br /&gt;
* &amp;lt;code&amp;gt;addr&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;address&amp;lt;/code&amp;gt;: IP addresses (Layer 3)&lt;br /&gt;
* &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt;: Routing table entries&lt;br /&gt;
* &amp;lt;code&amp;gt;neigh&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;neighbour&amp;lt;/code&amp;gt;: ARP/Neighbor cache&lt;br /&gt;
&lt;br /&gt;
Common commands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip link show&amp;lt;/code&amp;gt;: List all network interfaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt;: Show IP addresses assigned to interfaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route show&amp;lt;/code&amp;gt;: Display the routing table&lt;br /&gt;
* &amp;lt;code&amp;gt;ip neigh show&amp;lt;/code&amp;gt;: Display the neighbor (ARP) cache&lt;br /&gt;
&lt;br /&gt;
The beauty of &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; is that it provides a consistent, scriptable interface to the kernel&amp;#039;s network state. Unlike older tools, &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; is designed for automation and parsing by scripts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-namespaces-virtual-computers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Network Namespaces: Virtual Computers ====&lt;br /&gt;
&lt;br /&gt;
A Network Namespace (often abbreviated &amp;amp;quot;netns&amp;amp;quot;) is a kernel feature that creates an isolated copy of the network stack. Each namespace has its own:&lt;br /&gt;
&lt;br /&gt;
* Set of network interfaces&lt;br /&gt;
* Routing table&lt;br /&gt;
* ARP cache&lt;br /&gt;
* Firewall rules&lt;br /&gt;
* Socket listening ports&lt;br /&gt;
&lt;br /&gt;
From the kernel&amp;#039;s perspective, a network namespace is effectively a separate &amp;amp;quot;Virtual Computer&amp;amp;quot; with its own completely independent networking configuration. Processes running inside a namespace cannot see or interact with interfaces or connections in other namespaces (unless explicitly bridged).&lt;br /&gt;
&lt;br /&gt;
Your Linux host runs in the &amp;amp;quot;default&amp;amp;quot; or &amp;amp;quot;root&amp;amp;quot; namespace. When you run &amp;lt;code&amp;gt;ip link show&amp;lt;/code&amp;gt; normally, you see the default namespace&amp;#039;s interfaces. But we can create new namespaces to simulate remote machines for testing, all on a single physical computer.&lt;br /&gt;
&lt;br /&gt;
Network namespaces are the foundation of container networking. When you run a Docker container, Docker creates a new network namespace for it, giving the container its own isolated network stack.&lt;br /&gt;
&lt;br /&gt;
Commands for working with namespaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns add NAME&amp;lt;/code&amp;gt;: Create a new namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns list&amp;lt;/code&amp;gt;: List all namespaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec NAME COMMAND&amp;lt;/code&amp;gt;: Execute a command inside a namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns delete NAME&amp;lt;/code&amp;gt;: Remove a namespace&lt;br /&gt;
&lt;br /&gt;
When you execute a command with &amp;lt;code&amp;gt;ip netns exec client_ns ip link&amp;lt;/code&amp;gt;, you&amp;#039;re running the &amp;lt;code&amp;gt;ip link&amp;lt;/code&amp;gt; command inside the &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; namespace, so it sees only that namespace&amp;#039;s interfaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
In this lab, we will start by building a simple direct connection between two virtual computers, then evolve it step-by-step into a complex switched network with routing and NAT.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-1-network-interfaces-and-virtual-cables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 1: Network Interfaces and Virtual Cables ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-virtual-ethernet-veth-pair&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Virtual Ethernet (veth) Pair ====&lt;br /&gt;
&lt;br /&gt;
Physical networks require network interface cards (NICs) and physical cables. Virtual networks require virtual equivalents. The Linux kernel provides several types of virtual interfaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;veth (Virtual Ethernet) pairs&amp;#039;&amp;#039;&amp;#039;: A veth pair is like a virtual patch cable with two ends. Packets sent into one end immediately come out the other end. veth pairs are the fundamental building block for connecting network namespaces.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;bridges&amp;#039;&amp;#039;&amp;#039;: Virtual switches that connect multiple interfaces together.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;tun/tap devices&amp;#039;&amp;#039;&amp;#039;: Virtual interfaces used by userspace programs (like VPNs).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;vlan devices&amp;#039;&amp;#039;&amp;#039;: Virtual interfaces representing 802.1Q VLAN tags.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we focus on veth pairs. When you create a veth pair, the kernel creates two interfaces that are permanently wired together. You can place these interfaces in different namespaces, effectively running a &amp;amp;quot;cable&amp;amp;quot; between two virtual machines.&lt;br /&gt;
&lt;br /&gt;
Every network interface, physical or virtual, has several properties:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Name&amp;#039;&amp;#039;&amp;#039;: The interface identifier (e.g., &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MAC Address&amp;#039;&amp;#039;&amp;#039;: A Layer 2 hardware address (48 bits, typically written as six hex pairs like &amp;lt;code&amp;gt;aa:bb:cc:dd:ee:ff&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: UP (enabled) or DOWN (disabled)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MTU&amp;#039;&amp;#039;&amp;#039;: Maximum Transmission Unit, the largest packet size the interface can handle&lt;br /&gt;
&lt;br /&gt;
Even virtual interfaces have MAC addresses. The kernel auto-generates them, though you can set them manually if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-creating-your-first-virtual-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Creating Your First Virtual Network ====&lt;br /&gt;
&lt;br /&gt;
We will create two virtual computers: your host (running in the default namespace) and a client (running in a new namespace called &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;). We&amp;#039;ll connect them with a veth pair.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-examine-your-current-network-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Examine Your Current Network Configuration =====&lt;br /&gt;
&lt;br /&gt;
Before we begin constructing virtual networks, let&amp;#039;s see what we&amp;#039;re starting with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see at least two interfaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: The loopback interface (127.0.0.1), used for local communication within the machine&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;ens33&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enp0s3&amp;lt;/code&amp;gt;, etc.): Your primary physical (or virtualized) network interface connected to the outside world&lt;br /&gt;
&lt;br /&gt;
Note the MAC addresses, the state (UP or DOWN), and the MTU (typically 1500 bytes for Ethernet).&lt;br /&gt;
&lt;br /&gt;
Each interface has an &amp;amp;quot;ifindex&amp;amp;quot; number, which is the kernel&amp;#039;s internal identifier. You&amp;#039;ll see output like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000&lt;br /&gt;
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&lt;br /&gt;
2: eth0: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000&lt;br /&gt;
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff&amp;lt;/pre&amp;gt;&lt;br /&gt;
The flags like &amp;lt;code&amp;gt;&amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt;&amp;lt;/code&amp;gt; indicate interface capabilities and state. &amp;lt;code&amp;gt;UP&amp;lt;/code&amp;gt; means the interface is enabled, &amp;lt;code&amp;gt;LOWER_UP&amp;lt;/code&amp;gt; means the link layer is connected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-create-a-virtual-ethernet-cable&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Create a Virtual Ethernet Cable =====&lt;br /&gt;
&lt;br /&gt;
Create a veth pair. This is like manufacturing a network cable with two connectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add dev v-host type veth peer name v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip link add&amp;lt;/code&amp;gt;: Create a new interface&lt;br /&gt;
* &amp;lt;code&amp;gt;dev v-host&amp;lt;/code&amp;gt;: Name one end &amp;amp;quot;v-host&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;type veth&amp;lt;/code&amp;gt;: The interface type is a virtual ethernet pair&lt;br /&gt;
* &amp;lt;code&amp;gt;peer name v-client&amp;lt;/code&amp;gt;: Name the other end &amp;amp;quot;v-client&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Verify the creation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should now see two additional interfaces: &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;. Both will be in state &amp;lt;code&amp;gt;DOWN&amp;lt;/code&amp;gt; initially. Notice that each has been automatically assigned a MAC address by the kernel. These MAC addresses are randomly generated to avoid collisions.&lt;br /&gt;
&lt;br /&gt;
Important: The two interfaces are linked. Packets sent to &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; will appear on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;, and vice versa. This is how we&amp;#039;ll connect our host to the virtual client machine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-create-a-virtual-computer-network-namespace&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Create a Virtual Computer (Network Namespace) =====&lt;br /&gt;
&lt;br /&gt;
Create a new network namespace to simulate a separate computer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add client_ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify it exists:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; in the output.&lt;br /&gt;
&lt;br /&gt;
This namespace is now a completely isolated network environment. It has its own set of interfaces (initially just a loopback), its own routing table, and its own ARP cache.&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s peek inside to see what interfaces exist in the new namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only the loopback interface (&amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;). The &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface we created is still in the default namespace.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-connect-the-virtual-cable&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Connect the Virtual Cable =====&lt;br /&gt;
&lt;br /&gt;
Now we&amp;#039;ll &amp;amp;quot;plug&amp;amp;quot; one end of our virtual cable into the virtual computer. We do this by moving the &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface from the default namespace into &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-client netns client_ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command transfers ownership of the &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface to the &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-verify-the-topology&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Verify the Topology =====&lt;br /&gt;
&lt;br /&gt;
Verify the change in the default namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice that &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; is now &amp;#039;&amp;#039;&amp;#039;gone&amp;#039;&amp;#039;&amp;#039; from the default namespace. It has moved to &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify inside the namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now you should see both &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; inside the namespace.&lt;br /&gt;
&lt;br /&gt;
At this point, we have successfully created a virtual topology:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host&amp;#039;&amp;#039;&amp;#039; (default namespace): Has interface &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Client&amp;#039;&amp;#039;&amp;#039; (client_ns namespace): Has interface &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; are connected by a virtual cable&lt;br /&gt;
&lt;br /&gt;
However, neither interface is UP yet, and neither has an IP address. They cannot communicate yet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 1, provide the output of the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep v-host&lt;br /&gt;
sudo ip netns exec client_ns ip link show | grep v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-2-ip-addresses-and-subnets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 2: IP Addresses and Subnets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-identity-and-reachability&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Identity and Reachability ====&lt;br /&gt;
&lt;br /&gt;
Network interfaces allow computers to physically connect to a network, but to actually communicate, they need identities—IP addresses. An IP address serves two purposes:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Identity&amp;#039;&amp;#039;&amp;#039;: It uniquely identifies a device on a network (like a phone number)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routing&amp;#039;&amp;#039;&amp;#039;: It indicates which network the device belongs to (like an area code)&lt;br /&gt;
&lt;br /&gt;
An IP address by itself is not enough. We also need a subnet mask (or prefix length) that defines the &amp;amp;quot;scope&amp;amp;quot; of the local network—which other IP addresses are directly reachable without routing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-subnet-masks-and-cidr-notation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Understanding Subnet Masks and CIDR Notation =====&lt;br /&gt;
&lt;br /&gt;
A subnet mask divides an IP address into two parts:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network portion&amp;#039;&amp;#039;&amp;#039;: Identifies which network the address belongs to&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host portion&amp;#039;&amp;#039;&amp;#039;: Identifies the specific device within that network&lt;br /&gt;
&lt;br /&gt;
CIDR (Classless Inter-Domain Routing) notation uses a slash followed by the number of bits in the network portion. For example:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.5/24&amp;lt;/code&amp;gt;: The first 24 bits (10.0.0) are the network, leaving 8 bits for hosts (256 addresses: 10.0.0.0 - 10.0.0.255)&lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.10/16&amp;lt;/code&amp;gt;: The first 16 bits (192.168) are the network, leaving 16 bits for hosts (65,536 addresses)&lt;br /&gt;
* &amp;lt;code&amp;gt;172.16.0.1/8&amp;lt;/code&amp;gt;: The first 8 bits (172) are the network, leaving 24 bits for hosts (16,777,216 addresses)&lt;br /&gt;
&lt;br /&gt;
When you assign an IP address with a prefix length (e.g., &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;), the kernel automatically understands that all addresses matching the network portion (10.0.0.x) are &amp;amp;quot;local&amp;amp;quot; or &amp;amp;quot;directly reachable.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-subnet-decision-how-the-kernel-routes-locally&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Subnet Decision: How the Kernel Routes Locally =====&lt;br /&gt;
&lt;br /&gt;
When a process wants to send a packet to a destination IP address, the kernel must decide: &amp;amp;quot;Is this destination a neighbor on one of my local networks, or do I need to forward it to a router?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The kernel&amp;#039;s logic:&lt;br /&gt;
&lt;br /&gt;
# For each interface with an assigned IP address and subnet mask, calculate the network address&lt;br /&gt;
# Check if the destination IP falls within any of these networks&lt;br /&gt;
# If yes → the destination is directly reachable; send the packet out that interface using the destination&amp;#039;s MAC address&lt;br /&gt;
# If no → consult the routing table for a gateway to forward the packet through&lt;br /&gt;
&lt;br /&gt;
Example: Your interface has IP &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Network: &amp;lt;code&amp;gt;10.0.0.0/24&amp;lt;/code&amp;gt; (all addresses from 10.0.0.0 to 10.0.0.255)&lt;br /&gt;
* If you ping &amp;lt;code&amp;gt;10.0.0.50&amp;lt;/code&amp;gt;: The kernel recognizes this is in the local subnet and sends directly&lt;br /&gt;
* If you ping &amp;lt;code&amp;gt;8.8.8.8&amp;lt;/code&amp;gt;: The kernel recognizes this is NOT local and looks for a route to a gateway&lt;br /&gt;
&lt;br /&gt;
This automatic local route is created when you assign an IP address. You don&amp;#039;t need to manually configure routes for local subnets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;static-vs-dynamic-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Static vs. Dynamic Configuration =====&lt;br /&gt;
&lt;br /&gt;
IP addresses can be configured in two ways:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Static&amp;#039;&amp;#039;&amp;#039;: Manually assigned by an administrator using &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic&amp;#039;&amp;#039;&amp;#039;: Automatically assigned by a DHCP server&lt;br /&gt;
&lt;br /&gt;
In production systems, DHCP is common because it allows centralized management of address allocation. In this lab, we use static configuration to understand exactly what the kernel is doing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-assigning-ip-addresses&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Assigning IP Addresses ====&lt;br /&gt;
&lt;br /&gt;
We will assign IP addresses to both ends of our virtual cable, placing them in the same subnet so they can communicate directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-understand-the-current-state&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Understand the Current State =====&lt;br /&gt;
&lt;br /&gt;
Check if any IP addresses are assigned to &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only the MAC address, no IP addresses. The interface is also DOWN (note &amp;lt;code&amp;gt;state DOWN&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Similarly, check the client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Same situation: no IP address, interface DOWN.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-assign-ip-address-to-the-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Assign IP Address to the Client =====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll assign &amp;lt;code&amp;gt;10.0.0.2/24&amp;lt;/code&amp;gt; to the client&amp;#039;s interface. The &amp;lt;code&amp;gt;/24&amp;lt;/code&amp;gt; means the first 24 bits are the network portion, so this device considers all addresses from &amp;lt;code&amp;gt;10.0.0.0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;10.0.0.255&amp;lt;/code&amp;gt; as local neighbors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr add 10.0.0.2/24 dev v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;inet 10.0.0.2/24 scope global v-client&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-bring-the-client-interface-up&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Bring the Client Interface UP =====&lt;br /&gt;
&lt;br /&gt;
Interfaces are created in the DOWN state by default. We must explicitly enable them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link set v-client up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Also bring up the loopback interface (required for many network operations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify the state changed to UP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;state UP&amp;lt;/code&amp;gt; in the output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-assign-ip-address-to-the-host&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Assign IP Address to the Host =====&lt;br /&gt;
&lt;br /&gt;
Now configure the host end of the cable with &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt; (same subnet):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip addr add 10.0.0.1/24 dev v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-host up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-verify-connectivity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Verify Connectivity =====&lt;br /&gt;
&lt;br /&gt;
The moment of truth. Can the host reach the client?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If successful, you&amp;#039;ll see output like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.045 ms&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.038 ms&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.040 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
Congratulations! You&amp;#039;ve established your first virtual network connection. The host and client can now communicate.&lt;br /&gt;
&lt;br /&gt;
Can the client ping the host?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This should also succeed. Communication is bidirectional.&lt;br /&gt;
&lt;br /&gt;
Why Does This Work?&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s trace what happens when you &amp;lt;code&amp;gt;ping 10.0.0.2&amp;lt;/code&amp;gt; from the host:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Destination Analysis&amp;#039;&amp;#039;&amp;#039;: The kernel looks at the destination IP &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; and your interface &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; with IP &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;. It calculates: &amp;amp;quot;10.0.0.2 is in the 10.0.0.0/24 network, which matches my v-host subnet. This is a local destination.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;MAC Address Resolution&amp;#039;&amp;#039;&amp;#039;: The kernel needs the MAC address of 10.0.0.2. It doesn&amp;#039;t know it yet, so it broadcasts an ARP request on &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;: &amp;amp;quot;Who has 10.0.0.2? Please tell 10.0.0.1.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ARP Reply&amp;#039;&amp;#039;&amp;#039;: The client (in client_ns) receives the ARP request on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;, recognizes its own IP, and replies: &amp;amp;quot;10.0.0.2 is at MAC address aa:bb:cc:dd:ee:ff&amp;amp;quot; (the MAC of v-client).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Packet Transmission&amp;#039;&amp;#039;&amp;#039;: Now the host knows the MAC address. It constructs an ICMP Echo Request packet, wraps it in an Ethernet frame addressed to the client&amp;#039;s MAC, and sends it out &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Packet Reception&amp;#039;&amp;#039;&amp;#039;: The packet instantly appears on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; (because they&amp;#039;re a veth pair), travels up the network stack in client_ns, and the kernel processes the ICMP Echo Request.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reply&amp;#039;&amp;#039;&amp;#039;: The client sends an ICMP Echo Reply back to 10.0.0.1, using the same process in reverse.&lt;br /&gt;
&lt;br /&gt;
All of this happens in milliseconds, managed entirely by the kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 2, show the output of the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&lt;br /&gt;
sudo ip netns exec client_ns ip addr show v-client&lt;br /&gt;
ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-3-neighbor-discovery-and-arp&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 3: Neighbor Discovery and ARP ===&lt;br /&gt;
&lt;br /&gt;
So far, we&amp;#039;ve worked with IP addresses , but the physical network uses MAC addresses. How does the kernel translate between them?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;thwory&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Thwory ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-address-resolution-protocol-arp&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Address Resolution Protocol (ARP) =====&lt;br /&gt;
&lt;br /&gt;
ARP is the protocol that maps IP addresses to MAC addresses on Ethernet networks. When the kernel needs to send a packet to an IP address that it knows is on a local network, it must first discover the target&amp;#039;s MAC address.&lt;br /&gt;
&lt;br /&gt;
The ARP process:&lt;br /&gt;
&lt;br /&gt;
# The kernel checks its &amp;#039;&amp;#039;&amp;#039;Neighbor Table&amp;#039;&amp;#039;&amp;#039; (ARP cache) for an existing entry mapping the destination IP to a MAC address&lt;br /&gt;
# If found, use it&lt;br /&gt;
# If not found:&lt;br /&gt;
#* Broadcast an ARP Request packet on the local network: &amp;amp;quot;Who has IP X.X.X.X? Tell Y.Y.Y.Y.&amp;amp;quot;&lt;br /&gt;
#* Wait for an ARP Reply: &amp;amp;quot;IP X.X.X.X is at MAC aa:bb:cc:dd:ee:ff&amp;amp;quot;&lt;br /&gt;
#* Cache this mapping in the Neighbor Table for future use&lt;br /&gt;
&lt;br /&gt;
ARP is a broadcast protocol at Layer 2. Every device on the local network segment receives the ARP request, but only the device with the matching IP address responds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-neighbor-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Neighbor Table =====&lt;br /&gt;
&lt;br /&gt;
The kernel maintains a Neighbor Table (also called the ARP cache) mapping IP addresses to MAC addresses. Entries have states:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;REACHABLE&amp;#039;&amp;#039;&amp;#039;: The entry is valid and recently confirmed&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;STALE&amp;#039;&amp;#039;&amp;#039;: The entry is old but assumed still valid&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DELAY&amp;#039;&amp;#039;&amp;#039;: Awaiting confirmation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;INCOMPLETE&amp;#039;&amp;#039;&amp;#039;: ARP request sent, waiting for reply&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FAILED&amp;#039;&amp;#039;&amp;#039;: ARP request failed, no reply received&lt;br /&gt;
&lt;br /&gt;
Entries automatically expire after a timeout (typically a few minutes of inactivity) to handle cases where devices change MAC addresses or leave the network.&lt;br /&gt;
&lt;br /&gt;
You can view the Neighbor Table with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or the older command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arp -n&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;why-arp-matters-for-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Why ARP Matters for Troubleshooting =====&lt;br /&gt;
&lt;br /&gt;
Many network connectivity issues that seem like &amp;amp;quot;routing problems&amp;amp;quot; are actually ARP problems:&lt;br /&gt;
&lt;br /&gt;
* The kernel can&amp;#039;t deliver packets because it never receives an ARP reply&lt;br /&gt;
* Stale ARP entries point to the wrong MAC address after a device changes&lt;br /&gt;
* ARP conflicts occur when two devices claim the same IP address&lt;br /&gt;
&lt;br /&gt;
Understanding ARP is essential for diagnosing these issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-inspecting-the-arp-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Inspecting the ARP Cache ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-view-current-neighbors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: View Current Neighbors =====&lt;br /&gt;
&lt;br /&gt;
Check your neighbor table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see an entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; (the client) with its MAC address and state &amp;lt;code&amp;gt;REACHABLE&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;10.0.0.2 dev v-host lladdr aa:bb:cc:dd:ee:ff REACHABLE&amp;lt;/pre&amp;gt;&lt;br /&gt;
This entry was created when you pinged the client in Exercise 2. The &amp;amp;quot;lladdr&amp;amp;quot; (link-layer address) is the MAC address.&lt;br /&gt;
&lt;br /&gt;
Also check from the client&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see an entry for &amp;lt;code&amp;gt;10.0.0.1&amp;lt;/code&amp;gt; (the host).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-flush-the-arp-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Flush the ARP Cache =====&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s simulate a situation where the kernel has forgotten the MAC address mappings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip neigh flush all&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify they&amp;#039;re gone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; should be absent or in state &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;INCOMPLETE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-watch-arp-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Watch ARP in Action =====&lt;br /&gt;
&lt;br /&gt;
Now ping the client again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 1 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This ping should succeed. Immediately check the neighbor table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; has been automatically recreated. The kernel performed ARP resolution transparently during the ping.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-arp-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Common ARP Issues =====&lt;br /&gt;
&lt;br /&gt;
When troubleshooting connectivity:&lt;br /&gt;
&lt;br /&gt;
# If &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; shows &amp;amp;quot;Destination Host Unreachable&amp;amp;quot;, check &amp;lt;code&amp;gt;ip neigh&amp;lt;/code&amp;gt;. If the entry is &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;, the remote host isn&amp;#039;t responding to ARP requests (possibly down, wrong subnet, or firewall blocking ARP).&lt;br /&gt;
# If &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; works but other services don&amp;#039;t, ARP isn&amp;#039;t the problem—look at routing or firewall rules.&lt;br /&gt;
# If connectivity is intermittent, check for duplicate IP addresses (two devices responding to the same IP).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 3, provide the output of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&lt;br /&gt;
sudo ip neigh flush all&lt;br /&gt;
ip neigh show  # Should be empty or show failed entries&lt;br /&gt;
ping -c 1 10.0.0.2&lt;br /&gt;
ip neigh show  # Should show REACHABLE entry&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-4-routing-and-gateway-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 4: Routing and Gateway Configuration ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-beyond-the-local-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Beyond the Local Network ====&lt;br /&gt;
&lt;br /&gt;
So far, our host and client can communicate because they&amp;#039;re in the same subnet (10.0.0.0/24). But what happens when you try to reach an IP address that doesn&amp;#039;t match any of your local subnets? For example, how do you reach the internet (like Google&amp;#039;s DNS server at 8.8.8.8)?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-routing-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Routing Table =====&lt;br /&gt;
&lt;br /&gt;
The routing table is a kernel data structure that maps destination networks to interfaces and gateways. When the kernel needs to send a packet, it consults this table to decide where to send it.&lt;br /&gt;
&lt;br /&gt;
Each routing table entry specifies:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Destination network&amp;#039;&amp;#039;&amp;#039;: Which IP addresses this route applies to (e.g., &amp;lt;code&amp;gt;10.0.0.0/24&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;0.0.0.0/0&amp;lt;/code&amp;gt; for default)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gateway&amp;#039;&amp;#039;&amp;#039;: The IP address of the router to forward packets through (or &amp;amp;quot;direct&amp;amp;quot; if no gateway needed)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039;: Which network interface to send packets out&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Metric&amp;#039;&amp;#039;&amp;#039;: Priority when multiple routes match (lower is preferred)&lt;br /&gt;
&lt;br /&gt;
View the routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or the older command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;route -n&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 192.168.1.1 dev eth0 metric 100&lt;br /&gt;
10.0.0.0/24 dev v-host proto kernel scope link src 10.0.0.1&lt;br /&gt;
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s decode this:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;default via 192.168.1.1 dev eth0&amp;lt;/code&amp;gt;: For any destination not matched by more specific routes, forward packets to the gateway at 192.168.1.1 through interface eth0. This is called the &amp;amp;quot;default route&amp;amp;quot; or &amp;amp;quot;default gateway.&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.0/24 dev v-host&amp;lt;/code&amp;gt;: For destinations in 10.0.0.0/24, send directly out v-host (no gateway needed). This route was automatically created when we assigned 10.0.0.1/24 to v-host.&lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.0/24 dev eth0&amp;lt;/code&amp;gt;: Local network, send directly out eth0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-default-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Default Route =====&lt;br /&gt;
&lt;br /&gt;
The default route (destination &amp;lt;code&amp;gt;0.0.0.0/0&amp;lt;/code&amp;gt; or shown as &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt;) is the &amp;amp;quot;route of last resort.&amp;amp;quot; It&amp;#039;s a catch-all that matches any destination not covered by more specific routes. Without a default route, the kernel can only reach directly connected networks.&lt;br /&gt;
&lt;br /&gt;
The gateway specified in the default route must itself be reachable via a local network. You can&amp;#039;t set a gateway to an IP address that the kernel doesn&amp;#039;t know how to reach.&lt;br /&gt;
&lt;br /&gt;
How Routing Decisions Work&lt;br /&gt;
&lt;br /&gt;
When sending a packet to destination IP D:&lt;br /&gt;
&lt;br /&gt;
# The kernel searches the routing table for the most specific match (longest prefix match)&lt;br /&gt;
# If a match is found, use that route&amp;#039;s interface and gateway&lt;br /&gt;
# If no match is found and no default route exists, return &amp;amp;quot;Network is unreachable&amp;amp;quot; error&lt;br /&gt;
&lt;br /&gt;
Example: Routing to 8.8.8.8&lt;br /&gt;
&lt;br /&gt;
* Check local routes: Does 8.8.8.8 match 10.0.0.0/24? No. Does it match 192.168.1.0/24? No.&lt;br /&gt;
* Check default route: Yes, &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt; matches everything&lt;br /&gt;
* Use the default route: Send packet to gateway 192.168.1.1 via eth0&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-address-translation-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Network Address Translation (NAT) =====&lt;br /&gt;
&lt;br /&gt;
Private IP addresses (like 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are not routable on the public internet. If the client (10.0.0.2) wants to reach Google (8.8.8.8), it faces a problem: Google&amp;#039;s servers can&amp;#039;t send replies back to 10.0.0.2 because that address is private and potentially used by millions of devices worldwide.&lt;br /&gt;
&lt;br /&gt;
The solution is NAT (Network Address Translation), specifically a technique called &amp;amp;quot;masquerading&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# The client sends a packet to 8.8.8.8 with source IP 10.0.0.2&lt;br /&gt;
# The packet reaches the host (acting as a router/gateway)&lt;br /&gt;
# The host &amp;#039;&amp;#039;&amp;#039;rewrites&amp;#039;&amp;#039;&amp;#039; the source IP from 10.0.0.2 to its own public IP (e.g., 203.0.113.5)&lt;br /&gt;
# The host forwards the packet to the internet&lt;br /&gt;
# Google replies to 203.0.113.5&lt;br /&gt;
# The host receives the reply, recognizes it belongs to the client&amp;#039;s connection, rewrites the destination IP back to 10.0.0.2, and forwards it to the client&lt;br /&gt;
&lt;br /&gt;
This source IP rewriting is called &amp;amp;quot;masquerading&amp;amp;quot; because the host &amp;amp;quot;masks&amp;amp;quot; the private IP behind its own public IP. The host maintains a connection table to track which internal client made which request.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ip-forwarding&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== IP Forwarding =====&lt;br /&gt;
&lt;br /&gt;
For the host to act as a router, the kernel must be configured to forward packets between interfaces. By default, Linux does not forward (for security reasons). We enable it with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This setting tells the kernel: &amp;amp;quot;If a packet arrives on one interface and is destined for an address reachable via another interface, forward it.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-enabling-internet-access&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Enabling Internet Access ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-the-problem---no-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: The Problem - No Route =====&lt;br /&gt;
&lt;br /&gt;
From the client namespace, try to ping Google&amp;#039;s DNS server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Result: &amp;lt;code&amp;gt;connect: Network is unreachable&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Why? Let&amp;#039;s check the client&amp;#039;s routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;10.0.0.0/24 dev v-client proto kernel scope link src 10.0.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This means the client knows how to reach 10.0.0.0/24, but it has no idea how to reach 8.8.8.8. There&amp;#039;s no default route.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-add-a-default-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Add a Default Route =====&lt;br /&gt;
&lt;br /&gt;
Tell the client to use the host (10.0.0.1) as its default gateway:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should now see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 10.0.0.1 dev v-client&lt;br /&gt;
10.0.0.0/24 dev v-client proto kernel scope link src 10.0.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This tells the kernel: &amp;amp;quot;For any destination I don&amp;#039;t have a specific route for, send it to 10.0.0.1.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-the-problem---no-forwarding&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: The Problem - No Forwarding =====&lt;br /&gt;
&lt;br /&gt;
Try pinging again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It might still fail or timeout. Why? Even though the client sends packets to the host, the host might not be forwarding them. Let&amp;#039;s enable IP forwarding on the host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Should show: &amp;lt;code&amp;gt;net.ipv4.ip_forward = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-the-problem---no-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: The Problem - No NAT =====&lt;br /&gt;
&lt;br /&gt;
Try pinging again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It still might not work. The packets are being forwarded, but they have source IP 10.0.0.2. The internet routers don&amp;#039;t know how to route replies back to private IP addresses. We need NAT.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-configure-nat-masquerade&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Configure NAT (Masquerade) =====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use nftables to configure NAT. First, identify your internet-connected interface (replace &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; if yours is different):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip route show default&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;default via X.X.X.X dev INTERFACE&amp;lt;/code&amp;gt;. Note the interface name (e.g., &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Now configure NAT to masquerade traffic from the 10.0.0.0/24 subnet going out your internet interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important&amp;#039;&amp;#039;&amp;#039;: Replace &amp;lt;code&amp;gt;&amp;amp;quot;eth0&amp;amp;quot;&amp;lt;/code&amp;gt; with your actual internet interface name.&lt;br /&gt;
&lt;br /&gt;
The nftables interface is a complex and powerful kernel tool for firewall and NAT managment. Understanding exactly how this works is beyond the scope of this lab. For now, use the commands aboce &amp;amp;quot;as-is&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Verify the rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft list ruleset&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-6-success&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 6: Success! =====&lt;br /&gt;
&lt;br /&gt;
Now try pinging from the client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Success! The client can now reach the internet.&lt;br /&gt;
&lt;br /&gt;
What Just Happened?&lt;br /&gt;
&lt;br /&gt;
When the client pings 8.8.8.8:&lt;br /&gt;
&lt;br /&gt;
# Client kernel checks routing table, finds default route via 10.0.0.1&lt;br /&gt;
# Client sends packet to 10.0.0.1 (the host)&lt;br /&gt;
# Host receives packet, checks if IP forwarding is enabled (yes)&lt;br /&gt;
# Host checks its routing table for 8.8.8.8, finds default route via internet gateway&lt;br /&gt;
# Host&amp;#039;s nftables NAT rule rewrites source IP from 10.0.0.2 to host&amp;#039;s public IP&lt;br /&gt;
# Host forwards packet to internet gateway&lt;br /&gt;
# Packet reaches 8.8.8.8, reply comes back to host&amp;#039;s public IP&lt;br /&gt;
# Host&amp;#039;s NAT table recognizes this is a reply to client&amp;#039;s connection&lt;br /&gt;
# Host rewrites destination IP from host&amp;#039;s public IP back to 10.0.0.2&lt;br /&gt;
# Host forwards reply to client via v-host interface&lt;br /&gt;
&lt;br /&gt;
All of this happens transparently at wire speed, managed by the kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 4, provide the output of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&lt;br /&gt;
sudo ip netns exec client_ns ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-5-virtual-switches-linux-bridge&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 5: Virtual Switches (Linux Bridge) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-hub-and-spoke-vs-switched-networks&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Hub-and-Spoke vs. Switched Networks ====&lt;br /&gt;
&lt;br /&gt;
So far, we&amp;#039;ve created a simple point-to-point connection using a veth pair. This works for connecting two devices, but what if we want to connect multiple devices? We could create veth pairs between every pair of devices, but that&amp;#039;s not scalable. A three-device network would need 3 pairs, a four-device network would need 6 pairs, and so on.&lt;br /&gt;
&lt;br /&gt;
The traditional solution is a network switch.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;physical-vs-virtual-switches&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Physical vs. Virtual Switches =====&lt;br /&gt;
&lt;br /&gt;
A physical Ethernet switch is a hardware device with multiple ports. When it receives a frame on one port, it examines the destination MAC address and forwards the frame only to the port where that MAC address is located. The switch learns MAC address locations by observing source addresses on incoming frames.&lt;br /&gt;
&lt;br /&gt;
A Linux bridge is a software implementation of a network switch. It&amp;#039;s a virtual interface that you can &amp;amp;quot;plug&amp;amp;quot; other interfaces into. The bridge learns MAC addresses and forwards frames intelligently, just like a physical switch.&lt;br /&gt;
&lt;br /&gt;
Key characteristics of a bridge:&lt;br /&gt;
&lt;br /&gt;
* Operates at the network level (MAC addresses)&lt;br /&gt;
* Supports multiple connected interfaces&lt;br /&gt;
* Learns MAC addresses dynamically&lt;br /&gt;
* Provides broadcast domain for protocols like ARP&lt;br /&gt;
* Transparent to IP protocols&lt;br /&gt;
&lt;br /&gt;
Creating a Switched Topology&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll recreate our network with a proper switched architecture:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    [Host]&lt;br /&gt;
       |&lt;br /&gt;
       | v-host (connected to br-lab)&lt;br /&gt;
       |&lt;br /&gt;
   [br-lab] (bridge/switch)&lt;br /&gt;
       |&lt;br /&gt;
       +--- v-red-br  ~~~ v-red-ns ----&amp;amp;gt; [Red NS]&lt;br /&gt;
       |&lt;br /&gt;
       +--- v-blue-br ~~~ v-blue-ns ---&amp;amp;gt; [Blue NS]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acts as a switch. The host, red namespace, and blue namespace are all &amp;amp;quot;plugged into&amp;amp;quot; different ports of this virtual switch.&lt;br /&gt;
&lt;br /&gt;
Why This Matters&lt;br /&gt;
&lt;br /&gt;
This architecture mirrors real networks:&lt;br /&gt;
&lt;br /&gt;
* Home networks: Your home router has a built-in switch connecting your devices&lt;br /&gt;
* Data centers: Switches connect servers to network backbones&lt;br /&gt;
* Cloud environments: Virtual switches (like Open vSwitch) connect containers and VMs&lt;br /&gt;
* Container orchestration: Docker and Kubernetes use Linux bridges for container networking&lt;br /&gt;
&lt;br /&gt;
Understanding bridges is essential for working with modern virtualization and containerization technologies.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-a-switched-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building a Switched Network ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-clean-up-previous-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Clean Up Previous Configuration =====&lt;br /&gt;
&lt;br /&gt;
Remove the old point-to-point setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns delete client_ns&lt;br /&gt;
sudo ip link delete v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The veth pair is automatically cleaned up when we delete v-host (since they&amp;#039;re paired).&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&lt;br /&gt;
ip link show | grep v-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should show no results.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-create-the-bridge-virtual-switch&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Create the Bridge (Virtual Switch) =====&lt;br /&gt;
&lt;br /&gt;
Create a Linux bridge named &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add name br-lab type bridge&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set br-lab up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see a new interface of type &amp;lt;code&amp;gt;bridge&amp;lt;/code&amp;gt; in state UP.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-create-network-namespaces-for-two-clients&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Create Network Namespaces for Two Clients =====&lt;br /&gt;
&lt;br /&gt;
Create namespaces for &amp;amp;quot;red&amp;amp;quot; and &amp;amp;quot;blue&amp;amp;quot; clients:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red&lt;br /&gt;
sudo ip netns add blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-create-and-connect-red-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Create and Connect Red Client =====&lt;br /&gt;
&lt;br /&gt;
Create a veth pair for the red client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-red-br type veth peer name v-red-ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Connect one end (&amp;lt;code&amp;gt;v-red-br&amp;lt;/code&amp;gt;) to the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-br master br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This &amp;amp;quot;plugs&amp;amp;quot; v-red-br into the switch. The &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; keyword means &amp;amp;quot;this interface is now a port of the bridge.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Move the other end into the red namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-ns netns red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring up the bridge-side interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-br up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Assign IP to red client and bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip addr add 10.0.0.2/24 dev v-red-ns&lt;br /&gt;
sudo ip netns exec red ip link set v-red-ns up&lt;br /&gt;
sudo ip netns exec red ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-create-and-connect-blue-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Create and Connect Blue Client =====&lt;br /&gt;
&lt;br /&gt;
Repeat the same process for blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-blue-br type veth peer name v-blue-ns&lt;br /&gt;
sudo ip link set v-blue-br master br-lab&lt;br /&gt;
sudo ip link set v-blue-ns netns blue&lt;br /&gt;
sudo ip link set v-blue-br up&lt;br /&gt;
sudo ip netns exec blue ip addr add 10.0.0.3/24 dev v-blue-ns&lt;br /&gt;
sudo ip netns exec blue ip link set v-blue-ns up&lt;br /&gt;
sudo ip netns exec blue ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-6-configure-the-host-interface&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 6: Configure the Host Interface =====&lt;br /&gt;
&lt;br /&gt;
The host also needs to be connected to the switch. We&amp;#039;ll assign an IP address directly to the bridge interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip addr add 10.0.0.1/24 dev br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Why does this work? A bridge can have an IP address, making the host itself a participant on the switched network. This is simpler than creating another veth pair.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-7-verify-the-topology&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 7: Verify the Topology =====&lt;br /&gt;
&lt;br /&gt;
Check bridge status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show master br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This shows all interfaces connected to the bridge. You should see &amp;lt;code&amp;gt;v-red-br&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-blue-br&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or use the bridge utility:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;bridge link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check IP addresses:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show br-lab&lt;br /&gt;
sudo ip netns exec red ip addr show v-red-ns&lt;br /&gt;
sudo ip netns exec blue ip addr show v-blue-ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-8-test-connectivity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 8: Test Connectivity =====&lt;br /&gt;
&lt;br /&gt;
Red to Blue (peer-to-peer):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Blue to Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Red to Host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Blue to Host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All of these should succeed. The bridge is forwarding frames between all three participants.&lt;br /&gt;
&lt;br /&gt;
Host to Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Host to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-9-enable-internet-access-for-clients&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 9: Enable Internet Access for Clients =====&lt;br /&gt;
&lt;br /&gt;
Add default routes for both clients:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
sudo ip netns exec blue ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify IP forwarding is still enabled (should be from Exercise 4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not set to 1, enable it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensure NAT is still configured (should be from Exercise 4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft list ruleset | grep masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not present, add it again (replace &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; with your internet interface):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 8.8.8.8&lt;br /&gt;
sudo ip netns exec blue ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both clients can now reach the internet through the host, which acts as both a switch (via the bridge) and a router (via IP forwarding and NAT).&lt;br /&gt;
&lt;br /&gt;
Understanding the Data Flow&lt;br /&gt;
&lt;br /&gt;
When Red (10.0.0.2) pings Blue (10.0.0.3):&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s kernel sees 10.0.0.3 is in the local subnet, sends ARP request&lt;br /&gt;
# ARP request goes out v-red-ns, through the veth pair to v-red-br&lt;br /&gt;
# v-red-br is connected to br-lab (the switch)&lt;br /&gt;
# br-lab broadcasts the ARP request to all connected interfaces (v-blue-br and itself)&lt;br /&gt;
# Blue receives ARP request via v-blue-ns, replies with its MAC&lt;br /&gt;
# br-lab learns Blue&amp;#039;s MAC address is on port v-blue-br&lt;br /&gt;
# Subsequent packets from Red to Blue are forwarded directly to v-blue-br (no broadcast)&lt;br /&gt;
# The bridge maintains a MAC address table, just like a physical switch&lt;br /&gt;
&lt;br /&gt;
When Red (10.0.0.2) pings Google (8.8.8.8):&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s kernel sees 8.8.8.8 is not in the local subnet, consults routing table&lt;br /&gt;
# Default route says to send to gateway 10.0.0.1 (the host)&lt;br /&gt;
# Red uses ARP to find MAC of 10.0.0.1&lt;br /&gt;
# Packet goes through veth pair to br-lab&lt;br /&gt;
# br-lab forwards to its own IP (10.0.0.1 is assigned to br-lab)&lt;br /&gt;
# Host kernel receives packet, sees it&amp;#039;s destined for 8.8.8.8&lt;br /&gt;
# IP forwarding is enabled, so host checks routing table&lt;br /&gt;
# Host finds default route via internet gateway&lt;br /&gt;
# NAT rule rewrites source IP from 10.0.0.2 to host&amp;#039;s public IP&lt;br /&gt;
# Packet forwarded to internet&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Scripting Challenges ===&lt;br /&gt;
&lt;br /&gt;
These challenges test your ability to automate network configuration and extract useful diagnostic information from the kernel&amp;#039;s network subsystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-automated-network-builder&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Challenge 1: Automated Network Builder ====&lt;br /&gt;
&lt;br /&gt;
Write a bash script &amp;lt;code&amp;gt;lab7_builder.sh&amp;lt;/code&amp;gt; that automatically constructs the Red/Blue/Bridge topology from Exercise 5, assigns IP addresses, enables routing and NAT, and provides cleanup functionality.&lt;br /&gt;
&lt;br /&gt;
Requirements:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Script Name &amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;lab7_builder.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Shebang and Best Practices&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Start with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; for error handling&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Arguments&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;: Create and configure the network&lt;br /&gt;
#* &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt;: Clean up all created resources&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Start Operation Must&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Create bridge &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Create namespaces &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Create veth pairs and connect them to the bridge and namespaces&lt;br /&gt;
#* Assign IP addresses:&lt;br /&gt;
#** Host (br-lab): 10.0.0.1/24&lt;br /&gt;
#** Red: 10.0.0.2/24&lt;br /&gt;
#** Blue: 10.0.0.3/24&lt;br /&gt;
#* Bring all interfaces UP&lt;br /&gt;
#* Add default routes for both clients pointing to 10.0.0.1&lt;br /&gt;
#* Enable IP forwarding&lt;br /&gt;
#* Configure NAT/masquerade for 10.0.0.0/24 traffic going out the default internet interface&lt;br /&gt;
#* Print a success message: &amp;amp;quot;Network topology created successfully&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Stop Operation Must&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Delete namespaces (this automatically removes interfaces inside them)&lt;br /&gt;
#* Delete bridge&lt;br /&gt;
#* Flush nftables nat table&lt;br /&gt;
#* Disable IP forwarding (set back to 0)&lt;br /&gt;
#* Print a success message: &amp;amp;quot;Network topology cleaned up successfully&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Testing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x ./lab7_builder.sh&lt;br /&gt;
sudo ./lab7_builder.sh start&lt;br /&gt;
sudo ip netns exec red ping -c 2 10.0.0.3&lt;br /&gt;
sudo ip netns exec red ping -c 2 8.8.8.8&lt;br /&gt;
sudo ./lab7_builder.sh stop&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-challenge-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Deliverable Challenge 1: =====&lt;br /&gt;
&lt;br /&gt;
* Complete script source code with comments&lt;br /&gt;
* Output of running the script with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; argument&lt;br /&gt;
* Output of connectivity tests (red to blue, red to internet)&lt;br /&gt;
* Output of running the script with &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt; argument&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-namespace-inspector&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Challenge 2: Namespace Inspector ====&lt;br /&gt;
&lt;br /&gt;
Write a bash script &amp;lt;code&amp;gt;lab7_inspect.sh&amp;lt;/code&amp;gt; that takes a namespace name as an argument and displays diagnostic information about that namespace&amp;#039;s network configuration.&lt;br /&gt;
&lt;br /&gt;
Requirements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Script Name&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;lab7_inspect.sh&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Shebang and Best Practices&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use functions&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Comment your code&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Arguments&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Takes exactly one argument: the namespace name&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If no argument or more than one argument, print usage and exit with error&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Output Format&amp;#039;&amp;#039;&amp;#039; (exactly as shown):&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;=== Network Inspection for Namespace: &amp;amp;lt;ns_name&amp;amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
[Interfaces]&lt;br /&gt;
&amp;amp;lt;output of ip link show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[IP Addresses]&lt;br /&gt;
&amp;amp;lt;output of ip addr show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Routes]&lt;br /&gt;
&amp;amp;lt;output of ip route show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Default Gateway]&lt;br /&gt;
&amp;amp;lt;extract and show only the default gateway IP, or &amp;amp;quot;None&amp;amp;quot; if not configured&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Neighbors (ARP Cache)]&lt;br /&gt;
&amp;amp;lt;output of ip neigh show&amp;amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Functionality&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Check if namespace exists before attempting to inspect&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;All commands should run inside the specified namespace&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Extract the default gateway IP from the routing table (hint: grep for &amp;amp;quot;default&amp;amp;quot; and extract the IP after &amp;amp;quot;via&amp;amp;quot;)&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testing (after running Challenge 1&amp;#039;s start command):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x ./lab7_inspect.sh&lt;br /&gt;
sudo ./lab7_inspect.sh red&lt;br /&gt;
sudo ./lab7_inspect.sh blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-challenge-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
====== Deliverable Challenge 2: ======&lt;br /&gt;
&lt;br /&gt;
* Complete script source code with comments&lt;br /&gt;
* Output of running the script for the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace (after pinging a few addresses to populate ARP cache)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-network-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Network Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for the commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;interface-management-ip-link&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Interface Management (ip link) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List all interfaces&lt;br /&gt;
ip link show&lt;br /&gt;
&lt;br /&gt;
# Create veth pair&lt;br /&gt;
sudo ip link add dev &amp;lt;name1&amp;gt; type veth peer name &amp;lt;name2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Move interface to namespace&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; netns &amp;lt;namespace&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Bring interface up/down&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; up&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; down&lt;br /&gt;
&lt;br /&gt;
# Delete interface&lt;br /&gt;
sudo ip link delete &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Create bridge&lt;br /&gt;
sudo ip link add name &amp;lt;bridge&amp;gt; type bridge&lt;br /&gt;
&lt;br /&gt;
# Connect interface to bridge&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; master &amp;lt;bridge&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;address-management-ip-addr&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Address Management (ip addr) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show addresses&lt;br /&gt;
ip addr show&lt;br /&gt;
ip addr show &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add address&lt;br /&gt;
sudo ip addr add &amp;lt;ip&amp;gt;/&amp;lt;prefix&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
# Example: sudo ip addr add 10.0.0.1/24 dev eth0&lt;br /&gt;
&lt;br /&gt;
# Remove address&lt;br /&gt;
sudo ip addr del &amp;lt;ip&amp;gt;/&amp;lt;prefix&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Flush all addresses from interface&lt;br /&gt;
sudo ip addr flush dev &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;routing-management-ip-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Routing Management (ip route) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show routing table&lt;br /&gt;
ip route show&lt;br /&gt;
&lt;br /&gt;
# Add route&lt;br /&gt;
sudo ip route add &amp;lt;network&amp;gt;/&amp;lt;prefix&amp;gt; via &amp;lt;gateway&amp;gt;&lt;br /&gt;
# Example: sudo ip route add 192.168.2.0/24 via 192.168.1.1&lt;br /&gt;
&lt;br /&gt;
# Add default route&lt;br /&gt;
sudo ip route add default via &amp;lt;gateway&amp;gt;&lt;br /&gt;
# Example: sudo ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Delete route&lt;br /&gt;
sudo ip route del &amp;lt;network&amp;gt;/&amp;lt;prefix&amp;gt;&lt;br /&gt;
sudo ip route del default&lt;br /&gt;
&lt;br /&gt;
# Flush routing table&lt;br /&gt;
sudo ip route flush table main&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;neighbor-management-ip-neigh&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Neighbor Management (ip neigh) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show ARP cache&lt;br /&gt;
ip neigh show&lt;br /&gt;
&lt;br /&gt;
# Flush ARP cache&lt;br /&gt;
sudo ip neigh flush all&lt;br /&gt;
&lt;br /&gt;
# Add static ARP entry&lt;br /&gt;
sudo ip neigh add &amp;lt;ip&amp;gt; lladdr &amp;lt;mac&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete ARP entry&lt;br /&gt;
sudo ip neigh del &amp;lt;ip&amp;gt; dev &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;namespace-management-ip-netns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Namespace Management (ip netns) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List namespaces&lt;br /&gt;
ip netns list&lt;br /&gt;
&lt;br /&gt;
# Create namespace&lt;br /&gt;
sudo ip netns add &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete namespace&lt;br /&gt;
sudo ip netns delete &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Execute command in namespace&lt;br /&gt;
sudo ip netns exec &amp;lt;name&amp;gt; &amp;lt;command&amp;gt;&lt;br /&gt;
# Example: sudo ip netns exec red ping 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Get shell in namespace&lt;br /&gt;
sudo ip netns exec &amp;lt;name&amp;gt; bash&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bridge-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Bridge Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show bridge details&lt;br /&gt;
bridge link show&lt;br /&gt;
bridge fdb show  # Show MAC address table&lt;br /&gt;
&lt;br /&gt;
# Show which interfaces are part of a bridge&lt;br /&gt;
ip link show master &amp;lt;bridge&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ip-forwarding-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== IP Forwarding ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check status&lt;br /&gt;
sysctl net.ipv4.ip_forward&lt;br /&gt;
&lt;br /&gt;
# Enable (temporary)&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Disable&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=0&lt;br /&gt;
&lt;br /&gt;
# Enable permanently (edit /etc/sysctl.conf)&lt;br /&gt;
echo &amp;quot;net.ipv4.ip_forward=1&amp;quot; | sudo tee -a /etc/sysctl.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;nat-with-nftables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== NAT with nftables ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List current ruleset&lt;br /&gt;
sudo nft list ruleset&lt;br /&gt;
&lt;br /&gt;
# Create NAT table and rules&lt;br /&gt;
sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&lt;br /&gt;
&lt;br /&gt;
# Delete NAT table&lt;br /&gt;
sudo nft delete table ip nat&lt;br /&gt;
&lt;br /&gt;
# Flush ruleset&lt;br /&gt;
sudo nft flush ruleset&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;diagnostic-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Diagnostic Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test connectivity&lt;br /&gt;
ping -c 3 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trace route&lt;br /&gt;
traceroute &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show listening sockets&lt;br /&gt;
ss -tuln&lt;br /&gt;
&lt;br /&gt;
# Monitor traffic&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n arp  # Show only ARP traffic&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n icmp # Show only ping traffic&lt;br /&gt;
&lt;br /&gt;
# Show interface statistics&lt;br /&gt;
ip -s link show &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-network-topology-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Network Topology Patterns ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;point-to-point-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Point-to-Point Connection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Host] &amp;amp;lt;---veth pair---&amp;amp;gt; [Namespace]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Simple container isolation, testing&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;switched-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Switched Network ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Bridge]&lt;br /&gt;
            |&lt;br /&gt;
    +-------+-------+&lt;br /&gt;
    |       |       |&lt;br /&gt;
  [Host] [NS1]   [NS2]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Multiple containers/VMs on same network&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;routed-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Routed Network ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Internet] &amp;amp;lt;---&amp;amp;gt; [Host/Router] &amp;amp;lt;---&amp;amp;gt; [Bridge] &amp;amp;lt;---&amp;amp;gt; [Containers]&lt;br /&gt;
                     (NAT)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Containers with internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;summary-table-network-components&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Summary Table: Network Components ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Component&lt;br /&gt;
! Purpose&lt;br /&gt;
! Layer&lt;br /&gt;
! Key Commands&lt;br /&gt;
|-&lt;br /&gt;
| veth pair&lt;br /&gt;
| Virtual cable connecting two interfaces&lt;br /&gt;
| L2&lt;br /&gt;
| &amp;lt;code&amp;gt;ip link add type veth&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| bridge&lt;br /&gt;
| Virtual switch&lt;br /&gt;
| L2&lt;br /&gt;
| &amp;lt;code&amp;gt;ip link add type bridge&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| IP address&lt;br /&gt;
| Device identity and subnet membership&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Route&lt;br /&gt;
| Path determination&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip route add&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ARP/Neighbor&lt;br /&gt;
| IP-to-MAC mapping&lt;br /&gt;
| L2/L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip neigh show&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| NAT&lt;br /&gt;
| Private-to-public IP translation&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;nft add rule masquerade&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Namespace&lt;br /&gt;
| Isolated network stack&lt;br /&gt;
| All&lt;br /&gt;
| &amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing:&lt;br /&gt;
&lt;br /&gt;
The deliverables A-D and solutions to the challanges 1 and 2.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== For further study: ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advanced Topics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* VLANs and 802.1Q tagging&lt;br /&gt;
* Open vSwitch (OVS) for advanced switching&lt;br /&gt;
* Network policies and firewalling with nftables&lt;br /&gt;
* Quality of Service (QoS) and traffic shaping&lt;br /&gt;
* IPv6 configuration and dual-stack networking&lt;br /&gt;
* VPN setup with WireGuard or OpenVPN&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Networking&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Docker networking modes (bridge, host, overlay)&lt;br /&gt;
* Kubernetes networking model and CNI plugins&lt;br /&gt;
* Service meshes (Istio, Linkerd)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance and Monitoring&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network performance testing with iperf&lt;br /&gt;
* Packet capture and analysis with Wireshark&lt;br /&gt;
* Continuous monitoring with Prometheus and Grafana&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network segmentation and isolation&lt;br /&gt;
* Firewall rule design&lt;br /&gt;
* Intrusion detection systems&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 8 ip          # iproute2 command reference&lt;br /&gt;
man 8 ip-link     # Interface management&lt;br /&gt;
man 8 ip-address  # Address management&lt;br /&gt;
man 8 ip-route    # Routing management&lt;br /&gt;
man 8 ip-netns    # Namespace management&lt;br /&gt;
man 8 bridge      # Bridge management&lt;br /&gt;
man 7 netdevice   # Network device overview&lt;br /&gt;
man 7 arp         # ARP protocol&lt;br /&gt;
man 5 nft         # nftables syntax&lt;br /&gt;
man 8 tcpdump     # Packet capture&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://lartc.org/ Linux Advanced Routing &amp;amp;amp; Traffic Control HOWTO]&lt;br /&gt;
* [https://wiki.linuxfoundation.org/networking/iproute2 iproute2 documentation]&lt;br /&gt;
* [https://www.kernel.org/doc/html/latest/networking/ The Linux Kernel Networking Stack]&lt;br /&gt;
* [https://wiki.nftables.org/ nftables Wiki]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_7_-_The_Network_Subsystem&amp;diff=8180</id>
		<title>OS Lab 7 - The Network Subsystem</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_7_-_The_Network_Subsystem&amp;diff=8180"/>
		<updated>2025-11-21T14:49:06Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain how the kernel represents network abstractions: Interfaces, Addresses,...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel represents network abstractions: Interfaces, Addresses, Routes, and Neighbors.&lt;br /&gt;
* Use the modern iproute2 suite (&amp;lt;code&amp;gt;ip link&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip addr&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip route&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ip neigh&amp;lt;/code&amp;gt;) to manage kernel network objects.&lt;br /&gt;
* Create isolated network environments using Network Namespaces to simulate multiple virtual computers.&lt;br /&gt;
* Construct a complete virtual network topology (Switch, Router, NAT) from scratch using OS primitives.&lt;br /&gt;
* Diagnose connectivity issues by inspecting the ARP cache and Routing table to understand packet delivery.&lt;br /&gt;
* Understand the relationship between Layer 2 (MAC addresses) and Layer 3 (IP addresses) networking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 6, we explored Inter-Process Communication (IPC) mechanisms that allow processes to exchange data and coordinate their actions. We touched briefly on Sockets, which extend IPC beyond a single machine by enabling communication across network boundaries. However, using sockets effectively requires understanding what happens beneath the abstraction: how does data actually travel from one machine to another?&lt;br /&gt;
&lt;br /&gt;
The answer lies in the kernel&amp;#039;s network stack, a complex subsystem responsible for managing network interfaces, addressing, routing, and protocol handling. Usually, the network stack is configured automatically by background services like NetworkManager, systemd-networkd, or DHCP clients. These tools shield users from complexity, but they also obscure the fundamental mechanisms at work.&lt;br /&gt;
&lt;br /&gt;
In this lab, we peel back those layers to interact directly with the kernel&amp;#039;s networking subsystem. We will not use physical network cables or hardware switches. Instead, we will use the operating system itself to manufacture virtual hardware—virtual network cards, virtual cables, and virtual switches. By constructing networks programmatically, we gain deep insight into how the kernel manages connectivity, routing, and address resolution.&lt;br /&gt;
&lt;br /&gt;
This lab is structured around a progression: we start with a simple point-to-point connection between two virtual machines, then evolve it into a switched network with multiple clients, routing capabilities, and internet access via Network Address Translation (NAT). Along the way, we examine the kernel&amp;#039;s internal state at each step to understand exactly how packets flow through the system.&lt;br /&gt;
&lt;br /&gt;
Understanding the network subsystem is essential for system administration, DevOps, container orchestration (Docker, Kubernetes), network troubleshooting, and security. These same primitives underlie modern cloud networking, virtual private networks, and service meshes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of Linux machine with root privileges (via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y iproute2 iputils-ping traceroute nftables&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt;: The modern suite for network configuration, replacing legacy tools like &amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt;. Provides &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command.&lt;br /&gt;
* &amp;lt;code&amp;gt;iputils-ping&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; utility for testing connectivity.&lt;br /&gt;
* &amp;lt;code&amp;gt;traceroute&amp;lt;/code&amp;gt;: Maps the path packets take through networks.&lt;br /&gt;
* &amp;lt;code&amp;gt;nftables&amp;lt;/code&amp;gt;: The modern Linux firewall/NAT framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Knowledge Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, process hierarchy)&lt;br /&gt;
* File permissions from Lab 4 (execute bit, ownership)&lt;br /&gt;
* Bash scripting from Lab 5 (shebangs, variables, loops, functions)&lt;br /&gt;
* IPC concepts from Lab 6 (understanding of how processes communicate)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-network-subsystem-kernel-fundamentals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== The Network Subsystem: Kernel Fundamentals ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-a-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== What is a Network? ====&lt;br /&gt;
&lt;br /&gt;
To the Linux kernel, a &amp;amp;quot;Network&amp;amp;quot; is not a physical thing—it&amp;#039;s an abstraction representing a group of computers that can communicate with each other directly, without requiring a router as an intermediary. This direct communication capability is what defines a network segment or broadcast domain.&lt;br /&gt;
&lt;br /&gt;
A single computer can participate in multiple networks simultaneously. It simply needs a separate Network Interface for each network it wants to join. Think of interfaces as &amp;amp;quot;plugs&amp;amp;quot; or &amp;amp;quot;sockets&amp;amp;quot; that connect your computer to different communication channels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-four-pillars-of-networking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Four Pillars of Networking ====&lt;br /&gt;
&lt;br /&gt;
The kernel&amp;#039;s network subsystem is built on four fundamental abstractions. Understanding these is key to mastering network configuration:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Interfaces&amp;#039;&amp;#039;&amp;#039;: The physical or virtual network adapters that can send and receive packets. Each interface represents a connection point to a network.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Addresses&amp;#039;&amp;#039;&amp;#039;: IP addresses assigned to interfaces, giving them identities on the network. Addresses define &amp;amp;quot;who&amp;amp;quot; you are.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routes&amp;#039;&amp;#039;&amp;#039;: Kernel rules that determine which interface should be used to reach a given destination. Routes define &amp;amp;quot;how to get there.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Neighbors&amp;#039;&amp;#039;&amp;#039;: The kernel&amp;#039;s cache mapping IP addresses to MAC addresses for direct communication. Neighbors define &amp;amp;quot;where exactly on this wire.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
These four abstractions work together. When you send a packet to an IP address:&lt;br /&gt;
&lt;br /&gt;
# The kernel consults the &amp;#039;&amp;#039;&amp;#039;Routing Table&amp;#039;&amp;#039;&amp;#039; to determine which &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039; to use.&lt;br /&gt;
# If the route indicates the destination is local (directly reachable), the kernel consults the &amp;#039;&amp;#039;&amp;#039;Neighbor Table&amp;#039;&amp;#039;&amp;#039; to find the MAC address.&lt;br /&gt;
# If the MAC address is unknown, the kernel uses ARP (Address Resolution Protocol) to discover it.&lt;br /&gt;
# The kernel constructs an Ethernet frame with the destination MAC address and sends it out the chosen &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-iproute2-suite-modern-network-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The iproute2 Suite: Modern Network Management ====&lt;br /&gt;
&lt;br /&gt;
Historically, Linux used separate commands for each aspect of networking: &amp;lt;code&amp;gt;ifconfig&amp;lt;/code&amp;gt; for interfaces, &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt; for routing, &amp;lt;code&amp;gt;arp&amp;lt;/code&amp;gt; for neighbors. These tools are now considered legacy. Modern Linux uses the unified &amp;lt;code&amp;gt;iproute2&amp;lt;/code&amp;gt; suite, centered around the &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; command uses a consistent syntax:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip OBJECT COMMAND [OPTIONS]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Where OBJECT is one of:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;link&amp;lt;/code&amp;gt;: Network interfaces (Layer 2)&lt;br /&gt;
* &amp;lt;code&amp;gt;addr&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;address&amp;lt;/code&amp;gt;: IP addresses (Layer 3)&lt;br /&gt;
* &amp;lt;code&amp;gt;route&amp;lt;/code&amp;gt;: Routing table entries&lt;br /&gt;
* &amp;lt;code&amp;gt;neigh&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;neighbour&amp;lt;/code&amp;gt;: ARP/Neighbor cache&lt;br /&gt;
&lt;br /&gt;
Common commands:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip link show&amp;lt;/code&amp;gt;: List all network interfaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip addr show&amp;lt;/code&amp;gt;: Show IP addresses assigned to interfaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip route show&amp;lt;/code&amp;gt;: Display the routing table&lt;br /&gt;
* &amp;lt;code&amp;gt;ip neigh show&amp;lt;/code&amp;gt;: Display the neighbor (ARP) cache&lt;br /&gt;
&lt;br /&gt;
The beauty of &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; is that it provides a consistent, scriptable interface to the kernel&amp;#039;s network state. Unlike older tools, &amp;lt;code&amp;gt;ip&amp;lt;/code&amp;gt; is designed for automation and parsing by scripts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-namespaces-virtual-computers&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Network Namespaces: Virtual Computers ====&lt;br /&gt;
&lt;br /&gt;
A Network Namespace (often abbreviated &amp;amp;quot;netns&amp;amp;quot;) is a kernel feature that creates an isolated copy of the network stack. Each namespace has its own:&lt;br /&gt;
&lt;br /&gt;
* Set of network interfaces&lt;br /&gt;
* Routing table&lt;br /&gt;
* ARP cache&lt;br /&gt;
* Firewall rules&lt;br /&gt;
* Socket listening ports&lt;br /&gt;
&lt;br /&gt;
From the kernel&amp;#039;s perspective, a network namespace is effectively a separate &amp;amp;quot;Virtual Computer&amp;amp;quot; with its own completely independent networking configuration. Processes running inside a namespace cannot see or interact with interfaces or connections in other namespaces (unless explicitly bridged).&lt;br /&gt;
&lt;br /&gt;
Your Linux host runs in the &amp;amp;quot;default&amp;amp;quot; or &amp;amp;quot;root&amp;amp;quot; namespace. When you run &amp;lt;code&amp;gt;ip link show&amp;lt;/code&amp;gt; normally, you see the default namespace&amp;#039;s interfaces. But we can create new namespaces to simulate remote machines for testing, all on a single physical computer.&lt;br /&gt;
&lt;br /&gt;
Network namespaces are the foundation of container networking. When you run a Docker container, Docker creates a new network namespace for it, giving the container its own isolated network stack.&lt;br /&gt;
&lt;br /&gt;
Commands for working with namespaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns add NAME&amp;lt;/code&amp;gt;: Create a new namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns list&amp;lt;/code&amp;gt;: List all namespaces&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns exec NAME COMMAND&amp;lt;/code&amp;gt;: Execute a command inside a namespace&lt;br /&gt;
* &amp;lt;code&amp;gt;ip netns delete NAME&amp;lt;/code&amp;gt;: Remove a namespace&lt;br /&gt;
&lt;br /&gt;
When you execute a command with &amp;lt;code&amp;gt;ip netns exec client_ns ip link&amp;lt;/code&amp;gt;, you&amp;#039;re running the &amp;lt;code&amp;gt;ip link&amp;lt;/code&amp;gt; command inside the &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; namespace, so it sees only that namespace&amp;#039;s interfaces.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
In this lab, we will start by building a simple direct connection between two virtual computers, then evolve it step-by-step into a complex switched network with routing and NAT.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-1-network-interfaces-and-virtual-cables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 1: Network Interfaces and Virtual Cables ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-the-virtual-ethernet-veth-pair&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: The Virtual Ethernet (veth) Pair ====&lt;br /&gt;
&lt;br /&gt;
Physical networks require network interface cards (NICs) and physical cables. Virtual networks require virtual equivalents. The Linux kernel provides several types of virtual interfaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;veth (Virtual Ethernet) pairs&amp;#039;&amp;#039;&amp;#039;: A veth pair is like a virtual patch cable with two ends. Packets sent into one end immediately come out the other end. veth pairs are the fundamental building block for connecting network namespaces.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;bridges&amp;#039;&amp;#039;&amp;#039;: Virtual switches that connect multiple interfaces together.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;tun/tap devices&amp;#039;&amp;#039;&amp;#039;: Virtual interfaces used by userspace programs (like VPNs).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;vlan devices&amp;#039;&amp;#039;&amp;#039;: Virtual interfaces representing 802.1Q VLAN tags.&lt;br /&gt;
&lt;br /&gt;
In this exercise, we focus on veth pairs. When you create a veth pair, the kernel creates two interfaces that are permanently wired together. You can place these interfaces in different namespaces, effectively running a &amp;amp;quot;cable&amp;amp;quot; between two virtual machines.&lt;br /&gt;
&lt;br /&gt;
Every network interface, physical or virtual, has several properties:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Name&amp;#039;&amp;#039;&amp;#039;: The interface identifier (e.g., &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MAC Address&amp;#039;&amp;#039;&amp;#039;: A Layer 2 hardware address (48 bits, typically written as six hex pairs like &amp;lt;code&amp;gt;aa:bb:cc:dd:ee:ff&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;State&amp;#039;&amp;#039;&amp;#039;: UP (enabled) or DOWN (disabled)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MTU&amp;#039;&amp;#039;&amp;#039;: Maximum Transmission Unit, the largest packet size the interface can handle&lt;br /&gt;
&lt;br /&gt;
Even virtual interfaces have MAC addresses. The kernel auto-generates them, though you can set them manually if needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-creating-your-first-virtual-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Creating Your First Virtual Network ====&lt;br /&gt;
&lt;br /&gt;
We will create two virtual computers: your host (running in the default namespace) and a client (running in a new namespace called &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;). We&amp;#039;ll connect them with a veth pair.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-examine-your-current-network-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Examine Your Current Network Configuration =====&lt;br /&gt;
&lt;br /&gt;
Before we begin constructing virtual networks, let&amp;#039;s see what we&amp;#039;re starting with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see at least two interfaces:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;: The loopback interface (127.0.0.1), used for local communication within the machine&lt;br /&gt;
* &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;ens33&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;enp0s3&amp;lt;/code&amp;gt;, etc.): Your primary physical (or virtualized) network interface connected to the outside world&lt;br /&gt;
&lt;br /&gt;
Note the MAC addresses, the state (UP or DOWN), and the MTU (typically 1500 bytes for Ethernet).&lt;br /&gt;
&lt;br /&gt;
Each interface has an &amp;amp;quot;ifindex&amp;amp;quot; number, which is the kernel&amp;#039;s internal identifier. You&amp;#039;ll see output like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;1: lo: &amp;amp;lt;LOOPBACK,UP,LOWER_UP&amp;amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000&lt;br /&gt;
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00&lt;br /&gt;
2: eth0: &amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt; mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000&lt;br /&gt;
    link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff&amp;lt;/pre&amp;gt;&lt;br /&gt;
The flags like &amp;lt;code&amp;gt;&amp;amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;amp;gt;&amp;lt;/code&amp;gt; indicate interface capabilities and state. &amp;lt;code&amp;gt;UP&amp;lt;/code&amp;gt; means the interface is enabled, &amp;lt;code&amp;gt;LOWER_UP&amp;lt;/code&amp;gt; means the link layer is connected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-create-a-virtual-ethernet-cable&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Create a Virtual Ethernet Cable =====&lt;br /&gt;
&lt;br /&gt;
Create a veth pair. This is like manufacturing a network cable with two connectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add dev v-host type veth peer name v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Let&amp;#039;s break down this command:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ip link add&amp;lt;/code&amp;gt;: Create a new interface&lt;br /&gt;
* &amp;lt;code&amp;gt;dev v-host&amp;lt;/code&amp;gt;: Name one end &amp;amp;quot;v-host&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;type veth&amp;lt;/code&amp;gt;: The interface type is a virtual ethernet pair&lt;br /&gt;
* &amp;lt;code&amp;gt;peer name v-client&amp;lt;/code&amp;gt;: Name the other end &amp;amp;quot;v-client&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Verify the creation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should now see two additional interfaces: &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;. Both will be in state &amp;lt;code&amp;gt;DOWN&amp;lt;/code&amp;gt; initially. Notice that each has been automatically assigned a MAC address by the kernel. These MAC addresses are randomly generated to avoid collisions.&lt;br /&gt;
&lt;br /&gt;
Important: The two interfaces are linked. Packets sent to &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; will appear on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;, and vice versa. This is how we&amp;#039;ll connect our host to the virtual client machine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-create-a-virtual-computer-network-namespace&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Create a Virtual Computer (Network Namespace) =====&lt;br /&gt;
&lt;br /&gt;
Create a new network namespace to simulate a separate computer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add client_ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify it exists:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; in the output.&lt;br /&gt;
&lt;br /&gt;
This namespace is now a completely isolated network environment. It has its own set of interfaces (initially just a loopback), its own routing table, and its own ARP cache.&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s peek inside to see what interfaces exist in the new namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only the loopback interface (&amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt;). The &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface we created is still in the default namespace.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-connect-the-virtual-cable&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Connect the Virtual Cable =====&lt;br /&gt;
&lt;br /&gt;
Now we&amp;#039;ll &amp;amp;quot;plug&amp;amp;quot; one end of our virtual cable into the virtual computer. We do this by moving the &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface from the default namespace into &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-client netns client_ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command transfers ownership of the &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; interface to the &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt; namespace.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-verify-the-topology&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Verify the Topology =====&lt;br /&gt;
&lt;br /&gt;
Verify the change in the default namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice that &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; is now &amp;#039;&amp;#039;&amp;#039;gone&amp;#039;&amp;#039;&amp;#039; from the default namespace. It has moved to &amp;lt;code&amp;gt;client_ns&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Verify inside the namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now you should see both &amp;lt;code&amp;gt;lo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; inside the namespace.&lt;br /&gt;
&lt;br /&gt;
At this point, we have successfully created a virtual topology:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host&amp;#039;&amp;#039;&amp;#039; (default namespace): Has interface &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Client&amp;#039;&amp;#039;&amp;#039; (client_ns namespace): Has interface &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Connection&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; are connected by a virtual cable&lt;br /&gt;
&lt;br /&gt;
However, neither interface is UP yet, and neither has an IP address. They cannot communicate yet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-a&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable A ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 1, provide the output of the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show | grep v-host&lt;br /&gt;
sudo ip netns exec client_ns ip link show | grep v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-2-ip-addresses-and-subnets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 2: IP Addresses and Subnets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-identity-and-reachability&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Identity and Reachability ====&lt;br /&gt;
&lt;br /&gt;
Network interfaces allow computers to physically connect to a network, but to actually communicate, they need identities—IP addresses. An IP address serves two purposes:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Identity&amp;#039;&amp;#039;&amp;#039;: It uniquely identifies a device on a network (like a phone number)&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Routing&amp;#039;&amp;#039;&amp;#039;: It indicates which network the device belongs to (like an area code)&lt;br /&gt;
&lt;br /&gt;
An IP address by itself is not enough. We also need a subnet mask (or prefix length) that defines the &amp;amp;quot;scope&amp;amp;quot; of the local network—which other IP addresses are directly reachable without routing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;understanding-subnet-masks-and-cidr-notation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Understanding Subnet Masks and CIDR Notation =====&lt;br /&gt;
&lt;br /&gt;
A subnet mask divides an IP address into two parts:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Network portion&amp;#039;&amp;#039;&amp;#039;: Identifies which network the address belongs to&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Host portion&amp;#039;&amp;#039;&amp;#039;: Identifies the specific device within that network&lt;br /&gt;
&lt;br /&gt;
CIDR (Classless Inter-Domain Routing) notation uses a slash followed by the number of bits in the network portion. For example:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.5/24&amp;lt;/code&amp;gt;: The first 24 bits (10.0.0) are the network, leaving 8 bits for hosts (256 addresses: 10.0.0.0 - 10.0.0.255)&lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.10/16&amp;lt;/code&amp;gt;: The first 16 bits (192.168) are the network, leaving 16 bits for hosts (65,536 addresses)&lt;br /&gt;
* &amp;lt;code&amp;gt;172.16.0.1/8&amp;lt;/code&amp;gt;: The first 8 bits (172) are the network, leaving 24 bits for hosts (16,777,216 addresses)&lt;br /&gt;
&lt;br /&gt;
When you assign an IP address with a prefix length (e.g., &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;), the kernel automatically understands that all addresses matching the network portion (10.0.0.x) are &amp;amp;quot;local&amp;amp;quot; or &amp;amp;quot;directly reachable.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-subnet-decision-how-the-kernel-routes-locally&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Subnet Decision: How the Kernel Routes Locally =====&lt;br /&gt;
&lt;br /&gt;
When a process wants to send a packet to a destination IP address, the kernel must decide: &amp;amp;quot;Is this destination a neighbor on one of my local networks, or do I need to forward it to a router?&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
The kernel&amp;#039;s logic:&lt;br /&gt;
&lt;br /&gt;
# For each interface with an assigned IP address and subnet mask, calculate the network address&lt;br /&gt;
# Check if the destination IP falls within any of these networks&lt;br /&gt;
# If yes → the destination is directly reachable; send the packet out that interface using the destination&amp;#039;s MAC address&lt;br /&gt;
# If no → consult the routing table for a gateway to forward the packet through&lt;br /&gt;
&lt;br /&gt;
Example: Your interface has IP &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Network: &amp;lt;code&amp;gt;10.0.0.0/24&amp;lt;/code&amp;gt; (all addresses from 10.0.0.0 to 10.0.0.255)&lt;br /&gt;
* If you ping &amp;lt;code&amp;gt;10.0.0.50&amp;lt;/code&amp;gt;: The kernel recognizes this is in the local subnet and sends directly&lt;br /&gt;
* If you ping &amp;lt;code&amp;gt;8.8.8.8&amp;lt;/code&amp;gt;: The kernel recognizes this is NOT local and looks for a route to a gateway&lt;br /&gt;
&lt;br /&gt;
This automatic local route is created when you assign an IP address. You don&amp;#039;t need to manually configure routes for local subnets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;static-vs-dynamic-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Static vs. Dynamic Configuration =====&lt;br /&gt;
&lt;br /&gt;
IP addresses can be configured in two ways:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Static&amp;#039;&amp;#039;&amp;#039;: Manually assigned by an administrator using &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Dynamic&amp;#039;&amp;#039;&amp;#039;: Automatically assigned by a DHCP server&lt;br /&gt;
&lt;br /&gt;
In production systems, DHCP is common because it allows centralized management of address allocation. In this lab, we use static configuration to understand exactly what the kernel is doing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-assigning-ip-addresses&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Assigning IP Addresses ====&lt;br /&gt;
&lt;br /&gt;
We will assign IP addresses to both ends of our virtual cable, placing them in the same subnet so they can communicate directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-understand-the-current-state&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Understand the Current State =====&lt;br /&gt;
&lt;br /&gt;
Check if any IP addresses are assigned to &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only the MAC address, no IP addresses. The interface is also DOWN (note &amp;lt;code&amp;gt;state DOWN&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Similarly, check the client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Same situation: no IP address, interface DOWN.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-assign-ip-address-to-the-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Assign IP Address to the Client =====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll assign &amp;lt;code&amp;gt;10.0.0.2/24&amp;lt;/code&amp;gt; to the client&amp;#039;s interface. The &amp;lt;code&amp;gt;/24&amp;lt;/code&amp;gt; means the first 24 bits are the network portion, so this device considers all addresses from &amp;lt;code&amp;gt;10.0.0.0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;10.0.0.255&amp;lt;/code&amp;gt; as local neighbors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr add 10.0.0.2/24 dev v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip addr show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;inet 10.0.0.2/24 scope global v-client&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-bring-the-client-interface-up&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Bring the Client Interface UP =====&lt;br /&gt;
&lt;br /&gt;
Interfaces are created in the DOWN state by default. We must explicitly enable them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link set v-client up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Also bring up the loopback interface (required for many network operations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify the state changed to UP:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip link show v-client&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;state UP&amp;lt;/code&amp;gt; in the output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-assign-ip-address-to-the-host&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Assign IP Address to the Host =====&lt;br /&gt;
&lt;br /&gt;
Now configure the host end of the cable with &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt; (same subnet):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip addr add 10.0.0.1/24 dev v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-host up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-verify-connectivity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Verify Connectivity =====&lt;br /&gt;
&lt;br /&gt;
The moment of truth. Can the host reach the client?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If successful, you&amp;#039;ll see output like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.045 ms&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.038 ms&lt;br /&gt;
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.040 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
Congratulations! You&amp;#039;ve established your first virtual network connection. The host and client can now communicate.&lt;br /&gt;
&lt;br /&gt;
Can the client ping the host?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This should also succeed. Communication is bidirectional.&lt;br /&gt;
&lt;br /&gt;
Why Does This Work?&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s trace what happens when you &amp;lt;code&amp;gt;ping 10.0.0.2&amp;lt;/code&amp;gt; from the host:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Destination Analysis&amp;#039;&amp;#039;&amp;#039;: The kernel looks at the destination IP &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; and your interface &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt; with IP &amp;lt;code&amp;gt;10.0.0.1/24&amp;lt;/code&amp;gt;. It calculates: &amp;amp;quot;10.0.0.2 is in the 10.0.0.0/24 network, which matches my v-host subnet. This is a local destination.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;MAC Address Resolution&amp;#039;&amp;#039;&amp;#039;: The kernel needs the MAC address of 10.0.0.2. It doesn&amp;#039;t know it yet, so it broadcasts an ARP request on &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;: &amp;amp;quot;Who has 10.0.0.2? Please tell 10.0.0.1.&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;ARP Reply&amp;#039;&amp;#039;&amp;#039;: The client (in client_ns) receives the ARP request on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt;, recognizes its own IP, and replies: &amp;amp;quot;10.0.0.2 is at MAC address aa:bb:cc:dd:ee:ff&amp;amp;quot; (the MAC of v-client).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Packet Transmission&amp;#039;&amp;#039;&amp;#039;: Now the host knows the MAC address. It constructs an ICMP Echo Request packet, wraps it in an Ethernet frame addressed to the client&amp;#039;s MAC, and sends it out &amp;lt;code&amp;gt;v-host&amp;lt;/code&amp;gt;.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Packet Reception&amp;#039;&amp;#039;&amp;#039;: The packet instantly appears on &amp;lt;code&amp;gt;v-client&amp;lt;/code&amp;gt; (because they&amp;#039;re a veth pair), travels up the network stack in client_ns, and the kernel processes the ICMP Echo Request.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Reply&amp;#039;&amp;#039;&amp;#039;: The client sends an ICMP Echo Reply back to 10.0.0.1, using the same process in reverse.&lt;br /&gt;
&lt;br /&gt;
All of this happens in milliseconds, managed entirely by the kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable B ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 2, show the output of the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show v-host&lt;br /&gt;
sudo ip netns exec client_ns ip addr show v-client&lt;br /&gt;
ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-3-neighbor-discovery-and-arp&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 3: Neighbor Discovery and ARP ===&lt;br /&gt;
&lt;br /&gt;
So far, we&amp;#039;ve worked with IP addresses , but the physical network uses MAC addresses. How does the kernel translate between them?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;thwory&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Thwory ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-address-resolution-protocol-arp&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Address Resolution Protocol (ARP) =====&lt;br /&gt;
&lt;br /&gt;
ARP is the protocol that maps IP addresses to MAC addresses on Ethernet networks. When the kernel needs to send a packet to an IP address that it knows is on a local network, it must first discover the target&amp;#039;s MAC address.&lt;br /&gt;
&lt;br /&gt;
The ARP process:&lt;br /&gt;
&lt;br /&gt;
# The kernel checks its &amp;#039;&amp;#039;&amp;#039;Neighbor Table&amp;#039;&amp;#039;&amp;#039; (ARP cache) for an existing entry mapping the destination IP to a MAC address&lt;br /&gt;
# If found, use it&lt;br /&gt;
# If not found:&lt;br /&gt;
#* Broadcast an ARP Request packet on the local network: &amp;amp;quot;Who has IP X.X.X.X? Tell Y.Y.Y.Y.&amp;amp;quot;&lt;br /&gt;
#* Wait for an ARP Reply: &amp;amp;quot;IP X.X.X.X is at MAC aa:bb:cc:dd:ee:ff&amp;amp;quot;&lt;br /&gt;
#* Cache this mapping in the Neighbor Table for future use&lt;br /&gt;
&lt;br /&gt;
ARP is a broadcast protocol at Layer 2. Every device on the local network segment receives the ARP request, but only the device with the matching IP address responds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-neighbor-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Neighbor Table =====&lt;br /&gt;
&lt;br /&gt;
The kernel maintains a Neighbor Table (also called the ARP cache) mapping IP addresses to MAC addresses. Entries have states:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;REACHABLE&amp;#039;&amp;#039;&amp;#039;: The entry is valid and recently confirmed&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;STALE&amp;#039;&amp;#039;&amp;#039;: The entry is old but assumed still valid&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DELAY&amp;#039;&amp;#039;&amp;#039;: Awaiting confirmation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;INCOMPLETE&amp;#039;&amp;#039;&amp;#039;: ARP request sent, waiting for reply&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FAILED&amp;#039;&amp;#039;&amp;#039;: ARP request failed, no reply received&lt;br /&gt;
&lt;br /&gt;
Entries automatically expire after a timeout (typically a few minutes of inactivity) to handle cases where devices change MAC addresses or leave the network.&lt;br /&gt;
&lt;br /&gt;
You can view the Neighbor Table with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or the older command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arp -n&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;why-arp-matters-for-troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Why ARP Matters for Troubleshooting =====&lt;br /&gt;
&lt;br /&gt;
Many network connectivity issues that seem like &amp;amp;quot;routing problems&amp;amp;quot; are actually ARP problems:&lt;br /&gt;
&lt;br /&gt;
* The kernel can&amp;#039;t deliver packets because it never receives an ARP reply&lt;br /&gt;
* Stale ARP entries point to the wrong MAC address after a device changes&lt;br /&gt;
* ARP conflicts occur when two devices claim the same IP address&lt;br /&gt;
&lt;br /&gt;
Understanding ARP is essential for diagnosing these issues.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-inspecting-the-arp-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Inspecting the ARP Cache ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-view-current-neighbors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: View Current Neighbors =====&lt;br /&gt;
&lt;br /&gt;
Check your neighbor table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see an entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; (the client) with its MAC address and state &amp;lt;code&amp;gt;REACHABLE&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;10.0.0.2 dev v-host lladdr aa:bb:cc:dd:ee:ff REACHABLE&amp;lt;/pre&amp;gt;&lt;br /&gt;
This entry was created when you pinged the client in Exercise 2. The &amp;amp;quot;lladdr&amp;amp;quot; (link-layer address) is the MAC address.&lt;br /&gt;
&lt;br /&gt;
Also check from the client&amp;#039;s perspective:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see an entry for &amp;lt;code&amp;gt;10.0.0.1&amp;lt;/code&amp;gt; (the host).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-flush-the-arp-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Flush the ARP Cache =====&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s simulate a situation where the kernel has forgotten the MAC address mappings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip neigh flush all&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify they&amp;#039;re gone:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; should be absent or in state &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;INCOMPLETE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-watch-arp-in-action&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Watch ARP in Action =====&lt;br /&gt;
&lt;br /&gt;
Now ping the client again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 1 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This ping should succeed. Immediately check the neighbor table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The entry for &amp;lt;code&amp;gt;10.0.0.2&amp;lt;/code&amp;gt; has been automatically recreated. The kernel performed ARP resolution transparently during the ping.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-arp-issues&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Common ARP Issues =====&lt;br /&gt;
&lt;br /&gt;
When troubleshooting connectivity:&lt;br /&gt;
&lt;br /&gt;
# If &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; shows &amp;amp;quot;Destination Host Unreachable&amp;amp;quot;, check &amp;lt;code&amp;gt;ip neigh&amp;lt;/code&amp;gt;. If the entry is &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;, the remote host isn&amp;#039;t responding to ARP requests (possibly down, wrong subnet, or firewall blocking ARP).&lt;br /&gt;
# If &amp;lt;code&amp;gt;ping&amp;lt;/code&amp;gt; works but other services don&amp;#039;t, ARP isn&amp;#039;t the problem—look at routing or firewall rules.&lt;br /&gt;
# If connectivity is intermittent, check for duplicate IP addresses (two devices responding to the same IP).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-c&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable C: ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 3, provide the output of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip neigh show&lt;br /&gt;
sudo ip neigh flush all&lt;br /&gt;
ip neigh show  # Should be empty or show failed entries&lt;br /&gt;
ping -c 1 10.0.0.2&lt;br /&gt;
ip neigh show  # Should show REACHABLE entry&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-4-routing-and-gateway-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 4: Routing and Gateway Configuration ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-beyond-the-local-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Beyond the Local Network ====&lt;br /&gt;
&lt;br /&gt;
So far, our host and client can communicate because they&amp;#039;re in the same subnet (10.0.0.0/24). But what happens when you try to reach an IP address that doesn&amp;#039;t match any of your local subnets? For example, how do you reach the internet (like Google&amp;#039;s DNS server at 8.8.8.8)?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-routing-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Routing Table =====&lt;br /&gt;
&lt;br /&gt;
The routing table is a kernel data structure that maps destination networks to interfaces and gateways. When the kernel needs to send a packet, it consults this table to decide where to send it.&lt;br /&gt;
&lt;br /&gt;
Each routing table entry specifies:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Destination network&amp;#039;&amp;#039;&amp;#039;: Which IP addresses this route applies to (e.g., &amp;lt;code&amp;gt;10.0.0.0/24&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;0.0.0.0/0&amp;lt;/code&amp;gt; for default)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gateway&amp;#039;&amp;#039;&amp;#039;: The IP address of the router to forward packets through (or &amp;amp;quot;direct&amp;amp;quot; if no gateway needed)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Interface&amp;#039;&amp;#039;&amp;#039;: Which network interface to send packets out&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Metric&amp;#039;&amp;#039;&amp;#039;: Priority when multiple routes match (lower is preferred)&lt;br /&gt;
&lt;br /&gt;
View the routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or the older command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;route -n&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Example output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 192.168.1.1 dev eth0 metric 100&lt;br /&gt;
10.0.0.0/24 dev v-host proto kernel scope link src 10.0.0.1&lt;br /&gt;
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50&amp;lt;/pre&amp;gt;&lt;br /&gt;
Let&amp;#039;s decode this:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;default via 192.168.1.1 dev eth0&amp;lt;/code&amp;gt;: For any destination not matched by more specific routes, forward packets to the gateway at 192.168.1.1 through interface eth0. This is called the &amp;amp;quot;default route&amp;amp;quot; or &amp;amp;quot;default gateway.&amp;amp;quot;&lt;br /&gt;
* &amp;lt;code&amp;gt;10.0.0.0/24 dev v-host&amp;lt;/code&amp;gt;: For destinations in 10.0.0.0/24, send directly out v-host (no gateway needed). This route was automatically created when we assigned 10.0.0.1/24 to v-host.&lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.0/24 dev eth0&amp;lt;/code&amp;gt;: Local network, send directly out eth0.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-default-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== The Default Route =====&lt;br /&gt;
&lt;br /&gt;
The default route (destination &amp;lt;code&amp;gt;0.0.0.0/0&amp;lt;/code&amp;gt; or shown as &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt;) is the &amp;amp;quot;route of last resort.&amp;amp;quot; It&amp;#039;s a catch-all that matches any destination not covered by more specific routes. Without a default route, the kernel can only reach directly connected networks.&lt;br /&gt;
&lt;br /&gt;
The gateway specified in the default route must itself be reachable via a local network. You can&amp;#039;t set a gateway to an IP address that the kernel doesn&amp;#039;t know how to reach.&lt;br /&gt;
&lt;br /&gt;
How Routing Decisions Work&lt;br /&gt;
&lt;br /&gt;
When sending a packet to destination IP D:&lt;br /&gt;
&lt;br /&gt;
# The kernel searches the routing table for the most specific match (longest prefix match)&lt;br /&gt;
# If a match is found, use that route&amp;#039;s interface and gateway&lt;br /&gt;
# If no match is found and no default route exists, return &amp;amp;quot;Network is unreachable&amp;amp;quot; error&lt;br /&gt;
&lt;br /&gt;
Example: Routing to 8.8.8.8&lt;br /&gt;
&lt;br /&gt;
* Check local routes: Does 8.8.8.8 match 10.0.0.0/24? No. Does it match 192.168.1.0/24? No.&lt;br /&gt;
* Check default route: Yes, &amp;lt;code&amp;gt;default&amp;lt;/code&amp;gt; matches everything&lt;br /&gt;
* Use the default route: Send packet to gateway 192.168.1.1 via eth0&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;network-address-translation-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Network Address Translation (NAT) =====&lt;br /&gt;
&lt;br /&gt;
Private IP addresses (like 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are not routable on the public internet. If the client (10.0.0.2) wants to reach Google (8.8.8.8), it faces a problem: Google&amp;#039;s servers can&amp;#039;t send replies back to 10.0.0.2 because that address is private and potentially used by millions of devices worldwide.&lt;br /&gt;
&lt;br /&gt;
The solution is NAT (Network Address Translation), specifically a technique called &amp;amp;quot;masquerading&amp;amp;quot;:&lt;br /&gt;
&lt;br /&gt;
# The client sends a packet to 8.8.8.8 with source IP 10.0.0.2&lt;br /&gt;
# The packet reaches the host (acting as a router/gateway)&lt;br /&gt;
# The host &amp;#039;&amp;#039;&amp;#039;rewrites&amp;#039;&amp;#039;&amp;#039; the source IP from 10.0.0.2 to its own public IP (e.g., 203.0.113.5)&lt;br /&gt;
# The host forwards the packet to the internet&lt;br /&gt;
# Google replies to 203.0.113.5&lt;br /&gt;
# The host receives the reply, recognizes it belongs to the client&amp;#039;s connection, rewrites the destination IP back to 10.0.0.2, and forwards it to the client&lt;br /&gt;
&lt;br /&gt;
This source IP rewriting is called &amp;amp;quot;masquerading&amp;amp;quot; because the host &amp;amp;quot;masks&amp;amp;quot; the private IP behind its own public IP. The host maintains a connection table to track which internal client made which request.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ip-forwarding&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== IP Forwarding =====&lt;br /&gt;
&lt;br /&gt;
For the host to act as a router, the kernel must be configured to forward packets between interfaces. By default, Linux does not forward (for security reasons). We enable it with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This setting tells the kernel: &amp;amp;quot;If a packet arrives on one interface and is destined for an address reachable via another interface, forward it.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-enabling-internet-access&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Enabling Internet Access ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-the-problem---no-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: The Problem - No Route =====&lt;br /&gt;
&lt;br /&gt;
From the client namespace, try to ping Google&amp;#039;s DNS server:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Result: &amp;lt;code&amp;gt;connect: Network is unreachable&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Why? Let&amp;#039;s check the client&amp;#039;s routing table:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see only:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;10.0.0.0/24 dev v-client proto kernel scope link src 10.0.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This means the client knows how to reach 10.0.0.0/24, but it has no idea how to reach 8.8.8.8. There&amp;#039;s no default route.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-add-a-default-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Add a Default Route =====&lt;br /&gt;
&lt;br /&gt;
Tell the client to use the host (10.0.0.1) as its default gateway:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should now see:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;default via 10.0.0.1 dev v-client&lt;br /&gt;
10.0.0.0/24 dev v-client proto kernel scope link src 10.0.0.2&amp;lt;/pre&amp;gt;&lt;br /&gt;
This tells the kernel: &amp;amp;quot;For any destination I don&amp;#039;t have a specific route for, send it to 10.0.0.1.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-the-problem---no-forwarding&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: The Problem - No Forwarding =====&lt;br /&gt;
&lt;br /&gt;
Try pinging again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It might still fail or timeout. Why? Even though the client sends packets to the host, the host might not be forwarding them. Let&amp;#039;s enable IP forwarding on the host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Should show: &amp;lt;code&amp;gt;net.ipv4.ip_forward = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-the-problem---no-nat&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: The Problem - No NAT =====&lt;br /&gt;
&lt;br /&gt;
Try pinging again:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 2 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
It still might not work. The packets are being forwarded, but they have source IP 10.0.0.2. The internet routers don&amp;#039;t know how to route replies back to private IP addresses. We need NAT.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-configure-nat-masquerade&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Configure NAT (Masquerade) =====&lt;br /&gt;
&lt;br /&gt;
We&amp;#039;ll use nftables to configure NAT. First, identify your internet-connected interface (replace &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; if yours is different):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip route show default&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Look for &amp;lt;code&amp;gt;default via X.X.X.X dev INTERFACE&amp;lt;/code&amp;gt;. Note the interface name (e.g., &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Now configure NAT to masquerade traffic from the 10.0.0.0/24 subnet going out your internet interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Important&amp;#039;&amp;#039;&amp;#039;: Replace &amp;lt;code&amp;gt;&amp;amp;quot;eth0&amp;amp;quot;&amp;lt;/code&amp;gt; with your actual internet interface name.&lt;br /&gt;
&lt;br /&gt;
The nftables interface is a complex and powerful kernel tool for firewall and NAT managment. Understanding exactly how this works is beyond the scope of this lab. For now, use the commands aboce &amp;amp;quot;as-is&amp;amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Verify the rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft list ruleset&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-6-success&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 6: Success! =====&lt;br /&gt;
&lt;br /&gt;
Now try pinging from the client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Success! The client can now reach the internet. You can also try:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ping -c 3 google.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This tests DNS resolution as well. If this works, your client has full internet connectivity.&lt;br /&gt;
&lt;br /&gt;
What Just Happened?&lt;br /&gt;
&lt;br /&gt;
When the client pings 8.8.8.8:&lt;br /&gt;
&lt;br /&gt;
# Client kernel checks routing table, finds default route via 10.0.0.1&lt;br /&gt;
# Client sends packet to 10.0.0.1 (the host)&lt;br /&gt;
# Host receives packet, checks if IP forwarding is enabled (yes)&lt;br /&gt;
# Host checks its routing table for 8.8.8.8, finds default route via internet gateway&lt;br /&gt;
# Host&amp;#039;s nftables NAT rule rewrites source IP from 10.0.0.2 to host&amp;#039;s public IP&lt;br /&gt;
# Host forwards packet to internet gateway&lt;br /&gt;
# Packet reaches 8.8.8.8, reply comes back to host&amp;#039;s public IP&lt;br /&gt;
# Host&amp;#039;s NAT table recognizes this is a reply to client&amp;#039;s connection&lt;br /&gt;
# Host rewrites destination IP from host&amp;#039;s public IP back to 10.0.0.2&lt;br /&gt;
# Host forwards reply to client via v-host interface&lt;br /&gt;
&lt;br /&gt;
All of this happens transparently at wire speed, managed by the kernel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-d&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Deliverable D ====&lt;br /&gt;
&lt;br /&gt;
After following exercise 4, provide the output of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec client_ns ip route show&lt;br /&gt;
sudo ip netns exec client_ns ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-5-virtual-switches-linux-bridge&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise 5: Virtual Switches (Linux Bridge) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theory-hub-and-spoke-vs-switched-networks&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Theory: Hub-and-Spoke vs. Switched Networks ====&lt;br /&gt;
&lt;br /&gt;
So far, we&amp;#039;ve created a simple point-to-point connection using a veth pair. This works for connecting two devices, but what if we want to connect multiple devices? We could create veth pairs between every pair of devices, but that&amp;#039;s not scalable. A three-device network would need 3 pairs, a four-device network would need 6 pairs, and so on.&lt;br /&gt;
&lt;br /&gt;
The traditional solution is a network switch.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;physical-vs-virtual-switches&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Physical vs. Virtual Switches =====&lt;br /&gt;
&lt;br /&gt;
A physical Ethernet switch is a hardware device with multiple ports. When it receives a frame on one port, it examines the destination MAC address and forwards the frame only to the port where that MAC address is located. The switch learns MAC address locations by observing source addresses on incoming frames.&lt;br /&gt;
&lt;br /&gt;
A Linux bridge is a software implementation of a network switch. It&amp;#039;s a virtual interface that you can &amp;amp;quot;plug&amp;amp;quot; other interfaces into. The bridge learns MAC addresses and forwards frames intelligently, just like a physical switch.&lt;br /&gt;
&lt;br /&gt;
Key characteristics of a bridge:&lt;br /&gt;
&lt;br /&gt;
* Operates at the network level (MAC addresses)&lt;br /&gt;
* Supports multiple connected interfaces&lt;br /&gt;
* Learns MAC addresses dynamically&lt;br /&gt;
* Provides broadcast domain for protocols like ARP&lt;br /&gt;
* Transparent to IP protocols&lt;br /&gt;
&lt;br /&gt;
Creating a Switched Topology&lt;br /&gt;
&lt;br /&gt;
In this exercise, we&amp;#039;ll recreate our network with a proper switched architecture:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;    [Host]&lt;br /&gt;
       |&lt;br /&gt;
       | v-host (connected to br-lab)&lt;br /&gt;
       |&lt;br /&gt;
   [br-lab] (bridge/switch)&lt;br /&gt;
       |&lt;br /&gt;
       +--- v-red-br  ~~~ v-red-ns ----&amp;amp;gt; [Red NS]&lt;br /&gt;
       |&lt;br /&gt;
       +--- v-blue-br ~~~ v-blue-ns ---&amp;amp;gt; [Blue NS]&amp;lt;/pre&amp;gt;&lt;br /&gt;
The bridge (&amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;) acts as a switch. The host, red namespace, and blue namespace are all &amp;amp;quot;plugged into&amp;amp;quot; different ports of this virtual switch.&lt;br /&gt;
&lt;br /&gt;
Why This Matters&lt;br /&gt;
&lt;br /&gt;
This architecture mirrors real networks:&lt;br /&gt;
&lt;br /&gt;
* Home networks: Your home router has a built-in switch connecting your devices&lt;br /&gt;
* Data centers: Switches connect servers to network backbones&lt;br /&gt;
* Cloud environments: Virtual switches (like Open vSwitch) connect containers and VMs&lt;br /&gt;
* Container orchestration: Docker and Kubernetes use Linux bridges for container networking&lt;br /&gt;
&lt;br /&gt;
Understanding bridges is essential for working with modern virtualization and containerization technologies.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;practice-building-a-switched-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Practice: Building a Switched Network ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-1-clean-up-previous-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 1: Clean Up Previous Configuration =====&lt;br /&gt;
&lt;br /&gt;
Remove the old point-to-point setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns delete client_ns&lt;br /&gt;
sudo ip link delete v-host&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The veth pair is automatically cleaned up when we delete v-host (since they&amp;#039;re paired).&lt;br /&gt;
&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&lt;br /&gt;
ip link show | grep v-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both should show no results.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-2-create-the-bridge-virtual-switch&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 2: Create the Bridge (Virtual Switch) =====&lt;br /&gt;
&lt;br /&gt;
Create a Linux bridge named &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add name br-lab type bridge&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set br-lab up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see a new interface of type &amp;lt;code&amp;gt;bridge&amp;lt;/code&amp;gt; in state UP.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-3-create-network-namespaces-for-two-clients&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 3: Create Network Namespaces for Two Clients =====&lt;br /&gt;
&lt;br /&gt;
Create namespaces for &amp;amp;quot;red&amp;amp;quot; and &amp;amp;quot;blue&amp;amp;quot; clients:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns add red&lt;br /&gt;
sudo ip netns add blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip netns list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-4-create-and-connect-red-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 4: Create and Connect Red Client =====&lt;br /&gt;
&lt;br /&gt;
Create a veth pair for the red client:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-red-br type veth peer name v-red-ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Connect one end (&amp;lt;code&amp;gt;v-red-br&amp;lt;/code&amp;gt;) to the bridge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-br master br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This &amp;amp;quot;plugs&amp;amp;quot; v-red-br into the switch. The &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; keyword means &amp;amp;quot;this interface is now a port of the bridge.&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Move the other end into the red namespace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-ns netns red&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bring up the bridge-side interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link set v-red-br up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Assign IP to red client and bring it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip addr add 10.0.0.2/24 dev v-red-ns&lt;br /&gt;
sudo ip netns exec red ip link set v-red-ns up&lt;br /&gt;
sudo ip netns exec red ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-5-create-and-connect-blue-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 5: Create and Connect Blue Client =====&lt;br /&gt;
&lt;br /&gt;
Repeat the same process for blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip link add v-blue-br type veth peer name v-blue-ns&lt;br /&gt;
sudo ip link set v-blue-br master br-lab&lt;br /&gt;
sudo ip link set v-blue-ns netns blue&lt;br /&gt;
sudo ip link set v-blue-br up&lt;br /&gt;
sudo ip netns exec blue ip addr add 10.0.0.3/24 dev v-blue-ns&lt;br /&gt;
sudo ip netns exec blue ip link set v-blue-ns up&lt;br /&gt;
sudo ip netns exec blue ip link set lo up&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-6-configure-the-host-interface&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 6: Configure the Host Interface =====&lt;br /&gt;
&lt;br /&gt;
The host also needs to be connected to the switch. We&amp;#039;ll assign an IP address directly to the bridge interface:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip addr add 10.0.0.1/24 dev br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Why does this work? A bridge can have an IP address, making the host itself a participant on the switched network. This is simpler than creating another veth pair.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-7-verify-the-topology&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 7: Verify the Topology =====&lt;br /&gt;
&lt;br /&gt;
Check bridge status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip link show master br-lab&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This shows all interfaces connected to the bridge. You should see &amp;lt;code&amp;gt;v-red-br&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;v-blue-br&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Or use the bridge utility:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;bridge link show&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Check IP addresses:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ip addr show br-lab&lt;br /&gt;
sudo ip netns exec red ip addr show v-red-ns&lt;br /&gt;
sudo ip netns exec blue ip addr show v-blue-ns&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-8-test-connectivity&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 8: Test Connectivity =====&lt;br /&gt;
&lt;br /&gt;
Red to Blue (peer-to-peer):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Blue to Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Red to Host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Blue to Host:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec blue ping -c 3 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All of these should succeed. The bridge is forwarding frames between all three participants.&lt;br /&gt;
&lt;br /&gt;
Host to Red:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Host to Blue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ping -c 3 10.0.0.3&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;step-9-enable-internet-access-for-clients&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Step 9: Enable Internet Access for Clients =====&lt;br /&gt;
&lt;br /&gt;
Add default routes for both clients:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ip route add default via 10.0.0.1&lt;br /&gt;
sudo ip netns exec blue ip route add default via 10.0.0.1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Verify IP forwarding is still enabled (should be from Exercise 4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sysctl net.ipv4.ip_forward&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not set to 1, enable it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo sysctl -w net.ipv4.ip_forward=1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ensure NAT is still configured (should be from Exercise 4):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft list ruleset | grep masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If not present, add it again (replace &amp;lt;code&amp;gt;eth0&amp;lt;/code&amp;gt; with your internet interface):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Test internet access:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo ip netns exec red ping -c 3 8.8.8.8&lt;br /&gt;
sudo ip netns exec blue ping -c 3 8.8.8.8&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Both clients can now reach the internet through the host, which acts as both a switch (via the bridge) and a router (via IP forwarding and NAT).&lt;br /&gt;
&lt;br /&gt;
Understanding the Data Flow&lt;br /&gt;
&lt;br /&gt;
When Red (10.0.0.2) pings Blue (10.0.0.3):&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s kernel sees 10.0.0.3 is in the local subnet, sends ARP request&lt;br /&gt;
# ARP request goes out v-red-ns, through the veth pair to v-red-br&lt;br /&gt;
# v-red-br is connected to br-lab (the switch)&lt;br /&gt;
# br-lab broadcasts the ARP request to all connected interfaces (v-blue-br and itself)&lt;br /&gt;
# Blue receives ARP request via v-blue-ns, replies with its MAC&lt;br /&gt;
# br-lab learns Blue&amp;#039;s MAC address is on port v-blue-br&lt;br /&gt;
# Subsequent packets from Red to Blue are forwarded directly to v-blue-br (no broadcast)&lt;br /&gt;
# The bridge maintains a MAC address table, just like a physical switch&lt;br /&gt;
&lt;br /&gt;
When Red (10.0.0.2) pings Google (8.8.8.8):&lt;br /&gt;
&lt;br /&gt;
# Red&amp;#039;s kernel sees 8.8.8.8 is not in the local subnet, consults routing table&lt;br /&gt;
# Default route says to send to gateway 10.0.0.1 (the host)&lt;br /&gt;
# Red uses ARP to find MAC of 10.0.0.1&lt;br /&gt;
# Packet goes through veth pair to br-lab&lt;br /&gt;
# br-lab forwards to its own IP (10.0.0.1 is assigned to br-lab)&lt;br /&gt;
# Host kernel receives packet, sees it&amp;#039;s destined for 8.8.8.8&lt;br /&gt;
# IP forwarding is enabled, so host checks routing table&lt;br /&gt;
# Host finds default route via internet gateway&lt;br /&gt;
# NAT rule rewrites source IP from 10.0.0.2 to host&amp;#039;s public IP&lt;br /&gt;
# Packet forwarded to internet&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Scripting Challenges ===&lt;br /&gt;
&lt;br /&gt;
These challenges test your ability to automate network configuration and extract useful diagnostic information from the kernel&amp;#039;s network subsystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-automated-network-builder&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Challenge 1: Automated Network Builder ====&lt;br /&gt;
&lt;br /&gt;
Write a bash script &amp;lt;code&amp;gt;lab7_builder.sh&amp;lt;/code&amp;gt; that automatically constructs the Red/Blue/Bridge topology from Exercise 5, assigns IP addresses, enables routing and NAT, and provides cleanup functionality.&lt;br /&gt;
&lt;br /&gt;
Requirements:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Script Name &amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;lab7_builder.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Shebang and Best Practices&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Start with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; for error handling&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Arguments&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;: Create and configure the network&lt;br /&gt;
#* &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt;: Clean up all created resources&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Start Operation Must&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Create bridge &amp;lt;code&amp;gt;br-lab&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Create namespaces &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;blue&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Create veth pairs and connect them to the bridge and namespaces&lt;br /&gt;
#* Assign IP addresses:&lt;br /&gt;
#** Host (br-lab): 10.0.0.1/24&lt;br /&gt;
#** Red: 10.0.0.2/24&lt;br /&gt;
#** Blue: 10.0.0.3/24&lt;br /&gt;
#* Bring all interfaces UP&lt;br /&gt;
#* Add default routes for both clients pointing to 10.0.0.1&lt;br /&gt;
#* Enable IP forwarding&lt;br /&gt;
#* Configure NAT/masquerade for 10.0.0.0/24 traffic going out the default internet interface&lt;br /&gt;
#* Print a success message: &amp;amp;quot;Network topology created successfully&amp;amp;quot;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Stop Operation Must&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
#* Delete namespaces (this automatically removes interfaces inside them)&lt;br /&gt;
#* Delete bridge&lt;br /&gt;
#* Flush nftables nat table&lt;br /&gt;
#* Disable IP forwarding (set back to 0)&lt;br /&gt;
#* Print a success message: &amp;amp;quot;Network topology cleaned up successfully&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
Testing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x ./lab7_builder.sh&lt;br /&gt;
sudo ./lab7_builder.sh start&lt;br /&gt;
sudo ip netns exec red ping -c 2 10.0.0.3&lt;br /&gt;
sudo ip netns exec red ping -c 2 8.8.8.8&lt;br /&gt;
sudo ./lab7_builder.sh stop&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-challenge-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
===== Deliverable Challenge 1: =====&lt;br /&gt;
&lt;br /&gt;
* Complete script source code with comments&lt;br /&gt;
* Output of running the script with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; argument&lt;br /&gt;
* Output of connectivity tests (red to blue, red to internet)&lt;br /&gt;
* Output of running the script with &amp;lt;code&amp;gt;stop&amp;lt;/code&amp;gt; argument&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-namespace-inspector&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Challenge 2: Namespace Inspector ====&lt;br /&gt;
&lt;br /&gt;
Write a bash script &amp;lt;code&amp;gt;lab7_inspect.sh&amp;lt;/code&amp;gt; that takes a namespace name as an argument and displays diagnostic information about that namespace&amp;#039;s network configuration.&lt;br /&gt;
&lt;br /&gt;
Requirements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Script Name&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;lab7_inspect.sh&amp;lt;/code&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Shebang and Best Practices&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use functions&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Comment your code&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Arguments&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Takes exactly one argument: the namespace name&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If no argument or more than one argument, print usage and exit with error&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Output Format&amp;#039;&amp;#039;&amp;#039; (exactly as shown):&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;=== Network Inspection for Namespace: &amp;amp;lt;ns_name&amp;amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
[Interfaces]&lt;br /&gt;
&amp;amp;lt;output of ip link show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[IP Addresses]&lt;br /&gt;
&amp;amp;lt;output of ip addr show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Routes]&lt;br /&gt;
&amp;amp;lt;output of ip route show&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Default Gateway]&lt;br /&gt;
&amp;amp;lt;extract and show only the default gateway IP, or &amp;amp;quot;None&amp;amp;quot; if not configured&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Neighbors (ARP Cache)]&lt;br /&gt;
&amp;amp;lt;output of ip neigh show&amp;amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;Functionality&amp;#039;&amp;#039;&amp;#039;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Check if namespace exists before attempting to inspect&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;All commands should run inside the specified namespace&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Extract the default gateway IP from the routing table (hint: grep for &amp;amp;quot;default&amp;amp;quot; and extract the IP after &amp;amp;quot;via&amp;amp;quot;)&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testing (after running Challenge 1&amp;#039;s start command):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x ./lab7_inspect.sh&lt;br /&gt;
sudo ./lab7_inspect.sh red&lt;br /&gt;
sudo ./lab7_inspect.sh blue&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverable-challenge-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
====== Deliverable Challenge 2: ======&lt;br /&gt;
&lt;br /&gt;
* Complete script source code with comments&lt;br /&gt;
* Output of running the script for the &amp;lt;code&amp;gt;red&amp;lt;/code&amp;gt; namespace (after pinging a few addresses to populate ARP cache)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-network-command-quick-guide&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Network Command Quick Guide ==&lt;br /&gt;
&lt;br /&gt;
This section provides a quick reference for the commands introduced in this lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;interface-management-ip-link&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Interface Management (ip link) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List all interfaces&lt;br /&gt;
ip link show&lt;br /&gt;
&lt;br /&gt;
# Create veth pair&lt;br /&gt;
sudo ip link add dev &amp;lt;name1&amp;gt; type veth peer name &amp;lt;name2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Move interface to namespace&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; netns &amp;lt;namespace&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Bring interface up/down&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; up&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; down&lt;br /&gt;
&lt;br /&gt;
# Delete interface&lt;br /&gt;
sudo ip link delete &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Create bridge&lt;br /&gt;
sudo ip link add name &amp;lt;bridge&amp;gt; type bridge&lt;br /&gt;
&lt;br /&gt;
# Connect interface to bridge&lt;br /&gt;
sudo ip link set &amp;lt;interface&amp;gt; master &amp;lt;bridge&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;address-management-ip-addr&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Address Management (ip addr) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show addresses&lt;br /&gt;
ip addr show&lt;br /&gt;
ip addr show &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add address&lt;br /&gt;
sudo ip addr add &amp;lt;ip&amp;gt;/&amp;lt;prefix&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
# Example: sudo ip addr add 10.0.0.1/24 dev eth0&lt;br /&gt;
&lt;br /&gt;
# Remove address&lt;br /&gt;
sudo ip addr del &amp;lt;ip&amp;gt;/&amp;lt;prefix&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Flush all addresses from interface&lt;br /&gt;
sudo ip addr flush dev &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;routing-management-ip-route&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Routing Management (ip route) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show routing table&lt;br /&gt;
ip route show&lt;br /&gt;
&lt;br /&gt;
# Add route&lt;br /&gt;
sudo ip route add &amp;lt;network&amp;gt;/&amp;lt;prefix&amp;gt; via &amp;lt;gateway&amp;gt;&lt;br /&gt;
# Example: sudo ip route add 192.168.2.0/24 via 192.168.1.1&lt;br /&gt;
&lt;br /&gt;
# Add default route&lt;br /&gt;
sudo ip route add default via &amp;lt;gateway&amp;gt;&lt;br /&gt;
# Example: sudo ip route add default via 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Delete route&lt;br /&gt;
sudo ip route del &amp;lt;network&amp;gt;/&amp;lt;prefix&amp;gt;&lt;br /&gt;
sudo ip route del default&lt;br /&gt;
&lt;br /&gt;
# Flush routing table&lt;br /&gt;
sudo ip route flush table main&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;neighbor-management-ip-neigh&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Neighbor Management (ip neigh) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show ARP cache&lt;br /&gt;
ip neigh show&lt;br /&gt;
&lt;br /&gt;
# Flush ARP cache&lt;br /&gt;
sudo ip neigh flush all&lt;br /&gt;
&lt;br /&gt;
# Add static ARP entry&lt;br /&gt;
sudo ip neigh add &amp;lt;ip&amp;gt; lladdr &amp;lt;mac&amp;gt; dev &amp;lt;interface&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete ARP entry&lt;br /&gt;
sudo ip neigh del &amp;lt;ip&amp;gt; dev &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;namespace-management-ip-netns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Namespace Management (ip netns) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List namespaces&lt;br /&gt;
ip netns list&lt;br /&gt;
&lt;br /&gt;
# Create namespace&lt;br /&gt;
sudo ip netns add &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete namespace&lt;br /&gt;
sudo ip netns delete &amp;lt;name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Execute command in namespace&lt;br /&gt;
sudo ip netns exec &amp;lt;name&amp;gt; &amp;lt;command&amp;gt;&lt;br /&gt;
# Example: sudo ip netns exec red ping 10.0.0.1&lt;br /&gt;
&lt;br /&gt;
# Get shell in namespace&lt;br /&gt;
sudo ip netns exec &amp;lt;name&amp;gt; bash&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bridge-management&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Bridge Management ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Show bridge details&lt;br /&gt;
bridge link show&lt;br /&gt;
bridge fdb show  # Show MAC address table&lt;br /&gt;
&lt;br /&gt;
# Show which interfaces are part of a bridge&lt;br /&gt;
ip link show master &amp;lt;bridge&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;ip-forwarding-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== IP Forwarding ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Check status&lt;br /&gt;
sysctl net.ipv4.ip_forward&lt;br /&gt;
&lt;br /&gt;
# Enable (temporary)&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=1&lt;br /&gt;
&lt;br /&gt;
# Disable&lt;br /&gt;
sudo sysctl -w net.ipv4.ip_forward=0&lt;br /&gt;
&lt;br /&gt;
# Enable permanently (edit /etc/sysctl.conf)&lt;br /&gt;
echo &amp;quot;net.ipv4.ip_forward=1&amp;quot; | sudo tee -a /etc/sysctl.conf&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;nat-with-nftables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== NAT with nftables ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# List current ruleset&lt;br /&gt;
sudo nft list ruleset&lt;br /&gt;
&lt;br /&gt;
# Create NAT table and rules&lt;br /&gt;
sudo nft add table ip nat&lt;br /&gt;
sudo nft add chain ip nat postrouting { type nat hook postrouting priority srcnat \; }&lt;br /&gt;
sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/24 oifname &amp;quot;eth0&amp;quot; masquerade&lt;br /&gt;
&lt;br /&gt;
# Delete NAT table&lt;br /&gt;
sudo nft delete table ip nat&lt;br /&gt;
&lt;br /&gt;
# Flush ruleset&lt;br /&gt;
sudo nft flush ruleset&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;diagnostic-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Diagnostic Tools ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Test connectivity&lt;br /&gt;
ping -c 3 &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trace route&lt;br /&gt;
traceroute &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Show listening sockets&lt;br /&gt;
ss -tuln&lt;br /&gt;
&lt;br /&gt;
# Monitor traffic&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt;&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n arp  # Show only ARP traffic&lt;br /&gt;
sudo tcpdump -i &amp;lt;interface&amp;gt; -n icmp # Show only ping traffic&lt;br /&gt;
&lt;br /&gt;
# Show interface statistics&lt;br /&gt;
ip -s link show &amp;lt;interface&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-network-topology-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Network Topology Patterns ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;point-to-point-connection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Point-to-Point Connection ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Host] &amp;amp;lt;---veth pair---&amp;amp;gt; [Namespace]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Simple container isolation, testing&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;switched-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Switched Network ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;         [Bridge]&lt;br /&gt;
            |&lt;br /&gt;
    +-------+-------+&lt;br /&gt;
    |       |       |&lt;br /&gt;
  [Host] [NS1]   [NS2]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Multiple containers/VMs on same network&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;routed-network&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Routed Network ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[Internet] &amp;amp;lt;---&amp;amp;gt; [Host/Router] &amp;amp;lt;---&amp;amp;gt; [Bridge] &amp;amp;lt;---&amp;amp;gt; [Containers]&lt;br /&gt;
                     (NAT)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Use case: Containers with internet access&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;summary-table-network-components&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Summary Table: Network Components ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Component&lt;br /&gt;
! Purpose&lt;br /&gt;
! Layer&lt;br /&gt;
! Key Commands&lt;br /&gt;
|-&lt;br /&gt;
| veth pair&lt;br /&gt;
| Virtual cable connecting two interfaces&lt;br /&gt;
| L2&lt;br /&gt;
| &amp;lt;code&amp;gt;ip link add type veth&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| bridge&lt;br /&gt;
| Virtual switch&lt;br /&gt;
| L2&lt;br /&gt;
| &amp;lt;code&amp;gt;ip link add type bridge&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| IP address&lt;br /&gt;
| Device identity and subnet membership&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip addr add&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Route&lt;br /&gt;
| Path determination&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip route add&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ARP/Neighbor&lt;br /&gt;
| IP-to-MAC mapping&lt;br /&gt;
| L2/L3&lt;br /&gt;
| &amp;lt;code&amp;gt;ip neigh show&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| NAT&lt;br /&gt;
| Private-to-public IP translation&lt;br /&gt;
| L3&lt;br /&gt;
| &amp;lt;code&amp;gt;nft add rule masquerade&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Namespace&lt;br /&gt;
| Isolated network stack&lt;br /&gt;
| All&lt;br /&gt;
| &amp;lt;code&amp;gt;ip netns add&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single PDF document containing:&lt;br /&gt;
&lt;br /&gt;
The deliverables A-D and solutions to the challanges 1 and 2.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;for-further-study&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== For further study: ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Advanced Topics&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* VLANs and 802.1Q tagging&lt;br /&gt;
* Open vSwitch (OVS) for advanced switching&lt;br /&gt;
* Network policies and firewalling with nftables&lt;br /&gt;
* Quality of Service (QoS) and traffic shaping&lt;br /&gt;
* IPv6 configuration and dual-stack networking&lt;br /&gt;
* VPN setup with WireGuard or OpenVPN&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Container Networking&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Docker networking modes (bridge, host, overlay)&lt;br /&gt;
* Kubernetes networking model and CNI plugins&lt;br /&gt;
* Service meshes (Istio, Linkerd)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Performance and Monitoring&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network performance testing with iperf&lt;br /&gt;
* Packet capture and analysis with Wireshark&lt;br /&gt;
* Continuous monitoring with Prometheus and Grafana&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Security&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
* Network segmentation and isolation&lt;br /&gt;
* Firewall rule design&lt;br /&gt;
* Intrusion detection systems&lt;br /&gt;
* Zero-trust networking&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;relevant-manual-pages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Relevant Manual Pages: ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;man 8 ip          # iproute2 command reference&lt;br /&gt;
man 8 ip-link     # Interface management&lt;br /&gt;
man 8 ip-address  # Address management&lt;br /&gt;
man 8 ip-route    # Routing management&lt;br /&gt;
man 8 ip-netns    # Namespace management&lt;br /&gt;
man 8 bridge      # Bridge management&lt;br /&gt;
man 7 netdevice   # Network device overview&lt;br /&gt;
man 7 arp         # ARP protocol&lt;br /&gt;
man 5 nft         # nftables syntax&lt;br /&gt;
man 8 tcpdump     # Packet capture&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;online-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Online Resources: ===&lt;br /&gt;
&lt;br /&gt;
* [https://lartc.org/ Linux Advanced Routing &amp;amp;amp; Traffic Control HOWTO]&lt;br /&gt;
* [https://wiki.linuxfoundation.org/networking/iproute2 iproute2 documentation]&lt;br /&gt;
* [https://www.kernel.org/doc/html/latest/networking/ The Linux Kernel Networking Stack]&lt;br /&gt;
* [https://wiki.nftables.org/ nftables Wiki]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8179</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8179"/>
		<updated>2025-11-21T14:45:44Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;br /&gt;
* Lab 7 - [[OS Lab 7 - The Network Subsystem]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8178</id>
		<title>OS Lab 6 - Inter Process Communication</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8178"/>
		<updated>2025-11-14T15:06:48Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Deliverables and Assessment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel provides IPC mechanisms and how bash exposes them to scripts.&lt;br /&gt;
* Use environment variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; to pass configuration from parent processes to children.&lt;br /&gt;
* Construct pipelines with anonymous pipes to connect process standard streams.&lt;br /&gt;
* Create and use named pipes (FIFOs) for communication between unrelated processes.&lt;br /&gt;
* Send signals to processes and handle them with &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for asynchronous event-driven scripting.&lt;br /&gt;
* Demonstrate basic socket communication using existing utilities for network IPC.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 5, we explored bash scripting as a way to automate tasks by writing programs that the shell interprets. We learned how processes execute scripts, how to control flow with conditionals and loops, and how to structure code with functions. However, all of our scripts operated in isolation. Each script was a single process (or spawned child processes) that performed its task independently.&lt;br /&gt;
&lt;br /&gt;
Real-world systems require processes to cooperate. A web server must communicate with a database. A shell pipeline connects the output of one program to the input of another. A service must respond to signals sent by the init system. This lab explores the kernel-provided mechanisms that enable processes to exchange data and coordinate their actions. We focus on five fundamental IPC mechanisms, all accessible from bash: environment variables, pipes, named pipes, signals, and sockets.&lt;br /&gt;
&lt;br /&gt;
Understanding IPC is essential for system administration and software development. These mechanisms form the foundation of everything from command pipelines to distributed systems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy socat&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: A modern HTTP server with automatic HTTPS. We use it to demonstrate socket communication.&lt;br /&gt;
* &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;: A versatile networking tool that can work with Unix domain sockets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with: - Process concepts from Lab 3 (PIDs, process hierarchy, file descriptors) - File permissions from Lab 4 (execute bit, ownership) - Bash scripting fundamentals from Lab 5 (shebangs, builtins, variables, quoting, exit status, redirection, loops, functions)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;inter-process-communication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Inter-Process Communication ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-ipc&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What is IPC? ===&lt;br /&gt;
&lt;br /&gt;
By default, processes are isolated from one another. Each process has its own memory space, file descriptor table, and execution context. This isolation provides security and stability, but it creates a problem: how can processes cooperate to accomplish complex tasks?&lt;br /&gt;
&lt;br /&gt;
Inter-Process Communication (IPC) refers to the kernel-provided mechanisms that allow processes to exchange data and synchronize their actions. The kernel acts as an intermediary, providing channels, buffers, and signaling primitives that processes can use to communicate safely without violating isolation boundaries.&lt;br /&gt;
&lt;br /&gt;
In this lab, we examine five IPC mechanisms arranged roughly by complexity:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Environment Variables&amp;#039;&amp;#039;&amp;#039;: The simplest form of IPC. A parent process passes key-value configuration to its children via inherited environment variables. Communication is unidirectional (parent → child) and occurs only at process creation.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Pipes&amp;#039;&amp;#039;&amp;#039;: The kernel creates a buffer connecting one process’s standard output to another’s standard input. Data flows in one direction through the pipe. Anonymous pipes exist only while the processes using them are running.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Named Pipes (FIFOs)&amp;#039;&amp;#039;&amp;#039;: Like anonymous pipes, but visible in the filesystem. This persistence allows unrelated processes to connect to the same pipe by opening a file path.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Signals&amp;#039;&amp;#039;&amp;#039;: Asynchronous notifications sent from one process to another (or from the kernel to a process). Signals interrupt the receiving process, which can catch and handle them or allow default behavior (often termination).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Sockets&amp;#039;&amp;#039;&amp;#039;: Bidirectional communication channels that work across network boundaries or locally via Unix domain sockets. Multiple processes can connect to the same socket, enabling one-to-many communication patterns.&lt;br /&gt;
&lt;br /&gt;
Each mechanism solves different problems and has different trade-offs in terms of complexity, performance, and flexibility.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Role ====&lt;br /&gt;
&lt;br /&gt;
When a process forks, the child inherits a copy of the parent’s environment: a set of key-value string pairs maintained by the kernel for each process. The child can read these values and modify its own copy, but changes do not propagate back to the parent or to other processes. This inheritance mechanism provides a simple, unidirectional channel for passing configuration from parent to child.&lt;br /&gt;
&lt;br /&gt;
Environment variables are not limited to shell scripts. Every process has an environment. When you run any program, it receives the environment from the shell that launched it. Programs written in C access this via the &amp;lt;code&amp;gt;environ&amp;lt;/code&amp;gt; global variable or the third parameter to &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;. Python uses &amp;lt;code&amp;gt;os.environ&amp;lt;/code&amp;gt;. The environment is a universal convention for passing configuration.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-role-export-and-env&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Role: &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
In bash, variables are local to the shell process by default. When you set &amp;lt;code&amp;gt;name=value&amp;lt;/code&amp;gt;, that variable exists in the shell’s memory but is &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; passed to child processes. The &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; builtin marks a variable for inclusion in the environment of future child processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
export myvar&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or, more concisely:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export myvar=&amp;quot;hello&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Once exported, all child processes started by this shell (scripts, programs, or subshells) will inherit &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in their environment. To see the current environment, use the &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; command (or &amp;lt;code&amp;gt;printenv&amp;lt;/code&amp;gt;), which prints all exported variables.&lt;br /&gt;
&lt;br /&gt;
You can also set environment variables for a single command without affecting the shell:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;DEBUG=1 ./myscript.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This syntax sets &amp;lt;code&amp;gt;DEBUG=1&amp;lt;/code&amp;gt; in the environment of &amp;lt;code&amp;gt;myscript.sh&amp;lt;/code&amp;gt; only, without exporting it in the parent shell.&lt;br /&gt;
&lt;br /&gt;
Common environment variables you’ve already been using include &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (where bash searches for commands), &amp;lt;code&amp;gt;HOME&amp;lt;/code&amp;gt; (your home directory), &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; (your username), and &amp;lt;code&amp;gt;SHELL&amp;lt;/code&amp;gt; (your login shell). These are all set by the login process and inherited by every subsequent process in your session.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-environment-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Environment Variables ====&lt;br /&gt;
&lt;br /&gt;
Environment variables are appropriate for: - Configuration that should be inherited by all child processes (e.g., locale settings, proxy configuration) - Passing secrets or credentials to programs without embedding them in command-line arguments (which are visible via &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt;) - Controlling program behavior via well-known variables like &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LD_LIBRARY_PATH&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;TZ&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Runtime communication between already-running processes (use pipes, sockets, or signals) - Large amounts of data (environment is limited in size, typically a few megabytes) - Bi-directional communication (child changes don’t affect parent)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Pipes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-pipe-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Pipe Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A pipe is a one-way data channel maintained in kernel memory. When you create a pipe, the kernel allocates a buffer (typically 64 KB on Linux) and returns two file descriptors: one for writing and one for reading. Data written to the write end is buffered by the kernel and can be read from the read end in FIFO (first-in, first-out) order.&lt;br /&gt;
&lt;br /&gt;
Pipes are anonymous: they have no name in the filesystem. They exist only as long as at least one process holds a file descriptor to them. When all processes close their references to a pipe, the kernel deallocates it.&lt;br /&gt;
&lt;br /&gt;
The key insight from Lab 3: a pipeline like &amp;lt;code&amp;gt;cat file.txt | grep &amp;amp;quot;error&amp;amp;quot; | wc -l&amp;lt;/code&amp;gt; creates multiple processes (all in the same process group) connected by pipes. The kernel sets up the file descriptors so that &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdin, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;’s stdin. The processes run concurrently, with data flowing through kernel buffers as it’s produced and consumed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-pipe-operator&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Pipe Operator ====&lt;br /&gt;
&lt;br /&gt;
Bash creates pipes using the &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; operator. The syntax is simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command1 | command2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a pipe and starts two processes. Bash configures &amp;lt;code&amp;gt;command1&amp;lt;/code&amp;gt;’s stdout (FD 1) to point to the pipe’s write end and &amp;lt;code&amp;gt;command2&amp;lt;/code&amp;gt;’s stdin (FD 0) to point to the pipe’s read end. The commands execute concurrently.&lt;br /&gt;
&lt;br /&gt;
Longer pipelines work the same way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat data.txt | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates three pipes connecting four processes. Data flows left-to-right through kernel buffers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes-in-scripts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes in Scripts ====&lt;br /&gt;
&lt;br /&gt;
You can use pipes inside scripts just as you would interactively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# Count unique IP addresses in an access log&lt;br /&gt;
cat /var/log/access.log | cut -d&amp;#039; &amp;#039; -f1 | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pipes are particularly powerful when combined with bash’s process substitution feature &amp;lt;code&amp;gt;&amp;amp;lt;(command)&amp;lt;/code&amp;gt;, but that’s beyond the scope of this introductory lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Pipes ====&lt;br /&gt;
&lt;br /&gt;
Pipes are ideal for: - Connecting the output of one program to the input of another in a linear processing chain - Streaming data processing where data is generated and consumed incrementally - Quick, one-off data transformations at the command line&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Bi-directional communication (data flows one way only) - Communication between unrelated processes that weren’t started in a pipeline - Persistent communication (pipe disappears when processes exit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-fifo-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s FIFO Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A named pipe, or FIFO (First-In, First-Out), is a pipe with a name in the filesystem. Unlike anonymous pipes, FIFOs persist as filesystem entries (though the data buffer is still in kernel memory). Any process with appropriate permissions can open the FIFO by its path, allowing unrelated processes to communicate.&lt;br /&gt;
&lt;br /&gt;
When a process opens a FIFO for reading, it blocks until another process opens the same FIFO for writing (and vice versa). Once both ends are open, data flows through the kernel buffer just like an anonymous pipe. When all processes close their connections, the FIFO remains as a filesystem entry but the kernel buffer is deallocated.&lt;br /&gt;
&lt;br /&gt;
You can identify a FIFO in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output by the leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; in the permission string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;prw-r--r-- 1 user user 0 Nov  6 10:00 myfifo&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;creating-fifos-with-mkfifo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Creating FIFOs with &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; command creates a named pipe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkfifo /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now two unrelated processes can communicate by opening this file. One writes to it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Hello from writer&amp;quot; &amp;gt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command will block until a reader appears. In another terminal (or in the background), a reader can consume the data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When the reader connects, the writer unblocks, the message flows through the kernel buffer, and both commands complete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Named Pipes ====&lt;br /&gt;
&lt;br /&gt;
Named pipes are useful for: - Communication between unrelated processes that start at different times - Producer-consumer patterns where one process generates data and another processes it - Simple IPC without needing network sockets or shared files&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Multiple simultaneous readers or writers (FIFO semantics become unpredictable) - Persistent data storage (data is lost when all processes disconnect) - Bi-directional communication (like anonymous pipes, FIFOs are one-way)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-signal-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Signal Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A signal is an asynchronous notification sent to a process. Signals can be sent by other processes (via the &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt; system call) or by the kernel itself in response to events like segmentation faults, keyboard interrupts (Ctrl+C), or child process termination.&lt;br /&gt;
&lt;br /&gt;
When the kernel delivers a signal to a process, it interrupts the process’s normal execution. The process can respond in one of three ways:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Default Action&amp;#039;&amp;#039;&amp;#039;: Each signal has a default behavior, often terminating the process. For example, &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (signal 15) gracefully terminates, while &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (signal 9) forces immediate termination.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ignore&amp;#039;&amp;#039;&amp;#039;: The process can choose to ignore certain signals (except &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;, which cannot be caught or ignored).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom Handler&amp;#039;&amp;#039;&amp;#039;: The process can register a function (signal handler) to execute when the signal arrives. This allows the process to perform cleanup or take other actions before deciding whether to continue, terminate, or take other action.&lt;br /&gt;
&lt;br /&gt;
Common signals: - &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; (2): Sent by Ctrl+C. Default: terminate. - &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (15): Polite request to terminate. Default: terminate. Most services handle this to perform clean shutdown. - &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (9): Immediate termination. Cannot be caught or ignored. Used as a last resort. - &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (1): Historically “hang up” (modem disconnected). Often used to tell daemons to reload configuration. - &amp;lt;code&amp;gt;SIGCHLD&amp;lt;/code&amp;gt; (17): Sent to a parent when a child process terminates. - &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; (10) and &amp;lt;code&amp;gt;SIGUSR2&amp;lt;/code&amp;gt; (12): User-defined signals for custom purposes.&lt;br /&gt;
&lt;br /&gt;
Signals are sent by PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;kill -TERM 1234    # Send SIGTERM to process 1234&lt;br /&gt;
kill -9 1234       # Send SIGKILL to process 1234&lt;br /&gt;
kill -HUP 1234     # Send SIGHUP to process 1234&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also use the &amp;lt;code&amp;gt;%n&amp;lt;/code&amp;gt; job notation from Lab 3 to send signals to background jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-trap-builtin&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; Builtin ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin allows a bash script to register a handler for incoming signals:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Caught SIGINT&amp;quot;; exit&amp;#039; INT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This tells bash: “When SIGINT arrives, execute the command &amp;lt;code&amp;gt;echo &amp;amp;quot;Caught SIGINT&amp;amp;quot;; exit&amp;lt;/code&amp;gt;.” The handler can be any bash command or function.&lt;br /&gt;
&lt;br /&gt;
Common pattern for cleanup on exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up temporary files...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myscript.*&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Script body&lt;br /&gt;
echo &amp;quot;Running...&amp;quot;&lt;br /&gt;
sleep 10&lt;br /&gt;
echo &amp;quot;Done&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cleanup() is automatically called on normal exit, Ctrl+C, or errors (with set -e)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The special signal &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt; isn’t a real signal; it’s a bash pseudo-signal that fires whenever the script exits for any reason.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-signals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Signals ====&lt;br /&gt;
&lt;br /&gt;
Signals are appropriate for: - Event-driven scripts that respond to external events - Graceful shutdown and cleanup on termination - Daemon control (reload config with &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;, graceful restart with &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;) - Inter-process coordination where one process needs to notify another of state changes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Transferring data (signals carry almost no information, just the signal number) - Reliable communication (signals can be lost or delayed) - Complex coordination (race conditions are common)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Sockets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-socket-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Socket Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A socket is a bidirectional communication endpoint. Unlike pipes, sockets support two-way data flow. Unlike named pipes, sockets support multiple concurrent connections, making them suitable for client-server architectures.&lt;br /&gt;
&lt;br /&gt;
There are two main types:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Sockets&amp;#039;&amp;#039;&amp;#039;: Use TCP or UDP to communicate over IP networks. These work across machines and are the foundation of the internet.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Unix Domain Sockets&amp;#039;&amp;#039;&amp;#039;: Use file paths (like named pipes) but support bidirectional communication and connection multiplexing. These work only on the same machine but are faster than network sockets.&lt;br /&gt;
&lt;br /&gt;
When a server process binds to a socket, it listens for incoming connections. Multiple client processes can connect to the same server socket. Each connection is independent, with its own bidirectional channel. This one-to-many pattern distinguishes sockets from pipes and FIFOs.&lt;br /&gt;
&lt;br /&gt;
For network sockets, the server binds to a port number (e.g., port 80 for HTTP). For Unix domain sockets, it binds to a filesystem path (e.g., &amp;lt;code&amp;gt;/tmp/myservice.sock&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-communication-from-bash&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Socket Communication from Bash ====&lt;br /&gt;
&lt;br /&gt;
While bash doesn’t have native socket support, we can use existing tools to demonstrate socket communication without writing low-level code:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;caddy&amp;#039;&amp;#039;&amp;#039;: A modern web server that can listen on both network and Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;curl&amp;#039;&amp;#039;&amp;#039;: A command-line HTTP client that supports Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;socat&amp;#039;&amp;#039;&amp;#039;: A general-purpose networking tool for creating and connecting to various socket types&lt;br /&gt;
&lt;br /&gt;
These tools abstract the complexity of socket programming, allowing us to focus on the conceptual model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Sockets ====&lt;br /&gt;
&lt;br /&gt;
Sockets are ideal for: - Client-server applications with multiple concurrent clients - Networked communication across machines - Bidirectional data exchange - Services that need to accept connections from many processes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; necessary for: - Simple one-to-one, one-way communication (use pipes instead) - Configuration passing (use environment variables) - Asynchronous notifications (use signals)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates how environment variables are inherited by child processes but not propagated back to parents.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Check your current environment. Run &amp;lt;code&amp;gt;env | head -n 10&amp;lt;/code&amp;gt; and observe some of the variables already set.&lt;br /&gt;
# Create a shell variable without exporting it: &amp;lt;code&amp;gt;myvar=&amp;amp;quot;not exported&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell with &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt; and try to read &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;echo &amp;amp;quot;$myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. It should be empty. Exit the subshell.&lt;br /&gt;
# Back in your original shell, export the variable: &amp;lt;code&amp;gt;export myvar=&amp;amp;quot;exported now&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell again with &amp;lt;code&amp;gt;bash -c &amp;#039;echo &amp;amp;quot;In child: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt; and verify that &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; is now accessible.&lt;br /&gt;
# In a subshell, modify the variable: &amp;lt;code&amp;gt;bash -c &amp;#039;myvar=&amp;amp;quot;changed in child&amp;amp;quot;; echo &amp;amp;quot;Child modified: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Print &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in the parent shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;Parent still has: $myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. Observe that the parent’s value is unchanged.&lt;br /&gt;
# Demonstrate setting an environment variable for a single command: &amp;lt;code&amp;gt;GREETING=&amp;amp;quot;Hello&amp;amp;quot; bash -c &amp;#039;echo $GREETING&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify that &amp;lt;code&amp;gt;GREETING&amp;lt;/code&amp;gt; is not set in your current shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;GREETING in parent: $GREETING&amp;amp;quot;&amp;lt;/code&amp;gt; (should be empty).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; to run a command with a specific environment: &amp;lt;code&amp;gt;env DEBUG=1 bash -c &amp;#039;echo &amp;amp;quot;DEBUG=$DEBUG&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide the output showing: the subshell cannot see the unexported variable, the subshell can see the exported variable, the parent is unaffected by child changes, and single-command environment variable setting.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-pipes-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Pipes in Practice ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores anonymous pipes and how they connect processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use a simple pipe to count lines: &amp;lt;code&amp;gt;cat /etc/passwd | wc -l&amp;lt;/code&amp;gt;. Observe the result.&lt;br /&gt;
# Build a longer pipeline to find how many unique shells are in use: &amp;lt;code&amp;gt;cat /etc/passwd | cut -d: -f7 | sort | uniq&amp;lt;/code&amp;gt;. Count them manually or pipe to &amp;lt;code&amp;gt;wc -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate that processes in a pipeline run concurrently. Run &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; command produces infinite output, but &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt; reads only 5 lines and then exits, causing &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to receive a &amp;lt;code&amp;gt;SIGPIPE&amp;lt;/code&amp;gt; and terminate.&lt;br /&gt;
# Create a sample log file for analysis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab6_sample.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout&lt;br /&gt;
2024-01-15 10:15:00 INFO Retry successful&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration&lt;br /&gt;
2024-01-15 10:25:00 WARN Low memory&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;5&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to count ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | wc -l&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to extract and count unique log levels: &amp;lt;code&amp;gt;cut -d&amp;#039; &amp;#039; -f3 /tmp/lab6_sample.log | sort | uniq -c&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Combine multiple operations: Find the timestamps of all ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | cut -d&amp;#039; &amp;#039; -f1,2&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the output from the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; shell analysis, the &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt; demonstration, and the log file analysis commands showing ERROR count and unique log levels with counts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates persistent named pipes and communication between unrelated processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a named pipe: &amp;lt;code&amp;gt;mkfifo /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify it exists and note its type: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;. The leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; indicates a FIFO.&lt;br /&gt;
# In one terminal, start a reader that will block: &amp;lt;code&amp;gt;cat &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;. Leave this running.&lt;br /&gt;
# In a second terminal, write to the FIFO: &amp;lt;code&amp;gt;echo &amp;amp;quot;Hello via FIFO&amp;amp;quot; &amp;amp;gt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe that the reader in the first terminal unblocks, displays the message, and exits.&lt;br /&gt;
# Demonstrate that the FIFO persists. List it again: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Test a more complex scenario. In terminal 1: &amp;lt;code&amp;gt;while read line; do echo &amp;amp;quot;Received: $line&amp;amp;quot;; done &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In terminal 2, send multiple messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Message 1&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 2&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 3&amp;quot; &amp;gt; /tmp/lab6_fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;9&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Observe the messages being received. Press Ctrl+C in terminal 1 to stop the reader.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Remove the FIFO: &amp;lt;code&amp;gt;rm /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the FIFO type, screenshots or output from both terminals showing the message exchange, and a brief explanation of how the FIFO persists between writes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates signal handling in bash using interactive commands.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Start a simple sleep command: &amp;lt;code&amp;gt;sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C to interrupt it. Observe that it terminates immediately.&lt;br /&gt;
# Now run a command that ignores SIGINT: &amp;lt;code&amp;gt;trap &amp;#039;echo &amp;amp;quot;Caught SIGINT, ignoring...&amp;amp;quot;&amp;#039; INT; sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C. The trap catches the signal and the sleep continues.&lt;br /&gt;
# Demonstrate cleanup on exit. Run this compound command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Cleanup: removing temp file&amp;quot;; rm -f /tmp/lab6_test.txt&amp;#039; EXIT; \&lt;br /&gt;
touch /tmp/lab6_test.txt; \&lt;br /&gt;
echo &amp;quot;File created. Press Ctrl+C or wait...&amp;quot;; \&lt;br /&gt;
sleep 10; \&lt;br /&gt;
echo &amp;quot;Normal exit&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;4&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Try both: let it complete normally, then run it again and interrupt with Ctrl+C. Observe cleanup happens both times.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start a background sleep: &amp;lt;code&amp;gt;sleep 100 &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID displayed.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGTERM to it: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; (replace &amp;lt;code&amp;gt;&amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; with the actual PID). Verify it terminated: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start another background process that will ignore SIGTERM:&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;(trap &amp;#039;echo &amp;quot;Caught SIGTERM, staying alive&amp;quot;&amp;#039; TERM; \&lt;br /&gt;
 while true; do echo &amp;quot;Running...&amp;quot;; sleep 5; done) &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;8&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Note the PID, then send SIGTERM: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. Observe the trap message.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGKILL to force termination: &amp;lt;code&amp;gt;kill -9 &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. This cannot be caught.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verify all background jobs are gone: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide output showing: the trapped SIGINT message, cleanup on both normal exit and Ctrl+C, the SIGTERM being caught with the trap message, and SIGKILL forcing termination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-socket-communication-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Socket Communication Basics ===&lt;br /&gt;
&lt;br /&gt;
This exercise provides a brief introduction to socket communication using existing tools.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a simple static site directory: &amp;lt;code&amp;gt;mkdir -p /tmp/lab6_site &amp;amp;amp;&amp;amp;amp; echo &amp;amp;quot;&amp;amp;lt;h1&amp;amp;gt;Hello from Caddy&amp;amp;lt;/h1&amp;amp;gt;&amp;amp;quot; &amp;amp;gt; /tmp/lab6_site/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start Caddy as a file server listening on a Unix domain socket: &amp;lt;code&amp;gt;caddy file-server --root /tmp/lab6_site --listen unix//tmp/caddy.sock &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID.&lt;br /&gt;
# Wait a moment for Caddy to start. Verify the socket exists: &amp;lt;code&amp;gt;ls -l /tmp/caddy.sock&amp;lt;/code&amp;gt;. Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; type indicating a socket.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; to make an HTTP request via the Unix domain socket: &amp;lt;code&amp;gt;curl -v --unix-socket /tmp/caddy.sock http://localhost/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the response. Note that the communication is bidirectional: you sent an HTTP request, and the server responded with the HTML content.&lt;br /&gt;
# (Optional) Open multiple terminals and run &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; simultaneously several times. Each request is handled independently, demonstrating the one-to-many capability of sockets.&lt;br /&gt;
# Stop Caddy by sending &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;caddy_pid&amp;amp;gt;&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;pkill caddy&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Clean up: &amp;lt;code&amp;gt;rm -rf /tmp/lab6_site /tmp/caddy.sock&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the socket file, the &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; output demonstrating the request and response (especially the HTTP headers and HTML body), and a brief explanation (2-3 sentences) of how this differs from a named pipe in terms of directionality and connection multiplexing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-aggregator-with-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log Aggregator with Named Pipes ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_log_aggregator.sh&amp;lt;/code&amp;gt; that aggregates log messages from multiple sources using named pipes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Create three named pipes: &amp;lt;code&amp;gt;/tmp/log_fifo1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo3&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Start three background processes that each write timestamped messages to one of the FIFOs (simulating different log sources). Each should write 5 messages at 1-second intervals.&lt;br /&gt;
* Implement a main loop that reads from all three FIFOs simultaneously (hint: use &amp;lt;code&amp;gt;select&amp;lt;/code&amp;gt; via &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; with timeout, or use multiple background readers).&lt;br /&gt;
* Write all received messages to a combined log file &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; with timestamps.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; to clean up: kill background processes, remove FIFOs, and exit gracefully.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, background processes (&amp;lt;code&amp;gt;&amp;amp;amp;&amp;lt;/code&amp;gt;), proper cleanup.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab6_log_aggregator.sh&lt;br /&gt;
/tmp/lab6_log_aggregator.sh &amp;amp;&lt;br /&gt;
AGG_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -TERM $AGG_PID&lt;br /&gt;
wait&lt;br /&gt;
cat /tmp/aggregated.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Contents of &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; showing interleaved messages from all three sources - Brief explanation (2-3 sentences) of why named pipes were necessary here instead of anonymous pipes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-graceful-service-controller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Graceful Service Controller ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_service_controller.sh&amp;lt;/code&amp;gt; that manages a long-running service and responds to signals.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* The script acts as a daemon that runs indefinitely, printing a heartbeat message every 5 seconds.&lt;br /&gt;
* Accept one optional argument: a “config file” path. If provided, read a setting (e.g., &amp;lt;code&amp;gt;INTERVAL=10&amp;lt;/code&amp;gt;) from the file to control the heartbeat interval.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;: Reload the configuration file and adjust the interval dynamically without restarting the script. Print “Configuration reloaded.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: Perform graceful shutdown. Print “Shutting down gracefully…”, wait 2 seconds, then exit cleanly.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt;: Export the current status to &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; (e.g., uptime, number of heartbeats sent, current interval). Print “Status exported.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt;: Perform cleanup. Print “Service stopped.”&lt;br /&gt;
* Maintain internal state: count the number of heartbeats sent, track start time.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for multiple signals, variables for state, a loop, cleanup handler.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;INTERVAL=3&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
chmod +x /tmp/lab6_service_controller.sh&lt;br /&gt;
/tmp/lab6_service_controller.sh /tmp/service.conf &amp;amp;&lt;br /&gt;
SVC_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -USR1 $SVC_PID  # Export status&lt;br /&gt;
cat /tmp/service_status.txt&lt;br /&gt;
echo &amp;quot;INTERVAL=1&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
kill -HUP $SVC_PID  # Reload config&lt;br /&gt;
sleep 5&lt;br /&gt;
kill -TERM $SVC_PID  # Graceful shutdown&lt;br /&gt;
wait&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Output showing heartbeats before and after &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (with visible interval change) - Contents of &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; after &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; - Output showing graceful shutdown message after &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-ipc-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common IPC Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for IPC mechanisms covered in this lab:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Environment Variables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Export a variable for child processes&lt;br /&gt;
export VAR=&amp;quot;value&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Set for one command only&lt;br /&gt;
VAR=&amp;quot;value&amp;quot; command&lt;br /&gt;
&lt;br /&gt;
# View all environment variables&lt;br /&gt;
env&lt;br /&gt;
printenv&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Anonymous Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Simple pipeline&lt;br /&gt;
command1 | command2&lt;br /&gt;
&lt;br /&gt;
# Multi-stage pipeline&lt;br /&gt;
cat file.txt | grep &amp;quot;pattern&amp;quot; | sort | uniq&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Named Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create a FIFO&lt;br /&gt;
mkfifo /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Write to FIFO (blocks until reader connects)&lt;br /&gt;
echo &amp;quot;data&amp;quot; &amp;gt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Read from FIFO (blocks until writer connects)&lt;br /&gt;
cat &amp;lt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Remove FIFO&lt;br /&gt;
rm /path/to/fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Send signal by name&lt;br /&gt;
kill -TERM &amp;lt;pid&amp;gt;&lt;br /&gt;
kill -HUP &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Send signal by number&lt;br /&gt;
kill -15 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Force kill (cannot be caught)&lt;br /&gt;
kill -9 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trap signals in a script&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught signal&amp;quot;&amp;#039; INT TERM&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Define cleanup function&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myfiles.*&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sockets (using existing tools):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start a server on Unix domain socket (using socat)&lt;br /&gt;
socat UNIX-LISTEN:/tmp/service.sock,fork EXEC:&amp;#039;/usr/bin/myhandler&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Connect as client (using socat)&lt;br /&gt;
echo &amp;quot;request&amp;quot; | socat - UNIX-CONNECT:/tmp/service.sock&lt;br /&gt;
&lt;br /&gt;
# HTTP via Unix socket (using curl)&lt;br /&gt;
curl --unix-socket /tmp/service.sock http://localhost/path&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-patterns-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Patterns Table ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mechanism&lt;br /&gt;
! Direction&lt;br /&gt;
! Persistence&lt;br /&gt;
! Use Case&lt;br /&gt;
|-&lt;br /&gt;
| Environment Variables&lt;br /&gt;
| Parent → Child (one-way)&lt;br /&gt;
| Inherited at fork&lt;br /&gt;
| Configuration, credentials&lt;br /&gt;
|-&lt;br /&gt;
| Pipes (&amp;lt;code&amp;gt;\|&amp;lt;/code&amp;gt;)&lt;br /&gt;
| One-way&lt;br /&gt;
| Ephemeral (process lifetime)&lt;br /&gt;
| Command chaining, streaming&lt;br /&gt;
|-&lt;br /&gt;
| Named Pipes (FIFO)&lt;br /&gt;
| One-way&lt;br /&gt;
| Filesystem entry persists&lt;br /&gt;
| Unrelated process communication&lt;br /&gt;
|-&lt;br /&gt;
| Signals&lt;br /&gt;
| One-way (notification)&lt;br /&gt;
| Asynchronous event&lt;br /&gt;
| Process control, event notification&lt;br /&gt;
|-&lt;br /&gt;
| Sockets&lt;br /&gt;
| Bidirectional&lt;br /&gt;
| Filesystem entry persists (Unix domain)&lt;br /&gt;
| Client-server, network services&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Exercise A: Outputs demonstrating export behavior, parent-child isolation, and single-command environment variables&lt;br /&gt;
* Exercise B: Pipeline outputs and complete analysis script&lt;br /&gt;
* Exercise C: FIFO creation output, producer/consumer scripts with sample output&lt;br /&gt;
* Exercise D: Complete daemon simulation script with signal handling demonstrations&lt;br /&gt;
* Exercise E: Socket file listing, curl output with HTTP headers, explanation of socket vs FIFO differences&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Challenge 1: Complete log aggregator script, aggregated log contents, explanation&lt;br /&gt;
* Challenge 2: Complete service controller script, outputs demonstrating all signal handlers (SIGHUP reload, SIGUSR1 status export, SIGTERM shutdown)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Additional:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Each deliverable should include command outputs (screenshots or text) and brief explanations where requested.&lt;br /&gt;
* For scripts, include the complete, commented source code and example execution output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab covers the fundamental IPC mechanisms accessible from bash. You’ve learned how processes inherit environment variables, communicate via pipes, respond to signals, and use sockets for network communication. These concepts form the foundation for system administration, scripting, and understanding how complex systems coordinate.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
* Advanced IPC: Shared memory, message queues, semaphores (typically used in C/C++ programs, not bash)&lt;br /&gt;
* Network programming: TCP/UDP sockets, client-server architecture&lt;br /&gt;
* D-Bus: A modern IPC system used by desktop environments and systemd&lt;br /&gt;
* Signal safety: Writing robust signal handlers (critical in C, less relevant in bash)&lt;br /&gt;
&lt;br /&gt;
Relevant manual pages:&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 pipe&amp;lt;/code&amp;gt; - Pipe overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 fifo&amp;lt;/code&amp;gt; - Named pipe overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 signal&amp;lt;/code&amp;gt; - Signal overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 unix&amp;lt;/code&amp;gt; - Unix domain sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt; - Section on trap and job control&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8177</id>
		<title>OS Lab 6 - Inter Process Communication</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8177"/>
		<updated>2025-11-14T15:05:52Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Additional Resources */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel provides IPC mechanisms and how bash exposes them to scripts.&lt;br /&gt;
* Use environment variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; to pass configuration from parent processes to children.&lt;br /&gt;
* Construct pipelines with anonymous pipes to connect process standard streams.&lt;br /&gt;
* Create and use named pipes (FIFOs) for communication between unrelated processes.&lt;br /&gt;
* Send signals to processes and handle them with &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for asynchronous event-driven scripting.&lt;br /&gt;
* Demonstrate basic socket communication using existing utilities for network IPC.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 5, we explored bash scripting as a way to automate tasks by writing programs that the shell interprets. We learned how processes execute scripts, how to control flow with conditionals and loops, and how to structure code with functions. However, all of our scripts operated in isolation. Each script was a single process (or spawned child processes) that performed its task independently.&lt;br /&gt;
&lt;br /&gt;
Real-world systems require processes to cooperate. A web server must communicate with a database. A shell pipeline connects the output of one program to the input of another. A service must respond to signals sent by the init system. This lab explores the kernel-provided mechanisms that enable processes to exchange data and coordinate their actions. We focus on five fundamental IPC mechanisms, all accessible from bash: environment variables, pipes, named pipes, signals, and sockets.&lt;br /&gt;
&lt;br /&gt;
Understanding IPC is essential for system administration and software development. These mechanisms form the foundation of everything from command pipelines to distributed systems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy socat&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: A modern HTTP server with automatic HTTPS. We use it to demonstrate socket communication.&lt;br /&gt;
* &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;: A versatile networking tool that can work with Unix domain sockets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with: - Process concepts from Lab 3 (PIDs, process hierarchy, file descriptors) - File permissions from Lab 4 (execute bit, ownership) - Bash scripting fundamentals from Lab 5 (shebangs, builtins, variables, quoting, exit status, redirection, loops, functions)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;inter-process-communication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Inter-Process Communication ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-ipc&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What is IPC? ===&lt;br /&gt;
&lt;br /&gt;
By default, processes are isolated from one another. Each process has its own memory space, file descriptor table, and execution context. This isolation provides security and stability, but it creates a problem: how can processes cooperate to accomplish complex tasks?&lt;br /&gt;
&lt;br /&gt;
Inter-Process Communication (IPC) refers to the kernel-provided mechanisms that allow processes to exchange data and synchronize their actions. The kernel acts as an intermediary, providing channels, buffers, and signaling primitives that processes can use to communicate safely without violating isolation boundaries.&lt;br /&gt;
&lt;br /&gt;
In this lab, we examine five IPC mechanisms arranged roughly by complexity:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Environment Variables&amp;#039;&amp;#039;&amp;#039;: The simplest form of IPC. A parent process passes key-value configuration to its children via inherited environment variables. Communication is unidirectional (parent → child) and occurs only at process creation.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Pipes&amp;#039;&amp;#039;&amp;#039;: The kernel creates a buffer connecting one process’s standard output to another’s standard input. Data flows in one direction through the pipe. Anonymous pipes exist only while the processes using them are running.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Named Pipes (FIFOs)&amp;#039;&amp;#039;&amp;#039;: Like anonymous pipes, but visible in the filesystem. This persistence allows unrelated processes to connect to the same pipe by opening a file path.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Signals&amp;#039;&amp;#039;&amp;#039;: Asynchronous notifications sent from one process to another (or from the kernel to a process). Signals interrupt the receiving process, which can catch and handle them or allow default behavior (often termination).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Sockets&amp;#039;&amp;#039;&amp;#039;: Bidirectional communication channels that work across network boundaries or locally via Unix domain sockets. Multiple processes can connect to the same socket, enabling one-to-many communication patterns.&lt;br /&gt;
&lt;br /&gt;
Each mechanism solves different problems and has different trade-offs in terms of complexity, performance, and flexibility.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Role ====&lt;br /&gt;
&lt;br /&gt;
When a process forks, the child inherits a copy of the parent’s environment: a set of key-value string pairs maintained by the kernel for each process. The child can read these values and modify its own copy, but changes do not propagate back to the parent or to other processes. This inheritance mechanism provides a simple, unidirectional channel for passing configuration from parent to child.&lt;br /&gt;
&lt;br /&gt;
Environment variables are not limited to shell scripts. Every process has an environment. When you run any program, it receives the environment from the shell that launched it. Programs written in C access this via the &amp;lt;code&amp;gt;environ&amp;lt;/code&amp;gt; global variable or the third parameter to &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;. Python uses &amp;lt;code&amp;gt;os.environ&amp;lt;/code&amp;gt;. The environment is a universal convention for passing configuration.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-role-export-and-env&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Role: &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
In bash, variables are local to the shell process by default. When you set &amp;lt;code&amp;gt;name=value&amp;lt;/code&amp;gt;, that variable exists in the shell’s memory but is &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; passed to child processes. The &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; builtin marks a variable for inclusion in the environment of future child processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
export myvar&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or, more concisely:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export myvar=&amp;quot;hello&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Once exported, all child processes started by this shell (scripts, programs, or subshells) will inherit &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in their environment. To see the current environment, use the &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; command (or &amp;lt;code&amp;gt;printenv&amp;lt;/code&amp;gt;), which prints all exported variables.&lt;br /&gt;
&lt;br /&gt;
You can also set environment variables for a single command without affecting the shell:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;DEBUG=1 ./myscript.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This syntax sets &amp;lt;code&amp;gt;DEBUG=1&amp;lt;/code&amp;gt; in the environment of &amp;lt;code&amp;gt;myscript.sh&amp;lt;/code&amp;gt; only, without exporting it in the parent shell.&lt;br /&gt;
&lt;br /&gt;
Common environment variables you’ve already been using include &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (where bash searches for commands), &amp;lt;code&amp;gt;HOME&amp;lt;/code&amp;gt; (your home directory), &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; (your username), and &amp;lt;code&amp;gt;SHELL&amp;lt;/code&amp;gt; (your login shell). These are all set by the login process and inherited by every subsequent process in your session.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-environment-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Environment Variables ====&lt;br /&gt;
&lt;br /&gt;
Environment variables are appropriate for: - Configuration that should be inherited by all child processes (e.g., locale settings, proxy configuration) - Passing secrets or credentials to programs without embedding them in command-line arguments (which are visible via &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt;) - Controlling program behavior via well-known variables like &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LD_LIBRARY_PATH&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;TZ&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Runtime communication between already-running processes (use pipes, sockets, or signals) - Large amounts of data (environment is limited in size, typically a few megabytes) - Bi-directional communication (child changes don’t affect parent)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Pipes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-pipe-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Pipe Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A pipe is a one-way data channel maintained in kernel memory. When you create a pipe, the kernel allocates a buffer (typically 64 KB on Linux) and returns two file descriptors: one for writing and one for reading. Data written to the write end is buffered by the kernel and can be read from the read end in FIFO (first-in, first-out) order.&lt;br /&gt;
&lt;br /&gt;
Pipes are anonymous: they have no name in the filesystem. They exist only as long as at least one process holds a file descriptor to them. When all processes close their references to a pipe, the kernel deallocates it.&lt;br /&gt;
&lt;br /&gt;
The key insight from Lab 3: a pipeline like &amp;lt;code&amp;gt;cat file.txt | grep &amp;amp;quot;error&amp;amp;quot; | wc -l&amp;lt;/code&amp;gt; creates multiple processes (all in the same process group) connected by pipes. The kernel sets up the file descriptors so that &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdin, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;’s stdin. The processes run concurrently, with data flowing through kernel buffers as it’s produced and consumed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-pipe-operator&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Pipe Operator ====&lt;br /&gt;
&lt;br /&gt;
Bash creates pipes using the &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; operator. The syntax is simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command1 | command2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a pipe and starts two processes. Bash configures &amp;lt;code&amp;gt;command1&amp;lt;/code&amp;gt;’s stdout (FD 1) to point to the pipe’s write end and &amp;lt;code&amp;gt;command2&amp;lt;/code&amp;gt;’s stdin (FD 0) to point to the pipe’s read end. The commands execute concurrently.&lt;br /&gt;
&lt;br /&gt;
Longer pipelines work the same way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat data.txt | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates three pipes connecting four processes. Data flows left-to-right through kernel buffers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes-in-scripts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes in Scripts ====&lt;br /&gt;
&lt;br /&gt;
You can use pipes inside scripts just as you would interactively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# Count unique IP addresses in an access log&lt;br /&gt;
cat /var/log/access.log | cut -d&amp;#039; &amp;#039; -f1 | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pipes are particularly powerful when combined with bash’s process substitution feature &amp;lt;code&amp;gt;&amp;amp;lt;(command)&amp;lt;/code&amp;gt;, but that’s beyond the scope of this introductory lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Pipes ====&lt;br /&gt;
&lt;br /&gt;
Pipes are ideal for: - Connecting the output of one program to the input of another in a linear processing chain - Streaming data processing where data is generated and consumed incrementally - Quick, one-off data transformations at the command line&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Bi-directional communication (data flows one way only) - Communication between unrelated processes that weren’t started in a pipeline - Persistent communication (pipe disappears when processes exit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-fifo-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s FIFO Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A named pipe, or FIFO (First-In, First-Out), is a pipe with a name in the filesystem. Unlike anonymous pipes, FIFOs persist as filesystem entries (though the data buffer is still in kernel memory). Any process with appropriate permissions can open the FIFO by its path, allowing unrelated processes to communicate.&lt;br /&gt;
&lt;br /&gt;
When a process opens a FIFO for reading, it blocks until another process opens the same FIFO for writing (and vice versa). Once both ends are open, data flows through the kernel buffer just like an anonymous pipe. When all processes close their connections, the FIFO remains as a filesystem entry but the kernel buffer is deallocated.&lt;br /&gt;
&lt;br /&gt;
You can identify a FIFO in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output by the leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; in the permission string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;prw-r--r-- 1 user user 0 Nov  6 10:00 myfifo&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;creating-fifos-with-mkfifo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Creating FIFOs with &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; command creates a named pipe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkfifo /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now two unrelated processes can communicate by opening this file. One writes to it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Hello from writer&amp;quot; &amp;gt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command will block until a reader appears. In another terminal (or in the background), a reader can consume the data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When the reader connects, the writer unblocks, the message flows through the kernel buffer, and both commands complete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Named Pipes ====&lt;br /&gt;
&lt;br /&gt;
Named pipes are useful for: - Communication between unrelated processes that start at different times - Producer-consumer patterns where one process generates data and another processes it - Simple IPC without needing network sockets or shared files&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Multiple simultaneous readers or writers (FIFO semantics become unpredictable) - Persistent data storage (data is lost when all processes disconnect) - Bi-directional communication (like anonymous pipes, FIFOs are one-way)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-signal-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Signal Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A signal is an asynchronous notification sent to a process. Signals can be sent by other processes (via the &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt; system call) or by the kernel itself in response to events like segmentation faults, keyboard interrupts (Ctrl+C), or child process termination.&lt;br /&gt;
&lt;br /&gt;
When the kernel delivers a signal to a process, it interrupts the process’s normal execution. The process can respond in one of three ways:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Default Action&amp;#039;&amp;#039;&amp;#039;: Each signal has a default behavior, often terminating the process. For example, &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (signal 15) gracefully terminates, while &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (signal 9) forces immediate termination.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ignore&amp;#039;&amp;#039;&amp;#039;: The process can choose to ignore certain signals (except &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;, which cannot be caught or ignored).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom Handler&amp;#039;&amp;#039;&amp;#039;: The process can register a function (signal handler) to execute when the signal arrives. This allows the process to perform cleanup or take other actions before deciding whether to continue, terminate, or take other action.&lt;br /&gt;
&lt;br /&gt;
Common signals: - &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; (2): Sent by Ctrl+C. Default: terminate. - &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (15): Polite request to terminate. Default: terminate. Most services handle this to perform clean shutdown. - &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (9): Immediate termination. Cannot be caught or ignored. Used as a last resort. - &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (1): Historically “hang up” (modem disconnected). Often used to tell daemons to reload configuration. - &amp;lt;code&amp;gt;SIGCHLD&amp;lt;/code&amp;gt; (17): Sent to a parent when a child process terminates. - &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; (10) and &amp;lt;code&amp;gt;SIGUSR2&amp;lt;/code&amp;gt; (12): User-defined signals for custom purposes.&lt;br /&gt;
&lt;br /&gt;
Signals are sent by PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;kill -TERM 1234    # Send SIGTERM to process 1234&lt;br /&gt;
kill -9 1234       # Send SIGKILL to process 1234&lt;br /&gt;
kill -HUP 1234     # Send SIGHUP to process 1234&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also use the &amp;lt;code&amp;gt;%n&amp;lt;/code&amp;gt; job notation from Lab 3 to send signals to background jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-trap-builtin&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; Builtin ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin allows a bash script to register a handler for incoming signals:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Caught SIGINT&amp;quot;; exit&amp;#039; INT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This tells bash: “When SIGINT arrives, execute the command &amp;lt;code&amp;gt;echo &amp;amp;quot;Caught SIGINT&amp;amp;quot;; exit&amp;lt;/code&amp;gt;.” The handler can be any bash command or function.&lt;br /&gt;
&lt;br /&gt;
Common pattern for cleanup on exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up temporary files...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myscript.*&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Script body&lt;br /&gt;
echo &amp;quot;Running...&amp;quot;&lt;br /&gt;
sleep 10&lt;br /&gt;
echo &amp;quot;Done&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cleanup() is automatically called on normal exit, Ctrl+C, or errors (with set -e)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The special signal &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt; isn’t a real signal; it’s a bash pseudo-signal that fires whenever the script exits for any reason.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-signals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Signals ====&lt;br /&gt;
&lt;br /&gt;
Signals are appropriate for: - Event-driven scripts that respond to external events - Graceful shutdown and cleanup on termination - Daemon control (reload config with &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;, graceful restart with &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;) - Inter-process coordination where one process needs to notify another of state changes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Transferring data (signals carry almost no information, just the signal number) - Reliable communication (signals can be lost or delayed) - Complex coordination (race conditions are common)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Sockets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-socket-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Socket Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A socket is a bidirectional communication endpoint. Unlike pipes, sockets support two-way data flow. Unlike named pipes, sockets support multiple concurrent connections, making them suitable for client-server architectures.&lt;br /&gt;
&lt;br /&gt;
There are two main types:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Sockets&amp;#039;&amp;#039;&amp;#039;: Use TCP or UDP to communicate over IP networks. These work across machines and are the foundation of the internet.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Unix Domain Sockets&amp;#039;&amp;#039;&amp;#039;: Use file paths (like named pipes) but support bidirectional communication and connection multiplexing. These work only on the same machine but are faster than network sockets.&lt;br /&gt;
&lt;br /&gt;
When a server process binds to a socket, it listens for incoming connections. Multiple client processes can connect to the same server socket. Each connection is independent, with its own bidirectional channel. This one-to-many pattern distinguishes sockets from pipes and FIFOs.&lt;br /&gt;
&lt;br /&gt;
For network sockets, the server binds to a port number (e.g., port 80 for HTTP). For Unix domain sockets, it binds to a filesystem path (e.g., &amp;lt;code&amp;gt;/tmp/myservice.sock&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-communication-from-bash&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Socket Communication from Bash ====&lt;br /&gt;
&lt;br /&gt;
While bash doesn’t have native socket support, we can use existing tools to demonstrate socket communication without writing low-level code:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;caddy&amp;#039;&amp;#039;&amp;#039;: A modern web server that can listen on both network and Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;curl&amp;#039;&amp;#039;&amp;#039;: A command-line HTTP client that supports Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;socat&amp;#039;&amp;#039;&amp;#039;: A general-purpose networking tool for creating and connecting to various socket types&lt;br /&gt;
&lt;br /&gt;
These tools abstract the complexity of socket programming, allowing us to focus on the conceptual model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Sockets ====&lt;br /&gt;
&lt;br /&gt;
Sockets are ideal for: - Client-server applications with multiple concurrent clients - Networked communication across machines - Bidirectional data exchange - Services that need to accept connections from many processes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; necessary for: - Simple one-to-one, one-way communication (use pipes instead) - Configuration passing (use environment variables) - Asynchronous notifications (use signals)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates how environment variables are inherited by child processes but not propagated back to parents.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Check your current environment. Run &amp;lt;code&amp;gt;env | head -n 10&amp;lt;/code&amp;gt; and observe some of the variables already set.&lt;br /&gt;
# Create a shell variable without exporting it: &amp;lt;code&amp;gt;myvar=&amp;amp;quot;not exported&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell with &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt; and try to read &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;echo &amp;amp;quot;$myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. It should be empty. Exit the subshell.&lt;br /&gt;
# Back in your original shell, export the variable: &amp;lt;code&amp;gt;export myvar=&amp;amp;quot;exported now&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell again with &amp;lt;code&amp;gt;bash -c &amp;#039;echo &amp;amp;quot;In child: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt; and verify that &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; is now accessible.&lt;br /&gt;
# In a subshell, modify the variable: &amp;lt;code&amp;gt;bash -c &amp;#039;myvar=&amp;amp;quot;changed in child&amp;amp;quot;; echo &amp;amp;quot;Child modified: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Print &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in the parent shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;Parent still has: $myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. Observe that the parent’s value is unchanged.&lt;br /&gt;
# Demonstrate setting an environment variable for a single command: &amp;lt;code&amp;gt;GREETING=&amp;amp;quot;Hello&amp;amp;quot; bash -c &amp;#039;echo $GREETING&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify that &amp;lt;code&amp;gt;GREETING&amp;lt;/code&amp;gt; is not set in your current shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;GREETING in parent: $GREETING&amp;amp;quot;&amp;lt;/code&amp;gt; (should be empty).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; to run a command with a specific environment: &amp;lt;code&amp;gt;env DEBUG=1 bash -c &amp;#039;echo &amp;amp;quot;DEBUG=$DEBUG&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide the output showing: the subshell cannot see the unexported variable, the subshell can see the exported variable, the parent is unaffected by child changes, and single-command environment variable setting.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-pipes-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Pipes in Practice ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores anonymous pipes and how they connect processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use a simple pipe to count lines: &amp;lt;code&amp;gt;cat /etc/passwd | wc -l&amp;lt;/code&amp;gt;. Observe the result.&lt;br /&gt;
# Build a longer pipeline to find how many unique shells are in use: &amp;lt;code&amp;gt;cat /etc/passwd | cut -d: -f7 | sort | uniq&amp;lt;/code&amp;gt;. Count them manually or pipe to &amp;lt;code&amp;gt;wc -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate that processes in a pipeline run concurrently. Run &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; command produces infinite output, but &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt; reads only 5 lines and then exits, causing &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to receive a &amp;lt;code&amp;gt;SIGPIPE&amp;lt;/code&amp;gt; and terminate.&lt;br /&gt;
# Create a sample log file for analysis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab6_sample.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout&lt;br /&gt;
2024-01-15 10:15:00 INFO Retry successful&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration&lt;br /&gt;
2024-01-15 10:25:00 WARN Low memory&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;5&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to count ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | wc -l&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to extract and count unique log levels: &amp;lt;code&amp;gt;cut -d&amp;#039; &amp;#039; -f3 /tmp/lab6_sample.log | sort | uniq -c&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Combine multiple operations: Find the timestamps of all ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | cut -d&amp;#039; &amp;#039; -f1,2&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the output from the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; shell analysis, the &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt; demonstration, and the log file analysis commands showing ERROR count and unique log levels with counts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates persistent named pipes and communication between unrelated processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a named pipe: &amp;lt;code&amp;gt;mkfifo /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify it exists and note its type: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;. The leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; indicates a FIFO.&lt;br /&gt;
# In one terminal, start a reader that will block: &amp;lt;code&amp;gt;cat &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;. Leave this running.&lt;br /&gt;
# In a second terminal, write to the FIFO: &amp;lt;code&amp;gt;echo &amp;amp;quot;Hello via FIFO&amp;amp;quot; &amp;amp;gt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe that the reader in the first terminal unblocks, displays the message, and exits.&lt;br /&gt;
# Demonstrate that the FIFO persists. List it again: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Test a more complex scenario. In terminal 1: &amp;lt;code&amp;gt;while read line; do echo &amp;amp;quot;Received: $line&amp;amp;quot;; done &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In terminal 2, send multiple messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Message 1&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 2&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 3&amp;quot; &amp;gt; /tmp/lab6_fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;9&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Observe the messages being received. Press Ctrl+C in terminal 1 to stop the reader.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Remove the FIFO: &amp;lt;code&amp;gt;rm /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the FIFO type, screenshots or output from both terminals showing the message exchange, and a brief explanation of how the FIFO persists between writes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates signal handling in bash using interactive commands.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Start a simple sleep command: &amp;lt;code&amp;gt;sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C to interrupt it. Observe that it terminates immediately.&lt;br /&gt;
# Now run a command that ignores SIGINT: &amp;lt;code&amp;gt;trap &amp;#039;echo &amp;amp;quot;Caught SIGINT, ignoring...&amp;amp;quot;&amp;#039; INT; sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C. The trap catches the signal and the sleep continues.&lt;br /&gt;
# Demonstrate cleanup on exit. Run this compound command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Cleanup: removing temp file&amp;quot;; rm -f /tmp/lab6_test.txt&amp;#039; EXIT; \&lt;br /&gt;
touch /tmp/lab6_test.txt; \&lt;br /&gt;
echo &amp;quot;File created. Press Ctrl+C or wait...&amp;quot;; \&lt;br /&gt;
sleep 10; \&lt;br /&gt;
echo &amp;quot;Normal exit&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;4&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Try both: let it complete normally, then run it again and interrupt with Ctrl+C. Observe cleanup happens both times.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start a background sleep: &amp;lt;code&amp;gt;sleep 100 &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID displayed.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGTERM to it: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; (replace &amp;lt;code&amp;gt;&amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; with the actual PID). Verify it terminated: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start another background process that will ignore SIGTERM:&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;(trap &amp;#039;echo &amp;quot;Caught SIGTERM, staying alive&amp;quot;&amp;#039; TERM; \&lt;br /&gt;
 while true; do echo &amp;quot;Running...&amp;quot;; sleep 5; done) &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;8&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Note the PID, then send SIGTERM: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. Observe the trap message.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGKILL to force termination: &amp;lt;code&amp;gt;kill -9 &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. This cannot be caught.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verify all background jobs are gone: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide output showing: the trapped SIGINT message, cleanup on both normal exit and Ctrl+C, the SIGTERM being caught with the trap message, and SIGKILL forcing termination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-socket-communication-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Socket Communication Basics ===&lt;br /&gt;
&lt;br /&gt;
This exercise provides a brief introduction to socket communication using existing tools.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a simple static site directory: &amp;lt;code&amp;gt;mkdir -p /tmp/lab6_site &amp;amp;amp;&amp;amp;amp; echo &amp;amp;quot;&amp;amp;lt;h1&amp;amp;gt;Hello from Caddy&amp;amp;lt;/h1&amp;amp;gt;&amp;amp;quot; &amp;amp;gt; /tmp/lab6_site/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start Caddy as a file server listening on a Unix domain socket: &amp;lt;code&amp;gt;caddy file-server --root /tmp/lab6_site --listen unix//tmp/caddy.sock &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID.&lt;br /&gt;
# Wait a moment for Caddy to start. Verify the socket exists: &amp;lt;code&amp;gt;ls -l /tmp/caddy.sock&amp;lt;/code&amp;gt;. Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; type indicating a socket.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; to make an HTTP request via the Unix domain socket: &amp;lt;code&amp;gt;curl -v --unix-socket /tmp/caddy.sock http://localhost/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the response. Note that the communication is bidirectional: you sent an HTTP request, and the server responded with the HTML content.&lt;br /&gt;
# (Optional) Open multiple terminals and run &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; simultaneously several times. Each request is handled independently, demonstrating the one-to-many capability of sockets.&lt;br /&gt;
# Stop Caddy by sending &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;caddy_pid&amp;amp;gt;&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;pkill caddy&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Clean up: &amp;lt;code&amp;gt;rm -rf /tmp/lab6_site /tmp/caddy.sock&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the socket file, the &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; output demonstrating the request and response (especially the HTTP headers and HTML body), and a brief explanation (2-3 sentences) of how this differs from a named pipe in terms of directionality and connection multiplexing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-aggregator-with-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log Aggregator with Named Pipes ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_log_aggregator.sh&amp;lt;/code&amp;gt; that aggregates log messages from multiple sources using named pipes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Create three named pipes: &amp;lt;code&amp;gt;/tmp/log_fifo1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo3&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Start three background processes that each write timestamped messages to one of the FIFOs (simulating different log sources). Each should write 5 messages at 1-second intervals.&lt;br /&gt;
* Implement a main loop that reads from all three FIFOs simultaneously (hint: use &amp;lt;code&amp;gt;select&amp;lt;/code&amp;gt; via &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; with timeout, or use multiple background readers).&lt;br /&gt;
* Write all received messages to a combined log file &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; with timestamps.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; to clean up: kill background processes, remove FIFOs, and exit gracefully.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, background processes (&amp;lt;code&amp;gt;&amp;amp;amp;&amp;lt;/code&amp;gt;), proper cleanup.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab6_log_aggregator.sh&lt;br /&gt;
/tmp/lab6_log_aggregator.sh &amp;amp;&lt;br /&gt;
AGG_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -TERM $AGG_PID&lt;br /&gt;
wait&lt;br /&gt;
cat /tmp/aggregated.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Contents of &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; showing interleaved messages from all three sources - Brief explanation (2-3 sentences) of why named pipes were necessary here instead of anonymous pipes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-graceful-service-controller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Graceful Service Controller ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_service_controller.sh&amp;lt;/code&amp;gt; that manages a long-running service and responds to signals.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* The script acts as a daemon that runs indefinitely, printing a heartbeat message every 5 seconds.&lt;br /&gt;
* Accept one optional argument: a “config file” path. If provided, read a setting (e.g., &amp;lt;code&amp;gt;INTERVAL=10&amp;lt;/code&amp;gt;) from the file to control the heartbeat interval.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;: Reload the configuration file and adjust the interval dynamically without restarting the script. Print “Configuration reloaded.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: Perform graceful shutdown. Print “Shutting down gracefully…”, wait 2 seconds, then exit cleanly.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt;: Export the current status to &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; (e.g., uptime, number of heartbeats sent, current interval). Print “Status exported.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt;: Perform cleanup. Print “Service stopped.”&lt;br /&gt;
* Maintain internal state: count the number of heartbeats sent, track start time.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for multiple signals, variables for state, a loop, cleanup handler.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;INTERVAL=3&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
chmod +x /tmp/lab6_service_controller.sh&lt;br /&gt;
/tmp/lab6_service_controller.sh /tmp/service.conf &amp;amp;&lt;br /&gt;
SVC_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -USR1 $SVC_PID  # Export status&lt;br /&gt;
cat /tmp/service_status.txt&lt;br /&gt;
echo &amp;quot;INTERVAL=1&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
kill -HUP $SVC_PID  # Reload config&lt;br /&gt;
sleep 5&lt;br /&gt;
kill -TERM $SVC_PID  # Graceful shutdown&lt;br /&gt;
wait&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Output showing heartbeats before and after &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (with visible interval change) - Contents of &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; after &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; - Output showing graceful shutdown message after &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-ipc-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common IPC Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for IPC mechanisms covered in this lab:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Environment Variables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Export a variable for child processes&lt;br /&gt;
export VAR=&amp;quot;value&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Set for one command only&lt;br /&gt;
VAR=&amp;quot;value&amp;quot; command&lt;br /&gt;
&lt;br /&gt;
# View all environment variables&lt;br /&gt;
env&lt;br /&gt;
printenv&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Anonymous Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Simple pipeline&lt;br /&gt;
command1 | command2&lt;br /&gt;
&lt;br /&gt;
# Multi-stage pipeline&lt;br /&gt;
cat file.txt | grep &amp;quot;pattern&amp;quot; | sort | uniq&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Named Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create a FIFO&lt;br /&gt;
mkfifo /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Write to FIFO (blocks until reader connects)&lt;br /&gt;
echo &amp;quot;data&amp;quot; &amp;gt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Read from FIFO (blocks until writer connects)&lt;br /&gt;
cat &amp;lt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Remove FIFO&lt;br /&gt;
rm /path/to/fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Send signal by name&lt;br /&gt;
kill -TERM &amp;lt;pid&amp;gt;&lt;br /&gt;
kill -HUP &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Send signal by number&lt;br /&gt;
kill -15 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Force kill (cannot be caught)&lt;br /&gt;
kill -9 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trap signals in a script&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught signal&amp;quot;&amp;#039; INT TERM&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Define cleanup function&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myfiles.*&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sockets (using existing tools):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start a server on Unix domain socket (using socat)&lt;br /&gt;
socat UNIX-LISTEN:/tmp/service.sock,fork EXEC:&amp;#039;/usr/bin/myhandler&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Connect as client (using socat)&lt;br /&gt;
echo &amp;quot;request&amp;quot; | socat - UNIX-CONNECT:/tmp/service.sock&lt;br /&gt;
&lt;br /&gt;
# HTTP via Unix socket (using curl)&lt;br /&gt;
curl --unix-socket /tmp/service.sock http://localhost/path&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-patterns-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Patterns Table ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mechanism&lt;br /&gt;
! Direction&lt;br /&gt;
! Persistence&lt;br /&gt;
! Use Case&lt;br /&gt;
|-&lt;br /&gt;
| Environment Variables&lt;br /&gt;
| Parent → Child (one-way)&lt;br /&gt;
| Inherited at fork&lt;br /&gt;
| Configuration, credentials&lt;br /&gt;
|-&lt;br /&gt;
| Pipes (&amp;lt;code&amp;gt;\|&amp;lt;/code&amp;gt;)&lt;br /&gt;
| One-way&lt;br /&gt;
| Ephemeral (process lifetime)&lt;br /&gt;
| Command chaining, streaming&lt;br /&gt;
|-&lt;br /&gt;
| Named Pipes (FIFO)&lt;br /&gt;
| One-way&lt;br /&gt;
| Filesystem entry persists&lt;br /&gt;
| Unrelated process communication&lt;br /&gt;
|-&lt;br /&gt;
| Signals&lt;br /&gt;
| One-way (notification)&lt;br /&gt;
| Asynchronous event&lt;br /&gt;
| Process control, event notification&lt;br /&gt;
|-&lt;br /&gt;
| Sockets&lt;br /&gt;
| Bidirectional&lt;br /&gt;
| Filesystem entry persists (Unix domain)&lt;br /&gt;
| Client-server, network services&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039; - Exercise A: Outputs demonstrating export behavior, parent-child isolation, and single-command environment variables - Exercise B: Pipeline outputs and complete analysis script - Exercise C: FIFO creation output, producer/consumer scripts with sample output - Exercise D: Complete daemon simulation script with signal handling demonstrations - Exercise E: Socket file listing, curl output with HTTP headers, explanation of socket vs FIFO differences&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039; - Challenge 1: Complete log aggregator script, aggregated log contents, explanation - Challenge 2: Complete service controller script, outputs demonstrating all signal handlers (SIGHUP reload, SIGUSR1 status export, SIGTERM shutdown)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Additional:&amp;#039;&amp;#039;&amp;#039; - Each deliverable should include command outputs (screenshots or text) and brief explanations where requested. - For scripts, include the complete, commented source code and example execution output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab covers the fundamental IPC mechanisms accessible from bash. You’ve learned how processes inherit environment variables, communicate via pipes, respond to signals, and use sockets for network communication. These concepts form the foundation for system administration, scripting, and understanding how complex systems coordinate.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
* Advanced IPC: Shared memory, message queues, semaphores (typically used in C/C++ programs, not bash)&lt;br /&gt;
* Network programming: TCP/UDP sockets, client-server architecture&lt;br /&gt;
* D-Bus: A modern IPC system used by desktop environments and systemd&lt;br /&gt;
* Signal safety: Writing robust signal handlers (critical in C, less relevant in bash)&lt;br /&gt;
&lt;br /&gt;
Relevant manual pages:&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 pipe&amp;lt;/code&amp;gt; - Pipe overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 fifo&amp;lt;/code&amp;gt; - Named pipe overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 signal&amp;lt;/code&amp;gt; - Signal overview&lt;br /&gt;
* &amp;lt;code&amp;gt;man 7 unix&amp;lt;/code&amp;gt; - Unix domain sockets&lt;br /&gt;
* &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt; - Section on trap and job control&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8176</id>
		<title>OS Lab 6 - Inter Process Communication</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8176"/>
		<updated>2025-11-14T15:03:42Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel provides IPC mechanisms and how bash exposes them to scripts.&lt;br /&gt;
* Use environment variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; to pass configuration from parent processes to children.&lt;br /&gt;
* Construct pipelines with anonymous pipes to connect process standard streams.&lt;br /&gt;
* Create and use named pipes (FIFOs) for communication between unrelated processes.&lt;br /&gt;
* Send signals to processes and handle them with &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for asynchronous event-driven scripting.&lt;br /&gt;
* Demonstrate basic socket communication using existing utilities for network IPC.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 5, we explored bash scripting as a way to automate tasks by writing programs that the shell interprets. We learned how processes execute scripts, how to control flow with conditionals and loops, and how to structure code with functions. However, all of our scripts operated in isolation. Each script was a single process (or spawned child processes) that performed its task independently.&lt;br /&gt;
&lt;br /&gt;
Real-world systems require processes to cooperate. A web server must communicate with a database. A shell pipeline connects the output of one program to the input of another. A service must respond to signals sent by the init system. This lab explores the kernel-provided mechanisms that enable processes to exchange data and coordinate their actions. We focus on five fundamental IPC mechanisms, all accessible from bash: environment variables, pipes, named pipes, signals, and sockets.&lt;br /&gt;
&lt;br /&gt;
Understanding IPC is essential for system administration and software development. These mechanisms form the foundation of everything from command pipelines to distributed systems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy socat&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: A modern HTTP server with automatic HTTPS. We use it to demonstrate socket communication.&lt;br /&gt;
* &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;: A versatile networking tool that can work with Unix domain sockets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with: - Process concepts from Lab 3 (PIDs, process hierarchy, file descriptors) - File permissions from Lab 4 (execute bit, ownership) - Bash scripting fundamentals from Lab 5 (shebangs, builtins, variables, quoting, exit status, redirection, loops, functions)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;inter-process-communication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Inter-Process Communication ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-ipc&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What is IPC? ===&lt;br /&gt;
&lt;br /&gt;
By default, processes are isolated from one another. Each process has its own memory space, file descriptor table, and execution context. This isolation provides security and stability, but it creates a problem: how can processes cooperate to accomplish complex tasks?&lt;br /&gt;
&lt;br /&gt;
Inter-Process Communication (IPC) refers to the kernel-provided mechanisms that allow processes to exchange data and synchronize their actions. The kernel acts as an intermediary, providing channels, buffers, and signaling primitives that processes can use to communicate safely without violating isolation boundaries.&lt;br /&gt;
&lt;br /&gt;
In this lab, we examine five IPC mechanisms arranged roughly by complexity:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Environment Variables&amp;#039;&amp;#039;&amp;#039;: The simplest form of IPC. A parent process passes key-value configuration to its children via inherited environment variables. Communication is unidirectional (parent → child) and occurs only at process creation.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Pipes&amp;#039;&amp;#039;&amp;#039;: The kernel creates a buffer connecting one process’s standard output to another’s standard input. Data flows in one direction through the pipe. Anonymous pipes exist only while the processes using them are running.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Named Pipes (FIFOs)&amp;#039;&amp;#039;&amp;#039;: Like anonymous pipes, but visible in the filesystem. This persistence allows unrelated processes to connect to the same pipe by opening a file path.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Signals&amp;#039;&amp;#039;&amp;#039;: Asynchronous notifications sent from one process to another (or from the kernel to a process). Signals interrupt the receiving process, which can catch and handle them or allow default behavior (often termination).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Sockets&amp;#039;&amp;#039;&amp;#039;: Bidirectional communication channels that work across network boundaries or locally via Unix domain sockets. Multiple processes can connect to the same socket, enabling one-to-many communication patterns.&lt;br /&gt;
&lt;br /&gt;
Each mechanism solves different problems and has different trade-offs in terms of complexity, performance, and flexibility.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Role ====&lt;br /&gt;
&lt;br /&gt;
When a process forks, the child inherits a copy of the parent’s environment: a set of key-value string pairs maintained by the kernel for each process. The child can read these values and modify its own copy, but changes do not propagate back to the parent or to other processes. This inheritance mechanism provides a simple, unidirectional channel for passing configuration from parent to child.&lt;br /&gt;
&lt;br /&gt;
Environment variables are not limited to shell scripts. Every process has an environment. When you run any program, it receives the environment from the shell that launched it. Programs written in C access this via the &amp;lt;code&amp;gt;environ&amp;lt;/code&amp;gt; global variable or the third parameter to &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;. Python uses &amp;lt;code&amp;gt;os.environ&amp;lt;/code&amp;gt;. The environment is a universal convention for passing configuration.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-role-export-and-env&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Role: &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
In bash, variables are local to the shell process by default. When you set &amp;lt;code&amp;gt;name=value&amp;lt;/code&amp;gt;, that variable exists in the shell’s memory but is &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; passed to child processes. The &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; builtin marks a variable for inclusion in the environment of future child processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
export myvar&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or, more concisely:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export myvar=&amp;quot;hello&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Once exported, all child processes started by this shell (scripts, programs, or subshells) will inherit &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in their environment. To see the current environment, use the &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; command (or &amp;lt;code&amp;gt;printenv&amp;lt;/code&amp;gt;), which prints all exported variables.&lt;br /&gt;
&lt;br /&gt;
You can also set environment variables for a single command without affecting the shell:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;DEBUG=1 ./myscript.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This syntax sets &amp;lt;code&amp;gt;DEBUG=1&amp;lt;/code&amp;gt; in the environment of &amp;lt;code&amp;gt;myscript.sh&amp;lt;/code&amp;gt; only, without exporting it in the parent shell.&lt;br /&gt;
&lt;br /&gt;
Common environment variables you’ve already been using include &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (where bash searches for commands), &amp;lt;code&amp;gt;HOME&amp;lt;/code&amp;gt; (your home directory), &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; (your username), and &amp;lt;code&amp;gt;SHELL&amp;lt;/code&amp;gt; (your login shell). These are all set by the login process and inherited by every subsequent process in your session.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-environment-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Environment Variables ====&lt;br /&gt;
&lt;br /&gt;
Environment variables are appropriate for: - Configuration that should be inherited by all child processes (e.g., locale settings, proxy configuration) - Passing secrets or credentials to programs without embedding them in command-line arguments (which are visible via &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt;) - Controlling program behavior via well-known variables like &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LD_LIBRARY_PATH&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;TZ&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Runtime communication between already-running processes (use pipes, sockets, or signals) - Large amounts of data (environment is limited in size, typically a few megabytes) - Bi-directional communication (child changes don’t affect parent)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Pipes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-pipe-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Pipe Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A pipe is a one-way data channel maintained in kernel memory. When you create a pipe, the kernel allocates a buffer (typically 64 KB on Linux) and returns two file descriptors: one for writing and one for reading. Data written to the write end is buffered by the kernel and can be read from the read end in FIFO (first-in, first-out) order.&lt;br /&gt;
&lt;br /&gt;
Pipes are anonymous: they have no name in the filesystem. They exist only as long as at least one process holds a file descriptor to them. When all processes close their references to a pipe, the kernel deallocates it.&lt;br /&gt;
&lt;br /&gt;
The key insight from Lab 3: a pipeline like &amp;lt;code&amp;gt;cat file.txt | grep &amp;amp;quot;error&amp;amp;quot; | wc -l&amp;lt;/code&amp;gt; creates multiple processes (all in the same process group) connected by pipes. The kernel sets up the file descriptors so that &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdin, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;’s stdin. The processes run concurrently, with data flowing through kernel buffers as it’s produced and consumed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-pipe-operator&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Pipe Operator ====&lt;br /&gt;
&lt;br /&gt;
Bash creates pipes using the &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; operator. The syntax is simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command1 | command2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a pipe and starts two processes. Bash configures &amp;lt;code&amp;gt;command1&amp;lt;/code&amp;gt;’s stdout (FD 1) to point to the pipe’s write end and &amp;lt;code&amp;gt;command2&amp;lt;/code&amp;gt;’s stdin (FD 0) to point to the pipe’s read end. The commands execute concurrently.&lt;br /&gt;
&lt;br /&gt;
Longer pipelines work the same way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat data.txt | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates three pipes connecting four processes. Data flows left-to-right through kernel buffers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes-in-scripts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes in Scripts ====&lt;br /&gt;
&lt;br /&gt;
You can use pipes inside scripts just as you would interactively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# Count unique IP addresses in an access log&lt;br /&gt;
cat /var/log/access.log | cut -d&amp;#039; &amp;#039; -f1 | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pipes are particularly powerful when combined with bash’s process substitution feature &amp;lt;code&amp;gt;&amp;amp;lt;(command)&amp;lt;/code&amp;gt;, but that’s beyond the scope of this introductory lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Pipes ====&lt;br /&gt;
&lt;br /&gt;
Pipes are ideal for: - Connecting the output of one program to the input of another in a linear processing chain - Streaming data processing where data is generated and consumed incrementally - Quick, one-off data transformations at the command line&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Bi-directional communication (data flows one way only) - Communication between unrelated processes that weren’t started in a pipeline - Persistent communication (pipe disappears when processes exit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-fifo-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s FIFO Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A named pipe, or FIFO (First-In, First-Out), is a pipe with a name in the filesystem. Unlike anonymous pipes, FIFOs persist as filesystem entries (though the data buffer is still in kernel memory). Any process with appropriate permissions can open the FIFO by its path, allowing unrelated processes to communicate.&lt;br /&gt;
&lt;br /&gt;
When a process opens a FIFO for reading, it blocks until another process opens the same FIFO for writing (and vice versa). Once both ends are open, data flows through the kernel buffer just like an anonymous pipe. When all processes close their connections, the FIFO remains as a filesystem entry but the kernel buffer is deallocated.&lt;br /&gt;
&lt;br /&gt;
You can identify a FIFO in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output by the leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; in the permission string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;prw-r--r-- 1 user user 0 Nov  6 10:00 myfifo&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;creating-fifos-with-mkfifo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Creating FIFOs with &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; command creates a named pipe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkfifo /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now two unrelated processes can communicate by opening this file. One writes to it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Hello from writer&amp;quot; &amp;gt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command will block until a reader appears. In another terminal (or in the background), a reader can consume the data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When the reader connects, the writer unblocks, the message flows through the kernel buffer, and both commands complete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Named Pipes ====&lt;br /&gt;
&lt;br /&gt;
Named pipes are useful for: - Communication between unrelated processes that start at different times - Producer-consumer patterns where one process generates data and another processes it - Simple IPC without needing network sockets or shared files&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Multiple simultaneous readers or writers (FIFO semantics become unpredictable) - Persistent data storage (data is lost when all processes disconnect) - Bi-directional communication (like anonymous pipes, FIFOs are one-way)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-signal-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Signal Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A signal is an asynchronous notification sent to a process. Signals can be sent by other processes (via the &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt; system call) or by the kernel itself in response to events like segmentation faults, keyboard interrupts (Ctrl+C), or child process termination.&lt;br /&gt;
&lt;br /&gt;
When the kernel delivers a signal to a process, it interrupts the process’s normal execution. The process can respond in one of three ways:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Default Action&amp;#039;&amp;#039;&amp;#039;: Each signal has a default behavior, often terminating the process. For example, &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (signal 15) gracefully terminates, while &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (signal 9) forces immediate termination.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ignore&amp;#039;&amp;#039;&amp;#039;: The process can choose to ignore certain signals (except &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;, which cannot be caught or ignored).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom Handler&amp;#039;&amp;#039;&amp;#039;: The process can register a function (signal handler) to execute when the signal arrives. This allows the process to perform cleanup or take other actions before deciding whether to continue, terminate, or take other action.&lt;br /&gt;
&lt;br /&gt;
Common signals: - &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; (2): Sent by Ctrl+C. Default: terminate. - &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (15): Polite request to terminate. Default: terminate. Most services handle this to perform clean shutdown. - &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (9): Immediate termination. Cannot be caught or ignored. Used as a last resort. - &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (1): Historically “hang up” (modem disconnected). Often used to tell daemons to reload configuration. - &amp;lt;code&amp;gt;SIGCHLD&amp;lt;/code&amp;gt; (17): Sent to a parent when a child process terminates. - &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; (10) and &amp;lt;code&amp;gt;SIGUSR2&amp;lt;/code&amp;gt; (12): User-defined signals for custom purposes.&lt;br /&gt;
&lt;br /&gt;
Signals are sent by PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;kill -TERM 1234    # Send SIGTERM to process 1234&lt;br /&gt;
kill -9 1234       # Send SIGKILL to process 1234&lt;br /&gt;
kill -HUP 1234     # Send SIGHUP to process 1234&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also use the &amp;lt;code&amp;gt;%n&amp;lt;/code&amp;gt; job notation from Lab 3 to send signals to background jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-trap-builtin&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; Builtin ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin allows a bash script to register a handler for incoming signals:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Caught SIGINT&amp;quot;; exit&amp;#039; INT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This tells bash: “When SIGINT arrives, execute the command &amp;lt;code&amp;gt;echo &amp;amp;quot;Caught SIGINT&amp;amp;quot;; exit&amp;lt;/code&amp;gt;.” The handler can be any bash command or function.&lt;br /&gt;
&lt;br /&gt;
Common pattern for cleanup on exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up temporary files...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myscript.*&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Script body&lt;br /&gt;
echo &amp;quot;Running...&amp;quot;&lt;br /&gt;
sleep 10&lt;br /&gt;
echo &amp;quot;Done&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cleanup() is automatically called on normal exit, Ctrl+C, or errors (with set -e)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The special signal &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt; isn’t a real signal; it’s a bash pseudo-signal that fires whenever the script exits for any reason.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-signals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Signals ====&lt;br /&gt;
&lt;br /&gt;
Signals are appropriate for: - Event-driven scripts that respond to external events - Graceful shutdown and cleanup on termination - Daemon control (reload config with &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;, graceful restart with &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;) - Inter-process coordination where one process needs to notify another of state changes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Transferring data (signals carry almost no information, just the signal number) - Reliable communication (signals can be lost or delayed) - Complex coordination (race conditions are common)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Sockets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-socket-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Socket Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A socket is a bidirectional communication endpoint. Unlike pipes, sockets support two-way data flow. Unlike named pipes, sockets support multiple concurrent connections, making them suitable for client-server architectures.&lt;br /&gt;
&lt;br /&gt;
There are two main types:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Sockets&amp;#039;&amp;#039;&amp;#039;: Use TCP or UDP to communicate over IP networks. These work across machines and are the foundation of the internet.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Unix Domain Sockets&amp;#039;&amp;#039;&amp;#039;: Use file paths (like named pipes) but support bidirectional communication and connection multiplexing. These work only on the same machine but are faster than network sockets.&lt;br /&gt;
&lt;br /&gt;
When a server process binds to a socket, it listens for incoming connections. Multiple client processes can connect to the same server socket. Each connection is independent, with its own bidirectional channel. This one-to-many pattern distinguishes sockets from pipes and FIFOs.&lt;br /&gt;
&lt;br /&gt;
For network sockets, the server binds to a port number (e.g., port 80 for HTTP). For Unix domain sockets, it binds to a filesystem path (e.g., &amp;lt;code&amp;gt;/tmp/myservice.sock&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-communication-from-bash&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Socket Communication from Bash ====&lt;br /&gt;
&lt;br /&gt;
While bash doesn’t have native socket support, we can use existing tools to demonstrate socket communication without writing low-level code:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;caddy&amp;#039;&amp;#039;&amp;#039;: A modern web server that can listen on both network and Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;curl&amp;#039;&amp;#039;&amp;#039;: A command-line HTTP client that supports Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;socat&amp;#039;&amp;#039;&amp;#039;: A general-purpose networking tool for creating and connecting to various socket types&lt;br /&gt;
&lt;br /&gt;
These tools abstract the complexity of socket programming, allowing us to focus on the conceptual model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Sockets ====&lt;br /&gt;
&lt;br /&gt;
Sockets are ideal for: - Client-server applications with multiple concurrent clients - Networked communication across machines - Bidirectional data exchange - Services that need to accept connections from many processes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; necessary for: - Simple one-to-one, one-way communication (use pipes instead) - Configuration passing (use environment variables) - Asynchronous notifications (use signals)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates how environment variables are inherited by child processes but not propagated back to parents.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Check your current environment. Run &amp;lt;code&amp;gt;env | head -n 10&amp;lt;/code&amp;gt; and observe some of the variables already set.&lt;br /&gt;
# Create a shell variable without exporting it: &amp;lt;code&amp;gt;myvar=&amp;amp;quot;not exported&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell with &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt; and try to read &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;echo &amp;amp;quot;$myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. It should be empty. Exit the subshell.&lt;br /&gt;
# Back in your original shell, export the variable: &amp;lt;code&amp;gt;export myvar=&amp;amp;quot;exported now&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell again with &amp;lt;code&amp;gt;bash -c &amp;#039;echo &amp;amp;quot;In child: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt; and verify that &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; is now accessible.&lt;br /&gt;
# In a subshell, modify the variable: &amp;lt;code&amp;gt;bash -c &amp;#039;myvar=&amp;amp;quot;changed in child&amp;amp;quot;; echo &amp;amp;quot;Child modified: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Print &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in the parent shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;Parent still has: $myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. Observe that the parent’s value is unchanged.&lt;br /&gt;
# Demonstrate setting an environment variable for a single command: &amp;lt;code&amp;gt;GREETING=&amp;amp;quot;Hello&amp;amp;quot; bash -c &amp;#039;echo $GREETING&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify that &amp;lt;code&amp;gt;GREETING&amp;lt;/code&amp;gt; is not set in your current shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;GREETING in parent: $GREETING&amp;amp;quot;&amp;lt;/code&amp;gt; (should be empty).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; to run a command with a specific environment: &amp;lt;code&amp;gt;env DEBUG=1 bash -c &amp;#039;echo &amp;amp;quot;DEBUG=$DEBUG&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide the output showing: the subshell cannot see the unexported variable, the subshell can see the exported variable, the parent is unaffected by child changes, and single-command environment variable setting.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-pipes-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Pipes in Practice ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores anonymous pipes and how they connect processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use a simple pipe to count lines: &amp;lt;code&amp;gt;cat /etc/passwd | wc -l&amp;lt;/code&amp;gt;. Observe the result.&lt;br /&gt;
# Build a longer pipeline to find how many unique shells are in use: &amp;lt;code&amp;gt;cat /etc/passwd | cut -d: -f7 | sort | uniq&amp;lt;/code&amp;gt;. Count them manually or pipe to &amp;lt;code&amp;gt;wc -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate that processes in a pipeline run concurrently. Run &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; command produces infinite output, but &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt; reads only 5 lines and then exits, causing &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to receive a &amp;lt;code&amp;gt;SIGPIPE&amp;lt;/code&amp;gt; and terminate.&lt;br /&gt;
# Create a sample log file for analysis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab6_sample.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout&lt;br /&gt;
2024-01-15 10:15:00 INFO Retry successful&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration&lt;br /&gt;
2024-01-15 10:25:00 WARN Low memory&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;5&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to count ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | wc -l&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to extract and count unique log levels: &amp;lt;code&amp;gt;cut -d&amp;#039; &amp;#039; -f3 /tmp/lab6_sample.log | sort | uniq -c&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Combine multiple operations: Find the timestamps of all ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | cut -d&amp;#039; &amp;#039; -f1,2&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the output from the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; shell analysis, the &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt; demonstration, and the log file analysis commands showing ERROR count and unique log levels with counts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates persistent named pipes and communication between unrelated processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a named pipe: &amp;lt;code&amp;gt;mkfifo /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify it exists and note its type: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;. The leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; indicates a FIFO.&lt;br /&gt;
# In one terminal, start a reader that will block: &amp;lt;code&amp;gt;cat &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;. Leave this running.&lt;br /&gt;
# In a second terminal, write to the FIFO: &amp;lt;code&amp;gt;echo &amp;amp;quot;Hello via FIFO&amp;amp;quot; &amp;amp;gt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe that the reader in the first terminal unblocks, displays the message, and exits.&lt;br /&gt;
# Demonstrate that the FIFO persists. List it again: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Test a more complex scenario. In terminal 1: &amp;lt;code&amp;gt;while read line; do echo &amp;amp;quot;Received: $line&amp;amp;quot;; done &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In terminal 2, send multiple messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Message 1&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 2&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 3&amp;quot; &amp;gt; /tmp/lab6_fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;9&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Observe the messages being received. Press Ctrl+C in terminal 1 to stop the reader.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Remove the FIFO: &amp;lt;code&amp;gt;rm /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the FIFO type, screenshots or output from both terminals showing the message exchange, and a brief explanation of how the FIFO persists between writes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates signal handling in bash using interactive commands.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Start a simple sleep command: &amp;lt;code&amp;gt;sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C to interrupt it. Observe that it terminates immediately.&lt;br /&gt;
# Now run a command that ignores SIGINT: &amp;lt;code&amp;gt;trap &amp;#039;echo &amp;amp;quot;Caught SIGINT, ignoring...&amp;amp;quot;&amp;#039; INT; sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C. The trap catches the signal and the sleep continues.&lt;br /&gt;
# Demonstrate cleanup on exit. Run this compound command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Cleanup: removing temp file&amp;quot;; rm -f /tmp/lab6_test.txt&amp;#039; EXIT; \&lt;br /&gt;
touch /tmp/lab6_test.txt; \&lt;br /&gt;
echo &amp;quot;File created. Press Ctrl+C or wait...&amp;quot;; \&lt;br /&gt;
sleep 10; \&lt;br /&gt;
echo &amp;quot;Normal exit&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;4&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Try both: let it complete normally, then run it again and interrupt with Ctrl+C. Observe cleanup happens both times.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start a background sleep: &amp;lt;code&amp;gt;sleep 100 &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID displayed.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGTERM to it: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; (replace &amp;lt;code&amp;gt;&amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; with the actual PID). Verify it terminated: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start another background process that will ignore SIGTERM:&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;(trap &amp;#039;echo &amp;quot;Caught SIGTERM, staying alive&amp;quot;&amp;#039; TERM; \&lt;br /&gt;
 while true; do echo &amp;quot;Running...&amp;quot;; sleep 5; done) &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;8&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Note the PID, then send SIGTERM: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. Observe the trap message.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGKILL to force termination: &amp;lt;code&amp;gt;kill -9 &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. This cannot be caught.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verify all background jobs are gone: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide output showing: the trapped SIGINT message, cleanup on both normal exit and Ctrl+C, the SIGTERM being caught with the trap message, and SIGKILL forcing termination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-socket-communication-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Socket Communication Basics ===&lt;br /&gt;
&lt;br /&gt;
This exercise provides a brief introduction to socket communication using existing tools.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a simple static site directory: &amp;lt;code&amp;gt;mkdir -p /tmp/lab6_site &amp;amp;amp;&amp;amp;amp; echo &amp;amp;quot;&amp;amp;lt;h1&amp;amp;gt;Hello from Caddy&amp;amp;lt;/h1&amp;amp;gt;&amp;amp;quot; &amp;amp;gt; /tmp/lab6_site/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start Caddy as a file server listening on a Unix domain socket: &amp;lt;code&amp;gt;caddy file-server --root /tmp/lab6_site --listen unix//tmp/caddy.sock &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID.&lt;br /&gt;
# Wait a moment for Caddy to start. Verify the socket exists: &amp;lt;code&amp;gt;ls -l /tmp/caddy.sock&amp;lt;/code&amp;gt;. Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; type indicating a socket.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; to make an HTTP request via the Unix domain socket: &amp;lt;code&amp;gt;curl -v --unix-socket /tmp/caddy.sock http://localhost/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the response. Note that the communication is bidirectional: you sent an HTTP request, and the server responded with the HTML content.&lt;br /&gt;
# (Optional) Open multiple terminals and run &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; simultaneously several times. Each request is handled independently, demonstrating the one-to-many capability of sockets.&lt;br /&gt;
# Stop Caddy by sending &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;caddy_pid&amp;amp;gt;&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;pkill caddy&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Clean up: &amp;lt;code&amp;gt;rm -rf /tmp/lab6_site /tmp/caddy.sock&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the socket file, the &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; output demonstrating the request and response (especially the HTTP headers and HTML body), and a brief explanation (2-3 sentences) of how this differs from a named pipe in terms of directionality and connection multiplexing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-aggregator-with-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log Aggregator with Named Pipes ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_log_aggregator.sh&amp;lt;/code&amp;gt; that aggregates log messages from multiple sources using named pipes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Create three named pipes: &amp;lt;code&amp;gt;/tmp/log_fifo1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo3&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Start three background processes that each write timestamped messages to one of the FIFOs (simulating different log sources). Each should write 5 messages at 1-second intervals.&lt;br /&gt;
* Implement a main loop that reads from all three FIFOs simultaneously (hint: use &amp;lt;code&amp;gt;select&amp;lt;/code&amp;gt; via &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; with timeout, or use multiple background readers).&lt;br /&gt;
* Write all received messages to a combined log file &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; with timestamps.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; to clean up: kill background processes, remove FIFOs, and exit gracefully.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, background processes (&amp;lt;code&amp;gt;&amp;amp;amp;&amp;lt;/code&amp;gt;), proper cleanup.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab6_log_aggregator.sh&lt;br /&gt;
/tmp/lab6_log_aggregator.sh &amp;amp;&lt;br /&gt;
AGG_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -TERM $AGG_PID&lt;br /&gt;
wait&lt;br /&gt;
cat /tmp/aggregated.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Contents of &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; showing interleaved messages from all three sources - Brief explanation (2-3 sentences) of why named pipes were necessary here instead of anonymous pipes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-graceful-service-controller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Graceful Service Controller ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_service_controller.sh&amp;lt;/code&amp;gt; that manages a long-running service and responds to signals.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* The script acts as a daemon that runs indefinitely, printing a heartbeat message every 5 seconds.&lt;br /&gt;
* Accept one optional argument: a “config file” path. If provided, read a setting (e.g., &amp;lt;code&amp;gt;INTERVAL=10&amp;lt;/code&amp;gt;) from the file to control the heartbeat interval.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;: Reload the configuration file and adjust the interval dynamically without restarting the script. Print “Configuration reloaded.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: Perform graceful shutdown. Print “Shutting down gracefully…”, wait 2 seconds, then exit cleanly.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt;: Export the current status to &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; (e.g., uptime, number of heartbeats sent, current interval). Print “Status exported.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt;: Perform cleanup. Print “Service stopped.”&lt;br /&gt;
* Maintain internal state: count the number of heartbeats sent, track start time.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for multiple signals, variables for state, a loop, cleanup handler.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;INTERVAL=3&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
chmod +x /tmp/lab6_service_controller.sh&lt;br /&gt;
/tmp/lab6_service_controller.sh /tmp/service.conf &amp;amp;&lt;br /&gt;
SVC_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -USR1 $SVC_PID  # Export status&lt;br /&gt;
cat /tmp/service_status.txt&lt;br /&gt;
echo &amp;quot;INTERVAL=1&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
kill -HUP $SVC_PID  # Reload config&lt;br /&gt;
sleep 5&lt;br /&gt;
kill -TERM $SVC_PID  # Graceful shutdown&lt;br /&gt;
wait&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Output showing heartbeats before and after &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (with visible interval change) - Contents of &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; after &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; - Output showing graceful shutdown message after &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-ipc-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common IPC Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for IPC mechanisms covered in this lab:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Environment Variables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Export a variable for child processes&lt;br /&gt;
export VAR=&amp;quot;value&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Set for one command only&lt;br /&gt;
VAR=&amp;quot;value&amp;quot; command&lt;br /&gt;
&lt;br /&gt;
# View all environment variables&lt;br /&gt;
env&lt;br /&gt;
printenv&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Anonymous Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Simple pipeline&lt;br /&gt;
command1 | command2&lt;br /&gt;
&lt;br /&gt;
# Multi-stage pipeline&lt;br /&gt;
cat file.txt | grep &amp;quot;pattern&amp;quot; | sort | uniq&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Named Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create a FIFO&lt;br /&gt;
mkfifo /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Write to FIFO (blocks until reader connects)&lt;br /&gt;
echo &amp;quot;data&amp;quot; &amp;gt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Read from FIFO (blocks until writer connects)&lt;br /&gt;
cat &amp;lt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Remove FIFO&lt;br /&gt;
rm /path/to/fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Send signal by name&lt;br /&gt;
kill -TERM &amp;lt;pid&amp;gt;&lt;br /&gt;
kill -HUP &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Send signal by number&lt;br /&gt;
kill -15 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Force kill (cannot be caught)&lt;br /&gt;
kill -9 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trap signals in a script&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught signal&amp;quot;&amp;#039; INT TERM&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Define cleanup function&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myfiles.*&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sockets (using existing tools):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start a server on Unix domain socket (using socat)&lt;br /&gt;
socat UNIX-LISTEN:/tmp/service.sock,fork EXEC:&amp;#039;/usr/bin/myhandler&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Connect as client (using socat)&lt;br /&gt;
echo &amp;quot;request&amp;quot; | socat - UNIX-CONNECT:/tmp/service.sock&lt;br /&gt;
&lt;br /&gt;
# HTTP via Unix socket (using curl)&lt;br /&gt;
curl --unix-socket /tmp/service.sock http://localhost/path&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-patterns-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Patterns Table ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mechanism&lt;br /&gt;
! Direction&lt;br /&gt;
! Persistence&lt;br /&gt;
! Use Case&lt;br /&gt;
|-&lt;br /&gt;
| Environment Variables&lt;br /&gt;
| Parent → Child (one-way)&lt;br /&gt;
| Inherited at fork&lt;br /&gt;
| Configuration, credentials&lt;br /&gt;
|-&lt;br /&gt;
| Pipes (&amp;lt;code&amp;gt;\|&amp;lt;/code&amp;gt;)&lt;br /&gt;
| One-way&lt;br /&gt;
| Ephemeral (process lifetime)&lt;br /&gt;
| Command chaining, streaming&lt;br /&gt;
|-&lt;br /&gt;
| Named Pipes (FIFO)&lt;br /&gt;
| One-way&lt;br /&gt;
| Filesystem entry persists&lt;br /&gt;
| Unrelated process communication&lt;br /&gt;
|-&lt;br /&gt;
| Signals&lt;br /&gt;
| One-way (notification)&lt;br /&gt;
| Asynchronous event&lt;br /&gt;
| Process control, event notification&lt;br /&gt;
|-&lt;br /&gt;
| Sockets&lt;br /&gt;
| Bidirectional&lt;br /&gt;
| Filesystem entry persists (Unix domain)&lt;br /&gt;
| Client-server, network services&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039; - Exercise A: Outputs demonstrating export behavior, parent-child isolation, and single-command environment variables - Exercise B: Pipeline outputs and complete analysis script - Exercise C: FIFO creation output, producer/consumer scripts with sample output - Exercise D: Complete daemon simulation script with signal handling demonstrations - Exercise E: Socket file listing, curl output with HTTP headers, explanation of socket vs FIFO differences&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039; - Challenge 1: Complete log aggregator script, aggregated log contents, explanation - Challenge 2: Complete service controller script, outputs demonstrating all signal handlers (SIGHUP reload, SIGUSR1 status export, SIGTERM shutdown)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Additional:&amp;#039;&amp;#039;&amp;#039; - Each deliverable should include command outputs (screenshots or text) and brief explanations where requested. - For scripts, include the complete, commented source code and example execution output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab covers the fundamental IPC mechanisms accessible from bash. You’ve learned how processes inherit environment variables, communicate via pipes, respond to signals, and use sockets for network communication. These concepts form the foundation for system administration, scripting, and understanding how complex systems coordinate.&lt;br /&gt;
&lt;br /&gt;
For further study: - Advanced IPC: Shared memory, message queues, semaphores (typically used in C/C++ programs, not bash) - Network programming: TCP/UDP sockets, client-server architecture - D-Bus: A modern IPC system used by desktop environments and systemd - Signal safety: Writing robust signal handlers (critical in C, less relevant in bash)&lt;br /&gt;
&lt;br /&gt;
Relevant manual pages: - &amp;lt;code&amp;gt;man 7 pipe&amp;lt;/code&amp;gt; - Pipe overview - &amp;lt;code&amp;gt;man 7 fifo&amp;lt;/code&amp;gt; - Named pipe overview&amp;lt;br /&amp;gt;&lt;br /&gt;
- &amp;lt;code&amp;gt;man 7 signal&amp;lt;/code&amp;gt; - Signal overview - &amp;lt;code&amp;gt;man 7 unix&amp;lt;/code&amp;gt; - Unix domain sockets - &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt; - Section on trap and job control&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8175</id>
		<title>OS Lab 6 - Inter Process Communication</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8175"/>
		<updated>2025-11-14T15:02:49Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;os-lab-6---inter-process-communication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= OS Lab 6 - Inter-Process Communication =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel provides IPC mechanisms and how bash exposes them to scripts.&lt;br /&gt;
* Use environment variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; to pass configuration from parent processes to children.&lt;br /&gt;
* Construct pipelines with anonymous pipes to connect process standard streams.&lt;br /&gt;
* Create and use named pipes (FIFOs) for communication between unrelated processes.&lt;br /&gt;
* Send signals to processes and handle them with &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for asynchronous event-driven scripting.&lt;br /&gt;
* Demonstrate basic socket communication using existing utilities for network IPC.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In Lab 5, we explored bash scripting as a way to automate tasks by writing programs that the shell interprets. We learned how processes execute scripts, how to control flow with conditionals and loops, and how to structure code with functions. However, all of our scripts operated in isolation. Each script was a single process (or spawned child processes) that performed its task independently.&lt;br /&gt;
&lt;br /&gt;
Real-world systems require processes to cooperate. A web server must communicate with a database. A shell pipeline connects the output of one program to the input of another. A service must respond to signals sent by the init system. This lab explores the kernel-provided mechanisms that enable processes to exchange data and coordinate their actions. We focus on five fundamental IPC mechanisms, all accessible from bash: environment variables, pipes, named pipes, signals, and sockets.&lt;br /&gt;
&lt;br /&gt;
Understanding IPC is essential for system administration and software development. These mechanisms form the foundation of everything from command pipelines to distributed systems.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt update&lt;br /&gt;
sudo apt install -y caddy socat&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;caddy&amp;lt;/code&amp;gt;: A modern HTTP server with automatic HTTPS. We use it to demonstrate socket communication.&lt;br /&gt;
* &amp;lt;code&amp;gt;socat&amp;lt;/code&amp;gt;: A versatile networking tool that can work with Unix domain sockets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with: - Process concepts from Lab 3 (PIDs, process hierarchy, file descriptors) - File permissions from Lab 4 (execute bit, ownership) - Bash scripting fundamentals from Lab 5 (shebangs, builtins, variables, quoting, exit status, redirection, loops, functions)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;inter-process-communication&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Inter-Process Communication ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;what-is-ipc&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== What is IPC? ===&lt;br /&gt;
&lt;br /&gt;
By default, processes are isolated from one another. Each process has its own memory space, file descriptor table, and execution context. This isolation provides security and stability, but it creates a problem: how can processes cooperate to accomplish complex tasks?&lt;br /&gt;
&lt;br /&gt;
Inter-Process Communication (IPC) refers to the kernel-provided mechanisms that allow processes to exchange data and synchronize their actions. The kernel acts as an intermediary, providing channels, buffers, and signaling primitives that processes can use to communicate safely without violating isolation boundaries.&lt;br /&gt;
&lt;br /&gt;
In this lab, we examine five IPC mechanisms arranged roughly by complexity:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Environment Variables&amp;#039;&amp;#039;&amp;#039;: The simplest form of IPC. A parent process passes key-value configuration to its children via inherited environment variables. Communication is unidirectional (parent → child) and occurs only at process creation.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Pipes&amp;#039;&amp;#039;&amp;#039;: The kernel creates a buffer connecting one process’s standard output to another’s standard input. Data flows in one direction through the pipe. Anonymous pipes exist only while the processes using them are running.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Named Pipes (FIFOs)&amp;#039;&amp;#039;&amp;#039;: Like anonymous pipes, but visible in the filesystem. This persistence allows unrelated processes to connect to the same pipe by opening a file path.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Signals&amp;#039;&amp;#039;&amp;#039;: Asynchronous notifications sent from one process to another (or from the kernel to a process). Signals interrupt the receiving process, which can catch and handle them or allow default behavior (often termination).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Sockets&amp;#039;&amp;#039;&amp;#039;: Bidirectional communication channels that work across network boundaries or locally via Unix domain sockets. Multiple processes can connect to the same socket, enabling one-to-many communication patterns.&lt;br /&gt;
&lt;br /&gt;
Each mechanism solves different problems and has different trade-offs in terms of complexity, performance, and flexibility.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Role ====&lt;br /&gt;
&lt;br /&gt;
When a process forks, the child inherits a copy of the parent’s environment: a set of key-value string pairs maintained by the kernel for each process. The child can read these values and modify its own copy, but changes do not propagate back to the parent or to other processes. This inheritance mechanism provides a simple, unidirectional channel for passing configuration from parent to child.&lt;br /&gt;
&lt;br /&gt;
Environment variables are not limited to shell scripts. Every process has an environment. When you run any program, it receives the environment from the shell that launched it. Programs written in C access this via the &amp;lt;code&amp;gt;environ&amp;lt;/code&amp;gt; global variable or the third parameter to &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;. Python uses &amp;lt;code&amp;gt;os.environ&amp;lt;/code&amp;gt;. The environment is a universal convention for passing configuration.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-role-export-and-env&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Role: &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
In bash, variables are local to the shell process by default. When you set &amp;lt;code&amp;gt;name=value&amp;lt;/code&amp;gt;, that variable exists in the shell’s memory but is &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; passed to child processes. The &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; builtin marks a variable for inclusion in the environment of future child processes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
export myvar&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or, more concisely:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;export myvar=&amp;quot;hello&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Once exported, all child processes started by this shell (scripts, programs, or subshells) will inherit &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in their environment. To see the current environment, use the &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; command (or &amp;lt;code&amp;gt;printenv&amp;lt;/code&amp;gt;), which prints all exported variables.&lt;br /&gt;
&lt;br /&gt;
You can also set environment variables for a single command without affecting the shell:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;DEBUG=1 ./myscript.sh&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This syntax sets &amp;lt;code&amp;gt;DEBUG=1&amp;lt;/code&amp;gt; in the environment of &amp;lt;code&amp;gt;myscript.sh&amp;lt;/code&amp;gt; only, without exporting it in the parent shell.&lt;br /&gt;
&lt;br /&gt;
Common environment variables you’ve already been using include &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; (where bash searches for commands), &amp;lt;code&amp;gt;HOME&amp;lt;/code&amp;gt; (your home directory), &amp;lt;code&amp;gt;USER&amp;lt;/code&amp;gt; (your username), and &amp;lt;code&amp;gt;SHELL&amp;lt;/code&amp;gt; (your login shell). These are all set by the login process and inherited by every subsequent process in your session.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-environment-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Environment Variables ====&lt;br /&gt;
&lt;br /&gt;
Environment variables are appropriate for: - Configuration that should be inherited by all child processes (e.g., locale settings, proxy configuration) - Passing secrets or credentials to programs without embedding them in command-line arguments (which are visible via &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt;) - Controlling program behavior via well-known variables like &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LD_LIBRARY_PATH&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;TZ&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Runtime communication between already-running processes (use pipes, sockets, or signals) - Large amounts of data (environment is limited in size, typically a few megabytes) - Bi-directional communication (child changes don’t affect parent)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Pipes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-pipe-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Pipe Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A pipe is a one-way data channel maintained in kernel memory. When you create a pipe, the kernel allocates a buffer (typically 64 KB on Linux) and returns two file descriptors: one for writing and one for reading. Data written to the write end is buffered by the kernel and can be read from the read end in FIFO (first-in, first-out) order.&lt;br /&gt;
&lt;br /&gt;
Pipes are anonymous: they have no name in the filesystem. They exist only as long as at least one process holds a file descriptor to them. When all processes close their references to a pipe, the kernel deallocates it.&lt;br /&gt;
&lt;br /&gt;
The key insight from Lab 3: a pipeline like &amp;lt;code&amp;gt;cat file.txt | grep &amp;amp;quot;error&amp;amp;quot; | wc -l&amp;lt;/code&amp;gt; creates multiple processes (all in the same process group) connected by pipes. The kernel sets up the file descriptors so that &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdin, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;’s stdout is connected to &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;’s stdin. The processes run concurrently, with data flowing through kernel buffers as it’s produced and consumed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-pipe-operator&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s Pipe Operator ====&lt;br /&gt;
&lt;br /&gt;
Bash creates pipes using the &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; operator. The syntax is simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command1 | command2&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates a pipe and starts two processes. Bash configures &amp;lt;code&amp;gt;command1&amp;lt;/code&amp;gt;’s stdout (FD 1) to point to the pipe’s write end and &amp;lt;code&amp;gt;command2&amp;lt;/code&amp;gt;’s stdin (FD 0) to point to the pipe’s read end. The commands execute concurrently.&lt;br /&gt;
&lt;br /&gt;
Longer pipelines work the same way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat data.txt | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This creates three pipes connecting four processes. Data flows left-to-right through kernel buffers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes-in-scripts&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes in Scripts ====&lt;br /&gt;
&lt;br /&gt;
You can use pipes inside scripts just as you would interactively:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# Count unique IP addresses in an access log&lt;br /&gt;
cat /var/log/access.log | cut -d&amp;#039; &amp;#039; -f1 | sort | uniq | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Pipes are particularly powerful when combined with bash’s process substitution feature &amp;lt;code&amp;gt;&amp;amp;lt;(command)&amp;lt;/code&amp;gt;, but that’s beyond the scope of this introductory lab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Pipes ====&lt;br /&gt;
&lt;br /&gt;
Pipes are ideal for: - Connecting the output of one program to the input of another in a linear processing chain - Streaming data processing where data is generated and consumed incrementally - Quick, one-off data transformations at the command line&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Bi-directional communication (data flows one way only) - Communication between unrelated processes that weren’t started in a pipeline - Persistent communication (pipe disappears when processes exit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-fifo-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s FIFO Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A named pipe, or FIFO (First-In, First-Out), is a pipe with a name in the filesystem. Unlike anonymous pipes, FIFOs persist as filesystem entries (though the data buffer is still in kernel memory). Any process with appropriate permissions can open the FIFO by its path, allowing unrelated processes to communicate.&lt;br /&gt;
&lt;br /&gt;
When a process opens a FIFO for reading, it blocks until another process opens the same FIFO for writing (and vice versa). Once both ends are open, data flows through the kernel buffer just like an anonymous pipe. When all processes close their connections, the FIFO remains as a filesystem entry but the kernel buffer is deallocated.&lt;br /&gt;
&lt;br /&gt;
You can identify a FIFO in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output by the leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; in the permission string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;prw-r--r-- 1 user user 0 Nov  6 10:00 myfifo&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;creating-fifos-with-mkfifo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Creating FIFOs with &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt; command creates a named pipe:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkfifo /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now two unrelated processes can communicate by opening this file. One writes to it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Hello from writer&amp;quot; &amp;gt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command will block until a reader appears. In another terminal (or in the background), a reader can consume the data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;lt; /tmp/myfifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When the reader connects, the writer unblocks, the message flows through the kernel buffer, and both commands complete.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Named Pipes ====&lt;br /&gt;
&lt;br /&gt;
Named pipes are useful for: - Communication between unrelated processes that start at different times - Producer-consumer patterns where one process generates data and another processes it - Simple IPC without needing network sockets or shared files&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Multiple simultaneous readers or writers (FIFO semantics become unpredictable) - Persistent data storage (data is lost when all processes disconnect) - Bi-directional communication (like anonymous pipes, FIFOs are one-way)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-signal-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Signal Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A signal is an asynchronous notification sent to a process. Signals can be sent by other processes (via the &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt; system call) or by the kernel itself in response to events like segmentation faults, keyboard interrupts (Ctrl+C), or child process termination.&lt;br /&gt;
&lt;br /&gt;
When the kernel delivers a signal to a process, it interrupts the process’s normal execution. The process can respond in one of three ways:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Default Action&amp;#039;&amp;#039;&amp;#039;: Each signal has a default behavior, often terminating the process. For example, &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (signal 15) gracefully terminates, while &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (signal 9) forces immediate termination.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Ignore&amp;#039;&amp;#039;&amp;#039;: The process can choose to ignore certain signals (except &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;, which cannot be caught or ignored).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Custom Handler&amp;#039;&amp;#039;&amp;#039;: The process can register a function (signal handler) to execute when the signal arrives. This allows the process to perform cleanup or take other actions before deciding whether to continue, terminate, or take other action.&lt;br /&gt;
&lt;br /&gt;
Common signals: - &amp;lt;code&amp;gt;SIGINT&amp;lt;/code&amp;gt; (2): Sent by Ctrl+C. Default: terminate. - &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; (15): Polite request to terminate. Default: terminate. Most services handle this to perform clean shutdown. - &amp;lt;code&amp;gt;SIGKILL&amp;lt;/code&amp;gt; (9): Immediate termination. Cannot be caught or ignored. Used as a last resort. - &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (1): Historically “hang up” (modem disconnected). Often used to tell daemons to reload configuration. - &amp;lt;code&amp;gt;SIGCHLD&amp;lt;/code&amp;gt; (17): Sent to a parent when a child process terminates. - &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; (10) and &amp;lt;code&amp;gt;SIGUSR2&amp;lt;/code&amp;gt; (12): User-defined signals for custom purposes.&lt;br /&gt;
&lt;br /&gt;
Signals are sent by PID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;kill -TERM 1234    # Send SIGTERM to process 1234&lt;br /&gt;
kill -9 1234       # Send SIGKILL to process 1234&lt;br /&gt;
kill -HUP 1234     # Send SIGHUP to process 1234&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can also use the &amp;lt;code&amp;gt;%n&amp;lt;/code&amp;gt; job notation from Lab 3 to send signals to background jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bashs-trap-builtin&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Bash’s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; Builtin ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin allows a bash script to register a handler for incoming signals:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Caught SIGINT&amp;quot;; exit&amp;#039; INT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This tells bash: “When SIGINT arrives, execute the command &amp;lt;code&amp;gt;echo &amp;amp;quot;Caught SIGINT&amp;amp;quot;; exit&amp;lt;/code&amp;gt;.” The handler can be any bash command or function.&lt;br /&gt;
&lt;br /&gt;
Common pattern for cleanup on exit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up temporary files...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myscript.*&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Script body&lt;br /&gt;
echo &amp;quot;Running...&amp;quot;&lt;br /&gt;
sleep 10&lt;br /&gt;
echo &amp;quot;Done&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cleanup() is automatically called on normal exit, Ctrl+C, or errors (with set -e)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The special signal &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt; isn’t a real signal; it’s a bash pseudo-signal that fires whenever the script exits for any reason.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-signals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Signals ====&lt;br /&gt;
&lt;br /&gt;
Signals are appropriate for: - Event-driven scripts that respond to external events - Graceful shutdown and cleanup on termination - Daemon control (reload config with &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;, graceful restart with &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;) - Inter-process coordination where one process needs to notify another of state changes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; suitable for: - Transferring data (signals carry almost no information, just the signal number) - Reliable communication (signals can be lost or delayed) - Complex coordination (race conditions are common)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Sockets ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-socket-mechanism&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel’s Socket Mechanism ====&lt;br /&gt;
&lt;br /&gt;
A socket is a bidirectional communication endpoint. Unlike pipes, sockets support two-way data flow. Unlike named pipes, sockets support multiple concurrent connections, making them suitable for client-server architectures.&lt;br /&gt;
&lt;br /&gt;
There are two main types:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Network Sockets&amp;#039;&amp;#039;&amp;#039;: Use TCP or UDP to communicate over IP networks. These work across machines and are the foundation of the internet.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Unix Domain Sockets&amp;#039;&amp;#039;&amp;#039;: Use file paths (like named pipes) but support bidirectional communication and connection multiplexing. These work only on the same machine but are faster than network sockets.&lt;br /&gt;
&lt;br /&gt;
When a server process binds to a socket, it listens for incoming connections. Multiple client processes can connect to the same server socket. Each connection is independent, with its own bidirectional channel. This one-to-many pattern distinguishes sockets from pipes and FIFOs.&lt;br /&gt;
&lt;br /&gt;
For network sockets, the server binds to a port number (e.g., port 80 for HTTP). For Unix domain sockets, it binds to a filesystem path (e.g., &amp;lt;code&amp;gt;/tmp/myservice.sock&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;socket-communication-from-bash&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Socket Communication from Bash ====&lt;br /&gt;
&lt;br /&gt;
While bash doesn’t have native socket support, we can use existing tools to demonstrate socket communication without writing low-level code:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;caddy&amp;#039;&amp;#039;&amp;#039;: A modern web server that can listen on both network and Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;curl&amp;#039;&amp;#039;&amp;#039;: A command-line HTTP client that supports Unix domain sockets&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;socat&amp;#039;&amp;#039;&amp;#039;: A general-purpose networking tool for creating and connecting to various socket types&lt;br /&gt;
&lt;br /&gt;
These tools abstract the complexity of socket programming, allowing us to focus on the conceptual model.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;when-to-use-sockets&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== When to Use Sockets ====&lt;br /&gt;
&lt;br /&gt;
Sockets are ideal for: - Client-server applications with multiple concurrent clients - Networked communication across machines - Bidirectional data exchange - Services that need to accept connections from many processes&lt;br /&gt;
&lt;br /&gt;
They are &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; necessary for: - Simple one-to-one, one-way communication (use pipes instead) - Configuration passing (use environment variables) - Asynchronous notifications (use signals)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercises&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Hands-on Exercises ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-environment-variables-and-export&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Environment Variables and &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates how environment variables are inherited by child processes but not propagated back to parents.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Check your current environment. Run &amp;lt;code&amp;gt;env | head -n 10&amp;lt;/code&amp;gt; and observe some of the variables already set.&lt;br /&gt;
# Create a shell variable without exporting it: &amp;lt;code&amp;gt;myvar=&amp;amp;quot;not exported&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell with &amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt; and try to read &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;echo &amp;amp;quot;$myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. It should be empty. Exit the subshell.&lt;br /&gt;
# Back in your original shell, export the variable: &amp;lt;code&amp;gt;export myvar=&amp;amp;quot;exported now&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start a subshell again with &amp;lt;code&amp;gt;bash -c &amp;#039;echo &amp;amp;quot;In child: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt; and verify that &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; is now accessible.&lt;br /&gt;
# In a subshell, modify the variable: &amp;lt;code&amp;gt;bash -c &amp;#039;myvar=&amp;amp;quot;changed in child&amp;amp;quot;; echo &amp;amp;quot;Child modified: $myvar&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Print &amp;lt;code&amp;gt;myvar&amp;lt;/code&amp;gt; in the parent shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;Parent still has: $myvar&amp;amp;quot;&amp;lt;/code&amp;gt;. Observe that the parent’s value is unchanged.&lt;br /&gt;
# Demonstrate setting an environment variable for a single command: &amp;lt;code&amp;gt;GREETING=&amp;amp;quot;Hello&amp;amp;quot; bash -c &amp;#039;echo $GREETING&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify that &amp;lt;code&amp;gt;GREETING&amp;lt;/code&amp;gt; is not set in your current shell: &amp;lt;code&amp;gt;echo &amp;amp;quot;GREETING in parent: $GREETING&amp;amp;quot;&amp;lt;/code&amp;gt; (should be empty).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;env&amp;lt;/code&amp;gt; to run a command with a specific environment: &amp;lt;code&amp;gt;env DEBUG=1 bash -c &amp;#039;echo &amp;amp;quot;DEBUG=$DEBUG&amp;amp;quot;&amp;#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide the output showing: the subshell cannot see the unexported variable, the subshell can see the exported variable, the parent is unaffected by child changes, and single-command environment variable setting.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-pipes-in-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Pipes in Practice ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores anonymous pipes and how they connect processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use a simple pipe to count lines: &amp;lt;code&amp;gt;cat /etc/passwd | wc -l&amp;lt;/code&amp;gt;. Observe the result.&lt;br /&gt;
# Build a longer pipeline to find how many unique shells are in use: &amp;lt;code&amp;gt;cat /etc/passwd | cut -d: -f7 | sort | uniq&amp;lt;/code&amp;gt;. Count them manually or pipe to &amp;lt;code&amp;gt;wc -l&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate that processes in a pipeline run concurrently. Run &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; command produces infinite output, but &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt; reads only 5 lines and then exits, causing &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to receive a &amp;lt;code&amp;gt;SIGPIPE&amp;lt;/code&amp;gt; and terminate.&lt;br /&gt;
# Create a sample log file for analysis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab6_sample.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout&lt;br /&gt;
2024-01-15 10:15:00 INFO Retry successful&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration&lt;br /&gt;
2024-01-15 10:25:00 WARN Low memory&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;5&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to count ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | wc -l&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use a pipeline to extract and count unique log levels: &amp;lt;code&amp;gt;cut -d&amp;#039; &amp;#039; -f3 /tmp/lab6_sample.log | sort | uniq -c&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Combine multiple operations: Find the timestamps of all ERROR entries: &amp;lt;code&amp;gt;grep &amp;amp;quot;ERROR&amp;amp;quot; /tmp/lab6_sample.log | cut -d&amp;#039; &amp;#039; -f1,2&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the output from the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; shell analysis, the &amp;lt;code&amp;gt;yes | head -n 5&amp;lt;/code&amp;gt; demonstration, and the log file analysis commands showing ERROR count and unique log levels with counts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-named-pipes-fifos&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Named Pipes (FIFOs) ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates persistent named pipes and communication between unrelated processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a named pipe: &amp;lt;code&amp;gt;mkfifo /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify it exists and note its type: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;. The leading &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; indicates a FIFO.&lt;br /&gt;
# In one terminal, start a reader that will block: &amp;lt;code&amp;gt;cat &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;. Leave this running.&lt;br /&gt;
# In a second terminal, write to the FIFO: &amp;lt;code&amp;gt;echo &amp;amp;quot;Hello via FIFO&amp;amp;quot; &amp;amp;gt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe that the reader in the first terminal unblocks, displays the message, and exits.&lt;br /&gt;
# Demonstrate that the FIFO persists. List it again: &amp;lt;code&amp;gt;ls -l /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Test a more complex scenario. In terminal 1: &amp;lt;code&amp;gt;while read line; do echo &amp;amp;quot;Received: $line&amp;amp;quot;; done &amp;amp;lt; /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In terminal 2, send multiple messages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;Message 1&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 2&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 3&amp;quot; &amp;gt; /tmp/lab6_fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;9&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Observe the messages being received. Press Ctrl+C in terminal 1 to stop the reader.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Remove the FIFO: &amp;lt;code&amp;gt;rm /tmp/lab6_fifo&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the FIFO type, screenshots or output from both terminals showing the message exchange, and a brief explanation of how the FIFO persists between writes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-signals-and-trap&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Signals and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates signal handling in bash using interactive commands.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Start a simple sleep command: &amp;lt;code&amp;gt;sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C to interrupt it. Observe that it terminates immediately.&lt;br /&gt;
# Now run a command that ignores SIGINT: &amp;lt;code&amp;gt;trap &amp;#039;echo &amp;amp;quot;Caught SIGINT, ignoring...&amp;amp;quot;&amp;#039; INT; sleep 30&amp;lt;/code&amp;gt;. Press Ctrl+C. The trap catches the signal and the sleep continues.&lt;br /&gt;
# Demonstrate cleanup on exit. Run this compound command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;trap &amp;#039;echo &amp;quot;Cleanup: removing temp file&amp;quot;; rm -f /tmp/lab6_test.txt&amp;#039; EXIT; \&lt;br /&gt;
touch /tmp/lab6_test.txt; \&lt;br /&gt;
echo &amp;quot;File created. Press Ctrl+C or wait...&amp;quot;; \&lt;br /&gt;
sleep 10; \&lt;br /&gt;
echo &amp;quot;Normal exit&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;4&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Try both: let it complete normally, then run it again and interrupt with Ctrl+C. Observe cleanup happens both times.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start a background sleep: &amp;lt;code&amp;gt;sleep 100 &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID displayed.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGTERM to it: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; (replace &amp;lt;code&amp;gt;&amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt; with the actual PID). Verify it terminated: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Start another background process that will ignore SIGTERM:&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;(trap &amp;#039;echo &amp;quot;Caught SIGTERM, staying alive&amp;quot;&amp;#039; TERM; \&lt;br /&gt;
 while true; do echo &amp;quot;Running...&amp;quot;; sleep 5; done) &amp;amp;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;ol start=&amp;quot;8&amp;quot; style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Note the PID, then send SIGTERM: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. Observe the trap message.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Send SIGKILL to force termination: &amp;lt;code&amp;gt;kill -9 &amp;amp;lt;pid&amp;amp;gt;&amp;lt;/code&amp;gt;. This cannot be caught.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Verify all background jobs are gone: &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide output showing: the trapped SIGINT message, cleanup on both normal exit and Ctrl+C, the SIGTERM being caught with the trap message, and SIGKILL forcing termination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-socket-communication-basics&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: Socket Communication Basics ===&lt;br /&gt;
&lt;br /&gt;
This exercise provides a brief introduction to socket communication using existing tools.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a simple static site directory: &amp;lt;code&amp;gt;mkdir -p /tmp/lab6_site &amp;amp;amp;&amp;amp;amp; echo &amp;amp;quot;&amp;amp;lt;h1&amp;amp;gt;Hello from Caddy&amp;amp;lt;/h1&amp;amp;gt;&amp;amp;quot; &amp;amp;gt; /tmp/lab6_site/index.html&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Start Caddy as a file server listening on a Unix domain socket: &amp;lt;code&amp;gt;caddy file-server --root /tmp/lab6_site --listen unix//tmp/caddy.sock &amp;amp;amp;&amp;lt;/code&amp;gt;. Note the PID.&lt;br /&gt;
# Wait a moment for Caddy to start. Verify the socket exists: &amp;lt;code&amp;gt;ls -l /tmp/caddy.sock&amp;lt;/code&amp;gt;. Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; type indicating a socket.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; to make an HTTP request via the Unix domain socket: &amp;lt;code&amp;gt;curl -v --unix-socket /tmp/caddy.sock http://localhost/&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the response. Note that the communication is bidirectional: you sent an HTTP request, and the server responded with the HTML content.&lt;br /&gt;
# (Optional) Open multiple terminals and run &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; simultaneously several times. Each request is handled independently, demonstrating the one-to-many capability of sockets.&lt;br /&gt;
# Stop Caddy by sending &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;kill -TERM &amp;amp;lt;caddy_pid&amp;amp;gt;&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;pkill caddy&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Clean up: &amp;lt;code&amp;gt;rm -rf /tmp/lab6_site /tmp/caddy.sock&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output showing the socket file, the &amp;lt;code&amp;gt;curl&amp;lt;/code&amp;gt; output demonstrating the request and response (especially the HTTP headers and HTML body), and a brief explanation (2-3 sentences) of how this differs from a named pipe in terms of directionality and connection multiplexing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-aggregator-with-named-pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log Aggregator with Named Pipes ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_log_aggregator.sh&amp;lt;/code&amp;gt; that aggregates log messages from multiple sources using named pipes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Create three named pipes: &amp;lt;code&amp;gt;/tmp/log_fifo1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/tmp/log_fifo3&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Start three background processes that each write timestamped messages to one of the FIFOs (simulating different log sources). Each should write 5 messages at 1-second intervals.&lt;br /&gt;
* Implement a main loop that reads from all three FIFOs simultaneously (hint: use &amp;lt;code&amp;gt;select&amp;lt;/code&amp;gt; via &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; with timeout, or use multiple background readers).&lt;br /&gt;
* Write all received messages to a combined log file &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; with timestamps.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; to clean up: kill background processes, remove FIFOs, and exit gracefully.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, background processes (&amp;lt;code&amp;gt;&amp;amp;amp;&amp;lt;/code&amp;gt;), proper cleanup.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab6_log_aggregator.sh&lt;br /&gt;
/tmp/lab6_log_aggregator.sh &amp;amp;&lt;br /&gt;
AGG_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -TERM $AGG_PID&lt;br /&gt;
wait&lt;br /&gt;
cat /tmp/aggregated.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Contents of &amp;lt;code&amp;gt;/tmp/aggregated.log&amp;lt;/code&amp;gt; showing interleaved messages from all three sources - Brief explanation (2-3 sentences) of why named pipes were necessary here instead of anonymous pipes&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-graceful-service-controller&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Graceful Service Controller ===&lt;br /&gt;
&lt;br /&gt;
Write a script &amp;lt;code&amp;gt;/tmp/lab6_service_controller.sh&amp;lt;/code&amp;gt; that manages a long-running service and responds to signals.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* The script acts as a daemon that runs indefinitely, printing a heartbeat message every 5 seconds.&lt;br /&gt;
* Accept one optional argument: a “config file” path. If provided, read a setting (e.g., &amp;lt;code&amp;gt;INTERVAL=10&amp;lt;/code&amp;gt;) from the file to control the heartbeat interval.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt;: Reload the configuration file and adjust the interval dynamically without restarting the script. Print “Configuration reloaded.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;: Perform graceful shutdown. Print “Shutting down gracefully…”, wait 2 seconds, then exit cleanly.&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt;: Export the current status to &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; (e.g., uptime, number of heartbeats sent, current interval). Print “Status exported.”&lt;br /&gt;
* Trap &amp;lt;code&amp;gt;EXIT&amp;lt;/code&amp;gt;: Perform cleanup. Print “Service stopped.”&lt;br /&gt;
* Maintain internal state: count the number of heartbeats sent, track start time.&lt;br /&gt;
* Use: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions, &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; for multiple signals, variables for state, a loop, cleanup handler.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;INTERVAL=3&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
chmod +x /tmp/lab6_service_controller.sh&lt;br /&gt;
/tmp/lab6_service_controller.sh /tmp/service.conf &amp;amp;&lt;br /&gt;
SVC_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -USR1 $SVC_PID  # Export status&lt;br /&gt;
cat /tmp/service_status.txt&lt;br /&gt;
echo &amp;quot;INTERVAL=1&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
kill -HUP $SVC_PID  # Reload config&lt;br /&gt;
sleep 5&lt;br /&gt;
kill -TERM $SVC_PID  # Graceful shutdown&lt;br /&gt;
wait&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039; - Complete script with comments - Output showing heartbeats before and after &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; (with visible interval change) - Contents of &amp;lt;code&amp;gt;/tmp/service_status.txt&amp;lt;/code&amp;gt; after &amp;lt;code&amp;gt;SIGUSR1&amp;lt;/code&amp;gt; - Output showing graceful shutdown message after &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-ipc-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common IPC Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for IPC mechanisms covered in this lab:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Environment Variables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Export a variable for child processes&lt;br /&gt;
export VAR=&amp;quot;value&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Set for one command only&lt;br /&gt;
VAR=&amp;quot;value&amp;quot; command&lt;br /&gt;
&lt;br /&gt;
# View all environment variables&lt;br /&gt;
env&lt;br /&gt;
printenv&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Anonymous Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Simple pipeline&lt;br /&gt;
command1 | command2&lt;br /&gt;
&lt;br /&gt;
# Multi-stage pipeline&lt;br /&gt;
cat file.txt | grep &amp;quot;pattern&amp;quot; | sort | uniq&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Named Pipes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create a FIFO&lt;br /&gt;
mkfifo /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Write to FIFO (blocks until reader connects)&lt;br /&gt;
echo &amp;quot;data&amp;quot; &amp;gt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Read from FIFO (blocks until writer connects)&lt;br /&gt;
cat &amp;lt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Remove FIFO&lt;br /&gt;
rm /path/to/fifo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signals:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Send signal by name&lt;br /&gt;
kill -TERM &amp;lt;pid&amp;gt;&lt;br /&gt;
kill -HUP &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Send signal by number&lt;br /&gt;
kill -15 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Force kill (cannot be caught)&lt;br /&gt;
kill -9 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trap signals in a script&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught signal&amp;quot;&amp;#039; INT TERM&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Define cleanup function&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myfiles.*&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sockets (using existing tools):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Start a server on Unix domain socket (using socat)&lt;br /&gt;
socat UNIX-LISTEN:/tmp/service.sock,fork EXEC:&amp;#039;/usr/bin/myhandler&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Connect as client (using socat)&lt;br /&gt;
echo &amp;quot;request&amp;quot; | socat - UNIX-CONNECT:/tmp/service.sock&lt;br /&gt;
&lt;br /&gt;
# HTTP via Unix socket (using curl)&lt;br /&gt;
curl --unix-socket /tmp/service.sock http://localhost/path&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-patterns-table&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Patterns Table ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mechanism&lt;br /&gt;
! Direction&lt;br /&gt;
! Persistence&lt;br /&gt;
! Use Case&lt;br /&gt;
|-&lt;br /&gt;
| Environment Variables&lt;br /&gt;
| Parent → Child (one-way)&lt;br /&gt;
| Inherited at fork&lt;br /&gt;
| Configuration, credentials&lt;br /&gt;
|-&lt;br /&gt;
| Pipes (&amp;lt;code&amp;gt;\|&amp;lt;/code&amp;gt;)&lt;br /&gt;
| One-way&lt;br /&gt;
| Ephemeral (process lifetime)&lt;br /&gt;
| Command chaining, streaming&lt;br /&gt;
|-&lt;br /&gt;
| Named Pipes (FIFO)&lt;br /&gt;
| One-way&lt;br /&gt;
| Filesystem entry persists&lt;br /&gt;
| Unrelated process communication&lt;br /&gt;
|-&lt;br /&gt;
| Signals&lt;br /&gt;
| One-way (notification)&lt;br /&gt;
| Asynchronous event&lt;br /&gt;
| Process control, event notification&lt;br /&gt;
|-&lt;br /&gt;
| Sockets&lt;br /&gt;
| Bidirectional&lt;br /&gt;
| Filesystem entry persists (Unix domain)&lt;br /&gt;
| Client-server, network services&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039; - Exercise A: Outputs demonstrating export behavior, parent-child isolation, and single-command environment variables - Exercise B: Pipeline outputs and complete analysis script - Exercise C: FIFO creation output, producer/consumer scripts with sample output - Exercise D: Complete daemon simulation script with signal handling demonstrations - Exercise E: Socket file listing, curl output with HTTP headers, explanation of socket vs FIFO differences&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039; - Challenge 1: Complete log aggregator script, aggregated log contents, explanation - Challenge 2: Complete service controller script, outputs demonstrating all signal handlers (SIGHUP reload, SIGUSR1 status export, SIGTERM shutdown)&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Additional:&amp;#039;&amp;#039;&amp;#039; - Each deliverable should include command outputs (screenshots or text) and brief explanations where requested. - For scripts, include the complete, commented source code and example execution output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab covers the fundamental IPC mechanisms accessible from bash. You’ve learned how processes inherit environment variables, communicate via pipes, respond to signals, and use sockets for network communication. These concepts form the foundation for system administration, scripting, and understanding how complex systems coordinate.&lt;br /&gt;
&lt;br /&gt;
For further study: - Advanced IPC: Shared memory, message queues, semaphores (typically used in C/C++ programs, not bash) - Network programming: TCP/UDP sockets, client-server architecture - D-Bus: A modern IPC system used by desktop environments and systemd - Signal safety: Writing robust signal handlers (critical in C, less relevant in bash)&lt;br /&gt;
&lt;br /&gt;
Relevant manual pages: - &amp;lt;code&amp;gt;man 7 pipe&amp;lt;/code&amp;gt; - Pipe overview - &amp;lt;code&amp;gt;man 7 fifo&amp;lt;/code&amp;gt; - Named pipe overview&amp;lt;br /&amp;gt;&lt;br /&gt;
- &amp;lt;code&amp;gt;man 7 signal&amp;lt;/code&amp;gt; - Signal overview - &amp;lt;code&amp;gt;man 7 unix&amp;lt;/code&amp;gt; - Unix domain sockets - &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt; - Section on trap and job control&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8174</id>
		<title>OS Lab 6 - Inter Process Communication</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_6_-_Inter_Process_Communication&amp;diff=8174"/>
		<updated>2025-11-14T15:02:24Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: # OS Lab 6 - Inter-Process Communication  ## Objectives  Upon completion of this lab, you will be able to:  - Explain how the kernel provides IPC mechanisms and how bash exposes th...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# OS Lab 6 - Inter-Process Communication&lt;br /&gt;
&lt;br /&gt;
## Objectives&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
- Explain how the kernel provides IPC mechanisms and how bash exposes them to scripts.&lt;br /&gt;
- Use environment variables and `export` to pass configuration from parent processes to children.&lt;br /&gt;
- Construct pipelines with anonymous pipes to connect process standard streams.&lt;br /&gt;
- Create and use named pipes (FIFOs) for communication between unrelated processes.&lt;br /&gt;
- Send signals to processes and handle them with `trap` for asynchronous event-driven scripting.&lt;br /&gt;
- Demonstrate basic socket communication using existing utilities for network IPC.&lt;br /&gt;
&lt;br /&gt;
## Introduction&lt;br /&gt;
&lt;br /&gt;
In Lab 5, we explored bash scripting as a way to automate tasks by writing programs that the shell interprets. We learned how processes execute scripts, how to control flow with conditionals and loops, and how to structure code with functions. However, all of our scripts operated in isolation. Each script was a single process (or spawned child processes) that performed its task independently.&lt;br /&gt;
&lt;br /&gt;
Real-world systems require processes to cooperate. A web server must communicate with a database. A shell pipeline connects the output of one program to the input of another. A service must respond to signals sent by the init system. This lab explores the kernel-provided mechanisms that enable processes to exchange data and coordinate their actions. We focus on five fundamental IPC mechanisms, all accessible from bash: environment variables, pipes, named pipes, signals, and sockets.&lt;br /&gt;
&lt;br /&gt;
Understanding IPC is essential for system administration and software development. These mechanisms form the foundation of everything from command pipelines to distributed systems.&lt;br /&gt;
&lt;br /&gt;
## Prerequisites&lt;br /&gt;
&lt;br /&gt;
### System Requirements&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
### Required Packages&lt;br /&gt;
&lt;br /&gt;
The following packages must be installed:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y caddy socat&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
- `caddy`: A modern HTTP server with automatic HTTPS. We use it to demonstrate socket communication.&lt;br /&gt;
- `socat`: A versatile networking tool that can work with Unix domain sockets.&lt;br /&gt;
&lt;br /&gt;
### Knowledge Prerequisites&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
- Process concepts from Lab 3 (PIDs, process hierarchy, file descriptors)&lt;br /&gt;
- File permissions from Lab 4 (execute bit, ownership)&lt;br /&gt;
- Bash scripting fundamentals from Lab 5 (shebangs, builtins, variables, quoting, exit status, redirection, loops, functions)&lt;br /&gt;
&lt;br /&gt;
## Inter-Process Communication&lt;br /&gt;
&lt;br /&gt;
### What is IPC?&lt;br /&gt;
&lt;br /&gt;
By default, processes are isolated from one another. Each process has its own memory space, file descriptor table, and execution context. This isolation provides security and stability, but it creates a problem: how can processes cooperate to accomplish complex tasks?&lt;br /&gt;
&lt;br /&gt;
Inter-Process Communication (IPC) refers to the kernel-provided mechanisms that allow processes to exchange data and synchronize their actions. The kernel acts as an intermediary, providing channels, buffers, and signaling primitives that processes can use to communicate safely without violating isolation boundaries.&lt;br /&gt;
&lt;br /&gt;
In this lab, we examine five IPC mechanisms arranged roughly by complexity:&lt;br /&gt;
&lt;br /&gt;
1. **Environment Variables**: The simplest form of IPC. A parent process passes key-value configuration to its children via inherited environment variables. Communication is unidirectional (parent → child) and occurs only at process creation.&lt;br /&gt;
&lt;br /&gt;
2. **Pipes**: The kernel creates a buffer connecting one process&amp;#039;s standard output to another&amp;#039;s standard input. Data flows in one direction through the pipe. Anonymous pipes exist only while the processes using them are running.&lt;br /&gt;
&lt;br /&gt;
3. **Named Pipes (FIFOs)**: Like anonymous pipes, but visible in the filesystem. This persistence allows unrelated processes to connect to the same pipe by opening a file path.&lt;br /&gt;
&lt;br /&gt;
4. **Signals**: Asynchronous notifications sent from one process to another (or from the kernel to a process). Signals interrupt the receiving process, which can catch and handle them or allow default behavior (often termination).&lt;br /&gt;
&lt;br /&gt;
5. **Sockets**: Bidirectional communication channels that work across network boundaries or locally via Unix domain sockets. Multiple processes can connect to the same socket, enabling one-to-many communication patterns.&lt;br /&gt;
&lt;br /&gt;
Each mechanism solves different problems and has different trade-offs in terms of complexity, performance, and flexibility.&lt;br /&gt;
&lt;br /&gt;
### Environment Variables and `export`&lt;br /&gt;
&lt;br /&gt;
#### The Kernel&amp;#039;s Role&lt;br /&gt;
&lt;br /&gt;
When a process forks, the child inherits a copy of the parent&amp;#039;s environment: a set of key-value string pairs maintained by the kernel for each process. The child can read these values and modify its own copy, but changes do not propagate back to the parent or to other processes. This inheritance mechanism provides a simple, unidirectional channel for passing configuration from parent to child.&lt;br /&gt;
&lt;br /&gt;
Environment variables are not limited to shell scripts. Every process has an environment. When you run any program, it receives the environment from the shell that launched it. Programs written in C access this via the `environ` global variable or the third parameter to `main()`. Python uses `os.environ`. The environment is a universal convention for passing configuration.&lt;br /&gt;
&lt;br /&gt;
#### Bash&amp;#039;s Role: `export` and `env`&lt;br /&gt;
&lt;br /&gt;
In bash, variables are local to the shell process by default. When you set `name=value`, that variable exists in the shell&amp;#039;s memory but is **not** passed to child processes. The `export` builtin marks a variable for inclusion in the environment of future child processes:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
export myvar&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Or, more concisely:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
export myvar=&amp;quot;hello&amp;quot;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Once exported, all child processes started by this shell (scripts, programs, or subshells) will inherit `myvar` in their environment. To see the current environment, use the `env` command (or `printenv`), which prints all exported variables.&lt;br /&gt;
&lt;br /&gt;
You can also set environment variables for a single command without affecting the shell:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
DEBUG=1 ./myscript.sh&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
This syntax sets `DEBUG=1` in the environment of `myscript.sh` only, without exporting it in the parent shell.&lt;br /&gt;
&lt;br /&gt;
Common environment variables you&amp;#039;ve already been using include `PATH` (where bash searches for commands), `HOME` (your home directory), `USER` (your username), and `SHELL` (your login shell). These are all set by the login process and inherited by every subsequent process in your session.&lt;br /&gt;
&lt;br /&gt;
#### When to Use Environment Variables&lt;br /&gt;
&lt;br /&gt;
Environment variables are appropriate for:&lt;br /&gt;
- Configuration that should be inherited by all child processes (e.g., locale settings, proxy configuration)&lt;br /&gt;
- Passing secrets or credentials to programs without embedding them in command-line arguments (which are visible via `ps`)&lt;br /&gt;
- Controlling program behavior via well-known variables like `PATH`, `LD_LIBRARY_PATH`, or `TZ`&lt;br /&gt;
&lt;br /&gt;
They are **not** suitable for:&lt;br /&gt;
- Runtime communication between already-running processes (use pipes, sockets, or signals)&lt;br /&gt;
- Large amounts of data (environment is limited in size, typically a few megabytes)&lt;br /&gt;
- Bi-directional communication (child changes don&amp;#039;t affect parent)&lt;br /&gt;
&lt;br /&gt;
### Pipes&lt;br /&gt;
&lt;br /&gt;
#### The Kernel&amp;#039;s Pipe Mechanism&lt;br /&gt;
&lt;br /&gt;
A pipe is a one-way data channel maintained in kernel memory. When you create a pipe, the kernel allocates a buffer (typically 64 KB on Linux) and returns two file descriptors: one for writing and one for reading. Data written to the write end is buffered by the kernel and can be read from the read end in FIFO (first-in, first-out) order.&lt;br /&gt;
&lt;br /&gt;
Pipes are anonymous: they have no name in the filesystem. They exist only as long as at least one process holds a file descriptor to them. When all processes close their references to a pipe, the kernel deallocates it.&lt;br /&gt;
&lt;br /&gt;
The key insight from Lab 3: a pipeline like `cat file.txt | grep &amp;quot;error&amp;quot; | wc -l` creates multiple processes (all in the same process group) connected by pipes. The kernel sets up the file descriptors so that `cat`&amp;#039;s stdout is connected to `grep`&amp;#039;s stdin, and `grep`&amp;#039;s stdout is connected to `wc`&amp;#039;s stdin. The processes run concurrently, with data flowing through kernel buffers as it&amp;#039;s produced and consumed.&lt;br /&gt;
&lt;br /&gt;
#### Bash&amp;#039;s Pipe Operator&lt;br /&gt;
&lt;br /&gt;
Bash creates pipes using the `|` operator. The syntax is simple:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
command1 | command2&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
This creates a pipe and starts two processes. Bash configures `command1`&amp;#039;s stdout (FD 1) to point to the pipe&amp;#039;s write end and `command2`&amp;#039;s stdin (FD 0) to point to the pipe&amp;#039;s read end. The commands execute concurrently.&lt;br /&gt;
&lt;br /&gt;
Longer pipelines work the same way:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
cat data.txt | sort | uniq | wc -l&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
This creates three pipes connecting four processes. Data flows left-to-right through kernel buffers.&lt;br /&gt;
&lt;br /&gt;
#### Pipes in Scripts&lt;br /&gt;
&lt;br /&gt;
You can use pipes inside scripts just as you would interactively:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
# Count unique IP addresses in an access log&lt;br /&gt;
cat /var/log/access.log | cut -d&amp;#039; &amp;#039; -f1 | sort | uniq | wc -l&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Pipes are particularly powerful when combined with bash&amp;#039;s process substitution feature `&amp;lt;(command)`, but that&amp;#039;s beyond the scope of this introductory lab.&lt;br /&gt;
&lt;br /&gt;
#### When to Use Pipes&lt;br /&gt;
&lt;br /&gt;
Pipes are ideal for:&lt;br /&gt;
- Connecting the output of one program to the input of another in a linear processing chain&lt;br /&gt;
- Streaming data processing where data is generated and consumed incrementally&lt;br /&gt;
- Quick, one-off data transformations at the command line&lt;br /&gt;
&lt;br /&gt;
They are **not** suitable for:&lt;br /&gt;
- Bi-directional communication (data flows one way only)&lt;br /&gt;
- Communication between unrelated processes that weren&amp;#039;t started in a pipeline&lt;br /&gt;
- Persistent communication (pipe disappears when processes exit)&lt;br /&gt;
&lt;br /&gt;
### Named Pipes (FIFOs)&lt;br /&gt;
&lt;br /&gt;
#### The Kernel&amp;#039;s FIFO Mechanism&lt;br /&gt;
&lt;br /&gt;
A named pipe, or FIFO (First-In, First-Out), is a pipe with a name in the filesystem. Unlike anonymous pipes, FIFOs persist as filesystem entries (though the data buffer is still in kernel memory). Any process with appropriate permissions can open the FIFO by its path, allowing unrelated processes to communicate.&lt;br /&gt;
&lt;br /&gt;
When a process opens a FIFO for reading, it blocks until another process opens the same FIFO for writing (and vice versa). Once both ends are open, data flows through the kernel buffer just like an anonymous pipe. When all processes close their connections, the FIFO remains as a filesystem entry but the kernel buffer is deallocated.&lt;br /&gt;
&lt;br /&gt;
You can identify a FIFO in `ls -l` output by the leading `p` in the permission string:&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
prw-r--r-- 1 user user 0 Nov  6 10:00 myfifo&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
#### Creating FIFOs with `mkfifo`&lt;br /&gt;
&lt;br /&gt;
The `mkfifo` command creates a named pipe:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
mkfifo /tmp/myfifo&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
Now two unrelated processes can communicate by opening this file. One writes to it:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
echo &amp;quot;Hello from writer&amp;quot; &amp;gt; /tmp/myfifo&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
This command will block until a reader appears. In another terminal (or in the background), a reader can consume the data:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
cat &amp;lt; /tmp/myfifo&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
When the reader connects, the writer unblocks, the message flows through the kernel buffer, and both commands complete.&lt;br /&gt;
&lt;br /&gt;
#### When to Use Named Pipes&lt;br /&gt;
&lt;br /&gt;
Named pipes are useful for:&lt;br /&gt;
- Communication between unrelated processes that start at different times&lt;br /&gt;
- Producer-consumer patterns where one process generates data and another processes it&lt;br /&gt;
- Simple IPC without needing network sockets or shared files&lt;br /&gt;
&lt;br /&gt;
They are **not** suitable for:&lt;br /&gt;
- Multiple simultaneous readers or writers (FIFO semantics become unpredictable)&lt;br /&gt;
- Persistent data storage (data is lost when all processes disconnect)&lt;br /&gt;
- Bi-directional communication (like anonymous pipes, FIFOs are one-way)&lt;br /&gt;
&lt;br /&gt;
### Signals and `trap`&lt;br /&gt;
&lt;br /&gt;
#### The Kernel&amp;#039;s Signal Mechanism&lt;br /&gt;
&lt;br /&gt;
A signal is an asynchronous notification sent to a process. Signals can be sent by other processes (via the `kill` system call) or by the kernel itself in response to events like segmentation faults, keyboard interrupts (Ctrl+C), or child process termination.&lt;br /&gt;
&lt;br /&gt;
When the kernel delivers a signal to a process, it interrupts the process&amp;#039;s normal execution. The process can respond in one of three ways:&lt;br /&gt;
&lt;br /&gt;
1. **Default Action**: Each signal has a default behavior, often terminating the process. For example, `SIGTERM` (signal 15) gracefully terminates, while `SIGKILL` (signal 9) forces immediate termination.&lt;br /&gt;
&lt;br /&gt;
2. **Ignore**: The process can choose to ignore certain signals (except `SIGKILL` and `SIGSTOP`, which cannot be caught or ignored).&lt;br /&gt;
&lt;br /&gt;
3. **Custom Handler**: The process can register a function (signal handler) to execute when the signal arrives. This allows the process to perform cleanup or take other actions before deciding whether to continue, terminate, or take other action.&lt;br /&gt;
&lt;br /&gt;
Common signals:&lt;br /&gt;
- `SIGINT` (2): Sent by Ctrl+C. Default: terminate.&lt;br /&gt;
- `SIGTERM` (15): Polite request to terminate. Default: terminate. Most services handle this to perform clean shutdown.&lt;br /&gt;
- `SIGKILL` (9): Immediate termination. Cannot be caught or ignored. Used as a last resort.&lt;br /&gt;
- `SIGHUP` (1): Historically &amp;quot;hang up&amp;quot; (modem disconnected). Often used to tell daemons to reload configuration.&lt;br /&gt;
- `SIGCHLD` (17): Sent to a parent when a child process terminates.&lt;br /&gt;
- `SIGUSR1` (10) and `SIGUSR2` (12): User-defined signals for custom purposes.&lt;br /&gt;
&lt;br /&gt;
Signals are sent by PID:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
kill -TERM 1234    # Send SIGTERM to process 1234&lt;br /&gt;
kill -9 1234       # Send SIGKILL to process 1234&lt;br /&gt;
kill -HUP 1234     # Send SIGHUP to process 1234&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
You can also use the `%n` job notation from Lab 3 to send signals to background jobs.&lt;br /&gt;
&lt;br /&gt;
#### Bash&amp;#039;s `trap` Builtin&lt;br /&gt;
&lt;br /&gt;
The `trap` builtin allows a bash script to register a handler for incoming signals:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught SIGINT&amp;quot;; exit&amp;#039; INT&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
This tells bash: &amp;quot;When SIGINT arrives, execute the command `echo &amp;quot;Caught SIGINT&amp;quot;; exit`.&amp;quot; The handler can be any bash command or function.&lt;br /&gt;
&lt;br /&gt;
Common pattern for cleanup on exit:&lt;br /&gt;
&lt;br /&gt;
```bash&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
set -euo pipefail&lt;br /&gt;
&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up temporary files...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myscript.*&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Script body&lt;br /&gt;
echo &amp;quot;Running...&amp;quot;&lt;br /&gt;
sleep 10&lt;br /&gt;
echo &amp;quot;Done&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cleanup() is automatically called on normal exit, Ctrl+C, or errors (with set -e)&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
The special signal `EXIT` isn&amp;#039;t a real signal; it&amp;#039;s a bash pseudo-signal that fires whenever the script exits for any reason.&lt;br /&gt;
&lt;br /&gt;
#### When to Use Signals&lt;br /&gt;
&lt;br /&gt;
Signals are appropriate for:&lt;br /&gt;
- Event-driven scripts that respond to external events&lt;br /&gt;
- Graceful shutdown and cleanup on termination&lt;br /&gt;
- Daemon control (reload config with `SIGHUP`, graceful restart with `SIGTERM`)&lt;br /&gt;
- Inter-process coordination where one process needs to notify another of state changes&lt;br /&gt;
&lt;br /&gt;
They are **not** suitable for:&lt;br /&gt;
- Transferring data (signals carry almost no information, just the signal number)&lt;br /&gt;
- Reliable communication (signals can be lost or delayed)&lt;br /&gt;
- Complex coordination (race conditions are common)&lt;br /&gt;
&lt;br /&gt;
### Sockets&lt;br /&gt;
&lt;br /&gt;
#### The Kernel&amp;#039;s Socket Mechanism&lt;br /&gt;
&lt;br /&gt;
A socket is a bidirectional communication endpoint. Unlike pipes, sockets support two-way data flow. Unlike named pipes, sockets support multiple concurrent connections, making them suitable for client-server architectures.&lt;br /&gt;
&lt;br /&gt;
There are two main types:&lt;br /&gt;
&lt;br /&gt;
1. **Network Sockets**: Use TCP or UDP to communicate over IP networks. These work across machines and are the foundation of the internet.&lt;br /&gt;
&lt;br /&gt;
2. **Unix Domain Sockets**: Use file paths (like named pipes) but support bidirectional communication and connection multiplexing. These work only on the same machine but are faster than network sockets.&lt;br /&gt;
&lt;br /&gt;
When a server process binds to a socket, it listens for incoming connections. Multiple client processes can connect to the same server socket. Each connection is independent, with its own bidirectional channel. This one-to-many pattern distinguishes sockets from pipes and FIFOs.&lt;br /&gt;
&lt;br /&gt;
For network sockets, the server binds to a port number (e.g., port 80 for HTTP). For Unix domain sockets, it binds to a filesystem path (e.g., `/tmp/myservice.sock`).&lt;br /&gt;
&lt;br /&gt;
#### Socket Communication from Bash&lt;br /&gt;
&lt;br /&gt;
While bash doesn&amp;#039;t have native socket support, we can use existing tools to demonstrate socket communication without writing low-level code:&lt;br /&gt;
&lt;br /&gt;
- **caddy**: A modern web server that can listen on both network and Unix domain sockets&lt;br /&gt;
- **curl**: A command-line HTTP client that supports Unix domain sockets&lt;br /&gt;
- **socat**: A general-purpose networking tool for creating and connecting to various socket types&lt;br /&gt;
&lt;br /&gt;
These tools abstract the complexity of socket programming, allowing us to focus on the conceptual model.&lt;br /&gt;
&lt;br /&gt;
#### When to Use Sockets&lt;br /&gt;
&lt;br /&gt;
Sockets are ideal for:&lt;br /&gt;
- Client-server applications with multiple concurrent clients&lt;br /&gt;
- Networked communication across machines&lt;br /&gt;
- Bidirectional data exchange&lt;br /&gt;
- Services that need to accept connections from many processes&lt;br /&gt;
&lt;br /&gt;
They are **not** necessary for:&lt;br /&gt;
- Simple one-to-one, one-way communication (use pipes instead)&lt;br /&gt;
- Configuration passing (use environment variables)&lt;br /&gt;
- Asynchronous notifications (use signals)&lt;br /&gt;
&lt;br /&gt;
## Hands-on Exercises&lt;br /&gt;
&lt;br /&gt;
### Exercise A: Environment Variables and `export`&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates how environment variables are inherited by child processes but not propagated back to parents.&lt;br /&gt;
&lt;br /&gt;
**Steps:**&lt;br /&gt;
&lt;br /&gt;
1. Check your current environment. Run `env | head -n 10` and observe some of the variables already set.&lt;br /&gt;
2. Create a shell variable without exporting it: `myvar=&amp;quot;not exported&amp;quot;`.&lt;br /&gt;
3. Start a subshell with `bash` and try to read `myvar` with `echo &amp;quot;$myvar&amp;quot;`. It should be empty. Exit the subshell.&lt;br /&gt;
4. Back in your original shell, export the variable: `export myvar=&amp;quot;exported now&amp;quot;`.&lt;br /&gt;
5. Start a subshell again with `bash -c &amp;#039;echo &amp;quot;In child: $myvar&amp;quot;&amp;#039;` and verify that `myvar` is now accessible.&lt;br /&gt;
6. In a subshell, modify the variable: `bash -c &amp;#039;myvar=&amp;quot;changed in child&amp;quot;; echo &amp;quot;Child modified: $myvar&amp;quot;&amp;#039;`.&lt;br /&gt;
7. Print `myvar` in the parent shell: `echo &amp;quot;Parent still has: $myvar&amp;quot;`. Observe that the parent&amp;#039;s value is unchanged.&lt;br /&gt;
8. Demonstrate setting an environment variable for a single command: `GREETING=&amp;quot;Hello&amp;quot; bash -c &amp;#039;echo $GREETING&amp;#039;`. &lt;br /&gt;
9. Verify that `GREETING` is not set in your current shell: `echo &amp;quot;GREETING in parent: $GREETING&amp;quot;` (should be empty).&lt;br /&gt;
10. Use `env` to run a command with a specific environment: `env DEBUG=1 bash -c &amp;#039;echo &amp;quot;DEBUG=$DEBUG&amp;quot;&amp;#039;`.&lt;br /&gt;
&lt;br /&gt;
**Deliverable A:** Provide the output showing: the subshell cannot see the unexported variable, the subshell can see the exported variable, the parent is unaffected by child changes, and single-command environment variable setting.&lt;br /&gt;
&lt;br /&gt;
### Exercise B: Pipes in Practice&lt;br /&gt;
&lt;br /&gt;
This exercise explores anonymous pipes and how they connect processes.&lt;br /&gt;
&lt;br /&gt;
**Steps:**&lt;br /&gt;
&lt;br /&gt;
1. Use a simple pipe to count lines: `cat /etc/passwd | wc -l`. Observe the result.&lt;br /&gt;
2. Build a longer pipeline to find how many unique shells are in use: `cat /etc/passwd | cut -d: -f7 | sort | uniq`. Count them manually or pipe to `wc -l`.&lt;br /&gt;
3. Demonstrate that processes in a pipeline run concurrently. Run `yes | head -n 5`. The `yes` command produces infinite output, but `head` reads only 5 lines and then exits, causing `yes` to receive a `SIGPIPE` and terminate.&lt;br /&gt;
4. Create a sample log file for analysis:&lt;br /&gt;
```bash&lt;br /&gt;
cat &amp;gt; /tmp/lab6_sample.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout&lt;br /&gt;
2024-01-15 10:15:00 INFO Retry successful&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration&lt;br /&gt;
2024-01-15 10:25:00 WARN Low memory&lt;br /&gt;
EOF&lt;br /&gt;
```&lt;br /&gt;
5. Use a pipeline to count ERROR entries: `grep &amp;quot;ERROR&amp;quot; /tmp/lab6_sample.log | wc -l`.&lt;br /&gt;
6. Use a pipeline to extract and count unique log levels: `cut -d&amp;#039; &amp;#039; -f3 /tmp/lab6_sample.log | sort | uniq -c`.&lt;br /&gt;
7. Combine multiple operations: Find the timestamps of all ERROR entries: `grep &amp;quot;ERROR&amp;quot; /tmp/lab6_sample.log | cut -d&amp;#039; &amp;#039; -f1,2`.&lt;br /&gt;
&lt;br /&gt;
**Deliverable B:** Provide the output from the `/etc/passwd` shell analysis, the `yes | head -n 5` demonstration, and the log file analysis commands showing ERROR count and unique log levels with counts.&lt;br /&gt;
&lt;br /&gt;
### Exercise C: Named Pipes (FIFOs)&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates persistent named pipes and communication between unrelated processes.&lt;br /&gt;
&lt;br /&gt;
**Steps:**&lt;br /&gt;
&lt;br /&gt;
1. Create a named pipe: `mkfifo /tmp/lab6_fifo`.&lt;br /&gt;
2. Verify it exists and note its type: `ls -l /tmp/lab6_fifo`. The leading `p` indicates a FIFO.&lt;br /&gt;
3. In one terminal, start a reader that will block: `cat &amp;lt; /tmp/lab6_fifo`. Leave this running.&lt;br /&gt;
4. In a second terminal, write to the FIFO: `echo &amp;quot;Hello via FIFO&amp;quot; &amp;gt; /tmp/lab6_fifo`.&lt;br /&gt;
5. Observe that the reader in the first terminal unblocks, displays the message, and exits.&lt;br /&gt;
6. Demonstrate that the FIFO persists. List it again: `ls -l /tmp/lab6_fifo`.&lt;br /&gt;
7. Test a more complex scenario. In terminal 1: `while read line; do echo &amp;quot;Received: $line&amp;quot;; done &amp;lt; /tmp/lab6_fifo`.&lt;br /&gt;
8. In terminal 2, send multiple messages:&lt;br /&gt;
```bash&lt;br /&gt;
echo &amp;quot;Message 1&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 2&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
echo &amp;quot;Message 3&amp;quot; &amp;gt; /tmp/lab6_fifo&lt;br /&gt;
```&lt;br /&gt;
9. Observe the messages being received. Press Ctrl+C in terminal 1 to stop the reader.&lt;br /&gt;
10. Remove the FIFO: `rm /tmp/lab6_fifo`.&lt;br /&gt;
&lt;br /&gt;
**Deliverable C:** Provide the `ls -l` output showing the FIFO type, screenshots or output from both terminals showing the message exchange, and a brief explanation of how the FIFO persists between writes.&lt;br /&gt;
&lt;br /&gt;
### Exercise D: Signals and `trap`&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates signal handling in bash using interactive commands.&lt;br /&gt;
&lt;br /&gt;
**Steps:**&lt;br /&gt;
&lt;br /&gt;
1. Start a simple sleep command: `sleep 30`. Press Ctrl+C to interrupt it. Observe that it terminates immediately.&lt;br /&gt;
2. Now run a command that ignores SIGINT: `trap &amp;#039;echo &amp;quot;Caught SIGINT, ignoring...&amp;quot;&amp;#039; INT; sleep 30`. Press Ctrl+C. The trap catches the signal and the sleep continues.&lt;br /&gt;
3. Demonstrate cleanup on exit. Run this compound command:&lt;br /&gt;
```bash&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Cleanup: removing temp file&amp;quot;; rm -f /tmp/lab6_test.txt&amp;#039; EXIT; \&lt;br /&gt;
touch /tmp/lab6_test.txt; \&lt;br /&gt;
echo &amp;quot;File created. Press Ctrl+C or wait...&amp;quot;; \&lt;br /&gt;
sleep 10; \&lt;br /&gt;
echo &amp;quot;Normal exit&amp;quot;&lt;br /&gt;
```&lt;br /&gt;
4. Try both: let it complete normally, then run it again and interrupt with Ctrl+C. Observe cleanup happens both times.&lt;br /&gt;
5. Start a background sleep: `sleep 100 &amp;amp;`. Note the PID displayed.&lt;br /&gt;
6. Send SIGTERM to it: `kill -TERM &amp;lt;pid&amp;gt;` (replace `&amp;lt;pid&amp;gt;` with the actual PID). Verify it terminated: `jobs`.&lt;br /&gt;
7. Start another background process that will ignore SIGTERM:&lt;br /&gt;
```bash&lt;br /&gt;
(trap &amp;#039;echo &amp;quot;Caught SIGTERM, staying alive&amp;quot;&amp;#039; TERM; \&lt;br /&gt;
 while true; do echo &amp;quot;Running...&amp;quot;; sleep 5; done) &amp;amp;&lt;br /&gt;
```&lt;br /&gt;
8. Note the PID, then send SIGTERM: `kill -TERM &amp;lt;pid&amp;gt;`. Observe the trap message.&lt;br /&gt;
9. Send SIGKILL to force termination: `kill -9 &amp;lt;pid&amp;gt;`. This cannot be caught.&lt;br /&gt;
10. Verify all background jobs are gone: `jobs`.&lt;br /&gt;
&lt;br /&gt;
**Deliverable D:** Provide output showing: the trapped SIGINT message, cleanup on both normal exit and Ctrl+C, the SIGTERM being caught with the trap message, and SIGKILL forcing termination.&lt;br /&gt;
&lt;br /&gt;
### Exercise E: Socket Communication Basics&lt;br /&gt;
&lt;br /&gt;
This exercise provides a brief introduction to socket communication using existing tools.&lt;br /&gt;
&lt;br /&gt;
**Steps:**&lt;br /&gt;
&lt;br /&gt;
1. Create a simple static site directory: `mkdir -p /tmp/lab6_site &amp;amp;&amp;amp; echo &amp;quot;&amp;lt;h1&amp;gt;Hello from Caddy&amp;lt;/h1&amp;gt;&amp;quot; &amp;gt; /tmp/lab6_site/index.html`.&lt;br /&gt;
2. Start Caddy as a file server listening on a Unix domain socket: `caddy file-server --root /tmp/lab6_site --listen unix//tmp/caddy.sock &amp;amp;`. Note the PID.&lt;br /&gt;
3. Wait a moment for Caddy to start. Verify the socket exists: `ls -l /tmp/caddy.sock`. Note the `s` type indicating a socket.&lt;br /&gt;
4. Use `curl` to make an HTTP request via the Unix domain socket: `curl -v --unix-socket /tmp/caddy.sock http://localhost/`.&lt;br /&gt;
5. Observe the response. Note that the communication is bidirectional: you sent an HTTP request, and the server responded with the HTML content.&lt;br /&gt;
6. (Optional) Open multiple terminals and run `curl` simultaneously several times. Each request is handled independently, demonstrating the one-to-many capability of sockets.&lt;br /&gt;
7. Stop Caddy by sending `SIGTERM`: `kill -TERM &amp;lt;caddy_pid&amp;gt;` or use `pkill caddy`.&lt;br /&gt;
8. Clean up: `rm -rf /tmp/lab6_site /tmp/caddy.sock`.&lt;br /&gt;
&lt;br /&gt;
**Deliverable E:** Provide the `ls -l` output showing the socket file, the `curl` output demonstrating the request and response (especially the HTTP headers and HTML body), and a brief explanation (2-3 sentences) of how this differs from a named pipe in terms of directionality and connection multiplexing.&lt;br /&gt;
&lt;br /&gt;
## Scripting Challenges&lt;br /&gt;
&lt;br /&gt;
### Challenge 1: Log Aggregator with Named Pipes&lt;br /&gt;
&lt;br /&gt;
Write a script `/tmp/lab6_log_aggregator.sh` that aggregates log messages from multiple sources using named pipes.&lt;br /&gt;
&lt;br /&gt;
**Requirements:**&lt;br /&gt;
&lt;br /&gt;
- Create three named pipes: `/tmp/log_fifo1`, `/tmp/log_fifo2`, `/tmp/log_fifo3`.&lt;br /&gt;
- Start three background processes that each write timestamped messages to one of the FIFOs (simulating different log sources). Each should write 5 messages at 1-second intervals.&lt;br /&gt;
- Implement a main loop that reads from all three FIFOs simultaneously (hint: use `select` via `read` with timeout, or use multiple background readers).&lt;br /&gt;
- Write all received messages to a combined log file `/tmp/aggregated.log` with timestamps.&lt;br /&gt;
- Trap `SIGTERM` to clean up: kill background processes, remove FIFOs, and exit gracefully.&lt;br /&gt;
- Use: `set -euo pipefail`, functions, `trap`, `mkfifo`, background processes (`&amp;amp;`), proper cleanup.&lt;br /&gt;
&lt;br /&gt;
**Test:**&lt;br /&gt;
```bash&lt;br /&gt;
chmod +x /tmp/lab6_log_aggregator.sh&lt;br /&gt;
/tmp/lab6_log_aggregator.sh &amp;amp;&lt;br /&gt;
AGG_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -TERM $AGG_PID&lt;br /&gt;
wait&lt;br /&gt;
cat /tmp/aggregated.log&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Deliverable Challenge 1:**&lt;br /&gt;
- Complete script with comments&lt;br /&gt;
- Contents of `/tmp/aggregated.log` showing interleaved messages from all three sources&lt;br /&gt;
- Brief explanation (2-3 sentences) of why named pipes were necessary here instead of anonymous pipes&lt;br /&gt;
&lt;br /&gt;
### Challenge 2: Graceful Service Controller&lt;br /&gt;
&lt;br /&gt;
Write a script `/tmp/lab6_service_controller.sh` that manages a long-running service and responds to signals.&lt;br /&gt;
&lt;br /&gt;
**Requirements:**&lt;br /&gt;
&lt;br /&gt;
- The script acts as a daemon that runs indefinitely, printing a heartbeat message every 5 seconds.&lt;br /&gt;
- Accept one optional argument: a &amp;quot;config file&amp;quot; path. If provided, read a setting (e.g., `INTERVAL=10`) from the file to control the heartbeat interval.&lt;br /&gt;
- Trap `SIGHUP`: Reload the configuration file and adjust the interval dynamically without restarting the script. Print &amp;quot;Configuration reloaded.&amp;quot;&lt;br /&gt;
- Trap `SIGTERM`: Perform graceful shutdown. Print &amp;quot;Shutting down gracefully...&amp;quot;, wait 2 seconds, then exit cleanly.&lt;br /&gt;
- Trap `SIGUSR1`: Export the current status to `/tmp/service_status.txt` (e.g., uptime, number of heartbeats sent, current interval). Print &amp;quot;Status exported.&amp;quot;&lt;br /&gt;
- Trap `EXIT`: Perform cleanup. Print &amp;quot;Service stopped.&amp;quot;&lt;br /&gt;
- Maintain internal state: count the number of heartbeats sent, track start time.&lt;br /&gt;
- Use: `set -euo pipefail`, functions, `trap` for multiple signals, variables for state, a loop, cleanup handler.&lt;br /&gt;
&lt;br /&gt;
**Test:**&lt;br /&gt;
```bash&lt;br /&gt;
echo &amp;quot;INTERVAL=3&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
chmod +x /tmp/lab6_service_controller.sh&lt;br /&gt;
/tmp/lab6_service_controller.sh /tmp/service.conf &amp;amp;&lt;br /&gt;
SVC_PID=$!&lt;br /&gt;
sleep 10&lt;br /&gt;
kill -USR1 $SVC_PID  # Export status&lt;br /&gt;
cat /tmp/service_status.txt&lt;br /&gt;
echo &amp;quot;INTERVAL=1&amp;quot; &amp;gt; /tmp/service.conf&lt;br /&gt;
kill -HUP $SVC_PID  # Reload config&lt;br /&gt;
sleep 5&lt;br /&gt;
kill -TERM $SVC_PID  # Graceful shutdown&lt;br /&gt;
wait&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Deliverable Challenge 2:**&lt;br /&gt;
- Complete script with comments&lt;br /&gt;
- Output showing heartbeats before and after `SIGHUP` (with visible interval change)&lt;br /&gt;
- Contents of `/tmp/service_status.txt` after `SIGUSR1`&lt;br /&gt;
- Output showing graceful shutdown message after `SIGTERM`&lt;br /&gt;
&lt;br /&gt;
## Reference: Common IPC Patterns&lt;br /&gt;
&lt;br /&gt;
Quick reference for IPC mechanisms covered in this lab:&lt;br /&gt;
&lt;br /&gt;
**Environment Variables:**&lt;br /&gt;
```bash&lt;br /&gt;
# Export a variable for child processes&lt;br /&gt;
export VAR=&amp;quot;value&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Set for one command only&lt;br /&gt;
VAR=&amp;quot;value&amp;quot; command&lt;br /&gt;
&lt;br /&gt;
# View all environment variables&lt;br /&gt;
env&lt;br /&gt;
printenv&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Anonymous Pipes:**&lt;br /&gt;
```bash&lt;br /&gt;
# Simple pipeline&lt;br /&gt;
command1 | command2&lt;br /&gt;
&lt;br /&gt;
# Multi-stage pipeline&lt;br /&gt;
cat file.txt | grep &amp;quot;pattern&amp;quot; | sort | uniq&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Named Pipes:**&lt;br /&gt;
```bash&lt;br /&gt;
# Create a FIFO&lt;br /&gt;
mkfifo /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Write to FIFO (blocks until reader connects)&lt;br /&gt;
echo &amp;quot;data&amp;quot; &amp;gt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Read from FIFO (blocks until writer connects)&lt;br /&gt;
cat &amp;lt; /path/to/fifo&lt;br /&gt;
&lt;br /&gt;
# Remove FIFO&lt;br /&gt;
rm /path/to/fifo&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Signals:**&lt;br /&gt;
```bash&lt;br /&gt;
# Send signal by name&lt;br /&gt;
kill -TERM &amp;lt;pid&amp;gt;&lt;br /&gt;
kill -HUP &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Send signal by number&lt;br /&gt;
kill -15 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Force kill (cannot be caught)&lt;br /&gt;
kill -9 &amp;lt;pid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Trap signals in a script&lt;br /&gt;
trap &amp;#039;echo &amp;quot;Caught signal&amp;quot;&amp;#039; INT TERM&lt;br /&gt;
trap cleanup EXIT&lt;br /&gt;
&lt;br /&gt;
# Define cleanup function&lt;br /&gt;
cleanup() {&lt;br /&gt;
    echo &amp;quot;Cleaning up...&amp;quot;&lt;br /&gt;
    rm -f /tmp/myfiles.*&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**Sockets (using existing tools):**&lt;br /&gt;
```bash&lt;br /&gt;
# Start a server on Unix domain socket (using socat)&lt;br /&gt;
socat UNIX-LISTEN:/tmp/service.sock,fork EXEC:&amp;#039;/usr/bin/myhandler&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Connect as client (using socat)&lt;br /&gt;
echo &amp;quot;request&amp;quot; | socat - UNIX-CONNECT:/tmp/service.sock&lt;br /&gt;
&lt;br /&gt;
# HTTP via Unix socket (using curl)&lt;br /&gt;
curl --unix-socket /tmp/service.sock http://localhost/path&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
## Common Patterns Table&lt;br /&gt;
&lt;br /&gt;
| Mechanism | Direction | Persistence | Use Case |&lt;br /&gt;
|-----------|-----------|-------------|----------|&lt;br /&gt;
| Environment Variables | Parent → Child (one-way) | Inherited at fork | Configuration, credentials |&lt;br /&gt;
| Pipes (`\|`) | One-way | Ephemeral (process lifetime) | Command chaining, streaming |&lt;br /&gt;
| Named Pipes (FIFO) | One-way | Filesystem entry persists | Unrelated process communication |&lt;br /&gt;
| Signals | One-way (notification) | Asynchronous event | Process control, event notification |&lt;br /&gt;
| Sockets | Bidirectional | Filesystem entry persists (Unix domain) | Client-server, network services |&lt;br /&gt;
&lt;br /&gt;
## Deliverables and Assessment&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing:&lt;br /&gt;
&lt;br /&gt;
**Exercise Deliverables:**&lt;br /&gt;
- Exercise A: Outputs demonstrating export behavior, parent-child isolation, and single-command environment variables&lt;br /&gt;
- Exercise B: Pipeline outputs and complete analysis script&lt;br /&gt;
- Exercise C: FIFO creation output, producer/consumer scripts with sample output&lt;br /&gt;
- Exercise D: Complete daemon simulation script with signal handling demonstrations&lt;br /&gt;
- Exercise E: Socket file listing, curl output with HTTP headers, explanation of socket vs FIFO differences&lt;br /&gt;
&lt;br /&gt;
**Challenge Deliverables:**&lt;br /&gt;
- Challenge 1: Complete log aggregator script, aggregated log contents, explanation&lt;br /&gt;
- Challenge 2: Complete service controller script, outputs demonstrating all signal handlers (SIGHUP reload, SIGUSR1 status export, SIGTERM shutdown)&lt;br /&gt;
&lt;br /&gt;
**Additional:**&lt;br /&gt;
- Each deliverable should include command outputs (screenshots or text) and brief explanations where requested.&lt;br /&gt;
- For scripts, include the complete, commented source code and example execution output.&lt;br /&gt;
&lt;br /&gt;
## Additional Resources&lt;br /&gt;
&lt;br /&gt;
This lab covers the fundamental IPC mechanisms accessible from bash. You&amp;#039;ve learned how processes inherit environment variables, communicate via pipes, respond to signals, and use sockets for network communication. These concepts form the foundation for system administration, scripting, and understanding how complex systems coordinate.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
- Advanced IPC: Shared memory, message queues, semaphores (typically used in C/C++ programs, not bash)&lt;br /&gt;
- Network programming: TCP/UDP sockets, client-server architecture&lt;br /&gt;
- D-Bus: A modern IPC system used by desktop environments and systemd&lt;br /&gt;
- Signal safety: Writing robust signal handlers (critical in C, less relevant in bash)&lt;br /&gt;
&lt;br /&gt;
Relevant manual pages:&lt;br /&gt;
- `man 7 pipe` - Pipe overview&lt;br /&gt;
- `man 7 fifo` - Named pipe overview  &lt;br /&gt;
- `man 7 signal` - Signal overview&lt;br /&gt;
- `man 7 unix` - Unix domain sockets&lt;br /&gt;
- `man bash` - Section on trap and job control&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8173</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8173"/>
		<updated>2025-11-14T15:02:14Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;br /&gt;
* Lab 6 - [[OS Lab 6 - Inter Process Communication]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8172</id>
		<title>OS Lab 5 - Bash Scripting</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8172"/>
		<updated>2025-10-31T14:58:04Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel executes scripts via the shebang mechanism and how bash interprets script contents.&lt;br /&gt;
* Distinguish between shell builtins and external programs, understanding why certain commands must be builtins.&lt;br /&gt;
* Write scripts using proper variable quoting, recognizing that commands are fundamentally lists of strings.&lt;br /&gt;
* Use exit status to control script flow with conditionals and logical operators.&lt;br /&gt;
* Redirect file descriptors to control input and output streams.&lt;br /&gt;
* Implement loops with &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;, incorporating command substitution.&lt;br /&gt;
* Define functions with proper variable scoping and work with arrays.&lt;br /&gt;
* Apply defensive scripting practices using &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In previous labs, we explored processes, job control, and the permission model. These concepts form the foundation for understanding how programs execute and interact with the system. We now turn to bash scripting, which allows us to automate tasks and build tools without writing compiled programs.&lt;br /&gt;
&lt;br /&gt;
This lab emphasizes underlying mechanisms rather than syntax alone. We will examine how the kernel interprets shebangs, why certain commands must be shell builtins, and how bash&amp;#039;s command model treats everything as lists of strings. Understanding these foundations prepares you for the next lab, where we&amp;#039;ll explore signals and inter-process communication using bash scripts rather than C code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
All necessary utilities (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;) are pre-installed. No additional packages are required.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Basic command-line navigation and file operations&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, exit status)&lt;br /&gt;
* File permissions and the execute bit from Lab 4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Bash Scripting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Shebangs and Script Execution ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
When you execute a file like &amp;lt;code&amp;gt;./script.sh&amp;lt;/code&amp;gt;, the kernel first checks the execute permission bit. If set, the kernel examines the file&amp;#039;s first bytes. If they are &amp;lt;code&amp;gt;#!&amp;lt;/code&amp;gt; (the &amp;amp;quot;shebang&amp;amp;quot;), the kernel reads the rest of that line as a path to an interpreter and re-executes the file using that interpreter.&lt;br /&gt;
&lt;br /&gt;
For example, a script beginning with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt; causes the kernel to execute &amp;lt;code&amp;gt;/bin/bash ./script.sh&amp;lt;/code&amp;gt;. This mechanism is general-purpose: the kernel doesn&amp;#039;t care whether the interpreter is bash, Python, or any other program.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements for script execution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Execute bit must be set (&amp;lt;code&amp;gt;chmod +x script.sh&amp;lt;/code&amp;gt;)&lt;br /&gt;
* File must begin with a valid shebang pointing to an interpreter&lt;br /&gt;
* The interpreter itself must be an executable binary&lt;br /&gt;
&lt;br /&gt;
Without the execute bit, the kernel&amp;#039;s permission check fails. Without a shebang, the kernel cannot determine which interpreter to use, and behavior becomes system-dependent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shells-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Shell&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
Once the kernel launches bash with your script as an argument, bash reads the script line by line, parsing each line according to its syntax rules. This involves expanding variables, performing word splitting, executing commands, and capturing results.&lt;br /&gt;
&lt;br /&gt;
The fundamental principle: &amp;#039;&amp;#039;&amp;#039;every command is a list of strings&amp;#039;&amp;#039;&amp;#039;. When bash executes &amp;lt;code&amp;gt;echo Hello World&amp;lt;/code&amp;gt;, it constructs a list of three strings: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;. The first string identifies the command; remaining strings are arguments. This model applies universally to builtins, external programs, and functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;builtins-vs-external-programs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Builtins vs External Programs ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;external-programs-and-path&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== External Programs and PATH ====&lt;br /&gt;
&lt;br /&gt;
When bash encounters a command that isn&amp;#039;t a builtin, it searches the directories in the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable for an executable with that name. When found, bash forks a child process and executes the program. Commands like &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt; run in separate processes with their own address space.&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;which ls&lt;br /&gt;
# Output: /usr/bin/ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;shell-builtins&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Shell Builtins ====&lt;br /&gt;
&lt;br /&gt;
Builtins are commands built directly into bash. Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;type cd&lt;br /&gt;
# Output: cd is a shell builtin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Builtins exist for two reasons:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Process State Modification&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Some commands must modify the shell&amp;#039;s own process state. Consider &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;: if it were an external program, it would run in a child process, change that child&amp;#039;s directory, then exit. The parent shell&amp;#039;s directory would remain unchanged because child process state doesn&amp;#039;t propagate to parents. For &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; to work, it must execute within the shell&amp;#039;s own process, making it a necessary builtin.&lt;br /&gt;
&lt;br /&gt;
Other state-modifying builtins include &amp;lt;code&amp;gt;ulimit&amp;lt;/code&amp;gt; (resource limits) and &amp;lt;code&amp;gt;umask&amp;lt;/code&amp;gt; (permission mask). In the next lab, we&amp;#039;ll see &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; (environment variables) and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; (signal handlers).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Performance&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Commands like &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; are builtins for performance, avoiding process creation overhead. They could theoretically be external programs, but making them builtins is faster.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-your-first-script-and-the-shebang&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Your First Script and the Shebang ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates the kernel&amp;#039;s shebang mechanism and the importance of the execute bit.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a script &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt; containing:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
echo &amp;quot;Hello from bash!&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Check the file&amp;#039;s permissions with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;. Note that the execute bit is not set.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Attempt to execute the script directly: &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. This should fail with &amp;amp;quot;Permission denied&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Make the script executable with &amp;lt;code&amp;gt;chmod +x&amp;lt;/code&amp;gt; and verify with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script directly. It should now succeed.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script by passing it to bash: &amp;lt;code&amp;gt;bash /tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. Observe that this works even without the execute bit.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a second script without a shebang, make it executable, and attempt to run it. Observe the unpredictable behavior.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Clean up both test scripts.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output before and after setting the execute bit, the &amp;amp;quot;Permission denied&amp;amp;quot; error, and the successful execution output.&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-builtins-vs-external-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Builtins vs External Commands ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why certain commands must be builtins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify whether &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; are builtins or external commands.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs. Try &amp;lt;code&amp;gt;which cd&amp;lt;/code&amp;gt; and note that it doesn&amp;#039;t find the builtin.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_cd_test.sh&amp;lt;/code&amp;gt; that prints the current directory, changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, and prints the directory again.&lt;br /&gt;
# Make the script executable and run it. Observe that the script successfully changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check your shell&amp;#039;s current directory with &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;. Note that your shell is still in the original directory.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; directly in your shell to confirm it works when run as a builtin.&lt;br /&gt;
# Clean up the test script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; command outputs, the script output followed by your shell&amp;#039;s &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;variables-and-quoting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Variables and Quoting ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-list-of-strings-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The List-of-Strings Model ====&lt;br /&gt;
&lt;br /&gt;
Variables in bash are expanded before commands execute. How they&amp;#039;re expanded determines the final command&amp;#039;s argument list.&lt;br /&gt;
&lt;br /&gt;
Consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greeting=&amp;quot;Hello World&amp;quot;&lt;br /&gt;
echo $greeting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash expands &amp;lt;code&amp;gt;$greeting&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;amp;quot;Hello World&amp;amp;quot;&amp;lt;/code&amp;gt;, then performs &amp;#039;&amp;#039;&amp;#039;word splitting&amp;#039;&amp;#039;&amp;#039;, breaking it into separate strings at whitespace. The result: three strings passed to echo: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This works fine for echo, but consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;filename=&amp;quot;my document.txt&amp;quot;&lt;br /&gt;
ls $filename&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;my&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; command receives two arguments and tries to list two separate files, which fails.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;quoting-fixes-this&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Quoting Fixes This ====&lt;br /&gt;
&lt;br /&gt;
Double quotes prevent word splitting:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; receives one argument: &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The expansion happens, but the result stays as a single string.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Single quotes&amp;#039;&amp;#039;&amp;#039; prevent all expansion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;#039;$name&amp;#039;    # Outputs: $name (literal)&lt;br /&gt;
echo &amp;quot;$name&amp;quot;    # Outputs: the value of $name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Always quote variables unless you specifically need word splitting. Write &amp;lt;code&amp;gt;&amp;amp;quot;$variable&amp;amp;quot;&amp;lt;/code&amp;gt; not &amp;lt;code&amp;gt;$variable&amp;lt;/code&amp;gt;. This makes scripts robust when variables contain spaces or special characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-quoting-and-word-splitting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Quoting and Word Splitting ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why quoting variables is essential.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a file named &amp;lt;code&amp;gt;/tmp/my test file.txt&amp;lt;/code&amp;gt; (with spaces).&lt;br /&gt;
# Store the filename in a variable and try to list it without quotes: &amp;lt;code&amp;gt;ls $myfile&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Try again with proper quoting: &amp;lt;code&amp;gt;ls &amp;amp;quot;$myfile&amp;amp;quot;&amp;lt;/code&amp;gt;. This succeeds.&lt;br /&gt;
# Test echo with a variable containing multiple spaces, both with and without quotes.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_quote_test.sh&amp;lt;/code&amp;gt; that demonstrates variable quoting and array expansion both correctly and incorrectly. The script should show:&lt;br /&gt;
#* A variable with spaces, echoed with and without quotes&lt;br /&gt;
#* An array containing an element with spaces, iterated both ways&lt;br /&gt;
# Make the script executable and run it. Observe how the &amp;amp;quot;wrong way&amp;amp;quot; processes four items instead of three.&lt;br /&gt;
# Clean up the test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the error from unquoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, the success from quoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
Every command returns an exit status: a number from 0 to 255. By convention, 0 means success; non-zero means failure. Bash stores the last command&amp;#039;s exit status in &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /tmp&lt;br /&gt;
echo $?    # Outputs: 0 (success)&lt;br /&gt;
&lt;br /&gt;
ls /nonexistent&lt;br /&gt;
echo $?    # Outputs: non-zero (failure)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This exit status is bash&amp;#039;s fundamental true/false mechanism. An &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statement executes a command and checks if it succeeded:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if grep -q &amp;quot;ERROR&amp;quot; /var/log/syslog; then&lt;br /&gt;
    echo &amp;quot;Errors found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; finds &amp;amp;quot;ERROR&amp;amp;quot;, it returns 0, and the &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; block executes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the--command&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; Command ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; builtin performs tests and returns an exit status. Despite unusual syntax, it&amp;#039;s conceptually just another command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;[[ -f &amp;quot;file.txt&amp;quot; ]]    # Returns 0 if file exists, 1 otherwise&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common operators:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-f file&amp;lt;/code&amp;gt;: True if regular file exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-d dir&amp;lt;/code&amp;gt;: True if directory exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-e path&amp;lt;/code&amp;gt;: True if path exists&lt;br /&gt;
* &amp;lt;code&amp;gt;string1 == string2&amp;lt;/code&amp;gt;: True if strings equal&lt;br /&gt;
* &amp;lt;code&amp;gt;num1 -lt num2&amp;lt;/code&amp;gt;: True if num1 less than num2&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;config.txt&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Config file found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;logical-operators&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Logical Operators ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; (AND):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous succeeded (returned 0):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir /tmp/newdir &amp;amp;&amp;amp; cd /tmp/newdir&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; (OR):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous failed (returned non-zero):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd /tmp/important || echo &amp;quot;Failed to change directory&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explicit error ignoring:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/maybe-exists.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern documents that you&amp;#039;re intentionally ignoring potential errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores exit status and conditional logic.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /tmp&amp;lt;/code&amp;gt; and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be 0 (success).&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /nonexistent&amp;lt;/code&amp;gt; (redirect stderr to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;) and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be non-zero.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;[[ -f /etc/passwd ]]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;[[ -f /nonexistent ]]&amp;lt;/code&amp;gt;, checking &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt; after each.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_conditionals.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* File existence tests with &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;&lt;br /&gt;
#* The &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; operator for success chaining&lt;br /&gt;
#* The &amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; operator for failure handling&lt;br /&gt;
#* The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern for explicit error ignoring&lt;br /&gt;
#* Using command exit status directly in &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; (e.g., with &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;)&lt;br /&gt;
#* Numeric and string comparisons with &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it. Observe the output from each test.&lt;br /&gt;
# Clean up the script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide exit status values from steps 1-3, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;output-redirection-and-file-descriptors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Output Redirection and File Descriptors ===&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 3 that every process has file descriptors (FDs):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 0:&amp;#039;&amp;#039;&amp;#039; Standard input (stdin)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 1:&amp;#039;&amp;#039;&amp;#039; Standard output (stdout)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 2:&amp;#039;&amp;#039;&amp;#039; Standard error (stderr)&lt;br /&gt;
&lt;br /&gt;
Redirection allows you to control where these streams go.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdout to a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;output&amp;quot; &amp;gt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect stderr:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /nonexistent 2&amp;gt; error.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect both:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;amp;&amp;gt; combined.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or more explicitly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;gt; output.txt 2&amp;gt;&amp;amp;1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Order matters! &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; must come after &amp;lt;code&amp;gt;&amp;amp;gt; output.txt&amp;lt;/code&amp;gt; to redirect stderr to where stdout is pointing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes ====&lt;br /&gt;
&lt;br /&gt;
The pipe operator &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; connects one command&amp;#039;s stdout to another&amp;#039;s stdin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /var/log/syslog | grep &amp;quot;ERROR&amp;quot; | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Data flows left to right. The kernel creates an anonymous pipe, connecting the write end to the first command&amp;#039;s stdout and the read end to the second command&amp;#039;s stdin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;input-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Input Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdin from a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This differs from &amp;lt;code&amp;gt;wc -l file.txt&amp;lt;/code&amp;gt;: with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;, bash opens the file and connects it to stdin before running &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;loops&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Loops ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-for-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Iterates over a list of values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for item in value1 value2 value3; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common sources for the list:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filename expansion (globbing):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;Number $i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-while-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Executes while a command succeeds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;counter=1&lt;br /&gt;
while [[ $counter -le 5 ]]; do&lt;br /&gt;
    echo &amp;quot;Iteration $counter&amp;quot;&lt;br /&gt;
    counter=$((counter + 1))&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Reading files line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;Line: $line&amp;quot;&lt;br /&gt;
done &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; command returns 0 while reading lines successfully, and non-zero at end-of-file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-file-descriptor-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: File Descriptor Redirection ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores redirection, connecting to Lab 3&amp;#039;s file descriptor concepts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Redirect stdout to a file with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;, then display the file contents.&lt;br /&gt;
# Redirect stderr to a file with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt; by trying to list a nonexistent directory.&lt;br /&gt;
# Redirect both stdout and stderr to the same file using &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate the &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; pattern with explicit redirection order.&lt;br /&gt;
# Use a pipe to chain &amp;lt;code&amp;gt;cat /etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep &amp;amp;quot;root&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt; to extract the username.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_redirect.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* Output redirection with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Stderr capture with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Combined stream redirection with &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* A pipeline example&lt;br /&gt;
#* Input redirection with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it.&lt;br /&gt;
# Clean up all test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the captured stderr contents and complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;command-substitution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Command Substitution ===&lt;br /&gt;
&lt;br /&gt;
Command substitution captures a command&amp;#039;s stdout for use elsewhere:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;current_date=$(date +%Y-%m-%d)&lt;br /&gt;
echo &amp;quot;Today is $current_date&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash executes the command in &amp;lt;code&amp;gt;$()&amp;lt;/code&amp;gt;, captures its output, and substitutes it in place.&lt;br /&gt;
&lt;br /&gt;
Common in loops:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;$i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt; output (numbers 1-10, one per line) is captured, word-split, and becomes the loop&amp;#039;s value list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;functions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Functions ===&lt;br /&gt;
&lt;br /&gt;
Functions group commands for reuse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greet() {&lt;br /&gt;
    echo &amp;quot;Hello, $1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
greet &amp;quot;Alice&amp;quot;    # Outputs: Hello, Alice&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arguments:&amp;#039;&amp;#039;&amp;#039; Access via &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$2&amp;lt;/code&amp;gt;, etc. &amp;lt;code&amp;gt;$@&amp;lt;/code&amp;gt; expands to all arguments; &amp;lt;code&amp;gt;$#&amp;lt;/code&amp;gt; gives the count.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return values:&amp;#039;&amp;#039;&amp;#039; Functions return exit status (0-255) via &amp;lt;code&amp;gt;return&amp;lt;/code&amp;gt;. For other values, write to stdout and capture with command substitution:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;get_uppercase() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | tr &amp;#039;[:lower:]&amp;#039; &amp;#039;[:upper:]&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
result=$(get_uppercase &amp;quot;hello&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;variable-scope&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Variable Scope ====&lt;br /&gt;
&lt;br /&gt;
By default, variables are global. Use &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; for function-local variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;calculate() {&lt;br /&gt;
    local temp=$1&lt;br /&gt;
    local result=$((temp * 2))&lt;br /&gt;
    echo &amp;quot;$result&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Without &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, these variables would affect the global scope.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;arrays&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
Arrays are collections of strings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;files=(&amp;quot;file1.txt&amp;quot; &amp;quot;file 2.txt&amp;quot; &amp;quot;file3.txt&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access elements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;${files[0]}&amp;quot;           # First element&lt;br /&gt;
echo &amp;quot;${files[@]}&amp;quot;           # All elements&lt;br /&gt;
echo &amp;quot;${#files[@]}&amp;quot;          # Number of elements&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical syntax:&amp;#039;&amp;#039;&amp;#039; Use &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt; (with quotes) to preserve each element as a separate string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in &amp;quot;${files[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This correctly processes three files, including &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; as one item.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without quotes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in ${files[@]}; do    # WRONG&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;file&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;2.txt&amp;amp;quot;&amp;lt;/code&amp;gt;, processing four items instead of three.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;defensive-scripting-set--euo-pipefail&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Defensive Scripting: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Bash scripts, by default, continue after errors. This can cause silent failures that are hard to debug. These options make scripts safer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--e-exit-on-error&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -e&amp;lt;/code&amp;gt; (Exit on Error) ====&lt;br /&gt;
&lt;br /&gt;
Exit immediately if any command returns non-zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -e&lt;br /&gt;
&lt;br /&gt;
cp important.txt backup.txt&lt;br /&gt;
process backup.txt    # Won&amp;#039;t run if cp failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
To allow specific failures:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/file.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--u-error-on-undefined-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -u&amp;lt;/code&amp;gt; (Error on Undefined Variables) ====&lt;br /&gt;
&lt;br /&gt;
Treat undefined variables as errors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -u&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;$undefined_var&amp;quot;    # Error: undefined_var: unbound variable&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This catches typos and logic errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--o-pipefail-pipeline-failure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -o pipefail&amp;lt;/code&amp;gt; (Pipeline Failure) ====&lt;br /&gt;
&lt;br /&gt;
Normally, a pipeline&amp;#039;s exit status is the last command&amp;#039;s status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns 0 (head succeeded) even though grep failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With &amp;lt;code&amp;gt;pipefail&amp;lt;/code&amp;gt;, the pipeline fails if any command fails:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -o pipefail&lt;br /&gt;
&lt;br /&gt;
grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns non-zero (grep failed)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;recommended-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Recommended Practice ====&lt;br /&gt;
&lt;br /&gt;
Start every script with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
These options prevent silent errors and make debugging easier.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-file-analyzer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log File Analyzer ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;/tmp/lab5_log_analyzer.sh&amp;lt;/code&amp;gt; that analyzes a log file.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept one argument: log file path&lt;br /&gt;
* Print usage message if wrong number of arguments&lt;br /&gt;
* Print error if file doesn&amp;#039;t exist or isn&amp;#039;t readable&lt;br /&gt;
* Count lines containing &amp;amp;quot;ERROR&amp;amp;quot;, &amp;amp;quot;WARN&amp;amp;quot;, and &amp;amp;quot;INFO&amp;amp;quot;&lt;br /&gt;
* Print summary report&lt;br /&gt;
* Use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, proper quoting, &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statements, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, counting method&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab5_test.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 INFO User login successful&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout, retrying&lt;br /&gt;
2024-01-15 10:15:00 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:15:30 INFO Connection restored&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration parameter&lt;br /&gt;
2024-01-15 10:25:00 WARN Disk space low&lt;br /&gt;
2024-01-15 10:30:00 INFO Backup completed successfully&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Log Analysis Report for /tmp/lab5_test.log&lt;br /&gt;
==========================================&lt;br /&gt;
ERROR: 2&lt;br /&gt;
WARN:  2&lt;br /&gt;
INFO:  4&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /tmp/lab5_test.log&lt;br /&gt;
/tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /nonexistent.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script with comments&lt;br /&gt;
* Output from test log&lt;br /&gt;
* Output with no arguments (usage)&lt;br /&gt;
* Output with nonexistent file (error)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-batch-file-processor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Batch File Processor ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;lab5_batch_processor.sh&amp;lt;/code&amp;gt; that processes multiple files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept two arguments: directory path and operation (&amp;amp;quot;count&amp;amp;quot;, &amp;amp;quot;uppercase&amp;amp;quot;, or &amp;amp;quot;list&amp;amp;quot;)&lt;br /&gt;
* Print usage if wrong arguments or invalid operation&lt;br /&gt;
* Define three functions (one per operation):&lt;br /&gt;
** &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: Count lines in each &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
** &amp;lt;code&amp;gt;uppercase&amp;lt;/code&amp;gt;: Create &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; files with uppercase content&lt;br /&gt;
** &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;: List &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; files with sizes&lt;br /&gt;
* Store files in array, use loop to process&lt;br /&gt;
* Must use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions with &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, array with &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt;, loop, case/if for operation selection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p /tmp/lab5_batch_test&lt;br /&gt;
echo -e &amp;quot;line 1\nline 2\nline 3&amp;quot; &amp;gt; /tmp/lab5_batch_test/file1.txt&lt;br /&gt;
echo -e &amp;quot;hello world\ntest file&amp;quot; &amp;gt; /tmp/lab5_batch_test/file2.txt&lt;br /&gt;
echo -e &amp;quot;single line&amp;quot; &amp;gt; /tmp/lab5_batch_test/file3.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output for &amp;amp;quot;count&amp;amp;quot;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Processing files in /tmp/lab5_batch_test&lt;br /&gt;
Operation: count&lt;br /&gt;
========================================&lt;br /&gt;
file1.txt: 3 lines&lt;br /&gt;
file2.txt: 2 lines&lt;br /&gt;
file3.txt: 1 lines&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab5_batch_processor.sh&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test count&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test uppercase&lt;br /&gt;
cat /tmp/lab5_batch_test/file1.upper.txt&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script&lt;br /&gt;
* Output from &amp;amp;quot;count&amp;amp;quot; operation&lt;br /&gt;
* Output from &amp;amp;quot;uppercase&amp;amp;quot; operation + contents of one &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
* Output from &amp;amp;quot;list&amp;amp;quot; operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for common bash patterns:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check arguments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ $# -lt 1 ]]; then&lt;br /&gt;
    echo &amp;quot;Usage: $0 &amp;lt;arg&amp;gt;&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test file types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Regular file&amp;quot;&lt;br /&gt;
elif [[ -d &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Directory&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read file line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;$line&amp;quot;&lt;br /&gt;
done &amp;lt; &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Iterate over files:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    [[ -f &amp;quot;$file&amp;quot; ]] || continue&lt;br /&gt;
    echo &amp;quot;$file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;date=$(date +%Y-%m-%d)&lt;br /&gt;
count=$(wc -l &amp;lt; &amp;quot;$file&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arrays:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arr=(&amp;quot;item1&amp;quot; &amp;quot;item 2&amp;quot; &amp;quot;item3&amp;quot;)&lt;br /&gt;
for item in &amp;quot;${arr[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;process() {&lt;br /&gt;
    local file=&amp;quot;$1&amp;quot;&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&amp;#039;s a table of common commands for file usage and text manipulation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-commands-reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Common Commands Reference ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;file-operations&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== File Operations ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;&lt;br /&gt;
| List directory contents&lt;br /&gt;
| &amp;lt;code&amp;gt;ls -la /tmp&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;&lt;br /&gt;
| Change directory&lt;br /&gt;
| &amp;lt;code&amp;gt;cd /home/user&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;&lt;br /&gt;
| Print working directory&lt;br /&gt;
| &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Copy files/directories&lt;br /&gt;
| &amp;lt;code&amp;gt;cp file.txt backup.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;&lt;br /&gt;
| Move/rename files&lt;br /&gt;
| &amp;lt;code&amp;gt;mv old.txt new.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt;&lt;br /&gt;
| Remove files&lt;br /&gt;
| &amp;lt;code&amp;gt;rm file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;&lt;br /&gt;
| Create directory&lt;br /&gt;
| &amp;lt;code&amp;gt;mkdir -p /path/to/dir&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;rmdir&amp;lt;/code&amp;gt;&lt;br /&gt;
| Remove empty directory&lt;br /&gt;
| &amp;lt;code&amp;gt;rmdir olddir&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;touch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Create empty file or update timestamp&lt;br /&gt;
| &amp;lt;code&amp;gt;touch newfile.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ln&amp;lt;/code&amp;gt;&lt;br /&gt;
| Create links&lt;br /&gt;
| &amp;lt;code&amp;gt;ln -s target link&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;&lt;br /&gt;
| Search for files&lt;br /&gt;
| &amp;lt;code&amp;gt;find /home -name &amp;amp;quot;*.txt&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;&lt;br /&gt;
| Change file permissions&lt;br /&gt;
| &amp;lt;code&amp;gt;chmod +x script.sh&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&lt;br /&gt;
| Change file owner&lt;br /&gt;
| &amp;lt;code&amp;gt;chown user:group file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;text-viewing-and-editing&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Text Viewing and Editing ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;&lt;br /&gt;
| Concatenate and display files&lt;br /&gt;
| &amp;lt;code&amp;gt;cat file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;less&amp;lt;/code&amp;gt;&lt;br /&gt;
| View file with pagination&lt;br /&gt;
| &amp;lt;code&amp;gt;less largefile.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt;&lt;br /&gt;
| View file page by page&lt;br /&gt;
| &amp;lt;code&amp;gt;more file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;&lt;br /&gt;
| Display first lines of file&lt;br /&gt;
| &amp;lt;code&amp;gt;head -n 10 file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tail&amp;lt;/code&amp;gt;&lt;br /&gt;
| Display last lines of file&lt;br /&gt;
| &amp;lt;code&amp;gt;tail -n 20 file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;nano&amp;lt;/code&amp;gt;&lt;br /&gt;
| Simple text editor&lt;br /&gt;
| &amp;lt;code&amp;gt;nano file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt;&lt;br /&gt;
| Advanced text editor&lt;br /&gt;
| &amp;lt;code&amp;gt;vim file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;text-manipulation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Text Manipulation ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;&lt;br /&gt;
| Search for patterns in text&lt;br /&gt;
| &amp;lt;code&amp;gt;grep &amp;amp;quot;error&amp;amp;quot; log.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sed&amp;lt;/code&amp;gt;&lt;br /&gt;
| Stream editor for text transformation&lt;br /&gt;
| &amp;lt;code&amp;gt;sed &amp;#039;s/old/new/g&amp;#039; file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;awk&amp;lt;/code&amp;gt;&lt;br /&gt;
| Pattern scanning and processing&lt;br /&gt;
| &amp;lt;code&amp;gt;awk &amp;#039;{print $1}&amp;#039; file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt;&lt;br /&gt;
| Extract sections from lines&lt;br /&gt;
| &amp;lt;code&amp;gt;cut -d: -f1 /etc/passwd&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sort lines of text&lt;br /&gt;
| &amp;lt;code&amp;gt;sort file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;uniq&amp;lt;/code&amp;gt;&lt;br /&gt;
| Remove duplicate lines&lt;br /&gt;
| &amp;lt;code&amp;gt;uniq sorted.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;&lt;br /&gt;
| Translate or delete characters&lt;br /&gt;
| &amp;lt;code&amp;gt;tr &amp;#039;[:lower:]&amp;#039; &amp;#039;[:upper:]&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;&lt;br /&gt;
| Count lines, words, characters&lt;br /&gt;
| &amp;lt;code&amp;gt;wc -l file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt;&lt;br /&gt;
| Compare files line by line&lt;br /&gt;
| &amp;lt;code&amp;gt;diff file1.txt file2.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;paste&amp;lt;/code&amp;gt;&lt;br /&gt;
| Merge lines of files&lt;br /&gt;
| &amp;lt;code&amp;gt;paste file1.txt file2.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;join&amp;lt;/code&amp;gt;&lt;br /&gt;
| Join lines based on common field&lt;br /&gt;
| &amp;lt;code&amp;gt;join file1.txt file2.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;text-processing-utilities&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Text Processing Utilities ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;&lt;br /&gt;
| Display text&lt;br /&gt;
| &amp;lt;code&amp;gt;echo &amp;amp;quot;Hello World&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;&lt;br /&gt;
| Formatted output&lt;br /&gt;
| &amp;lt;code&amp;gt;printf &amp;amp;quot;%s\n&amp;amp;quot; &amp;amp;quot;text&amp;amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;xargs&amp;lt;/code&amp;gt;&lt;br /&gt;
| Build and execute commands from input&lt;br /&gt;
| &amp;lt;code&amp;gt;find . -name &amp;amp;quot;*.txt&amp;amp;quot; | xargs rm&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tee&amp;lt;/code&amp;gt;&lt;br /&gt;
| Read from stdin, write to stdout and files&lt;br /&gt;
| &amp;lt;code&amp;gt;echo &amp;amp;quot;test&amp;amp;quot; | tee file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;column&amp;lt;/code&amp;gt;&lt;br /&gt;
| Format output into columns&lt;br /&gt;
| &amp;lt;code&amp;gt;cat data.txt | column -t&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;expand&amp;lt;/code&amp;gt;&lt;br /&gt;
| Convert tabs to spaces&lt;br /&gt;
| &amp;lt;code&amp;gt;expand file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;unexpand&amp;lt;/code&amp;gt;&lt;br /&gt;
| Convert spaces to tabs&lt;br /&gt;
| &amp;lt;code&amp;gt;unexpand file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fold&amp;lt;/code&amp;gt;&lt;br /&gt;
| Wrap lines to specified width&lt;br /&gt;
| &amp;lt;code&amp;gt;fold -w 80 file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;file-information-and-comparison&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== File Information and Comparison ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determine file type&lt;br /&gt;
| &amp;lt;code&amp;gt;file document.pdf&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;&lt;br /&gt;
| Display file status&lt;br /&gt;
| &amp;lt;code&amp;gt;stat file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;du&amp;lt;/code&amp;gt;&lt;br /&gt;
| Disk usage&lt;br /&gt;
| &amp;lt;code&amp;gt;du -sh /home/user&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt;&lt;br /&gt;
| Disk free space&lt;br /&gt;
| &amp;lt;code&amp;gt;df -h&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;md5sum&amp;lt;/code&amp;gt;&lt;br /&gt;
| Calculate MD5 checksum&lt;br /&gt;
| &amp;lt;code&amp;gt;md5sum file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;sha256sum&amp;lt;/code&amp;gt;&lt;br /&gt;
| Calculate SHA256 checksum&lt;br /&gt;
| &amp;lt;code&amp;gt;sha256sum file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cmp&amp;lt;/code&amp;gt;&lt;br /&gt;
| Compare two files byte by byte&lt;br /&gt;
| &amp;lt;code&amp;gt;cmp file1 file2&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;compression-and-archives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Compression and Archives ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt;&lt;br /&gt;
| Archive files&lt;br /&gt;
| &amp;lt;code&amp;gt;tar -czf archive.tar.gz dir/&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt;&lt;br /&gt;
| Compress files&lt;br /&gt;
| &amp;lt;code&amp;gt;gzip file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;gunzip&amp;lt;/code&amp;gt;&lt;br /&gt;
| Decompress gzip files&lt;br /&gt;
| &amp;lt;code&amp;gt;gunzip file.txt.gz&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;zip&amp;lt;/code&amp;gt;&lt;br /&gt;
| Create zip archives&lt;br /&gt;
| &amp;lt;code&amp;gt;zip archive.zip file1 file2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;unzip&amp;lt;/code&amp;gt;&lt;br /&gt;
| Extract zip archives&lt;br /&gt;
| &amp;lt;code&amp;gt;unzip archive.zip&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;bzip2&amp;lt;/code&amp;gt;&lt;br /&gt;
| Compress with bzip2&lt;br /&gt;
| &amp;lt;code&amp;gt;bzip2 file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;xz&amp;lt;/code&amp;gt;&lt;br /&gt;
| Compress with xz&lt;br /&gt;
| &amp;lt;code&amp;gt;xz file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;text-search-and-pattern-matching&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Text Search and Pattern Matching ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Command&lt;br /&gt;
! Description&lt;br /&gt;
! Example&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;&lt;br /&gt;
| Search using basic regex&lt;br /&gt;
| &amp;lt;code&amp;gt;grep &amp;amp;quot;pattern&amp;amp;quot; file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -E&amp;lt;/code&amp;gt;&lt;br /&gt;
| Extended regex (same as &amp;lt;code&amp;gt;egrep&amp;lt;/code&amp;gt;)&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -E &amp;amp;quot;pat1|pat2&amp;amp;quot; file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -F&amp;lt;/code&amp;gt;&lt;br /&gt;
| Fixed strings (same as &amp;lt;code&amp;gt;fgrep&amp;lt;/code&amp;gt;)&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -F &amp;amp;quot;literal&amp;amp;quot; file.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -r&amp;lt;/code&amp;gt;&lt;br /&gt;
| Recursive search&lt;br /&gt;
| &amp;lt;code&amp;gt;grep -r &amp;amp;quot;pattern&amp;amp;quot; /path/&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;locate&amp;lt;/code&amp;gt;&lt;br /&gt;
| Find files by name (uses database)&lt;br /&gt;
| &amp;lt;code&amp;gt;locate filename&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt;&lt;br /&gt;
| Show full path of commands&lt;br /&gt;
| &amp;lt;code&amp;gt;which python&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;whereis&amp;lt;/code&amp;gt;&lt;br /&gt;
| Locate binary, source, and man pages&lt;br /&gt;
| &amp;lt;code&amp;gt;whereis bash&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;common-command-options&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Common Command Options ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Frequently used flags across commands:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive operation&lt;br /&gt;
* &amp;lt;code&amp;gt;-v&amp;lt;/code&amp;gt;: Verbose output or invert match (grep)&lt;br /&gt;
* &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt;: Force operation&lt;br /&gt;
* &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Ignore case (grep, sort) or interactive (rm, mv)&lt;br /&gt;
* &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show line numbers (grep, cat)&lt;br /&gt;
* &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;: All/append&lt;br /&gt;
* &amp;lt;code&amp;gt;-h&amp;lt;/code&amp;gt;: Human-readable output&lt;br /&gt;
* &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Long format or list files only&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing screenshots of:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise A:&amp;#039;&amp;#039;&amp;#039; Permission outputs, error message, successful output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise B:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; outputs, script + pwd output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise C:&amp;#039;&amp;#039;&amp;#039; Error/success outputs, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise D:&amp;#039;&amp;#039;&amp;#039; Exit status values, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise E:&amp;#039;&amp;#039;&amp;#039; Stderr contents, script output, FD explanations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 1:&amp;#039;&amp;#039;&amp;#039; Script code, test outputs (success, no args, nonexistent file)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 2:&amp;#039;&amp;#039;&amp;#039; Script code, outputs for all three operations, &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab provides foundations for the next exercise on signals and inter-process communication. You&amp;#039;ll use bash&amp;#039;s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin and named pipes without writing C code.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
&lt;br /&gt;
* Bash manual: &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt;&lt;br /&gt;
* ShellCheck: https://www.shellcheck.net/&lt;br /&gt;
* Advanced Bash-Scripting Guide: https://tldp.org/LDP/abs/html/&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8171</id>
		<title>OS Lab 5 - Bash Scripting</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8171"/>
		<updated>2025-10-31T14:54:46Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel executes scripts via the shebang mechanism and how bash interprets script contents.&lt;br /&gt;
* Distinguish between shell builtins and external programs, understanding why certain commands must be builtins.&lt;br /&gt;
* Write scripts using proper variable quoting, recognizing that commands are fundamentally lists of strings.&lt;br /&gt;
* Use exit status to control script flow with conditionals and logical operators.&lt;br /&gt;
* Redirect file descriptors to control input and output streams.&lt;br /&gt;
* Implement loops with &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;, incorporating command substitution.&lt;br /&gt;
* Define functions with proper variable scoping and work with arrays.&lt;br /&gt;
* Apply defensive scripting practices using &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In previous labs, we explored processes, job control, and the permission model. These concepts form the foundation for understanding how programs execute and interact with the system. We now turn to bash scripting, which allows us to automate tasks and build tools without writing compiled programs.&lt;br /&gt;
&lt;br /&gt;
This lab emphasizes underlying mechanisms rather than syntax alone. We will examine how the kernel interprets shebangs, why certain commands must be shell builtins, and how bash&amp;#039;s command model treats everything as lists of strings. Understanding these foundations prepares you for the next lab, where we&amp;#039;ll explore signals and inter-process communication using bash scripts rather than C code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
All necessary utilities (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;) are pre-installed. No additional packages are required.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Basic command-line navigation and file operations&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, exit status)&lt;br /&gt;
* File permissions and the execute bit from Lab 4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Bash Scripting ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Shebangs and Script Execution ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
When you execute a file like &amp;lt;code&amp;gt;./script.sh&amp;lt;/code&amp;gt;, the kernel first checks the execute permission bit. If set, the kernel examines the file&amp;#039;s first bytes. If they are &amp;lt;code&amp;gt;#!&amp;lt;/code&amp;gt; (the &amp;amp;quot;shebang&amp;amp;quot;), the kernel reads the rest of that line as a path to an interpreter and re-executes the file using that interpreter.&lt;br /&gt;
&lt;br /&gt;
For example, a script beginning with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt; causes the kernel to execute &amp;lt;code&amp;gt;/bin/bash ./script.sh&amp;lt;/code&amp;gt;. This mechanism is general-purpose: the kernel doesn&amp;#039;t care whether the interpreter is bash, Python, or any other program.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements for script execution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Execute bit must be set (&amp;lt;code&amp;gt;chmod +x script.sh&amp;lt;/code&amp;gt;)&lt;br /&gt;
* File must begin with a valid shebang pointing to an interpreter&lt;br /&gt;
* The interpreter itself must be an executable binary&lt;br /&gt;
&lt;br /&gt;
Without the execute bit, the kernel&amp;#039;s permission check fails. Without a shebang, the kernel cannot determine which interpreter to use, and behavior becomes system-dependent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shells-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Shell&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
Once the kernel launches bash with your script as an argument, bash reads the script line by line, parsing each line according to its syntax rules. This involves expanding variables, performing word splitting, executing commands, and capturing results.&lt;br /&gt;
&lt;br /&gt;
The fundamental principle: &amp;#039;&amp;#039;&amp;#039;every command is a list of strings&amp;#039;&amp;#039;&amp;#039;. When bash executes &amp;lt;code&amp;gt;echo Hello World&amp;lt;/code&amp;gt;, it constructs a list of three strings: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;. The first string identifies the command; remaining strings are arguments. This model applies universally to builtins, external programs, and functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;builtins-vs-external-programs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Builtins vs External Programs ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;external-programs-and-path&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== External Programs and PATH ====&lt;br /&gt;
&lt;br /&gt;
When bash encounters a command that isn&amp;#039;t a builtin, it searches the directories in the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable for an executable with that name. When found, bash forks a child process and executes the program. Commands like &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt; run in separate processes with their own address space.&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;which ls&lt;br /&gt;
# Output: /usr/bin/ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;shell-builtins&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Shell Builtins ====&lt;br /&gt;
&lt;br /&gt;
Builtins are commands built directly into bash. Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;type cd&lt;br /&gt;
# Output: cd is a shell builtin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Builtins exist for two reasons:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Process State Modification&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Some commands must modify the shell&amp;#039;s own process state. Consider &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;: if it were an external program, it would run in a child process, change that child&amp;#039;s directory, then exit. The parent shell&amp;#039;s directory would remain unchanged because child process state doesn&amp;#039;t propagate to parents. For &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; to work, it must execute within the shell&amp;#039;s own process, making it a necessary builtin.&lt;br /&gt;
&lt;br /&gt;
Other state-modifying builtins include &amp;lt;code&amp;gt;ulimit&amp;lt;/code&amp;gt; (resource limits) and &amp;lt;code&amp;gt;umask&amp;lt;/code&amp;gt; (permission mask). In the next lab, we&amp;#039;ll see &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; (environment variables) and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; (signal handlers).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Performance&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Commands like &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; are builtins for performance, avoiding process creation overhead. They could theoretically be external programs, but making them builtins is faster.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-your-first-script-and-the-shebang&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Your First Script and the Shebang ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates the kernel&amp;#039;s shebang mechanism and the importance of the execute bit.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a script &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt; containing:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
echo &amp;quot;Hello from bash!&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Check the file&amp;#039;s permissions with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;. Note that the execute bit is not set.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Attempt to execute the script directly: &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. This should fail with &amp;amp;quot;Permission denied&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Make the script executable with &amp;lt;code&amp;gt;chmod +x&amp;lt;/code&amp;gt; and verify with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script directly. It should now succeed.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script by passing it to bash: &amp;lt;code&amp;gt;bash /tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. Observe that this works even without the execute bit.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a second script without a shebang, make it executable, and attempt to run it. Observe the unpredictable behavior.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Clean up both test scripts.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output before and after setting the execute bit, the &amp;amp;quot;Permission denied&amp;amp;quot; error, and the successful execution output.&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-builtins-vs-external-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Builtins vs External Commands ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why certain commands must be builtins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify whether &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; are builtins or external commands.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs. Try &amp;lt;code&amp;gt;which cd&amp;lt;/code&amp;gt; and note that it doesn&amp;#039;t find the builtin.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_cd_test.sh&amp;lt;/code&amp;gt; that prints the current directory, changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, and prints the directory again.&lt;br /&gt;
# Make the script executable and run it. Observe that the script successfully changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check your shell&amp;#039;s current directory with &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;. Note that your shell is still in the original directory.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; directly in your shell to confirm it works when run as a builtin.&lt;br /&gt;
# Clean up the test script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; command outputs, the script output followed by your shell&amp;#039;s &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;variables-and-quoting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Variables and Quoting ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-list-of-strings-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The List-of-Strings Model ====&lt;br /&gt;
&lt;br /&gt;
Variables in bash are expanded before commands execute. How they&amp;#039;re expanded determines the final command&amp;#039;s argument list.&lt;br /&gt;
&lt;br /&gt;
Consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greeting=&amp;quot;Hello World&amp;quot;&lt;br /&gt;
echo $greeting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash expands &amp;lt;code&amp;gt;$greeting&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;amp;quot;Hello World&amp;amp;quot;&amp;lt;/code&amp;gt;, then performs &amp;#039;&amp;#039;&amp;#039;word splitting&amp;#039;&amp;#039;&amp;#039;, breaking it into separate strings at whitespace. The result: three strings passed to echo: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This works fine for echo, but consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;filename=&amp;quot;my document.txt&amp;quot;&lt;br /&gt;
ls $filename&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;my&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; command receives two arguments and tries to list two separate files, which fails.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;quoting-fixes-this&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Quoting Fixes This ====&lt;br /&gt;
&lt;br /&gt;
Double quotes prevent word splitting:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; receives one argument: &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The expansion happens, but the result stays as a single string.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Single quotes&amp;#039;&amp;#039;&amp;#039; prevent all expansion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;#039;$name&amp;#039;    # Outputs: $name (literal)&lt;br /&gt;
echo &amp;quot;$name&amp;quot;    # Outputs: the value of $name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Always quote variables unless you specifically need word splitting. Write &amp;lt;code&amp;gt;&amp;amp;quot;$variable&amp;amp;quot;&amp;lt;/code&amp;gt; not &amp;lt;code&amp;gt;$variable&amp;lt;/code&amp;gt;. This makes scripts robust when variables contain spaces or special characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-quoting-and-word-splitting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Quoting and Word Splitting ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why quoting variables is essential.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a file named &amp;lt;code&amp;gt;/tmp/my test file.txt&amp;lt;/code&amp;gt; (with spaces).&lt;br /&gt;
# Store the filename in a variable and try to list it without quotes: &amp;lt;code&amp;gt;ls $myfile&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Try again with proper quoting: &amp;lt;code&amp;gt;ls &amp;amp;quot;$myfile&amp;amp;quot;&amp;lt;/code&amp;gt;. This succeeds.&lt;br /&gt;
# Test echo with a variable containing multiple spaces, both with and without quotes.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_quote_test.sh&amp;lt;/code&amp;gt; that demonstrates variable quoting and array expansion both correctly and incorrectly. The script should show:&lt;br /&gt;
#* A variable with spaces, echoed with and without quotes&lt;br /&gt;
#* An array containing an element with spaces, iterated both ways&lt;br /&gt;
# Make the script executable and run it. Observe how the &amp;amp;quot;wrong way&amp;amp;quot; processes four items instead of three.&lt;br /&gt;
# Clean up the test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the error from unquoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, the success from quoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
Every command returns an exit status: a number from 0 to 255. By convention, 0 means success; non-zero means failure. Bash stores the last command&amp;#039;s exit status in &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /tmp&lt;br /&gt;
echo $?    # Outputs: 0 (success)&lt;br /&gt;
&lt;br /&gt;
ls /nonexistent&lt;br /&gt;
echo $?    # Outputs: non-zero (failure)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This exit status is bash&amp;#039;s fundamental true/false mechanism. An &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statement executes a command and checks if it succeeded:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if grep -q &amp;quot;ERROR&amp;quot; /var/log/syslog; then&lt;br /&gt;
    echo &amp;quot;Errors found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; finds &amp;amp;quot;ERROR&amp;amp;quot;, it returns 0, and the &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; block executes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the--command&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; Command ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; builtin performs tests and returns an exit status. Despite unusual syntax, it&amp;#039;s conceptually just another command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;[[ -f &amp;quot;file.txt&amp;quot; ]]    # Returns 0 if file exists, 1 otherwise&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common operators:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-f file&amp;lt;/code&amp;gt;: True if regular file exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-d dir&amp;lt;/code&amp;gt;: True if directory exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-e path&amp;lt;/code&amp;gt;: True if path exists&lt;br /&gt;
* &amp;lt;code&amp;gt;string1 == string2&amp;lt;/code&amp;gt;: True if strings equal&lt;br /&gt;
* &amp;lt;code&amp;gt;num1 -lt num2&amp;lt;/code&amp;gt;: True if num1 less than num2&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;config.txt&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Config file found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;logical-operators&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Logical Operators ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; (AND):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous succeeded (returned 0):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir /tmp/newdir &amp;amp;&amp;amp; cd /tmp/newdir&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; (OR):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous failed (returned non-zero):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd /tmp/important || echo &amp;quot;Failed to change directory&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explicit error ignoring:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/maybe-exists.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern documents that you&amp;#039;re intentionally ignoring potential errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores exit status and conditional logic.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /tmp&amp;lt;/code&amp;gt; and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be 0 (success).&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /nonexistent&amp;lt;/code&amp;gt; (redirect stderr to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;) and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be non-zero.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;[[ -f /etc/passwd ]]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;[[ -f /nonexistent ]]&amp;lt;/code&amp;gt;, checking &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt; after each.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_conditionals.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* File existence tests with &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;&lt;br /&gt;
#* The &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; operator for success chaining&lt;br /&gt;
#* The &amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; operator for failure handling&lt;br /&gt;
#* The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern for explicit error ignoring&lt;br /&gt;
#* Using command exit status directly in &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; (e.g., with &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;)&lt;br /&gt;
#* Numeric and string comparisons with &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it. Observe the output from each test.&lt;br /&gt;
# Clean up the script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide exit status values from steps 1-3, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;output-redirection-and-file-descriptors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Output Redirection and File Descriptors ===&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 3 that every process has file descriptors (FDs):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 0:&amp;#039;&amp;#039;&amp;#039; Standard input (stdin)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 1:&amp;#039;&amp;#039;&amp;#039; Standard output (stdout)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 2:&amp;#039;&amp;#039;&amp;#039; Standard error (stderr)&lt;br /&gt;
&lt;br /&gt;
Redirection allows you to control where these streams go.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdout to a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;output&amp;quot; &amp;gt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect stderr:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /nonexistent 2&amp;gt; error.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect both:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;amp;&amp;gt; combined.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or more explicitly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;gt; output.txt 2&amp;gt;&amp;amp;1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Order matters! &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; must come after &amp;lt;code&amp;gt;&amp;amp;gt; output.txt&amp;lt;/code&amp;gt; to redirect stderr to where stdout is pointing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes ====&lt;br /&gt;
&lt;br /&gt;
The pipe operator &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; connects one command&amp;#039;s stdout to another&amp;#039;s stdin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /var/log/syslog | grep &amp;quot;ERROR&amp;quot; | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Data flows left to right. The kernel creates an anonymous pipe, connecting the write end to the first command&amp;#039;s stdout and the read end to the second command&amp;#039;s stdin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;input-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Input Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdin from a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This differs from &amp;lt;code&amp;gt;wc -l file.txt&amp;lt;/code&amp;gt;: with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;, bash opens the file and connects it to stdin before running &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;loops&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Loops ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-for-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Iterates over a list of values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for item in value1 value2 value3; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common sources for the list:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filename expansion (globbing):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;Number $i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-while-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Executes while a command succeeds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;counter=1&lt;br /&gt;
while [[ $counter -le 5 ]]; do&lt;br /&gt;
    echo &amp;quot;Iteration $counter&amp;quot;&lt;br /&gt;
    counter=$((counter + 1))&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Reading files line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;Line: $line&amp;quot;&lt;br /&gt;
done &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; command returns 0 while reading lines successfully, and non-zero at end-of-file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-file-descriptor-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: File Descriptor Redirection ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores redirection, connecting to Lab 3&amp;#039;s file descriptor concepts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Redirect stdout to a file with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;, then display the file contents.&lt;br /&gt;
# Redirect stderr to a file with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt; by trying to list a nonexistent directory.&lt;br /&gt;
# Redirect both stdout and stderr to the same file using &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate the &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; pattern with explicit redirection order.&lt;br /&gt;
# Use a pipe to chain &amp;lt;code&amp;gt;cat /etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep &amp;amp;quot;root&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt; to extract the username.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_redirect.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* Output redirection with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Stderr capture with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Combined stream redirection with &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* A pipeline example&lt;br /&gt;
#* Input redirection with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it.&lt;br /&gt;
# Clean up all test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the captured stderr contents and complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;command-substitution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Command Substitution ===&lt;br /&gt;
&lt;br /&gt;
Command substitution captures a command&amp;#039;s stdout for use elsewhere:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;current_date=$(date +%Y-%m-%d)&lt;br /&gt;
echo &amp;quot;Today is $current_date&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash executes the command in &amp;lt;code&amp;gt;$()&amp;lt;/code&amp;gt;, captures its output, and substitutes it in place.&lt;br /&gt;
&lt;br /&gt;
Common in loops:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;$i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt; output (numbers 1-10, one per line) is captured, word-split, and becomes the loop&amp;#039;s value list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;functions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Functions ===&lt;br /&gt;
&lt;br /&gt;
Functions group commands for reuse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greet() {&lt;br /&gt;
    echo &amp;quot;Hello, $1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
greet &amp;quot;Alice&amp;quot;    # Outputs: Hello, Alice&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arguments:&amp;#039;&amp;#039;&amp;#039; Access via &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$2&amp;lt;/code&amp;gt;, etc. &amp;lt;code&amp;gt;$@&amp;lt;/code&amp;gt; expands to all arguments; &amp;lt;code&amp;gt;$#&amp;lt;/code&amp;gt; gives the count.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return values:&amp;#039;&amp;#039;&amp;#039; Functions return exit status (0-255) via &amp;lt;code&amp;gt;return&amp;lt;/code&amp;gt;. For other values, write to stdout and capture with command substitution:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;get_uppercase() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | tr &amp;#039;[:lower:]&amp;#039; &amp;#039;[:upper:]&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
result=$(get_uppercase &amp;quot;hello&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;variable-scope&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Variable Scope ====&lt;br /&gt;
&lt;br /&gt;
By default, variables are global. Use &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; for function-local variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;calculate() {&lt;br /&gt;
    local temp=$1&lt;br /&gt;
    local result=$((temp * 2))&lt;br /&gt;
    echo &amp;quot;$result&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Without &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, these variables would affect the global scope.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;arrays&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
Arrays are collections of strings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;files=(&amp;quot;file1.txt&amp;quot; &amp;quot;file 2.txt&amp;quot; &amp;quot;file3.txt&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access elements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;${files[0]}&amp;quot;           # First element&lt;br /&gt;
echo &amp;quot;${files[@]}&amp;quot;           # All elements&lt;br /&gt;
echo &amp;quot;${#files[@]}&amp;quot;          # Number of elements&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical syntax:&amp;#039;&amp;#039;&amp;#039; Use &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt; (with quotes) to preserve each element as a separate string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in &amp;quot;${files[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This correctly processes three files, including &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; as one item.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without quotes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in ${files[@]}; do    # WRONG&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;file&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;2.txt&amp;amp;quot;&amp;lt;/code&amp;gt;, processing four items instead of three.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;defensive-scripting-set--euo-pipefail&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Defensive Scripting: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Bash scripts, by default, continue after errors. This can cause silent failures that are hard to debug. These options make scripts safer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--e-exit-on-error&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -e&amp;lt;/code&amp;gt; (Exit on Error) ====&lt;br /&gt;
&lt;br /&gt;
Exit immediately if any command returns non-zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -e&lt;br /&gt;
&lt;br /&gt;
cp important.txt backup.txt&lt;br /&gt;
process backup.txt    # Won&amp;#039;t run if cp failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
To allow specific failures:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/file.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--u-error-on-undefined-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -u&amp;lt;/code&amp;gt; (Error on Undefined Variables) ====&lt;br /&gt;
&lt;br /&gt;
Treat undefined variables as errors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -u&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;$undefined_var&amp;quot;    # Error: undefined_var: unbound variable&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This catches typos and logic errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--o-pipefail-pipeline-failure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -o pipefail&amp;lt;/code&amp;gt; (Pipeline Failure) ====&lt;br /&gt;
&lt;br /&gt;
Normally, a pipeline&amp;#039;s exit status is the last command&amp;#039;s status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns 0 (head succeeded) even though grep failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With &amp;lt;code&amp;gt;pipefail&amp;lt;/code&amp;gt;, the pipeline fails if any command fails:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -o pipefail&lt;br /&gt;
&lt;br /&gt;
grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns non-zero (grep failed)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;recommended-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Recommended Practice ====&lt;br /&gt;
&lt;br /&gt;
Start every script with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
These options prevent silent errors and make debugging easier.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-file-analyzer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log File Analyzer ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;/tmp/lab5_log_analyzer.sh&amp;lt;/code&amp;gt; that analyzes a log file.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept one argument: log file path&lt;br /&gt;
* Print usage message if wrong number of arguments&lt;br /&gt;
* Print error if file doesn&amp;#039;t exist or isn&amp;#039;t readable&lt;br /&gt;
* Count lines containing &amp;amp;quot;ERROR&amp;amp;quot;, &amp;amp;quot;WARN&amp;amp;quot;, and &amp;amp;quot;INFO&amp;amp;quot;&lt;br /&gt;
* Print summary report&lt;br /&gt;
* Use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, proper quoting, &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statements, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, counting method&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab5_test.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 INFO User login successful&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout, retrying&lt;br /&gt;
2024-01-15 10:15:00 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:15:30 INFO Connection restored&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration parameter&lt;br /&gt;
2024-01-15 10:25:00 WARN Disk space low&lt;br /&gt;
2024-01-15 10:30:00 INFO Backup completed successfully&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Log Analysis Report for /tmp/lab5_test.log&lt;br /&gt;
==========================================&lt;br /&gt;
ERROR: 2&lt;br /&gt;
WARN:  2&lt;br /&gt;
INFO:  4&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /tmp/lab5_test.log&lt;br /&gt;
/tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /nonexistent.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script with comments&lt;br /&gt;
* Output from test log&lt;br /&gt;
* Output with no arguments (usage)&lt;br /&gt;
* Output with nonexistent file (error)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-batch-file-processor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Batch File Processor ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;lab5_batch_processor.sh&amp;lt;/code&amp;gt; that processes multiple files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept two arguments: directory path and operation (&amp;amp;quot;count&amp;amp;quot;, &amp;amp;quot;uppercase&amp;amp;quot;, or &amp;amp;quot;list&amp;amp;quot;)&lt;br /&gt;
* Print usage if wrong arguments or invalid operation&lt;br /&gt;
* Define three functions (one per operation):&lt;br /&gt;
** &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: Count lines in each &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
** &amp;lt;code&amp;gt;uppercase&amp;lt;/code&amp;gt;: Create &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; files with uppercase content&lt;br /&gt;
** &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;: List &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; files with sizes&lt;br /&gt;
* Store files in array, use loop to process&lt;br /&gt;
* Must use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions with &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, array with &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt;, loop, case/if for operation selection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p /tmp/lab5_batch_test&lt;br /&gt;
echo -e &amp;quot;line 1\nline 2\nline 3&amp;quot; &amp;gt; /tmp/lab5_batch_test/file1.txt&lt;br /&gt;
echo -e &amp;quot;hello world\ntest file&amp;quot; &amp;gt; /tmp/lab5_batch_test/file2.txt&lt;br /&gt;
echo -e &amp;quot;single line&amp;quot; &amp;gt; /tmp/lab5_batch_test/file3.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output for &amp;amp;quot;count&amp;amp;quot;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Processing files in /tmp/lab5_batch_test&lt;br /&gt;
Operation: count&lt;br /&gt;
========================================&lt;br /&gt;
file1.txt: 3 lines&lt;br /&gt;
file2.txt: 2 lines&lt;br /&gt;
file3.txt: 1 lines&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab5_batch_processor.sh&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test count&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test uppercase&lt;br /&gt;
cat /tmp/lab5_batch_test/file1.upper.txt&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script&lt;br /&gt;
* Output from &amp;amp;quot;count&amp;amp;quot; operation&lt;br /&gt;
* Output from &amp;amp;quot;uppercase&amp;amp;quot; operation + contents of one &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
* Output from &amp;amp;quot;list&amp;amp;quot; operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for common bash patterns:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check arguments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ $# -lt 1 ]]; then&lt;br /&gt;
    echo &amp;quot;Usage: $0 &amp;lt;arg&amp;gt;&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test file types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Regular file&amp;quot;&lt;br /&gt;
elif [[ -d &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Directory&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read file line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;$line&amp;quot;&lt;br /&gt;
done &amp;lt; &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Iterate over files:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    [[ -f &amp;quot;$file&amp;quot; ]] || continue&lt;br /&gt;
    echo &amp;quot;$file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;date=$(date +%Y-%m-%d)&lt;br /&gt;
count=$(wc -l &amp;lt; &amp;quot;$file&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arrays:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arr=(&amp;quot;item1&amp;quot; &amp;quot;item 2&amp;quot; &amp;quot;item3&amp;quot;)&lt;br /&gt;
for item in &amp;quot;${arr[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;process() {&lt;br /&gt;
    local file=&amp;quot;$1&amp;quot;&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing screenshots of:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise A:&amp;#039;&amp;#039;&amp;#039; Permission outputs, error message, successful output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise B:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; outputs, script + pwd output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise C:&amp;#039;&amp;#039;&amp;#039; Error/success outputs, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise D:&amp;#039;&amp;#039;&amp;#039; Exit status values, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise E:&amp;#039;&amp;#039;&amp;#039; Stderr contents, script output, FD explanations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 1:&amp;#039;&amp;#039;&amp;#039; Script code, test outputs (success, no args, nonexistent file)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 2:&amp;#039;&amp;#039;&amp;#039; Script code, outputs for all three operations, &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab provides foundations for the next exercise on signals and inter-process communication. You&amp;#039;ll use bash&amp;#039;s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin and named pipes without writing C code.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
&lt;br /&gt;
* Bash manual: &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt;&lt;br /&gt;
* ShellCheck: https://www.shellcheck.net/&lt;br /&gt;
* Advanced Bash-Scripting Guide: https://tldp.org/LDP/abs/html/&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8170</id>
		<title>OS Lab 5 - Bash Scripting</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Bash_Scripting&amp;diff=8170"/>
		<updated>2025-10-31T14:50:21Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * Explain how the kernel executes scripts via the shebang mechanism and how bash...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;objectives&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain how the kernel executes scripts via the shebang mechanism and how bash interprets script contents.&lt;br /&gt;
* Distinguish between shell builtins and external programs, understanding why certain commands must be builtins.&lt;br /&gt;
* Write scripts using proper variable quoting, recognizing that commands are fundamentally lists of strings.&lt;br /&gt;
* Use exit status to control script flow with conditionals and logical operators.&lt;br /&gt;
* Redirect file descriptors to control input and output streams.&lt;br /&gt;
* Implement loops with &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt;, incorporating command substitution.&lt;br /&gt;
* Define functions with proper variable scoping and work with arrays.&lt;br /&gt;
* Apply defensive scripting practices using &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In previous labs, we explored processes, job control, and the permission model. These concepts form the foundation for understanding how programs execute and interact with the system. We now turn to bash scripting, which allows us to automate tasks and build tools without writing compiled programs.&lt;br /&gt;
&lt;br /&gt;
This lab emphasizes underlying mechanisms rather than syntax alone. We will examine how the kernel interprets shebangs, why certain commands must be shell builtins, and how bash&amp;#039;s command model treats everything as lists of strings. Understanding these foundations prepares you for the next lab, where we&amp;#039;ll explore signals and inter-process communication using bash scripts rather than C code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;system-requirements&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;required-packages&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
&lt;br /&gt;
All necessary utilities (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;) are pre-installed. No additional packages are required.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;knowledge-prerequisites&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Knowledge Prerequisites ===&lt;br /&gt;
&lt;br /&gt;
You should be familiar with:&lt;br /&gt;
&lt;br /&gt;
* Basic command-line navigation and file operations&lt;br /&gt;
* Process concepts from Lab 3 (PIDs, exit status)&lt;br /&gt;
* File permissions and the execute bit from Lab 4&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;theoretical-background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;shebangs-and-script-execution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Shebangs and Script Execution ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-kernels-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Kernel&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
When you execute a file like &amp;lt;code&amp;gt;./script.sh&amp;lt;/code&amp;gt;, the kernel first checks the execute permission bit. If set, the kernel examines the file&amp;#039;s first bytes. If they are &amp;lt;code&amp;gt;#!&amp;lt;/code&amp;gt; (the &amp;amp;quot;shebang&amp;amp;quot;), the kernel reads the rest of that line as a path to an interpreter and re-executes the file using that interpreter.&lt;br /&gt;
&lt;br /&gt;
For example, a script beginning with &amp;lt;code&amp;gt;#!/bin/bash&amp;lt;/code&amp;gt; causes the kernel to execute &amp;lt;code&amp;gt;/bin/bash ./script.sh&amp;lt;/code&amp;gt;. This mechanism is general-purpose: the kernel doesn&amp;#039;t care whether the interpreter is bash, Python, or any other program.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements for script execution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Execute bit must be set (&amp;lt;code&amp;gt;chmod +x script.sh&amp;lt;/code&amp;gt;)&lt;br /&gt;
* File must begin with a valid shebang pointing to an interpreter&lt;br /&gt;
* The interpreter itself must be an executable binary&lt;br /&gt;
&lt;br /&gt;
Without the execute bit, the kernel&amp;#039;s permission check fails. Without a shebang, the kernel cannot determine which interpreter to use, and behavior becomes system-dependent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-shells-role&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The Shell&amp;#039;s Role ====&lt;br /&gt;
&lt;br /&gt;
Once the kernel launches bash with your script as an argument, bash reads the script line by line, parsing each line according to its syntax rules. This involves expanding variables, performing word splitting, executing commands, and capturing results.&lt;br /&gt;
&lt;br /&gt;
The fundamental principle: &amp;#039;&amp;#039;&amp;#039;every command is a list of strings&amp;#039;&amp;#039;&amp;#039;. When bash executes &amp;lt;code&amp;gt;echo Hello World&amp;lt;/code&amp;gt;, it constructs a list of three strings: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;. The first string identifies the command; remaining strings are arguments. This model applies universally to builtins, external programs, and functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;builtins-vs-external-programs&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Builtins vs External Programs ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;external-programs-and-path&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== External Programs and PATH ====&lt;br /&gt;
&lt;br /&gt;
When bash encounters a command that isn&amp;#039;t a builtin, it searches the directories in the &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable for an executable with that name. When found, bash forks a child process and executes the program. Commands like &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt; run in separate processes with their own address space.&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;which ls&lt;br /&gt;
# Output: /usr/bin/ls&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;shell-builtins&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Shell Builtins ====&lt;br /&gt;
&lt;br /&gt;
Builtins are commands built directly into bash. Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;type cd&lt;br /&gt;
# Output: cd is a shell builtin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Builtins exist for two reasons:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;1. Process State Modification&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Some commands must modify the shell&amp;#039;s own process state. Consider &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;: if it were an external program, it would run in a child process, change that child&amp;#039;s directory, then exit. The parent shell&amp;#039;s directory would remain unchanged because child process state doesn&amp;#039;t propagate to parents. For &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; to work, it must execute within the shell&amp;#039;s own process, making it a necessary builtin.&lt;br /&gt;
&lt;br /&gt;
Other state-modifying builtins include &amp;lt;code&amp;gt;ulimit&amp;lt;/code&amp;gt; (resource limits) and &amp;lt;code&amp;gt;umask&amp;lt;/code&amp;gt; (permission mask). In the next lab, we&amp;#039;ll see &amp;lt;code&amp;gt;export&amp;lt;/code&amp;gt; (environment variables) and &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; (signal handlers).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;2. Performance&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Commands like &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; are builtins for performance, avoiding process creation overhead. They could theoretically be external programs, but making them builtins is faster.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-a-your-first-script-and-the-shebang&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise A: Your First Script and the Shebang ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates the kernel&amp;#039;s shebang mechanism and the importance of the execute bit.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a script &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt; containing:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
echo &amp;quot;Hello from bash!&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Check the file&amp;#039;s permissions with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;. Note that the execute bit is not set.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Attempt to execute the script directly: &amp;lt;code&amp;gt;/tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. This should fail with &amp;amp;quot;Permission denied&amp;amp;quot;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Make the script executable with &amp;lt;code&amp;gt;chmod +x&amp;lt;/code&amp;gt; and verify with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script directly. It should now succeed.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Execute the script by passing it to bash: &amp;lt;code&amp;gt;bash /tmp/lab5_hello.sh&amp;lt;/code&amp;gt;. Observe that this works even without the execute bit.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Create a second script without a shebang, make it executable, and attempt to run it. Observe the unpredictable behavior.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Clean up both test scripts.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; output before and after setting the execute bit, the &amp;amp;quot;Permission denied&amp;amp;quot; error, and the successful execution output.&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-b-builtins-vs-external-commands&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise B: Builtins vs External Commands ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why certain commands must be builtins.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Use &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; to identify whether &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; are builtins or external commands.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;which&amp;lt;/code&amp;gt; to locate external programs. Try &amp;lt;code&amp;gt;which cd&amp;lt;/code&amp;gt; and note that it doesn&amp;#039;t find the builtin.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_cd_test.sh&amp;lt;/code&amp;gt; that prints the current directory, changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;, and prints the directory again.&lt;br /&gt;
# Make the script executable and run it. Observe that the script successfully changes to &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check your shell&amp;#039;s current directory with &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;. Note that your shell is still in the original directory.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; directly in your shell to confirm it works when run as a builtin.&lt;br /&gt;
# Clean up the test script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; command outputs, the script output followed by your shell&amp;#039;s &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;variables-and-quoting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Variables and Quoting ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-list-of-strings-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The List-of-Strings Model ====&lt;br /&gt;
&lt;br /&gt;
Variables in bash are expanded before commands execute. How they&amp;#039;re expanded determines the final command&amp;#039;s argument list.&lt;br /&gt;
&lt;br /&gt;
Consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greeting=&amp;quot;Hello World&amp;quot;&lt;br /&gt;
echo $greeting&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash expands &amp;lt;code&amp;gt;$greeting&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;&amp;amp;quot;Hello World&amp;amp;quot;&amp;lt;/code&amp;gt;, then performs &amp;#039;&amp;#039;&amp;#039;word splitting&amp;#039;&amp;#039;&amp;#039;, breaking it into separate strings at whitespace. The result: three strings passed to echo: &amp;lt;code&amp;gt;&amp;amp;quot;echo&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;Hello&amp;amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;amp;quot;World&amp;amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This works fine for echo, but consider:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;filename=&amp;quot;my document.txt&amp;quot;&lt;br /&gt;
ls $filename&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;my&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; command receives two arguments and tries to list two separate files, which fails.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;quoting-fixes-this&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Quoting Fixes This ====&lt;br /&gt;
&lt;br /&gt;
Double quotes prevent word splitting:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; receives one argument: &amp;lt;code&amp;gt;&amp;amp;quot;my document.txt&amp;amp;quot;&amp;lt;/code&amp;gt;. The expansion happens, but the result stays as a single string.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Single quotes&amp;#039;&amp;#039;&amp;#039; prevent all expansion:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;#039;$name&amp;#039;    # Outputs: $name (literal)&lt;br /&gt;
echo &amp;quot;$name&amp;quot;    # Outputs: the value of $name&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best practice:&amp;#039;&amp;#039;&amp;#039; Always quote variables unless you specifically need word splitting. Write &amp;lt;code&amp;gt;&amp;amp;quot;$variable&amp;amp;quot;&amp;lt;/code&amp;gt; not &amp;lt;code&amp;gt;$variable&amp;lt;/code&amp;gt;. This makes scripts robust when variables contain spaces or special characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-c-quoting-and-word-splitting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise C: Quoting and Word Splitting ===&lt;br /&gt;
&lt;br /&gt;
This exercise demonstrates why quoting variables is essential.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a file named &amp;lt;code&amp;gt;/tmp/my test file.txt&amp;lt;/code&amp;gt; (with spaces).&lt;br /&gt;
# Store the filename in a variable and try to list it without quotes: &amp;lt;code&amp;gt;ls $myfile&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Try again with proper quoting: &amp;lt;code&amp;gt;ls &amp;amp;quot;$myfile&amp;amp;quot;&amp;lt;/code&amp;gt;. This succeeds.&lt;br /&gt;
# Test echo with a variable containing multiple spaces, both with and without quotes.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_quote_test.sh&amp;lt;/code&amp;gt; that demonstrates variable quoting and array expansion both correctly and incorrectly. The script should show:&lt;br /&gt;
#* A variable with spaces, echoed with and without quotes&lt;br /&gt;
#* An array containing an element with spaces, iterated both ways&lt;br /&gt;
# Make the script executable and run it. Observe how the &amp;amp;quot;wrong way&amp;amp;quot; processes four items instead of three.&lt;br /&gt;
# Clean up the test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide the error from unquoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, the success from quoted &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
Every command returns an exit status: a number from 0 to 255. By convention, 0 means success; non-zero means failure. Bash stores the last command&amp;#039;s exit status in &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /tmp&lt;br /&gt;
echo $?    # Outputs: 0 (success)&lt;br /&gt;
&lt;br /&gt;
ls /nonexistent&lt;br /&gt;
echo $?    # Outputs: non-zero (failure)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This exit status is bash&amp;#039;s fundamental true/false mechanism. An &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statement executes a command and checks if it succeeded:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if grep -q &amp;quot;ERROR&amp;quot; /var/log/syslog; then&lt;br /&gt;
    echo &amp;quot;Errors found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; finds &amp;amp;quot;ERROR&amp;amp;quot;, it returns 0, and the &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; block executes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the--command&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; Command ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt; builtin performs tests and returns an exit status. Despite unusual syntax, it&amp;#039;s conceptually just another command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;[[ -f &amp;quot;file.txt&amp;quot; ]]    # Returns 0 if file exists, 1 otherwise&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common operators:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-f file&amp;lt;/code&amp;gt;: True if regular file exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-d dir&amp;lt;/code&amp;gt;: True if directory exists&lt;br /&gt;
* &amp;lt;code&amp;gt;-e path&amp;lt;/code&amp;gt;: True if path exists&lt;br /&gt;
* &amp;lt;code&amp;gt;string1 == string2&amp;lt;/code&amp;gt;: True if strings equal&lt;br /&gt;
* &amp;lt;code&amp;gt;num1 -lt num2&amp;lt;/code&amp;gt;: True if num1 less than num2&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;config.txt&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Config file found&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;logical-operators&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Logical Operators ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; (AND):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous succeeded (returned 0):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir /tmp/newdir &amp;amp;&amp;amp; cd /tmp/newdir&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; (OR):&amp;#039;&amp;#039;&amp;#039; Runs next command only if previous failed (returned non-zero):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd /tmp/important || echo &amp;quot;Failed to change directory&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explicit error ignoring:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/maybe-exists.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern documents that you&amp;#039;re intentionally ignoring potential errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-d-exit-status-and-conditionals&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise D: Exit Status and Conditionals ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores exit status and conditional logic.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /tmp&amp;lt;/code&amp;gt; and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be 0 (success).&lt;br /&gt;
# Execute &amp;lt;code&amp;gt;ls /nonexistent&amp;lt;/code&amp;gt; (redirect stderr to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;) and check &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt;. It should be non-zero.&lt;br /&gt;
# Test &amp;lt;code&amp;gt;[[ -f /etc/passwd ]]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;[[ -f /nonexistent ]]&amp;lt;/code&amp;gt;, checking &amp;lt;code&amp;gt;$?&amp;lt;/code&amp;gt; after each.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_conditionals.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* File existence tests with &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt;&lt;br /&gt;
#* The &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/code&amp;gt; operator for success chaining&lt;br /&gt;
#* The &amp;lt;code&amp;gt;||&amp;lt;/code&amp;gt; operator for failure handling&lt;br /&gt;
#* The &amp;lt;code&amp;gt;|| true&amp;lt;/code&amp;gt; pattern for explicit error ignoring&lt;br /&gt;
#* Using command exit status directly in &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; (e.g., with &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;)&lt;br /&gt;
#* Numeric and string comparisons with &amp;lt;code&amp;gt;[[&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it. Observe the output from each test.&lt;br /&gt;
# Clean up the script.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide exit status values from steps 1-3, complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;output-redirection-and-file-descriptors&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Output Redirection and File Descriptors ===&lt;br /&gt;
&lt;br /&gt;
Recall from Lab 3 that every process has file descriptors (FDs):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 0:&amp;#039;&amp;#039;&amp;#039; Standard input (stdin)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 1:&amp;#039;&amp;#039;&amp;#039; Standard output (stdout)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;FD 2:&amp;#039;&amp;#039;&amp;#039; Standard error (stderr)&lt;br /&gt;
&lt;br /&gt;
Redirection allows you to control where these streams go.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;basic-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Basic Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdout to a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;output&amp;quot; &amp;gt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect stderr:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls /nonexistent 2&amp;gt; error.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Redirect both:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;amp;&amp;gt; combined.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Or more explicitly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;command &amp;gt; output.txt 2&amp;gt;&amp;amp;1&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Order matters! &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; must come after &amp;lt;code&amp;gt;&amp;amp;gt; output.txt&amp;lt;/code&amp;gt; to redirect stderr to where stdout is pointing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;pipes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Pipes ====&lt;br /&gt;
&lt;br /&gt;
The pipe operator &amp;lt;code&amp;gt;|&amp;lt;/code&amp;gt; connects one command&amp;#039;s stdout to another&amp;#039;s stdin:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /var/log/syslog | grep &amp;quot;ERROR&amp;quot; | wc -l&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Data flows left to right. The kernel creates an anonymous pipe, connecting the write end to the first command&amp;#039;s stdout and the read end to the second command&amp;#039;s stdin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;input-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Input Redirection ====&lt;br /&gt;
&lt;br /&gt;
Redirect stdin from a file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;wc -l &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This differs from &amp;lt;code&amp;gt;wc -l file.txt&amp;lt;/code&amp;gt;: with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;, bash opens the file and connects it to stdin before running &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;loops&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Loops ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-for-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;for&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Iterates over a list of values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for item in value1 value2 value3; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Common sources for the list:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Filename expansion (globbing):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;Number $i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;the-while-loop&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== The &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt; Loop ====&lt;br /&gt;
&lt;br /&gt;
Executes while a command succeeds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;counter=1&lt;br /&gt;
while [[ $counter -le 5 ]]; do&lt;br /&gt;
    echo &amp;quot;Iteration $counter&amp;quot;&lt;br /&gt;
    counter=$((counter + 1))&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Reading files line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;Line: $line&amp;quot;&lt;br /&gt;
done &amp;lt; file.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;read&amp;lt;/code&amp;gt; command returns 0 while reading lines successfully, and non-zero at end-of-file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;exercise-e-file-descriptor-redirection&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Exercise E: File Descriptor Redirection ===&lt;br /&gt;
&lt;br /&gt;
This exercise explores redirection, connecting to Lab 3&amp;#039;s file descriptor concepts.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Steps:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Redirect stdout to a file with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;, then display the file contents.&lt;br /&gt;
# Redirect stderr to a file with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt; by trying to list a nonexistent directory.&lt;br /&gt;
# Redirect both stdout and stderr to the same file using &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Demonstrate the &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;amp;amp;1&amp;lt;/code&amp;gt; pattern with explicit redirection order.&lt;br /&gt;
# Use a pipe to chain &amp;lt;code&amp;gt;cat /etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep &amp;amp;quot;root&amp;amp;quot;&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;cut&amp;lt;/code&amp;gt; to extract the username.&lt;br /&gt;
# Create &amp;lt;code&amp;gt;/tmp/lab5_redirect.sh&amp;lt;/code&amp;gt; that demonstrates:&lt;br /&gt;
#* Output redirection with &amp;lt;code&amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;gt;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Stderr capture with &amp;lt;code&amp;gt;2&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Combined stream redirection with &amp;lt;code&amp;gt;&amp;amp;amp;&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
#* A pipeline example&lt;br /&gt;
#* Input redirection with &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Make the script executable and run it.&lt;br /&gt;
# Clean up all test files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; Provide the captured stderr contents and complete script output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;command-substitution&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Command Substitution ===&lt;br /&gt;
&lt;br /&gt;
Command substitution captures a command&amp;#039;s stdout for use elsewhere:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;current_date=$(date +%Y-%m-%d)&lt;br /&gt;
echo &amp;quot;Today is $current_date&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bash executes the command in &amp;lt;code&amp;gt;$()&amp;lt;/code&amp;gt;, captures its output, and substitutes it in place.&lt;br /&gt;
&lt;br /&gt;
Common in loops:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 1 10); do&lt;br /&gt;
    echo &amp;quot;$i&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt; output (numbers 1-10, one per line) is captured, word-split, and becomes the loop&amp;#039;s value list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;functions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Functions ===&lt;br /&gt;
&lt;br /&gt;
Functions group commands for reuse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;greet() {&lt;br /&gt;
    echo &amp;quot;Hello, $1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
greet &amp;quot;Alice&amp;quot;    # Outputs: Hello, Alice&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arguments:&amp;#039;&amp;#039;&amp;#039; Access via &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$2&amp;lt;/code&amp;gt;, etc. &amp;lt;code&amp;gt;$@&amp;lt;/code&amp;gt; expands to all arguments; &amp;lt;code&amp;gt;$#&amp;lt;/code&amp;gt; gives the count.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Return values:&amp;#039;&amp;#039;&amp;#039; Functions return exit status (0-255) via &amp;lt;code&amp;gt;return&amp;lt;/code&amp;gt;. For other values, write to stdout and capture with command substitution:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;get_uppercase() {&lt;br /&gt;
    echo &amp;quot;$1&amp;quot; | tr &amp;#039;[:lower:]&amp;#039; &amp;#039;[:upper:]&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
result=$(get_uppercase &amp;quot;hello&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;variable-scope&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Variable Scope ====&lt;br /&gt;
&lt;br /&gt;
By default, variables are global. Use &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt; for function-local variables:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;calculate() {&lt;br /&gt;
    local temp=$1&lt;br /&gt;
    local result=$((temp * 2))&lt;br /&gt;
    echo &amp;quot;$result&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Without &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, these variables would affect the global scope.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;arrays&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
Arrays are collections of strings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;files=(&amp;quot;file1.txt&amp;quot; &amp;quot;file 2.txt&amp;quot; &amp;quot;file3.txt&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Access elements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echo &amp;quot;${files[0]}&amp;quot;           # First element&lt;br /&gt;
echo &amp;quot;${files[@]}&amp;quot;           # All elements&lt;br /&gt;
echo &amp;quot;${#files[@]}&amp;quot;          # Number of elements&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Critical syntax:&amp;#039;&amp;#039;&amp;#039; Use &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt; (with quotes) to preserve each element as a separate string:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in &amp;quot;${files[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This correctly processes three files, including &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; as one item.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Without quotes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in ${files[@]}; do    # WRONG&lt;br /&gt;
    echo &amp;quot;Processing: $file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Word splitting breaks &amp;lt;code&amp;gt;&amp;amp;quot;file 2.txt&amp;amp;quot;&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;&amp;amp;quot;file&amp;amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;amp;quot;2.txt&amp;amp;quot;&amp;lt;/code&amp;gt;, processing four items instead of three.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;defensive-scripting-set--euo-pipefail&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Defensive Scripting: &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Bash scripts, by default, continue after errors. This can cause silent failures that are hard to debug. These options make scripts safer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--e-exit-on-error&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -e&amp;lt;/code&amp;gt; (Exit on Error) ====&lt;br /&gt;
&lt;br /&gt;
Exit immediately if any command returns non-zero:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -e&lt;br /&gt;
&lt;br /&gt;
cp important.txt backup.txt&lt;br /&gt;
process backup.txt    # Won&amp;#039;t run if cp failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
To allow specific failures:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;rm -f /tmp/file.txt || true&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--u-error-on-undefined-variables&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -u&amp;lt;/code&amp;gt; (Error on Undefined Variables) ====&lt;br /&gt;
&lt;br /&gt;
Treat undefined variables as errors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -u&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;$undefined_var&amp;quot;    # Error: undefined_var: unbound variable&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This catches typos and logic errors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;set--o-pipefail-pipeline-failure&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== &amp;lt;code&amp;gt;set -o pipefail&amp;lt;/code&amp;gt; (Pipeline Failure) ====&lt;br /&gt;
&lt;br /&gt;
Normally, a pipeline&amp;#039;s exit status is the last command&amp;#039;s status:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns 0 (head succeeded) even though grep failed&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With &amp;lt;code&amp;gt;pipefail&amp;lt;/code&amp;gt;, the pipeline fails if any command fails:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;set -o pipefail&lt;br /&gt;
&lt;br /&gt;
grep &amp;quot;ERROR&amp;quot; missing.log | head -n 10&lt;br /&gt;
# Returns non-zero (grep failed)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;recommended-practice&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Recommended Practice ====&lt;br /&gt;
&lt;br /&gt;
Start every script with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
These options prevent silent errors and make debugging easier.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;scripting-challenges&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Scripting Challenges ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-1-log-file-analyzer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 1: Log File Analyzer ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;/tmp/lab5_log_analyzer.sh&amp;lt;/code&amp;gt; that analyzes a log file.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept one argument: log file path&lt;br /&gt;
* Print usage message if wrong number of arguments&lt;br /&gt;
* Print error if file doesn&amp;#039;t exist or isn&amp;#039;t readable&lt;br /&gt;
* Count lines containing &amp;amp;quot;ERROR&amp;amp;quot;, &amp;amp;quot;WARN&amp;amp;quot;, and &amp;amp;quot;INFO&amp;amp;quot;&lt;br /&gt;
* Print summary report&lt;br /&gt;
* Use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, proper quoting, &amp;lt;code&amp;gt;if&amp;lt;/code&amp;gt; statements, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;, counting method&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /tmp/lab5_test.log &amp;lt;&amp;lt;&amp;#039;EOF&amp;#039;&lt;br /&gt;
2024-01-15 10:00:00 INFO Application started&lt;br /&gt;
2024-01-15 10:05:23 INFO User login successful&lt;br /&gt;
2024-01-15 10:12:45 WARN Connection timeout, retrying&lt;br /&gt;
2024-01-15 10:15:00 ERROR Database connection failed&lt;br /&gt;
2024-01-15 10:15:30 INFO Connection restored&lt;br /&gt;
2024-01-15 10:20:00 ERROR Invalid configuration parameter&lt;br /&gt;
2024-01-15 10:25:00 WARN Disk space low&lt;br /&gt;
2024-01-15 10:30:00 INFO Backup completed successfully&lt;br /&gt;
EOF&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Log Analysis Report for /tmp/lab5_test.log&lt;br /&gt;
==========================================&lt;br /&gt;
ERROR: 2&lt;br /&gt;
WARN:  2&lt;br /&gt;
INFO:  4&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x /tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /tmp/lab5_test.log&lt;br /&gt;
/tmp/lab5_log_analyzer.sh&lt;br /&gt;
/tmp/lab5_log_analyzer.sh /nonexistent.log&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 1:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script with comments&lt;br /&gt;
* Output from test log&lt;br /&gt;
* Output with no arguments (usage)&lt;br /&gt;
* Output with nonexistent file (error)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;challenge-2-batch-file-processor&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Challenge 2: Batch File Processor ===&lt;br /&gt;
&lt;br /&gt;
Write &amp;lt;code&amp;gt;lab5_batch_processor.sh&amp;lt;/code&amp;gt; that processes multiple files.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Requirements:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Accept two arguments: directory path and operation (&amp;amp;quot;count&amp;amp;quot;, &amp;amp;quot;uppercase&amp;amp;quot;, or &amp;amp;quot;list&amp;amp;quot;)&lt;br /&gt;
* Print usage if wrong arguments or invalid operation&lt;br /&gt;
* Define three functions (one per operation):&lt;br /&gt;
** &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;: Count lines in each &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
** &amp;lt;code&amp;gt;uppercase&amp;lt;/code&amp;gt;: Create &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; files with uppercase content&lt;br /&gt;
** &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;: List &amp;lt;code&amp;gt;.txt&amp;lt;/code&amp;gt; files with sizes&lt;br /&gt;
* Store files in array, use loop to process&lt;br /&gt;
* Must use: shebang, &amp;lt;code&amp;gt;set -euo pipefail&amp;lt;/code&amp;gt;, functions with &amp;lt;code&amp;gt;local&amp;lt;/code&amp;gt;, array with &amp;lt;code&amp;gt;&amp;amp;quot;${array[@]}&amp;amp;quot;&amp;lt;/code&amp;gt;, loop, case/if for operation selection&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test data:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;mkdir -p /tmp/lab5_batch_test&lt;br /&gt;
echo -e &amp;quot;line 1\nline 2\nline 3&amp;quot; &amp;gt; /tmp/lab5_batch_test/file1.txt&lt;br /&gt;
echo -e &amp;quot;hello world\ntest file&amp;quot; &amp;gt; /tmp/lab5_batch_test/file2.txt&lt;br /&gt;
echo -e &amp;quot;single line&amp;quot; &amp;gt; /tmp/lab5_batch_test/file3.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Expected output for &amp;amp;quot;count&amp;amp;quot;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Processing files in /tmp/lab5_batch_test&lt;br /&gt;
Operation: count&lt;br /&gt;
========================================&lt;br /&gt;
file1.txt: 3 lines&lt;br /&gt;
file2.txt: 2 lines&lt;br /&gt;
file3.txt: 1 lines&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod +x lab5_batch_processor.sh&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test count&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test uppercase&lt;br /&gt;
cat /tmp/lab5_batch_test/file1.upper.txt&lt;br /&gt;
./lab5_batch_processor.sh /tmp/lab5_batch_test list&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable Challenge 2:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Complete script&lt;br /&gt;
* Output from &amp;amp;quot;count&amp;amp;quot; operation&lt;br /&gt;
* Output from &amp;amp;quot;uppercase&amp;amp;quot; operation + contents of one &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; file&lt;br /&gt;
* Output from &amp;amp;quot;list&amp;amp;quot; operation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference-common-patterns&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference: Common Patterns ==&lt;br /&gt;
&lt;br /&gt;
Quick reference for common bash patterns:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Script header:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/bash&lt;br /&gt;
set -euo pipefail&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Check arguments:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ $# -lt 1 ]]; then&lt;br /&gt;
    echo &amp;quot;Usage: $0 &amp;lt;arg&amp;gt;&amp;quot; &amp;gt;&amp;amp;2&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Test file types:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;if [[ -f &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Regular file&amp;quot;&lt;br /&gt;
elif [[ -d &amp;quot;$file&amp;quot; ]]; then&lt;br /&gt;
    echo &amp;quot;Directory&amp;quot;&lt;br /&gt;
fi&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Read file line-by-line:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;while read -r line; do&lt;br /&gt;
    echo &amp;quot;$line&amp;quot;&lt;br /&gt;
done &amp;lt; &amp;quot;$filename&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Iterate over files:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for file in *.txt; do&lt;br /&gt;
    [[ -f &amp;quot;$file&amp;quot; ]] || continue&lt;br /&gt;
    echo &amp;quot;$file&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Command substitution:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;date=$(date +%Y-%m-%d)&lt;br /&gt;
count=$(wc -l &amp;lt; &amp;quot;$file&amp;quot;)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Arrays:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;arr=(&amp;quot;item1&amp;quot; &amp;quot;item 2&amp;quot; &amp;quot;item3&amp;quot;)&lt;br /&gt;
for item in &amp;quot;${arr[@]}&amp;quot;; do&lt;br /&gt;
    echo &amp;quot;$item&amp;quot;&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Functions:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;process() {&lt;br /&gt;
    local file=&amp;quot;$1&amp;quot;&lt;br /&gt;
    echo &amp;quot;Processing $file&amp;quot;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or similar) containing screenshots of:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise A:&amp;#039;&amp;#039;&amp;#039; Permission outputs, error message, successful output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise B:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; outputs, script + pwd output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise C:&amp;#039;&amp;#039;&amp;#039; Error/success outputs, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise D:&amp;#039;&amp;#039;&amp;#039; Exit status values, script output, explanation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Exercise E:&amp;#039;&amp;#039;&amp;#039; Stderr contents, script output, FD explanations&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Challenge Deliverables:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 1:&amp;#039;&amp;#039;&amp;#039; Script code, test outputs (success, no args, nonexistent file)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Challenge 2:&amp;#039;&amp;#039;&amp;#039; Script code, outputs for all three operations, &amp;lt;code&amp;gt;.upper.txt&amp;lt;/code&amp;gt; contents&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;additional-resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Additional Resources ==&lt;br /&gt;
&lt;br /&gt;
This lab provides foundations for the next exercise on signals and inter-process communication. You&amp;#039;ll use bash&amp;#039;s &amp;lt;code&amp;gt;trap&amp;lt;/code&amp;gt; builtin and named pipes without writing C code.&lt;br /&gt;
&lt;br /&gt;
For further study:&lt;br /&gt;
&lt;br /&gt;
* Bash manual: &amp;lt;code&amp;gt;man bash&amp;lt;/code&amp;gt;&lt;br /&gt;
* ShellCheck: https://www.shellcheck.net/&lt;br /&gt;
* Advanced Bash-Scripting Guide: https://tldp.org/LDP/abs/html/&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8169</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8169"/>
		<updated>2025-10-31T14:41:49Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;br /&gt;
* Lab 5 - [[OS Lab 5 - Bash Scripting]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=FPGA_Student_Classroom_tutorial&amp;diff=8168</id>
		<title>FPGA Student Classroom tutorial</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=FPGA_Student_Classroom_tutorial&amp;diff=8168"/>
		<updated>2025-10-27T08:27:40Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* FAQ */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FPGA Student Classroom Access Tutorial ==&lt;br /&gt;
&lt;br /&gt;
This is a guide that aims to show how to connect to the classroom resources remotely, in order to access the software and hardware resources (FPGAs) available at the university. The goal is to create a remote desktop session to one of the computers in the lab. This session can then be used to run software (e.g., Vivado) and to program hardware FPGAs.&lt;br /&gt;
&lt;br /&gt;
Before you can connect to the classroom cluster, you will need to obtain the following from a system administrator:&lt;br /&gt;
&lt;br /&gt;
* A VPN configuration file (e.g., a415-myname.conf)&lt;br /&gt;
* A username and password&lt;br /&gt;
* An assigned workstation and FPGA board (e.g., station07 &amp;amp; pynq07)&lt;br /&gt;
&lt;br /&gt;
The connection process consists of the following steps:&lt;br /&gt;
&lt;br /&gt;
# Connect via VPN to the cluster network.&lt;br /&gt;
# Connect to your workstation via SSH and create a remote desktop (VNC) instance.&lt;br /&gt;
# Connect to the remote desktop instance.&lt;br /&gt;
&lt;br /&gt;
== Step 1 - VPN ==&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster network via VPN, you will need the provided VPN configuration file (e.g., a415-myname.conf) (ask your system administrator if you don&amp;#039;t have it) and a WireGuard VPN client.&lt;br /&gt;
&lt;br /&gt;
For a full guide on how to use the WireGuard VPN, check [https://wiki.dcae.pub.ro/index.php/VPN_Tutorial_-_en this page].&lt;br /&gt;
&lt;br /&gt;
== Step 2 - SSH ==&lt;br /&gt;
&lt;br /&gt;
In order to connect to your workstation via SSH to open the remote desktop instance, you will need a terminal application or a dedicated SSH client.&lt;br /&gt;
&lt;br /&gt;
* **Windows 11** comes with Windows Terminal. Windows 10 users can download the [https://apps.microsoft.com/detail/9n0dx20hk701 Windows Terminal] from the Microsoft Store or use the older Command Prompt application.&lt;br /&gt;
* **macOS** has a pre-installed Terminal application. [https://iterm2.com/ iTerm2] is a popular community alternative with more features.&lt;br /&gt;
&lt;br /&gt;
First, connect to the workstation using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh myname@station07&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If this is your first time logging in, you will be prompted to change your initial password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
(myname@station07) Password: &lt;br /&gt;
Password expired. Change your password now.&lt;br /&gt;
(myname@station07) Current Password: &lt;br /&gt;
(myname@station07) New password: &lt;br /&gt;
(myname@station07) Retype new password:&lt;br /&gt;
&lt;br /&gt;
myname@station07:~$&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace `myname` and `station07` with your assigned username and workstation.&lt;br /&gt;
&lt;br /&gt;
Once connected, launch a VNC instance with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -localhost no&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If this is your first time starting a VNC instance, you will be prompted to set a password. You will also need to note the **VNC port** and **X display number** from the output. If you forget these numbers, you can retrieve them with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -list&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To stop the VNC session, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -kill :3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace `:3` with your actual X display number.&lt;br /&gt;
&lt;br /&gt;
== Step 3 - VNC ==&lt;br /&gt;
&lt;br /&gt;
To connect to your workstation using VNC, you will need a VNC client. **TigerVNC** is a popular choice, though others like RealVNC, TightVNC, and Remmina are also available.&lt;br /&gt;
&lt;br /&gt;
* **Windows:** Download TigerVNC from [https://sourceforge.net/projects/tigervnc/files/stable/1.15.0/ here].&lt;br /&gt;
* **macOS:** Download TigerVNC from [https://tigervnc.macupdate.com/ here].&lt;br /&gt;
* **Linux:** Install TigerVNC via Flatpak:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
flatpak install flathub org.tigervnc.vncviewer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once installed, enter your workstation name followed by a colon and the VNC port (e.g., `station07:5903`).&lt;br /&gt;
&lt;br /&gt;
[[File:Tigervnc.png|Enter your workstation and port here.]]&lt;br /&gt;
&lt;br /&gt;
Click **Connect**, then enter your **VNC session password** (not your account password).&lt;br /&gt;
&lt;br /&gt;
[[File:TigerVNC_password.png|Enter your VNC password here.]]&lt;br /&gt;
&lt;br /&gt;
If successful, you will see your remote desktop.&lt;br /&gt;
&lt;br /&gt;
[[File:GNOME_desktop.png|You are now connected!]]&lt;br /&gt;
&lt;br /&gt;
You can now open a terminal and launch Vivado:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vivado&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Vivado_open.png|Vivado running on remote desktop.]]&lt;br /&gt;
&lt;br /&gt;
== FAQ ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I cannot connect to my VNC session.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A: Connect via SSH and check with `vncserver -list`. Ensure the VNC server was started with `-localhost no`.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I cannot connect via SSH; my workstation appears to be powered off.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A: Some students turn off the stations despite instructions. Contact a system administrator to power it on remotely.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I can connect to VNC, but I see a login screen and an &amp;quot;Authentication error&amp;quot; message.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
A: This is a [https://askubuntu.com/questions/1224957/i-cannot-log-in-a-vnc-session-after-the-screen-locks-authentification-error known bug] in TigerVNC. While connected via VNC, open an SSH session and run&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
loginctl list-sessions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to see the open sessions. Identify your session and run&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
loginctl unlock-session &amp;lt;session-id&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
replacing session-id with the id of your session.&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=FPGA_Student_Classroom_tutorial&amp;diff=8167</id>
		<title>FPGA Student Classroom tutorial</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=FPGA_Student_Classroom_tutorial&amp;diff=8167"/>
		<updated>2025-10-27T08:27:23Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FPGA Student Classroom Access Tutorial ==&lt;br /&gt;
&lt;br /&gt;
This is a guide that aims to show how to connect to the classroom resources remotely, in order to access the software and hardware resources (FPGAs) available at the university. The goal is to create a remote desktop session to one of the computers in the lab. This session can then be used to run software (e.g., Vivado) and to program hardware FPGAs.&lt;br /&gt;
&lt;br /&gt;
Before you can connect to the classroom cluster, you will need to obtain the following from a system administrator:&lt;br /&gt;
&lt;br /&gt;
* A VPN configuration file (e.g., a415-myname.conf)&lt;br /&gt;
* A username and password&lt;br /&gt;
* An assigned workstation and FPGA board (e.g., station07 &amp;amp; pynq07)&lt;br /&gt;
&lt;br /&gt;
The connection process consists of the following steps:&lt;br /&gt;
&lt;br /&gt;
# Connect via VPN to the cluster network.&lt;br /&gt;
# Connect to your workstation via SSH and create a remote desktop (VNC) instance.&lt;br /&gt;
# Connect to the remote desktop instance.&lt;br /&gt;
&lt;br /&gt;
== Step 1 - VPN ==&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster network via VPN, you will need the provided VPN configuration file (e.g., a415-myname.conf) (ask your system administrator if you don&amp;#039;t have it) and a WireGuard VPN client.&lt;br /&gt;
&lt;br /&gt;
For a full guide on how to use the WireGuard VPN, check [https://wiki.dcae.pub.ro/index.php/VPN_Tutorial_-_en this page].&lt;br /&gt;
&lt;br /&gt;
== Step 2 - SSH ==&lt;br /&gt;
&lt;br /&gt;
In order to connect to your workstation via SSH to open the remote desktop instance, you will need a terminal application or a dedicated SSH client.&lt;br /&gt;
&lt;br /&gt;
* **Windows 11** comes with Windows Terminal. Windows 10 users can download the [https://apps.microsoft.com/detail/9n0dx20hk701 Windows Terminal] from the Microsoft Store or use the older Command Prompt application.&lt;br /&gt;
* **macOS** has a pre-installed Terminal application. [https://iterm2.com/ iTerm2] is a popular community alternative with more features.&lt;br /&gt;
&lt;br /&gt;
First, connect to the workstation using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ssh myname@station07&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If this is your first time logging in, you will be prompted to change your initial password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
(myname@station07) Password: &lt;br /&gt;
Password expired. Change your password now.&lt;br /&gt;
(myname@station07) Current Password: &lt;br /&gt;
(myname@station07) New password: &lt;br /&gt;
(myname@station07) Retype new password:&lt;br /&gt;
&lt;br /&gt;
myname@station07:~$&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace `myname` and `station07` with your assigned username and workstation.&lt;br /&gt;
&lt;br /&gt;
Once connected, launch a VNC instance with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -localhost no&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If this is your first time starting a VNC instance, you will be prompted to set a password. You will also need to note the **VNC port** and **X display number** from the output. If you forget these numbers, you can retrieve them with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -list&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To stop the VNC session, use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vncserver -kill :3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace `:3` with your actual X display number.&lt;br /&gt;
&lt;br /&gt;
== Step 3 - VNC ==&lt;br /&gt;
&lt;br /&gt;
To connect to your workstation using VNC, you will need a VNC client. **TigerVNC** is a popular choice, though others like RealVNC, TightVNC, and Remmina are also available.&lt;br /&gt;
&lt;br /&gt;
* **Windows:** Download TigerVNC from [https://sourceforge.net/projects/tigervnc/files/stable/1.15.0/ here].&lt;br /&gt;
* **macOS:** Download TigerVNC from [https://tigervnc.macupdate.com/ here].&lt;br /&gt;
* **Linux:** Install TigerVNC via Flatpak:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
flatpak install flathub org.tigervnc.vncviewer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once installed, enter your workstation name followed by a colon and the VNC port (e.g., `station07:5903`).&lt;br /&gt;
&lt;br /&gt;
[[File:Tigervnc.png|Enter your workstation and port here.]]&lt;br /&gt;
&lt;br /&gt;
Click **Connect**, then enter your **VNC session password** (not your account password).&lt;br /&gt;
&lt;br /&gt;
[[File:TigerVNC_password.png|Enter your VNC password here.]]&lt;br /&gt;
&lt;br /&gt;
If successful, you will see your remote desktop.&lt;br /&gt;
&lt;br /&gt;
[[File:GNOME_desktop.png|You are now connected!]]&lt;br /&gt;
&lt;br /&gt;
You can now open a terminal and launch Vivado:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
vivado&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Vivado_open.png|Vivado running on remote desktop.]]&lt;br /&gt;
&lt;br /&gt;
== FAQ ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I cannot connect to my VNC session.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
A: Connect via SSH and check with `vncserver -list`. Ensure the VNC server was started with `-localhost no`.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I cannot connect via SSH; my workstation appears to be powered off.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
A: Some students turn off the stations despite instructions. Contact a system administrator to power it on remotely.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Q: I can connect to VNC, but I see a login screen and an &amp;quot;Authentication error&amp;quot; message.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
A: This is a [https://askubuntu.com/questions/1224957/i-cannot-log-in-a-vnc-session-after-the-screen-locks-authentification-error known bug] in TigerVNC. While connected via VNC, open an SSH session and run&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
loginctl list-sessions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
to see the open sessions. Identify your session and run&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
loginctl unlock-session &amp;lt;session-id&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
replacing session-id with the id of your session.&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8166</id>
		<title>OS Lab 4 - Users, Groups and Permissions</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8166"/>
		<updated>2025-10-24T15:09:22Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Deliverables and Assessment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs, GIDs, supplementary groups) and how it enforces permissions on files and directories.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Demonstrate&amp;#039;&amp;#039;&amp;#039; how privilege elevation works via effective UIDs/GIDs and setuid/setgid binaries (e.g., via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Describe&amp;#039;&amp;#039;&amp;#039; how user-space conventions (account databases, name service switch) map human-readable names to kernel numeric IDs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Investigate and manipulate&amp;#039;&amp;#039;&amp;#039; these mechanisms on a Linux system to reinforce understanding of the kernel vs. user-space boundary.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In our previous lab, we explored processes as the fundamental unit of execution. Now, we investigate the security model that governs what these processes can &amp;#039;&amp;#039;do&amp;#039;&amp;#039;. This lab dives into the Linux security model, carefully separating the &amp;#039;&amp;#039;&amp;#039;kernel’s&amp;#039;&amp;#039;&amp;#039; rigid, number-based rules from the &amp;#039;&amp;#039;&amp;#039;user-space’s&amp;#039;&amp;#039;&amp;#039; human-friendly conventions layered on top. Understanding this distinction is critical for system administration, security, and software development.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-i-kernel-abstraction-the-real-security-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kernel Abstraction: The Real Security Model ==&lt;br /&gt;
&lt;br /&gt;
This section focuses on how the Linux kernel &amp;#039;&amp;#039;itself&amp;#039;&amp;#039; sees users and permissions. To the kernel, a “user” is just a number (a UID), and security is an algorithm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.a-identities-and-permission-checking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Identities and Permission Checking ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Every process running on a Linux system carries a set of numeric credentials. The most important are:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User ID (UID):&amp;#039;&amp;#039;&amp;#039; A number identifying the user who “owns” the process.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Group ID (GID):&amp;#039;&amp;#039;&amp;#039; A number identifying the process’s primary group.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Supplementary Groups:&amp;#039;&amp;#039;&amp;#039; A set of additional group IDs the process belongs to.&lt;br /&gt;
&lt;br /&gt;
Likewise, every file and directory on the filesystem has metadata stored in its &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, which includes:&lt;br /&gt;
&lt;br /&gt;
* An &amp;#039;&amp;#039;&amp;#039;owner UID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;group GID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Permission bits&amp;#039;&amp;#039;&amp;#039; (the mode), which define access rights for three distinct categories: the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039;, the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039;, and &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
These permissions are the classic &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read), &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write), and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) triplets. Their meaning, however, depends on whether the object is a file or a directory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Permission&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;File&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read the contents of the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can list the contents of the directory (i.e., see filenames).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify or truncate the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can create, delete, or rename files &amp;#039;&amp;#039;within&amp;#039;&amp;#039; the directory.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can execute the file (if it’s a program/script).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can enter (e.g., &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;) the directory and access its inodes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Permission Checking Algorithm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When a process (with its set of IDs) tries to access a file (with its owner/group/mode), the kernel performs a simple, sequential check:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Is the process’s effective UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (superuser/root)?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Access is almost always granted. Stop. (This is a simplification; mechanisms like SELinux can add further checks).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does the process’s effective UID match the file’s owner UID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;rwx&amp;lt;/code&amp;gt;—). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does any of the process’s GIDs (primary or supplementary) match the file’s group GID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;---rwx---&amp;lt;/code&amp;gt;). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;None of the above?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Apply the &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;------rwx&amp;lt;/code&amp;gt;). Access is granted or denied.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;superuser&amp;#039;&amp;#039;&amp;#039; (UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) is special. It bypasses these standard checks. This power allows it to manage the system, but it also carries significant risk.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Capability&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Regular User (UID &amp;amp;gt; 0)&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Superuser (UID = 0)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Read/Write Files&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Restricted by owner/group/other permissions.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read/write &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file, regardless of permissions.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Ownership&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Cannot&amp;#039;&amp;#039;&amp;#039; change file ownership, not even for their own files.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file’s owner and group to &amp;#039;&amp;#039;any&amp;#039;&amp;#039; user/group.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Permissions&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can only change permissions on files they own.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change permissions on &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Bind to Privileged Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Cannot bind to ports 1-1023.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can bind to any port.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;System Configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Limited to own user settings.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify kernel parameters, load modules, change system-wide settings.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: File permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check your identity&lt;br /&gt;
id&lt;br /&gt;
&lt;br /&gt;
# Create an empty file&lt;br /&gt;
touch /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# List file details&lt;br /&gt;
ls -l /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change permissions (numeric)&lt;br /&gt;
chmod 640 /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change ownership (root only)&lt;br /&gt;
sudo chown user:group /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Read a file’s contents&lt;br /&gt;
cat /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username command&lt;br /&gt;
&lt;br /&gt;
# Write text to a file as root&lt;br /&gt;
sudo tee /path/to/filename &amp;lt;&amp;lt;&amp;lt; &amp;quot;text&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Remove a file (as root)&lt;br /&gt;
sudo rm /path/to/filename&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.1-file-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise A: File Permissions ====&lt;br /&gt;
&lt;br /&gt;
Let’s test this model with a file.&lt;br /&gt;
&lt;br /&gt;
# First, check your own identity.&lt;br /&gt;
# Create a new empty file named &amp;lt;code&amp;gt;/tmp/lab4_testfile&amp;lt;/code&amp;gt;.&lt;br /&gt;
# List the file’s details and observe that you are the owner.&lt;br /&gt;
# Change the file’s permissions to &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Change the file’s owner and group to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;. List the details again to confirm.&lt;br /&gt;
# As your normal user, &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to read the file. This should fail. Document the “Permission denied” error.&lt;br /&gt;
# Now, try to read the file as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user. This should also fail.&lt;br /&gt;
# Finally, demonstrate root’s power:&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to write the text “hello from root” into the file, bypassing its permissions.&lt;br /&gt;
#* Read the file using &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to confirm the text is there.&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to change the file’s permissions to &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-------&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* List the file’s details one last time.&lt;br /&gt;
# Clean up by removing the file as root.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Directory permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Create a directory&lt;br /&gt;
mkdir /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Change permissions (symbolic)&lt;br /&gt;
chmod u-x,g+r /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# List the directory itself (not its contents)&lt;br /&gt;
ls -ld /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Enter the directory&lt;br /&gt;
cd /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Remove a directory and all its contents&lt;br /&gt;
rm -r /path/to/dirname&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.2-directory-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise B: Directory Permissions ====&lt;br /&gt;
&lt;br /&gt;
Directory permissions are different. Let’s test the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) bit.&lt;br /&gt;
&lt;br /&gt;
# Create a new directory named &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt; and a file named &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Set the directory’s permissions to &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rwx------&amp;lt;/code&amp;gt;). List the directory’s details to confirm.&lt;br /&gt;
# Verify you can list the directory’s contents and read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Now, remove &amp;#039;&amp;#039;only&amp;#039;&amp;#039; the &amp;#039;&amp;#039;&amp;#039;execute (&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;) bit&amp;#039;&amp;#039;&amp;#039; for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will fail.&lt;br /&gt;
# Try to list the directory’s contents. This will also fail.&lt;br /&gt;
# Try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; using its full path. This will also fail, as you can’t traverse the directory.&lt;br /&gt;
# Now, restore the execute bit (&amp;lt;code&amp;gt;u+x&amp;lt;/code&amp;gt;) but remove the read bit (&amp;lt;code&amp;gt;u-r&amp;lt;/code&amp;gt;) for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;. Its permissions should be &amp;lt;code&amp;gt;--wx------&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to list the directory’s contents. This will fail.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Once inside, try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; (which you know exists). This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Return to the parent directory (&amp;lt;code&amp;gt;cd ..&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Clean up: To remove the directory, you must first restore permissions (e.g., &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt;) so you can delete its contents, then remove the directory and its contents.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.b-privilege-changes-and-sudo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Privilege Changes and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The permission model is static, but a process’s identity can change. The kernel tracks multiple UIDs for each process:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real UID (ruid):&amp;#039;&amp;#039;&amp;#039; The UID of the user who &amp;#039;&amp;#039;launched&amp;#039;&amp;#039; the process. This rarely changes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Effective UID (euid):&amp;#039;&amp;#039;&amp;#039; The UID the kernel &amp;#039;&amp;#039;uses for permission checks&amp;#039;&amp;#039;. This is the ID that matters for file access.&lt;br /&gt;
&lt;br /&gt;
Normally, &amp;lt;code&amp;gt;ruid == euid&amp;lt;/code&amp;gt;. However, a special permission bit on an executable file, the &amp;#039;&amp;#039;&amp;#039;setuid&amp;#039;&amp;#039;&amp;#039; bit, can change this.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Setuid (Set User ID):&amp;#039;&amp;#039;&amp;#039; If an executable file has this bit set (shown as &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; in the &amp;#039;&amp;#039;owner’s&amp;#039;&amp;#039; execute-bit position, e.g., &amp;lt;code&amp;gt;-rwsr-xr-x&amp;lt;/code&amp;gt;), when any user executes it, the kernel sets the new process’s &amp;#039;&amp;#039;&amp;#039;effective UID (euid)&amp;#039;&amp;#039;&amp;#039; to be the &amp;#039;&amp;#039;&amp;#039;UID of the file’s owner&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; command is the classic example:&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (&amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;) is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It has the &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit set.&lt;br /&gt;
# When a normal user (e.g., &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt;) runs &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, the kernel starts the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process with &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt; but &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt; (root).&lt;br /&gt;
# Now running with root’s &amp;#039;&amp;#039;effective&amp;#039;&amp;#039; permissions, the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process is allowed to read its configuration file, &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It checks &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; to see if the &amp;#039;&amp;#039;real&amp;#039;&amp;#039; user (&amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;) is allowed to perform the requested action.&lt;br /&gt;
# If allowed, &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (still running as &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt;) uses its power to launch the target command (e.g., &amp;lt;code&amp;gt;apt update&amp;lt;/code&amp;gt;) as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (or as another specified user).&lt;br /&gt;
&lt;br /&gt;
This mechanism is also used by utilities like &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; (which must run as root to edit &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;login&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; (which start as root to authenticate users, then &amp;#039;&amp;#039;drop&amp;#039;&amp;#039; privileges to become the user who just logged in).&lt;br /&gt;
&lt;br /&gt;
A parallel &amp;#039;&amp;#039;&amp;#039;setgid (Set Group ID)&amp;#039;&amp;#039;&amp;#039; bit exists, which changes the process’s &amp;#039;&amp;#039;&amp;#039;effective GID (egid)&amp;#039;&amp;#039;&amp;#039; to the file’s group. This is less common today, as modern systems tend to give users more flexible supplementary groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Sudo and su ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# List file permissions&lt;br /&gt;
ls -l /path/to/binary&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username id&lt;br /&gt;
&lt;br /&gt;
# Run a command as root (prompts for root password -- this will usually not work, because root&amp;#039;s password is usually not set)&lt;br /&gt;
su -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user (prompts for that user&amp;#039;s password)&lt;br /&gt;
su - username -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a script&lt;br /&gt;
echo &amp;#039;#!/bin/bash&amp;#039; &amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Append to the script&lt;br /&gt;
echo &amp;#039;echo &amp;quot;My eUID is: $(id -u -n)&amp;quot;&amp;#039; &amp;gt;&amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Make the script executable&lt;br /&gt;
chmod +x script.sh&lt;br /&gt;
&lt;br /&gt;
# Set the setuid bit (root only)&lt;br /&gt;
sudo chmod u+s script.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.1-investigating-sudo-and-su&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise C: Investigating &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
# View the permissions and ownership of the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;). Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# View the permissions of the &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/bin/su&amp;lt;/code&amp;gt;). Note if it is also setuid root.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; as your normal user and compare the output.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command, first as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and then as &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the password prompts. Unlike &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (which asks for &amp;#039;&amp;#039;your&amp;#039;&amp;#039; password), &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; asks for the &amp;#039;&amp;#039;target user’s&amp;#039;&amp;#039; password.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.2-setuid-script-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise O1: Setuid Script (Optional) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;(Optional, may be blocked by security policies)&amp;#039;&amp;#039;&amp;#039; Attempt to create a setuid script. Note that for security reasons, most modern systems &amp;#039;&amp;#039;ignore&amp;#039;&amp;#039; the setuid bit on shell scripts. This only works on compiled binaries.&lt;br /&gt;
&lt;br /&gt;
# Create a simple shell script at &amp;lt;code&amp;gt;/tmp/lab4_script.sh&amp;lt;/code&amp;gt; that prints the process’s effective and real user IDs.&lt;br /&gt;
# Make the script executable.&lt;br /&gt;
# As root, change the script’s owner to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and set its &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# List the script’s permissions to confirm the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; is present.&lt;br /&gt;
# Run the script as your normal, non-root user.&lt;br /&gt;
# Observe the output: does the eUID show ‘root’ or your user? On most modern systems, the setuid bit will be ignored for scripts.&lt;br /&gt;
# Clean up the script file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.c-additional-reading-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Additional Reading (Optional) ===&lt;br /&gt;
&lt;br /&gt;
This lab covers the standard security model. For further study, explore these advanced topics that refine or alter this model:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SUID and FSUID:&amp;#039;&amp;#039;&amp;#039; Aside from the &amp;lt;code&amp;gt;ruid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;, each process has two more numeric ids. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;suid&amp;lt;/code&amp;gt; (saved set-user-ID) stores the previous &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt; so a process (e.g., a setuid program) can drop privileges and later regain them safely. Similarly, there is an &amp;lt;code&amp;gt;fsgid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sgid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ACLs (Access Control Lists):&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;getfacl&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setfacl&amp;lt;/code&amp;gt; commands manage permissions for &amp;#039;&amp;#039;specific&amp;#039;&amp;#039; additional users and groups, going beyond the simple owner/group/other model.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SELinux and AppArmor:&amp;#039;&amp;#039;&amp;#039; optional kernel modules that provide stricter access control rules. AppArmor specifies restricitons for particular applications and is easier to slot into existing configurations; it is used by Ubuntu. SELinux provides very complex fine-grain control, but can break deployments which do not take it into account; it is used by Red Hat Enterprise Linux.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities:&amp;#039;&amp;#039;&amp;#039; A way to break root’s “all-or-nothing” power into discrete privileges (e.g., &amp;lt;code&amp;gt;CAP_NET_BIND_SERVICE&amp;lt;/code&amp;gt; allows binding to privileged ports &amp;#039;&amp;#039;without&amp;#039;&amp;#039; granting full root).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User Namespaces:&amp;#039;&amp;#039;&amp;#039; A key container technology that remaps UIDs, allowing a process to &amp;#039;&amp;#039;think&amp;#039;&amp;#039; it’s UID 0 inside its namespace while being a normal UID on the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-ii-user-space-convention-the-human-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== User-Space Convention: The Human Layer ==&lt;br /&gt;
&lt;br /&gt;
The kernel only cares about numbers. This section covers the user-space tools and files that map human-readable names like &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; to the kernel’s numeric UIDs and GIDs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.a-accounts-and-the-password-database&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Accounts and the Password Database ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The kernel knows you as UID 1001, not as &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;. The mapping from name to number (and other metadata) is handled in user-space, primarily by a set of text files in &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;: The main user database. It is &amp;#039;&amp;#039;&amp;#039;world-readable&amp;#039;&amp;#039;&amp;#039; and contains one line per user, with fields separated by colons (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;username:x:UID:GID:Full Name:/home/dir:/shell&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;: Your login name.&lt;br /&gt;
** &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;: A placeholder. In the past, the encrypted password hash was here.&lt;br /&gt;
** &amp;lt;code&amp;gt;UID&amp;lt;/code&amp;gt;: The all-important &amp;#039;&amp;#039;&amp;#039;User ID&amp;#039;&amp;#039;&amp;#039; number. This is what the kernel uses.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The user’s primary &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039; number.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Comment (GECOS):&amp;#039;&amp;#039;&amp;#039; This is a comment field, historically called the GECOS (General Electric Comprehensive Operating System) field. It is a comma-separated list that &amp;#039;&amp;#039;conventionally&amp;#039;&amp;#039; stores the following fields. Most systems today just use the first part for the display name.&lt;br /&gt;
**# User’s Full Name (e.g., &amp;lt;code&amp;gt;John Doe&amp;lt;/code&amp;gt;)&lt;br /&gt;
**# Room Number&lt;br /&gt;
**# Work Phone&lt;br /&gt;
**# Home Phone&lt;br /&gt;
**# Other contact info&lt;br /&gt;
** &amp;lt;code&amp;gt;/home/dir&amp;lt;/code&amp;gt;: The user’s home directory.&lt;br /&gt;
** &amp;lt;code&amp;gt;/shell&amp;lt;/code&amp;gt;: The user’s default login shell (e.g., &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;: Because &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; must be world-readable (so &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; can show names instead of numbers), the sensitive password hashes were moved here. This file is &amp;#039;&amp;#039;&amp;#039;only readable by root&amp;#039;&amp;#039;&amp;#039;. &amp;lt;code&amp;gt;username:password_hash:last_change:etc...&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;: The group database, also world-readable. &amp;lt;code&amp;gt;groupname:x:GID:member_list&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;groupname&amp;lt;/code&amp;gt;: The human-readable group name.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The numeric &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
** &amp;lt;code&amp;gt;member_list&amp;lt;/code&amp;gt;: A comma-separated list of &amp;#039;&amp;#039;additional&amp;#039;&amp;#039; users who are part of this group (for their supplementary groups).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Examples: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View the top of a file (first 10 lines)&lt;br /&gt;
head -n 10 /path/to/file&lt;br /&gt;
&lt;br /&gt;
# View a file as root&lt;br /&gt;
sudo cat /path/to/file&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a group&lt;br /&gt;
getent group &amp;lt;groupname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.1-inspecting-account-databases&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise D: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; user database.&lt;br /&gt;
# Attempt to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; to prove root can read it.&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; database.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; command to query the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; user and for your own user (using the &amp;lt;code&amp;gt;$USER&amp;lt;/code&amp;gt; variable).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to query the &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group and for your own primary group (which you can find from the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View user GECOS info&lt;br /&gt;
finger &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Change your GECOS info interactively (current user)&lt;br /&gt;
chfn&lt;br /&gt;
&lt;br /&gt;
# Change your password (current user)&lt;br /&gt;
passwd&lt;br /&gt;
&lt;br /&gt;
# Find a user’s line in /etc/passwd&lt;br /&gt;
grep &amp;quot;^&amp;lt;username&amp;gt;:&amp;quot; /etc/passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.2-modifying-gecos-and-passwords&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise E: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; command to view your user’s GECOS information.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt; command to interactively change your GECOS information (Full Name, Room Number, etc.). You may be prompted for your password.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; again to see the changes have been applied.&lt;br /&gt;
# Check your entry in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;) to see how the GECOS field (the 5th field) was updated.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; command to change your password. This command securely updates the &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.b-name-service-switch-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Name Service Switch (NSS) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Storing all users in local &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; files doesn’t scale in a large organization. What if you want to log in to 1,000 different machines with the same username and password?&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Name Service Switch (NSS)&amp;#039;&amp;#039;&amp;#039; is a user-space (specifically, C library) mechanism that makes this possible. It allows system functions (like &amp;lt;code&amp;gt;getpwnam()&amp;lt;/code&amp;gt; which “gets password-entry-by-name”) to look for information in multiple sources.&lt;br /&gt;
&lt;br /&gt;
The configuration file is &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. It contains lines like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;passwd:    files systemd ldap&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;group:     files sss&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the system: “When looking up a user (&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;), first check local &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;), then ask the &amp;lt;code&amp;gt;systemd&amp;lt;/code&amp;gt; user database, and if still not found, query the &amp;lt;code&amp;gt;ldap&amp;lt;/code&amp;gt; server.”&lt;br /&gt;
&lt;br /&gt;
This system is powerful because it allows a a system to pull user/group info from network services like &amp;#039;&amp;#039;&amp;#039;LDAP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NIS&amp;#039;&amp;#039;&amp;#039;, or &amp;#039;&amp;#039;&amp;#039;SSS&amp;#039;&amp;#039;&amp;#039; (System Security Services Daemon) without changing any kernel behavior. The kernel &amp;#039;&amp;#039;still&amp;#039;&amp;#039; only receives and operates on the final numeric UID/GID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Print the nsswitch configuration&lt;br /&gt;
cat /etc/nsswitch.conf&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a specific user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.1-inspecting-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise F: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Display the lines from &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; that configure the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; databases (the lines starting with &amp;lt;code&amp;gt;passwd:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test the lookup system by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to resolve the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user and the &amp;lt;code&amp;gt;nogroup&amp;lt;/code&amp;gt; group.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;(Optional, use with extreme care)&amp;#039;&amp;#039;&amp;#039; As root, edit &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sudo nano /etc/nsswitch.conf&amp;lt;/code&amp;gt;). Put a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; at the beginning of the &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt; line to comment it out, and add a new line &amp;lt;code&amp;gt;group: sss&amp;lt;/code&amp;gt; (a service that is likely not running). Save the file.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Now, try to resolve group names using &amp;lt;code&amp;gt;getent group sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt;. Observe that names may now show up as numbers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;IMMEDIATELY&amp;#039;&amp;#039;&amp;#039; restore &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; to its original state and verify &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; works again. This exercise demonstrates how critical this user-space configuration is for the “human-friendly” layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Add a group&lt;br /&gt;
sudo groupadd &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add a user with options (home dir, primary group, supplementary group, login shell)&lt;br /&gt;
sudo useradd -m -g &amp;lt;primary_group&amp;gt; -G &amp;lt;suppl_group&amp;gt; -s /bin/bash &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Set a user’s password&lt;br /&gt;
sudo passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Switch to a user (login shell)&lt;br /&gt;
su - &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a user and their home directory&lt;br /&gt;
sudo userdel -r &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a group&lt;br /&gt;
sudo groupdel &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check the end of the /etc/group file (last 3 lines)&lt;br /&gt;
tail -n 3 /etc/group&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.2-creating-users-and-groups&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise G: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
Let’s use the standard user-space tools to create a new user and group. These tools automatically update the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; files.&lt;br /&gt;
&lt;br /&gt;
# As root, create a new group named &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the group was added by checking the end of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; file and by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As root, create a new user named &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;. When creating them, specify:&lt;br /&gt;
#* Their primary group should be &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* They should be added to the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; supplementary group.&lt;br /&gt;
#* Their home directory should be created (&amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Their login shell should be &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the user was created by querying the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;shadow&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;) databases using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group entries (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;) to confirm &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s membership.&lt;br /&gt;
# Set a password for &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; so they can log in.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to start a new login shell as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Once logged in as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;, run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to check your identity and &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; to see your home directory.&lt;br /&gt;
# &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s shell to return to your original session.&lt;br /&gt;
# Clean up: as root, delete the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; (making sure to remove their home directory) and delete the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; group.&lt;br /&gt;
# Verify they are gone using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-iii-tool-summary&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Tool Summary ==&lt;br /&gt;
&lt;br /&gt;
This lab uses two distinct categories of tools: those that interact directly with the kernel’s security model, and those that manage the user-space databases.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-level-privilege-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel-Level &amp;amp;amp; Privilege Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands directly manipulate kernel-level concepts (file ownership, process UIDs) or are used for privilege escalation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;own&amp;#039;&amp;#039;&amp;#039;er. Changes the UID and GID owner of a file/directory. &amp;#039;&amp;#039;Requires kernel-level privilege (e.g., eUID=0) to change owner to someone else.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;user:group&amp;lt;/code&amp;gt;: Set both user and group&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chgrp&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;gr&amp;#039;&amp;#039;&amp;#039;ou&amp;#039;&amp;#039;&amp;#039;p&amp;#039;&amp;#039;&amp;#039;. Changes the GID owner of a file/directory. &amp;#039;&amp;#039;You must be the owner of the file AND a member of the target group.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Display process identity (UID, GID, groups).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Show UID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;: Show GID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show name instead of number&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser &amp;#039;&amp;#039;&amp;#039;do&amp;#039;&amp;#039;&amp;#039;. Executes a command as another user (default: root) based on &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; rules. Uses setuid.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u user&amp;lt;/code&amp;gt;: Run as &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Start a root login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: List allowed commands&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser. Switches to another user account (default: root). Authenticates using the &amp;#039;&amp;#039;target&amp;#039;&amp;#039; user’s password.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Start a full login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-c &amp;#039;cmd&amp;#039;&amp;lt;/code&amp;gt;: Run a single command&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-space-account-database-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== User-Space Account Database Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands read from or write to the user-space databases (e.g., &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;), which are then interpreted by NSS.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Change a user’s password. Securely updates &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Change password for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Lock account&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Unlock account&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Get ent&amp;#039;&amp;#039;&amp;#039;ries from NSS databases. The correct way to look up users/groups.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;passwd [user]&amp;lt;/code&amp;gt;: Look up user&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;group [group]&amp;lt;/code&amp;gt;: Look up group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;shadow [user]&amp;lt;/code&amp;gt;: Look up shadow entry&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;f&amp;#039;&amp;#039;&amp;#039;i&amp;#039;&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;#039;ger. Modifies the GECOS field in &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Modify for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Displays user information, primarily by parsing the GECOS field.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Show info for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new user account. Updates &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Create home directory&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g group&amp;lt;/code&amp;gt;: Set primary group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-G groups&amp;lt;/code&amp;gt;: Set supplementary groups&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-s shell&amp;lt;/code&amp;gt;: Set login shell&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;groupadd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new group. Updates &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[groupname]&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Command Output:&amp;#039;&amp;#039;&amp;#039; Screenshots of command output for each non-optional hands-on exercise (A-G).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Short written answers to the “Conclusion Questions” below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;conclusion-questions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Please answer the following summary questions based on the lab’s content.&lt;br /&gt;
&lt;br /&gt;
# Explain the kernel’s permission checking algorithm. If a file &amp;lt;code&amp;gt;/data/report.txt&amp;lt;/code&amp;gt; is owned by &amp;lt;code&amp;gt;root:webdev&amp;lt;/code&amp;gt; and has permissions &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;), can the user &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; (who is a member of the &amp;lt;code&amp;gt;webdev&amp;lt;/code&amp;gt; group) &amp;#039;&amp;#039;read&amp;#039;&amp;#039; the file? Why or why not?&lt;br /&gt;
# Why was the sensitive password hash moved from the world-readable &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; to the root-only-readable &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;? What kernel-level permission difference enables this security?&lt;br /&gt;
# Explain the relationship between the user-space tool &amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;, the file &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, the Name Service Switch (NSS), and the kernel’s UID-based permission checks. How do they all interact when you run &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;?&lt;br /&gt;
# What would happen if you (as root) manually edited &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; and changed your user’s UID from &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;5001&amp;lt;/code&amp;gt;, but did &amp;#039;&amp;#039;not&amp;#039;&amp;#039; change the ownership of any files in your home directory (which are all still owned by kernel UID &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt;)? What specific problems would you face at your next login?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8165</id>
		<title>OS Lab 4 - Users, Groups and Permissions</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8165"/>
		<updated>2025-10-24T15:09:09Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Deliverables and Assessment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs, GIDs, supplementary groups) and how it enforces permissions on files and directories.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Demonstrate&amp;#039;&amp;#039;&amp;#039; how privilege elevation works via effective UIDs/GIDs and setuid/setgid binaries (e.g., via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Describe&amp;#039;&amp;#039;&amp;#039; how user-space conventions (account databases, name service switch) map human-readable names to kernel numeric IDs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Investigate and manipulate&amp;#039;&amp;#039;&amp;#039; these mechanisms on a Linux system to reinforce understanding of the kernel vs. user-space boundary.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In our previous lab, we explored processes as the fundamental unit of execution. Now, we investigate the security model that governs what these processes can &amp;#039;&amp;#039;do&amp;#039;&amp;#039;. This lab dives into the Linux security model, carefully separating the &amp;#039;&amp;#039;&amp;#039;kernel’s&amp;#039;&amp;#039;&amp;#039; rigid, number-based rules from the &amp;#039;&amp;#039;&amp;#039;user-space’s&amp;#039;&amp;#039;&amp;#039; human-friendly conventions layered on top. Understanding this distinction is critical for system administration, security, and software development.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-i-kernel-abstraction-the-real-security-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kernel Abstraction: The Real Security Model ==&lt;br /&gt;
&lt;br /&gt;
This section focuses on how the Linux kernel &amp;#039;&amp;#039;itself&amp;#039;&amp;#039; sees users and permissions. To the kernel, a “user” is just a number (a UID), and security is an algorithm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.a-identities-and-permission-checking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Identities and Permission Checking ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Every process running on a Linux system carries a set of numeric credentials. The most important are:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User ID (UID):&amp;#039;&amp;#039;&amp;#039; A number identifying the user who “owns” the process.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Group ID (GID):&amp;#039;&amp;#039;&amp;#039; A number identifying the process’s primary group.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Supplementary Groups:&amp;#039;&amp;#039;&amp;#039; A set of additional group IDs the process belongs to.&lt;br /&gt;
&lt;br /&gt;
Likewise, every file and directory on the filesystem has metadata stored in its &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, which includes:&lt;br /&gt;
&lt;br /&gt;
* An &amp;#039;&amp;#039;&amp;#039;owner UID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;group GID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Permission bits&amp;#039;&amp;#039;&amp;#039; (the mode), which define access rights for three distinct categories: the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039;, the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039;, and &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
These permissions are the classic &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read), &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write), and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) triplets. Their meaning, however, depends on whether the object is a file or a directory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Permission&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;File&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read the contents of the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can list the contents of the directory (i.e., see filenames).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify or truncate the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can create, delete, or rename files &amp;#039;&amp;#039;within&amp;#039;&amp;#039; the directory.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can execute the file (if it’s a program/script).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can enter (e.g., &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;) the directory and access its inodes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Permission Checking Algorithm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When a process (with its set of IDs) tries to access a file (with its owner/group/mode), the kernel performs a simple, sequential check:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Is the process’s effective UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (superuser/root)?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Access is almost always granted. Stop. (This is a simplification; mechanisms like SELinux can add further checks).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does the process’s effective UID match the file’s owner UID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;rwx&amp;lt;/code&amp;gt;—). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does any of the process’s GIDs (primary or supplementary) match the file’s group GID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;---rwx---&amp;lt;/code&amp;gt;). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;None of the above?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Apply the &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;------rwx&amp;lt;/code&amp;gt;). Access is granted or denied.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;superuser&amp;#039;&amp;#039;&amp;#039; (UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) is special. It bypasses these standard checks. This power allows it to manage the system, but it also carries significant risk.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Capability&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Regular User (UID &amp;amp;gt; 0)&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Superuser (UID = 0)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Read/Write Files&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Restricted by owner/group/other permissions.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read/write &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file, regardless of permissions.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Ownership&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Cannot&amp;#039;&amp;#039;&amp;#039; change file ownership, not even for their own files.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file’s owner and group to &amp;#039;&amp;#039;any&amp;#039;&amp;#039; user/group.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Permissions&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can only change permissions on files they own.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change permissions on &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Bind to Privileged Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Cannot bind to ports 1-1023.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can bind to any port.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;System Configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Limited to own user settings.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify kernel parameters, load modules, change system-wide settings.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: File permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check your identity&lt;br /&gt;
id&lt;br /&gt;
&lt;br /&gt;
# Create an empty file&lt;br /&gt;
touch /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# List file details&lt;br /&gt;
ls -l /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change permissions (numeric)&lt;br /&gt;
chmod 640 /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change ownership (root only)&lt;br /&gt;
sudo chown user:group /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Read a file’s contents&lt;br /&gt;
cat /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username command&lt;br /&gt;
&lt;br /&gt;
# Write text to a file as root&lt;br /&gt;
sudo tee /path/to/filename &amp;lt;&amp;lt;&amp;lt; &amp;quot;text&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Remove a file (as root)&lt;br /&gt;
sudo rm /path/to/filename&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.1-file-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise A: File Permissions ====&lt;br /&gt;
&lt;br /&gt;
Let’s test this model with a file.&lt;br /&gt;
&lt;br /&gt;
# First, check your own identity.&lt;br /&gt;
# Create a new empty file named &amp;lt;code&amp;gt;/tmp/lab4_testfile&amp;lt;/code&amp;gt;.&lt;br /&gt;
# List the file’s details and observe that you are the owner.&lt;br /&gt;
# Change the file’s permissions to &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Change the file’s owner and group to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;. List the details again to confirm.&lt;br /&gt;
# As your normal user, &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to read the file. This should fail. Document the “Permission denied” error.&lt;br /&gt;
# Now, try to read the file as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user. This should also fail.&lt;br /&gt;
# Finally, demonstrate root’s power:&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to write the text “hello from root” into the file, bypassing its permissions.&lt;br /&gt;
#* Read the file using &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to confirm the text is there.&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to change the file’s permissions to &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-------&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* List the file’s details one last time.&lt;br /&gt;
# Clean up by removing the file as root.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Directory permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Create a directory&lt;br /&gt;
mkdir /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Change permissions (symbolic)&lt;br /&gt;
chmod u-x,g+r /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# List the directory itself (not its contents)&lt;br /&gt;
ls -ld /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Enter the directory&lt;br /&gt;
cd /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Remove a directory and all its contents&lt;br /&gt;
rm -r /path/to/dirname&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.2-directory-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise B: Directory Permissions ====&lt;br /&gt;
&lt;br /&gt;
Directory permissions are different. Let’s test the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) bit.&lt;br /&gt;
&lt;br /&gt;
# Create a new directory named &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt; and a file named &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Set the directory’s permissions to &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rwx------&amp;lt;/code&amp;gt;). List the directory’s details to confirm.&lt;br /&gt;
# Verify you can list the directory’s contents and read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Now, remove &amp;#039;&amp;#039;only&amp;#039;&amp;#039; the &amp;#039;&amp;#039;&amp;#039;execute (&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;) bit&amp;#039;&amp;#039;&amp;#039; for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will fail.&lt;br /&gt;
# Try to list the directory’s contents. This will also fail.&lt;br /&gt;
# Try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; using its full path. This will also fail, as you can’t traverse the directory.&lt;br /&gt;
# Now, restore the execute bit (&amp;lt;code&amp;gt;u+x&amp;lt;/code&amp;gt;) but remove the read bit (&amp;lt;code&amp;gt;u-r&amp;lt;/code&amp;gt;) for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;. Its permissions should be &amp;lt;code&amp;gt;--wx------&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to list the directory’s contents. This will fail.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Once inside, try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; (which you know exists). This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Return to the parent directory (&amp;lt;code&amp;gt;cd ..&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Clean up: To remove the directory, you must first restore permissions (e.g., &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt;) so you can delete its contents, then remove the directory and its contents.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.b-privilege-changes-and-sudo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Privilege Changes and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The permission model is static, but a process’s identity can change. The kernel tracks multiple UIDs for each process:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real UID (ruid):&amp;#039;&amp;#039;&amp;#039; The UID of the user who &amp;#039;&amp;#039;launched&amp;#039;&amp;#039; the process. This rarely changes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Effective UID (euid):&amp;#039;&amp;#039;&amp;#039; The UID the kernel &amp;#039;&amp;#039;uses for permission checks&amp;#039;&amp;#039;. This is the ID that matters for file access.&lt;br /&gt;
&lt;br /&gt;
Normally, &amp;lt;code&amp;gt;ruid == euid&amp;lt;/code&amp;gt;. However, a special permission bit on an executable file, the &amp;#039;&amp;#039;&amp;#039;setuid&amp;#039;&amp;#039;&amp;#039; bit, can change this.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Setuid (Set User ID):&amp;#039;&amp;#039;&amp;#039; If an executable file has this bit set (shown as &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; in the &amp;#039;&amp;#039;owner’s&amp;#039;&amp;#039; execute-bit position, e.g., &amp;lt;code&amp;gt;-rwsr-xr-x&amp;lt;/code&amp;gt;), when any user executes it, the kernel sets the new process’s &amp;#039;&amp;#039;&amp;#039;effective UID (euid)&amp;#039;&amp;#039;&amp;#039; to be the &amp;#039;&amp;#039;&amp;#039;UID of the file’s owner&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; command is the classic example:&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (&amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;) is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It has the &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit set.&lt;br /&gt;
# When a normal user (e.g., &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt;) runs &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, the kernel starts the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process with &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt; but &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt; (root).&lt;br /&gt;
# Now running with root’s &amp;#039;&amp;#039;effective&amp;#039;&amp;#039; permissions, the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process is allowed to read its configuration file, &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It checks &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; to see if the &amp;#039;&amp;#039;real&amp;#039;&amp;#039; user (&amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;) is allowed to perform the requested action.&lt;br /&gt;
# If allowed, &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (still running as &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt;) uses its power to launch the target command (e.g., &amp;lt;code&amp;gt;apt update&amp;lt;/code&amp;gt;) as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (or as another specified user).&lt;br /&gt;
&lt;br /&gt;
This mechanism is also used by utilities like &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; (which must run as root to edit &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;login&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; (which start as root to authenticate users, then &amp;#039;&amp;#039;drop&amp;#039;&amp;#039; privileges to become the user who just logged in).&lt;br /&gt;
&lt;br /&gt;
A parallel &amp;#039;&amp;#039;&amp;#039;setgid (Set Group ID)&amp;#039;&amp;#039;&amp;#039; bit exists, which changes the process’s &amp;#039;&amp;#039;&amp;#039;effective GID (egid)&amp;#039;&amp;#039;&amp;#039; to the file’s group. This is less common today, as modern systems tend to give users more flexible supplementary groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Sudo and su ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# List file permissions&lt;br /&gt;
ls -l /path/to/binary&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username id&lt;br /&gt;
&lt;br /&gt;
# Run a command as root (prompts for root password -- this will usually not work, because root&amp;#039;s password is usually not set)&lt;br /&gt;
su -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user (prompts for that user&amp;#039;s password)&lt;br /&gt;
su - username -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a script&lt;br /&gt;
echo &amp;#039;#!/bin/bash&amp;#039; &amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Append to the script&lt;br /&gt;
echo &amp;#039;echo &amp;quot;My eUID is: $(id -u -n)&amp;quot;&amp;#039; &amp;gt;&amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Make the script executable&lt;br /&gt;
chmod +x script.sh&lt;br /&gt;
&lt;br /&gt;
# Set the setuid bit (root only)&lt;br /&gt;
sudo chmod u+s script.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.1-investigating-sudo-and-su&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise C: Investigating &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
# View the permissions and ownership of the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;). Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# View the permissions of the &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/bin/su&amp;lt;/code&amp;gt;). Note if it is also setuid root.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; as your normal user and compare the output.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command, first as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and then as &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the password prompts. Unlike &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (which asks for &amp;#039;&amp;#039;your&amp;#039;&amp;#039; password), &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; asks for the &amp;#039;&amp;#039;target user’s&amp;#039;&amp;#039; password.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.2-setuid-script-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise O1: Setuid Script (Optional) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;(Optional, may be blocked by security policies)&amp;#039;&amp;#039;&amp;#039; Attempt to create a setuid script. Note that for security reasons, most modern systems &amp;#039;&amp;#039;ignore&amp;#039;&amp;#039; the setuid bit on shell scripts. This only works on compiled binaries.&lt;br /&gt;
&lt;br /&gt;
# Create a simple shell script at &amp;lt;code&amp;gt;/tmp/lab4_script.sh&amp;lt;/code&amp;gt; that prints the process’s effective and real user IDs.&lt;br /&gt;
# Make the script executable.&lt;br /&gt;
# As root, change the script’s owner to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and set its &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# List the script’s permissions to confirm the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; is present.&lt;br /&gt;
# Run the script as your normal, non-root user.&lt;br /&gt;
# Observe the output: does the eUID show ‘root’ or your user? On most modern systems, the setuid bit will be ignored for scripts.&lt;br /&gt;
# Clean up the script file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.c-additional-reading-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Additional Reading (Optional) ===&lt;br /&gt;
&lt;br /&gt;
This lab covers the standard security model. For further study, explore these advanced topics that refine or alter this model:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SUID and FSUID:&amp;#039;&amp;#039;&amp;#039; Aside from the &amp;lt;code&amp;gt;ruid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;, each process has two more numeric ids. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;suid&amp;lt;/code&amp;gt; (saved set-user-ID) stores the previous &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt; so a process (e.g., a setuid program) can drop privileges and later regain them safely. Similarly, there is an &amp;lt;code&amp;gt;fsgid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sgid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ACLs (Access Control Lists):&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;getfacl&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setfacl&amp;lt;/code&amp;gt; commands manage permissions for &amp;#039;&amp;#039;specific&amp;#039;&amp;#039; additional users and groups, going beyond the simple owner/group/other model.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SELinux and AppArmor:&amp;#039;&amp;#039;&amp;#039; optional kernel modules that provide stricter access control rules. AppArmor specifies restricitons for particular applications and is easier to slot into existing configurations; it is used by Ubuntu. SELinux provides very complex fine-grain control, but can break deployments which do not take it into account; it is used by Red Hat Enterprise Linux.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities:&amp;#039;&amp;#039;&amp;#039; A way to break root’s “all-or-nothing” power into discrete privileges (e.g., &amp;lt;code&amp;gt;CAP_NET_BIND_SERVICE&amp;lt;/code&amp;gt; allows binding to privileged ports &amp;#039;&amp;#039;without&amp;#039;&amp;#039; granting full root).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User Namespaces:&amp;#039;&amp;#039;&amp;#039; A key container technology that remaps UIDs, allowing a process to &amp;#039;&amp;#039;think&amp;#039;&amp;#039; it’s UID 0 inside its namespace while being a normal UID on the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-ii-user-space-convention-the-human-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== User-Space Convention: The Human Layer ==&lt;br /&gt;
&lt;br /&gt;
The kernel only cares about numbers. This section covers the user-space tools and files that map human-readable names like &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; to the kernel’s numeric UIDs and GIDs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.a-accounts-and-the-password-database&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Accounts and the Password Database ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The kernel knows you as UID 1001, not as &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;. The mapping from name to number (and other metadata) is handled in user-space, primarily by a set of text files in &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;: The main user database. It is &amp;#039;&amp;#039;&amp;#039;world-readable&amp;#039;&amp;#039;&amp;#039; and contains one line per user, with fields separated by colons (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;username:x:UID:GID:Full Name:/home/dir:/shell&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;: Your login name.&lt;br /&gt;
** &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;: A placeholder. In the past, the encrypted password hash was here.&lt;br /&gt;
** &amp;lt;code&amp;gt;UID&amp;lt;/code&amp;gt;: The all-important &amp;#039;&amp;#039;&amp;#039;User ID&amp;#039;&amp;#039;&amp;#039; number. This is what the kernel uses.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The user’s primary &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039; number.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Comment (GECOS):&amp;#039;&amp;#039;&amp;#039; This is a comment field, historically called the GECOS (General Electric Comprehensive Operating System) field. It is a comma-separated list that &amp;#039;&amp;#039;conventionally&amp;#039;&amp;#039; stores the following fields. Most systems today just use the first part for the display name.&lt;br /&gt;
**# User’s Full Name (e.g., &amp;lt;code&amp;gt;John Doe&amp;lt;/code&amp;gt;)&lt;br /&gt;
**# Room Number&lt;br /&gt;
**# Work Phone&lt;br /&gt;
**# Home Phone&lt;br /&gt;
**# Other contact info&lt;br /&gt;
** &amp;lt;code&amp;gt;/home/dir&amp;lt;/code&amp;gt;: The user’s home directory.&lt;br /&gt;
** &amp;lt;code&amp;gt;/shell&amp;lt;/code&amp;gt;: The user’s default login shell (e.g., &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;: Because &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; must be world-readable (so &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; can show names instead of numbers), the sensitive password hashes were moved here. This file is &amp;#039;&amp;#039;&amp;#039;only readable by root&amp;#039;&amp;#039;&amp;#039;. &amp;lt;code&amp;gt;username:password_hash:last_change:etc...&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;: The group database, also world-readable. &amp;lt;code&amp;gt;groupname:x:GID:member_list&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;groupname&amp;lt;/code&amp;gt;: The human-readable group name.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The numeric &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
** &amp;lt;code&amp;gt;member_list&amp;lt;/code&amp;gt;: A comma-separated list of &amp;#039;&amp;#039;additional&amp;#039;&amp;#039; users who are part of this group (for their supplementary groups).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Examples: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View the top of a file (first 10 lines)&lt;br /&gt;
head -n 10 /path/to/file&lt;br /&gt;
&lt;br /&gt;
# View a file as root&lt;br /&gt;
sudo cat /path/to/file&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a group&lt;br /&gt;
getent group &amp;lt;groupname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.1-inspecting-account-databases&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise D: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; user database.&lt;br /&gt;
# Attempt to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; to prove root can read it.&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; database.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; command to query the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; user and for your own user (using the &amp;lt;code&amp;gt;$USER&amp;lt;/code&amp;gt; variable).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to query the &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group and for your own primary group (which you can find from the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View user GECOS info&lt;br /&gt;
finger &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Change your GECOS info interactively (current user)&lt;br /&gt;
chfn&lt;br /&gt;
&lt;br /&gt;
# Change your password (current user)&lt;br /&gt;
passwd&lt;br /&gt;
&lt;br /&gt;
# Find a user’s line in /etc/passwd&lt;br /&gt;
grep &amp;quot;^&amp;lt;username&amp;gt;:&amp;quot; /etc/passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.2-modifying-gecos-and-passwords&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise E: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; command to view your user’s GECOS information.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt; command to interactively change your GECOS information (Full Name, Room Number, etc.). You may be prompted for your password.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; again to see the changes have been applied.&lt;br /&gt;
# Check your entry in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;) to see how the GECOS field (the 5th field) was updated.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; command to change your password. This command securely updates the &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.b-name-service-switch-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Name Service Switch (NSS) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Storing all users in local &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; files doesn’t scale in a large organization. What if you want to log in to 1,000 different machines with the same username and password?&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Name Service Switch (NSS)&amp;#039;&amp;#039;&amp;#039; is a user-space (specifically, C library) mechanism that makes this possible. It allows system functions (like &amp;lt;code&amp;gt;getpwnam()&amp;lt;/code&amp;gt; which “gets password-entry-by-name”) to look for information in multiple sources.&lt;br /&gt;
&lt;br /&gt;
The configuration file is &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. It contains lines like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;passwd:    files systemd ldap&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;group:     files sss&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the system: “When looking up a user (&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;), first check local &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;), then ask the &amp;lt;code&amp;gt;systemd&amp;lt;/code&amp;gt; user database, and if still not found, query the &amp;lt;code&amp;gt;ldap&amp;lt;/code&amp;gt; server.”&lt;br /&gt;
&lt;br /&gt;
This system is powerful because it allows a a system to pull user/group info from network services like &amp;#039;&amp;#039;&amp;#039;LDAP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NIS&amp;#039;&amp;#039;&amp;#039;, or &amp;#039;&amp;#039;&amp;#039;SSS&amp;#039;&amp;#039;&amp;#039; (System Security Services Daemon) without changing any kernel behavior. The kernel &amp;#039;&amp;#039;still&amp;#039;&amp;#039; only receives and operates on the final numeric UID/GID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Print the nsswitch configuration&lt;br /&gt;
cat /etc/nsswitch.conf&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a specific user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.1-inspecting-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise F: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Display the lines from &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; that configure the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; databases (the lines starting with &amp;lt;code&amp;gt;passwd:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test the lookup system by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to resolve the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user and the &amp;lt;code&amp;gt;nogroup&amp;lt;/code&amp;gt; group.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;(Optional, use with extreme care)&amp;#039;&amp;#039;&amp;#039; As root, edit &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sudo nano /etc/nsswitch.conf&amp;lt;/code&amp;gt;). Put a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; at the beginning of the &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt; line to comment it out, and add a new line &amp;lt;code&amp;gt;group: sss&amp;lt;/code&amp;gt; (a service that is likely not running). Save the file.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Now, try to resolve group names using &amp;lt;code&amp;gt;getent group sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt;. Observe that names may now show up as numbers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;IMMEDIATELY&amp;#039;&amp;#039;&amp;#039; restore &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; to its original state and verify &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; works again. This exercise demonstrates how critical this user-space configuration is for the “human-friendly” layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Add a group&lt;br /&gt;
sudo groupadd &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add a user with options (home dir, primary group, supplementary group, login shell)&lt;br /&gt;
sudo useradd -m -g &amp;lt;primary_group&amp;gt; -G &amp;lt;suppl_group&amp;gt; -s /bin/bash &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Set a user’s password&lt;br /&gt;
sudo passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Switch to a user (login shell)&lt;br /&gt;
su - &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a user and their home directory&lt;br /&gt;
sudo userdel -r &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a group&lt;br /&gt;
sudo groupdel &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check the end of the /etc/group file (last 3 lines)&lt;br /&gt;
tail -n 3 /etc/group&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.2-creating-users-and-groups&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise G: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
Let’s use the standard user-space tools to create a new user and group. These tools automatically update the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; files.&lt;br /&gt;
&lt;br /&gt;
# As root, create a new group named &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the group was added by checking the end of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; file and by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As root, create a new user named &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;. When creating them, specify:&lt;br /&gt;
#* Their primary group should be &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* They should be added to the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; supplementary group.&lt;br /&gt;
#* Their home directory should be created (&amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Their login shell should be &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the user was created by querying the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;shadow&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;) databases using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group entries (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;) to confirm &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s membership.&lt;br /&gt;
# Set a password for &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; so they can log in.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to start a new login shell as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Once logged in as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;, run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to check your identity and &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; to see your home directory.&lt;br /&gt;
# &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s shell to return to your original session.&lt;br /&gt;
# Clean up: as root, delete the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; (making sure to remove their home directory) and delete the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; group.&lt;br /&gt;
# Verify they are gone using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-iii-tool-summary&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Tool Summary ==&lt;br /&gt;
&lt;br /&gt;
This lab uses two distinct categories of tools: those that interact directly with the kernel’s security model, and those that manage the user-space databases.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-level-privilege-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel-Level &amp;amp;amp; Privilege Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands directly manipulate kernel-level concepts (file ownership, process UIDs) or are used for privilege escalation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;own&amp;#039;&amp;#039;&amp;#039;er. Changes the UID and GID owner of a file/directory. &amp;#039;&amp;#039;Requires kernel-level privilege (e.g., eUID=0) to change owner to someone else.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;user:group&amp;lt;/code&amp;gt;: Set both user and group&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chgrp&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;gr&amp;#039;&amp;#039;&amp;#039;ou&amp;#039;&amp;#039;&amp;#039;p&amp;#039;&amp;#039;&amp;#039;. Changes the GID owner of a file/directory. &amp;#039;&amp;#039;You must be the owner of the file AND a member of the target group.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Display process identity (UID, GID, groups).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Show UID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;: Show GID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show name instead of number&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser &amp;#039;&amp;#039;&amp;#039;do&amp;#039;&amp;#039;&amp;#039;. Executes a command as another user (default: root) based on &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; rules. Uses setuid.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u user&amp;lt;/code&amp;gt;: Run as &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Start a root login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: List allowed commands&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser. Switches to another user account (default: root). Authenticates using the &amp;#039;&amp;#039;target&amp;#039;&amp;#039; user’s password.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Start a full login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-c &amp;#039;cmd&amp;#039;&amp;lt;/code&amp;gt;: Run a single command&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-space-account-database-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== User-Space Account Database Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands read from or write to the user-space databases (e.g., &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;), which are then interpreted by NSS.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Change a user’s password. Securely updates &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Change password for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Lock account&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Unlock account&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Get ent&amp;#039;&amp;#039;&amp;#039;ries from NSS databases. The correct way to look up users/groups.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;passwd [user]&amp;lt;/code&amp;gt;: Look up user&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;group [group]&amp;lt;/code&amp;gt;: Look up group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;shadow [user]&amp;lt;/code&amp;gt;: Look up shadow entry&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;f&amp;#039;&amp;#039;&amp;#039;i&amp;#039;&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;#039;ger. Modifies the GECOS field in &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Modify for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Displays user information, primarily by parsing the GECOS field.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Show info for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new user account. Updates &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Create home directory&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g group&amp;lt;/code&amp;gt;: Set primary group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-G groups&amp;lt;/code&amp;gt;: Set supplementary groups&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-s shell&amp;lt;/code&amp;gt;: Set login shell&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;groupadd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new group. Updates &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[groupname]&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Command Output:&amp;#039;&amp;#039;&amp;#039; Screenshots of command output for each non-optional hands-on exercise (A-G).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Short written answers to the “Conclusion Questions” below.&lt;br /&gt;
Grading will consider the completeness of your outputs, the correctness and clarity of your reasoning in the conclusion answers, and your adherence to instructions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;conclusion-questions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Please answer the following summary questions based on the lab’s content.&lt;br /&gt;
&lt;br /&gt;
# Explain the kernel’s permission checking algorithm. If a file &amp;lt;code&amp;gt;/data/report.txt&amp;lt;/code&amp;gt; is owned by &amp;lt;code&amp;gt;root:webdev&amp;lt;/code&amp;gt; and has permissions &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;), can the user &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; (who is a member of the &amp;lt;code&amp;gt;webdev&amp;lt;/code&amp;gt; group) &amp;#039;&amp;#039;read&amp;#039;&amp;#039; the file? Why or why not?&lt;br /&gt;
# Why was the sensitive password hash moved from the world-readable &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; to the root-only-readable &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;? What kernel-level permission difference enables this security?&lt;br /&gt;
# Explain the relationship between the user-space tool &amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;, the file &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, the Name Service Switch (NSS), and the kernel’s UID-based permission checks. How do they all interact when you run &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;?&lt;br /&gt;
# What would happen if you (as root) manually edited &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; and changed your user’s UID from &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;5001&amp;lt;/code&amp;gt;, but did &amp;#039;&amp;#039;not&amp;#039;&amp;#039; change the ownership of any files in your home directory (which are all still owned by kernel UID &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt;)? What specific problems would you face at your next login?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8164</id>
		<title>OS Lab 4 - Users, Groups and Permissions</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8164"/>
		<updated>2025-10-24T15:08:58Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Deliverables and Assessment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs, GIDs, supplementary groups) and how it enforces permissions on files and directories.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Demonstrate&amp;#039;&amp;#039;&amp;#039; how privilege elevation works via effective UIDs/GIDs and setuid/setgid binaries (e.g., via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Describe&amp;#039;&amp;#039;&amp;#039; how user-space conventions (account databases, name service switch) map human-readable names to kernel numeric IDs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Investigate and manipulate&amp;#039;&amp;#039;&amp;#039; these mechanisms on a Linux system to reinforce understanding of the kernel vs. user-space boundary.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In our previous lab, we explored processes as the fundamental unit of execution. Now, we investigate the security model that governs what these processes can &amp;#039;&amp;#039;do&amp;#039;&amp;#039;. This lab dives into the Linux security model, carefully separating the &amp;#039;&amp;#039;&amp;#039;kernel’s&amp;#039;&amp;#039;&amp;#039; rigid, number-based rules from the &amp;#039;&amp;#039;&amp;#039;user-space’s&amp;#039;&amp;#039;&amp;#039; human-friendly conventions layered on top. Understanding this distinction is critical for system administration, security, and software development.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-i-kernel-abstraction-the-real-security-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kernel Abstraction: The Real Security Model ==&lt;br /&gt;
&lt;br /&gt;
This section focuses on how the Linux kernel &amp;#039;&amp;#039;itself&amp;#039;&amp;#039; sees users and permissions. To the kernel, a “user” is just a number (a UID), and security is an algorithm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.a-identities-and-permission-checking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Identities and Permission Checking ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Every process running on a Linux system carries a set of numeric credentials. The most important are:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User ID (UID):&amp;#039;&amp;#039;&amp;#039; A number identifying the user who “owns” the process.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Group ID (GID):&amp;#039;&amp;#039;&amp;#039; A number identifying the process’s primary group.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Supplementary Groups:&amp;#039;&amp;#039;&amp;#039; A set of additional group IDs the process belongs to.&lt;br /&gt;
&lt;br /&gt;
Likewise, every file and directory on the filesystem has metadata stored in its &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, which includes:&lt;br /&gt;
&lt;br /&gt;
* An &amp;#039;&amp;#039;&amp;#039;owner UID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;group GID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Permission bits&amp;#039;&amp;#039;&amp;#039; (the mode), which define access rights for three distinct categories: the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039;, the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039;, and &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
These permissions are the classic &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read), &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write), and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) triplets. Their meaning, however, depends on whether the object is a file or a directory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Permission&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;File&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read the contents of the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can list the contents of the directory (i.e., see filenames).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify or truncate the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can create, delete, or rename files &amp;#039;&amp;#039;within&amp;#039;&amp;#039; the directory.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can execute the file (if it’s a program/script).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can enter (e.g., &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;) the directory and access its inodes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Permission Checking Algorithm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When a process (with its set of IDs) tries to access a file (with its owner/group/mode), the kernel performs a simple, sequential check:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Is the process’s effective UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (superuser/root)?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Access is almost always granted. Stop. (This is a simplification; mechanisms like SELinux can add further checks).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does the process’s effective UID match the file’s owner UID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;rwx&amp;lt;/code&amp;gt;—). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does any of the process’s GIDs (primary or supplementary) match the file’s group GID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;---rwx---&amp;lt;/code&amp;gt;). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;None of the above?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Apply the &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;------rwx&amp;lt;/code&amp;gt;). Access is granted or denied.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;superuser&amp;#039;&amp;#039;&amp;#039; (UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) is special. It bypasses these standard checks. This power allows it to manage the system, but it also carries significant risk.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Capability&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Regular User (UID &amp;amp;gt; 0)&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Superuser (UID = 0)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Read/Write Files&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Restricted by owner/group/other permissions.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read/write &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file, regardless of permissions.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Ownership&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Cannot&amp;#039;&amp;#039;&amp;#039; change file ownership, not even for their own files.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file’s owner and group to &amp;#039;&amp;#039;any&amp;#039;&amp;#039; user/group.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Permissions&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can only change permissions on files they own.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change permissions on &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Bind to Privileged Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Cannot bind to ports 1-1023.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can bind to any port.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;System Configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Limited to own user settings.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify kernel parameters, load modules, change system-wide settings.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: File permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check your identity&lt;br /&gt;
id&lt;br /&gt;
&lt;br /&gt;
# Create an empty file&lt;br /&gt;
touch /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# List file details&lt;br /&gt;
ls -l /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change permissions (numeric)&lt;br /&gt;
chmod 640 /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change ownership (root only)&lt;br /&gt;
sudo chown user:group /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Read a file’s contents&lt;br /&gt;
cat /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username command&lt;br /&gt;
&lt;br /&gt;
# Write text to a file as root&lt;br /&gt;
sudo tee /path/to/filename &amp;lt;&amp;lt;&amp;lt; &amp;quot;text&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Remove a file (as root)&lt;br /&gt;
sudo rm /path/to/filename&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.1-file-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise A: File Permissions ====&lt;br /&gt;
&lt;br /&gt;
Let’s test this model with a file.&lt;br /&gt;
&lt;br /&gt;
# First, check your own identity.&lt;br /&gt;
# Create a new empty file named &amp;lt;code&amp;gt;/tmp/lab4_testfile&amp;lt;/code&amp;gt;.&lt;br /&gt;
# List the file’s details and observe that you are the owner.&lt;br /&gt;
# Change the file’s permissions to &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Change the file’s owner and group to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;. List the details again to confirm.&lt;br /&gt;
# As your normal user, &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to read the file. This should fail. Document the “Permission denied” error.&lt;br /&gt;
# Now, try to read the file as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user. This should also fail.&lt;br /&gt;
# Finally, demonstrate root’s power:&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to write the text “hello from root” into the file, bypassing its permissions.&lt;br /&gt;
#* Read the file using &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to confirm the text is there.&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to change the file’s permissions to &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-------&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* List the file’s details one last time.&lt;br /&gt;
# Clean up by removing the file as root.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Directory permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Create a directory&lt;br /&gt;
mkdir /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Change permissions (symbolic)&lt;br /&gt;
chmod u-x,g+r /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# List the directory itself (not its contents)&lt;br /&gt;
ls -ld /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Enter the directory&lt;br /&gt;
cd /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Remove a directory and all its contents&lt;br /&gt;
rm -r /path/to/dirname&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.2-directory-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise B: Directory Permissions ====&lt;br /&gt;
&lt;br /&gt;
Directory permissions are different. Let’s test the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) bit.&lt;br /&gt;
&lt;br /&gt;
# Create a new directory named &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt; and a file named &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Set the directory’s permissions to &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rwx------&amp;lt;/code&amp;gt;). List the directory’s details to confirm.&lt;br /&gt;
# Verify you can list the directory’s contents and read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Now, remove &amp;#039;&amp;#039;only&amp;#039;&amp;#039; the &amp;#039;&amp;#039;&amp;#039;execute (&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;) bit&amp;#039;&amp;#039;&amp;#039; for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will fail.&lt;br /&gt;
# Try to list the directory’s contents. This will also fail.&lt;br /&gt;
# Try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; using its full path. This will also fail, as you can’t traverse the directory.&lt;br /&gt;
# Now, restore the execute bit (&amp;lt;code&amp;gt;u+x&amp;lt;/code&amp;gt;) but remove the read bit (&amp;lt;code&amp;gt;u-r&amp;lt;/code&amp;gt;) for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;. Its permissions should be &amp;lt;code&amp;gt;--wx------&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to list the directory’s contents. This will fail.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Once inside, try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; (which you know exists). This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Return to the parent directory (&amp;lt;code&amp;gt;cd ..&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Clean up: To remove the directory, you must first restore permissions (e.g., &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt;) so you can delete its contents, then remove the directory and its contents.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.b-privilege-changes-and-sudo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Privilege Changes and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The permission model is static, but a process’s identity can change. The kernel tracks multiple UIDs for each process:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real UID (ruid):&amp;#039;&amp;#039;&amp;#039; The UID of the user who &amp;#039;&amp;#039;launched&amp;#039;&amp;#039; the process. This rarely changes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Effective UID (euid):&amp;#039;&amp;#039;&amp;#039; The UID the kernel &amp;#039;&amp;#039;uses for permission checks&amp;#039;&amp;#039;. This is the ID that matters for file access.&lt;br /&gt;
&lt;br /&gt;
Normally, &amp;lt;code&amp;gt;ruid == euid&amp;lt;/code&amp;gt;. However, a special permission bit on an executable file, the &amp;#039;&amp;#039;&amp;#039;setuid&amp;#039;&amp;#039;&amp;#039; bit, can change this.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Setuid (Set User ID):&amp;#039;&amp;#039;&amp;#039; If an executable file has this bit set (shown as &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; in the &amp;#039;&amp;#039;owner’s&amp;#039;&amp;#039; execute-bit position, e.g., &amp;lt;code&amp;gt;-rwsr-xr-x&amp;lt;/code&amp;gt;), when any user executes it, the kernel sets the new process’s &amp;#039;&amp;#039;&amp;#039;effective UID (euid)&amp;#039;&amp;#039;&amp;#039; to be the &amp;#039;&amp;#039;&amp;#039;UID of the file’s owner&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; command is the classic example:&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (&amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;) is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It has the &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit set.&lt;br /&gt;
# When a normal user (e.g., &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt;) runs &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, the kernel starts the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process with &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt; but &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt; (root).&lt;br /&gt;
# Now running with root’s &amp;#039;&amp;#039;effective&amp;#039;&amp;#039; permissions, the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process is allowed to read its configuration file, &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It checks &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; to see if the &amp;#039;&amp;#039;real&amp;#039;&amp;#039; user (&amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;) is allowed to perform the requested action.&lt;br /&gt;
# If allowed, &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (still running as &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt;) uses its power to launch the target command (e.g., &amp;lt;code&amp;gt;apt update&amp;lt;/code&amp;gt;) as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (or as another specified user).&lt;br /&gt;
&lt;br /&gt;
This mechanism is also used by utilities like &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; (which must run as root to edit &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;login&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; (which start as root to authenticate users, then &amp;#039;&amp;#039;drop&amp;#039;&amp;#039; privileges to become the user who just logged in).&lt;br /&gt;
&lt;br /&gt;
A parallel &amp;#039;&amp;#039;&amp;#039;setgid (Set Group ID)&amp;#039;&amp;#039;&amp;#039; bit exists, which changes the process’s &amp;#039;&amp;#039;&amp;#039;effective GID (egid)&amp;#039;&amp;#039;&amp;#039; to the file’s group. This is less common today, as modern systems tend to give users more flexible supplementary groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Sudo and su ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# List file permissions&lt;br /&gt;
ls -l /path/to/binary&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username id&lt;br /&gt;
&lt;br /&gt;
# Run a command as root (prompts for root password -- this will usually not work, because root&amp;#039;s password is usually not set)&lt;br /&gt;
su -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user (prompts for that user&amp;#039;s password)&lt;br /&gt;
su - username -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a script&lt;br /&gt;
echo &amp;#039;#!/bin/bash&amp;#039; &amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Append to the script&lt;br /&gt;
echo &amp;#039;echo &amp;quot;My eUID is: $(id -u -n)&amp;quot;&amp;#039; &amp;gt;&amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Make the script executable&lt;br /&gt;
chmod +x script.sh&lt;br /&gt;
&lt;br /&gt;
# Set the setuid bit (root only)&lt;br /&gt;
sudo chmod u+s script.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.1-investigating-sudo-and-su&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise C: Investigating &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
# View the permissions and ownership of the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;). Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# View the permissions of the &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/bin/su&amp;lt;/code&amp;gt;). Note if it is also setuid root.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; as your normal user and compare the output.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command, first as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and then as &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the password prompts. Unlike &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (which asks for &amp;#039;&amp;#039;your&amp;#039;&amp;#039; password), &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; asks for the &amp;#039;&amp;#039;target user’s&amp;#039;&amp;#039; password.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.2-setuid-script-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise O1: Setuid Script (Optional) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;(Optional, may be blocked by security policies)&amp;#039;&amp;#039;&amp;#039; Attempt to create a setuid script. Note that for security reasons, most modern systems &amp;#039;&amp;#039;ignore&amp;#039;&amp;#039; the setuid bit on shell scripts. This only works on compiled binaries.&lt;br /&gt;
&lt;br /&gt;
# Create a simple shell script at &amp;lt;code&amp;gt;/tmp/lab4_script.sh&amp;lt;/code&amp;gt; that prints the process’s effective and real user IDs.&lt;br /&gt;
# Make the script executable.&lt;br /&gt;
# As root, change the script’s owner to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and set its &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# List the script’s permissions to confirm the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; is present.&lt;br /&gt;
# Run the script as your normal, non-root user.&lt;br /&gt;
# Observe the output: does the eUID show ‘root’ or your user? On most modern systems, the setuid bit will be ignored for scripts.&lt;br /&gt;
# Clean up the script file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.c-additional-reading-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Additional Reading (Optional) ===&lt;br /&gt;
&lt;br /&gt;
This lab covers the standard security model. For further study, explore these advanced topics that refine or alter this model:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SUID and FSUID:&amp;#039;&amp;#039;&amp;#039; Aside from the &amp;lt;code&amp;gt;ruid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;, each process has two more numeric ids. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;suid&amp;lt;/code&amp;gt; (saved set-user-ID) stores the previous &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt; so a process (e.g., a setuid program) can drop privileges and later regain them safely. Similarly, there is an &amp;lt;code&amp;gt;fsgid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sgid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ACLs (Access Control Lists):&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;getfacl&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setfacl&amp;lt;/code&amp;gt; commands manage permissions for &amp;#039;&amp;#039;specific&amp;#039;&amp;#039; additional users and groups, going beyond the simple owner/group/other model.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SELinux and AppArmor:&amp;#039;&amp;#039;&amp;#039; optional kernel modules that provide stricter access control rules. AppArmor specifies restricitons for particular applications and is easier to slot into existing configurations; it is used by Ubuntu. SELinux provides very complex fine-grain control, but can break deployments which do not take it into account; it is used by Red Hat Enterprise Linux.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities:&amp;#039;&amp;#039;&amp;#039; A way to break root’s “all-or-nothing” power into discrete privileges (e.g., &amp;lt;code&amp;gt;CAP_NET_BIND_SERVICE&amp;lt;/code&amp;gt; allows binding to privileged ports &amp;#039;&amp;#039;without&amp;#039;&amp;#039; granting full root).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User Namespaces:&amp;#039;&amp;#039;&amp;#039; A key container technology that remaps UIDs, allowing a process to &amp;#039;&amp;#039;think&amp;#039;&amp;#039; it’s UID 0 inside its namespace while being a normal UID on the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-ii-user-space-convention-the-human-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== User-Space Convention: The Human Layer ==&lt;br /&gt;
&lt;br /&gt;
The kernel only cares about numbers. This section covers the user-space tools and files that map human-readable names like &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; to the kernel’s numeric UIDs and GIDs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.a-accounts-and-the-password-database&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Accounts and the Password Database ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The kernel knows you as UID 1001, not as &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;. The mapping from name to number (and other metadata) is handled in user-space, primarily by a set of text files in &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;: The main user database. It is &amp;#039;&amp;#039;&amp;#039;world-readable&amp;#039;&amp;#039;&amp;#039; and contains one line per user, with fields separated by colons (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;username:x:UID:GID:Full Name:/home/dir:/shell&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;: Your login name.&lt;br /&gt;
** &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;: A placeholder. In the past, the encrypted password hash was here.&lt;br /&gt;
** &amp;lt;code&amp;gt;UID&amp;lt;/code&amp;gt;: The all-important &amp;#039;&amp;#039;&amp;#039;User ID&amp;#039;&amp;#039;&amp;#039; number. This is what the kernel uses.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The user’s primary &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039; number.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Comment (GECOS):&amp;#039;&amp;#039;&amp;#039; This is a comment field, historically called the GECOS (General Electric Comprehensive Operating System) field. It is a comma-separated list that &amp;#039;&amp;#039;conventionally&amp;#039;&amp;#039; stores the following fields. Most systems today just use the first part for the display name.&lt;br /&gt;
**# User’s Full Name (e.g., &amp;lt;code&amp;gt;John Doe&amp;lt;/code&amp;gt;)&lt;br /&gt;
**# Room Number&lt;br /&gt;
**# Work Phone&lt;br /&gt;
**# Home Phone&lt;br /&gt;
**# Other contact info&lt;br /&gt;
** &amp;lt;code&amp;gt;/home/dir&amp;lt;/code&amp;gt;: The user’s home directory.&lt;br /&gt;
** &amp;lt;code&amp;gt;/shell&amp;lt;/code&amp;gt;: The user’s default login shell (e.g., &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;: Because &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; must be world-readable (so &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; can show names instead of numbers), the sensitive password hashes were moved here. This file is &amp;#039;&amp;#039;&amp;#039;only readable by root&amp;#039;&amp;#039;&amp;#039;. &amp;lt;code&amp;gt;username:password_hash:last_change:etc...&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;: The group database, also world-readable. &amp;lt;code&amp;gt;groupname:x:GID:member_list&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;groupname&amp;lt;/code&amp;gt;: The human-readable group name.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The numeric &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
** &amp;lt;code&amp;gt;member_list&amp;lt;/code&amp;gt;: A comma-separated list of &amp;#039;&amp;#039;additional&amp;#039;&amp;#039; users who are part of this group (for their supplementary groups).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Examples: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View the top of a file (first 10 lines)&lt;br /&gt;
head -n 10 /path/to/file&lt;br /&gt;
&lt;br /&gt;
# View a file as root&lt;br /&gt;
sudo cat /path/to/file&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a group&lt;br /&gt;
getent group &amp;lt;groupname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.1-inspecting-account-databases&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise D: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; user database.&lt;br /&gt;
# Attempt to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; to prove root can read it.&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; database.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; command to query the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; user and for your own user (using the &amp;lt;code&amp;gt;$USER&amp;lt;/code&amp;gt; variable).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to query the &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group and for your own primary group (which you can find from the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View user GECOS info&lt;br /&gt;
finger &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Change your GECOS info interactively (current user)&lt;br /&gt;
chfn&lt;br /&gt;
&lt;br /&gt;
# Change your password (current user)&lt;br /&gt;
passwd&lt;br /&gt;
&lt;br /&gt;
# Find a user’s line in /etc/passwd&lt;br /&gt;
grep &amp;quot;^&amp;lt;username&amp;gt;:&amp;quot; /etc/passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.2-modifying-gecos-and-passwords&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise E: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; command to view your user’s GECOS information.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt; command to interactively change your GECOS information (Full Name, Room Number, etc.). You may be prompted for your password.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; again to see the changes have been applied.&lt;br /&gt;
# Check your entry in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;) to see how the GECOS field (the 5th field) was updated.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; command to change your password. This command securely updates the &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.b-name-service-switch-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Name Service Switch (NSS) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Storing all users in local &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; files doesn’t scale in a large organization. What if you want to log in to 1,000 different machines with the same username and password?&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Name Service Switch (NSS)&amp;#039;&amp;#039;&amp;#039; is a user-space (specifically, C library) mechanism that makes this possible. It allows system functions (like &amp;lt;code&amp;gt;getpwnam()&amp;lt;/code&amp;gt; which “gets password-entry-by-name”) to look for information in multiple sources.&lt;br /&gt;
&lt;br /&gt;
The configuration file is &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. It contains lines like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;passwd:    files systemd ldap&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;group:     files sss&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the system: “When looking up a user (&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;), first check local &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;), then ask the &amp;lt;code&amp;gt;systemd&amp;lt;/code&amp;gt; user database, and if still not found, query the &amp;lt;code&amp;gt;ldap&amp;lt;/code&amp;gt; server.”&lt;br /&gt;
&lt;br /&gt;
This system is powerful because it allows a a system to pull user/group info from network services like &amp;#039;&amp;#039;&amp;#039;LDAP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NIS&amp;#039;&amp;#039;&amp;#039;, or &amp;#039;&amp;#039;&amp;#039;SSS&amp;#039;&amp;#039;&amp;#039; (System Security Services Daemon) without changing any kernel behavior. The kernel &amp;#039;&amp;#039;still&amp;#039;&amp;#039; only receives and operates on the final numeric UID/GID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Print the nsswitch configuration&lt;br /&gt;
cat /etc/nsswitch.conf&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a specific user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.1-inspecting-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise F: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Display the lines from &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; that configure the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; databases (the lines starting with &amp;lt;code&amp;gt;passwd:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test the lookup system by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to resolve the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user and the &amp;lt;code&amp;gt;nogroup&amp;lt;/code&amp;gt; group.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;(Optional, use with extreme care)&amp;#039;&amp;#039;&amp;#039; As root, edit &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sudo nano /etc/nsswitch.conf&amp;lt;/code&amp;gt;). Put a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; at the beginning of the &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt; line to comment it out, and add a new line &amp;lt;code&amp;gt;group: sss&amp;lt;/code&amp;gt; (a service that is likely not running). Save the file.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Now, try to resolve group names using &amp;lt;code&amp;gt;getent group sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt;. Observe that names may now show up as numbers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;IMMEDIATELY&amp;#039;&amp;#039;&amp;#039; restore &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; to its original state and verify &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; works again. This exercise demonstrates how critical this user-space configuration is for the “human-friendly” layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Add a group&lt;br /&gt;
sudo groupadd &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add a user with options (home dir, primary group, supplementary group, login shell)&lt;br /&gt;
sudo useradd -m -g &amp;lt;primary_group&amp;gt; -G &amp;lt;suppl_group&amp;gt; -s /bin/bash &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Set a user’s password&lt;br /&gt;
sudo passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Switch to a user (login shell)&lt;br /&gt;
su - &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a user and their home directory&lt;br /&gt;
sudo userdel -r &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a group&lt;br /&gt;
sudo groupdel &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check the end of the /etc/group file (last 3 lines)&lt;br /&gt;
tail -n 3 /etc/group&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.2-creating-users-and-groups&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise G: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
Let’s use the standard user-space tools to create a new user and group. These tools automatically update the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; files.&lt;br /&gt;
&lt;br /&gt;
# As root, create a new group named &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the group was added by checking the end of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; file and by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As root, create a new user named &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;. When creating them, specify:&lt;br /&gt;
#* Their primary group should be &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* They should be added to the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; supplementary group.&lt;br /&gt;
#* Their home directory should be created (&amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Their login shell should be &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the user was created by querying the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;shadow&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;) databases using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group entries (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;) to confirm &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s membership.&lt;br /&gt;
# Set a password for &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; so they can log in.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to start a new login shell as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Once logged in as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;, run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to check your identity and &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; to see your home directory.&lt;br /&gt;
# &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s shell to return to your original session.&lt;br /&gt;
# Clean up: as root, delete the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; (making sure to remove their home directory) and delete the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; group.&lt;br /&gt;
# Verify they are gone using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-iii-tool-summary&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Tool Summary ==&lt;br /&gt;
&lt;br /&gt;
This lab uses two distinct categories of tools: those that interact directly with the kernel’s security model, and those that manage the user-space databases.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-level-privilege-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel-Level &amp;amp;amp; Privilege Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands directly manipulate kernel-level concepts (file ownership, process UIDs) or are used for privilege escalation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;own&amp;#039;&amp;#039;&amp;#039;er. Changes the UID and GID owner of a file/directory. &amp;#039;&amp;#039;Requires kernel-level privilege (e.g., eUID=0) to change owner to someone else.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;user:group&amp;lt;/code&amp;gt;: Set both user and group&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chgrp&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;gr&amp;#039;&amp;#039;&amp;#039;ou&amp;#039;&amp;#039;&amp;#039;p&amp;#039;&amp;#039;&amp;#039;. Changes the GID owner of a file/directory. &amp;#039;&amp;#039;You must be the owner of the file AND a member of the target group.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Display process identity (UID, GID, groups).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Show UID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;: Show GID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show name instead of number&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser &amp;#039;&amp;#039;&amp;#039;do&amp;#039;&amp;#039;&amp;#039;. Executes a command as another user (default: root) based on &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; rules. Uses setuid.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u user&amp;lt;/code&amp;gt;: Run as &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Start a root login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: List allowed commands&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser. Switches to another user account (default: root). Authenticates using the &amp;#039;&amp;#039;target&amp;#039;&amp;#039; user’s password.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Start a full login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-c &amp;#039;cmd&amp;#039;&amp;lt;/code&amp;gt;: Run a single command&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-space-account-database-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== User-Space Account Database Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands read from or write to the user-space databases (e.g., &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;), which are then interpreted by NSS.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Change a user’s password. Securely updates &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Change password for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Lock account&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Unlock account&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Get ent&amp;#039;&amp;#039;&amp;#039;ries from NSS databases. The correct way to look up users/groups.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;passwd [user]&amp;lt;/code&amp;gt;: Look up user&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;group [group]&amp;lt;/code&amp;gt;: Look up group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;shadow [user]&amp;lt;/code&amp;gt;: Look up shadow entry&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;f&amp;#039;&amp;#039;&amp;#039;i&amp;#039;&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;#039;ger. Modifies the GECOS field in &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Modify for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Displays user information, primarily by parsing the GECOS field.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Show info for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new user account. Updates &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Create home directory&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g group&amp;lt;/code&amp;gt;: Set primary group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-G groups&amp;lt;/code&amp;gt;: Set supplementary groups&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-s shell&amp;lt;/code&amp;gt;: Set login shell&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;groupadd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new group. Updates &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[groupname]&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Command Output:&amp;#039;&amp;#039;&amp;#039; Screenshots of command output for each non-optional hands-on exercise (A-G), clearly labelled by section.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Short written answers to the “Conclusion Questions” below.&lt;br /&gt;
Grading will consider the completeness of your outputs, the correctness and clarity of your reasoning in the conclusion answers, and your adherence to instructions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;conclusion-questions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Please answer the following summary questions based on the lab’s content.&lt;br /&gt;
&lt;br /&gt;
# Explain the kernel’s permission checking algorithm. If a file &amp;lt;code&amp;gt;/data/report.txt&amp;lt;/code&amp;gt; is owned by &amp;lt;code&amp;gt;root:webdev&amp;lt;/code&amp;gt; and has permissions &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;), can the user &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; (who is a member of the &amp;lt;code&amp;gt;webdev&amp;lt;/code&amp;gt; group) &amp;#039;&amp;#039;read&amp;#039;&amp;#039; the file? Why or why not?&lt;br /&gt;
# Why was the sensitive password hash moved from the world-readable &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; to the root-only-readable &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;? What kernel-level permission difference enables this security?&lt;br /&gt;
# Explain the relationship between the user-space tool &amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;, the file &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, the Name Service Switch (NSS), and the kernel’s UID-based permission checks. How do they all interact when you run &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;?&lt;br /&gt;
# What would happen if you (as root) manually edited &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; and changed your user’s UID from &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;5001&amp;lt;/code&amp;gt;, but did &amp;#039;&amp;#039;not&amp;#039;&amp;#039; change the ownership of any files in your home directory (which are all still owned by kernel UID &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt;)? What specific problems would you face at your next login?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8163</id>
		<title>OS Lab 4 - Users, Groups and Permissions</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8163"/>
		<updated>2025-10-24T13:56:59Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Background */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs, GIDs, supplementary groups) and how it enforces permissions on files and directories.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Demonstrate&amp;#039;&amp;#039;&amp;#039; how privilege elevation works via effective UIDs/GIDs and setuid/setgid binaries (e.g., via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Describe&amp;#039;&amp;#039;&amp;#039; how user-space conventions (account databases, name service switch) map human-readable names to kernel numeric IDs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Investigate and manipulate&amp;#039;&amp;#039;&amp;#039; these mechanisms on a Linux system to reinforce understanding of the kernel vs. user-space boundary.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In our previous lab, we explored processes as the fundamental unit of execution. Now, we investigate the security model that governs what these processes can &amp;#039;&amp;#039;do&amp;#039;&amp;#039;. This lab dives into the Linux security model, carefully separating the &amp;#039;&amp;#039;&amp;#039;kernel’s&amp;#039;&amp;#039;&amp;#039; rigid, number-based rules from the &amp;#039;&amp;#039;&amp;#039;user-space’s&amp;#039;&amp;#039;&amp;#039; human-friendly conventions layered on top. Understanding this distinction is critical for system administration, security, and software development.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-i-kernel-abstraction-the-real-security-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kernel Abstraction: The Real Security Model ==&lt;br /&gt;
&lt;br /&gt;
This section focuses on how the Linux kernel &amp;#039;&amp;#039;itself&amp;#039;&amp;#039; sees users and permissions. To the kernel, a “user” is just a number (a UID), and security is an algorithm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.a-identities-and-permission-checking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Identities and Permission Checking ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Every process running on a Linux system carries a set of numeric credentials. The most important are:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User ID (UID):&amp;#039;&amp;#039;&amp;#039; A number identifying the user who “owns” the process.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Group ID (GID):&amp;#039;&amp;#039;&amp;#039; A number identifying the process’s primary group.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Supplementary Groups:&amp;#039;&amp;#039;&amp;#039; A set of additional group IDs the process belongs to.&lt;br /&gt;
&lt;br /&gt;
Likewise, every file and directory on the filesystem has metadata stored in its &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, which includes:&lt;br /&gt;
&lt;br /&gt;
* An &amp;#039;&amp;#039;&amp;#039;owner UID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;group GID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Permission bits&amp;#039;&amp;#039;&amp;#039; (the mode), which define access rights for three distinct categories: the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039;, the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039;, and &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
These permissions are the classic &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read), &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write), and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) triplets. Their meaning, however, depends on whether the object is a file or a directory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Permission&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;File&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read the contents of the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can list the contents of the directory (i.e., see filenames).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify or truncate the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can create, delete, or rename files &amp;#039;&amp;#039;within&amp;#039;&amp;#039; the directory.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can execute the file (if it’s a program/script).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can enter (e.g., &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;) the directory and access its inodes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Permission Checking Algorithm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When a process (with its set of IDs) tries to access a file (with its owner/group/mode), the kernel performs a simple, sequential check:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Is the process’s effective UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (superuser/root)?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Access is almost always granted. Stop. (This is a simplification; mechanisms like SELinux can add further checks).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does the process’s effective UID match the file’s owner UID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;rwx&amp;lt;/code&amp;gt;—). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does any of the process’s GIDs (primary or supplementary) match the file’s group GID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;---rwx---&amp;lt;/code&amp;gt;). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;None of the above?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Apply the &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;------rwx&amp;lt;/code&amp;gt;). Access is granted or denied.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;superuser&amp;#039;&amp;#039;&amp;#039; (UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) is special. It bypasses these standard checks. This power allows it to manage the system, but it also carries significant risk.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Capability&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Regular User (UID &amp;amp;gt; 0)&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Superuser (UID = 0)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Read/Write Files&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Restricted by owner/group/other permissions.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read/write &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file, regardless of permissions.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Ownership&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Cannot&amp;#039;&amp;#039;&amp;#039; change file ownership, not even for their own files.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file’s owner and group to &amp;#039;&amp;#039;any&amp;#039;&amp;#039; user/group.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Permissions&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can only change permissions on files they own.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change permissions on &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Bind to Privileged Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Cannot bind to ports 1-1023.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can bind to any port.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;System Configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Limited to own user settings.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify kernel parameters, load modules, change system-wide settings.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: File permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check your identity&lt;br /&gt;
id&lt;br /&gt;
&lt;br /&gt;
# Create an empty file&lt;br /&gt;
touch /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# List file details&lt;br /&gt;
ls -l /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change permissions (numeric)&lt;br /&gt;
chmod 640 /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change ownership (root only)&lt;br /&gt;
sudo chown user:group /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Read a file’s contents&lt;br /&gt;
cat /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username command&lt;br /&gt;
&lt;br /&gt;
# Write text to a file as root&lt;br /&gt;
sudo tee /path/to/filename &amp;lt;&amp;lt;&amp;lt; &amp;quot;text&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Remove a file (as root)&lt;br /&gt;
sudo rm /path/to/filename&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.1-file-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise A: File Permissions ====&lt;br /&gt;
&lt;br /&gt;
Let’s test this model with a file.&lt;br /&gt;
&lt;br /&gt;
# First, check your own identity.&lt;br /&gt;
# Create a new empty file named &amp;lt;code&amp;gt;/tmp/lab4_testfile&amp;lt;/code&amp;gt;.&lt;br /&gt;
# List the file’s details and observe that you are the owner.&lt;br /&gt;
# Change the file’s permissions to &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Change the file’s owner and group to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;. List the details again to confirm.&lt;br /&gt;
# As your normal user, &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to read the file. This should fail. Document the “Permission denied” error.&lt;br /&gt;
# Now, try to read the file as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user. This should also fail.&lt;br /&gt;
# Finally, demonstrate root’s power:&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to write the text “hello from root” into the file, bypassing its permissions.&lt;br /&gt;
#* Read the file using &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to confirm the text is there.&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to change the file’s permissions to &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-------&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* List the file’s details one last time.&lt;br /&gt;
# Clean up by removing the file as root.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Directory permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Create a directory&lt;br /&gt;
mkdir /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Change permissions (symbolic)&lt;br /&gt;
chmod u-x,g+r /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# List the directory itself (not its contents)&lt;br /&gt;
ls -ld /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Enter the directory&lt;br /&gt;
cd /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Remove a directory and all its contents&lt;br /&gt;
rm -r /path/to/dirname&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.2-directory-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise B: Directory Permissions ====&lt;br /&gt;
&lt;br /&gt;
Directory permissions are different. Let’s test the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) bit.&lt;br /&gt;
&lt;br /&gt;
# Create a new directory named &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt; and a file named &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Set the directory’s permissions to &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rwx------&amp;lt;/code&amp;gt;). List the directory’s details to confirm.&lt;br /&gt;
# Verify you can list the directory’s contents and read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Now, remove &amp;#039;&amp;#039;only&amp;#039;&amp;#039; the &amp;#039;&amp;#039;&amp;#039;execute (&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;) bit&amp;#039;&amp;#039;&amp;#039; for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will fail.&lt;br /&gt;
# Try to list the directory’s contents. This will also fail.&lt;br /&gt;
# Try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; using its full path. This will also fail, as you can’t traverse the directory.&lt;br /&gt;
# Now, restore the execute bit (&amp;lt;code&amp;gt;u+x&amp;lt;/code&amp;gt;) but remove the read bit (&amp;lt;code&amp;gt;u-r&amp;lt;/code&amp;gt;) for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;. Its permissions should be &amp;lt;code&amp;gt;--wx------&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to list the directory’s contents. This will fail.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Once inside, try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; (which you know exists). This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Return to the parent directory (&amp;lt;code&amp;gt;cd ..&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Clean up: To remove the directory, you must first restore permissions (e.g., &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt;) so you can delete its contents, then remove the directory and its contents.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.b-privilege-changes-and-sudo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Privilege Changes and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The permission model is static, but a process’s identity can change. The kernel tracks multiple UIDs for each process:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real UID (ruid):&amp;#039;&amp;#039;&amp;#039; The UID of the user who &amp;#039;&amp;#039;launched&amp;#039;&amp;#039; the process. This rarely changes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Effective UID (euid):&amp;#039;&amp;#039;&amp;#039; The UID the kernel &amp;#039;&amp;#039;uses for permission checks&amp;#039;&amp;#039;. This is the ID that matters for file access.&lt;br /&gt;
&lt;br /&gt;
Normally, &amp;lt;code&amp;gt;ruid == euid&amp;lt;/code&amp;gt;. However, a special permission bit on an executable file, the &amp;#039;&amp;#039;&amp;#039;setuid&amp;#039;&amp;#039;&amp;#039; bit, can change this.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Setuid (Set User ID):&amp;#039;&amp;#039;&amp;#039; If an executable file has this bit set (shown as &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; in the &amp;#039;&amp;#039;owner’s&amp;#039;&amp;#039; execute-bit position, e.g., &amp;lt;code&amp;gt;-rwsr-xr-x&amp;lt;/code&amp;gt;), when any user executes it, the kernel sets the new process’s &amp;#039;&amp;#039;&amp;#039;effective UID (euid)&amp;#039;&amp;#039;&amp;#039; to be the &amp;#039;&amp;#039;&amp;#039;UID of the file’s owner&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; command is the classic example:&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (&amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;) is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It has the &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit set.&lt;br /&gt;
# When a normal user (e.g., &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt;) runs &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, the kernel starts the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process with &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt; but &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt; (root).&lt;br /&gt;
# Now running with root’s &amp;#039;&amp;#039;effective&amp;#039;&amp;#039; permissions, the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process is allowed to read its configuration file, &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It checks &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; to see if the &amp;#039;&amp;#039;real&amp;#039;&amp;#039; user (&amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;) is allowed to perform the requested action.&lt;br /&gt;
# If allowed, &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (still running as &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt;) uses its power to launch the target command (e.g., &amp;lt;code&amp;gt;apt update&amp;lt;/code&amp;gt;) as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (or as another specified user).&lt;br /&gt;
&lt;br /&gt;
This mechanism is also used by utilities like &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; (which must run as root to edit &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;login&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; (which start as root to authenticate users, then &amp;#039;&amp;#039;drop&amp;#039;&amp;#039; privileges to become the user who just logged in).&lt;br /&gt;
&lt;br /&gt;
A parallel &amp;#039;&amp;#039;&amp;#039;setgid (Set Group ID)&amp;#039;&amp;#039;&amp;#039; bit exists, which changes the process’s &amp;#039;&amp;#039;&amp;#039;effective GID (egid)&amp;#039;&amp;#039;&amp;#039; to the file’s group. This is less common today, as modern systems tend to give users more flexible supplementary groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Sudo and su ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# List file permissions&lt;br /&gt;
ls -l /path/to/binary&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username id&lt;br /&gt;
&lt;br /&gt;
# Run a command as root (prompts for root password -- this will usually not work, because root&amp;#039;s password is usually not set)&lt;br /&gt;
su -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user (prompts for that user&amp;#039;s password)&lt;br /&gt;
su - username -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a script&lt;br /&gt;
echo &amp;#039;#!/bin/bash&amp;#039; &amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Append to the script&lt;br /&gt;
echo &amp;#039;echo &amp;quot;My eUID is: $(id -u -n)&amp;quot;&amp;#039; &amp;gt;&amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Make the script executable&lt;br /&gt;
chmod +x script.sh&lt;br /&gt;
&lt;br /&gt;
# Set the setuid bit (root only)&lt;br /&gt;
sudo chmod u+s script.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.1-investigating-sudo-and-su&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise C: Investigating &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
# View the permissions and ownership of the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;). Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# View the permissions of the &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/bin/su&amp;lt;/code&amp;gt;). Note if it is also setuid root.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; as your normal user and compare the output.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command, first as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and then as &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the password prompts. Unlike &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (which asks for &amp;#039;&amp;#039;your&amp;#039;&amp;#039; password), &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; asks for the &amp;#039;&amp;#039;target user’s&amp;#039;&amp;#039; password.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.2-setuid-script-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise O1: Setuid Script (Optional) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;(Optional, may be blocked by security policies)&amp;#039;&amp;#039;&amp;#039; Attempt to create a setuid script. Note that for security reasons, most modern systems &amp;#039;&amp;#039;ignore&amp;#039;&amp;#039; the setuid bit on shell scripts. This only works on compiled binaries.&lt;br /&gt;
&lt;br /&gt;
# Create a simple shell script at &amp;lt;code&amp;gt;/tmp/lab4_script.sh&amp;lt;/code&amp;gt; that prints the process’s effective and real user IDs.&lt;br /&gt;
# Make the script executable.&lt;br /&gt;
# As root, change the script’s owner to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and set its &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# List the script’s permissions to confirm the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; is present.&lt;br /&gt;
# Run the script as your normal, non-root user.&lt;br /&gt;
# Observe the output: does the eUID show ‘root’ or your user? On most modern systems, the setuid bit will be ignored for scripts.&lt;br /&gt;
# Clean up the script file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.c-additional-reading-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Additional Reading (Optional) ===&lt;br /&gt;
&lt;br /&gt;
This lab covers the standard security model. For further study, explore these advanced topics that refine or alter this model:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SUID and FSUID:&amp;#039;&amp;#039;&amp;#039; Aside from the &amp;lt;code&amp;gt;ruid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;, each process has two more numeric ids. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;suid&amp;lt;/code&amp;gt; (saved set-user-ID) stores the previous &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt; so a process (e.g., a setuid program) can drop privileges and later regain them safely. Similarly, there is an &amp;lt;code&amp;gt;fsgid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sgid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ACLs (Access Control Lists):&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;getfacl&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setfacl&amp;lt;/code&amp;gt; commands manage permissions for &amp;#039;&amp;#039;specific&amp;#039;&amp;#039; additional users and groups, going beyond the simple owner/group/other model.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SELinux and AppArmor:&amp;#039;&amp;#039;&amp;#039; optional kernel modules that provide stricter access control rules. AppArmor specifies restricitons for particular applications and is easier to slot into existing configurations; it is used by Ubuntu. SELinux provides very complex fine-grain control, but can break deployments which do not take it into account; it is used by Red Hat Enterprise Linux.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities:&amp;#039;&amp;#039;&amp;#039; A way to break root’s “all-or-nothing” power into discrete privileges (e.g., &amp;lt;code&amp;gt;CAP_NET_BIND_SERVICE&amp;lt;/code&amp;gt; allows binding to privileged ports &amp;#039;&amp;#039;without&amp;#039;&amp;#039; granting full root).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User Namespaces:&amp;#039;&amp;#039;&amp;#039; A key container technology that remaps UIDs, allowing a process to &amp;#039;&amp;#039;think&amp;#039;&amp;#039; it’s UID 0 inside its namespace while being a normal UID on the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-ii-user-space-convention-the-human-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== User-Space Convention: The Human Layer ==&lt;br /&gt;
&lt;br /&gt;
The kernel only cares about numbers. This section covers the user-space tools and files that map human-readable names like &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; to the kernel’s numeric UIDs and GIDs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.a-accounts-and-the-password-database&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Accounts and the Password Database ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The kernel knows you as UID 1001, not as &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;. The mapping from name to number (and other metadata) is handled in user-space, primarily by a set of text files in &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;: The main user database. It is &amp;#039;&amp;#039;&amp;#039;world-readable&amp;#039;&amp;#039;&amp;#039; and contains one line per user, with fields separated by colons (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;username:x:UID:GID:Full Name:/home/dir:/shell&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;: Your login name.&lt;br /&gt;
** &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;: A placeholder. In the past, the encrypted password hash was here.&lt;br /&gt;
** &amp;lt;code&amp;gt;UID&amp;lt;/code&amp;gt;: The all-important &amp;#039;&amp;#039;&amp;#039;User ID&amp;#039;&amp;#039;&amp;#039; number. This is what the kernel uses.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The user’s primary &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039; number.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Comment (GECOS):&amp;#039;&amp;#039;&amp;#039; This is a comment field, historically called the GECOS (General Electric Comprehensive Operating System) field. It is a comma-separated list that &amp;#039;&amp;#039;conventionally&amp;#039;&amp;#039; stores the following fields. Most systems today just use the first part for the display name.&lt;br /&gt;
**# User’s Full Name (e.g., &amp;lt;code&amp;gt;John Doe&amp;lt;/code&amp;gt;)&lt;br /&gt;
**# Room Number&lt;br /&gt;
**# Work Phone&lt;br /&gt;
**# Home Phone&lt;br /&gt;
**# Other contact info&lt;br /&gt;
** &amp;lt;code&amp;gt;/home/dir&amp;lt;/code&amp;gt;: The user’s home directory.&lt;br /&gt;
** &amp;lt;code&amp;gt;/shell&amp;lt;/code&amp;gt;: The user’s default login shell (e.g., &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;: Because &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; must be world-readable (so &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; can show names instead of numbers), the sensitive password hashes were moved here. This file is &amp;#039;&amp;#039;&amp;#039;only readable by root&amp;#039;&amp;#039;&amp;#039;. &amp;lt;code&amp;gt;username:password_hash:last_change:etc...&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;: The group database, also world-readable. &amp;lt;code&amp;gt;groupname:x:GID:member_list&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;groupname&amp;lt;/code&amp;gt;: The human-readable group name.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The numeric &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
** &amp;lt;code&amp;gt;member_list&amp;lt;/code&amp;gt;: A comma-separated list of &amp;#039;&amp;#039;additional&amp;#039;&amp;#039; users who are part of this group (for their supplementary groups).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Examples: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View the top of a file (first 10 lines)&lt;br /&gt;
head -n 10 /path/to/file&lt;br /&gt;
&lt;br /&gt;
# View a file as root&lt;br /&gt;
sudo cat /path/to/file&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a group&lt;br /&gt;
getent group &amp;lt;groupname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.1-inspecting-account-databases&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise D: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; user database.&lt;br /&gt;
# Attempt to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; to prove root can read it.&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; database.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; command to query the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; user and for your own user (using the &amp;lt;code&amp;gt;$USER&amp;lt;/code&amp;gt; variable).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to query the &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group and for your own primary group (which you can find from the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View user GECOS info&lt;br /&gt;
finger &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Change your GECOS info interactively (current user)&lt;br /&gt;
chfn&lt;br /&gt;
&lt;br /&gt;
# Change your password (current user)&lt;br /&gt;
passwd&lt;br /&gt;
&lt;br /&gt;
# Find a user’s line in /etc/passwd&lt;br /&gt;
grep &amp;quot;^&amp;lt;username&amp;gt;:&amp;quot; /etc/passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.2-modifying-gecos-and-passwords&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise E: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; command to view your user’s GECOS information.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt; command to interactively change your GECOS information (Full Name, Room Number, etc.). You may be prompted for your password.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; again to see the changes have been applied.&lt;br /&gt;
# Check your entry in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;) to see how the GECOS field (the 5th field) was updated.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; command to change your password. This command securely updates the &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.b-name-service-switch-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Name Service Switch (NSS) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Storing all users in local &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; files doesn’t scale in a large organization. What if you want to log in to 1,000 different machines with the same username and password?&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Name Service Switch (NSS)&amp;#039;&amp;#039;&amp;#039; is a user-space (specifically, C library) mechanism that makes this possible. It allows system functions (like &amp;lt;code&amp;gt;getpwnam()&amp;lt;/code&amp;gt; which “gets password-entry-by-name”) to look for information in multiple sources.&lt;br /&gt;
&lt;br /&gt;
The configuration file is &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. It contains lines like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;passwd:    files systemd ldap&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;group:     files sss&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the system: “When looking up a user (&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;), first check local &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;), then ask the &amp;lt;code&amp;gt;systemd&amp;lt;/code&amp;gt; user database, and if still not found, query the &amp;lt;code&amp;gt;ldap&amp;lt;/code&amp;gt; server.”&lt;br /&gt;
&lt;br /&gt;
This system is powerful because it allows a a system to pull user/group info from network services like &amp;#039;&amp;#039;&amp;#039;LDAP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NIS&amp;#039;&amp;#039;&amp;#039;, or &amp;#039;&amp;#039;&amp;#039;SSS&amp;#039;&amp;#039;&amp;#039; (System Security Services Daemon) without changing any kernel behavior. The kernel &amp;#039;&amp;#039;still&amp;#039;&amp;#039; only receives and operates on the final numeric UID/GID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Print the nsswitch configuration&lt;br /&gt;
cat /etc/nsswitch.conf&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a specific user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.1-inspecting-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise F: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Display the lines from &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; that configure the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; databases (the lines starting with &amp;lt;code&amp;gt;passwd:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test the lookup system by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to resolve the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user and the &amp;lt;code&amp;gt;nogroup&amp;lt;/code&amp;gt; group.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;(Optional, use with extreme care)&amp;#039;&amp;#039;&amp;#039; As root, edit &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sudo nano /etc/nsswitch.conf&amp;lt;/code&amp;gt;). Put a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; at the beginning of the &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt; line to comment it out, and add a new line &amp;lt;code&amp;gt;group: sss&amp;lt;/code&amp;gt; (a service that is likely not running). Save the file.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Now, try to resolve group names using &amp;lt;code&amp;gt;getent group sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt;. Observe that names may now show up as numbers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;IMMEDIATELY&amp;#039;&amp;#039;&amp;#039; restore &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; to its original state and verify &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; works again. This exercise demonstrates how critical this user-space configuration is for the “human-friendly” layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Add a group&lt;br /&gt;
sudo groupadd &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add a user with options (home dir, primary group, supplementary group, login shell)&lt;br /&gt;
sudo useradd -m -g &amp;lt;primary_group&amp;gt; -G &amp;lt;suppl_group&amp;gt; -s /bin/bash &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Set a user’s password&lt;br /&gt;
sudo passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Switch to a user (login shell)&lt;br /&gt;
su - &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a user and their home directory&lt;br /&gt;
sudo userdel -r &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a group&lt;br /&gt;
sudo groupdel &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check the end of the /etc/group file (last 3 lines)&lt;br /&gt;
tail -n 3 /etc/group&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.2-creating-users-and-groups&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise G: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
Let’s use the standard user-space tools to create a new user and group. These tools automatically update the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; files.&lt;br /&gt;
&lt;br /&gt;
# As root, create a new group named &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the group was added by checking the end of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; file and by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As root, create a new user named &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;. When creating them, specify:&lt;br /&gt;
#* Their primary group should be &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* They should be added to the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; supplementary group.&lt;br /&gt;
#* Their home directory should be created (&amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Their login shell should be &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the user was created by querying the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;shadow&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;) databases using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group entries (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;) to confirm &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s membership.&lt;br /&gt;
# Set a password for &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; so they can log in.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to start a new login shell as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Once logged in as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;, run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to check your identity and &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; to see your home directory.&lt;br /&gt;
# &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s shell to return to your original session.&lt;br /&gt;
# Clean up: as root, delete the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; (making sure to remove their home directory) and delete the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; group.&lt;br /&gt;
# Verify they are gone using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-iii-tool-summary&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Tool Summary ==&lt;br /&gt;
&lt;br /&gt;
This lab uses two distinct categories of tools: those that interact directly with the kernel’s security model, and those that manage the user-space databases.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-level-privilege-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel-Level &amp;amp;amp; Privilege Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands directly manipulate kernel-level concepts (file ownership, process UIDs) or are used for privilege escalation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;own&amp;#039;&amp;#039;&amp;#039;er. Changes the UID and GID owner of a file/directory. &amp;#039;&amp;#039;Requires kernel-level privilege (e.g., eUID=0) to change owner to someone else.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;user:group&amp;lt;/code&amp;gt;: Set both user and group&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chgrp&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;gr&amp;#039;&amp;#039;&amp;#039;ou&amp;#039;&amp;#039;&amp;#039;p&amp;#039;&amp;#039;&amp;#039;. Changes the GID owner of a file/directory. &amp;#039;&amp;#039;You must be the owner of the file AND a member of the target group.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Display process identity (UID, GID, groups).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Show UID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;: Show GID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show name instead of number&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser &amp;#039;&amp;#039;&amp;#039;do&amp;#039;&amp;#039;&amp;#039;. Executes a command as another user (default: root) based on &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; rules. Uses setuid.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u user&amp;lt;/code&amp;gt;: Run as &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Start a root login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: List allowed commands&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser. Switches to another user account (default: root). Authenticates using the &amp;#039;&amp;#039;target&amp;#039;&amp;#039; user’s password.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Start a full login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-c &amp;#039;cmd&amp;#039;&amp;lt;/code&amp;gt;: Run a single command&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-space-account-database-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== User-Space Account Database Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands read from or write to the user-space databases (e.g., &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;), which are then interpreted by NSS.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Change a user’s password. Securely updates &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Change password for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Lock account&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Unlock account&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Get ent&amp;#039;&amp;#039;&amp;#039;ries from NSS databases. The correct way to look up users/groups.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;passwd [user]&amp;lt;/code&amp;gt;: Look up user&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;group [group]&amp;lt;/code&amp;gt;: Look up group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;shadow [user]&amp;lt;/code&amp;gt;: Look up shadow entry&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;f&amp;#039;&amp;#039;&amp;#039;i&amp;#039;&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;#039;ger. Modifies the GECOS field in &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Modify for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Displays user information, primarily by parsing the GECOS field.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Show info for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new user account. Updates &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Create home directory&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g group&amp;lt;/code&amp;gt;: Set primary group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-G groups&amp;lt;/code&amp;gt;: Set supplementary groups&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-s shell&amp;lt;/code&amp;gt;: Set login shell&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;groupadd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new group. Updates &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[groupname]&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or Markdown) containing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Command Output:&amp;#039;&amp;#039;&amp;#039; Screenshots or text-based command output for each non-optional hands-on exercise (A-G), clearly labelled by section.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Short written answers to the “Conclusion Questions” below.&lt;br /&gt;
Grading will consider the completeness of your outputs, the correctness and clarity of your reasoning in the conclusion answers, and your adherence to instructions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;conclusion-questions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Please answer the following summary questions based on the lab’s content.&lt;br /&gt;
&lt;br /&gt;
# Explain the kernel’s permission checking algorithm. If a file &amp;lt;code&amp;gt;/data/report.txt&amp;lt;/code&amp;gt; is owned by &amp;lt;code&amp;gt;root:webdev&amp;lt;/code&amp;gt; and has permissions &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;), can the user &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; (who is a member of the &amp;lt;code&amp;gt;webdev&amp;lt;/code&amp;gt; group) &amp;#039;&amp;#039;read&amp;#039;&amp;#039; the file? Why or why not?&lt;br /&gt;
# Why was the sensitive password hash moved from the world-readable &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; to the root-only-readable &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;? What kernel-level permission difference enables this security?&lt;br /&gt;
# Explain the relationship between the user-space tool &amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;, the file &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, the Name Service Switch (NSS), and the kernel’s UID-based permission checks. How do they all interact when you run &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;?&lt;br /&gt;
# What would happen if you (as root) manually edited &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; and changed your user’s UID from &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;5001&amp;lt;/code&amp;gt;, but did &amp;#039;&amp;#039;not&amp;#039;&amp;#039; change the ownership of any files in your home directory (which are all still owned by kernel UID &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt;)? What specific problems would you face at your next login?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8162</id>
		<title>OS Lab 4 - Users, Groups and Permissions</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_4_-_Users,_Groups_and_Permissions&amp;diff=8162"/>
		<updated>2025-10-24T13:55:59Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: &amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; == Objectives ==  Upon completion of this lab, you will be able to:  * &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs,...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span id=&amp;quot;learning-outcomes&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
Upon completion of this lab, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Explain&amp;#039;&amp;#039;&amp;#039; how the kernel represents user and group identities (UIDs, GIDs, supplementary groups) and how it enforces permissions on files and directories.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Demonstrate&amp;#039;&amp;#039;&amp;#039; how privilege elevation works via effective UIDs/GIDs and setuid/setgid binaries (e.g., via &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Describe&amp;#039;&amp;#039;&amp;#039; how user-space conventions (account databases, name service switch) map human-readable names to kernel numeric IDs.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Investigate and manipulate&amp;#039;&amp;#039;&amp;#039; these mechanisms on a Linux system to reinforce understanding of the kernel vs. user-space boundary.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;introduction&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
In our previous lab, we explored processes as the fundamental unit of execution. Now, we investigate the security model that governs what these processes can &amp;#039;&amp;#039;do&amp;#039;&amp;#039;. This lab dives into the Linux security model, carefully separating the &amp;#039;&amp;#039;&amp;#039;kernel’s&amp;#039;&amp;#039;&amp;#039; rigid, number-based rules from the &amp;#039;&amp;#039;&amp;#039;user-space’s&amp;#039;&amp;#039;&amp;#039; human-friendly conventions layered on top. Understanding this distinction is critical for system administration, security, and software development.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-i-kernel-abstraction-the-real-security-model&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Kernel Abstraction: The Real Security Model ==&lt;br /&gt;
&lt;br /&gt;
This section focuses on how the Linux kernel &amp;#039;&amp;#039;itself&amp;#039;&amp;#039; sees users and permissions. To the kernel, a “user” is just a number (a UID), and security is an algorithm.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.a-identities-and-permission-checking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Identities and Permission Checking ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Every process running on a Linux system carries a set of numeric credentials. The most important are:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User ID (UID):&amp;#039;&amp;#039;&amp;#039; A number identifying the user who “owns” the process.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Group ID (GID):&amp;#039;&amp;#039;&amp;#039; A number identifying the process’s primary group.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Supplementary Groups:&amp;#039;&amp;#039;&amp;#039; A set of additional group IDs the process belongs to.&lt;br /&gt;
&lt;br /&gt;
Likewise, every file and directory on the filesystem has metadata stored in its &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039;, which includes:&lt;br /&gt;
&lt;br /&gt;
* An &amp;#039;&amp;#039;&amp;#039;owner UID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* A &amp;#039;&amp;#039;&amp;#039;group GID&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Permission bits&amp;#039;&amp;#039;&amp;#039; (the mode), which define access rights for three distinct categories: the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039;, the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039;, and &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
These permissions are the classic &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read), &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write), and &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) triplets. Their meaning, however, depends on whether the object is a file or a directory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Permission&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;File&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Meaning for a &amp;#039;&amp;#039;&amp;#039;Directory&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; (read)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read the contents of the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can list the contents of the directory (i.e., see filenames).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; (write)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify or truncate the file.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can create, delete, or rename files &amp;#039;&amp;#039;within&amp;#039;&amp;#039; the directory.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can execute the file (if it’s a program/script).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can enter (e.g., &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;) the directory and access its inodes.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;The Permission Checking Algorithm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
When a process (with its set of IDs) tries to access a file (with its owner/group/mode), the kernel performs a simple, sequential check:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Is the process’s effective UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (superuser/root)?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Access is almost always granted. Stop. (This is a simplification; mechanisms like SELinux can add further checks).&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does the process’s effective UID match the file’s owner UID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;owner&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;rwx&amp;lt;/code&amp;gt;—). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Does any of the process’s GIDs (primary or supplementary) match the file’s group GID?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Yes:&amp;#039;&amp;#039;&amp;#039; Apply the &amp;#039;&amp;#039;&amp;#039;group&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;---rwx---&amp;lt;/code&amp;gt;). Access is granted or denied based &amp;#039;&amp;#039;only&amp;#039;&amp;#039; on these bits. Stop.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;None of the above?&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Apply the &amp;#039;&amp;#039;&amp;#039;others&amp;#039;&amp;#039;&amp;#039; permission bits (&amp;lt;code&amp;gt;------rwx&amp;lt;/code&amp;gt;). Access is granted or denied.&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;superuser&amp;#039;&amp;#039;&amp;#039; (UID &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) is special. It bypasses these standard checks. This power allows it to manage the system, but it also carries significant risk.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Capability&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Regular User (UID &amp;amp;gt; 0)&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Superuser (UID = 0)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Read/Write Files&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Restricted by owner/group/other permissions.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can read/write &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file, regardless of permissions.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Ownership&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Cannot&amp;#039;&amp;#039;&amp;#039; change file ownership, not even for their own files.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file’s owner and group to &amp;#039;&amp;#039;any&amp;#039;&amp;#039; user/group.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Change Permissions&amp;#039;&amp;#039;&amp;#039; (&amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can only change permissions on files they own.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can change permissions on &amp;#039;&amp;#039;any&amp;#039;&amp;#039; file.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Bind to Privileged Ports&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Cannot bind to ports 1-1023.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can bind to any port.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;System Configuration&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Limited to own user settings.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Can modify kernel parameters, load modules, change system-wide settings.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: File permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Check your identity&lt;br /&gt;
id&lt;br /&gt;
&lt;br /&gt;
# Create an empty file&lt;br /&gt;
touch /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# List file details&lt;br /&gt;
ls -l /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change permissions (numeric)&lt;br /&gt;
chmod 640 /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Change ownership (root only)&lt;br /&gt;
sudo chown user:group /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Read a file’s contents&lt;br /&gt;
cat /path/to/filename&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username command&lt;br /&gt;
&lt;br /&gt;
# Write text to a file as root&lt;br /&gt;
sudo tee /path/to/filename &amp;lt;&amp;lt;&amp;lt; &amp;quot;text&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Remove a file (as root)&lt;br /&gt;
sudo rm /path/to/filename&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.1-file-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise A: File Permissions ====&lt;br /&gt;
&lt;br /&gt;
Let’s test this model with a file.&lt;br /&gt;
&lt;br /&gt;
# First, check your own identity.&lt;br /&gt;
# Create a new empty file named &amp;lt;code&amp;gt;/tmp/lab4_testfile&amp;lt;/code&amp;gt;.&lt;br /&gt;
# List the file’s details and observe that you are the owner.&lt;br /&gt;
# Change the file’s permissions to &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Change the file’s owner and group to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;. List the details again to confirm.&lt;br /&gt;
# As your normal user, &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to read the file. This should fail. Document the “Permission denied” error.&lt;br /&gt;
# Now, try to read the file as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user. This should also fail.&lt;br /&gt;
# Finally, demonstrate root’s power:&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to write the text “hello from root” into the file, bypassing its permissions.&lt;br /&gt;
#* Read the file using &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to confirm the text is there.&lt;br /&gt;
#* Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to change the file’s permissions to &amp;lt;code&amp;gt;600&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-------&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* List the file’s details one last time.&lt;br /&gt;
# Clean up by removing the file as root.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Directory permissions ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Create a directory&lt;br /&gt;
mkdir /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Change permissions (symbolic)&lt;br /&gt;
chmod u-x,g+r /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# List the directory itself (not its contents)&lt;br /&gt;
ls -ld /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Enter the directory&lt;br /&gt;
cd /path/to/dirname&lt;br /&gt;
&lt;br /&gt;
# Remove a directory and all its contents&lt;br /&gt;
rm -r /path/to/dirname&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.a.2-directory-permissions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise B: Directory Permissions ====&lt;br /&gt;
&lt;br /&gt;
Directory permissions are different. Let’s test the &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; (execute) bit.&lt;br /&gt;
&lt;br /&gt;
# Create a new directory named &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt; and a file named &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Set the directory’s permissions to &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rwx------&amp;lt;/code&amp;gt;). List the directory’s details to confirm.&lt;br /&gt;
# Verify you can list the directory’s contents and read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; inside it.&lt;br /&gt;
# Now, remove &amp;#039;&amp;#039;only&amp;#039;&amp;#039; the &amp;#039;&amp;#039;&amp;#039;execute (&amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;) bit&amp;#039;&amp;#039;&amp;#039; for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will fail.&lt;br /&gt;
# Try to list the directory’s contents. This will also fail.&lt;br /&gt;
# Try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; using its full path. This will also fail, as you can’t traverse the directory.&lt;br /&gt;
# Now, restore the execute bit (&amp;lt;code&amp;gt;u+x&amp;lt;/code&amp;gt;) but remove the read bit (&amp;lt;code&amp;gt;u-r&amp;lt;/code&amp;gt;) for the owner of &amp;lt;code&amp;gt;/tmp/lab4_dir&amp;lt;/code&amp;gt;. Its permissions should be &amp;lt;code&amp;gt;--wx------&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Try to list the directory’s contents. This will fail.&lt;br /&gt;
# Try to &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt; into the directory. This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Once inside, try to read the &amp;lt;code&amp;gt;secretfile&amp;lt;/code&amp;gt; (which you know exists). This will &amp;#039;&amp;#039;succeed&amp;#039;&amp;#039;.&lt;br /&gt;
# Return to the parent directory (&amp;lt;code&amp;gt;cd ..&amp;lt;/code&amp;gt;).&lt;br /&gt;
# Clean up: To remove the directory, you must first restore permissions (e.g., &amp;lt;code&amp;gt;700&amp;lt;/code&amp;gt;) so you can delete its contents, then remove the directory and its contents.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.b-privilege-changes-and-sudo&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Privilege Changes and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The permission model is static, but a process’s identity can change. The kernel tracks multiple UIDs for each process:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Real UID (ruid):&amp;#039;&amp;#039;&amp;#039; The UID of the user who &amp;#039;&amp;#039;launched&amp;#039;&amp;#039; the process. This rarely changes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Effective UID (euid):&amp;#039;&amp;#039;&amp;#039; The UID the kernel &amp;#039;&amp;#039;uses for permission checks&amp;#039;&amp;#039;. This is the ID that matters for file access.&lt;br /&gt;
&lt;br /&gt;
Normally, &amp;lt;code&amp;gt;ruid == euid&amp;lt;/code&amp;gt;. However, a special permission bit on an executable file, the &amp;#039;&amp;#039;&amp;#039;setuid&amp;#039;&amp;#039;&amp;#039; bit, can change this.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Setuid (Set User ID):&amp;#039;&amp;#039;&amp;#039; If an executable file has this bit set (shown as &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; in the &amp;#039;&amp;#039;owner’s&amp;#039;&amp;#039; execute-bit position, e.g., &amp;lt;code&amp;gt;-rwsr-xr-x&amp;lt;/code&amp;gt;), when any user executes it, the kernel sets the new process’s &amp;#039;&amp;#039;&amp;#039;effective UID (euid)&amp;#039;&amp;#039;&amp;#039; to be the &amp;#039;&amp;#039;&amp;#039;UID of the file’s owner&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; command is the classic example:&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (&amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;) is owned by &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It has the &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit set.&lt;br /&gt;
# When a normal user (e.g., &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt;) runs &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;, the kernel starts the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process with &amp;lt;code&amp;gt;ruid=1001&amp;lt;/code&amp;gt; but &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt; (root).&lt;br /&gt;
# Now running with root’s &amp;#039;&amp;#039;effective&amp;#039;&amp;#039; permissions, the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; process is allowed to read its configuration file, &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;.&lt;br /&gt;
# It checks &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; to see if the &amp;#039;&amp;#039;real&amp;#039;&amp;#039; user (&amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;) is allowed to perform the requested action.&lt;br /&gt;
# If allowed, &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (still running as &amp;lt;code&amp;gt;euid=0&amp;lt;/code&amp;gt;) uses its power to launch the target command (e.g., &amp;lt;code&amp;gt;apt update&amp;lt;/code&amp;gt;) as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (or as another specified user).&lt;br /&gt;
&lt;br /&gt;
This mechanism is also used by utilities like &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; (which must run as root to edit &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;login&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;sshd&amp;lt;/code&amp;gt; (which start as root to authenticate users, then &amp;#039;&amp;#039;drop&amp;#039;&amp;#039; privileges to become the user who just logged in).&lt;br /&gt;
&lt;br /&gt;
A parallel &amp;#039;&amp;#039;&amp;#039;setgid (Set Group ID)&amp;#039;&amp;#039;&amp;#039; bit exists, which changes the process’s &amp;#039;&amp;#039;&amp;#039;effective GID (egid)&amp;#039;&amp;#039;&amp;#039; to the file’s group. This is less common today, as modern systems tend to give users more flexible supplementary groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-i.b&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Sudo and su ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# List file permissions&lt;br /&gt;
ls -l /path/to/binary&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user&lt;br /&gt;
sudo -u username id&lt;br /&gt;
&lt;br /&gt;
# Run a command as root (prompts for root password -- this will usually not work, because root&amp;#039;s password is usually not set)&lt;br /&gt;
su -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Run a command as another user (prompts for that user&amp;#039;s password)&lt;br /&gt;
su - username -c &amp;#039;id&amp;#039;&lt;br /&gt;
&lt;br /&gt;
# Create a script&lt;br /&gt;
echo &amp;#039;#!/bin/bash&amp;#039; &amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Append to the script&lt;br /&gt;
echo &amp;#039;echo &amp;quot;My eUID is: $(id -u -n)&amp;quot;&amp;#039; &amp;gt;&amp;gt; script.sh&lt;br /&gt;
&lt;br /&gt;
# Make the script executable&lt;br /&gt;
chmod +x script.sh&lt;br /&gt;
&lt;br /&gt;
# Set the setuid bit (root only)&lt;br /&gt;
sudo chmod u+s script.sh&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.1-investigating-sudo-and-su&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise C: Investigating &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
# View the permissions and ownership of the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/usr/bin/sudo&amp;lt;/code&amp;gt;). Note the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# View the permissions of the &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; binary (likely &amp;lt;code&amp;gt;/bin/su&amp;lt;/code&amp;gt;). Note if it is also setuid root.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command as the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; as your normal user and compare the output.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to &amp;#039;&amp;#039;attempt&amp;#039;&amp;#039; to run the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command, first as &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and then as &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Observe the password prompts. Unlike &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; (which asks for &amp;#039;&amp;#039;your&amp;#039;&amp;#039; password), &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; asks for the &amp;#039;&amp;#039;target user’s&amp;#039;&amp;#039; password.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-i.b.2-setuid-script-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise O1: Setuid Script (Optional) ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;(Optional, may be blocked by security policies)&amp;#039;&amp;#039;&amp;#039; Attempt to create a setuid script. Note that for security reasons, most modern systems &amp;#039;&amp;#039;ignore&amp;#039;&amp;#039; the setuid bit on shell scripts. This only works on compiled binaries.&lt;br /&gt;
&lt;br /&gt;
# Create a simple shell script at &amp;lt;code&amp;gt;/tmp/lab4_script.sh&amp;lt;/code&amp;gt; that prints the process’s effective and real user IDs.&lt;br /&gt;
# Make the script executable.&lt;br /&gt;
# As root, change the script’s owner to &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; and set its &amp;lt;code&amp;gt;setuid&amp;lt;/code&amp;gt; bit.&lt;br /&gt;
# List the script’s permissions to confirm the &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; is present.&lt;br /&gt;
# Run the script as your normal, non-root user.&lt;br /&gt;
# Observe the output: does the eUID show ‘root’ or your user? On most modern systems, the setuid bit will be ignored for scripts.&lt;br /&gt;
# Clean up the script file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;i.c-additional-reading-optional&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Additional Reading (Optional) ===&lt;br /&gt;
&lt;br /&gt;
This lab covers the standard security model. For further study, explore these advanced topics that refine or alter this model:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SUID and FSUID:&amp;#039;&amp;#039;&amp;#039; Aside from the &amp;lt;code&amp;gt;ruid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;, each process has two more numeric ids. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;fsuid&amp;lt;/code&amp;gt; (filesystem UID) is an internal Linux mechanism that allows a privileged process (like an NFS server) to perform filesystem operations as another user without fully changing its &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;suid&amp;lt;/code&amp;gt; (saved set-user-ID) stores the previous &amp;lt;code&amp;gt;euid&amp;lt;/code&amp;gt; so a process (e.g., a setuid program) can drop privileges and later regain them safely. Similarly, there is an &amp;lt;code&amp;gt;fsgid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sgid&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;ACLs (Access Control Lists):&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;getfacl&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;setfacl&amp;lt;/code&amp;gt; commands manage permissions for &amp;#039;&amp;#039;specific&amp;#039;&amp;#039; additional users and groups, going beyond the simple owner/group/other model.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;SELinux and AppArmor:&amp;#039;&amp;#039;&amp;#039; optional kernel modules that provide stricter access control rules. AppArmor specifies restricitons for particular applications and is easier to slot into existing configurations; it is used by Ubuntu. SELinux provides very complex fine-grain control, but can break deployments which do not take it into account; it is used by Red Hat Enterprise Linux.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities:&amp;#039;&amp;#039;&amp;#039; A way to break root’s “all-or-nothing” power into discrete privileges (e.g., &amp;lt;code&amp;gt;CAP_NET_BIND_SERVICE&amp;lt;/code&amp;gt; allows binding to privileged ports &amp;#039;&amp;#039;without&amp;#039;&amp;#039; granting full root).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;User Namespaces:&amp;#039;&amp;#039;&amp;#039; A key container technology that remaps UIDs, allowing a process to &amp;#039;&amp;#039;think&amp;#039;&amp;#039; it’s UID 0 inside its namespace while being a normal UID on the host.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-ii-user-space-convention-the-human-layer&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== User-Space Convention: The Human Layer ==&lt;br /&gt;
&lt;br /&gt;
The kernel only cares about numbers. This section covers the user-space tools and files that map human-readable names like &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; to the kernel’s numeric UIDs and GIDs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.a-accounts-and-the-password-database&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Accounts and the Password Database ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
The kernel knows you as UID 1001, not as &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt;. The mapping from name to number (and other metadata) is handled in user-space, primarily by a set of text files in &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;: The main user database. It is &amp;#039;&amp;#039;&amp;#039;world-readable&amp;#039;&amp;#039;&amp;#039; and contains one line per user, with fields separated by colons (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;username:x:UID:GID:Full Name:/home/dir:/shell&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;: Your login name.&lt;br /&gt;
** &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;: A placeholder. In the past, the encrypted password hash was here.&lt;br /&gt;
** &amp;lt;code&amp;gt;UID&amp;lt;/code&amp;gt;: The all-important &amp;#039;&amp;#039;&amp;#039;User ID&amp;#039;&amp;#039;&amp;#039; number. This is what the kernel uses.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The user’s primary &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039; number.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Full Name (GECOS):&amp;#039;&amp;#039;&amp;#039; This is a comment field, historically called the GECOS (General Electric Comprehensive Operating System) field. It is a comma-separated list that &amp;#039;&amp;#039;conventionally&amp;#039;&amp;#039; stores:&lt;br /&gt;
**# User’s Full Name (e.g., &amp;lt;code&amp;gt;John Doe&amp;lt;/code&amp;gt;)&lt;br /&gt;
**# Room Number&lt;br /&gt;
**# Work Phone&lt;br /&gt;
**# Home Phone&lt;br /&gt;
**# Other contact info Most systems today just use the first part for the display name.&lt;br /&gt;
** &amp;lt;code&amp;gt;/home/dir&amp;lt;/code&amp;gt;: The user’s home directory.&lt;br /&gt;
** &amp;lt;code&amp;gt;/shell&amp;lt;/code&amp;gt;: The user’s default login shell (e.g., &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;: Because &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; must be world-readable (so &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; can show names instead of numbers), the sensitive password hashes were moved here. This file is &amp;#039;&amp;#039;&amp;#039;only readable by root&amp;#039;&amp;#039;&amp;#039;. &amp;lt;code&amp;gt;username:password_hash:last_change:etc...&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;: The group database, also world-readable. &amp;lt;code&amp;gt;groupname:x:GID:member_list&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;groupname&amp;lt;/code&amp;gt;: The human-readable group name.&lt;br /&gt;
** &amp;lt;code&amp;gt;GID&amp;lt;/code&amp;gt;: The numeric &amp;#039;&amp;#039;&amp;#039;Group ID&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
** &amp;lt;code&amp;gt;member_list&amp;lt;/code&amp;gt;: A comma-separated list of &amp;#039;&amp;#039;additional&amp;#039;&amp;#039; users who are part of this group (for their supplementary groups).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View the top of a file (first 10 lines)&lt;br /&gt;
head -n 10 /path/to/file&lt;br /&gt;
&lt;br /&gt;
# View a file as root&lt;br /&gt;
sudo cat /path/to/file&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a group&lt;br /&gt;
getent group &amp;lt;groupname&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.1-inspecting-account-databases&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise D: Inspecting Account Databases ====&lt;br /&gt;
&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; user database.&lt;br /&gt;
# Attempt to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;. This will fail.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; to display the first 5 lines of &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; to prove root can read it.&lt;br /&gt;
# Display the first 10 lines of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; database.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; command to query the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; user and for your own user (using the &amp;lt;code&amp;gt;$USER&amp;lt;/code&amp;gt; variable).&lt;br /&gt;
# Use &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to query the &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; database for the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group and for your own primary group (which you can find from the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; command).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.a.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# View user GECOS info&lt;br /&gt;
finger &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Change your GECOS info interactively (current user)&lt;br /&gt;
chfn&lt;br /&gt;
&lt;br /&gt;
# Change your password (current user)&lt;br /&gt;
passwd&lt;br /&gt;
&lt;br /&gt;
# Find a user’s line in /etc/passwd&lt;br /&gt;
grep &amp;quot;^&amp;lt;username&amp;gt;:&amp;quot; /etc/passwd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.a.2-modifying-gecos-and-passwords&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise E: Modifying GECOS and Passwords ====&lt;br /&gt;
&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; command to view your user’s GECOS information.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt; command to interactively change your GECOS information (Full Name, Room Number, etc.). You may be prompted for your password.&lt;br /&gt;
# Run &amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt; again to see the changes have been applied.&lt;br /&gt;
# Check your entry in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;) to see how the GECOS field (the 5th field) was updated.&lt;br /&gt;
# Use the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; command to change your password. This command securely updates the &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;ii.b-name-service-switch-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Name Service Switch (NSS) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;background-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Background ====&lt;br /&gt;
&lt;br /&gt;
Storing all users in local &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; files doesn’t scale in a large organization. What if you want to log in to 1,000 different machines with the same username and password?&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;&amp;#039;Name Service Switch (NSS)&amp;#039;&amp;#039;&amp;#039; is a user-space (specifically, C library) mechanism that makes this possible. It allows system functions (like &amp;lt;code&amp;gt;getpwnam()&amp;lt;/code&amp;gt; which “gets password-entry-by-name”) to look for information in multiple sources.&lt;br /&gt;
&lt;br /&gt;
The configuration file is &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt;. It contains lines like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;passwd:    files systemd ldap&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;group:     files sss&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This tells the system: “When looking up a user (&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;), first check local &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;), then ask the &amp;lt;code&amp;gt;systemd&amp;lt;/code&amp;gt; user database, and if still not found, query the &amp;lt;code&amp;gt;ldap&amp;lt;/code&amp;gt; server.”&lt;br /&gt;
&lt;br /&gt;
This system is powerful because it allows a a system to pull user/group info from network services like &amp;#039;&amp;#039;&amp;#039;LDAP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NIS&amp;#039;&amp;#039;&amp;#039;, or &amp;#039;&amp;#039;&amp;#039;SSS&amp;#039;&amp;#039;&amp;#039; (System Security Services Daemon) without changing any kernel behavior. The kernel &amp;#039;&amp;#039;still&amp;#039;&amp;#039; only receives and operates on the final numeric UID/GID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Print the nsswitch configuration&lt;br /&gt;
cat /etc/nsswitch.conf&lt;br /&gt;
&lt;br /&gt;
# Query the NSS database for a specific user&lt;br /&gt;
getent passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.1-inspecting-nss&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise F: Inspecting NSS ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type: decimal;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Display the lines from &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; that configure the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group&amp;lt;/code&amp;gt; databases (the lines starting with &amp;lt;code&amp;gt;passwd:&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt;).&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test the lookup system by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt; to resolve the &amp;lt;code&amp;gt;nobody&amp;lt;/code&amp;gt; user and the &amp;lt;code&amp;gt;nogroup&amp;lt;/code&amp;gt; group.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;(Optional, use with extreme care)&amp;#039;&amp;#039;&amp;#039; As root, edit &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sudo nano /etc/nsswitch.conf&amp;lt;/code&amp;gt;). Put a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; at the beginning of the &amp;lt;code&amp;gt;group:&amp;lt;/code&amp;gt; line to comment it out, and add a new line &amp;lt;code&amp;gt;group: sss&amp;lt;/code&amp;gt; (a service that is likely not running). Save the file.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Now, try to resolve group names using &amp;lt;code&amp;gt;getent group sudo&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ls -l /&amp;lt;/code&amp;gt;. Observe that names may now show up as numbers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;#039;&amp;#039;&amp;#039;IMMEDIATELY&amp;#039;&amp;#039;&amp;#039; restore &amp;lt;code&amp;gt;/etc/nsswitch.conf&amp;lt;/code&amp;gt; to its original state and verify &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; works again. This exercise demonstrates how critical this user-space configuration is for the “human-friendly” layer.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;examples-building-blocks-ii.b.2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Examples: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Add a group&lt;br /&gt;
sudo groupadd &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Add a user with options (home dir, primary group, supplementary group, login shell)&lt;br /&gt;
sudo useradd -m -g &amp;lt;primary_group&amp;gt; -G &amp;lt;suppl_group&amp;gt; -s /bin/bash &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Set a user’s password&lt;br /&gt;
sudo passwd &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Switch to a user (login shell)&lt;br /&gt;
su - &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a user and their home directory&lt;br /&gt;
sudo userdel -r &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Delete a group&lt;br /&gt;
sudo groupdel &amp;lt;groupname&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Check the end of the /etc/group file (last 3 lines)&lt;br /&gt;
tail -n 3 /etc/group&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;hands-on-exercise-ii.b.2-creating-users-and-groups&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
==== Hands-on Exercise G: Creating Users and Groups ====&lt;br /&gt;
&lt;br /&gt;
Let’s use the standard user-space tools to create a new user and group. These tools automatically update the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; files.&lt;br /&gt;
&lt;br /&gt;
# As root, create a new group named &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the group was added by checking the end of the &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt; file and by using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# As root, create a new user named &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;. When creating them, specify:&lt;br /&gt;
#* Their primary group should be &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* They should be added to the &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; supplementary group.&lt;br /&gt;
#* Their home directory should be created (&amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Their login shell should be &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Verify the user was created by querying the &amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;shadow&amp;lt;/code&amp;gt; (with &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;) databases using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Check the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; group entries (using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;) to confirm &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s membership.&lt;br /&gt;
# Set a password for &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; so they can log in.&lt;br /&gt;
# Use &amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt; to start a new login shell as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Once logged in as &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;, run &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; to check your identity and &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt; to see your home directory.&lt;br /&gt;
# &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt;’s shell to return to your original session.&lt;br /&gt;
# Clean up: as root, delete the &amp;lt;code&amp;gt;testuser&amp;lt;/code&amp;gt; (making sure to remove their home directory) and delete the &amp;lt;code&amp;gt;labtesters&amp;lt;/code&amp;gt; group.&lt;br /&gt;
# Verify they are gone using &amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;part-iii-tool-summary&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Tool Summary ==&lt;br /&gt;
&lt;br /&gt;
This lab uses two distinct categories of tools: those that interact directly with the kernel’s security model, and those that manage the user-space databases.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;kernel-level-privilege-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== Kernel-Level &amp;amp;amp; Privilege Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands directly manipulate kernel-level concepts (file ownership, process UIDs) or are used for privilege escalation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;own&amp;#039;&amp;#039;&amp;#039;er. Changes the UID and GID owner of a file/directory. &amp;#039;&amp;#039;Requires kernel-level privilege (e.g., eUID=0) to change owner to someone else.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;user:group&amp;lt;/code&amp;gt;: Set both user and group&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chgrp&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;gr&amp;#039;&amp;#039;&amp;#039;ou&amp;#039;&amp;#039;&amp;#039;p&amp;#039;&amp;#039;&amp;#039;. Changes the GID owner of a file/directory. &amp;#039;&amp;#039;You must be the owner of the file AND a member of the target group.&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-R&amp;lt;/code&amp;gt;: Recursive&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Display process identity (UID, GID, groups).&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Show UID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g&amp;lt;/code&amp;gt;: Show GID only&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;: Show name instead of number&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser &amp;#039;&amp;#039;&amp;#039;do&amp;#039;&amp;#039;&amp;#039;. Executes a command as another user (default: root) based on &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; rules. Uses setuid.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-u user&amp;lt;/code&amp;gt;: Run as &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;: Start a root login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: List allowed commands&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;su&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;ubstitute &amp;#039;&amp;#039;&amp;#039;u&amp;#039;&amp;#039;&amp;#039;ser. Switches to another user account (default: root). Authenticates using the &amp;#039;&amp;#039;target&amp;#039;&amp;#039; user’s password.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Start a full login shell&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-c &amp;#039;cmd&amp;#039;&amp;lt;/code&amp;gt;: Run a single command&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-space-account-database-tools&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
=== User-Space Account Database Tools ===&lt;br /&gt;
&lt;br /&gt;
These commands read from or write to the user-space databases (e.g., &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;), which are then interpreted by NSS.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Command&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Description&lt;br /&gt;
! style=&amp;quot;text-align: left;&amp;quot;| Common Flags&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;passwd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Change a user’s password. Securely updates &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Change password for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-l&amp;lt;/code&amp;gt;: Lock account&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt;: Unlock account&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;getent&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Get ent&amp;#039;&amp;#039;&amp;#039;ries from NSS databases. The correct way to look up users/groups.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;passwd [user]&amp;lt;/code&amp;gt;: Look up user&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;group [group]&amp;lt;/code&amp;gt;: Look up group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;shadow [user]&amp;lt;/code&amp;gt;: Look up shadow entry&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;chfn&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;Ch&amp;#039;&amp;#039;&amp;#039;ange &amp;#039;&amp;#039;&amp;#039;f&amp;#039;&amp;#039;&amp;#039;i&amp;#039;&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;#039;ger. Modifies the GECOS field in &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Modify for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt; (root only)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;finger&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Displays user information, primarily by parsing the GECOS field.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[username]&amp;lt;/code&amp;gt;: Show info for &amp;lt;code&amp;gt;username&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new user account. Updates &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt;: Create home directory&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-g group&amp;lt;/code&amp;gt;: Set primary group&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-G groups&amp;lt;/code&amp;gt;: Set supplementary groups&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;-s shell&amp;lt;/code&amp;gt;: Set login shell&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;groupadd&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| Creates a new group. Updates &amp;lt;code&amp;gt;/etc/group&amp;lt;/code&amp;gt;.&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot;| &amp;lt;code&amp;gt;[groupname]&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;deliverables-and-assessment&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document (PDF or Markdown) containing:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Command Output:&amp;#039;&amp;#039;&amp;#039; Screenshots or text-based command output for each non-optional hands-on exercise (A-G), clearly labelled by section.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Short written answers to the “Conclusion Questions” below.&lt;br /&gt;
Grading will consider the completeness of your outputs, the correctness and clarity of your reasoning in the conclusion answers, and your adherence to instructions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;conclusion-questions&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Please answer the following summary questions based on the lab’s content.&lt;br /&gt;
&lt;br /&gt;
# Explain the kernel’s permission checking algorithm. If a file &amp;lt;code&amp;gt;/data/report.txt&amp;lt;/code&amp;gt; is owned by &amp;lt;code&amp;gt;root:webdev&amp;lt;/code&amp;gt; and has permissions &amp;lt;code&amp;gt;640&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;rw-r-----&amp;lt;/code&amp;gt;), can the user &amp;lt;code&amp;gt;student&amp;lt;/code&amp;gt; (who is a member of the &amp;lt;code&amp;gt;webdev&amp;lt;/code&amp;gt; group) &amp;#039;&amp;#039;read&amp;#039;&amp;#039; the file? Why or why not?&lt;br /&gt;
# Why was the sensitive password hash moved from the world-readable &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; to the root-only-readable &amp;lt;code&amp;gt;/etc/shadow&amp;lt;/code&amp;gt;? What kernel-level permission difference enables this security?&lt;br /&gt;
# Explain the relationship between the user-space tool &amp;lt;code&amp;gt;useradd&amp;lt;/code&amp;gt;, the file &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;, the Name Service Switch (NSS), and the kernel’s UID-based permission checks. How do they all interact when you run &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;?&lt;br /&gt;
# What would happen if you (as root) manually edited &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; and changed your user’s UID from &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;5001&amp;lt;/code&amp;gt;, but did &amp;#039;&amp;#039;not&amp;#039;&amp;#039; change the ownership of any files in your home directory (which are all still owned by kernel UID &amp;lt;code&amp;gt;1001&amp;lt;/code&amp;gt;)? What specific problems would you face at your next login?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8161</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8161"/>
		<updated>2025-10-24T12:43:01Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;br /&gt;
* Lab 4 - [[OS Lab 4 - Users, Groups and Permissions]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8160</id>
		<title>OS Lab 3 - Processes and Jobs</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8160"/>
		<updated>2025-10-17T13:32:48Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Conclusion Questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchical relationship between sessions, process groups, processes, and threads. The lab will cover methods for observing process states, managing concurrent tasks through job control, analyzing the structure of multi-process and multi-threaded applications, and inspecting system resource allocation, such as file descriptors and network sockets.&lt;br /&gt;
&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
* Upon successful completion of this laboratory exercise, the student will be able to:&lt;br /&gt;
* Differentiate between and identify Session IDs (SID), Process Group IDs (PGID), Process IDs (PID), Parent Process IDs (PPID), and Thread IDs (TID/SPID) in standard utility output.&lt;br /&gt;
* Describe the primary process states: Running (R), Interruptible Sleep (S), Uninterruptible Sleep (D), Stopped (T), and Zombie (Z).&lt;br /&gt;
* Describe the relationship between a controlling terminal, a session, a process group (shell job), a process, and a thread.&lt;br /&gt;
* Utilize standard Linux utilities to observe multi-threaded execution within a single process and multiple processes within a single process group (e.g., a command pipeline).&lt;br /&gt;
* Explain the concept of process niceness and its effect on CPU scheduling priority.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; to identify which processes have open file descriptors or network sockets.&lt;br /&gt;
* Employ shell job control mechanisms (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;) to manage long-running background tasks.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
The following packages must be installed to complete all sections of this exercise. Execute the commands below to update the package index and install the necessary utilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;p7zip-full&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; utility, which can be used to generate a multi-threaded, CPU-bound workload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iotop&amp;lt;/code&amp;gt;: Advanced, interactive process and I/O monitoring tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;: A utility to list open files and the processes that opened them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y p7zip-full htop iotop lsof&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
=== Process Hierarchy and Identifiers ===&lt;br /&gt;
The Linux kernel organizes execution into a strict hierarchy to manage resources and control.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process (PID):&amp;#039;&amp;#039;&amp;#039; A process is an instance of a running program. It has its own memory space, file descriptors, and other system resources. Each process is assigned a unique &amp;#039;&amp;#039;&amp;#039;Process ID (PID)&amp;#039;&amp;#039;&amp;#039;. The process that creates a new process is known as the parent, and its identifier is the &amp;#039;&amp;#039;&amp;#039;Parent Process ID (PPID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Thread (TID/SPID):&amp;#039;&amp;#039;&amp;#039; A thread is the smallest unit of execution that the kernel scheduler manages. A process can contain one or more threads, all of which share the process&amp;#039;s memory space and resources. In Linux, threads are sometimes referred to as light-weight processes. The &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command displays the &amp;#039;&amp;#039;&amp;#039;Thread ID&amp;#039;&amp;#039;&amp;#039; under the &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TID&amp;lt;/code&amp;gt; column. For a single-threaded process, the PID and TID are identical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process Group (PGID):&amp;#039;&amp;#039;&amp;#039; A process group is a collection of one or more processes. This abstraction is primarily used for job control. Signals can be sent to an entire process group at once. Each group has a unique &amp;#039;&amp;#039;&amp;#039;Process Group ID (PGID)&amp;#039;&amp;#039;&amp;#039;, which is typically the PID of the first process in the group (the group leader). All processes in a command pipeline share the same PGID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Session (SID):&amp;#039;&amp;#039;&amp;#039; A session is a collection of one or more process groups associated with a single controlling terminal. When a user logs out, a &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; signal is typically sent to all processes in the session, causing them to terminate. Each session has a unique &amp;#039;&amp;#039;&amp;#039;Session ID (SID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== Process States ===&lt;br /&gt;
A process can exist in several states throughout its lifecycle. The most common are:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;R (Running or Runnable):&amp;#039;&amp;#039;&amp;#039; The process is either currently executing on a CPU core or is in a run queue, ready for execution.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;S (Interruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is waiting for an event to complete, such as I/O from a terminal or the expiration of a timer. It can be interrupted by signals. This is the most common state for idle processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;D (Uninterruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is in a sleep state, typically waiting for a direct hardware I/O operation to complete (e.g., reading from a disk). It cannot be interrupted by signals to prevent data corruption. This state is usually short-lived.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;T (Stopped):&amp;#039;&amp;#039;&amp;#039; The process execution has been suspended by a signal, typically &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; (sent by Ctrl+Z) or &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;. It can be resumed by a &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Z (Zombie):&amp;#039;&amp;#039;&amp;#039; The process has terminated, but its entry in the process table has not yet been removed because its parent process has not yet read its exit status via the &amp;lt;code&amp;gt;wait()&amp;lt;/code&amp;gt; system call. The zombie process consumes no resources other than a process table slot.&lt;br /&gt;
&lt;br /&gt;
=== Job Control ===&lt;br /&gt;
An interactive shell provides mechanisms to manage jobs. A job is a shell&amp;#039;s representation of a process group.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Appending &amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; to a command starts it as a background job. The shell does not wait for its completion and immediately returns to the user prompt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Listing (&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Lists all active jobs associated with the current shell session.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Foreground (&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Moves job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; from the background to the foreground, reattaching it to the terminal&amp;#039;s input/output.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Resumes a stopped job &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, keeping it running in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signaling (&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Sends a signal to the process group associated with job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;. By default, this is the &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; signal (graceful termination).&lt;br /&gt;
&lt;br /&gt;
== Experimental Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Job Control Familiarization ===&lt;br /&gt;
Execute a long-running command in the background. Note the job number and PID reported by the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 600 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the active jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jobs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bring the job to the foreground, then suspend it using the &amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt; key combination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fg %1&lt;br /&gt;
# Press Ctrl+Z&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resume the suspended job in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
bg %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Terminate the job using its job number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Observing Process Identifiers ===&lt;br /&gt;
Execute the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command with options to display thread-level information (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;) and custom output format (&amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Observe that all processes spawned from your terminal share the same &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;. For single-threaded processes like your shell (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;), the &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; are identical.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide 3–6 lines of the output and annotate one full row to label the &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PPID&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
=== Manipulating Process States ===&lt;br /&gt;
Start a background process and observe its initial &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 1000 &amp;amp;&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; signal to stop the job, placing it in the &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -TSTP %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal to continue the job, returning it to the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -CONT %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines showing the process in the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. For each state, provide a one-line explanation of what that state signifies.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multi-threading in a Single Process ===&lt;br /&gt;
Execute a multi-threaded CPU benchmark in the background. The &amp;lt;code&amp;gt;-mmt&amp;lt;/code&amp;gt; flag enables multi-threading.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Display the process and thread information for the benchmark.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pgid,pid,ppid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The output will show multiple lines with the same &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but distinct &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt;s. This confirms that the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; application has spawned multiple execution threads to perform its benchmark, all sharing the same process resources.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide a snapshot of the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output where one PID is associated with multiple SPIDs.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multiple Processes in a Single Process Group ===&lt;br /&gt;
Execute a pipeline of commands in the background. This pipeline generates continuous output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
yes | tr &amp;#039;y&amp;#039; &amp;#039;Y&amp;#039; | dd of=/dev/null &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the processes associated with this pipeline.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note that the &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;dd&amp;lt;/code&amp;gt; processes each have a unique &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but all share the same &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;. The shell places them in the same process group to allow them to be managed as a single unit via job control.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing at least two different PIDs that share the same PGID.&lt;br /&gt;
&lt;br /&gt;
=== Modifying Process Scheduling Priority (Niceness) ===&lt;br /&gt;
Run the same multi-threaded benchmark, but with a modified niceness value. A higher niceness value (range -20 to 19) suggests to the kernel scheduler that the process is lower priority.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nice -n 10 7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe the niceness (&amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt;) column in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Optional) For a dynamic view, run &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; in another terminal and observe the &amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%CPU&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; State the niceness value used and provide one line of &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing this value in the &amp;lt;code&amp;gt;ni&amp;lt;/code&amp;gt; column.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting Open Files and Sockets ===&lt;br /&gt;
Start a simple web server in the background. It will listen on TCP port 8000 by default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
python3 -m http.server 8000 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify which process is listening on TCP port 8000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof -iTCP:8000 -sTCP:LISTEN&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inspect the file descriptors (FDs) open by your current shell process. &amp;lt;code&amp;gt;$$&amp;lt;/code&amp;gt; is a shell variable that expands to the PID of the current shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsof -p $$ | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
File descriptors are integer handles that a process uses to refer to open files, sockets, or other I/O resources. FDs 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Provide the output that identifies the PID holding TCP port 8000, and one line from the &amp;lt;code&amp;gt;lsof -p $$&amp;lt;/code&amp;gt; output showing an open file descriptor.&lt;br /&gt;
&lt;br /&gt;
=== Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error ===&lt;br /&gt;
If not already present from a previous lab, create and mount a loopback filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These commands are for context; reuse your existing setup if possible.&lt;br /&gt;
&lt;br /&gt;
dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
&lt;br /&gt;
mkfs.ext4 loop.img&lt;br /&gt;
&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
sudo mount loop.img /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Induce a &amp;quot;busy&amp;quot; state. The simplest method is to change the current working directory of a process to be inside the mount point. Open a new terminal or use your existing one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a &amp;#039;&amp;#039;different&amp;#039;&amp;#039; terminal, attempt to unmount the filesystem. This will fail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify the process that is preventing the unmount.&lt;br /&gt;
&lt;br /&gt;
* Using &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fuser -vm /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Using &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof +D /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; Provide the command and output that identified the conflicting process. In one sentence, explain why the &amp;lt;code&amp;gt;umount&amp;lt;/code&amp;gt; operation failed, referencing the information from the command&amp;#039;s output.&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Provide concise answers to the following questions:&lt;br /&gt;
&lt;br /&gt;
# Define the following terms in the context of a Linux operating system: SID, PGID, PID, and SPID.&lt;br /&gt;
# For each process state (R, S, D, T, Z), provide one realistic scenario or cause. Explain what makes the D and Z states unique compared to the others.&lt;br /&gt;
# Describe the effect of the &amp;lt;code&amp;gt;nice -n 10&amp;lt;/code&amp;gt; command on the scheduling priority of the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; process as observed in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
# State one functional difference between the &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; utilities, based on observation during the benchmark task, or, if there was no observable difference, explain why there was none.&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing the outputs and answers for the following items.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Annotated &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing SID, PGID, PID, PPID, and SPID from section [[#Observing Process Identifiers]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Three &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines and explanations for the S, T, and S process states from section [[#Manipulating Process States]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing a single PID with multiple SPIDs from section [[#Observing Multi-threading in a Single Process]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing multiple PIDs with a common PGID from section [[#Observing Multiple Processes in a Single Process Group]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; The niceness value used and the corresponding &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output from section [[#Modifying Process Scheduling Priority (Niceness)]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Proof of PID holding TCP port 8000 and one line showing a file descriptor from section [[#Inspecting Open Files and Sockets]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; The command and output identifying the process preventing unmount, and a one-sentence explanation from section [[#Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Answer the questions in section [[#Conclusion Questions]].&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8159</id>
		<title>OS Lab 3 - Processes and Jobs</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8159"/>
		<updated>2025-10-17T13:31:32Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchical relationship between sessions, process groups, processes, and threads. The lab will cover methods for observing process states, managing concurrent tasks through job control, analyzing the structure of multi-process and multi-threaded applications, and inspecting system resource allocation, such as file descriptors and network sockets.&lt;br /&gt;
&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
* Upon successful completion of this laboratory exercise, the student will be able to:&lt;br /&gt;
* Differentiate between and identify Session IDs (SID), Process Group IDs (PGID), Process IDs (PID), Parent Process IDs (PPID), and Thread IDs (TID/SPID) in standard utility output.&lt;br /&gt;
* Describe the primary process states: Running (R), Interruptible Sleep (S), Uninterruptible Sleep (D), Stopped (T), and Zombie (Z).&lt;br /&gt;
* Describe the relationship between a controlling terminal, a session, a process group (shell job), a process, and a thread.&lt;br /&gt;
* Utilize standard Linux utilities to observe multi-threaded execution within a single process and multiple processes within a single process group (e.g., a command pipeline).&lt;br /&gt;
* Explain the concept of process niceness and its effect on CPU scheduling priority.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; to identify which processes have open file descriptors or network sockets.&lt;br /&gt;
* Employ shell job control mechanisms (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;) to manage long-running background tasks.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
The following packages must be installed to complete all sections of this exercise. Execute the commands below to update the package index and install the necessary utilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;p7zip-full&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; utility, which can be used to generate a multi-threaded, CPU-bound workload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iotop&amp;lt;/code&amp;gt;: Advanced, interactive process and I/O monitoring tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;: A utility to list open files and the processes that opened them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y p7zip-full htop iotop lsof&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
=== Process Hierarchy and Identifiers ===&lt;br /&gt;
The Linux kernel organizes execution into a strict hierarchy to manage resources and control.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process (PID):&amp;#039;&amp;#039;&amp;#039; A process is an instance of a running program. It has its own memory space, file descriptors, and other system resources. Each process is assigned a unique &amp;#039;&amp;#039;&amp;#039;Process ID (PID)&amp;#039;&amp;#039;&amp;#039;. The process that creates a new process is known as the parent, and its identifier is the &amp;#039;&amp;#039;&amp;#039;Parent Process ID (PPID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Thread (TID/SPID):&amp;#039;&amp;#039;&amp;#039; A thread is the smallest unit of execution that the kernel scheduler manages. A process can contain one or more threads, all of which share the process&amp;#039;s memory space and resources. In Linux, threads are sometimes referred to as light-weight processes. The &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command displays the &amp;#039;&amp;#039;&amp;#039;Thread ID&amp;#039;&amp;#039;&amp;#039; under the &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TID&amp;lt;/code&amp;gt; column. For a single-threaded process, the PID and TID are identical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process Group (PGID):&amp;#039;&amp;#039;&amp;#039; A process group is a collection of one or more processes. This abstraction is primarily used for job control. Signals can be sent to an entire process group at once. Each group has a unique &amp;#039;&amp;#039;&amp;#039;Process Group ID (PGID)&amp;#039;&amp;#039;&amp;#039;, which is typically the PID of the first process in the group (the group leader). All processes in a command pipeline share the same PGID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Session (SID):&amp;#039;&amp;#039;&amp;#039; A session is a collection of one or more process groups associated with a single controlling terminal. When a user logs out, a &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; signal is typically sent to all processes in the session, causing them to terminate. Each session has a unique &amp;#039;&amp;#039;&amp;#039;Session ID (SID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== Process States ===&lt;br /&gt;
A process can exist in several states throughout its lifecycle. The most common are:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;R (Running or Runnable):&amp;#039;&amp;#039;&amp;#039; The process is either currently executing on a CPU core or is in a run queue, ready for execution.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;S (Interruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is waiting for an event to complete, such as I/O from a terminal or the expiration of a timer. It can be interrupted by signals. This is the most common state for idle processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;D (Uninterruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is in a sleep state, typically waiting for a direct hardware I/O operation to complete (e.g., reading from a disk). It cannot be interrupted by signals to prevent data corruption. This state is usually short-lived.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;T (Stopped):&amp;#039;&amp;#039;&amp;#039; The process execution has been suspended by a signal, typically &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; (sent by Ctrl+Z) or &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;. It can be resumed by a &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Z (Zombie):&amp;#039;&amp;#039;&amp;#039; The process has terminated, but its entry in the process table has not yet been removed because its parent process has not yet read its exit status via the &amp;lt;code&amp;gt;wait()&amp;lt;/code&amp;gt; system call. The zombie process consumes no resources other than a process table slot.&lt;br /&gt;
&lt;br /&gt;
=== Job Control ===&lt;br /&gt;
An interactive shell provides mechanisms to manage jobs. A job is a shell&amp;#039;s representation of a process group.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Appending &amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; to a command starts it as a background job. The shell does not wait for its completion and immediately returns to the user prompt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Listing (&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Lists all active jobs associated with the current shell session.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Foreground (&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Moves job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; from the background to the foreground, reattaching it to the terminal&amp;#039;s input/output.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Resumes a stopped job &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, keeping it running in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signaling (&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Sends a signal to the process group associated with job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;. By default, this is the &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; signal (graceful termination).&lt;br /&gt;
&lt;br /&gt;
== Experimental Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Job Control Familiarization ===&lt;br /&gt;
Execute a long-running command in the background. Note the job number and PID reported by the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 600 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the active jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jobs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bring the job to the foreground, then suspend it using the &amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt; key combination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fg %1&lt;br /&gt;
# Press Ctrl+Z&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resume the suspended job in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
bg %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Terminate the job using its job number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Observing Process Identifiers ===&lt;br /&gt;
Execute the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command with options to display thread-level information (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;) and custom output format (&amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Observe that all processes spawned from your terminal share the same &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;. For single-threaded processes like your shell (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;), the &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; are identical.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide 3–6 lines of the output and annotate one full row to label the &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PPID&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
=== Manipulating Process States ===&lt;br /&gt;
Start a background process and observe its initial &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 1000 &amp;amp;&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; signal to stop the job, placing it in the &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -TSTP %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal to continue the job, returning it to the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -CONT %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines showing the process in the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. For each state, provide a one-line explanation of what that state signifies.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multi-threading in a Single Process ===&lt;br /&gt;
Execute a multi-threaded CPU benchmark in the background. The &amp;lt;code&amp;gt;-mmt&amp;lt;/code&amp;gt; flag enables multi-threading.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Display the process and thread information for the benchmark.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pgid,pid,ppid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The output will show multiple lines with the same &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but distinct &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt;s. This confirms that the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; application has spawned multiple execution threads to perform its benchmark, all sharing the same process resources.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide a snapshot of the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output where one PID is associated with multiple SPIDs.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multiple Processes in a Single Process Group ===&lt;br /&gt;
Execute a pipeline of commands in the background. This pipeline generates continuous output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
yes | tr &amp;#039;y&amp;#039; &amp;#039;Y&amp;#039; | dd of=/dev/null &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the processes associated with this pipeline.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note that the &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;dd&amp;lt;/code&amp;gt; processes each have a unique &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but all share the same &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;. The shell places them in the same process group to allow them to be managed as a single unit via job control.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing at least two different PIDs that share the same PGID.&lt;br /&gt;
&lt;br /&gt;
=== Modifying Process Scheduling Priority (Niceness) ===&lt;br /&gt;
Run the same multi-threaded benchmark, but with a modified niceness value. A higher niceness value (range -20 to 19) suggests to the kernel scheduler that the process is lower priority.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nice -n 10 7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe the niceness (&amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt;) column in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Optional) For a dynamic view, run &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; in another terminal and observe the &amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%CPU&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; State the niceness value used and provide one line of &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing this value in the &amp;lt;code&amp;gt;ni&amp;lt;/code&amp;gt; column.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting Open Files and Sockets ===&lt;br /&gt;
Start a simple web server in the background. It will listen on TCP port 8000 by default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
python3 -m http.server 8000 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify which process is listening on TCP port 8000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof -iTCP:8000 -sTCP:LISTEN&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inspect the file descriptors (FDs) open by your current shell process. &amp;lt;code&amp;gt;$$&amp;lt;/code&amp;gt; is a shell variable that expands to the PID of the current shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsof -p $$ | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
File descriptors are integer handles that a process uses to refer to open files, sockets, or other I/O resources. FDs 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Provide the output that identifies the PID holding TCP port 8000, and one line from the &amp;lt;code&amp;gt;lsof -p $$&amp;lt;/code&amp;gt; output showing an open file descriptor.&lt;br /&gt;
&lt;br /&gt;
=== Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error ===&lt;br /&gt;
If not already present from a previous lab, create and mount a loopback filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These commands are for context; reuse your existing setup if possible.&lt;br /&gt;
&lt;br /&gt;
dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
&lt;br /&gt;
mkfs.ext4 loop.img&lt;br /&gt;
&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
sudo mount loop.img /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Induce a &amp;quot;busy&amp;quot; state. The simplest method is to change the current working directory of a process to be inside the mount point. Open a new terminal or use your existing one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a &amp;#039;&amp;#039;different&amp;#039;&amp;#039; terminal, attempt to unmount the filesystem. This will fail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify the process that is preventing the unmount.&lt;br /&gt;
&lt;br /&gt;
* Using &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fuser -vm /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Using &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof +D /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; Provide the command and output that identified the conflicting process. In one sentence, explain why the &amp;lt;code&amp;gt;umount&amp;lt;/code&amp;gt; operation failed, referencing the information from the command&amp;#039;s output.&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Provide concise answers to the following questions:&lt;br /&gt;
&lt;br /&gt;
# Define the following terms in the context of a Linux operating system: SID, PGID, PID, and SPID.&lt;br /&gt;
# For each process state (R, S, D, T, Z), provide one realistic scenario or cause. Explain what makes the D and Z states unique compared to the others.&lt;br /&gt;
# Describe the effect of the &amp;lt;code&amp;gt;nice -n 10&amp;lt;/code&amp;gt; command on the scheduling priority of the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; process as observed in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
# State one functional difference between the &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; utilities, based on observation during the benchmark task.&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing the outputs and answers for the following items.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Annotated &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing SID, PGID, PID, PPID, and SPID from section [[#Observing Process Identifiers]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Three &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines and explanations for the S, T, and S process states from section [[#Manipulating Process States]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing a single PID with multiple SPIDs from section [[#Observing Multi-threading in a Single Process]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing multiple PIDs with a common PGID from section [[#Observing Multiple Processes in a Single Process Group]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; The niceness value used and the corresponding &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output from section [[#Modifying Process Scheduling Priority (Niceness)]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Proof of PID holding TCP port 8000 and one line showing a file descriptor from section [[#Inspecting Open Files and Sockets]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; The command and output identifying the process preventing unmount, and a one-sentence explanation from section [[#Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Answer the questions in section [[#Conclusion Questions]].&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8158</id>
		<title>OS Lab 3 - Processes and Jobs</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8158"/>
		<updated>2025-10-17T13:29:13Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchical relationship between sessions, process groups, processes, and threads. The lab will cover methods for observing process states, managing concurrent tasks through job control, analyzing the structure of multi-process and multi-threaded applications, and inspecting system resource allocation, such as file descriptors and network sockets.&lt;br /&gt;
&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
* Upon successful completion of this laboratory exercise, the student will be able to:&lt;br /&gt;
* Differentiate between and identify Session IDs (SID), Process Group IDs (PGID), Process IDs (PID), Parent Process IDs (PPID), and Thread IDs (TID/SPID) in standard utility output.&lt;br /&gt;
* Describe the primary process states: Running (R), Interruptible Sleep (S), Uninterruptible Sleep (D), Stopped (T), and Zombie (Z).&lt;br /&gt;
* Describe the relationship between a controlling terminal, a session, a process group (shell job), a process, and a thread.&lt;br /&gt;
* Utilize standard Linux utilities to observe multi-threaded execution within a single process and multiple processes within a single process group (e.g., a command pipeline).&lt;br /&gt;
* Explain the concept of process niceness and its effect on CPU scheduling priority.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; to identify which processes have open file descriptors or network sockets.&lt;br /&gt;
* Employ shell job control mechanisms (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;) to manage long-running background tasks.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
The following packages must be installed to complete all sections of this exercise. Execute the commands below to update the package index and install the necessary utilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;p7zip-full&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; utility, which can be used to generate a multi-threaded, CPU-bound workload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iotop&amp;lt;/code&amp;gt;: Advanced, interactive process and I/O monitoring tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;: A utility to list open files and the processes that opened them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y p7zip-full htop iotop lsof&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
=== Process Hierarchy and Identifiers ===&lt;br /&gt;
The Linux kernel organizes execution into a strict hierarchy to manage resources and control.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process (PID):&amp;#039;&amp;#039;&amp;#039; A process is an instance of a running program. It has its own memory space, file descriptors, and other system resources. Each process is assigned a unique &amp;#039;&amp;#039;&amp;#039;Process ID (PID)&amp;#039;&amp;#039;&amp;#039;. The process that creates a new process is known as the parent, and its identifier is the &amp;#039;&amp;#039;&amp;#039;Parent Process ID (PPID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Thread (TID/SPID):&amp;#039;&amp;#039;&amp;#039; A thread is the smallest unit of execution that the kernel scheduler manages. A process can contain one or more threads, all of which share the process&amp;#039;s memory space and resources. In Linux, threads are sometimes referred to as light-weight processes. The &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command displays the &amp;#039;&amp;#039;&amp;#039;Thread ID&amp;#039;&amp;#039;&amp;#039; under the &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TID&amp;lt;/code&amp;gt; column. For a single-threaded process, the PID and TID are identical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process Group (PGID):&amp;#039;&amp;#039;&amp;#039; A process group is a collection of one or more processes. This abstraction is primarily used for job control. Signals can be sent to an entire process group at once. Each group has a unique &amp;#039;&amp;#039;&amp;#039;Process Group ID (PGID)&amp;#039;&amp;#039;&amp;#039;, which is typically the PID of the first process in the group (the group leader). All processes in a command pipeline share the same PGID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Session (SID):&amp;#039;&amp;#039;&amp;#039; A session is a collection of one or more process groups associated with a single controlling terminal. When a user logs out, a &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; signal is typically sent to all processes in the session, causing them to terminate. Each session has a unique &amp;#039;&amp;#039;&amp;#039;Session ID (SID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== Process States ===&lt;br /&gt;
A process can exist in several states throughout its lifecycle. The most common are:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;R (Running or Runnable):&amp;#039;&amp;#039;&amp;#039; The process is either currently executing on a CPU core or is in a run queue, ready for execution.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;S (Interruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is waiting for an event to complete, such as I/O from a terminal or the expiration of a timer. It can be interrupted by signals. This is the most common state for idle processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;D (Uninterruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is in a sleep state, typically waiting for a direct hardware I/O operation to complete (e.g., reading from a disk). It cannot be interrupted by signals to prevent data corruption. This state is usually short-lived.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;T (Stopped):&amp;#039;&amp;#039;&amp;#039; The process execution has been suspended by a signal, typically &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; (sent by Ctrl+Z) or &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;. It can be resumed by a &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Z (Zombie):&amp;#039;&amp;#039;&amp;#039; The process has terminated, but its entry in the process table has not yet been removed because its parent process has not yet read its exit status via the &amp;lt;code&amp;gt;wait()&amp;lt;/code&amp;gt; system call. The zombie process consumes no resources other than a process table slot.&lt;br /&gt;
&lt;br /&gt;
=== Job Control ===&lt;br /&gt;
An interactive shell provides mechanisms to manage jobs. A job is a shell&amp;#039;s representation of a process group.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Appending &amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; to a command starts it as a background job. The shell does not wait for its completion and immediately returns to the user prompt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Listing (&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Lists all active jobs associated with the current shell session.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Foreground (&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Moves job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; from the background to the foreground, reattaching it to the terminal&amp;#039;s input/output.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Resumes a stopped job &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, keeping it running in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signaling (&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Sends a signal to the process group associated with job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;. By default, this is the &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; signal (graceful termination).&lt;br /&gt;
&lt;br /&gt;
== Experimental Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Job Control Familiarization ===&lt;br /&gt;
Execute a long-running command in the background. Note the job number and PID reported by the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 600 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the active jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jobs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bring the job to the foreground, then suspend it using the &amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt; key combination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fg %1&lt;br /&gt;
# Press Ctrl+Z&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resume the suspended job in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
bg %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Terminate the job using its job number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Observing Process Identifiers ===&lt;br /&gt;
Execute the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command with options to display thread-level information (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;) and custom output format (&amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Observe that all processes spawned from your terminal share the same &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;. For single-threaded processes like your shell (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;), the &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; are identical.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide 3–6 lines of the output and annotate one full row to label the &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PPID&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
=== Manipulating Process States ===&lt;br /&gt;
Start a background process and observe its initial &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 1000 &amp;amp;&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; signal to stop the job, placing it in the &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -TSTP %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal to continue the job, returning it to the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -CONT %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines showing the process in the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. For each state, provide a one-line explanation of what that state signifies.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multi-threading in a Single Process ===&lt;br /&gt;
Execute a multi-threaded CPU benchmark in the background. The &amp;lt;code&amp;gt;-mmt&amp;lt;/code&amp;gt; flag enables multi-threading.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Display the process and thread information for the benchmark.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pgid,pid,ppid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The output will show multiple lines with the same &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but distinct &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt;s. This confirms that the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; application has spawned multiple execution threads to perform its benchmark, all sharing the same process resources.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide a snapshot of the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output where one PID is associated with multiple SPIDs.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multiple Processes in a Single Process Group ===&lt;br /&gt;
Execute a pipeline of commands in the background. This pipeline generates continuous output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
yes | tr &amp;#039;y&amp;#039; &amp;#039;Y&amp;#039; | dd of=/dev/null &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the processes associated with this pipeline.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note that the &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;dd&amp;lt;/code&amp;gt; processes each have a unique &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but all share the same &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;. The shell places them in the same process group to allow them to be managed as a single unit via job control.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing at least two different PIDs that share the same PGID.&lt;br /&gt;
&lt;br /&gt;
=== Modifying Process Scheduling Priority (Niceness) ===&lt;br /&gt;
Run the same multi-threaded benchmark, but with a modified niceness value. A higher niceness value (range -20 to 19) suggests to the kernel scheduler that the process is lower priority.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nice -n 10 7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe the niceness (&amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt;) column in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pid,spid,ni,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Optional) For a dynamic view, run &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; in another terminal and observe the &amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%CPU&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; State the niceness value used and provide one line of &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing this value in the &amp;lt;code&amp;gt;ni&amp;lt;/code&amp;gt; column.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting Open Files and Sockets ===&lt;br /&gt;
Start a simple web server in the background. It will listen on TCP port 8000 by default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
python3 -m http.server 8000 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify which process is listening on TCP port 8000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof -iTCP:8000 -sTCP:LISTEN&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inspect the file descriptors (FDs) open by your current shell process. &amp;lt;code&amp;gt;$$&amp;lt;/code&amp;gt; is a shell variable that expands to the PID of the current shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsof -p $$ | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
File descriptors are integer handles that a process uses to refer to open files, sockets, or other I/O resources. FDs 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Provide the output that identifies the PID holding TCP port 8000, and one line from the &amp;lt;code&amp;gt;lsof -p $$&amp;lt;/code&amp;gt; output showing an open file descriptor.&lt;br /&gt;
&lt;br /&gt;
=== Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error ===&lt;br /&gt;
If not already present from a previous lab, create and mount a loopback filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These commands are for context; reuse your existing setup if possible.&lt;br /&gt;
&lt;br /&gt;
dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
&lt;br /&gt;
mkfs.ext4 loop.img&lt;br /&gt;
&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
sudo mount loop.img /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Induce a &amp;quot;busy&amp;quot; state. The simplest method is to change the current working directory of a process to be inside the mount point. Open a new terminal or use your existing one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a &amp;#039;&amp;#039;different&amp;#039;&amp;#039; terminal, attempt to unmount the filesystem. This will fail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify the process that is preventing the unmount.&lt;br /&gt;
&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fuser -vm /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof +D /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; Provide the command and output that identified the conflicting process. In one sentence, explain why the &amp;lt;code&amp;gt;umount&amp;lt;/code&amp;gt; operation failed, referencing the information from the command&amp;#039;s output.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Provide concise answers to the following questions:&lt;br /&gt;
&lt;br /&gt;
# Define the following terms in the context of a Linux operating system: SID, PGID, PID, and SPID.&lt;br /&gt;
# For each process state (R, S, D, T, Z), provide one realistic scenario or cause. Explain what makes the D and Z states unique compared to the others.&lt;br /&gt;
# Describe the effect of the &amp;lt;code&amp;gt;nice -n 10&amp;lt;/code&amp;gt; command on the scheduling priority of the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; process as observed in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
# State one functional difference between the &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; utilities, based on observation during the benchmark task.&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing the outputs and answers for the following items.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Annotated &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing SID, PGID, PID, PPID, and SPID from section [[#Observing Process Identifiers]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Three &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines and explanations for the S, T, and S process states from section [[#Manipulating Process States]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing a single PID with multiple SPIDs from section [[#Observing Multi-threading in a Single Process]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing multiple PIDs with a common PGID from section [[#Observing Multiple Processes in a Single Process Group]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; The niceness value used and the corresponding &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output from section [[#Modifying Process Scheduling Priority (Niceness)]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Proof of PID holding TCP port 8000 and one line showing a file descriptor from section [[#Inspecting Open Files and Sockets]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; The command and output identifying the process preventing unmount, and a one-sentence explanation from section [[#Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Answer the questions in section [[#Conclusion Questions]].&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8157</id>
		<title>OS Lab 3 - Processes and Jobs</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8157"/>
		<updated>2025-10-17T13:28:04Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchical relationship between sessions, process groups, processes, and threads. The lab will cover methods for observing process states, managing concurrent tasks through job control, analyzing the structure of multi-process and multi-threaded applications, and inspecting system resource allocation, such as file descriptors and network sockets.&lt;br /&gt;
&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
* Upon successful completion of this laboratory exercise, the student will be able to:&lt;br /&gt;
* Differentiate between and identify Session IDs (SID), Process Group IDs (PGID), Process IDs (PID), Parent Process IDs (PPID), and Thread IDs (TID/SPID) in standard utility output.&lt;br /&gt;
* Describe the primary process states: Running (R), Interruptible Sleep (S), Uninterruptible Sleep (D), Stopped (T), and Zombie (Z).&lt;br /&gt;
* Describe the relationship between a controlling terminal, a session, a process group (shell job), a process, and a thread.&lt;br /&gt;
* Utilize standard Linux utilities to observe multi-threaded execution within a single process and multiple processes within a single process group (e.g., a command pipeline).&lt;br /&gt;
* Explain the concept of process niceness and its effect on CPU scheduling priority.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; to identify which processes have open file descriptors or network sockets.&lt;br /&gt;
* Employ shell job control mechanisms (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;) to manage long-running background tasks.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
The following packages must be installed to complete all sections of this exercise. Execute the commands below to update the package index and install the necessary utilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;p7zip-full&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; utility, which can be used to generate a multi-threaded, CPU-bound workload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iotop&amp;lt;/code&amp;gt;: Advanced, interactive process and I/O monitoring tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;: A utility to list open files and the processes that opened them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y p7zip-full htop iotop lsof&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
=== Process Hierarchy and Identifiers ===&lt;br /&gt;
The Linux kernel organizes execution into a strict hierarchy to manage resources and control.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process (PID):&amp;#039;&amp;#039;&amp;#039; A process is an instance of a running program. It has its own memory space, file descriptors, and other system resources. Each process is assigned a unique &amp;#039;&amp;#039;&amp;#039;Process ID (PID)&amp;#039;&amp;#039;&amp;#039;. The process that creates a new process is known as the parent, and its identifier is the &amp;#039;&amp;#039;&amp;#039;Parent Process ID (PPID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Thread (TID/SPID):&amp;#039;&amp;#039;&amp;#039; A thread is the smallest unit of execution that the kernel scheduler manages. A process can contain one or more threads, all of which share the process&amp;#039;s memory space and resources. In Linux, threads are sometimes referred to as light-weight processes. The &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command displays the &amp;#039;&amp;#039;&amp;#039;Thread ID&amp;#039;&amp;#039;&amp;#039; under the &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TID&amp;lt;/code&amp;gt; column. For a single-threaded process, the PID and TID are identical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process Group (PGID):&amp;#039;&amp;#039;&amp;#039; A process group is a collection of one or more processes. This abstraction is primarily used for job control. Signals can be sent to an entire process group at once. Each group has a unique &amp;#039;&amp;#039;&amp;#039;Process Group ID (PGID)&amp;#039;&amp;#039;&amp;#039;, which is typically the PID of the first process in the group (the group leader). All processes in a command pipeline share the same PGID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Session (SID):&amp;#039;&amp;#039;&amp;#039; A session is a collection of one or more process groups associated with a single controlling terminal. When a user logs out, a &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; signal is typically sent to all processes in the session, causing them to terminate. Each session has a unique &amp;#039;&amp;#039;&amp;#039;Session ID (SID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== Process States ===&lt;br /&gt;
A process can exist in several states throughout its lifecycle. The most common are:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;R (Running or Runnable):&amp;#039;&amp;#039;&amp;#039; The process is either currently executing on a CPU core or is in a run queue, ready for execution.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;S (Interruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is waiting for an event to complete, such as I/O from a terminal or the expiration of a timer. It can be interrupted by signals. This is the most common state for idle processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;D (Uninterruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is in a sleep state, typically waiting for a direct hardware I/O operation to complete (e.g., reading from a disk). It cannot be interrupted by signals to prevent data corruption. This state is usually short-lived.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;T (Stopped):&amp;#039;&amp;#039;&amp;#039; The process execution has been suspended by a signal, typically &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; (sent by Ctrl+Z) or &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;. It can be resumed by a &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Z (Zombie):&amp;#039;&amp;#039;&amp;#039; The process has terminated, but its entry in the process table has not yet been removed because its parent process has not yet read its exit status via the &amp;lt;code&amp;gt;wait()&amp;lt;/code&amp;gt; system call. The zombie process consumes no resources other than a process table slot.&lt;br /&gt;
&lt;br /&gt;
=== Job Control ===&lt;br /&gt;
An interactive shell provides mechanisms to manage jobs. A job is a shell&amp;#039;s representation of a process group.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Appending &amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; to a command starts it as a background job. The shell does not wait for its completion and immediately returns to the user prompt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Listing (&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Lists all active jobs associated with the current shell session.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Foreground (&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Moves job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; from the background to the foreground, reattaching it to the terminal&amp;#039;s input/output.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Resumes a stopped job &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, keeping it running in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signaling (&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Sends a signal to the process group associated with job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;. By default, this is the &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; signal (graceful termination).&lt;br /&gt;
&lt;br /&gt;
== Experimental Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Job Control Familiarization ===&lt;br /&gt;
Execute a long-running command in the background. Note the job number and PID reported by the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 600 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the active jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jobs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bring the job to the foreground, then suspend it using the &amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt; key combination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fg %1&lt;br /&gt;
# Press Ctrl+Z&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resume the suspended job in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
bg %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Terminate the job using its job number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Observing Process Identifiers ===&lt;br /&gt;
Execute the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command with options to display thread-level information (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;) and custom output format (&amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Observe that all processes spawned from your terminal share the same &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;. For single-threaded processes like your shell (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;), the &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; are identical.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide 3–6 lines of the output and annotate one full row to label the &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PPID&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
=== Manipulating Process States ===&lt;br /&gt;
Start a background process and observe its initial &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. The &amp;lt;code&amp;gt;[s]leep&amp;lt;/code&amp;gt; pattern in &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; prevents the &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; process itself from appearing in the output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 1000 &amp;amp;&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; signal to stop the job, placing it in the &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -TSTP %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal to continue the job, returning it to the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -CONT %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines showing the process in the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. For each state, provide a one-line explanation of what that state signifies.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multi-threading in a Single Process ===&lt;br /&gt;
Execute a multi-threaded CPU benchmark in the background. The &amp;lt;code&amp;gt;-mmt&amp;lt;/code&amp;gt; flag enables multi-threading.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Display the process and thread information for the benchmark.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pgid,pid,ppid,spid,ni,state,cmd | grep &amp;#039;[7]z b&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The output will show multiple lines with the same &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but distinct &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt;s. This confirms that the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; application has spawned multiple execution threads to perform its benchmark, all sharing the same process resources.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide a snapshot of the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output where one PID is associated with multiple SPIDs.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multiple Processes in a Single Process Group ===&lt;br /&gt;
Execute a pipeline of commands in the background. This pipeline generates continuous output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
yes | tr &amp;#039;y&amp;#039; &amp;#039;Y&amp;#039; | dd of=/dev/null &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the processes associated with this pipeline.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd | egrep &amp;#039;yes |tr |dd &amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note that the &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;dd&amp;lt;/code&amp;gt; processes each have a unique &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but all share the same &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;. The shell places them in the same process group to allow them to be managed as a single unit via job control.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing at least two different PIDs that share the same PGID.&lt;br /&gt;
&lt;br /&gt;
=== Modifying Process Scheduling Priority (Niceness) ===&lt;br /&gt;
Run the same multi-threaded benchmark, but with a modified niceness value. A higher niceness value (range -20 to 19) suggests to the kernel scheduler that the process is lower priority.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nice -n 10 7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe the niceness (&amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt;) column in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pid,spid,ni,state,cmd | grep &amp;#039;[7]z b&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Optional) For a dynamic view, run &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; in another terminal and observe the &amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%CPU&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; State the niceness value used and provide one line of &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing this value in the &amp;lt;code&amp;gt;ni&amp;lt;/code&amp;gt; column.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting Open Files and Sockets ===&lt;br /&gt;
Start a simple web server in the background. It will listen on TCP port 8000 by default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
python3 -m http.server 8000 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify which process is listening on TCP port 8000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof -iTCP:8000 -sTCP:LISTEN&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inspect the file descriptors (FDs) open by your current shell process. &amp;lt;code&amp;gt;$$&amp;lt;/code&amp;gt; is a shell variable that expands to the PID of the current shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsof -p $$ | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
File descriptors are integer handles that a process uses to refer to open files, sockets, or other I/O resources. FDs 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Provide the output that identifies the PID holding TCP port 8000, and one line from the &amp;lt;code&amp;gt;lsof -p $$&amp;lt;/code&amp;gt; output showing an open file descriptor.&lt;br /&gt;
&lt;br /&gt;
=== Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error ===&lt;br /&gt;
If not already present from a previous lab, create and mount a loopback filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These commands are for context; reuse your existing setup if possible.&lt;br /&gt;
&lt;br /&gt;
dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
&lt;br /&gt;
mkfs.ext4 loop.img&lt;br /&gt;
&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
sudo mount loop.img /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Induce a &amp;quot;busy&amp;quot; state. The simplest method is to change the current working directory of a process to be inside the mount point. Open a new terminal or use your existing one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a &amp;#039;&amp;#039;different&amp;#039;&amp;#039; terminal, attempt to unmount the filesystem. This will fail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify the process that is preventing the unmount.&lt;br /&gt;
&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fuser -vm /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof +D /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; Provide the command and output that identified the conflicting process. In one sentence, explain why the &amp;lt;code&amp;gt;umount&amp;lt;/code&amp;gt; operation failed, referencing the information from the command&amp;#039;s output.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Provide concise answers to the following questions:&lt;br /&gt;
&lt;br /&gt;
# Define the following terms in the context of a Linux operating system: SID, PGID, PID, and SPID.&lt;br /&gt;
# For each process state (R, S, D, T, Z), provide one realistic scenario or cause. Explain what makes the D and Z states unique compared to the others.&lt;br /&gt;
# Describe the effect of the &amp;lt;code&amp;gt;nice -n 10&amp;lt;/code&amp;gt; command on the scheduling priority of the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; process as observed in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
# State one functional difference between the &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; utilities, based on observation during the benchmark task.&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing the outputs and answers for the following items.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Annotated &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing SID, PGID, PID, PPID, and SPID from section [[#Observing Process Identifiers]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Three &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines and explanations for the S, T, and S process states from section [[#Manipulating Process States]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing a single PID with multiple SPIDs from section [[#Observing Multi-threading in a Single Process]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing multiple PIDs with a common PGID from section [[#Observing Multiple Processes in a Single Process Group]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; The niceness value used and the corresponding &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output from section [[#Modifying Process Scheduling Priority (Niceness)]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Proof of PID holding TCP port 8000 and one line showing a file descriptor from section [[#Inspecting Open Files and Sockets]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; The command and output identifying the process preventing unmount, and a one-sentence explanation from section [[#Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Answer the questions in section [[#Conclusion Questions]].&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8156</id>
		<title>OS Lab 3 - Processes and Jobs</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_3_-_Processes_and_Jobs&amp;diff=8156"/>
		<updated>2025-10-17T13:26:22Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: Pagină nouă: This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchic...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This exercise examines the fundamental concepts of execution management within the Linux operating system. It builds upon foundational command-line skills to explore the hierarchical relationship between sessions, process groups, processes, and threads. The lab will cover methods for observing process states, managing concurrent tasks through job control, analyzing the structure of multi-process and multi-threaded applications, and inspecting system resource allocation, such as file descriptors and network sockets.&lt;br /&gt;
&lt;br /&gt;
== Objectives ==&lt;br /&gt;
&lt;br /&gt;
* Upon successful completion of this laboratory exercise, the student will be able to:&lt;br /&gt;
* Differentiate between and identify Session IDs (SID), Process Group IDs (PGID), Process IDs (PID), Parent Process IDs (PPID), and Thread IDs (TID/SPID) in standard utility output.&lt;br /&gt;
* Describe the primary process states: Running (R), Interruptible Sleep (S), Uninterruptible Sleep (D), Stopped (T), and Zombie (Z).&lt;br /&gt;
* Describe the relationship between a controlling terminal, a session, a process group (shell job), a process, and a thread.&lt;br /&gt;
* Utilize standard Linux utilities to observe multi-threaded execution within a single process and multiple processes within a single process group (e.g., a command pipeline).&lt;br /&gt;
* Explain the concept of process niceness and its effect on CPU scheduling priority.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; to identify which processes have open file descriptors or network sockets.&lt;br /&gt;
* Employ shell job control mechanisms (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bg&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;kill&amp;lt;/code&amp;gt;) to manage long-running background tasks.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== System Requirements ===&lt;br /&gt;
&lt;br /&gt;
A running instance of the course-provided Linux virtual machine with SSH or direct terminal access.&lt;br /&gt;
&lt;br /&gt;
=== Required Packages ===&lt;br /&gt;
The following packages must be installed to complete all sections of this exercise. Execute the commands below to update the package index and install the necessary utilities.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;p7zip-full&amp;lt;/code&amp;gt;: Provides the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; utility, which can be used to generate a multi-threaded, CPU-bound workload.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;iotop&amp;lt;/code&amp;gt;: Advanced, interactive process and I/O monitoring tools.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;: A utility to list open files and the processes that opened them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt update&lt;br /&gt;
sudo apt install -y p7zip-full htop iotop lsof&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theoretical Background ==&lt;br /&gt;
&lt;br /&gt;
=== Process Hierarchy and Identifiers ===&lt;br /&gt;
The Linux kernel organizes execution into a strict hierarchy to manage resources and control.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process (PID):&amp;#039;&amp;#039;&amp;#039; A process is an instance of a running program. It has its own memory space, file descriptors, and other system resources. Each process is assigned a unique &amp;#039;&amp;#039;&amp;#039;Process ID (PID)&amp;#039;&amp;#039;&amp;#039;. The process that creates a new process is known as the parent, and its identifier is the &amp;#039;&amp;#039;&amp;#039;Parent Process ID (PPID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Thread (TID/SPID):&amp;#039;&amp;#039;&amp;#039; A thread is the smallest unit of execution that the kernel scheduler manages. A process can contain one or more threads, all of which share the process&amp;#039;s memory space and resources. In Linux, threads are sometimes referred to as light-weight processes. The &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command displays the &amp;#039;&amp;#039;&amp;#039;Thread ID&amp;#039;&amp;#039;&amp;#039; under the &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;TID&amp;lt;/code&amp;gt; column. For a single-threaded process, the PID and TID are identical.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Process Group (PGID):&amp;#039;&amp;#039;&amp;#039; A process group is a collection of one or more processes. This abstraction is primarily used for job control. Signals can be sent to an entire process group at once. Each group has a unique &amp;#039;&amp;#039;&amp;#039;Process Group ID (PGID)&amp;#039;&amp;#039;&amp;#039;, which is typically the PID of the first process in the group (the group leader). All processes in a command pipeline share the same PGID.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Session (SID):&amp;#039;&amp;#039;&amp;#039; A session is a collection of one or more process groups associated with a single controlling terminal. When a user logs out, a &amp;lt;code&amp;gt;SIGHUP&amp;lt;/code&amp;gt; signal is typically sent to all processes in the session, causing them to terminate. Each session has a unique &amp;#039;&amp;#039;&amp;#039;Session ID (SID)&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== Process States ===&lt;br /&gt;
A process can exist in several states throughout its lifecycle. The most common are:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;R (Running or Runnable):&amp;#039;&amp;#039;&amp;#039; The process is either currently executing on a CPU core or is in a run queue, ready for execution.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;S (Interruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is waiting for an event to complete, such as I/O from a terminal or the expiration of a timer. It can be interrupted by signals. This is the most common state for idle processes.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;D (Uninterruptible Sleep):&amp;#039;&amp;#039;&amp;#039; The process is in a sleep state, typically waiting for a direct hardware I/O operation to complete (e.g., reading from a disk). It cannot be interrupted by signals to prevent data corruption. This state is usually short-lived.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;T (Stopped):&amp;#039;&amp;#039;&amp;#039; The process execution has been suspended by a signal, typically &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; (sent by Ctrl+Z) or &amp;lt;code&amp;gt;SIGSTOP&amp;lt;/code&amp;gt;. It can be resumed by a &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Z (Zombie):&amp;#039;&amp;#039;&amp;#039; The process has terminated, but its entry in the process table has not yet been removed because its parent process has not yet read its exit status via the &amp;lt;code&amp;gt;wait()&amp;lt;/code&amp;gt; system call. The zombie process consumes no resources other than a process table slot.&lt;br /&gt;
&lt;br /&gt;
=== Job Control ===&lt;br /&gt;
An interactive shell provides mechanisms to manage jobs. A job is a shell&amp;#039;s representation of a process group.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Appending &amp;lt;code&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; to a command starts it as a background job. The shell does not wait for its completion and immediately returns to the user prompt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Listing (&amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Lists all active jobs associated with the current shell session.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Foreground (&amp;lt;code&amp;gt;fg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Moves job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; from the background to the foreground, reattaching it to the terminal&amp;#039;s input/output.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Background (&amp;lt;code&amp;gt;bg %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Resumes a stopped job &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;, keeping it running in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Signaling (&amp;lt;code&amp;gt;kill %n&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Sends a signal to the process group associated with job number &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;. By default, this is the &amp;lt;code&amp;gt;SIGTERM&amp;lt;/code&amp;gt; signal (graceful termination).&lt;br /&gt;
&lt;br /&gt;
== Experimental Procedure ==&lt;br /&gt;
&lt;br /&gt;
=== Initial Job Control Familiarization ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To practice the basic job control commands.&lt;br /&gt;
&lt;br /&gt;
Execute a long-running command in the background. Note the job number and PID reported by the shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 600 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the active jobs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
jobs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bring the job to the foreground, then suspend it using the &amp;lt;code&amp;gt;Ctrl+Z&amp;lt;/code&amp;gt; key combination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fg %1&lt;br /&gt;
# Press Ctrl+Z&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Resume the suspended job in the background.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
bg %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Terminate the job using its job number.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill %1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Observing Process Identifiers ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To examine the full hierarchy of process identifiers for the current session.&lt;br /&gt;
&lt;br /&gt;
Execute the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; command with options to display thread-level information (&amp;lt;code&amp;gt;-L&amp;lt;/code&amp;gt;) and custom output format (&amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039; Observe that all processes spawned from your terminal share the same &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;. For single-threaded processes like your shell (&amp;lt;code&amp;gt;bash&amp;lt;/code&amp;gt;), the &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; are identical.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Provide 3–6 lines of the output and annotate one full row to label the &amp;lt;code&amp;gt;SID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PPID&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
=== Manipulating Process States ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To create and observe the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; (Sleep), &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; (Stopped), and &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; (resumed Sleep) states.&lt;br /&gt;
&lt;br /&gt;
Start a background process and observe its initial &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. The &amp;lt;code&amp;gt;[s]leep&amp;lt;/code&amp;gt; pattern in &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; prevents the &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; process itself from appearing in the output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sleep 1000 &amp;amp;&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGTSTP&amp;lt;/code&amp;gt; signal to stop the job, placing it in the &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -TSTP %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Send the &amp;lt;code&amp;gt;SIGCONT&amp;lt;/code&amp;gt; signal to continue the job, returning it to the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
kill -CONT %1&lt;br /&gt;
ps -L -o pid,spid,state,cmd | grep &amp;#039;[s]leep&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Provide the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines showing the process in the &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;T&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; state. For each state, provide a one-line explanation of what that state signifies.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multi-threading in a Single Process ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To observe multiple threads (TIDs) operating under a single process (PID).&lt;br /&gt;
&lt;br /&gt;
Execute a multi-threaded CPU benchmark in the background. The &amp;lt;code&amp;gt;-mmt&amp;lt;/code&amp;gt; flag enables multi-threading.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Display the process and thread information for the benchmark.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pgid,pid,ppid,spid,ni,state,cmd | grep &amp;#039;[7]z b&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039; The output will show multiple lines with the same &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but distinct &amp;lt;code&amp;gt;SPID&amp;lt;/code&amp;gt;s. This confirms that the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; application has spawned multiple execution threads to perform its benchmark, all sharing the same process resources.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; Provide a snapshot of the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output where one PID is associated with multiple SPIDs.&lt;br /&gt;
&lt;br /&gt;
=== Observing Multiple Processes in a Single Process Group ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To demonstrate that a command pipeline constitutes a single job (one PGID) composed of multiple distinct processes (PIDs).&lt;br /&gt;
&lt;br /&gt;
Execute a pipeline of commands in the background. This pipeline generates continuous output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
yes | tr &amp;#039;y&amp;#039; &amp;#039;Y&amp;#039; | dd of=/dev/null &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List the processes associated with this pipeline.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o sid,pgid,pid,ppid,spid,state,cmd | egrep &amp;#039;yes |tr |dd &amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039; Note that the &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;dd&amp;lt;/code&amp;gt; processes each have a unique &amp;lt;code&amp;gt;PID&amp;lt;/code&amp;gt; but all share the same &amp;lt;code&amp;gt;PGID&amp;lt;/code&amp;gt;. The shell places them in the same process group to allow them to be managed as a single unit via job control.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; Provide &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing at least two different PIDs that share the same PGID.&lt;br /&gt;
&lt;br /&gt;
=== Modifying Process Scheduling Priority (Niceness) ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To alter and observe a process&amp;#039;s scheduling priority.&lt;br /&gt;
&lt;br /&gt;
Run the same multi-threaded benchmark, but with a modified niceness value. A higher niceness value (range -20 to 19) suggests to the kernel scheduler that the process is lower priority.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
nice -n 10 7z b -mmt &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe the niceness (&amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt;) column in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ps -L -o pid,spid,ni,state,cmd | grep &amp;#039;[7]z b&amp;#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Optional) For a dynamic view, run &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; in another terminal and observe the &amp;lt;code&amp;gt;NI&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;%CPU&amp;lt;/code&amp;gt; columns.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; State the niceness value used and provide one line of &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing this value in the &amp;lt;code&amp;gt;ni&amp;lt;/code&amp;gt; column.&lt;br /&gt;
&lt;br /&gt;
=== Inspecting Open Files and Sockets ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To identify which process is holding a specific network port or file descriptor.&lt;br /&gt;
&lt;br /&gt;
Start a simple web server in the background. It will listen on TCP port 8000 by default.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
python3 -m http.server 8000 &amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify which process is listening on TCP port 8000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof -iTCP:8000 -sTCP:LISTEN&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inspect the file descriptors (FDs) open by your current shell process. &amp;lt;code&amp;gt;$$&amp;lt;/code&amp;gt; is a shell variable that expands to the PID of the current shell.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsof -p $$ | head&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Analysis:&amp;#039;&amp;#039;&amp;#039; File descriptors are integer handles that a process uses to refer to open files, sockets, or other I/O resources. FDs 0, 1, and 2 correspond to standard input, standard output, and standard error, respectively.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Provide the output that identifies the PID holding TCP port 8000, and one line from the &amp;lt;code&amp;gt;lsof -p $$&amp;lt;/code&amp;gt; output showing an open file descriptor.&lt;br /&gt;
&lt;br /&gt;
=== Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective:&amp;#039;&amp;#039;&amp;#039; To apply file inspection utilities to resolve a common system administration problem.&lt;br /&gt;
&lt;br /&gt;
If not already present from a previous lab, create and mount a loopback filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These commands are for context; reuse your existing setup if possible.&lt;br /&gt;
&lt;br /&gt;
dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
&lt;br /&gt;
mkfs.ext4 loop.img&lt;br /&gt;
&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
sudo mount loop.img /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Induce a &amp;quot;busy&amp;quot; state. The simplest method is to change the current working directory of a process to be inside the mount point. Open a new terminal or use your existing one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a &amp;#039;&amp;#039;different&amp;#039;&amp;#039; terminal, attempt to unmount the filesystem. This will fail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt; to identify the process that is preventing the unmount.&lt;br /&gt;
&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;fuser&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
fuser -vm /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#* Using &amp;lt;code&amp;gt;lsof&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo lsof +D /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; Provide the command and output that identified the conflicting process. In one sentence, explain why the &amp;lt;code&amp;gt;umount&amp;lt;/code&amp;gt; operation failed, referencing the information from the command&amp;#039;s output.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Conclusion Questions ==&lt;br /&gt;
&lt;br /&gt;
Provide concise answers to the following questions:&lt;br /&gt;
&lt;br /&gt;
# Define the following terms in the context of a Linux operating system: SID, PGID, PID, and SPID.&lt;br /&gt;
# For each process state (R, S, D, T, Z), provide one realistic scenario or cause. Explain what makes the D and Z states unique compared to the others.&lt;br /&gt;
# Describe the effect of the &amp;lt;code&amp;gt;nice -n 10&amp;lt;/code&amp;gt; command on the scheduling priority of the &amp;lt;code&amp;gt;7z&amp;lt;/code&amp;gt; process as observed in the &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output.&lt;br /&gt;
# State one functional difference between the &amp;lt;code&amp;gt;top&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;htop&amp;lt;/code&amp;gt; utilities, based on observation during the benchmark task.&lt;br /&gt;
&lt;br /&gt;
== Deliverables and Assessment ==&lt;br /&gt;
&lt;br /&gt;
Submit a single document containing the outputs and answers for the following items.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable A:&amp;#039;&amp;#039;&amp;#039; Annotated &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing SID, PGID, PID, PPID, and SPID from section [[#Observing Process Identifiers]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable B:&amp;#039;&amp;#039;&amp;#039; Three &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output lines and explanations for the S, T, and S process states from section [[#Manipulating Process States]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable C:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing a single PID with multiple SPIDs from section [[#Observing Multi-threading in a Single Process]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable D:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output showing multiple PIDs with a common PGID from section [[#Observing Multiple Processes in a Single Process Group]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable E:&amp;#039;&amp;#039;&amp;#039; The niceness value used and the corresponding &amp;lt;code&amp;gt;ps&amp;lt;/code&amp;gt; output from section [[#Modifying Process Scheduling Priority (Niceness)]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable F:&amp;#039;&amp;#039;&amp;#039; Proof of PID holding TCP port 8000 and one line showing a file descriptor from section [[#Inspecting Open Files and Sockets]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Deliverable G:&amp;#039;&amp;#039;&amp;#039; The command and output identifying the process preventing unmount, and a one-sentence explanation from section [[#Use Case: Diagnosing a &amp;quot;Target is busy&amp;quot; Unmount Error]].&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Conclusion Questions:&amp;#039;&amp;#039;&amp;#039; Answer the questions in section [[#Conclusion Questions]].&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8155</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8155"/>
		<updated>2025-10-17T13:16:18Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Lab Sessions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Lab Sessions ==&lt;br /&gt;
&lt;br /&gt;
* Lab 1 - [[OS Lab 1 - Installing Linux]]&lt;br /&gt;
* Lab 2 - [[OS Lab 2 - Linux Filesystems]]&lt;br /&gt;
* Lab 3 - [[OS Lab 3 - Processes and Jobs]]&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8154</id>
		<title>OS Lab 2 - Linux Filesystems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8154"/>
		<updated>2025-10-10T15:26:13Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Learning Objectives =&lt;br /&gt;
&lt;br /&gt;
By the end, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the Linux filesystem hierarchy and how paths work (absolute vs. relative).&lt;br /&gt;
* Distinguish common filesystem node types: regular files, directories, symbolic links, block/character devices, FIFOs (named pipes), and Unix domain sockets.&lt;br /&gt;
* Discover mounted filesystems and underlying block devices using &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;, and explain the relationship between them.&lt;br /&gt;
* Manipulate filesystem nodes using standard CLI tools (&amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Create a loopback block device backed by a file and format it with &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt;, then mount, verify, and clean it up safely.&lt;br /&gt;
&lt;br /&gt;
= Quick Refresher: Paths and Hierarchy =&lt;br /&gt;
&lt;br /&gt;
Linux organizes everything in a single tree with &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; as the root. Example key directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; (system config), &amp;lt;code&amp;gt;/bin&amp;lt;/code&amp;gt; (essential binaries), &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; (user-land programs), &amp;lt;code&amp;gt;/home&amp;lt;/code&amp;gt; (user homes), &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; (variable data), &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; (temporary mounts), &amp;lt;code&amp;gt;/proc&amp;lt;/code&amp;gt; (procfs), &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (device nodes).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Absolute path&amp;#039;&amp;#039;&amp;#039;: starts at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/home/student/lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Relative path&amp;#039;&amp;#039;&amp;#039;: from current directory (e.g., &amp;lt;code&amp;gt;../lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
Use &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, and tab-completion to navigate.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pwd&lt;br /&gt;
cd ~&lt;br /&gt;
ls -la &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Filesystem Node Types =&lt;br /&gt;
&lt;br /&gt;
Each directory entry points to an &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039; (metadata: type, permissions, owner, timestamps, block pointers). The file type is shown by the first character in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Char !! Type !! How to see/create/observe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; || Regular file || &amp;lt;code&amp;gt;touch file&amp;lt;/code&amp;gt;; write with editors or &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; || Directory || &amp;lt;code&amp;gt;mkdir dir&amp;lt;/code&amp;gt;; remove with &amp;lt;code&amp;gt;rmdir&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;rm -r&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; || Symbolic link || &amp;lt;code&amp;gt;ln -s target linkname&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; || Block device || Usually in &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; || Character device || &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/tty&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; || FIFO (named pipe) || &amp;lt;code&amp;gt;mkfifo pipe&amp;lt;/code&amp;gt;; read/write with &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; || Unix socket || Created by programs (e.g., services); list with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ss -x&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore inode numbers:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -li&lt;br /&gt;
stat file &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hard vs. symbolic links:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir ~/lab-nodes &amp;amp;&amp;amp; cd ~/lab-nodes&lt;br /&gt;
printf &amp;#039;hello\n&amp;#039; &amp;gt; a.txt&lt;br /&gt;
ln a.txt a.hard        # hard link (same inode)&lt;br /&gt;
ln -s a.txt a.sym      # symlink (different inode)&lt;br /&gt;
ls -li&lt;br /&gt;
rm a.txt &amp;amp;&amp;amp; cat a.hard # still works; inode persists via hard link&lt;br /&gt;
cat a.sym              # will fail: dangling symlink &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Discovering Filesystems and Devices =&lt;br /&gt;
&lt;br /&gt;
Two essential tools:&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; — usage of mounted filesystems ==&lt;br /&gt;
&lt;br /&gt;
* Shows how full mounted filesystems are and where they are mounted.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-h&amp;lt;/code&amp;gt; (human), &amp;lt;code&amp;gt;-T&amp;lt;/code&amp;gt; (type), target path to narrow output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT&lt;br /&gt;
# Focus on a path&lt;br /&gt;
sudo mkdir -p /mnt &amp;amp;&amp;amp; df -hT /mnt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; — block devices and partitions ==&lt;br /&gt;
&lt;br /&gt;
* Shows block devices (disks, partitions, loop devices) regardless of mount state.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt; (FSType/Label/UUID), &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt; to customize columns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
lsblk -f&lt;br /&gt;
lsblk -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relationship between &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; lists devices (e.g., &amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;) and where they’re &amp;#039;&amp;#039;&amp;#039;mounted&amp;#039;&amp;#039;&amp;#039; (MOUNTPOINTS column).&lt;br /&gt;
* &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; reports &amp;#039;&amp;#039;&amp;#039;space usage&amp;#039;&amp;#039;&amp;#039; per &amp;#039;&amp;#039;&amp;#039;mounted filesystem&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;Mapping tip:&amp;#039;&amp;#039;&amp;#039; Find the row in &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; whose &amp;lt;code&amp;gt;MOUNTPOINTS&amp;lt;/code&amp;gt; equals the &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; mountpoint; the corresponding &amp;lt;code&amp;gt;NAME&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sda2&amp;lt;/code&amp;gt;) is the device backing that filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise:&amp;#039;&amp;#039;&amp;#039; Identify the device backing your home directory and its filesystem type. &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT ~&lt;br /&gt;
lsblk -f | grep $(df --output=source ~ | tail -1) &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working with Nodes =&lt;br /&gt;
&lt;br /&gt;
Core commands (read &amp;lt;code&amp;gt;man&amp;lt;/code&amp;gt; pages for details):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;List and inspect&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;ls -la&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree&amp;lt;/code&amp;gt; (optional), &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Create&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;touch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln -s&amp;lt;/code&amp;gt;, (rarely) &amp;lt;code&amp;gt;mknod&amp;lt;/code&amp;gt; for manual device nodes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy/Move/Delete&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; for dirs), &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; recursive, &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt; interactive).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Search&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;View&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;less&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tail&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Space usage&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;du -sh DIR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Mini-lab:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-fs/dir1/dir2&lt;br /&gt;
cd ~/lab-fs&lt;br /&gt;
touch notes.txt&lt;br /&gt;
echo &amp;quot;sample&amp;quot; &amp;gt; dir1/data&lt;br /&gt;
mkfifo dir1/pipe&lt;br /&gt;
ln -s dir1/data link-to-data&lt;br /&gt;
ls -lR&lt;br /&gt;
file notes.txt dir1/pipe link-to-data&lt;br /&gt;
find . -type f -size +0&lt;br /&gt;
stat notes.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: Note: Sockets are typically created by running daemons. You can spot them with &amp;lt;code&amp;gt;find -type s&amp;lt;/code&amp;gt; (e.g., in &amp;lt;code&amp;gt;/run/&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/var/run/&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
= Creating and Using a Loopback Filesystem =&lt;br /&gt;
&lt;br /&gt;
Goal: Create a &amp;#039;&amp;#039;&amp;#039;file-backed block device&amp;#039;&amp;#039;&amp;#039; with &amp;lt;code&amp;gt;losetup&amp;lt;/code&amp;gt;, format it as &amp;#039;&amp;#039;&amp;#039;ext4&amp;#039;&amp;#039;&amp;#039;, mount it, and verify via &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Create a sparse backing file ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-loop &amp;amp;&amp;amp; cd ~/lab-loop&lt;br /&gt;
truncate -s 100M loop.img   # fast; or: dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
ls -lh loop.img &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attach the file to a free loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo losetup -fP loop.img               # chooses next free /dev/loopX&lt;br /&gt;
losetup -a                               # show all loop mappings&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify with &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk -f | grep loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Make an ext4 filesystem on the loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039; ext4 creates a superblock and inode tables, then initializes data structures. &lt;br /&gt;
&lt;br /&gt;
== Mount and validate ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
df -hT /mnt/labfs&lt;br /&gt;
lsblk -f loopX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create some content and examine space usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo sh -c &amp;#039;echo &amp;quot;hello ext4&amp;quot; &amp;gt; /mnt/labfs/hello.txt&amp;#039;&lt;br /&gt;
sudo ls -la /mnt/labfs&lt;br /&gt;
sudo du -sh /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unmount and detach (clean up) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
sudo losetup -d /dev/loopX&lt;br /&gt;
rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;#039;&amp;#039;&amp;#039;Safety tip:&amp;#039;&amp;#039;&amp;#039; Always unmount before detaching the loop device. Use &amp;lt;code&amp;gt;lsof | grep /mnt/labfs&amp;lt;/code&amp;gt; if the mount is busy.&lt;br /&gt;
&lt;br /&gt;
= Mount Tables and Where Linux Stores Them =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; without args lists mounts.&lt;br /&gt;
* Modern systems reflect mounts in &amp;lt;code&amp;gt;/proc/self/mounts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/proc/mounts&amp;lt;/code&amp;gt;. Many distros keep &amp;lt;code&amp;gt;/etc/mtab&amp;lt;/code&amp;gt; as a compatibility symlink.&lt;br /&gt;
* Persistent mounts are configured in &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; (be careful!).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat /proc/mounts | head&lt;br /&gt;
mount | head&lt;br /&gt;
cat /etc/fstab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Assessment: Lab Submission Tasks =&lt;br /&gt;
&lt;br /&gt;
Complete the following tasks and submit a report with your commands and output.&lt;br /&gt;
&lt;br /&gt;
=== Device to Filesystem Mapping ===&lt;br /&gt;
* Run &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Identify the line in each result that corresponds to your &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (root) filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Links and Inodes ===&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;original.txt&amp;lt;/code&amp;gt;, a hard link &amp;lt;code&amp;gt;hard.link&amp;lt;/code&amp;gt;, and a symbolic link &amp;lt;code&amp;gt;symbolic.link&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
* Provide the output of &amp;lt;code&amp;gt;ls -li&amp;lt;/code&amp;gt; and explain why the inode numbers are the same or different.&lt;br /&gt;
&lt;br /&gt;
=== Loopback Filesystem Creation ===&lt;br /&gt;
* Create a 150&amp;amp;nbsp;MB &amp;lt;code&amp;gt;loop.img&amp;lt;/code&amp;gt;, format it with ext4 (label &amp;lt;code&amp;gt;MY_LOOP&amp;lt;/code&amp;gt;), and mount it on &amp;lt;code&amp;gt;/mnt/mydata&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;proof.txt&amp;lt;/code&amp;gt; inside it containing your name.&lt;br /&gt;
* Provide the final output of &amp;lt;code&amp;gt;df -hT /mnt/mydata&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; showing your mounted device.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; Command Practice ===&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command that lists all files in your home directory (&amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt;) larger than 1&amp;amp;nbsp;MB and modified in the last 7 days.&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to list all Unix domain sockets under &amp;lt;code&amp;gt;/run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Real-World Scenario: The Automated Organizer ===&lt;br /&gt;
# &amp;#039;&amp;#039;Imagine your &amp;lt;code&amp;gt;Downloads&amp;lt;/code&amp;gt; directory is cluttered. Your task is to write a sequence of commands to automatically clean it up.&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Setup:&amp;#039;&amp;#039;&amp;#039; Create a &amp;lt;code&amp;gt;~/cleanup_target&amp;lt;/code&amp;gt; directory with at least 10 files, including:&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;program1.log&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;data.tmp&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* One file larger than 10&amp;amp;nbsp;MB (e.g., &amp;lt;code&amp;gt;truncate -s 15M large_dataset.dat&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Some other miscellaneous files.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Automation Task:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Create subdirectories: &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all files larger than 10&amp;amp;nbsp;MB into &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Create a &amp;lt;code&amp;gt;cleanup_report.txt&amp;lt;/code&amp;gt; listing the contents of the new subdirectories.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Submission:&amp;#039;&amp;#039;&amp;#039; Provide the exact sequence of commands you used.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Provide a solution that does &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; use shell expansion (like &amp;lt;code&amp;gt;*.log&amp;lt;/code&amp;gt;). Instead, use a more robust tool like &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;-exec&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;xargs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Submission ===&lt;br /&gt;
Submit a short report with command history snippets and brief explanations (≈1–2 pages).&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mkfs.ext4: device is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Ensure it’s &amp;#039;&amp;#039;&amp;#039;not mounted&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;mount | grep loop&amp;lt;/code&amp;gt;; unmount, then retry.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;umount: target is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Find open files: &amp;lt;code&amp;gt;sudo lsof +f -- /mnt/labfs&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;fuser -vm /mnt/labfs&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No free loop device&amp;#039;&amp;#039;&amp;#039; — Create one: &amp;lt;code&amp;gt;sudo modprobe loop&amp;lt;/code&amp;gt;; or manually: &amp;lt;code&amp;gt;sudo losetup /dev/loop10 loop.img&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wrong device formatted&amp;#039;&amp;#039;&amp;#039; — Always confirm with &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; before &amp;lt;code&amp;gt;mkfs.*&amp;lt;/code&amp;gt;. In a VM, take a snapshot first.&lt;br /&gt;
&lt;br /&gt;
= Optional Extensions =&lt;br /&gt;
&lt;br /&gt;
* Create partitions &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the loop device (&amp;lt;code&amp;gt;sudo sfdisk /dev/loopX&amp;lt;/code&amp;gt;), then &amp;lt;code&amp;gt;partprobe&amp;lt;/code&amp;gt; and format &amp;lt;code&amp;gt;/dev/loopXp1&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Compare &amp;lt;code&amp;gt;mkfs.ext2&amp;lt;/code&amp;gt; vs. &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt; space usage and features (journaling, extent maps).&lt;br /&gt;
* Mount with options: &amp;lt;code&amp;gt;sudo mount -o noatime,nodiratime /dev/loopX /mnt/labfs&amp;lt;/code&amp;gt; and measure &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt; times.&lt;br /&gt;
&lt;br /&gt;
== Quick Command Cheat Sheet ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Discover&lt;br /&gt;
&lt;br /&gt;
pwd; ls -la; stat FILE; file FILE; du -sh DIR; df -hT; lsblk -f&lt;br /&gt;
&lt;br /&gt;
# Make nodes&lt;br /&gt;
&lt;br /&gt;
mkdir DIR; touch FILE; ln -s TARGET LINK; mkfifo PIPE&lt;br /&gt;
&lt;br /&gt;
# Links vs inodes&lt;br /&gt;
&lt;br /&gt;
ln SRC DEST   # hard link (same inode)&lt;br /&gt;
ln -s SRC DEST # symlink (diff inode)&lt;br /&gt;
&lt;br /&gt;
# Loopback + ext4&lt;br /&gt;
&lt;br /&gt;
truncate -s 100M loop.img&lt;br /&gt;
sudo losetup -fP loop.img; losetup -a&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX&lt;br /&gt;
sudo mkdir -p /mnt/labfs &amp;amp;&amp;amp; sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
df -hT /mnt/labfs; lsblk -f | grep loopX&lt;br /&gt;
sudo umount /mnt/labfs; sudo losetup -d /dev/loopX; rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8153</id>
		<title>OS Lab 2 - Linux Filesystems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8153"/>
		<updated>2025-10-10T13:50:42Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Mount and validate */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Learning Objectives =&lt;br /&gt;
&lt;br /&gt;
By the end, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the Linux filesystem hierarchy and how paths work (absolute vs. relative).&lt;br /&gt;
* Distinguish common filesystem node types: regular files, directories, symbolic links, block/character devices, FIFOs (named pipes), and Unix domain sockets.&lt;br /&gt;
* Discover mounted filesystems and underlying block devices using &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;, and explain the relationship between them.&lt;br /&gt;
* Manipulate filesystem nodes using standard CLI tools (&amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Create a loopback block device backed by a file and format it with &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt;, then mount, verify, and clean it up safely.&lt;br /&gt;
&lt;br /&gt;
= Quick Refresher: Paths and Hierarchy =&lt;br /&gt;
&lt;br /&gt;
Linux organizes everything in a single tree with &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; as the root. Example key directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; (system config), &amp;lt;code&amp;gt;/bin&amp;lt;/code&amp;gt; (essential binaries), &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; (user-land programs), &amp;lt;code&amp;gt;/home&amp;lt;/code&amp;gt; (user homes), &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; (variable data), &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; (temporary mounts), &amp;lt;code&amp;gt;/proc&amp;lt;/code&amp;gt; (procfs), &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (device nodes).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Absolute path&amp;#039;&amp;#039;&amp;#039;: starts at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/home/student/lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Relative path&amp;#039;&amp;#039;&amp;#039;: from current directory (e.g., &amp;lt;code&amp;gt;../lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
Use &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, and tab-completion to navigate.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pwd&lt;br /&gt;
cd ~&lt;br /&gt;
ls -la &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Filesystem Node Types =&lt;br /&gt;
&lt;br /&gt;
Each directory entry points to an &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039; (metadata: type, permissions, owner, timestamps, block pointers). The file type is shown by the first character in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Char !! Type !! How to see/create/observe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; || Regular file || &amp;lt;code&amp;gt;touch file&amp;lt;/code&amp;gt;; write with editors or &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; || Directory || &amp;lt;code&amp;gt;mkdir dir&amp;lt;/code&amp;gt;; remove with &amp;lt;code&amp;gt;rmdir&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;rm -r&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; || Symbolic link || &amp;lt;code&amp;gt;ln -s target linkname&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; || Block device || Usually in &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; || Character device || &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/tty&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; || FIFO (named pipe) || &amp;lt;code&amp;gt;mkfifo pipe&amp;lt;/code&amp;gt;; read/write with &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; || Unix socket || Created by programs (e.g., services); list with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ss -x&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore inode numbers:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -li&lt;br /&gt;
stat file &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hard vs. symbolic links:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir ~/lab-nodes &amp;amp;&amp;amp; cd ~/lab-nodes&lt;br /&gt;
printf &amp;#039;hello\n&amp;#039; &amp;gt; a.txt&lt;br /&gt;
ln a.txt a.hard        # hard link (same inode)&lt;br /&gt;
ln -s a.txt a.sym      # symlink (different inode)&lt;br /&gt;
ls -li&lt;br /&gt;
rm a.txt &amp;amp;&amp;amp; cat a.hard # still works; inode persists via hard link&lt;br /&gt;
cat a.sym              # will fail: dangling symlink &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Discovering Filesystems and Devices =&lt;br /&gt;
&lt;br /&gt;
Two essential tools:&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; — usage of mounted filesystems ==&lt;br /&gt;
&lt;br /&gt;
* Shows how full mounted filesystems are and where they are mounted.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-h&amp;lt;/code&amp;gt; (human), &amp;lt;code&amp;gt;-T&amp;lt;/code&amp;gt; (type), target path to narrow output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT&lt;br /&gt;
# Focus on a path&lt;br /&gt;
sudo mkdir -p /mnt &amp;amp;&amp;amp; df -hT /mnt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; — block devices and partitions ==&lt;br /&gt;
&lt;br /&gt;
* Shows block devices (disks, partitions, loop devices) regardless of mount state.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt; (FSType/Label/UUID), &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt; to customize columns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
lsblk -f&lt;br /&gt;
lsblk -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relationship between &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; lists devices (e.g., &amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;) and where they’re &amp;#039;&amp;#039;&amp;#039;mounted&amp;#039;&amp;#039;&amp;#039; (MOUNTPOINTS column).&lt;br /&gt;
* &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; reports &amp;#039;&amp;#039;&amp;#039;space usage&amp;#039;&amp;#039;&amp;#039; per &amp;#039;&amp;#039;&amp;#039;mounted filesystem&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;Mapping tip:&amp;#039;&amp;#039;&amp;#039; Find the row in &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; whose &amp;lt;code&amp;gt;MOUNTPOINTS&amp;lt;/code&amp;gt; equals the &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; mountpoint; the corresponding &amp;lt;code&amp;gt;NAME&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sda2&amp;lt;/code&amp;gt;) is the device backing that filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise:&amp;#039;&amp;#039;&amp;#039; Identify the device backing your home directory and its filesystem type. &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT ~&lt;br /&gt;
lsblk -f | grep $(df --output=source ~ | tail -1) &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working with Nodes =&lt;br /&gt;
&lt;br /&gt;
Core commands (read &amp;lt;code&amp;gt;man&amp;lt;/code&amp;gt; pages for details):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;List and inspect&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;ls -la&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree&amp;lt;/code&amp;gt; (optional), &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Create&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;touch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln -s&amp;lt;/code&amp;gt;, (rarely) &amp;lt;code&amp;gt;mknod&amp;lt;/code&amp;gt; for manual device nodes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy/Move/Delete&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; for dirs), &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; recursive, &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt; interactive).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Search&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;View&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;less&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tail&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Space usage&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;du -sh DIR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Mini-lab:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-fs/dir1/dir2&lt;br /&gt;
cd ~/lab-fs&lt;br /&gt;
touch notes.txt&lt;br /&gt;
echo &amp;quot;sample&amp;quot; &amp;gt; dir1/data&lt;br /&gt;
mkfifo dir1/pipe&lt;br /&gt;
ln -s dir1/data link-to-data&lt;br /&gt;
ls -lR&lt;br /&gt;
file notes.txt dir1/pipe link-to-data&lt;br /&gt;
find . -type f -size +0&lt;br /&gt;
chmod 640 notes.txt &amp;amp;&amp;amp; stat notes.txt &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: Note: Sockets are typically created by running daemons. You can spot them with &amp;lt;code&amp;gt;find -type s&amp;lt;/code&amp;gt; (e.g., in &amp;lt;code&amp;gt;/run/&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/var/run/&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
= Creating and Using a Loopback Filesystem =&lt;br /&gt;
&lt;br /&gt;
Goal: Create a &amp;#039;&amp;#039;&amp;#039;file-backed block device&amp;#039;&amp;#039;&amp;#039; with &amp;lt;code&amp;gt;losetup&amp;lt;/code&amp;gt;, format it as &amp;#039;&amp;#039;&amp;#039;ext4&amp;#039;&amp;#039;&amp;#039;, mount it, and verify via &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Create a sparse backing file ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-loop &amp;amp;&amp;amp; cd ~/lab-loop&lt;br /&gt;
truncate -s 100M loop.img   # fast; or: dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
ls -lh loop.img &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attach the file to a free loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo losetup -fP loop.img               # chooses next free /dev/loopX&lt;br /&gt;
losetup -a                               # show all loop mappings&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify with &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk -f | grep loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Make an ext4 filesystem on the loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039; ext4 creates a superblock and inode tables, then initializes data structures. &lt;br /&gt;
&lt;br /&gt;
== Mount and validate ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
df -hT /mnt/labfs&lt;br /&gt;
lsblk -f loopX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create some content and examine space usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo sh -c &amp;#039;echo &amp;quot;hello ext4&amp;quot; &amp;gt; /mnt/labfs/hello.txt&amp;#039;&lt;br /&gt;
sudo ls -la /mnt/labfs&lt;br /&gt;
sudo du -sh /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unmount and detach (clean up) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
sudo losetup -d /dev/loopX&lt;br /&gt;
rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;#039;&amp;#039;&amp;#039;Safety tip:&amp;#039;&amp;#039;&amp;#039; Always unmount before detaching the loop device. Use &amp;lt;code&amp;gt;lsof | grep /mnt/labfs&amp;lt;/code&amp;gt; if the mount is busy.&lt;br /&gt;
&lt;br /&gt;
= Mount Tables and Where Linux Stores Them =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; without args lists mounts.&lt;br /&gt;
* Modern systems reflect mounts in &amp;lt;code&amp;gt;/proc/self/mounts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/proc/mounts&amp;lt;/code&amp;gt;. Many distros keep &amp;lt;code&amp;gt;/etc/mtab&amp;lt;/code&amp;gt; as a compatibility symlink.&lt;br /&gt;
* Persistent mounts are configured in &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; (be careful!).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat /proc/mounts | head&lt;br /&gt;
mount | head&lt;br /&gt;
cat /etc/fstab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Assessment: Lab Submission Tasks =&lt;br /&gt;
&lt;br /&gt;
Complete the following tasks and submit a report with your commands and output.&lt;br /&gt;
&lt;br /&gt;
=== Device to Filesystem Mapping ===&lt;br /&gt;
* Run &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Identify the line in each result that corresponds to your &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (root) filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Links and Inodes ===&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;original.txt&amp;lt;/code&amp;gt;, a hard link &amp;lt;code&amp;gt;hard.link&amp;lt;/code&amp;gt;, and a symbolic link &amp;lt;code&amp;gt;symbolic.link&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
* Provide the output of &amp;lt;code&amp;gt;ls -li&amp;lt;/code&amp;gt; and explain why the inode numbers are the same or different.&lt;br /&gt;
&lt;br /&gt;
=== Loopback Filesystem Creation ===&lt;br /&gt;
* Create a 150&amp;amp;nbsp;MB &amp;lt;code&amp;gt;loop.img&amp;lt;/code&amp;gt;, format it with ext4 (label &amp;lt;code&amp;gt;MY_LOOP&amp;lt;/code&amp;gt;), and mount it on &amp;lt;code&amp;gt;/mnt/mydata&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;proof.txt&amp;lt;/code&amp;gt; inside it containing your name.&lt;br /&gt;
* Provide the final output of &amp;lt;code&amp;gt;df -hT /mnt/mydata&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; showing your mounted device.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; Command Practice ===&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command that lists all files in your home directory (&amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt;) larger than 1&amp;amp;nbsp;MB and modified in the last 7 days.&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to list all Unix domain sockets under &amp;lt;code&amp;gt;/run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Real-World Scenario: The Automated Organizer ===&lt;br /&gt;
# &amp;#039;&amp;#039;Imagine your &amp;lt;code&amp;gt;Downloads&amp;lt;/code&amp;gt; directory is cluttered. Your task is to write a sequence of commands to automatically clean it up.&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Setup:&amp;#039;&amp;#039;&amp;#039; Create a &amp;lt;code&amp;gt;~/cleanup_target&amp;lt;/code&amp;gt; directory with at least 10 files, including:&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;program1.log&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;data.tmp&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* One file larger than 10&amp;amp;nbsp;MB (e.g., &amp;lt;code&amp;gt;truncate -s 15M large_dataset.dat&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Some other miscellaneous files.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Automation Task:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Create subdirectories: &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all files larger than 10&amp;amp;nbsp;MB into &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Create a &amp;lt;code&amp;gt;cleanup_report.txt&amp;lt;/code&amp;gt; listing the contents of the new subdirectories.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Submission:&amp;#039;&amp;#039;&amp;#039; Provide the exact sequence of commands you used.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Provide a solution that does &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; use shell expansion (like &amp;lt;code&amp;gt;*.log&amp;lt;/code&amp;gt;). Instead, use a more robust tool like &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;-exec&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;xargs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Submission ===&lt;br /&gt;
Submit a short report with command history snippets and brief explanations (≈1–2 pages).&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mkfs.ext4: device is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Ensure it’s &amp;#039;&amp;#039;&amp;#039;not mounted&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;mount | grep loop&amp;lt;/code&amp;gt;; unmount, then retry.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;umount: target is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Find open files: &amp;lt;code&amp;gt;sudo lsof +f -- /mnt/labfs&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;fuser -vm /mnt/labfs&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No free loop device&amp;#039;&amp;#039;&amp;#039; — Create one: &amp;lt;code&amp;gt;sudo modprobe loop&amp;lt;/code&amp;gt;; or manually: &amp;lt;code&amp;gt;sudo losetup /dev/loop10 loop.img&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wrong device formatted&amp;#039;&amp;#039;&amp;#039; — Always confirm with &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; before &amp;lt;code&amp;gt;mkfs.*&amp;lt;/code&amp;gt;. In a VM, take a snapshot first.&lt;br /&gt;
&lt;br /&gt;
= Optional Extensions =&lt;br /&gt;
&lt;br /&gt;
* Create partitions &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the loop device (&amp;lt;code&amp;gt;sudo sfdisk /dev/loopX&amp;lt;/code&amp;gt;), then &amp;lt;code&amp;gt;partprobe&amp;lt;/code&amp;gt; and format &amp;lt;code&amp;gt;/dev/loopXp1&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Compare &amp;lt;code&amp;gt;mkfs.ext2&amp;lt;/code&amp;gt; vs. &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt; space usage and features (journaling, extent maps).&lt;br /&gt;
* Mount with options: &amp;lt;code&amp;gt;sudo mount -o noatime,nodiratime /dev/loopX /mnt/labfs&amp;lt;/code&amp;gt; and measure &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt; times.&lt;br /&gt;
&lt;br /&gt;
== Quick Command Cheat Sheet ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Discover&lt;br /&gt;
&lt;br /&gt;
pwd; ls -la; stat FILE; file FILE; du -sh DIR; df -hT; lsblk -f&lt;br /&gt;
&lt;br /&gt;
# Make nodes&lt;br /&gt;
&lt;br /&gt;
mkdir DIR; touch FILE; ln -s TARGET LINK; mkfifo PIPE&lt;br /&gt;
&lt;br /&gt;
# Links vs inodes&lt;br /&gt;
&lt;br /&gt;
ln SRC DEST   # hard link (same inode)&lt;br /&gt;
ln -s SRC DEST # symlink (diff inode)&lt;br /&gt;
&lt;br /&gt;
# Loopback + ext4&lt;br /&gt;
&lt;br /&gt;
truncate -s 100M loop.img&lt;br /&gt;
sudo losetup -fP loop.img; losetup -a&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX&lt;br /&gt;
sudo mkdir -p /mnt/labfs &amp;amp;&amp;amp; sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
df -hT /mnt/labfs; lsblk -f | grep loopX&lt;br /&gt;
sudo umount /mnt/labfs; sudo losetup -d /dev/loopX; rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8152</id>
		<title>OS Lab 2 - Linux Filesystems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8152"/>
		<updated>2025-10-10T13:50:16Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: /* Optional Extensions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Learning Objectives =&lt;br /&gt;
&lt;br /&gt;
By the end, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the Linux filesystem hierarchy and how paths work (absolute vs. relative).&lt;br /&gt;
* Distinguish common filesystem node types: regular files, directories, symbolic links, block/character devices, FIFOs (named pipes), and Unix domain sockets.&lt;br /&gt;
* Discover mounted filesystems and underlying block devices using &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;, and explain the relationship between them.&lt;br /&gt;
* Manipulate filesystem nodes using standard CLI tools (&amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Create a loopback block device backed by a file and format it with &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt;, then mount, verify, and clean it up safely.&lt;br /&gt;
&lt;br /&gt;
= Quick Refresher: Paths and Hierarchy =&lt;br /&gt;
&lt;br /&gt;
Linux organizes everything in a single tree with &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; as the root. Example key directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; (system config), &amp;lt;code&amp;gt;/bin&amp;lt;/code&amp;gt; (essential binaries), &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; (user-land programs), &amp;lt;code&amp;gt;/home&amp;lt;/code&amp;gt; (user homes), &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; (variable data), &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; (temporary mounts), &amp;lt;code&amp;gt;/proc&amp;lt;/code&amp;gt; (procfs), &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (device nodes).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Absolute path&amp;#039;&amp;#039;&amp;#039;: starts at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/home/student/lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Relative path&amp;#039;&amp;#039;&amp;#039;: from current directory (e.g., &amp;lt;code&amp;gt;../lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
Use &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, and tab-completion to navigate.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pwd&lt;br /&gt;
cd ~&lt;br /&gt;
ls -la &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Filesystem Node Types =&lt;br /&gt;
&lt;br /&gt;
Each directory entry points to an &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039; (metadata: type, permissions, owner, timestamps, block pointers). The file type is shown by the first character in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Char !! Type !! How to see/create/observe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; || Regular file || &amp;lt;code&amp;gt;touch file&amp;lt;/code&amp;gt;; write with editors or &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; || Directory || &amp;lt;code&amp;gt;mkdir dir&amp;lt;/code&amp;gt;; remove with &amp;lt;code&amp;gt;rmdir&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;rm -r&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; || Symbolic link || &amp;lt;code&amp;gt;ln -s target linkname&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; || Block device || Usually in &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; || Character device || &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/tty&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; || FIFO (named pipe) || &amp;lt;code&amp;gt;mkfifo pipe&amp;lt;/code&amp;gt;; read/write with &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; || Unix socket || Created by programs (e.g., services); list with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ss -x&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore inode numbers:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -li&lt;br /&gt;
stat file &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hard vs. symbolic links:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir ~/lab-nodes &amp;amp;&amp;amp; cd ~/lab-nodes&lt;br /&gt;
printf &amp;#039;hello\n&amp;#039; &amp;gt; a.txt&lt;br /&gt;
ln a.txt a.hard        # hard link (same inode)&lt;br /&gt;
ln -s a.txt a.sym      # symlink (different inode)&lt;br /&gt;
ls -li&lt;br /&gt;
rm a.txt &amp;amp;&amp;amp; cat a.hard # still works; inode persists via hard link&lt;br /&gt;
cat a.sym              # will fail: dangling symlink &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Discovering Filesystems and Devices =&lt;br /&gt;
&lt;br /&gt;
Two essential tools:&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; — usage of mounted filesystems ==&lt;br /&gt;
&lt;br /&gt;
* Shows how full mounted filesystems are and where they are mounted.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-h&amp;lt;/code&amp;gt; (human), &amp;lt;code&amp;gt;-T&amp;lt;/code&amp;gt; (type), target path to narrow output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT&lt;br /&gt;
# Focus on a path&lt;br /&gt;
sudo mkdir -p /mnt &amp;amp;&amp;amp; df -hT /mnt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; — block devices and partitions ==&lt;br /&gt;
&lt;br /&gt;
* Shows block devices (disks, partitions, loop devices) regardless of mount state.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt; (FSType/Label/UUID), &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt; to customize columns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
lsblk -f&lt;br /&gt;
lsblk -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relationship between &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; lists devices (e.g., &amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;) and where they’re &amp;#039;&amp;#039;&amp;#039;mounted&amp;#039;&amp;#039;&amp;#039; (MOUNTPOINTS column).&lt;br /&gt;
* &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; reports &amp;#039;&amp;#039;&amp;#039;space usage&amp;#039;&amp;#039;&amp;#039; per &amp;#039;&amp;#039;&amp;#039;mounted filesystem&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;Mapping tip:&amp;#039;&amp;#039;&amp;#039; Find the row in &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; whose &amp;lt;code&amp;gt;MOUNTPOINTS&amp;lt;/code&amp;gt; equals the &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; mountpoint; the corresponding &amp;lt;code&amp;gt;NAME&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sda2&amp;lt;/code&amp;gt;) is the device backing that filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise:&amp;#039;&amp;#039;&amp;#039; Identify the device backing your home directory and its filesystem type. &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT ~&lt;br /&gt;
lsblk -f | grep $(df --output=source ~ | tail -1) &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working with Nodes =&lt;br /&gt;
&lt;br /&gt;
Core commands (read &amp;lt;code&amp;gt;man&amp;lt;/code&amp;gt; pages for details):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;List and inspect&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;ls -la&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree&amp;lt;/code&amp;gt; (optional), &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Create&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;touch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln -s&amp;lt;/code&amp;gt;, (rarely) &amp;lt;code&amp;gt;mknod&amp;lt;/code&amp;gt; for manual device nodes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy/Move/Delete&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; for dirs), &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; recursive, &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt; interactive).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Search&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;View&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;less&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tail&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Space usage&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;du -sh DIR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Mini-lab:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-fs/dir1/dir2&lt;br /&gt;
cd ~/lab-fs&lt;br /&gt;
touch notes.txt&lt;br /&gt;
echo &amp;quot;sample&amp;quot; &amp;gt; dir1/data&lt;br /&gt;
mkfifo dir1/pipe&lt;br /&gt;
ln -s dir1/data link-to-data&lt;br /&gt;
ls -lR&lt;br /&gt;
file notes.txt dir1/pipe link-to-data&lt;br /&gt;
find . -type f -size +0&lt;br /&gt;
chmod 640 notes.txt &amp;amp;&amp;amp; stat notes.txt &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: Note: Sockets are typically created by running daemons. You can spot them with &amp;lt;code&amp;gt;find -type s&amp;lt;/code&amp;gt; (e.g., in &amp;lt;code&amp;gt;/run/&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/var/run/&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
= Creating and Using a Loopback Filesystem =&lt;br /&gt;
&lt;br /&gt;
Goal: Create a &amp;#039;&amp;#039;&amp;#039;file-backed block device&amp;#039;&amp;#039;&amp;#039; with &amp;lt;code&amp;gt;losetup&amp;lt;/code&amp;gt;, format it as &amp;#039;&amp;#039;&amp;#039;ext4&amp;#039;&amp;#039;&amp;#039;, mount it, and verify via &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Create a sparse backing file ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-loop &amp;amp;&amp;amp; cd ~/lab-loop&lt;br /&gt;
truncate -s 100M loop.img   # fast; or: dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
ls -lh loop.img &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attach the file to a free loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo losetup -fP loop.img               # chooses next free /dev/loopX&lt;br /&gt;
losetup -a                               # show all loop mappings&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify with &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk -f | grep loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Make an ext4 filesystem on the loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039; ext4 creates a superblock and inode tables, then initializes data structures. &lt;br /&gt;
&lt;br /&gt;
== Mount and validate ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
sudo mount &amp;quot;$LOOPDEV&amp;quot; /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
df -hT /mnt/labfs&lt;br /&gt;
lsblk -f | grep $(basename &amp;quot;$LOOPDEV&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create some content and examine space usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo sh -c &amp;#039;echo &amp;quot;hello ext4&amp;quot; &amp;gt; /mnt/labfs/hello.txt&amp;#039;&lt;br /&gt;
sudo ls -la /mnt/labfs&lt;br /&gt;
sudo du -sh /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unmount and detach (clean up) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
sudo losetup -d /dev/loopX&lt;br /&gt;
rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;#039;&amp;#039;&amp;#039;Safety tip:&amp;#039;&amp;#039;&amp;#039; Always unmount before detaching the loop device. Use &amp;lt;code&amp;gt;lsof | grep /mnt/labfs&amp;lt;/code&amp;gt; if the mount is busy.&lt;br /&gt;
&lt;br /&gt;
= Mount Tables and Where Linux Stores Them =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; without args lists mounts.&lt;br /&gt;
* Modern systems reflect mounts in &amp;lt;code&amp;gt;/proc/self/mounts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/proc/mounts&amp;lt;/code&amp;gt;. Many distros keep &amp;lt;code&amp;gt;/etc/mtab&amp;lt;/code&amp;gt; as a compatibility symlink.&lt;br /&gt;
* Persistent mounts are configured in &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; (be careful!).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat /proc/mounts | head&lt;br /&gt;
mount | head&lt;br /&gt;
cat /etc/fstab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Assessment: Lab Submission Tasks =&lt;br /&gt;
&lt;br /&gt;
Complete the following tasks and submit a report with your commands and output.&lt;br /&gt;
&lt;br /&gt;
=== Device to Filesystem Mapping ===&lt;br /&gt;
* Run &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Identify the line in each result that corresponds to your &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (root) filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Links and Inodes ===&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;original.txt&amp;lt;/code&amp;gt;, a hard link &amp;lt;code&amp;gt;hard.link&amp;lt;/code&amp;gt;, and a symbolic link &amp;lt;code&amp;gt;symbolic.link&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
* Provide the output of &amp;lt;code&amp;gt;ls -li&amp;lt;/code&amp;gt; and explain why the inode numbers are the same or different.&lt;br /&gt;
&lt;br /&gt;
=== Loopback Filesystem Creation ===&lt;br /&gt;
* Create a 150&amp;amp;nbsp;MB &amp;lt;code&amp;gt;loop.img&amp;lt;/code&amp;gt;, format it with ext4 (label &amp;lt;code&amp;gt;MY_LOOP&amp;lt;/code&amp;gt;), and mount it on &amp;lt;code&amp;gt;/mnt/mydata&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;proof.txt&amp;lt;/code&amp;gt; inside it containing your name.&lt;br /&gt;
* Provide the final output of &amp;lt;code&amp;gt;df -hT /mnt/mydata&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; showing your mounted device.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; Command Practice ===&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command that lists all files in your home directory (&amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt;) larger than 1&amp;amp;nbsp;MB and modified in the last 7 days.&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to list all Unix domain sockets under &amp;lt;code&amp;gt;/run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Real-World Scenario: The Automated Organizer ===&lt;br /&gt;
# &amp;#039;&amp;#039;Imagine your &amp;lt;code&amp;gt;Downloads&amp;lt;/code&amp;gt; directory is cluttered. Your task is to write a sequence of commands to automatically clean it up.&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Setup:&amp;#039;&amp;#039;&amp;#039; Create a &amp;lt;code&amp;gt;~/cleanup_target&amp;lt;/code&amp;gt; directory with at least 10 files, including:&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;program1.log&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;data.tmp&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* One file larger than 10&amp;amp;nbsp;MB (e.g., &amp;lt;code&amp;gt;truncate -s 15M large_dataset.dat&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Some other miscellaneous files.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Automation Task:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Create subdirectories: &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all files larger than 10&amp;amp;nbsp;MB into &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Create a &amp;lt;code&amp;gt;cleanup_report.txt&amp;lt;/code&amp;gt; listing the contents of the new subdirectories.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Submission:&amp;#039;&amp;#039;&amp;#039; Provide the exact sequence of commands you used.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Provide a solution that does &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; use shell expansion (like &amp;lt;code&amp;gt;*.log&amp;lt;/code&amp;gt;). Instead, use a more robust tool like &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;-exec&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;xargs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Submission ===&lt;br /&gt;
Submit a short report with command history snippets and brief explanations (≈1–2 pages).&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mkfs.ext4: device is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Ensure it’s &amp;#039;&amp;#039;&amp;#039;not mounted&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;mount | grep loop&amp;lt;/code&amp;gt;; unmount, then retry.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;umount: target is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Find open files: &amp;lt;code&amp;gt;sudo lsof +f -- /mnt/labfs&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;fuser -vm /mnt/labfs&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No free loop device&amp;#039;&amp;#039;&amp;#039; — Create one: &amp;lt;code&amp;gt;sudo modprobe loop&amp;lt;/code&amp;gt;; or manually: &amp;lt;code&amp;gt;sudo losetup /dev/loop10 loop.img&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wrong device formatted&amp;#039;&amp;#039;&amp;#039; — Always confirm with &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; before &amp;lt;code&amp;gt;mkfs.*&amp;lt;/code&amp;gt;. In a VM, take a snapshot first.&lt;br /&gt;
&lt;br /&gt;
= Optional Extensions =&lt;br /&gt;
&lt;br /&gt;
* Create partitions &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the loop device (&amp;lt;code&amp;gt;sudo sfdisk /dev/loopX&amp;lt;/code&amp;gt;), then &amp;lt;code&amp;gt;partprobe&amp;lt;/code&amp;gt; and format &amp;lt;code&amp;gt;/dev/loopXp1&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Compare &amp;lt;code&amp;gt;mkfs.ext2&amp;lt;/code&amp;gt; vs. &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt; space usage and features (journaling, extent maps).&lt;br /&gt;
* Mount with options: &amp;lt;code&amp;gt;sudo mount -o noatime,nodiratime /dev/loopX /mnt/labfs&amp;lt;/code&amp;gt; and measure &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt; times.&lt;br /&gt;
&lt;br /&gt;
== Quick Command Cheat Sheet ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Discover&lt;br /&gt;
&lt;br /&gt;
pwd; ls -la; stat FILE; file FILE; du -sh DIR; df -hT; lsblk -f&lt;br /&gt;
&lt;br /&gt;
# Make nodes&lt;br /&gt;
&lt;br /&gt;
mkdir DIR; touch FILE; ln -s TARGET LINK; mkfifo PIPE&lt;br /&gt;
&lt;br /&gt;
# Links vs inodes&lt;br /&gt;
&lt;br /&gt;
ln SRC DEST   # hard link (same inode)&lt;br /&gt;
ln -s SRC DEST # symlink (diff inode)&lt;br /&gt;
&lt;br /&gt;
# Loopback + ext4&lt;br /&gt;
&lt;br /&gt;
truncate -s 100M loop.img&lt;br /&gt;
sudo losetup -fP loop.img; losetup -a&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX&lt;br /&gt;
sudo mkdir -p /mnt/labfs &amp;amp;&amp;amp; sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
df -hT /mnt/labfs; lsblk -f | grep loopX&lt;br /&gt;
sudo umount /mnt/labfs; sudo losetup -d /dev/loopX; rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8151</id>
		<title>OS Lab 2 - Linux Filesystems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_2_-_Linux_Filesystems&amp;diff=8151"/>
		<updated>2025-10-10T13:44:42Z</updated>

		<summary type="html">&lt;p&gt;Vserbu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Learning Objectives =&lt;br /&gt;
&lt;br /&gt;
By the end, you will be able to:&lt;br /&gt;
&lt;br /&gt;
* Explain the Linux filesystem hierarchy and how paths work (absolute vs. relative).&lt;br /&gt;
* Distinguish common filesystem node types: regular files, directories, symbolic links, block/character devices, FIFOs (named pipes), and Unix domain sockets.&lt;br /&gt;
* Discover mounted filesystems and underlying block devices using &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;, and explain the relationship between them.&lt;br /&gt;
* Manipulate filesystem nodes using standard CLI tools (&amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chmod&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chown&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Create a loopback block device backed by a file and format it with &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt;, then mount, verify, and clean it up safely.&lt;br /&gt;
&lt;br /&gt;
= Quick Refresher: Paths and Hierarchy =&lt;br /&gt;
&lt;br /&gt;
Linux organizes everything in a single tree with &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; as the root. Example key directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/etc&amp;lt;/code&amp;gt; (system config), &amp;lt;code&amp;gt;/bin&amp;lt;/code&amp;gt; (essential binaries), &amp;lt;code&amp;gt;/usr&amp;lt;/code&amp;gt; (user-land programs), &amp;lt;code&amp;gt;/home&amp;lt;/code&amp;gt; (user homes), &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; (variable data), &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; (temporary mounts), &amp;lt;code&amp;gt;/proc&amp;lt;/code&amp;gt; (procfs), &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (device nodes).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Absolute path&amp;#039;&amp;#039;&amp;#039;: starts at &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/home/student/lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Relative path&amp;#039;&amp;#039;&amp;#039;: from current directory (e.g., &amp;lt;code&amp;gt;../lab/loop.img&amp;lt;/code&amp;gt;).&lt;br /&gt;
Use &amp;lt;code&amp;gt;pwd&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cd&amp;lt;/code&amp;gt;, and tab-completion to navigate.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Try:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
pwd&lt;br /&gt;
cd ~&lt;br /&gt;
ls -la &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Filesystem Node Types =&lt;br /&gt;
&lt;br /&gt;
Each directory entry points to an &amp;#039;&amp;#039;&amp;#039;inode&amp;#039;&amp;#039;&amp;#039; (metadata: type, permissions, owner, timestamps, block pointers). The file type is shown by the first character in &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Char !! Type !! How to see/create/observe&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; || Regular file || &amp;lt;code&amp;gt;touch file&amp;lt;/code&amp;gt;; write with editors or &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; || Directory || &amp;lt;code&amp;gt;mkdir dir&amp;lt;/code&amp;gt;; remove with &amp;lt;code&amp;gt;rmdir&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;rm -r&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; || Symbolic link || &amp;lt;code&amp;gt;ln -s target linkname&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;b&amp;lt;/code&amp;gt; || Block device || Usually in &amp;lt;code&amp;gt;/dev&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; || Character device || &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/tty&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;p&amp;lt;/code&amp;gt; || FIFO (named pipe) || &amp;lt;code&amp;gt;mkfifo pipe&amp;lt;/code&amp;gt;; read/write with &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; || Unix socket || Created by programs (e.g., services); list with &amp;lt;code&amp;gt;ls -l&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ss -x&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore inode numbers:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
ls -li&lt;br /&gt;
stat file &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Hard vs. symbolic links:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir ~/lab-nodes &amp;amp;&amp;amp; cd ~/lab-nodes&lt;br /&gt;
printf &amp;#039;hello\n&amp;#039; &amp;gt; a.txt&lt;br /&gt;
ln a.txt a.hard        # hard link (same inode)&lt;br /&gt;
ln -s a.txt a.sym      # symlink (different inode)&lt;br /&gt;
ls -li&lt;br /&gt;
rm a.txt &amp;amp;&amp;amp; cat a.hard # still works; inode persists via hard link&lt;br /&gt;
cat a.sym              # will fail: dangling symlink &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Discovering Filesystems and Devices =&lt;br /&gt;
&lt;br /&gt;
Two essential tools:&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; — usage of mounted filesystems ==&lt;br /&gt;
&lt;br /&gt;
* Shows how full mounted filesystems are and where they are mounted.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-h&amp;lt;/code&amp;gt; (human), &amp;lt;code&amp;gt;-T&amp;lt;/code&amp;gt; (type), target path to narrow output.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT&lt;br /&gt;
# Focus on a path&lt;br /&gt;
sudo mkdir -p /mnt &amp;amp;&amp;amp; df -hT /mnt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; — block devices and partitions ==&lt;br /&gt;
&lt;br /&gt;
* Shows block devices (disks, partitions, loop devices) regardless of mount state.&lt;br /&gt;
* Helpful flags: &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt; (FSType/Label/UUID), &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt; to customize columns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk&lt;br /&gt;
lsblk -f&lt;br /&gt;
lsblk -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relationship between &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt; lists devices (e.g., &amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/dev/loop0&amp;lt;/code&amp;gt;) and where they’re &amp;#039;&amp;#039;&amp;#039;mounted&amp;#039;&amp;#039;&amp;#039; (MOUNTPOINTS column).&lt;br /&gt;
* &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; reports &amp;#039;&amp;#039;&amp;#039;space usage&amp;#039;&amp;#039;&amp;#039; per &amp;#039;&amp;#039;&amp;#039;mounted filesystem&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
  &amp;#039;&amp;#039;&amp;#039;Mapping tip:&amp;#039;&amp;#039;&amp;#039; Find the row in &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; whose &amp;lt;code&amp;gt;MOUNTPOINTS&amp;lt;/code&amp;gt; equals the &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; mountpoint; the corresponding &amp;lt;code&amp;gt;NAME&amp;lt;/code&amp;gt; (e.g., &amp;lt;code&amp;gt;sda2&amp;lt;/code&amp;gt;) is the device backing that filesystem.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Exercise:&amp;#039;&amp;#039;&amp;#039; Identify the device backing your home directory and its filesystem type. &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
df -hT ~&lt;br /&gt;
lsblk -f | grep $(df --output=source ~ | tail -1) &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working with Nodes =&lt;br /&gt;
&lt;br /&gt;
Core commands (read &amp;lt;code&amp;gt;man&amp;lt;/code&amp;gt; pages for details):&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;List and inspect&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;ls -la&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tree&amp;lt;/code&amp;gt; (optional), &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Create&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;touch&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkdir&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mkfifo&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ln -s&amp;lt;/code&amp;gt;, (rarely) &amp;lt;code&amp;gt;mknod&amp;lt;/code&amp;gt; for manual device nodes.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Copy/Move/Delete&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cp&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; for dirs), &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;rm&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;-r&amp;lt;/code&amp;gt; recursive, &amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt; interactive).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Search&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;View&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;less&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tail&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;wc&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Space usage&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;du -sh DIR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Mini-lab:&amp;#039;&amp;#039;&amp;#039; &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-fs/dir1/dir2&lt;br /&gt;
cd ~/lab-fs&lt;br /&gt;
touch notes.txt&lt;br /&gt;
echo &amp;quot;sample&amp;quot; &amp;gt; dir1/data&lt;br /&gt;
mkfifo dir1/pipe&lt;br /&gt;
ln -s dir1/data link-to-data&lt;br /&gt;
ls -lR&lt;br /&gt;
file notes.txt dir1/pipe link-to-data&lt;br /&gt;
find . -type f -size +0&lt;br /&gt;
chmod 640 notes.txt &amp;amp;&amp;amp; stat notes.txt &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: Note: Sockets are typically created by running daemons. You can spot them with &amp;lt;code&amp;gt;find -type s&amp;lt;/code&amp;gt; (e.g., in &amp;lt;code&amp;gt;/run/&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;/var/run/&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
= Creating and Using a Loopback Filesystem =&lt;br /&gt;
&lt;br /&gt;
Goal: Create a &amp;#039;&amp;#039;&amp;#039;file-backed block device&amp;#039;&amp;#039;&amp;#039; with &amp;lt;code&amp;gt;losetup&amp;lt;/code&amp;gt;, format it as &amp;#039;&amp;#039;&amp;#039;ext4&amp;#039;&amp;#039;&amp;#039;, mount it, and verify via &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Create a sparse backing file ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/lab-loop &amp;amp;&amp;amp; cd ~/lab-loop&lt;br /&gt;
truncate -s 100M loop.img   # fast; or: dd if=/dev/zero of=loop.img bs=1M count=100&lt;br /&gt;
ls -lh loop.img &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attach the file to a free loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo losetup -fP loop.img               # chooses next free /dev/loopX&lt;br /&gt;
losetup -a                               # show all loop mappings&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verify with &amp;lt;code&amp;gt;lsblk&amp;lt;/code&amp;gt;: &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
lsblk -f | grep loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Make an ext4 filesystem on the loop device ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What happens:&amp;#039;&amp;#039;&amp;#039; ext4 creates a superblock and inode tables, then initializes data structures. &lt;br /&gt;
&lt;br /&gt;
== Mount and validate ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /mnt/labfs&lt;br /&gt;
sudo mount &amp;quot;$LOOPDEV&amp;quot; /mnt/labfs&lt;br /&gt;
&lt;br /&gt;
df -hT /mnt/labfs&lt;br /&gt;
lsblk -f | grep $(basename &amp;quot;$LOOPDEV&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create some content and examine space usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo sh -c &amp;#039;echo &amp;quot;hello ext4&amp;quot; &amp;gt; /mnt/labfs/hello.txt&amp;#039;&lt;br /&gt;
sudo ls -la /mnt/labfs&lt;br /&gt;
sudo du -sh /mnt/labfs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unmount and detach (clean up) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo umount /mnt/labfs&lt;br /&gt;
sudo losetup -d /dev/loopX&lt;br /&gt;
rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
: &amp;#039;&amp;#039;&amp;#039;Safety tip:&amp;#039;&amp;#039;&amp;#039; Always unmount before detaching the loop device. Use &amp;lt;code&amp;gt;lsof | grep /mnt/labfs&amp;lt;/code&amp;gt; if the mount is busy.&lt;br /&gt;
&lt;br /&gt;
= Mount Tables and Where Linux Stores Them =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;mount&amp;lt;/code&amp;gt; without args lists mounts.&lt;br /&gt;
* Modern systems reflect mounts in &amp;lt;code&amp;gt;/proc/self/mounts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/proc/mounts&amp;lt;/code&amp;gt;. Many distros keep &amp;lt;code&amp;gt;/etc/mtab&amp;lt;/code&amp;gt; as a compatibility symlink.&lt;br /&gt;
* Persistent mounts are configured in &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; (be careful!).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Explore:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cat /proc/mounts | head&lt;br /&gt;
mount | head&lt;br /&gt;
cat /etc/fstab&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Assessment: Lab Submission Tasks =&lt;br /&gt;
&lt;br /&gt;
Complete the following tasks and submit a report with your commands and output.&lt;br /&gt;
&lt;br /&gt;
=== Device to Filesystem Mapping ===&lt;br /&gt;
* Run &amp;lt;code&amp;gt;df -hT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Identify the line in each result that corresponds to your &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; (root) filesystem.&lt;br /&gt;
&lt;br /&gt;
=== Links and Inodes ===&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;original.txt&amp;lt;/code&amp;gt;, a hard link &amp;lt;code&amp;gt;hard.link&amp;lt;/code&amp;gt;, and a symbolic link &amp;lt;code&amp;gt;symbolic.link&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
* Provide the output of &amp;lt;code&amp;gt;ls -li&amp;lt;/code&amp;gt; and explain why the inode numbers are the same or different.&lt;br /&gt;
&lt;br /&gt;
=== Loopback Filesystem Creation ===&lt;br /&gt;
* Create a 150&amp;amp;nbsp;MB &amp;lt;code&amp;gt;loop.img&amp;lt;/code&amp;gt;, format it with ext4 (label &amp;lt;code&amp;gt;MY_LOOP&amp;lt;/code&amp;gt;), and mount it on &amp;lt;code&amp;gt;/mnt/mydata&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Create a file &amp;lt;code&amp;gt;proof.txt&amp;lt;/code&amp;gt; inside it containing your name.&lt;br /&gt;
* Provide the final output of &amp;lt;code&amp;gt;df -hT /mnt/mydata&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; showing your mounted device.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; Command Practice ===&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command that lists all files in your home directory (&amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt;) larger than 1&amp;amp;nbsp;MB and modified in the last 7 days.&lt;br /&gt;
* Provide the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to list all Unix domain sockets under &amp;lt;code&amp;gt;/run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Real-World Scenario: The Automated Organizer ===&lt;br /&gt;
# &amp;#039;&amp;#039;Imagine your &amp;lt;code&amp;gt;Downloads&amp;lt;/code&amp;gt; directory is cluttered. Your task is to write a sequence of commands to automatically clean it up.&amp;#039;&amp;#039;&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Setup:&amp;#039;&amp;#039;&amp;#039; Create a &amp;lt;code&amp;gt;~/cleanup_target&amp;lt;/code&amp;gt; directory with at least 10 files, including:&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;program1.log&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Three &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files (e.g., &amp;lt;code&amp;gt;data.tmp&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* One file larger than 10&amp;amp;nbsp;MB (e.g., &amp;lt;code&amp;gt;truncate -s 15M large_dataset.dat&amp;lt;/code&amp;gt;).&lt;br /&gt;
#* Some other miscellaneous files.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Automation Task:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
#* Create subdirectories: &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.log&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;logs&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all &amp;lt;code&amp;gt;.tmp&amp;lt;/code&amp;gt; files into &amp;lt;code&amp;gt;temp&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Move all files larger than 10&amp;amp;nbsp;MB into &amp;lt;code&amp;gt;large_files&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* Create a &amp;lt;code&amp;gt;cleanup_report.txt&amp;lt;/code&amp;gt; listing the contents of the new subdirectories.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Submission:&amp;#039;&amp;#039;&amp;#039; Provide the exact sequence of commands you used.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Provide a solution that does &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; use shell expansion (like &amp;lt;code&amp;gt;*.log&amp;lt;/code&amp;gt;). Instead, use a more robust tool like &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;-exec&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;xargs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Submission ===&lt;br /&gt;
Submit a short report with command history snippets and brief explanations (≈1–2 pages).&lt;br /&gt;
&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mkfs.ext4: device is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Ensure it’s &amp;#039;&amp;#039;&amp;#039;not mounted&amp;#039;&amp;#039;&amp;#039;: &amp;lt;code&amp;gt;mount | grep loop&amp;lt;/code&amp;gt;; unmount, then retry.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;umount: target is busy&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039; — Find open files: &amp;lt;code&amp;gt;sudo lsof +f -- /mnt/labfs&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;fuser -vm /mnt/labfs&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;No free loop device&amp;#039;&amp;#039;&amp;#039; — Create one: &amp;lt;code&amp;gt;sudo modprobe loop&amp;lt;/code&amp;gt;; or manually: &amp;lt;code&amp;gt;sudo losetup /dev/loop10 loop.img&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wrong device formatted&amp;#039;&amp;#039;&amp;#039; — Always confirm with &amp;lt;code&amp;gt;lsblk -f&amp;lt;/code&amp;gt; before &amp;lt;code&amp;gt;mkfs.*&amp;lt;/code&amp;gt;. In a VM, take a snapshot first.&lt;br /&gt;
&lt;br /&gt;
= Optional Extensions =&lt;br /&gt;
&lt;br /&gt;
* Create partitions &amp;#039;&amp;#039;&amp;#039;inside&amp;#039;&amp;#039;&amp;#039; the loop device (&amp;lt;code&amp;gt;sudo sfdisk &amp;quot;$LOOPDEV&amp;quot;&amp;lt;/code&amp;gt;), then &amp;lt;code&amp;gt;partprobe&amp;lt;/code&amp;gt; and format &amp;lt;code&amp;gt;/dev/loopXp1&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Compare &amp;lt;code&amp;gt;mkfs.ext2&amp;lt;/code&amp;gt; vs. &amp;lt;code&amp;gt;mkfs.ext4&amp;lt;/code&amp;gt; space usage and features (journaling, extent maps).&lt;br /&gt;
* Mount with options: &amp;lt;code&amp;gt;sudo mount -o noatime,nodiratime &amp;quot;$LOOPDEV&amp;quot; /mnt/labfs&amp;lt;/code&amp;gt; and measure &amp;lt;code&amp;gt;stat&amp;lt;/code&amp;gt; times.&lt;br /&gt;
&lt;br /&gt;
== Quick Command Cheat Sheet ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Discover&lt;br /&gt;
&lt;br /&gt;
pwd; ls -la; stat FILE; file FILE; du -sh DIR; df -hT; lsblk -f&lt;br /&gt;
&lt;br /&gt;
# Make nodes&lt;br /&gt;
&lt;br /&gt;
mkdir DIR; touch FILE; ln -s TARGET LINK; mkfifo PIPE&lt;br /&gt;
&lt;br /&gt;
# Links vs inodes&lt;br /&gt;
&lt;br /&gt;
ln SRC DEST   # hard link (same inode)&lt;br /&gt;
ln -s SRC DEST # symlink (diff inode)&lt;br /&gt;
&lt;br /&gt;
# Loopback + ext4&lt;br /&gt;
&lt;br /&gt;
truncate -s 100M loop.img&lt;br /&gt;
sudo losetup -fP loop.img; losetup -a&lt;br /&gt;
sudo mkfs.ext4 -L LAB_FS /dev/loopX&lt;br /&gt;
sudo mkdir -p /mnt/labfs &amp;amp;&amp;amp; sudo mount /dev/loopX /mnt/labfs&lt;br /&gt;
df -hT /mnt/labfs; lsblk -f | grep loopX&lt;br /&gt;
sudo umount /mnt/labfs; sudo losetup -d /dev/loopX; rm -f loop.img&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>