diff --git a/lab-01-trace-request/portfolio-summary.md b/lab-01-trace-request/portfolio-summary.md new file mode 100644 index 0000000..98c09d9 --- /dev/null +++ b/lab-01-trace-request/portfolio-summary.md @@ -0,0 +1,23 @@ +# Lab 1: Trace the Request + +## What I practiced + +In this lab, I worked with a simple virtual Linux network and used networking tools to examine: + +- name resolution, +- route selection, +- next-hop addressing, +- outgoing interface and source addressing, +- and neighbor information. + +## Request trace + +In one or two sentences, explain how the client moves from `web.minicorp.test` to its next-hop device. + +> Replace this line with your explanation. + +## Key takeaway + +Explain why the client needs both an IP address and a link-layer address when sending traffic to the next hop. + +> Replace this line with your explanation. diff --git a/lab-01-trace-request/scenario/README.md b/lab-01-trace-request/scenario/README.md new file mode 100644 index 0000000..651c0b3 --- /dev/null +++ b/lab-01-trace-request/scenario/README.md @@ -0,0 +1,19 @@ +# Lab 1 Scenario + +This directory contains the Lab 1-specific services and configuration for MiniCorp. + +The reusable Linux network topology is provided by: + +```text +shared/minicorp/ +``` + +Lab 1 uses a three-system virtual network: + +```text +client -------- router -------- server +``` + +The router provides DNS for the MiniCorp web service, and the server provides a simple HTTP service. + +Lab 1 intentionally keeps the application simple. Students investigate name resolution, routing, and neighbor information from the client's point of view. diff --git a/lab-01-trace-request/scenario/config.sh b/lab-01-trace-request/scenario/config.sh new file mode 100644 index 0000000..38e7153 --- /dev/null +++ b/lab-01-trace-request/scenario/config.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Lab 1: Trace the Request +# Scenario-specific configuration. +# +# Reusable network topology and addressing are provided by shared/minicorp. + +LAB_ID="lab-01-trace-request" +LAB_NAME="Trace the Request" + +# MiniCorp topology used by this scenario. +LAB_TOPOLOGY="client-router-server" + +# Web service. +LAB_WEB_HOSTNAME="web.minicorp.test" +LAB_WEB_PORT="8080" +LAB_WEB_STDOUT="/tmp/minicorp-lab1-web.stdout" + +# DNS service. +LAB_DNS_STDOUT="/tmp/minicorp-lab1-dns.stdout" + +# Namespace-specific resolver configuration. +LAB_CLIENT_RESOLV_DIR="/etc/netns/client" +LAB_CLIENT_RESOLV_CONF="/etc/netns/client/resolv.conf" diff --git a/lab-01-trace-request/scenario/reset.sh b/lab-01-trace-request/scenario/reset.sh new file mode 100755 index 0000000..3b21f36 --- /dev/null +++ b/lab-01-trace-request/scenario/reset.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LAB_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$LAB_DIR/../.." && pwd)" + +source "$SCRIPT_DIR/config.sh" +source "$REPO_ROOT/shared/minicorp/lib/minicorp.sh" +source "$REPO_ROOT/shared/minicorp/topologies/client-router-server.sh" + +printf '\n' +mc_log "Resetting $LAB_NAME" + +# Stop services and remove the MiniCorp network namespaces. +mc_topology_client_router_server_down + +# Remove the namespace-specific resolver configuration. +sudo rm -f "$LAB_CLIENT_RESOLV_CONF" +sudo rmdir "$LAB_CLIENT_RESOLV_DIR" 2>/dev/null || true + +# Remove temporary service output. +sudo rm -f \ + "$LAB_WEB_STDOUT" \ + "$LAB_DNS_STDOUT" + +printf '\n' +mc_log "$LAB_NAME has been reset." diff --git a/lab-01-trace-request/scenario/setup.sh b/lab-01-trace-request/scenario/setup.sh new file mode 100755 index 0000000..273844f --- /dev/null +++ b/lab-01-trace-request/scenario/setup.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LAB_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REPO_ROOT="$(cd "$LAB_DIR/../.." && pwd)" + +source "$SCRIPT_DIR/config.sh" +source "$REPO_ROOT/shared/minicorp/lib/minicorp.sh" +source "$REPO_ROOT/shared/minicorp/topologies/client-router-server.sh" + +printf '\n' +mc_log "Setting up $LAB_NAME" + +if [[ "$LAB_TOPOLOGY" != "client-router-server" ]]; then + mc_error "Unsupported topology: $LAB_TOPOLOGY" + exit 1 +fi + +# Verify the tools required by this lab. +for command_name in python3 dnsmasq ss curl getent dig; do + mc_require_command "$command_name" +done + +# Build the MiniCorp network from a known state. +# Suppress topology details so setup does not reveal investigation answers. +mc_log "Building MiniCorp virtual network" +mc_topology_client_router_server_up >/dev/null + +# Verify the topology without exposing the investigation answers. +if ! mc_topology_client_router_server_check >/dev/null; then + mc_error "MiniCorp topology validation failed." + exit 1 +fi + +# Remove stale service output from a previous run. +sudo rm -f \ + "$LAB_WEB_STDOUT" \ + "$LAB_DNS_STDOUT" + +# +# Web service +# + +mc_log "Starting MiniCorp web service" + +sudo ip netns exec "$MC_SERVER_NS" env \ + MINICORP_WEB_HOST="$MC_SERVER_ADDR" \ + MINICORP_WEB_PORT="$LAB_WEB_PORT" \ + python3 -u "$SCRIPT_DIR/web_server.py" \ + >"$LAB_WEB_STDOUT" 2>&1 & + +web_ready=0 + +for attempt in {1..20}; do + if mc_exec "$MC_SERVER_NS" ss -ltn | + grep -F "$MC_SERVER_ADDR:$LAB_WEB_PORT" >/dev/null + then + web_ready=1 + break + fi + + sleep 0.1 +done + +if [[ "$web_ready" -ne 1 ]]; then + mc_error "Web service failed to start." + + if [[ -f "$LAB_WEB_STDOUT" ]]; then + cat "$LAB_WEB_STDOUT" >&2 + fi + + exit 1 +fi + +# +# DNS service +# + +mc_log "Starting MiniCorp DNS service" + +sudo ip netns exec "$MC_ROUTER_NS" dnsmasq \ + --no-daemon \ + --interface="$MC_ROUTER_CLIENT_IF" \ + --listen-address="$MC_ROUTER_CLIENT_ADDR" \ + --bind-interfaces \ + --no-resolv \ + --no-hosts \ + --local=/minicorp.test/ \ + --host-record="$LAB_WEB_HOSTNAME,$MC_SERVER_ADDR" \ + --log-facility=- \ + >"$LAB_DNS_STDOUT" 2>&1 & + +dns_ready=0 + +for attempt in {1..20}; do + if mc_exec "$MC_ROUTER_NS" ss -lun | + grep -F "$MC_ROUTER_CLIENT_ADDR:53" >/dev/null + then + dns_ready=1 + break + fi + + sleep 0.1 +done + +if [[ "$dns_ready" -ne 1 ]]; then + mc_error "DNS service failed to start." + + if [[ -f "$LAB_DNS_STDOUT" ]]; then + cat "$LAB_DNS_STDOUT" >&2 + fi + + exit 1 +fi + +# +# Client resolver +# + +mc_log "Configuring client DNS resolver" + +sudo mkdir -p "$LAB_CLIENT_RESOLV_DIR" + +printf 'nameserver %s\n' "$MC_ROUTER_CLIENT_ADDR" | + sudo tee "$LAB_CLIENT_RESOLV_CONF" >/dev/null + +# +# Final health check +# + +if ! mc_exec "$MC_CLIENT_NS" \ + getent ahostsv4 "$LAB_WEB_HOSTNAME" >/dev/null +then + mc_error "Client name resolution check failed." + exit 1 +fi + +if ! mc_exec "$MC_CLIENT_NS" \ + curl --noproxy '*' -fsS \ + "http://$LAB_WEB_HOSTNAME:$LAB_WEB_PORT/" >/dev/null +then + mc_error "Client web request check failed." + exit 1 +fi + +printf '\n' +mc_log "$LAB_NAME is ready." diff --git a/lab-01-trace-request/scenario/web_server.py b/lab-01-trace-request/scenario/web_server.py new file mode 100755 index 0000000..12fb147 --- /dev/null +++ b/lab-01-trace-request/scenario/web_server.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +import os +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +HOST = os.environ.get("MINICORP_WEB_HOST", "10.53.2.20") +PORT = int(os.environ.get("MINICORP_WEB_PORT", "8080")) + + +class MiniCorpHandler(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/": + body = ( + "MiniCorp web service\n" + "Service: web.minicorp.test\n" + "Status: healthy\n" + ).encode("utf-8") + + self.send_response(200) + self.send_header("Content-Type", "text/plain; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + return + + self.send_error(404) + + def log_message(self, format, *args): + # Keep the student terminal focused on lab evidence. + pass + + +def main(): + server = ThreadingHTTPServer((HOST, PORT), MiniCorpHandler) + + print(f"MiniCorp web service listening on {HOST}:{PORT}") + server.serve_forever() + + +if __name__ == "__main__": + main()