Module 9 · Linux and Networking Basics
Shell scripts and cron
Turn commands into a reusable script with variables, conditions and exit codes, run it on a schedule with cron, and build a small health check that would have raised the alarm on 31 August.
About 25 minutes
The problem
Every check in this course so far was typed by hand, after the fact. The month-end outage, the full disk and the miner all left clear traces that a few lines of shell could have spotted automatically, minutes after they started.
Engineers automate checks like these with shell scripts, run on a schedule by cron (or by a monitoring system that calls the same kind of check). The attacker in lesson 6 used exactly the same tool to keep their miner running.
The concept
A script is a file of commands. The first line, #!/bin/bash, says which program runs it. Make it executable with chmod +x script.sh and run it with ./script.sh.
Building blocks
| Feature | Example |
|---|---|
| Variables | LOG=access.log then "$LOG" |
| Arguments | $1 is the first argument passed to the script |
| Command output | ERRORS=$(grep -c ' 504 ' "$LOG") |
| Conditions | if [ "$ERRORS" -gt 10 ]; then ... fi (-gt greater than, -lt less than) |
| Exit codes | exit 0 means OK, anything else means a problem; monitoring tools rely on them |
cron
A cron line has five time fields then the command:
# minute hour day-of-month month day-of-week command
*/5 * * * * /srv/scripts/health_check.sh /var/log/nginx/access.log
0 3 * * * /usr/sbin/logrotate /etc/logrotate.confThe first runs every 5 minutes; the second at 03:00 every day. crontab -l lists a user's jobs, and reviewing them is part of every security check (lesson 6's intruder added one).
Example
A health check: given a log and a time window, count server errors and slow requests, and exit with a warning code if either crosses a threshold. The cat > ... <<'EOF' line writes everything up to EOF into the file.
Shell (bash)
%%bash
cat > health_check.sh <<'EOF'
#!/bin/bash
# Usage: health_check.sh LOGFILE FROM TO (times as HH:MM)
LOG="$1"; FROM="$2"; TO="$3"
MAX_ERRORS=5
MAX_SLOW=10
read TOTAL ERRORS SLOW <<< "$(awk -v from="$FROM" -v to="$TO" '
{ t = substr($4, 14, 5) }
t >= from && t < to { n++; if ($9 >= 500) e++; if ($NF > 2) s++ }
END { print n + 0, e + 0, s + 0 }' "$LOG")"
echo "$FROM-$TO: $TOTAL requests, $ERRORS server errors, $SLOW slower than 2 s"
if [ "$ERRORS" -gt "$MAX_ERRORS" ] || [ "$SLOW" -gt "$MAX_SLOW" ]; then
echo "ALERT: thresholds exceeded"
exit 1
fi
echo "OK"
EOF
chmod +x health_check.sh
ls -l health_check.sh | cut -d' ' -f1-rwxr-xr-xRun it on three 5-minute windows: before the outage, the start of it, and after.
Shell (bash)
%%bash
curl -sO https://academy.cloudtechanalytics.com/datasets/linux/access.log
for window in "09:30 09:35" "09:40 09:45" "10:40 10:45"; do
./health_check.sh access.log $window
echo "exit code: $?"
done09:30-09:35: 44 requests, 0 server errors, 0 slower than 2 s
OK
exit code: 0
09:40-09:45: 37 requests, 17 server errors, 26 slower than 2 s
ALERT: thresholds exceeded
exit code: 1
10:40-10:45: 47 requests, 0 server errors, 0 slower than 2 s
OK
exit code: 0Run every five minutes by cron, this script would have alerted in the first window of the outage, at 09:45, and its exit code could page the on-call engineer automatically. Lesson 6's intruder used the same scheduler to restart their miner: listing all users' cron jobs is how you'd find theirs.
Walkthrough
- Run the cells. Change
MAX_ERRORSto 20. Does the alert still fire at 09:40? - Add a disk check to the script: read the
/varline ofdf.txtand alert above 90%. - Write the cron line that runs your script every 5 minutes, and one that runs it at 07:00 on the last day of each month only (hint: cron can't say "last day" directly; what could you do instead?).
- Write a second script that lists processes using more than 50% CPU from
ps.txt, not run bytallyb+,www-dataorroot.
Practice
Practice
How many server errors does the script report for the window 09:40 to 09:45?
Task
8 minWrite a short script check_miner.sh that reads ps.txt, prints any process using more than 50% CPU whose user is not tallyb+, www-data or root, and exits with 1 if it finds one (0 otherwise).
Your work is checked for
- Starts with #!/bin/bash
- Reads ps.txt
- Compares CPU with 50
- Excludes the expected users
- Exits with 1 when found and 0 otherwise
Check your understanding
Answer every question to check.