feat(root01): author Computing & Linux Foundations, and the real 01->05 prerequisite chain
Adds all 6 ROOT 01 modules (how computers run software, the shell, filesystem navigation, files & text, processes, users & permissions foundations), each with a real lesson, demonstration/guided exercise, knowledge check where warranted, a hands-on Lab, and a reflection — 35 activities, 29 new content files. Adds 6 new hands-on Linux scenarios (linux-inspect-hardware, linux-shell-basics, linux-filesystem-navigation, linux-files-and-text, linux-process-management, linux-permissions-foundations) plus 2 MCQ scenarios, using the existing generic scenario/ validation engine (docs/ROOT_FOUNDATIONS_LAB_STRATEGY.md §1) — every one tested end-to-end via the real scenario setup/validate API, including negative tests confirming they correctly FAIL when not actually solved (e.g. the process-management scenario fails while the target process is still running; the permissions scenario is tested against a real `su - labuser` denial). Wires rootnode-linux-foundations-assessment (2 required + 4 random-draw scenarios) using the existing Phase 3B assessment engine. Adds a real, server-side ROOT 01->02->03->04->05 course prerequisite chain (courses.prerequisites) — verified live on two students: one who completed ROOT 01 sees ROOT 02 unlock; a fresh student sees ROOT 02 correctly locked while 03/04/05 stay vacuously satisfied (their own prerequisite courses are still empty). ROOT 06+ and the Capstone are unaffected. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
+66
@@ -0,0 +1,66 @@
|
||||
# How Computers Run Software
|
||||
|
||||
## Hardware, OS, and application: three different layers
|
||||
|
||||
When you run a program, three distinct layers cooperate:
|
||||
|
||||
- **Hardware** — the physical CPU, RAM, and storage. It has no idea what a "file" or a "process"
|
||||
is; it just executes instructions and stores bits.
|
||||
- **The operating system (kernel)** — the layer that turns raw hardware into something usable:
|
||||
it decides which program gets the CPU next, hands out memory, and organizes storage into
|
||||
files and directories. Linux is a kernel plus a large set of standard tools around it.
|
||||
- **Applications (userspace)** — the programs you actually run. They ask the kernel for
|
||||
resources (memory, files, network) through a well-defined interface (system calls) instead of
|
||||
touching hardware directly.
|
||||
|
||||
This module gives you just enough of this model to reason about what you're actually looking at
|
||||
for the rest of ROOT_ — not a full computer architecture course.
|
||||
|
||||
## CPU: the thing that actually executes instructions
|
||||
|
||||
A CPU has some number of **cores** — each core can run one stream of instructions at a time.
|
||||
`nproc` tells you how many cores/threads Linux believes it has available. When a system feels
|
||||
"slow," it's very often because more processes want CPU time than there are cores to give them.
|
||||
|
||||
## RAM: fast, temporary, and finite
|
||||
|
||||
RAM holds the data and code a running program is actively using. It's fast but **volatile** —
|
||||
its contents disappear when power is lost, which is exactly why persistent data lives on
|
||||
storage instead. `free -h` shows you total memory, how much is in use, and how much is free.
|
||||
|
||||
## Storage: slower, but persistent
|
||||
|
||||
Storage (an SSD/disk) keeps data even when the machine is off. It's organized into a
|
||||
**filesystem** — the tree of directories and files you already have some intuition for. `df -h`
|
||||
shows how much space exists and how much is used on each mounted filesystem.
|
||||
|
||||
## Processes: a running program
|
||||
|
||||
A **process** is a program actually being executed — code plus its own private memory, tracked
|
||||
by the kernel with a unique ID (a PID). One program can have many processes running at once
|
||||
(open two terminals running the same shell, and you have two separate processes). ROOT 01
|
||||
Module 5 goes much deeper into this; for now, just know: everything you run becomes a process
|
||||
the kernel is responsible for scheduling onto a CPU core.
|
||||
|
||||
## Why this matters for everything after this module
|
||||
|
||||
Every concept the rest of ROOT_ builds on is a refinement of this same idea. A container is
|
||||
(mostly) an isolated process with its own filesystem view. A Kubernetes Pod is a group of
|
||||
containers the scheduler places onto a specific machine's CPU/RAM. None of that will make sense
|
||||
as anything other than magic if you don't first have this base model of what a computer is
|
||||
actually managing.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Give students just enough of a mental model to stop treating "the computer" as
|
||||
an opaque box — without turning this into a full CS/architecture course.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live with discussion.
|
||||
|
||||
**Common misconception to watch for:** students often conflate RAM and storage ("my computer
|
||||
has 8GB" meaning disk space). Draw the volatile-vs-persistent distinction explicitly and early.
|
||||
|
||||
**Discussion question:** "Why can't a program just talk to the hardware directly instead of
|
||||
going through the kernel?" (Answer: the kernel is what lets many programs safely share one CPU/
|
||||
one block of RAM without stepping on each other — this is worth naming explicitly, as it's the
|
||||
root of why containers and Kubernetes are even possible.)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Guided Exercise: Predict Before You Look
|
||||
|
||||
Before you run the real inspection commands in the Lab, commit to a guess — this is what makes
|
||||
the real numbers meaningful instead of just scrolling past them.
|
||||
|
||||
## Your task
|
||||
|
||||
On paper or in a scratch file, write down your best GUESS for:
|
||||
|
||||
1. How many CPU cores does this machine have?
|
||||
2. How much total RAM, roughly, in GB?
|
||||
3. How much free disk space is on the root filesystem?
|
||||
|
||||
Then think through: which of these three would you expect a "student Lab" environment to be
|
||||
MOST constrained on, and why? (Hint: think about what happens if 50 students are each running
|
||||
their own Lab on one shared physical machine at the same time.)
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
There's no single right numeric answer here — the point is the habit of predicting first. A
|
||||
reasonable answer to the "most constrained" question: memory and CPU are typically the tightest
|
||||
shared resource on a multi-tenant host, since every additional Lab needs its own slice of both,
|
||||
while disk is comparatively cheap to over-provision.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Have students commit to a number verbally or on a shared doc BEFORE anyone
|
||||
runs the real commands — the value is in noticing the gap between guess and reality, not in
|
||||
getting it right.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
|
||||
**Optional extension:** ask what would happen to their guess if this were a "real" laptop
|
||||
they personally own vs. a shared cloud server — good primer for later resource-limit
|
||||
discussions (ROOT 06's resource requests/limits, ROOT 09's resource governance).
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Reflection: You Just Looked Inside the Box
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. How close was your guess to the real numbers? What surprised you most?
|
||||
2. Name one thing you now understand about "the computer" that you couldn't have explained
|
||||
before this module.
|
||||
3. When you eventually run `kubectl describe pod` and see `requests`/`limits` for CPU and
|
||||
memory, what do you now expect those numbers to actually be constraining?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Every resource limit you'll ever configure — in Docker, in Kubernetes, in a systemd unit — is
|
||||
a promise about exactly the three things this module named: CPU, memory, and storage.** You
|
||||
can't reason well about "why did my container get killed" or "why is my Pod stuck Pending"
|
||||
without this base model. This module isn't computer-science trivia — it's the vocabulary the
|
||||
rest of ROOT_ assumes you have.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 3 is worth spending real time on — it's
|
||||
the first moment students connect this abstract lesson to something they'll type as a real
|
||||
command many courses from now. Naming that connection explicitly here pays off later.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# Linux and the Shell
|
||||
|
||||
## What "Linux" actually means
|
||||
|
||||
Linux is a **kernel** — the core piece of software from Module 1 that manages CPU, memory, and
|
||||
storage. What most people call "a Linux system" (Ubuntu, Debian, Fedora, Alpine...) is a
|
||||
**distribution**: the Linux kernel plus a chosen set of standard tools, package manager, and
|
||||
configuration conventions layered on top. Different distributions, same kernel underneath —
|
||||
which is why a command you learn on Ubuntu mostly still works on Alpine.
|
||||
|
||||
## Shell vs. terminal
|
||||
|
||||
These get used interchangeably but are different things:
|
||||
|
||||
- The **terminal** is the window/program that displays text and sends your keystrokes somewhere.
|
||||
- The **shell** is the program actually interpreting what you type as commands — `bash` is the
|
||||
shell this Lab uses. The terminal is just the display; the shell is the brain.
|
||||
|
||||
## Command structure
|
||||
|
||||
Every command follows roughly the same shape:
|
||||
|
||||
```
|
||||
command [options] [arguments]
|
||||
```
|
||||
|
||||
`ls -la /var/log` — `ls` is the command, `-la` are options (flags) that change its behavior,
|
||||
`/var/log` is the argument (what to act on). Options can usually be combined (`-l -a` = `-la`).
|
||||
|
||||
## Exit codes: how a command reports success or failure
|
||||
|
||||
Every command returns a number when it finishes: `0` means success, anything else (1-255) means
|
||||
some kind of failure. This isn't a Linux quirk — it's the exact mechanism scripts, CI pipelines,
|
||||
and (much later) Kubernetes health checks all rely on to know whether something worked. You can
|
||||
read the last command's exit code with the special variable `$?`.
|
||||
|
||||
## Getting help without leaving the terminal
|
||||
|
||||
- **Tab completion** — press Tab while typing a command or path; the shell completes it for you
|
||||
or shows you the options. This is a genuine time-saver, not just a convenience.
|
||||
- **Command history** — press the Up arrow to recall previous commands; `history` lists them.
|
||||
- **`man <command>`** — the manual page for almost any command, with every option explained.
|
||||
`man ls`, `man grep`, `man chmod` are all real, useful references — reaching for `man` before
|
||||
a search engine is a habit worth building now.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Establish shell fluency vocabulary (shell vs. terminal, exit codes, options vs.
|
||||
arguments) that every later module assumes without re-explaining.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live with a short live demo of tab
|
||||
completion and `man`.
|
||||
|
||||
**Common misconception to watch for:** students often think "Linux" and "the terminal" are the
|
||||
same thing — clarify the kernel/distribution/shell/terminal layering explicitly.
|
||||
|
||||
**Demo idea:** live-type a command that fails (e.g. `ls /nonexistent`), then run `echo $?`
|
||||
immediately after to show the exit code in real time — more convincing than describing it.
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Demonstration: Watching Exit Codes in Real Time
|
||||
|
||||
## OBSERVE
|
||||
|
||||
Run a command you know will succeed, then immediately check its exit code:
|
||||
|
||||
```
|
||||
$ true
|
||||
$ echo $?
|
||||
0
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Now predict: what will the exit code be if the command fails instead?
|
||||
|
||||
## INSPECT
|
||||
|
||||
Run a command that fails, and check immediately:
|
||||
|
||||
```
|
||||
$ ls /this-does-not-exist
|
||||
ls: cannot access '/this-does-not-exist': No such file or directory
|
||||
$ echo $?
|
||||
2
|
||||
```
|
||||
|
||||
## CHANGE
|
||||
|
||||
Try a few other failing commands and note their codes: `grep nomatch /etc/hostname` (exit 1 —
|
||||
"no matches found," a different kind of failure than "file not found").
|
||||
|
||||
## VERIFY
|
||||
|
||||
Notice: exit codes aren't just "0 or 1" — different failures often get different non-zero
|
||||
codes. `0` always means success; the exact non-zero value's MEANING varies by command, but ANY
|
||||
non-zero value reliably means "something went wrong," which is the part every script actually
|
||||
relies on (`if command; then ... else ...`).
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Make the abstract "exit code" concept concrete by actually watching `$?` change
|
||||
in real time, immediately after each command — this is far more convincing live than described
|
||||
in prose.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** running `echo $?` after ANY other command in between (even
|
||||
`ls`) silently checks the WRONG command's exit code — emphasize "check it immediately."
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Knowledge Check: Linux and the Shell
|
||||
|
||||
---
|
||||
|
||||
**1. What's the difference between a shell and a terminal?**
|
||||
|
||||
*Answer:* The terminal is the window/program that displays text and captures keystrokes; the
|
||||
shell (e.g. `bash`) is the program actually interpreting what you type as commands. The terminal
|
||||
is the display, the shell is the brain.
|
||||
|
||||
---
|
||||
|
||||
**2. A command exits with code `0`. What does that tell you?**
|
||||
|
||||
*Answer:* It succeeded. `0` is the universal "success" convention every script and pipeline
|
||||
relies on; any non-zero value means some kind of failure occurred.
|
||||
|
||||
---
|
||||
|
||||
**3. You've typed half a long path and can't remember the rest. What should you press?**
|
||||
|
||||
*Answer:* Tab — tab completion finishes the path (or shows the options) without you needing to
|
||||
type or remember it exactly.
|
||||
|
||||
---
|
||||
|
||||
**4. Where would you look to learn every option a command supports, without leaving the
|
||||
terminal?**
|
||||
|
||||
*Answer:* Its manual page — `man <command>`.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 1 is the one students most often blur together — worth confirming
|
||||
verbally before moving on.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Guided Exercise: Predict the Exit Code Before You Run It
|
||||
|
||||
This is a paper exercise — commit to an answer before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each command below, predict whether its exit code will be `0` (success) or non-zero
|
||||
(failure), and why:
|
||||
|
||||
1. `cat /etc/hostname` (a file that exists)
|
||||
2. `cat /etc/does-not-exist-at-all` (a file that doesn't)
|
||||
3. `grep root /etc/passwd` (a pattern that's very likely present)
|
||||
4. `grep zzz-not-a-real-user /etc/passwd` (a pattern that's very likely absent)
|
||||
5. `mkdir /tmp/already-exists && mkdir /tmp/already-exists` (running the same `mkdir` twice)
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. `0` — the file exists and is readable.
|
||||
2. Non-zero — the file doesn't exist, `cat` fails.
|
||||
3. `0` — `grep` succeeds (exit 0) when it finds at least one match.
|
||||
4. Non-zero (specifically `1`) — `grep` "fails" when it finds NO matches, even though nothing
|
||||
went technically wrong. This is a common surprise: `grep`'s exit code means "did I find a
|
||||
match," not "did an error occur."
|
||||
5. The SECOND `mkdir` fails (non-zero) — the directory already exists. The first one succeeds.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 4 is the one worth pausing on — many students assume "no error message"
|
||||
means "exit code 0," and `grep`'s no-match convention is a good early counter-example.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Reflection: Exit Codes Are Everywhere After This
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, did you know commands returned a machine-readable success/failure
|
||||
signal at all? What did you assume happened instead?
|
||||
2. Where do you think exit codes might matter later in ROOT_ — in a script, a CI pipeline, or
|
||||
a container?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Every automated system you'll ever build or operate checks exit codes, not error messages.**
|
||||
A script deciding whether to continue, a CI pipeline deciding whether to deploy, and (much
|
||||
later) a container orchestrator deciding whether a process is healthy, all reduce to the exact
|
||||
same question this module taught you to answer by hand: did this exit `0` or not? Getting
|
||||
comfortable checking `$?` now means none of those later systems will feel like magic.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2 previews Docker/Kubernetes health
|
||||
checks well before students get there — useful to name explicitly without going deep yet.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Navigating the Filesystem
|
||||
|
||||
## Everything is one tree, starting at `/`
|
||||
|
||||
Linux has exactly one filesystem tree, rooted at `/` (pronounced "root," unrelated to your
|
||||
username). Every file and directory, no matter what physical disk it's actually on, appears
|
||||
somewhere under this one tree — there's no separate "C:\" and "D:\" the way Windows shows
|
||||
multiple drives.
|
||||
|
||||
## Absolute vs. relative paths
|
||||
|
||||
An **absolute path** starts with `/` and always means the same location no matter where you
|
||||
currently are: `/home/student/notes.txt`. A **relative path** is interpreted relative to your
|
||||
CURRENT directory: `notes.txt` means something different depending on where you're standing
|
||||
when you type it. `.` means "this directory," `..` means "one directory up" — `cd ..` moves up
|
||||
one level, `cd ../..` moves up two.
|
||||
|
||||
## Key commands
|
||||
|
||||
- `pwd` — print your current directory (where am I?)
|
||||
- `ls` — list what's in a directory (`ls -la` shows hidden files and details)
|
||||
- `cd <path>` — change directory (absolute or relative)
|
||||
|
||||
## Hidden files
|
||||
|
||||
A file or directory starting with `.` (like `.bashrc` or `.config`) is "hidden" — `ls` skips it
|
||||
by default, `ls -a` shows it. This isn't a security feature, just a convention to keep
|
||||
configuration/metadata files out of the way of normal directory listings.
|
||||
|
||||
## The important top-level directories (conceptually, not memorized)
|
||||
|
||||
You don't need to memorize the entire Filesystem Hierarchy Standard, but recognizing these by
|
||||
purpose will save you real confusion later:
|
||||
|
||||
- **`/etc`** — system-wide configuration files (nginx's config lives at `/etc/nginx/nginx.conf`)
|
||||
- **`/var`** — variable data that changes while the system runs: logs (`/var/log`), among others
|
||||
- **`/home`** — personal directories for regular users
|
||||
- **`/tmp`** — temporary files, safe to assume anything here can disappear on reboot
|
||||
- **`/usr`** — most installed programs and their supporting files live somewhere under here
|
||||
- **`/proc`** — not real files on disk at all — a live, kernel-generated view into running
|
||||
processes and system state (you'll use this again in Module 5)
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Give students a working mental model of "one tree, absolute vs. relative
|
||||
paths" plus just enough top-level-directory recognition to not be lost later — not FHS
|
||||
memorization.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** students coming from Windows sometimes expect separate
|
||||
drives/roots — emphasize the single unified tree explicitly.
|
||||
|
||||
**Demo idea:** `cd /` then `ls`, narrating what each top-level directory is for as you see it,
|
||||
rather than listing them in the abstract.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Demonstration: The Same Word, Two Different Destinations
|
||||
|
||||
## OBSERVE
|
||||
|
||||
Starting from `/home/student`, run `cd project` — you land at `/home/student/project`.
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Now, from `/home/student/project`, what do you predict `cd project` will do this time?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ pwd
|
||||
/home/student/project
|
||||
$ cd project
|
||||
bash: cd: project: No such file or directory
|
||||
```
|
||||
|
||||
It fails — there's no `project` directory INSIDE `project` (unless you happened to create one).
|
||||
The exact same relative path meant something completely different depending on where you stood.
|
||||
|
||||
## CHANGE
|
||||
|
||||
From `/home/student/project`, run `cd ../project` instead — this correctly goes back up one
|
||||
level, then into `project` again, landing you right back where you started.
|
||||
|
||||
## VERIFY
|
||||
|
||||
`pwd` after each `cd` to confirm exactly where you ended up — this habit (verify your location
|
||||
before assuming it) prevents a huge share of "why did my command run somewhere unexpected"
|
||||
confusion later.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Make "relative paths depend on where you are" concrete and slightly surprising,
|
||||
rather than an abstract rule to memorize.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** students assume a relative path always means the SAME thing
|
||||
once they've used it once — reinforce that it's re-evaluated fresh every time, from wherever
|
||||
they currently are.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Knowledge Check: Navigating the Filesystem
|
||||
|
||||
---
|
||||
|
||||
**1. What does `..` mean in a path?**
|
||||
|
||||
*Answer:* One directory up from your current location.
|
||||
|
||||
---
|
||||
|
||||
**2. You're at `/home/student/project`. Does `cd project` do the same thing here as it did at
|
||||
`/home/student`?**
|
||||
|
||||
*Answer:* No — a relative path is re-evaluated from your CURRENT location every time. At
|
||||
`/home/student`, `project` refers to `/home/student/project`; from inside that same directory,
|
||||
`project` would refer to `/home/student/project/project`, which likely doesn't exist.
|
||||
|
||||
---
|
||||
|
||||
**3. A file is "hidden" from a normal `ls`. What actually makes it hidden?**
|
||||
|
||||
*Answer:* Its name starts with a `.` — it's purely a naming convention `ls` respects by
|
||||
default, not a real permission or security feature. `ls -a` shows it.
|
||||
|
||||
---
|
||||
|
||||
**4. Where would you expect to find a running system's log files?**
|
||||
|
||||
*Answer:* `/var/log` — `/var` holds data that changes while the system runs.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 2 is the one worth confirming out loud — it's the core "gotcha" of
|
||||
relative paths.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Guided Exercise: Plan a Directory Structure on Paper First
|
||||
|
||||
Before you build anything in the Lab, plan it — this is the same discipline you'll use later
|
||||
when planning a Kubernetes namespace layout or a Git repository structure.
|
||||
|
||||
## Your task
|
||||
|
||||
You're setting up a small project called `weather-app`. On paper, sketch a directory tree
|
||||
containing:
|
||||
|
||||
- A place for source code
|
||||
- A place for documentation
|
||||
- A place for tests
|
||||
- A single hidden configuration file at the project's root
|
||||
|
||||
Write out the exact `mkdir` and file-creation commands you'd use, including whether each path
|
||||
you write is absolute or relative.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
```
|
||||
mkdir -p weather-app/src weather-app/docs weather-app/tests
|
||||
echo "ready" > weather-app/.config
|
||||
```
|
||||
|
||||
(Or with an absolute path: `mkdir -p /srv/weather-app/src ...`.) The `-p` flag matters here —
|
||||
without it, `mkdir` refuses to create `weather-app/src` if `weather-app` doesn't already exist.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Have students commit to their command in writing before revealing the worked
|
||||
answer — the value is in noticing what `-p` actually buys them, not just copying a correct
|
||||
command.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
|
||||
**Common mistake to watch for:** forgetting `mkdir -p` and getting a "No such file or
|
||||
directory" error when creating a nested path in one command — a good moment to connect back to
|
||||
Module 2's exit-code lesson (that error IS a non-zero exit code).
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Reflection: Structure Isn't Bureaucracy, It's Communication
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Why might a real engineering team insist on a consistent directory structure across every
|
||||
project, instead of everyone organizing files however they like?
|
||||
2. Which of `/etc`, `/var`, `/home`, `/tmp`, `/usr`, `/proc` do you feel most confident you could
|
||||
explain to someone else right now? Which one still feels fuzzy?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**A predictable filesystem layout is what lets tools (and other engineers) reason about a
|
||||
system without asking you first.** `/etc` always means configuration, `/var/log` always means
|
||||
logs — this convention is exactly why a monitoring tool can be pointed at "check `/var/log`" on
|
||||
any Linux machine and just work. Container images later inherit this same convention (an
|
||||
nginx container still keeps its config at `/etc/nginx/nginx.conf`) — you're not learning a
|
||||
one-off Linux fact, you're learning a convention that outlives this specific module.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 connects directly to why Kubernetes
|
||||
namespaces and Git repository conventions matter later — a good moment to name that connection
|
||||
without going deep yet.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
# Files and Text
|
||||
|
||||
## Basic file operations
|
||||
|
||||
- `touch <file>` — create an empty file (or update its timestamp if it exists)
|
||||
- `mkdir <dir>` — create a directory (`-p` creates any missing parent directories too)
|
||||
- `cp <src> <dst>` — copy a file
|
||||
- `mv <src> <dst>` — move OR rename a file (Linux treats renaming as "moving to a new name in
|
||||
the same place")
|
||||
- `rm <file>` — delete a file (`-r` for a directory, `-f` to skip confirmation — dangerous
|
||||
combined: `rm -rf` deletes without asking, permanently)
|
||||
|
||||
## Reading text
|
||||
|
||||
- `cat <file>` — print the whole file at once
|
||||
- `less <file>` — page through a file interactively (press `q` to quit) — the right tool for
|
||||
anything too long to read in one screen
|
||||
- `head <file>` / `tail <file>` — the first/last 10 lines by default (`-n 20` for a different
|
||||
count); `tail -f` follows a file live as new lines are appended — exactly how you'd watch a
|
||||
log file update in real time
|
||||
|
||||
## Searching text: `grep`
|
||||
|
||||
`grep PATTERN file` prints only the lines matching PATTERN. This is one of the most-used
|
||||
commands in real operations work — "find the lines that matter" beats "read everything."
|
||||
|
||||
## Pipes and redirection: composing commands
|
||||
|
||||
- **A pipe (`|`)** sends one command's output as the next command's input:
|
||||
`cat app.log | grep ERROR` — read the file, then filter it.
|
||||
- **Redirection (`>`)** sends a command's output into a file instead of the screen (overwriting
|
||||
it): `grep ERROR app.log > errors.txt`. `>>` appends instead of overwriting.
|
||||
|
||||
## stdout vs. stderr
|
||||
|
||||
A command actually has TWO separate output streams: **stdout** (normal output) and **stderr**
|
||||
(error messages), even though both print to your screen by default. This is why
|
||||
`command 2>/dev/null` (redirecting stream `2`, stderr, to nowhere) can suppress error noise
|
||||
while still showing normal output — you'll use exactly this pattern in later Labs.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Build real fluency composing small commands together — this is the single
|
||||
highest-leverage Linux skill for troubleshooting work later in ROOT_.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** students often don't realize stdout/stderr are separate
|
||||
streams until they see `2>/dev/null` change what's suppressed — worth demonstrating live.
|
||||
|
||||
**Demo idea:** run a command that produces both real output and an error in the same call
|
||||
(e.g. `ls /etc /nonexistent`) and show how `2>/dev/null` changes what appears.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Demonstration: Filtering a Log Down to What Matters
|
||||
|
||||
## OBSERVE
|
||||
|
||||
A raw log file has five lines mixing `INFO` and `ERROR`:
|
||||
|
||||
```
|
||||
$ cat app.log
|
||||
INFO: boot ok
|
||||
ERROR: disk full
|
||||
INFO: retry
|
||||
ERROR: timeout
|
||||
INFO: done
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict what `grep ERROR app.log` will print, before running it.
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ grep ERROR app.log
|
||||
ERROR: disk full
|
||||
ERROR: timeout
|
||||
```
|
||||
|
||||
Only the matching lines — everything else is filtered out.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Now redirect that filtered result into a new file instead of just viewing it:
|
||||
|
||||
```
|
||||
$ grep ERROR app.log > errors.txt
|
||||
$ cat errors.txt
|
||||
ERROR: disk full
|
||||
ERROR: timeout
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
Count how many lines actually matched, without eyeballing them: `wc -l < errors.txt` (or
|
||||
`grep -c ERROR app.log` directly). This composition — filter, then count, then act — is the
|
||||
exact shape of a huge share of real troubleshooting work.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show the filter → redirect → verify chain as one continuous, composable
|
||||
workflow, not three unrelated commands.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** using `>` when they meant `>>` and accidentally overwriting a
|
||||
file they wanted to append to — worth calling out explicitly since it's a real, common mistake
|
||||
with no undo.
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Knowledge Check: Files and Text
|
||||
|
||||
---
|
||||
|
||||
**1. What's the difference between `>` and `>>`?**
|
||||
|
||||
*Answer:* `>` overwrites the target file's contents entirely; `>>` appends to the end of it,
|
||||
preserving what was already there.
|
||||
|
||||
---
|
||||
|
||||
**2. A command prints both normal output and an error message. What are these two streams
|
||||
called?**
|
||||
|
||||
*Answer:* stdout (normal output) and stderr (error messages) — two separate streams that both
|
||||
happen to print to your screen by default.
|
||||
|
||||
---
|
||||
|
||||
**3. You want to watch a log file update live, as new lines are appended, without re-running a
|
||||
command repeatedly.**
|
||||
|
||||
*Answer:* `tail -f <file>` — follows the file and prints new lines as they're written.
|
||||
|
||||
---
|
||||
|
||||
**4. `cat app.log | grep ERROR`. What does the `|` actually do?**
|
||||
|
||||
*Answer:* It pipes `cat`'s output directly into `grep`'s input, so `grep` filters the file's
|
||||
contents without needing a separate temporary file.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 2 is worth a live show-of-hands check — many students haven't
|
||||
consciously registered that stdout/stderr are separate until this module.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Guided Exercise: Which Tool Actually Answers the Question?
|
||||
|
||||
This is a paper exercise — commit to an answer before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each situation, name the ONE command (or short pipeline) you'd reach for first:
|
||||
|
||||
1. You want to see just the last 20 lines of a 50,000-line log file.
|
||||
2. You want to know how many lines in a file contain the word "timeout."
|
||||
3. You want to watch a log file update live while a process is running.
|
||||
4. You want to see a file's full contents but it's too long for one screen and you want to
|
||||
scroll through it interactively.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. `tail -n 20 file.log`
|
||||
2. `grep -c timeout file.log`
|
||||
3. `tail -f file.log`
|
||||
4. `less file.log`
|
||||
|
||||
Notice: `cat` would technically "work" for #4 too, but it dumps everything at once with no way
|
||||
to scroll back — `less` is the actual right tool once a file is longer than one screen.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 4 is worth discussing explicitly — "it works" isn't the same bar as
|
||||
"it's the right tool for this situation," and that distinction matters more as commands compose
|
||||
into real troubleshooting workflows.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Reflection: Filtering Is a Reasoning Skill, Not a Syntax Trick
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, how would you have found "the important lines" in a huge log file?
|
||||
How does that compare to `grep`?
|
||||
2. Which combination (pipe, redirect, or a specific command like `tail -f`) do you expect to
|
||||
use the most once you're doing real troubleshooting work?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**"Read everything and eyeball it" does not scale — filtering does.** Every troubleshooting
|
||||
framework you'll learn later in ROOT_ (starting with ROOT 02's own version, then again more
|
||||
formally in ROOT 10) depends on being able to quickly narrow a huge amount of system output down
|
||||
to the handful of lines that actually matter. `grep`, pipes, and redirection are the first real
|
||||
tools you have for that — not a syntax exercise, a reasoning skill.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 often produces good answers ("I'd
|
||||
just scroll and read") that make the value of filtering obvious by contrast — let students
|
||||
articulate this themselves rather than stating it for them.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Processes
|
||||
|
||||
## What a process actually is
|
||||
|
||||
A **process** is a running instance of a program — its own code, its own private memory, and a
|
||||
unique identifier the kernel tracks called a **PID** (Process ID). Run the same program twice
|
||||
(open two terminals both running `bash`), and you get two separate processes with two different
|
||||
PIDs, even though they're running identical code.
|
||||
|
||||
## Foreground vs. background
|
||||
|
||||
A command you run normally occupies your terminal until it finishes (**foreground**) — you
|
||||
can't type anything else until it's done. Append `&` to run it in the **background** instead,
|
||||
freeing your terminal immediately while the process keeps running. `sleep 300 &` starts a
|
||||
300-second sleep in the background and hands your prompt right back.
|
||||
|
||||
## Inspecting processes: `ps` and `top`
|
||||
|
||||
- `ps aux` — a snapshot of every process currently running, including its PID, how much CPU/
|
||||
memory it's using, and the exact command that started it.
|
||||
- `top` (or `htop`, if installed) — the same idea, but live-updating and sorted by resource
|
||||
usage, useful for "what's actually eating CPU/memory right now."
|
||||
|
||||
## Stopping a process: `kill` and signals
|
||||
|
||||
`kill <PID>` doesn't instantly destroy a process — it sends it a **signal**, a message the
|
||||
process can (usually) choose how to respond to. The default signal, `SIGTERM` (15), politely
|
||||
asks a process to shut down, giving it a chance to clean up first. `kill -9 <PID>` sends
|
||||
`SIGKILL`, which the kernel enforces immediately with no chance for the process to react —
|
||||
appropriate only when a process is truly stuck and ignoring `SIGTERM`. `kill -0 <PID>` is a
|
||||
special case: it sends NO real signal at all, just checks whether the PID still exists —
|
||||
exactly how you confirm "did that process actually stop" without side effects.
|
||||
|
||||
## Process hierarchy
|
||||
|
||||
Every process (except the very first one at boot) has a **parent** — the process that started
|
||||
it. Your shell is the parent of every command you run from it. This matters later: a parent
|
||||
process's own lifecycle can affect its children (this is part of why a container, which is
|
||||
often just one process tree, behaves the way it does when its main process exits).
|
||||
|
||||
## Why this matters for containers and Kubernetes
|
||||
|
||||
A container is, underneath, mostly an isolated process (or small process tree) with its own
|
||||
view of the filesystem and network — when people say "a container exited," they mean its main
|
||||
process exited, exactly the same event you'll cause on purpose in this module's Lab. Kubernetes'
|
||||
entire job, at its core, is deciding which machine a process (inside a container) runs on and
|
||||
restarting it if it dies unexpectedly — none of that will feel intuitive without this module's
|
||||
model of what a process actually is.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Build a real process mental model (PID, foreground/background, signals,
|
||||
parent/child) that ROOT 04 (containers) and ROOT 06 (Kubernetes workloads) both directly build
|
||||
on without re-explaining.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** students often think `kill` always means "forcefully
|
||||
destroy" — clarify that plain `kill` sends a polite, ignorable request (SIGTERM), and `-9` is
|
||||
the forceful escalation, not the default.
|
||||
|
||||
**Discussion question:** "What do you think happens to a container if its main process exits on
|
||||
its own, versus if something outside kills it?" (Don't need a complete answer yet — just plant
|
||||
the question for later.)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# Demonstration: Watching a Process Actually Terminate
|
||||
|
||||
## OBSERVE
|
||||
|
||||
Start a long-running background process and note its PID:
|
||||
|
||||
```
|
||||
$ sleep 300 &
|
||||
[1] 1421
|
||||
```
|
||||
|
||||
`1421` is the PID (yours will differ every time).
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: will `kill -0 1421` print anything if the process is still running?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ kill -0 1421 && echo "still running"
|
||||
still running
|
||||
```
|
||||
|
||||
`kill -0` sends no real signal — silence (or, here, our own `echo`) means the process exists.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Now actually terminate it:
|
||||
|
||||
```
|
||||
$ kill 1421
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ kill -0 1421 2>/dev/null && echo "still running" || echo "gone"
|
||||
gone
|
||||
```
|
||||
|
||||
The exact same check now reports the opposite result — real, observable proof the process is
|
||||
gone, not an assumption.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Make "checking before and after" a visible habit, not just narrated — students
|
||||
should see the SAME command return a different real answer once state actually changed.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** reaching for `kill -9` by default "to be safe" — reinforce that
|
||||
plain `kill` (SIGTERM) is the correct first attempt almost always.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Knowledge Check: Processes
|
||||
|
||||
---
|
||||
|
||||
**1. What does a PID identify?**
|
||||
|
||||
*Answer:* One specific running instance of a program — the kernel's unique identifier for that
|
||||
process, distinct from every other running process, even other copies of the same program.
|
||||
|
||||
---
|
||||
|
||||
**2. What's the actual difference between `kill <pid>` and `kill -9 <pid>`?**
|
||||
|
||||
*Answer:* Plain `kill` sends SIGTERM, a polite request the process can choose to handle (clean
|
||||
up, then exit). `kill -9` sends SIGKILL, which the kernel enforces immediately with no chance
|
||||
for the process to react — an escalation for when SIGTERM is ignored, not the default choice.
|
||||
|
||||
---
|
||||
|
||||
**3. You want to confirm a process is still running WITHOUT affecting it in any way. What do
|
||||
you use?**
|
||||
|
||||
*Answer:* `kill -0 <pid>` — sends no real signal, only checks whether the PID still exists.
|
||||
|
||||
---
|
||||
|
||||
**4. Why might understanding processes matter for understanding containers later?**
|
||||
|
||||
*Answer:* A container is, underneath, mostly an isolated process (or small process tree) — a
|
||||
container "exiting" is really its main process exiting, the exact same event this module's Lab
|
||||
causes on purpose.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 2 is worth confirming explicitly — "kill -9 by default" is a common,
|
||||
worth-correcting habit.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Guided Exercise: Choosing the Right Signal
|
||||
|
||||
This is a paper exercise — commit to an answer before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each situation, decide: plain `kill` (SIGTERM) first, or is `kill -9` (SIGKILL) actually
|
||||
justified?
|
||||
|
||||
1. A normal web server process you want to shut down for a routine restart.
|
||||
2. A process that received SIGTERM two minutes ago and is still running, showing no signs of
|
||||
responding.
|
||||
3. A script you just started by mistake, that hasn't done anything harmful yet.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. Plain `kill` — give it the chance to shut down cleanly (close connections, flush data, etc).
|
||||
2. `kill -9` is now justified — you already gave it the polite chance, and it didn't respond.
|
||||
3. Plain `kill` first, still — "just started" doesn't change the default; SIGKILL is a last
|
||||
resort, not a shortcut for convenience.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 3 sometimes trips students who reach for `-9` out of impatience — worth
|
||||
reinforcing that SIGTERM-first is the discipline REGARDLESS of urgency, since a process that
|
||||
ignores SIGTERM is the actual signal that escalation is warranted.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Reflection: You Just Stopped a Real Process, Not a Simulation
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before checking `kill -0` after your `kill`, how confident were you the process was
|
||||
actually gone? What would you have done if it hadn't stopped?
|
||||
2. Why do you think SIGTERM-before-SIGKILL matters more for a real production service than for
|
||||
a throwaway script in a Lab?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**"I ran the kill command" is not the same as "I confirmed the process is gone."** This module's
|
||||
Lab required you to verify state, not just take an action and assume it worked — that
|
||||
verification step is exactly what separates a confident operator from someone hoping for the
|
||||
best. You'll repeat this exact pattern (act, then verify) constantly for the rest of ROOT_,
|
||||
starting as soon as ROOT 04's containers and continuing all the way through Kubernetes
|
||||
troubleshooting.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2 is a good bridge to ROOT 06's later
|
||||
discussion of graceful shutdown/`terminationGracePeriodSeconds` — plant the idea now without
|
||||
naming Kubernetes specifics yet.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# Users and Permissions Foundations
|
||||
|
||||
## Users, groups, and ownership
|
||||
|
||||
Every file on Linux has an **owner** (a user) and a **group**. `ls -la` shows both: the third
|
||||
column is the owning user, the fourth is the owning group. Multiple users can belong to the same
|
||||
group, which is how permissions get shared across a team without giving every individual user
|
||||
direct ownership.
|
||||
|
||||
## Read, write, execute — for three different audiences
|
||||
|
||||
Every file has permissions for THREE separate audiences: the owner, the group, and everyone
|
||||
else ("other"). Each audience gets some combination of:
|
||||
|
||||
- **r**ead — can view the file's contents (or list a directory's contents)
|
||||
- **w**rite — can modify the file (or create/delete files inside a directory)
|
||||
- e**x**ecute — can run the file as a program (or enter/traverse into a directory)
|
||||
|
||||
`ls -la` shows this as a 10-character string like `-rw-r--r--`: the first character is the file
|
||||
type, then three groups of `rwx` for owner/group/other.
|
||||
|
||||
## Reading and setting permissions as octal numbers
|
||||
|
||||
Each `rwx` triplet can be written as one digit: read=4, write=2, execute=1, summed together.
|
||||
`rw-` = 4+2 = `6`. `r--` = `4`. So `644` means: owner can read+write (6), group can read-only
|
||||
(4), everyone else can read-only (4) — a very common, sensible default for a regular file.
|
||||
`chmod 644 <file>` sets exactly that.
|
||||
|
||||
## `sudo`: temporary elevated privilege
|
||||
|
||||
Rather than logging in AS the root user (with permanent full access to everything), `sudo
|
||||
<command>` runs just that one command with elevated privileges, after checking you're allowed
|
||||
to. This is deliberate: the less time you spend with full privileges, the smaller the window for
|
||||
a mistake (or a compromised session) to do serious damage — the exact same reasoning behind
|
||||
"least privilege," which this module introduces by name for the first time.
|
||||
|
||||
## Least privilege: the principle, not just the command
|
||||
|
||||
**Least privilege** means: grant exactly the access something needs to do its job, and nothing
|
||||
more. A user who only needs to read a file shouldn't also be able to write it. A process that
|
||||
only needs to read one directory shouldn't have write access to the whole filesystem. This
|
||||
principle isn't specific to Linux permissions — you'll meet it again, in a much more elaborate
|
||||
form, in ROOT 09's Kubernetes RBAC and SecurityContext content. This module is where it starts.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Build real fluency with rwx/octal permissions AND plant the least-privilege
|
||||
PRINCIPLE explicitly — not just command syntax, since ROOT 09 depends on students already
|
||||
having this vocabulary.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** students often memorize "644 = read/write" without being
|
||||
able to derive it from 4+2+1 — walk through the arithmetic explicitly rather than treating the
|
||||
numbers as magic.
|
||||
|
||||
**Discussion question:** "Why not just make everything readable and writable by everyone, to
|
||||
avoid permission headaches?" (Good setup for the least-privilege principle — let students
|
||||
generate the security argument themselves before you confirm it.)
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Demonstration: A Real Permission Denial, and the Real Fix
|
||||
|
||||
## OBSERVE
|
||||
|
||||
A file is owned by root with permissions `600` (owner read/write only):
|
||||
|
||||
```
|
||||
$ stat -c '%a %U' data.txt
|
||||
600 root
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: can a different, non-root user read this file?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ su - labuser -c "cat data.txt"
|
||||
cat: data.txt: Permission denied
|
||||
```
|
||||
|
||||
Confirmed — `600` grants nothing to group or other, so `labuser` (neither the owner nor root)
|
||||
is correctly denied.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Grant read access to everyone, while keeping write access limited to the owner:
|
||||
|
||||
```
|
||||
$ chmod 644 data.txt
|
||||
$ stat -c '%a' data.txt
|
||||
644
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ su - labuser -c "cat data.txt"
|
||||
sensitive
|
||||
```
|
||||
|
||||
The exact same command that failed before now succeeds — the permission bits, not the file's
|
||||
actual content, were the only thing that changed.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show a permission denial as a real, reproducible fact (not just a described
|
||||
error message) and the exact minimal fix, not an overcorrection like `chmod 777`.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** students reaching for `chmod 777` "to make the error go away"
|
||||
— explicitly name why that's wrong (it also grants WRITE to everyone, not just the read access
|
||||
actually needed) and connect it back to least privilege.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Knowledge Check: Users and Permissions Foundations
|
||||
|
||||
---
|
||||
|
||||
**1. A colleague says "permission denied" and fixes it with `chmod 777`. What's wrong with
|
||||
that fix?**
|
||||
|
||||
*Answer:* `777` grants read, write, AND execute to the owner, group, AND everyone else — almost
|
||||
always far more access than the actual problem required. It "fixes" the symptom while violating
|
||||
least privilege, usually granting write access nobody asked for.
|
||||
|
||||
---
|
||||
|
||||
**2. What does the permission `644` actually mean, broken down?**
|
||||
|
||||
*Answer:* Owner: read+write (4+2=6). Group: read-only (4). Other: read-only (4). Nobody but the
|
||||
owner can modify the file; everyone can read it.
|
||||
|
||||
---
|
||||
|
||||
**3. Why does `sudo <command>` exist instead of just logging in as root permanently?**
|
||||
|
||||
*Answer:* It grants elevated privilege only for the one command that needs it, minimizing the
|
||||
time spent with full access — reducing the chance a mistake (or a compromised session) causes
|
||||
serious, wide damage.
|
||||
|
||||
---
|
||||
|
||||
**4. State the least-privilege principle in your own words.**
|
||||
|
||||
*Answer:* Grant exactly the access something needs to do its job, and nothing more.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 1 is worth discussing as a group — most students have seen `chmod
|
||||
777` somewhere online as "the fix," and unlearning that habit early matters.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Guided Exercise: Pick the Minimal Permission, Not the Easy One
|
||||
|
||||
This is a paper exercise — commit to an answer before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each situation, write the exact `chmod` octal value that grants the MINIMUM permission
|
||||
actually needed:
|
||||
|
||||
1. A private SSH key file — only the owner should ever read or write it, nobody else, no
|
||||
execute for anyone.
|
||||
2. A shared script every user on the system should be able to run, but only the owner should be
|
||||
able to edit.
|
||||
3. A log file every user should be able to read (for troubleshooting), but only a logging
|
||||
service (the owner) should write to it.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. `600` — owner read+write (6), nothing for group or other (0, 0). This is the real, standard
|
||||
permission for private SSH keys — SSH itself refuses to use a key file that's more open than
|
||||
this.
|
||||
2. `755` — owner read+write+execute (7), group and other read+execute (5, 5) so they can run it
|
||||
but not modify it.
|
||||
3. `644` — owner read+write (6), group and other read-only (4, 4).
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 1 is a good moment to mention that this isn't a hypothetical rule — SSH
|
||||
genuinely refuses to use an overly-permissive private key file, a real-world consequence of
|
||||
exactly this principle.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
# Reflection: Least Privilege Is a Habit, Not a One-Time Fix
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, what would you have done the first time you hit a "permission denied"
|
||||
error? How does that compare to what you'd do now?
|
||||
2. Name one place outside of file permissions where you'd expect "grant only what's needed" to
|
||||
matter (a user account, an API key, a service's network access — anything).
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**Least privilege is the single idea from this entire module that reappears the most across
|
||||
the rest of ROOT_.** File permissions are just the first, most concrete place you'll apply it —
|
||||
ROOT 09 applies the exact same principle to Kubernetes RBAC (what can a user or workload
|
||||
actually DO) and SecurityContext (what can a container actually access). Getting comfortable
|
||||
asking "what's the minimum this actually needs" now, on a file you can see and touch, makes that
|
||||
same question much easier to ask later about something more abstract, like a ServiceAccount's
|
||||
permissions.
|
||||
|
||||
This module is complete. This is the end of ROOT 01 — continue to ROOT 02: Linux
|
||||
Administration & Networking.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2 works well as a round-robin — the
|
||||
variety of answers (API keys, database credentials, network firewall rules) is itself useful
|
||||
evidence of how far this one principle travels.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
|
||||
**Milestone note:** this is the last module of ROOT 01 — consider a short recap of the whole
|
||||
course's arc (hardware → shell → filesystem → files/text → processes → permissions) before
|
||||
moving on, so students see it as one coherent foundation rather than six separate topics.
|
||||
Reference in New Issue
Block a user