diff --git a/labs/linux-plus/1-1-1-boot-process/README.md b/labs/linux-plus/1-1-1-boot-process/README.md index 3324a70..5fcde12 100644 --- a/labs/linux-plus/1-1-1-boot-process/README.md +++ b/labs/linux-plus/1-1-1-boot-process/README.md @@ -20,7 +20,7 @@ were. run `systemctl list-units --state=running`, then `systemctl reboot`. Not graded: check yourself that only a handful of units were running. Root needs a password for rescue mode (`passwd root`); lock it again - afterwards with `passwd -l root`. + afterward with `passwd -l root`. 3. Make a lasting change to the kernel command line, then reboot. RHEL family: remove `quiet` from every installed kernel with grubby. Ubuntu: add `loglevel=7` to `GRUB_CMDLINE_LINUX_DEFAULT` and run `update-grub`. diff --git a/labs/linux-plus/1-5-1-shell-environment/README.md b/labs/linux-plus/1-5-1-shell-environment/README.md index d065cec..75b4664 100644 --- a/labs/linux-plus/1-5-1-shell-environment/README.md +++ b/labs/linux-plus/1-5-1-shell-environment/README.md @@ -28,5 +28,8 @@ login startup file is `~/.bash_profile`; on Ubuntu it's `~/.profile`. `chmod +x`. - `echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile` (or `~/.profile`), then `source` it; `which hi`. +- Both families already put `~/bin` on the PATH: AlmaLinux's default + `~/.bashrc` adds it, and Ubuntu's `~/.profile` adds it at login if it + exists. The `export` line is how you'd add any other directory. - bash runs only the first of `~/.bash_profile`, `~/.bash_login` and `~/.profile` that it finds for a login shell. diff --git a/labs/linux-plus/1-5-4-text-editors/README.md b/labs/linux-plus/1-5-4-text-editors/README.md index 7c3802a..8cecd7d 100644 --- a/labs/linux-plus/1-5-4-text-editors/README.md +++ b/labs/linux-plus/1-5-4-text-editors/README.md @@ -9,6 +9,15 @@ The lab removes `/tmp/s.conf` if it's there, and notes the startup files task 4 may change so that reset can put them back. Ubuntu empties `/tmp` at boot, so do task 2 without a reboot. +On the RHEL family, install full Vim and nano first. A minimal or cloud +install has only `vi`: + +``` +dnf install -y vim-enhanced nano +``` + +Reset removes them again if they weren't installed when the lab started. + ## Tasks 1. Run `vimtutor` and work through lessons 1 and 2 (about 15 minutes). Not @@ -25,8 +34,6 @@ boot, so do task 2 without a reboot. ## Hints -- On the RHEL family `vim` and `vimtutor` are in `vim-enhanced`, and nano - is in `nano`. - `:set number`, `/Port`, `yy`, `p`, `cw` or `C`, `:%s/yes/YES/g`, `u`, `:wq`. - In nano, `Ctrl+x` then `n` quits without saving. diff --git a/labs/linux-plus/1-5-4-text-editors/reset.sh b/labs/linux-plus/1-5-4-text-editors/reset.sh index 1cda51f..c2953e1 100644 --- a/labs/linux-plus/1-5-4-text-editors/reset.sh +++ b/labs/linux-plus/1-5-4-text-editors/reset.sh @@ -9,5 +9,8 @@ fi if [ -f "$LAB_STATE/absent" ]; then while read -r f; do case $f in *'*'*) ;; *) rm -rf "$f" ;; esac; done <"$LAB_STATE/absent" fi +if [ -s "$LAB_STATE/missing-pkgs" ]; then + dnf -y -q remove $(cat "$LAB_STATE/missing-pkgs") >/dev/null 2>&1 +fi command -v restorecon >/dev/null && restorecon -R /etc/environment /etc/profile.d /root /home 2>/dev/null true diff --git a/labs/linux-plus/1-5-4-text-editors/setup.sh b/labs/linux-plus/1-5-4-text-editors/setup.sh index 8a86751..89cc84e 100644 --- a/labs/linux-plus/1-5-4-text-editors/setup.sh +++ b/labs/linux-plus/1-5-4-text-editors/setup.sh @@ -11,3 +11,9 @@ for f in /etc/environment /etc/profile.d /root/.bashrc /root/.bash_profile /root printf '%s\n' "$f" >>"$LAB_STATE/absent" fi done +# The README has the student install full Vim and nano on the RHEL family; +# note which are missing, for reset to remove. +if is_rhel; then + for p in vim-enhanced nano; do pkg_installed $p || printf '%s\n' $p >>"$LAB_STATE/missing-pkgs"; done +fi +true diff --git a/labs/linux-plus/1-6-1-archives-and-compression/README.md b/labs/linux-plus/1-6-1-archives-and-compression/README.md index e417635..188445e 100644 --- a/labs/linux-plus/1-6-1-archives-and-compression/README.md +++ b/labs/linux-plus/1-6-1-archives-and-compression/README.md @@ -8,7 +8,8 @@ compressed log, and cpio. The lab clears away the files the tasks create in `/tmp`, if they're there. Ubuntu empties `/tmp` at boot, so do the tasks without a reboot, and run `le-lab check` before the chapter's clean-up step (`le-lab reset` cleans up -too). Tasks 2 and 3 ask for findings: write them to the files in +too, and removes `bzip2` again if it wasn't installed when the lab +started). Tasks 2 and 3 ask for findings: write them to the files in `/root/lab-answers/` that they name. ## Tasks @@ -16,10 +17,11 @@ too). Tasks 2 and 3 ask for findings: write them to the files in 1. Archive `/etc` into `/tmp/etc.tar.gz` with gzip, list it to find `sshd_config`, and restore just `etc/ssh/sshd_config` from it into `/tmp/restore`. -2. Archive `/etc` without compression into `/tmp/etc.tar`. Copy it to - `/tmp/t-gzip.tar`, `/tmp/t-bzip2.tar` and `/tmp/t-xz.tar`, and compress - each with the tool it's named after, keeping the copies. Write the name - of the tool that made the smallest file to `/root/lab-answers/2`. +2. Install `bzip2` if it's missing (it often is). Archive `/etc` without + compression into `/tmp/etc.tar`. Copy it to `/tmp/t-gzip.tar`, + `/tmp/t-bzip2.tar` and `/tmp/t-xz.tar`, and compress each with the tool + it's named after, keeping the copies. Write the name of the tool that + made the smallest file to `/root/lab-answers/2`. 3. Save the last 500 lines of the journal, compressed with gzip, as `/tmp/j.gz`. Without unpacking it, count the lines that mention `error` in any case, and write the count to `/root/lab-answers/3`. @@ -29,8 +31,8 @@ too). Tasks 2 and 3 ask for findings: write them to the files in ## Hints - `tar -czf`, `tar -tzf`, `tar -xzf ARCHIVE -C DIR MEMBER`. -- `gzip -k`, `bzip2 -k`, `xz -k`; `ls -lh /tmp/t-*`. bzip2 may need - installing. The tools skip a file whose name already ends in their own - suffix (`.gz`, `.bz2`, `.xz`). +- `dnf install bzip2` or `apt install bzip2`. `gzip -k`, `bzip2 -k`, + `xz -k`; `ls -lh /tmp/t-*`. The tools skip a file whose name already + ends in their own suffix (`.gz`, `.bz2`, `.xz`). - `journalctl -n 500 --no-pager | gzip > ...`; `zgrep -c -i`, `zless`. - `cd /etc && find ssh -type f | cpio -o > ...`; `cpio -idv < ...`. diff --git a/labs/linux-plus/1-6-1-archives-and-compression/reset.sh b/labs/linux-plus/1-6-1-archives-and-compression/reset.sh index 97cb578..7acadb7 100644 --- a/labs/linux-plus/1-6-1-archives-and-compression/reset.sh +++ b/labs/linux-plus/1-6-1-archives-and-compression/reset.sh @@ -1,2 +1,5 @@ rm -rf /tmp/etc.tar /tmp/etc.tar.gz /tmp/t-gzip.tar* /tmp/t-bzip2.tar* /tmp/t-xz.tar* /tmp/j.gz /tmp/ssh.cpio /tmp/restore /tmp/c +if [ -n "$(state_get no-bzip2)" ] && pkg_installed bzip2; then + if is_rhel; then dnf -y -q remove bzip2 >/dev/null 2>&1; else DEBIAN_FRONTEND=noninteractive apt-get -y -qq purge bzip2 >/dev/null 2>&1; fi +fi true diff --git a/labs/linux-plus/1-6-1-archives-and-compression/setup.sh b/labs/linux-plus/1-6-1-archives-and-compression/setup.sh index 05bb1c0..e08567e 100644 --- a/labs/linux-plus/1-6-1-archives-and-compression/setup.sh +++ b/labs/linux-plus/1-6-1-archives-and-compression/setup.sh @@ -1 +1,4 @@ rm -rf /tmp/etc.tar /tmp/etc.tar.gz /tmp/t-gzip.tar* /tmp/t-bzip2.tar* /tmp/t-xz.tar* /tmp/j.gz /tmp/ssh.cpio /tmp/restore /tmp/c +# Task 2 has the student install bzip2 if it's missing; reset removes it. +pkg_installed bzip2 || state_set no-bzip2 1 +true diff --git a/labs/linux-plus/2-4-4-web-and-mail-services/reset.sh b/labs/linux-plus/2-4-4-web-and-mail-services/reset.sh index 70f8fbd..13d1782 100644 --- a/labs/linux-plus/2-4-4-web-and-mail-services/reset.sh +++ b/labs/linux-plus/2-4-4-web-and-mail-services/reset.sh @@ -3,7 +3,7 @@ grep -ls 'lab\.test' /etc/nginx/conf.d/*.conf 2>/dev/null | xargs -r rm -f grep -ls 'shop\.test' /etc/httpd/conf.d/*.conf 2>/dev/null | xargs -r rm -f command -v semanage >/dev/null && semanage fcontext -d '/srv/www(/.*)?' 2>/dev/null rm -rf /srv/www -for p in nginx httpd postfix s-nail mailutils; do +for p in nginx httpd postfix s-nail mailutils policycoreutils-python-utils; do [ -n "$(state_get had-$p)" ] && continue pkg_installed $p || continue systemctl disable --now --quiet $p 2>/dev/null diff --git a/labs/linux-plus/2-4-4-web-and-mail-services/setup.sh b/labs/linux-plus/2-4-4-web-and-mail-services/setup.sh index 28a974e..0365779 100644 --- a/labs/linux-plus/2-4-4-web-and-mail-services/setup.sh +++ b/labs/linux-plus/2-4-4-web-and-mail-services/setup.sh @@ -1,3 +1,3 @@ [ ! -e /srv/www ] || refuse "/srv/www already exists; move it away first" -for p in nginx httpd postfix s-nail mailutils; do pkg_installed $p && state_set had-$p 1; done +for p in nginx httpd postfix s-nail mailutils policycoreutils-python-utils; do pkg_installed $p && state_set had-$p 1; done touch "$LAB_STATE/started" diff --git a/labs/lpic-1/102-4-debian-packages/README.md b/labs/lpic-1/102-4-debian-packages/README.md index 941ce49..9e05c0e 100644 --- a/labs/lpic-1/102-4-debian-packages/README.md +++ b/labs/lpic-1/102-4-debian-packages/README.md @@ -6,15 +6,16 @@ package. ## Before you start -The lab makes sure `cowsay` and `hello` aren't installed, and sets the -time zone to UTC if it's Europe/Berlin already (reset puts the old one -back). Write answers to the files in `/root/lab-answers/`. +The lab makes sure `mc` and `hello` aren't installed (reset puts `mc` +back if you had it), and sets the time zone to UTC if it's Europe/Berlin +already (reset puts the old one back). Write answers to the files in +`/root/lab-answers/`. ## Tasks -1. Install `cowsay` with `apt-get`, list its files with `dpkg -L`, check - its status with `dpkg -s`, then remove it and purge it, checking - `dpkg -l` after each step. +1. Install `mc` with `apt-get`, list its files with `dpkg -L`, check its + status with `dpkg -s`, then remove it and purge it, checking `dpkg -l` + after each step. 2. Find which package provides `/usr/bin/ls`, then try `/bin/ls` and see why it fails. Then use `apt-file` to find which package would provide the `ab` command, which you don't have yet. Write the package that owns @@ -27,11 +28,10 @@ back). Write answers to the files in `/root/lab-answers/`. ## Hints -- `dpkg -l cowsay` shows `ii` while it's installed. A package with - configuration files shows `rc` after `apt-get remove` and disappears - with `apt-get purge`; `cowsay` has none (`dpkg -s` lists no - `Conffiles:`), so it's gone after `remove` and `purge` has nothing left - to do. +- `dpkg -l mc` shows `ii` while it's installed. `mc` has configuration + files (`dpkg -s` lists them under `Conffiles:`), so it shows `rc` after + `apt-get remove`, and disappears with `apt-get purge`. A package without + any, such as `cowsay`, is gone after `remove`. - `dpkg -S`; `/bin` is a symlink to `usr/bin`, and the package lists the file only under `/usr/bin`. `apt-get install apt-file`, `apt-file update`, `apt-file search bin/ab`. diff --git a/labs/lpic-1/102-4-debian-packages/check.sh b/labs/lpic-1/102-4-debian-packages/check.sh index 0afae0e..fdeb820 100644 --- a/labs/lpic-1/102-4-debian-packages/check.sh +++ b/labs/lpic-1/102-4-debian-packages/check.sh @@ -4,12 +4,13 @@ dpkg_did() { cat /var/log/dpkg.log 2>/dev/null | awk -v s="$since" -v a="$1" -v p="$2" \ '($1 " " $2) >= s && $3 == a && ($4 == p || index($4, p ":") == 1) {ok = 1} END {exit !ok}' } -gone() { ! dpkg-query -W -f '${Status}' "$1" 2>/dev/null | grep -Eq 'installed|config-files'; } +gone() { ! dpkg-query -W -f '${Status}' "$1" 2>/dev/null | grep -Eq ' (installed|config-files)$'; } -task 1 "cowsay installed, then removed (and purged)" -need "dpkg's log shows cowsay installed" 'dpkg_did install cowsay' -need "dpkg's log shows cowsay removed" 'dpkg_did remove cowsay' -need "cowsay is gone, configuration files too (dpkg -l)" 'gone cowsay' +task 1 "mc installed, removed, then purged" +need "dpkg's log shows mc installed" 'dpkg_did install mc' +need "dpkg's log shows mc removed" 'dpkg_did remove mc' +need "dpkg's log shows mc purged" 'dpkg_did purge mc' +need "mc is gone, configuration files too (dpkg -l)" 'gone mc' task 2 "Owner of /usr/bin/ls and provider of ab found" need "line 1 of /root/lab-answers/2 is the package that owns /usr/bin/ls" '[ "$(sed -n 1p /root/lab-answers/2 | tr -d "[:space:]" | cut -d: -f1)" = "$(dpkg -S /usr/bin/ls | cut -d: -f1)" ]' diff --git a/labs/lpic-1/102-4-debian-packages/reset.sh b/labs/lpic-1/102-4-debian-packages/reset.sh index 9232b15..85437e0 100644 --- a/labs/lpic-1/102-4-debian-packages/reset.sh +++ b/labs/lpic-1/102-4-debian-packages/reset.sh @@ -1,7 +1,12 @@ -for p in cowsay hello; do - dpkg-query -W -f '${Status}' "$p" 2>/dev/null | grep -Eq 'installed|config-files' && +for p in mc hello; do + dpkg-query -W -f '${Status}' "$p" 2>/dev/null | grep -Eq ' (installed|config-files)$' && DEBIAN_FRONTEND=noninteractive apt-get -y -qq purge "$p" >/dev/null 2>&1 done +if [ -n "$(state_get had-mc)" ]; then + DEBIAN_FRONTEND=noninteractive apt-get -y -qq install mc >/dev/null 2>&1 +elif [ -z "$(state_get had-mc-data)" ] && pkg_installed mc-data; then + DEBIAN_FRONTEND=noninteractive apt-get -y -qq purge mc-data >/dev/null 2>&1 +fi rm -f /root/hello_*.deb tz=$(state_get tz) if [ -n "$tz" ] && [ -e "/usr/share/zoneinfo/$tz" ]; then diff --git a/labs/lpic-1/102-4-debian-packages/setup.sh b/labs/lpic-1/102-4-debian-packages/setup.sh index 21a6dcb..71a2c11 100644 --- a/labs/lpic-1/102-4-debian-packages/setup.sh +++ b/labs/lpic-1/102-4-debian-packages/setup.sh @@ -1,5 +1,8 @@ -for p in cowsay hello; do - pkg_installed "$p" && DEBIAN_FRONTEND=noninteractive apt-get -y -qq purge "$p" >/dev/null +pkg_installed mc && state_set had-mc 1 +pkg_installed mc-data && state_set had-mc-data 1 +for p in mc hello; do + dpkg-query -W -f '${Status}' "$p" 2>/dev/null | grep -Eq ' (installed|config-files)$' && + DEBIAN_FRONTEND=noninteractive apt-get -y -qq purge "$p" >/dev/null done state_set tz "$(timedatectl show -p Timezone --value)" [ "$(state_get tz)" = Europe/Berlin ] && timedatectl set-timezone Etc/UTC diff --git a/labs/lpic-1/102-5-rpm-and-yum/README.md b/labs/lpic-1/102-5-rpm-and-yum/README.md index 3538005..904a9c4 100644 --- a/labs/lpic-1/102-5-rpm-and-yum/README.md +++ b/labs/lpic-1/102-5-rpm-and-yum/README.md @@ -30,5 +30,6 @@ in `/root/lab-answers/`. checksum, `S` the size, `T` the time), `c` marks a configuration file. - `dnf download --destdir`, `rpm -qpi`, `rpm -qpl`; `rpm2cpio FILE.rpm | cpio -idv ./usr/bin/tree`, run inside `/root/rpms`. -- `dnf provides dig` downloads the file lists first, which can take a - minute. `dnf history`, `dnf history info last`. +- On RHEL 10 and its relatives, the first `dnf provides` downloads the + repositories' file lists, which can take several minutes on a slow + mirror. `dnf history`, `dnf history info last`. diff --git a/labs/lpic-1/104-1-partitions-and-filesystems/README.md b/labs/lpic-1/104-1-partitions-and-filesystems/README.md index a05b3ce..29d560c 100644 --- a/labs/lpic-1/104-1-partitions-and-filesystems/README.md +++ b/labs/lpic-1/104-1-partitions-and-filesystems/README.md @@ -8,8 +8,9 @@ partition, wiping a disk, and Btrfs subvolumes and snapshots. The lab adds two empty 4 GiB spare disks, `/dev/loop40` and `/dev/loop41` (the chapter's `/dev/sdb` and `/dev/sdc`). Their partitions show up as `/dev/loop40p1` and so on. It installs the Btrfs tools if they're missing. -Swap you turn on by hand is off again after a reboot, so check before you -reboot. +Task 5 needs Btrfs, which AlmaLinux 10 and Ubuntu have but RHEL and Rocky +Linux leave out, so the lab won't start there. Swap you turn on by hand +is off again after a reboot, so check before you reboot. ## Tasks diff --git a/labs/lpic-1/104-1-partitions-and-filesystems/setup.sh b/labs/lpic-1/104-1-partitions-and-filesystems/setup.sh index 8e4be3d..9a454f8 100644 --- a/labs/lpic-1/104-1-partitions-and-filesystems/setup.sh +++ b/labs/lpic-1/104-1-partitions-and-filesystems/setup.sh @@ -1,4 +1,7 @@ -command -v mkfs.btrfs >/dev/null || pkg_install btrfs-progs -modprobe btrfs 2>/dev/null || refuse "this kernel has no Btrfs support, which task 5 needs." +# Task 5 needs Btrfs. RHEL and Rocky Linux have neither the module nor +# btrfs-progs; AlmaLinux 10, Debian and Ubuntu have both. +command -v mkfs.btrfs >/dev/null || pkg_install btrfs-progs || + refuse "there's no btrfs-progs package here (RHEL and Rocky Linux leave Btrfs out), and task 5 needs it; use AlmaLinux or Ubuntu." +modprobe btrfs 2>/dev/null || refuse "this kernel has no Btrfs support, which task 5 needs (RHEL and Rocky Linux leave it out; use AlmaLinux or Ubuntu)." disk_add 0 4G >/dev/null disk_add 1 4G >/dev/null diff --git a/labs/lpic-1/104-6-links/README.md b/labs/lpic-1/104-6-links/README.md index 8fb8a8e..fb80fa1 100644 --- a/labs/lpic-1/104-6-links/README.md +++ b/labs/lpic-1/104-6-links/README.md @@ -15,8 +15,9 @@ that directory, and write answers to the files in `/root/lab-answers/`. symlink `soft.txt` to it, and look at all three with `ls -li`. Delete the original, then `cat` both links. 2. Try to hard-link a file onto another filesystem (`df` shows which - directories are on separate ones), and to hard-link a directory. Write - both error messages to `/root/lab-answers/2`. + directories are on separate ones; `/dev/shm` is a tmpfs everywhere, + while `/tmp` is only on some systems), and to hard-link a directory. + Write both error messages to `/root/lab-answers/2`. 3. Make a directory `parent`, check its link count, add two subdirectories, and check again. Write the first count on line 1 of `/root/lab-answers/3` and the second on line 2. @@ -32,8 +33,7 @@ that directory, and write answers to the files in `/root/lab-answers/`. - `ln`, `ln -s`, `ls -li`; a symlink stores a name, a hard link is another name for the same inode. -- `/tmp` is on the root filesystem on many systems; `/dev/shm` and `/run` - are tmpfs. `ln FILE /dev/shm/`; `ln DIR NEWNAME`. +- `ln FILE /dev/shm/`; `ln DIR NEWNAME`. - `ls -ld parent` (the second column), `stat -c %h parent`. - Without `-n`, `ln -sf 1.1 current` follows `current` into `1.0` and makes the link there. diff --git a/labs/lpic-1/108-4-printing/README.md b/labs/lpic-1/108-4-printing/README.md index 8a7455b..e90581a 100644 --- a/labs/lpic-1/108-4-printing/README.md +++ b/labs/lpic-1/108-4-printing/README.md @@ -1,20 +1,26 @@ # LPIC-1 108.4: Printers and printing -CUPS with a PDF printer: printing, holding and cancelling jobs, and debug +CUPS with a PDF printer: printing, holding and canceling jobs, and debug logging. ## Before you start The lab creates user `alice` (password `Lab-Pass-1`) and installs -`ipptool`, which the check uses to read CUPS's job history. CUPS itself isn't installed yet: that's task 1. Reset removes CUPS -and the PDF printer again. +`ipptool`, which the check uses to read CUPS's job history. CUPS itself +isn't installed yet: that's task 1. Reset removes CUPS and the PDF printer +again. ## Tasks -1. Install `cups` and a PDF printer (`printer-driver-cups-pdf` on Debian and - Ubuntu; `cups-pdf`, from EPEL, on the RHEL family), and find the printer - with `lpstat -p`. As `alice`, print a text file to it; find the PDF it - makes, and write the PDF's full path to `/root/lab-answers/1`. +1. Install `cups` and a PDF printer, and find it with `lpstat -p`. On + Debian and Ubuntu, install `printer-driver-cups-pdf` and `cups-bsd`; the + first adds a printer named `PDF` by itself. On the RHEL family, + `cups-pdf` comes from EPEL (`dnf install epel-release` first) and adds + no printer, so add one: + `lpadmin -p PDF -E -v cups-pdf:/ -m CUPS-PDF_opt.ppd`. As `alice`, print + a text file to it (a job printed by root makes no PDF on the RHEL + family); find the PDF it makes, and write the PDF's full path to + `/root/lab-answers/1`. 2. Disable the printer with a reason, print two files, list them with `lpq` and `lpstat -o`, remove one with `lprm` and the other with `cancel`, then enable it again. Also reject jobs on the printer and try to print; read @@ -26,10 +32,9 @@ and the PDF printer again. - `lpstat -p -v`; `lp -d PRINTER file`. The PDF lands in `~/PDF` on Debian and Ubuntu; on the RHEL family it goes to `~/Desktop`, or the home - directory when there's no Desktop. Root's own jobs may fail there. If - `lpstat -p` shows no printer, add one: `lpinfo -m | grep -i pdf`, then - `lpadmin -p PDF -E -v cups-pdf:/ -m ...`. + directory when there's no Desktop. `lpinfo -m | grep -i pdf` lists the + PDF printer's driver. - `cupsdisable -r "reason"`, `cupsenable`, `cupsreject -r`, `cupsaccept`; - `lpstat -p -l`. On Ubuntu, `lpq` and `lprm` are in the `cups-bsd` - package. + `lpstat -p -l`. On Debian and Ubuntu, `lpq` and `lprm` are in the + `cups-bsd` package. - `cupsctl --debug-logging`, `cupsctl`; `/var/log/cups/error_log`. diff --git a/labs/lpic-1/109-2-network-configuration/README.md b/labs/lpic-1/109-2-network-configuration/README.md index ff4f2a4..b1cd8b3 100644 --- a/labs/lpic-1/109-2-network-configuration/README.md +++ b/labs/lpic-1/109-2-network-configuration/README.md @@ -1,28 +1,33 @@ # LPIC-1 109.2: Persistent network configuration -NetworkManager's devices, profiles and their files, a static address that -lasts, the host name, and `/etc/hosts`. +NetworkManager's devices, profiles and their files (netplan's on Ubuntu), a +static address that lasts, the host name, and `/etc/hosts`. ## Before you start The peer, at `10.99.0.2` (name `peer`), stands in for another machine. -On the RHEL family, the lab also adds a second network for task 2, so your -own connection to the VM is never touched: interface `le-lan`, with the -NetworkManager profile `lab-net`, which gets an address by DHCP from a -server at `10.98.0.1` on the far side. Tasks 1 and 2 use NetworkManager and -are graded on the RHEL family only; Ubuntu servers use netplan instead. +The lab also adds a second network for task 2, so your own connection to +the VM is never touched: interface `le-lan`, with a machine at `10.98.0.1` +on the far side. On the RHEL family it has the NetworkManager profile +`lab-net`, which gets an address by DHCP from that machine. Ubuntu Server +uses netplan with networkd and doesn't install NetworkManager, so there +`le-lan` starts with no address, and you do tasks 1 and 2 with netplan. ## Tasks -1. RHEL family: read `nmcli device` and `nmcli connection show`, and find the - file of the main interface's active connection with - `nmcli -f NAME,FILENAME connection show`. Read it with `sudo`, and write - its path to `/root/lab-answers/1`. -2. RHEL family: give profile `lab-net` the static address `10.98.0.10/24` - with `nmcli connection modify`, bring the connection up, check with - `ip addr` and `ip route`, and reboot to show it lasts. (The chapter then - returns it to DHCP: do that after you've run the check.) +1. Find the configuration file of the main interface, and write its path + to `/root/lab-answers/1`. RHEL family: read `nmcli device` and + `nmcli connection show`, find the file of the active connection with + `nmcli -f NAME,FILENAME connection show`, and read it with `sudo`. + Ubuntu: read the files in `/etc/netplan/` and `networkctl status`, and + find the netplan file that sets up the main interface. +2. Give `le-lan` the static address `10.98.0.10/24`, check with `ip addr` + and `ip route`, and reboot to show it lasts. RHEL family: on profile + `lab-net`, with `nmcli connection modify`, then bring the connection up. + (The chapter then returns it to DHCP: do that after you've run the + check.) Ubuntu: in a new netplan file, `/etc/netplan/90-lab.yaml`, + applied with `netplan try`. 3. Change the host name to `lab2.example.net` with `hostnamectl`, and check `/etc/hostname`, the prompt in a new shell, and `hostname -f`. 4. Add the name `files.lab.example` for the peer's address, `10.99.0.2`, to @@ -30,8 +35,12 @@ are graded on the RHEL family only; Ubuntu servers use netplan instead. ## Hints -- `nmcli -f NAME,DEVICE,FILENAME connection show --active`. +- `nmcli -f NAME,DEVICE,FILENAME connection show --active`. On Ubuntu, + `ls /etc/netplan`, `networkctl list`, `networkctl status`. - `nmcli connection modify lab-net ipv4.method manual ipv4.addresses ...`, then `nmcli connection up lab-net`; `ip -br addr show le-lan`. +- In netplan, an `ethernets:` entry for `le-lan` with + `addresses: [10.98.0.10/24]`; `chmod 600` the file; `netplan try` (press + Enter to keep it). - `hostnamectl set-hostname`, `hostnamectl status`. - One line in `/etc/hosts`: the address, then the names; `getent hosts`. diff --git a/labs/lpic-1/109-2-network-configuration/check.sh b/labs/lpic-1/109-2-network-configuration/check.sh index 27ca8fd..8db84ae 100644 --- a/labs/lpic-1/109-2-network-configuration/check.sh +++ b/labs/lpic-1/109-2-network-configuration/check.sh @@ -1,15 +1,32 @@ +IF=$(ip -4 route show default | awk '{for (i = 1; i < NF; i++) if ($i == "dev") { print $(i + 1); exit }}') if is_rhel; then - IF=$(ip -4 route show default | awk '{for (i = 1; i < NF; i++) if ($i == "dev") { print $(i + 1); exit }}') MAINFILE=$(nmcli -t -f DEVICE,FILENAME connection show --active 2>/dev/null | awk -F: -v d="$IF" '$1 == d {print $2; exit}') prof() { nmcli -g "$1" connection show lab-net 2>/dev/null | tr -d '\\'; } fi +# The netplan file in answer 1 sets up the main interface (by name or MAC). +netplan_main_ok() { + local f mac + f=$(head -n1 /root/lab-answers/1 2>/dev/null) + case $f in /etc/netplan/*.yaml | /run/netplan/*.yaml | /lib/netplan/*.yaml | /usr/lib/netplan/*.yaml) ;; *) return 1 ;; esac + [ -f "$f" ] && [ -n "$IF" ] || return 1 + mac=$(cat "/sys/class/net/$IF/address" 2>/dev/null) + grep -qwF "$IF" "$f" || { [ -n "$mac" ] && grep -qiF "$mac" "$f"; } +} -task 1 "The main connection's file in /root/lab-answers/1" rhel -need "the answer is the file of $IF's active connection" '[ -n "$MAINFILE" ] && [ "$(head -n1 /root/lab-answers/1)" = "$MAINFILE" ]' +task 1 "The main interface's configuration file in /root/lab-answers/1" +if is_rhel; then + need "the answer is the file of $IF's active connection" '[ -n "$MAINFILE" ] && [ "$(head -n1 /root/lab-answers/1)" = "$MAINFILE" ]' +else + need "the answer is the netplan file that sets up $IF" netplan_main_ok +fi -task 2 "lab-net has the static address 10.98.0.10/24, persistently" rhel -need "lab-net's IPv4 method is manual" '[ "$(prof ipv4.method)" = manual ]' -need "lab-net has 10.98.0.10/24" 'prof ipv4.addresses | tr "," "\n" | tr -d " " | grep -qx 10.98.0.10/24' +task 2 "le-lan has the static address 10.98.0.10/24, persistently" +if is_rhel; then + need "lab-net's IPv4 method is manual" '[ "$(prof ipv4.method)" = manual ]' + need "lab-net has 10.98.0.10/24" 'prof ipv4.addresses | tr "," "\n" | tr -d " " | grep -qx 10.98.0.10/24' +else + need "a file in /etc/netplan/ gives le-lan 10.98.0.10/24" 'grep -qs le-lan /etc/netplan/*.yaml && netplan get ethernets.le-lan.addresses 2>/dev/null | grep -qF 10.98.0.10/24' +fi need "le-lan has 10.98.0.10/24, not from DHCP" 'ip -o -4 addr show dev le-lan | grep -F " 10.98.0.10/24 " | grep -vq dynamic' need "the route to 10.98.0.0/24 goes out le-lan" 'ip route get 10.98.0.1 | grep -q "dev le-lan"' need "10.98.0.1 answers ping" 'ping -c1 -W2 10.98.0.1' diff --git a/labs/lpic-1/109-2-network-configuration/reset.sh b/labs/lpic-1/109-2-network-configuration/reset.sh index 2f5f0d6..edc2a71 100644 --- a/labs/lpic-1/109-2-network-configuration/reset.sh +++ b/labs/lpic-1/109-2-network-configuration/reset.sh @@ -2,16 +2,25 @@ if is_rhel; then for c in $(nmcli -g UUID,DEVICE,NAME connection show 2>/dev/null | awk -F: '$2 == "le-lan" || $3 == "lab-net" {print $1}'); do nmcli connection delete "$c" >/dev/null 2>&1 done - systemctl disable --now --quiet le-lab-dhcp.service le-lab-lan.service 2>/dev/null - rm -f /etc/systemd/system/le-lab-dhcp.service /etc/systemd/system/le-lab-lan.service - systemctl daemon-reload - ip link del le-lan 2>/dev/null + systemctl disable --now --quiet le-lab-dhcp.service 2>/dev/null + rm -f /etc/systemd/system/le-lab-dhcp.service /var/lib/dnsmasq/le-lab.leases + [ -n "$(state_get added-dnsmasq)" ] && dnf -y -q remove dnsmasq >/dev/null 2>&1 +elif [ -f "$LAB_STATE/netplan.tar" ]; then + rm -rf /etc/netplan + tar -C /etc -xpf "$LAB_STATE/netplan.tar" + netplan generate 2>/dev/null + networkctl reload 2>/dev/null +fi +systemctl disable --now --quiet le-lab-lan.service 2>/dev/null +rm -f /etc/systemd/system/le-lab-lan.service +systemctl daemon-reload +ip link del le-lan 2>/dev/null +if is_rhel; then rm -f /etc/NetworkManager/conf.d/92-le-lab-lan.conf systemctl reload NetworkManager 2>/dev/null - rm -f /usr/local/lib/le-lab/lan-net.sh /var/lib/dnsmasq/le-lab.leases - rmdir /usr/local/lib/le-lab 2>/dev/null - [ -n "$(state_get added-dnsmasq)" ] && dnf -y -q remove dnsmasq >/dev/null 2>&1 fi +rm -f /usr/local/lib/le-lab/lan-net.sh +rmdir /usr/local/lib/le-lab 2>/dev/null old=$(state_get hostname) [ -n "$old" ] && [ "$(hostname)" != "$old" ] && hostnamectl set-hostname "$old" sed -i -E '/^[^#]*[[:space:]]files\.lab\.example([[:space:]]|$)/d' /etc/hosts diff --git a/labs/lpic-1/109-2-network-configuration/setup.sh b/labs/lpic-1/109-2-network-configuration/setup.sh index dc0aa86..09a3bd6 100644 --- a/labs/lpic-1/109-2-network-configuration/setup.sh +++ b/labs/lpic-1/109-2-network-configuration/setup.sh @@ -1,15 +1,22 @@ +if is_rhel; then + command -v nmcli >/dev/null && systemctl is-active --quiet NetworkManager || + refuse "NetworkManager isn't running; this lab uses it" +else + command -v netplan >/dev/null && systemctl is-active --quiet systemd-networkd || + refuse "this lab needs netplan with systemd-networkd" + grep -qs le-lan /etc/netplan/*.yaml && refuse "a file in /etc/netplan already mentions le-lan; remove it first" +fi +ip link show le-lan >/dev/null 2>&1 && refuse "an interface le-lan already exists; remove it first" + state_set hostname "$(hostname)" [ "$(hostname)" != lab2.example.net ] || hostnamectl set-hostname lab-vm sed -i -E '/^[^#]*[[:space:]]files\.lab\.example([[:space:]]|$)/d' /etc/hosts peer_up -if is_rhel; then - command -v nmcli >/dev/null && systemctl is-active --quiet NetworkManager || - refuse "NetworkManager isn't running; this lab uses it" - pkg_installed dnsmasq || { pkg_install dnsmasq; state_set added-dnsmasq 1; } - # le-lan, linked to the peer, where a DHCP server answers on 10.98.0.1. - mkdir -p /usr/local/lib/le-lab - cat >/usr/local/lib/le-lab/lan-net.sh <<'X' +# le-lan, linked to the peer, where 10.98.0.1 answers (and, on the RHEL +# family, a DHCP server). +mkdir -p /usr/local/lib/le-lab +cat >/usr/local/lib/le-lab/lan-net.sh <<'X' #!/bin/sh ip link show le-lan >/dev/null 2>&1 || ip link add le-lan type veth peer name le-lanp netns lepeer ip -n lepeer addr replace 10.98.0.1/24 dev le-lanp @@ -18,8 +25,8 @@ ip link set le-lan up command -v nmcli >/dev/null && nmcli device set le-lan managed yes 2>/dev/null true X - chmod 755 /usr/local/lib/le-lab/lan-net.sh - cat >/etc/systemd/system/le-lab-lan.service <<'X' +chmod 755 /usr/local/lib/le-lab/lan-net.sh +cat >/etc/systemd/system/le-lab-lan.service <<'X' [Unit] Description=Practice lab: the le-lan network Requires=le-lab-peer.service @@ -33,6 +40,9 @@ ExecStart=/usr/local/lib/le-lab/lan-net.sh [Install] WantedBy=multi-user.target X + +if is_rhel; then + pkg_installed dnsmasq || { pkg_install dnsmasq; state_set added-dnsmasq 1; } cat >/etc/systemd/system/le-lab-dhcp.service <<'X' [Unit] Description=Practice lab DHCP server at 10.98.0.1, on the peer @@ -56,5 +66,10 @@ X nmcli connection add type ethernet ifname le-lan con-name lab-net \ ipv4.method auto ipv6.method disabled >/dev/null timeout 60 nmcli connection up lab-net >/dev/null 2>&1 || warn "lab-net didn't get an address yet; try: nmcli connection up lab-net" +else + # For reset to put back. + tar -C /etc -cf "$LAB_STATE/netplan.tar" netplan + systemctl daemon-reload + systemctl enable --now --quiet le-lab-lan.service fi true diff --git a/labs/rhcsa/2-1-rpm-repositories-and-packages/README.md b/labs/rhcsa/2-1-rpm-repositories-and-packages/README.md index 89de51e..89a6328 100644 --- a/labs/rhcsa/2-1-rpm-repositories-and-packages/README.md +++ b/labs/rhcsa/2-1-rpm-repositories-and-packages/README.md @@ -26,7 +26,7 @@ with `sudo ip netns exec lepeer bash`. directory, and start `python3 -m http.server` there, bound to `10.99.0.2`. On this VM, configure that repository as `lab` in `/etc/yum.repos.d/lab.repo` with signature checking on, and install one - of its packages from it. + of its packages from it with `dnf install --repo lab`. 3. Find and install the package that provides the `semanage` command. 4. Remove a package you installed, then undo the removal from dnf's history. @@ -41,5 +41,7 @@ with `sudo ip netns exec lepeer bash`. The repo file needs `baseurl=http://10.99.0.2:8000/`, `gpgcheck=1` and a `gpgkey=` from `/etc/pki/rpm-gpg/`. `dnf repoinfo lab`; `dnf install --repo lab NAME` takes it from that repository only. -- `dnf provides semanage` (or `'*/semanage'`). +- `dnf provides semanage` (or `'*/semanage'`). The first `dnf provides` + downloads the file lists of every repository, which can take several + minutes. It isn't hung. - `dnf history list`, `dnf history info`, `dnf history undo`. diff --git a/labs/rhcsa/5-3-mounting-at-boot/README.md b/labs/rhcsa/5-3-mounting-at-boot/README.md index f9bd683..0934277 100644 --- a/labs/rhcsa/5-3-mounting-at-boot/README.md +++ b/labs/rhcsa/5-3-mounting-at-boot/README.md @@ -29,4 +29,7 @@ It also adds a line to `/etc/fstab` for a UUID that doesn't exist, at - `mkfs.ext4 -L backups`, or `e2label`; `LABEL=backups /backups ext4 defaults 0 2`. - `findmnt --verify` marks the bad line with `[E]`; run - `systemctl daemon-reload` after editing `/etc/fstab`. + `systemctl daemon-reload` after editing `/etc/fstab`. Run `findmnt + --verify` as root: as a normal user it can't read the devices and warns + `cannot detect on-disk filesystem type (Permission denied)` for every + line. diff --git a/labs/rhcsa/7-4-time-clients/README.md b/labs/rhcsa/7-4-time-clients/README.md index 658a730..1357e23 100644 --- a/labs/rhcsa/7-4-time-clients/README.md +++ b/labs/rhcsa/7-4-time-clients/README.md @@ -21,4 +21,7 @@ zone and chrony's configuration, and puts them back when you reset it. - `/etc/chrony.conf`: comment out the `pool` line, add a `server` line with `iburst`; `systemctl restart chronyd`; `chronyc sources`, `chronyc tracking`. +- If your DHCP server hands out NTP servers, they show up in + `chronyc sources` too: comment out the `sourcedir /run/chrony-dhcp` + line as well. - `timedatectl list-timezones`, `timedatectl set-timezone`, `timedatectl`. diff --git a/labs/rhcsa/8-1-ip-addresses/README.md b/labs/rhcsa/8-1-ip-addresses/README.md index ec60a5d..37bd0f0 100644 --- a/labs/rhcsa/8-1-ip-addresses/README.md +++ b/labs/rhcsa/8-1-ip-addresses/README.md @@ -23,8 +23,8 @@ service, `le-lab-watch`, that notes the profiles for `eth1` for the check. `fd00:50::10/64`, all persistent, in a profile made with `nmcli`. 2. Add a second IPv4 address, `192.168.50.11/24`, to the same profile without removing the first. -3. Delete that profile with `nmcli connection delete`, and do task 1 again - with `nmtui` on a fresh profile. +3. Delete that profile with `nmcli connection delete`, and do tasks 1 and + 2 again with `nmtui` on a fresh profile. ## Hints diff --git a/labs/rhcsa/8-1-ip-addresses/check.sh b/labs/rhcsa/8-1-ip-addresses/check.sh index 162e7a3..9085a2b 100644 --- a/labs/rhcsa/8-1-ip-addresses/check.sh +++ b/labs/rhcsa/8-1-ip-addresses/check.sh @@ -25,7 +25,9 @@ task1_needs task 2 "A second address, 192.168.50.11/24, on the same profile" need "a profile for eth1 had both 192.168.50.10/24 and 192.168.50.11/24" 'tr " " "\n" <"$LOG" | grep "192\.168\.50\.10/24" | grep -q "192\.168\.50\.11/24"' -task 3 "The profile deleted and made again with nmtui" +task 3 "The profile deleted and tasks 1 and 2 done again with nmtui" need "a second, fresh profile for eth1 was made" '[ "$(seen_uuids | wc -l)" -ge 2 ]' need "the first profile is gone" '! nmcli -g UUID connection show | grep -qx "$(seen_uuids | head -n1)"' task1_needs +need "the fresh profile has 192.168.50.11/24 too" 'prof ipv4.addresses | grep -q "192\.168\.50\.11/24"' +need "eth1 has 192.168.50.11/24 too" 'live 192.168.50.11/24'