From 83831b8622b8c57bcddc6037f69eccdfdc1d0f2b Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 19 Mar 2026 14:30:04 +0800 Subject: [PATCH] feat: expand linux incident playbooks --- COURSE_TASKS.json | 167 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 5 deletions(-) diff --git a/COURSE_TASKS.json b/COURSE_TASKS.json index 6e07232..306077c 100644 --- a/COURSE_TASKS.json +++ b/COURSE_TASKS.json @@ -1,13 +1,13 @@ { "meta": { - "version": "5.0", + "version": "5.1", "title": "Linux Operations Learning Lab", "author": "Codex", "updated": "2026-03-19", - "description": "A rebuilt and valid Linux course focused on command purpose, troubleshooting flow, and sandbox repetition.", - "module_count": 4, - "total_lessons": 8, - "total_exercises": 24, + "description": "A rebuilt Linux learning path that moves from command basics to real operations troubleshooting with sandbox exercises.", + "module_count": 5, + "total_lessons": 15, + "total_exercises": 48, "pedagogy": "learning-first", "orientation": "ops-workflow" }, @@ -58,6 +58,27 @@ { "id": "m1_l2_e2", "type": "operation", "title": "List the current directory", "hint": "Start with ls.", "success_test": "cmd == 'ls'", "solution": ["ls"], "success_msg": "You listed the current directory." }, { "id": "m1_l2_e3", "type": "operation", "title": "Show hidden files and details", "hint": "Use ls -la.", "success_test": "cmd == 'ls -la' or cmd == 'ls -al'", "solution": ["ls -la", "ls -al"], "success_msg": "You switched from a simple listing to a full inspection view." } ] + }, + { + "id": "m1_l3_cd_cat_echo", + "title": "Move, read, and confirm with cd, cat, and echo", + "goal": "Use navigation, file reads, and quick output together as one everyday workflow.", + "why_it_matters": "Many Linux tasks are just a chain of navigate, inspect, and confirm.", + "concepts": ["Changing directories", "Reading text files", "Using echo for quick verification"], + "command": "cd / cat / echo", + "examples": ["cd /tmp", "cat /etc/hosts", "echo Hello Linux"], + "pitfalls": ["Using cat on a directory", "Changing directories without verifying where you landed", "Ignoring echo as a debugging aid"], + "scenarios": ["Enter a workspace and inspect a config file", "Print quick checkpoints in a shell session"], + "troubleshooting_flow": ["Change to the right location", "Read the right file", "Echo the key assumption you want to verify"], + "related_commands": ["cd", "cat", "echo", "pwd"], + "classic_view": "These commands make context and state visible before you do more powerful operations.", + "takeaways": ["Use cd intentionally", "Use cat for quick text inspection", "Use echo to make invisible state explicit"], + "after_class": "Practice saying out loud what each command changed in your working context.", + "exercises": [ + { "id": "m1_l3_e1", "type": "operation", "title": "Enter /tmp", "hint": "Use cd /tmp.", "success_test": "cmd == 'cd /tmp' and cwd == '/tmp'", "solution": ["cd /tmp"], "success_msg": "You moved to the target working directory." }, + { "id": "m1_l3_e2", "type": "operation", "title": "Read the hosts file", "hint": "Run cat /etc/hosts.", "success_test": "cmd == 'cat /etc/hosts' and 'localhost' in output", "solution": ["cat /etc/hosts"], "success_msg": "You inspected a real text file in the sandbox." }, + { "id": "m1_l3_e3", "type": "operation", "title": "Print a confirmation message", "hint": "Run echo Hello Linux.", "success_test": "cmd == 'echo Hello Linux' and 'Hello Linux' in output", "solution": ["echo Hello Linux"], "success_msg": "You used echo to make the current step explicit." } + ] } ] }, @@ -107,6 +128,27 @@ { "id": "m2_l2_e2", "type": "operation", "title": "Rename the backup file", "hint": "Run mv /tmp/hosts.bak /tmp/hosts.backup.", "success_test": "cmd == 'mv /tmp/hosts.bak /tmp/hosts.backup' and exists('/tmp/hosts.backup')", "solution": ["mv /tmp/hosts.bak /tmp/hosts.backup"], "success_msg": "You renamed the file successfully." }, { "id": "m2_l2_e3", "type": "operation", "title": "Inspect hosts metadata", "hint": "Run stat /etc/hosts.", "success_test": "cmd == 'stat /etc/hosts' and 'File:' in output", "solution": ["stat /etc/hosts"], "success_msg": "You inspected the file metadata successfully." } ] + }, + { + "id": "m2_l3_cleanup_and_modes", + "title": "Clean up and change modes with rm and chmod", + "goal": "Delete temporary artifacts safely and connect permission changes to execution behavior.", + "why_it_matters": "Cleanup and permission changes are common, but both can create damage if you skip inspection.", + "concepts": ["Cleanup order", "Symbolic vs numeric modes", "Execution permission"], + "command": "rm / chmod", + "examples": ["rm /tmp/hosts.backup", "chmod 755 /tmp/lab", "chmod +x /tmp/lab/notes.txt"], + "pitfalls": ["Deleting before confirming the file is disposable", "Changing permissions without knowing why"], + "scenarios": ["Remove temporary files after verification", "Make a script or directory executable when needed"], + "troubleshooting_flow": ["Verify the target path", "Remove only temporary files", "Inspect and then change permissions with intent"], + "related_commands": ["rm", "chmod", "stat", "ls"], + "classic_view": "Cleanup and permission work should be deliberate, not reactive.", + "takeaways": ["Delete last", "Use numeric and symbolic modes intentionally", "Connect permissions to a real need"], + "after_class": "Repeat the sequence inspect -> remove or inspect -> chmod until it feels like a safe habit.", + "exercises": [ + { "id": "m2_l3_e1", "type": "operation", "title": "Remove the renamed backup", "hint": "Run rm /tmp/hosts.backup.", "success_test": "cmd == 'rm /tmp/hosts.backup' and not exists('/tmp/hosts.backup')", "solution": ["rm /tmp/hosts.backup"], "success_msg": "You cleaned up the temporary backup safely." }, + { "id": "m2_l3_e2", "type": "operation", "title": "Set the lab directory mode to 755", "hint": "Run chmod 755 /tmp/lab.", "success_test": "cmd == 'chmod 755 /tmp/lab'", "solution": ["chmod 755 /tmp/lab"], "success_msg": "You applied a numeric permission change." }, + { "id": "m2_l3_e3", "type": "operation", "title": "Add execute permission to notes.txt", "hint": "Run chmod +x /tmp/lab/notes.txt.", "success_test": "cmd == 'chmod +x /tmp/lab/notes.txt'", "solution": ["chmod +x /tmp/lab/notes.txt"], "success_msg": "You used a symbolic permission change." } + ] } ] }, @@ -156,6 +198,27 @@ { "id": "m3_l2_e2", "type": "operation", "title": "Find log files under /var/log", "hint": "Run find /var/log -type f.", "success_test": "cmd == 'find /var/log -type f' and '/var/log' in output", "solution": ["find /var/log -type f"], "success_msg": "You filtered the search to files under the log tree." }, { "id": "m3_l2_e3", "type": "operation", "title": "Read the newest auth log line", "hint": "Run tail -n 1 /var/log/auth.log.", "success_test": "cmd == 'tail -n 1 /var/log/auth.log' and 'sandbox_user' in output", "solution": ["tail -n 1 /var/log/auth.log"], "success_msg": "You previewed the most recent auth log line." } ] + }, + { + "id": "m3_l3_preview_and_pipeline", + "title": "Preview files with head, tail, and a simple pipeline", + "goal": "Read only the part of a file that answers your current question.", + "why_it_matters": "Large files are easier to scan when you only open the beginning or end first.", + "concepts": ["Top lines", "Bottom lines", "Simple pipelines for narrowing output"], + "command": "head / tail / cat | tail", + "examples": ["head /etc/passwd", "tail -n 1 /var/log/auth.log", "cat /var/log/auth.log | tail -n 1"], + "pitfalls": ["Using cat on a large file when a preview is enough", "Forgetting to change the line count for a focused question"], + "scenarios": ["Check whether a file starts with the right structure", "Inspect the last auth event in a log"], + "troubleshooting_flow": ["Choose the right end of the file", "Preview a small number of lines", "Switch to grep or full reads only if needed"], + "related_commands": ["head", "tail", "cat", "grep"], + "classic_view": "Preview commands reduce noise so you can decide whether the file deserves deeper inspection.", + "takeaways": ["Use head for structure", "Use tail for recent activity", "Use a pipeline when one command is not enough"], + "after_class": "Practice explaining why you chose head or tail before you run the command.", + "exercises": [ + { "id": "m3_l3_e1", "type": "operation", "title": "Preview the top of passwd", "hint": "Run head /etc/passwd.", "success_test": "cmd == 'head /etc/passwd' and 'root' in output", "solution": ["head /etc/passwd"], "success_msg": "You previewed the beginning of the file." }, + { "id": "m3_l3_e2", "type": "operation", "title": "Inspect the newest auth log line", "hint": "Run tail -n 1 /var/log/auth.log.", "success_test": "cmd == 'tail -n 1 /var/log/auth.log' and 'sandbox_user' in output", "solution": ["tail -n 1 /var/log/auth.log"], "success_msg": "You narrowed the file to the most recent line." }, + { "id": "m3_l3_e3", "type": "operation", "title": "Use a simple pipeline to preview auth.log", "hint": "Run cat /var/log/auth.log | tail -n 1.", "success_test": "cmd == 'cat /var/log/auth.log | tail -n 1' and 'sandbox_user' in output", "solution": ["cat /var/log/auth.log | tail -n 1"], "success_msg": "You combined two commands to narrow the log quickly." } + ] } ] }, @@ -205,6 +268,100 @@ { "id": "m4_l2_e2", "type": "operation", "title": "Test local connectivity", "hint": "Run ping 127.0.0.1.", "success_test": "cmd == 'ping 127.0.0.1' and 'packet loss' in output", "solution": ["ping 127.0.0.1"], "success_msg": "You verified the local connectivity layer." }, { "id": "m4_l2_e3", "type": "operation", "title": "Validate the application response", "hint": "Run curl http://127.0.0.1.", "success_test": "cmd == 'curl http://127.0.0.1' and 'hello localhost' in output", "solution": ["curl http://127.0.0.1"], "success_msg": "You completed the network path all the way to the application layer." } ] + }, + { + "id": "m4_l3_service", + "title": "Trace service health with systemctl and journalctl", + "goal": "Move from service state to recent logs in a deliberate sequence.", + "why_it_matters": "Service incidents are easier to solve when you separate service state from application logs.", + "concepts": ["Service unit state", "Recent journal lines", "From status to explanation"], + "command": "systemctl / journalctl", + "examples": ["systemctl status nginx", "journalctl -n 20", "systemctl restart nginx"], + "pitfalls": ["Restarting a service without checking why it failed", "Reading status without following the logs"], + "scenarios": ["A service is reported down or unstable", "You need the last error before considering a restart"], + "troubleshooting_flow": ["Read service state with systemctl status", "Open the recent journal lines", "Use the message to guide the next repair"], + "related_commands": ["systemctl", "journalctl", "ss"], + "classic_view": "Service status tells you what is happening; logs tell you why.", + "takeaways": ["Start with status", "Use logs for explanation", "Restart only after you understand the failure"], + "after_class": "Say out loud what systemctl tells you that journalctl does not, and vice versa.", + "exercises": [ + { "id": "m4_l3_e1", "type": "operation", "title": "Check nginx service status", "hint": "Run systemctl status nginx.", "success_test": "cmd == 'systemctl status nginx' and 'Active:' in output", "solution": ["systemctl status nginx"], "success_msg": "You inspected the service state." }, + { "id": "m4_l3_e2", "type": "operation", "title": "Read recent journal lines", "hint": "Run journalctl -n 20.", "success_test": "cmd == 'journalctl -n 20' and 'Started nginx.service' in output", "solution": ["journalctl -n 20"], "success_msg": "You pulled recent journal evidence." }, + { "id": "m4_l3_e3", "type": "operation", "title": "Simulate a service restart command", "hint": "Run systemctl restart nginx.", "success_test": "cmd == 'systemctl restart nginx' and 'completed for nginx' in output", "solution": ["systemctl restart nginx"], "success_msg": "You practiced the restart command after inspection." } + ] + } + ] + }, + { + "id": "module_5_incidents", + "title": "Module 5: Practice incident playbooks", + "summary": "Use the commands you learned in layered incident drills so the tools feel connected to real operations work.", + "lessons": [ + { + "id": "m5_l1_disk", + "title": "Incident drill: investigate disk pressure", + "goal": "Move from broad disk visibility to specific directory evidence.", + "why_it_matters": "Disk pressure can break writes, logs, and services, so you need a repeatable inspection path.", + "concepts": ["Filesystem usage", "Directory usage", "Large-file hunting"], + "command": "df / du / find", + "examples": ["df -h", "du -sh /var/log", "find /var/log -type f"], + "pitfalls": ["Stopping at df without drilling into the heavy directory", "Deleting files before confirming what they are for"], + "scenarios": ["A host reports that disk usage is full", "A log directory appears to be growing too quickly"], + "troubleshooting_flow": ["Confirm which filesystem is full", "Measure the likely heavy directory", "Locate concrete files before deciding cleanup"], + "related_commands": ["df", "du", "find", "sort"], + "classic_view": "Disk incidents are easier when you move from filesystem view to directory view to file view in order.", + "takeaways": ["Use df for the broad picture", "Use du for directory evidence", "Use find before deleting anything"], + "after_class": "Practice saying which layer each command belongs to: filesystem, directory, or file.", + "exercises": [ + { "id": "m5_l1_e1", "type": "operation", "title": "Check filesystem usage", "hint": "Run df -h.", "success_test": "cmd == 'df -h' and 'Filesystem' in output", "solution": ["df -h"], "success_msg": "You captured the broad disk usage picture." }, + { "id": "m5_l1_e2", "type": "operation", "title": "Measure /var/log usage", "hint": "Run du -sh /var/log.", "success_test": "cmd == 'du -sh /var/log' and '/var/log' in output", "solution": ["du -sh /var/log"], "success_msg": "You narrowed the issue to a directory-level view." }, + { "id": "m5_l1_e3", "type": "operation", "title": "Locate log files under /var/log", "hint": "Run find /var/log -type f.", "success_test": "cmd == 'find /var/log -type f' and '/var/log' in output", "solution": ["find /var/log -type f"], "success_msg": "You drilled down to the file layer." }, + { "id": "m5_l1_e4", "type": "scenario", "question": "Why is deleting files before directory and file inspection risky?", "answer": "Because you can remove useful evidence or the wrong data before you understand what is consuming space." } + ] + }, + { + "id": "m5_l2_auth", + "title": "Incident drill: investigate login or permission failures", + "goal": "Link identity, account records, and recent auth logs into one diagnosis sequence.", + "why_it_matters": "Access problems often mix user identity, account state, and authentication evidence.", + "concepts": ["Current identity", "Account presence", "Recent authentication history"], + "command": "whoami / id / grep / tail", + "examples": ["whoami", "id", "grep sandbox_user /etc/passwd", "cat /var/log/auth.log | tail -n 1"], + "pitfalls": ["Guessing a password issue without checking logs", "Ignoring account records and group membership"], + "scenarios": ["A login fails repeatedly", "A script exists but still cannot be used by the current user"], + "troubleshooting_flow": ["Confirm the current identity", "Verify the target account exists", "Read the newest auth evidence"], + "related_commands": ["whoami", "id", "grep", "tail"], + "classic_view": "Access issues are easier when you inspect identity, account records, and logs as one chain.", + "takeaways": ["Confirm the user context first", "Use account files as evidence", "Trust logs more than guesses"], + "after_class": "Repeat the identity -> account -> log sequence until it feels like one habit.", + "exercises": [ + { "id": "m5_l2_e1", "type": "operation", "title": "Confirm your current identity", "hint": "Run whoami.", "success_test": "cmd == 'whoami' and 'sandbox_user' in output", "solution": ["whoami"], "success_msg": "You confirmed the current shell identity." }, + { "id": "m5_l2_e2", "type": "operation", "title": "Inspect uid and group information", "hint": "Run id.", "success_test": "cmd == 'id' and 'uid=' in output", "solution": ["id"], "success_msg": "You inspected the uid and group context." }, + { "id": "m5_l2_e3", "type": "operation", "title": "Check whether sandbox_user exists in passwd", "hint": "Run grep sandbox_user /etc/passwd.", "success_test": "cmd == 'grep sandbox_user /etc/passwd' and 'sandbox_user' in output", "solution": ["grep sandbox_user /etc/passwd"], "success_msg": "You verified the account record." }, + { "id": "m5_l2_e4", "type": "operation", "title": "Read the newest auth log line", "hint": "Run cat /var/log/auth.log | tail -n 1.", "success_test": "cmd == 'cat /var/log/auth.log | tail -n 1' and 'sandbox_user' in output", "solution": ["cat /var/log/auth.log | tail -n 1"], "success_msg": "You retrieved recent authentication evidence." } + ] + }, + { + "id": "m5_l3_service_path", + "title": "Incident drill: service is up, but the application still feels broken", + "goal": "Check service state, listening sockets, and application output in one guided drill.", + "why_it_matters": "A service can look healthy at one layer but still fail at another layer.", + "concepts": ["Service state", "Socket listening state", "Application response validation"], + "command": "systemctl / ss / curl / journalctl", + "examples": ["systemctl status nginx", "ss -ltnp", "curl http://127.0.0.1", "journalctl -n 20"], + "pitfalls": ["Trusting systemctl alone without checking sockets or a real request", "Stopping at ss without making an application-layer call"], + "scenarios": ["A team says the service is up but the page still feels unavailable", "You need to prove whether the problem is service state, port state, or HTTP behavior"], + "troubleshooting_flow": ["Check systemctl for service state", "Check ss for listening ports", "Use curl to verify the real response", "Open journal logs if behavior still looks wrong"], + "related_commands": ["systemctl", "ss", "curl", "journalctl"], + "classic_view": "Healthy service state does not guarantee healthy application behavior, so finish the chain with a real request.", + "takeaways": ["Check multiple layers", "Do not confuse a listening port with a healthy app", "End the chain with a real request and logs when needed"], + "after_class": "Practice explaining which layer each command validates: service, socket, application, or logs.", + "exercises": [ + { "id": "m5_l3_e1", "type": "operation", "title": "Check nginx service state", "hint": "Run systemctl status nginx.", "success_test": "cmd == 'systemctl status nginx' and 'Active:' in output", "solution": ["systemctl status nginx"], "success_msg": "You confirmed the service layer state." }, + { "id": "m5_l3_e2", "type": "operation", "title": "Check listening sockets", "hint": "Run ss -ltnp.", "success_test": "cmd == 'ss -ltnp' and 'LISTEN' in output", "solution": ["ss -ltnp"], "success_msg": "You confirmed the socket layer." }, + { "id": "m5_l3_e3", "type": "operation", "title": "Make a real application request", "hint": "Run curl http://127.0.0.1.", "success_test": "cmd == 'curl http://127.0.0.1' and 'hello localhost' in output", "solution": ["curl http://127.0.0.1"], "success_msg": "You verified the final application response layer." }, + { "id": "m5_l3_e4", "type": "operation", "title": "Open recent journal lines", "hint": "Run journalctl -n 20.", "success_test": "cmd == 'journalctl -n 20' and 'Started nginx.service' in output", "solution": ["journalctl -n 20"], "success_msg": "You added log evidence to the service drill." } + ] } ] }