Skip to content

Exam 102-500 cheatsheet

A condensed command reference for exam 102-500 (topics 105 to 110), the notes I made for myself right before the exam. It is deliberately terse: commands, flags, and gotchas, no long explanations. For the full write-ups, see the Exam 102-500 pages.


105.1 Customize and use the shell environment

set -b # -b: tell me at once when a background job ends
set -e # -e (errexit): stop the script at the first failing command

env -u LANG my_command # -u (unset): run my_command without the LANG variable
env -i bash            # -i (ignore): start bash with an empty environment
printenv               # show all environment variables
printenv USER          # show one variable

friend=naruto     # create a local variable
set | grep friend # show the variable
unset friend      # remove it

export friend=nagato # exported: child processes see it too

source config.sh # run config.sh in the current shell
. config.sh      # same as source

alias                         # list all aliases
alias testping="ping 8.8.8.8" # create an alias

funnyls () {
    ls -ltrh # -l (long) -t (time sort) -r (reverse) -h (human sizes)
    echo "This is a funny ls"
}
funnyls      # call the function
# LOGIN SHELL (login with user + pass: SSH, console, su -)
# 1. /etc/profile                      system-wide
# 2. /etc/profile.d/*.sh               run by a line in /etc/profile
# 3. ONE of these (first found wins, then stops):
#      ~/.bash_profile
#      ~/.bash_login
#      ~/.profile
# 4. ~/.bashrc                         only if step 3 sources it

# NON-LOGIN INTERACTIVE SHELL (terminal in GUI, or typing bash)
# 1. /etc/bash.bashrc                  (or /etc/bashrc on some distros)
# 2. ~/.bashrc

# NON-LOGIN NON-INTERACTIVE (bash script.sh)
# reads NO startup files, only $BASH_ENV if set

# LOGOUT
# ~/.bash_logout                       runs when login shell exits

105.2 Customize or write simple scripts

cd /tmp; ls; pwd # ; run one after the other, always

cd /tmp && ls # && run ls only if cd worked
FILES=$(ls)   # put the output of ls in a variable
FILES=`ls`    # same, old style (backticks)

exec ping 8.8.8.8 # replace the shell with ping. when ping ends, the shell is gone

test -s filename  # -s (size): file exist && size > 0
test -d /tmp      # -d (directory): is a directory
test -x script.sh # -x (execute): file is executable

read name age # read 2 words into 2 variables
echo $name:$age

read -t 3 -p "entrez votre nom: " nom # -t (timeout) 3 sec | -p (prompt) text
echo $nom

expr 5 + 3  # shows 8
expr 5 \* 4 # shows 20

let result=10+3 # result = 13
let "x = 5 * 4" # x = 20

mail -s "subject" root                            # -s (subject). press Ctrl+D to send
echo "the backup failed" | mail -s "subject" root # body from a pipe

106.1 The Linux Graphical Stack

The graphical stack, from top (user) to bottom (hardware):

                    User
             (You, Nagato, Yoda, ...)
                  /        \
                 /          \
      Desktop Manager    Window Manager
    (GNOME, KDE, ...)    (OpenBox, i3, dwm, awesome, ...)
                 \          /
                  \        /
                Display Server
            (Xorg (X11), Wayland, ...)
                     |
                   Kernel
               (Linux, BSD, ...)
                     |
                  Hardware
          (amd64, ARM, PowerPC, ...)
Xorg -configure       # old command to generate config, nowadays X11 autodetects hardware
/etc/X11/xorg.conf    # main X config file
/etc/X11/xorg.conf.d/ # X config snippets
~/.xsession-errors    # X errors of your session
# VESA = generic fallback graphics driver, works on any card but basic only (no acceleration)


xhost                # show who may connect to your X server
xhost +              # + : allow every host (insecure)
xhost -              # - : only hosts in the list
xhost +192.168.45.28 # allow one host

xauth list # show X auth cookies


# X (X11) = the windowing system / protocol
#   │
#   ├── XFree86   old implementation (dead since 2004)
#   └── Xorg      current implementation (forked from XFree86)

# Wayland = modern replacement for X (not X, a new system)

# TIMELINE
# XFree86 ──forked──> Xorg ──being replaced by──> Wayland

106.2 Graphical desktops

# DESKTOP ENVIRONMENTS (full bundle: window manager + panels + apps)
#   GNOME    default on Ubuntu/Fedora, uses Mutter
#   KDE      feature-rich, uses KWin
#   XFCE     lightweight, good for old hardware

# DISPLAY MANAGER (the graphical login screen you see at boot)
#   can also offer remote graphical login with XDMCP
#   GDM (GNOME), SDDM (KDE), XDM (general) handle theming and login

# REMOTE CONNECTION TO GUIs
#
#   XDMCP    X Display Manager Control Protocol
#            Remote graphical login, native to X11.
#            Legacy/Obsolete: requires high bandwidth, highly insecure.
#
#   Spice    Simple Protocol for Independent Computing Environment
#            Fully open-source (released by Red Hat post-2008 acquisition).
#            Primary use case: connecting directly to KVM virtual machines.
#            High performance: near-local speed, low CPU overhead.
#
#   RDP      Remote Desktop Protocol
#            Microsoft native, but open to Linux via 'Xrdp' server listeners.
#            Default port 3389. Encrypted by default.
#            Excellent bandwidth compression and native multi-monitor support.
#
#   VNC      Virtual Network Computing
#            Cross-platform standard using the RFB (Remote Frame Buffer) protocol.
#            Default port 5900 + display offset (e.g., :1 = port 5901).
#            Highly compatible, but modern flavors (TigerVNC) require TLS setup for security.
#
#   Waypipe  Wayland Network Proxying
#            The modern successor to X11 forwarding over SSH ('ssh -X').
#            Efficiently pipes hardware-accelerated Wayland windows over network connections.

106.3 Accessibility

# KEYBOARD (all provided by AccessX, part of the X keyboard extension)
# command line tool: xkbset
#   Sticky Keys   - press modifier then key separately (Shift then A -> A)
#                   activation gesture: press Shift 5 times
#   Slow Keys     - key registers only if held down (gesture: hold Shift 8 sec)
#   Bounce Keys   - ignores same key pressed twice too fast (helps hand tremors)
#   Toggle Keys   - sound when Caps/Num Lock toggled
#   Mouse Keys    - numpad controls the mouse pointer

# MOUSE ASSIST
#   simulate right-click by holding left button
#   simulate click by hovering (holding pointer still)

# VISUAL (GNOME "Seeing" section)
#   High Contrast     - sharper colors for windows/buttons
#   Large Text        - bigger font
#   Cursor Size       - bigger mouse cursor
#   Screen Magnifier  - zoom part of screen (GNOME "Zoom", KDE "KMagnifier")

# SCREEN READER (text-to-speech)
#   Orca      - most popular, installed by default, GNOME project
#   Emacspeak - command-line option
#   works with Braille Display (hardware, raises pins to show braille)

# ON-SCREEN KEYBOARD
#   GNOME has it built in; other desktops can install "onboard" package

107.1 Manage user and group accounts

# KEY FILES
    # /etc/passwd       user accounts
    # /etc/shadow       password hashes + aging information
    # /etc/group        group definitions
    # /etc/skel/        template files for new users
    # /etc/login.defs   default user/group settings

# /etc/passwd FIELDS
    # name:x:UID:GID:comment:/home/dir:/bin/bash
    # x = password hash stored in /etc/shadow

# CREATE USERS
    adduser bob                         # high-level, interactive (Debian/Ubuntu)

    useradd bob                         # low-level, non-interactive
    useradd -m bob                      # -m (make home): create home directory
    useradd -d /tmp/bob bob             # -d (directory): home directory
    useradd -m -s /bin/bash bob         # -s (shell): set login shell
    useradd -c "comment!!" bob          # -c (comment): comment

# MODIFY / DELETE USERS
    usermod -g devs bob  # -g (lowercase): change bob's PRIMARY group to devs
    usermod -G devs bob  # -G (uppercase): set bob's SECONDARY groups (REPLACES all)
    usermod -aG devs bob # -a (append) + -G: ADD to secondary groups, keeps existing

    usermod -L bob                      # -L (Lock): lock password
    usermod -U bob                      # -U (Unlock): unlock password

    userdel bob                         # delete user
    userdel -r bob                      # -r (remove): delete user + home directory

# GROUPS
    addgroup devs                       # high-level Debian/Ubuntu tool

    groupadd -g 1200 devs               # -g (GID): specify GID
    groupadd devs                       # create group

    groupmod -n new old                 # -n (new name): rename group

    groupdel devs                       # delete group

    gpasswd devs                        # set a password for the group
    gpasswd -a bob devs                 # -a (add): add user to group

# PASSWORDS
    passwd bob                          # set/change password
    passwd -l bob                       # -l (lock): lock password
    passwd -u bob                       # -u (unlock): unlock password

# PASSWORD AGING
    chage -l bob            # -l (list): show current aging info (read-only)
    chage -M 90 bob         # -M (max): password must change after N days
    chage -m 7 bob          # -m (min): must wait N days before changing again
    chage -W 7 bob          # -W (warn): warn user N days before expiry
    chage -I 14 bob         # -I (inactive): lock account N days after password expires
    chage -E 2026-12-31 bob # -E (expire): account expiry DATE (or -1 = never)
    chage -d 0 bob          # -d (date): last change date. 0 = force change at next login
    chage bob               # interactive mode, asks each value one by one

# LOOKUP
    getent passwd bob                   # query user database
    getent group devs                   # query group database

    id bob                              # show UID, GID, and groups
# /etc/shadow
# Format:
# username:password:lastchange:min:max:warn:inactive:expire:reserved

# FIELDS
# 1. username     → account name
# 2. password     → password hash / lock marker
# 3. lastchange   → days since 1970 when password was last changed
# 4. min          → minimum days before password can be changed
# 5. max          → maximum password age in days
# 6. warn         → days before expiry to warn user
# 7. inactive     → days after password expires before account is disabled
# 8. expire       → account expiration date (days since 1970)
# 9. reserved     → reserved

# PASSWORD FIELD
# $6$...   → password hash
# *        → password authentication disabled
# !        → password locked
# empty    → no password

# EXAMPLE
# bob:$6$abc...:19800:0:90:7:14:19890:

107.2 Automate system administration tasks

# THREE WAYS TO SCHEDULE
#   cron          = repeating jobs on a schedule
#   at            = one-time job at a specific time
#   systemd timer = the systemd alternative to cron

# ===== CRON =====

# crontab (user's own scheduled jobs)
crontab -e        # -e (edit): edit your crontab
crontab -l        # -l (list): show your crontab
crontab -r        # -r (remove): delete your crontab
crontab -u bob -e # -u (user): edit another user's crontab (root)

# CRONTAB TIME FORMAT (5 fields + command)
#   min  hour  day-of-month  month  day-of-week  command
#   *    *     *             *      *            /path/script
#   ┌── minute (0-59)
#   │ ┌── hour (0-23)
#   │ │ ┌── day of month (1-31)
#   │ │ │ ┌── month (1-12)
#   │ │ │ │ ┌── day of week (0-7, 0 and 7 = Sunday)
#   * * * * * command
#
# examples:
#   30 2 * * *       every day at 2:30
#   0 * * * *        every hour (on the hour)
#   */15 * * * *     every 15 minutes
#   0 9 * * 1        every Monday at 9:00

# SPECIAL SHORTCUTS (replace the 5 fields)
#   @reboot @hourly @daily @weekly @monthly @yearly

# SYSTEM CRON LOCATIONS
#   /etc/crontab               system-wide crontab (has extra USER field)
#   /etc/cron.d/               drop-in system cron files
#   /etc/cron.hourly/          scripts run hourly
#   /etc/cron.daily/           scripts run daily
#   /etc/cron.weekly/          scripts run weekly
#   /etc/cron.monthly/         scripts run monthly
#   /var/spool/cron/           where user crontabs are stored
# NOTE: system crontab (/etc/crontab) has 7 fields (adds a USER before command)

# WHO CAN USE CRON
#   /etc/cron.allow    if exists, ONLY listed users can use cron
#   /etc/cron.deny     listed users are BLOCKED
#   (allow takes priority; if neither exists, usually all allowed)

# ===== AT (one-time jobs) =====
at 5pm              # schedule a job for 5pm (then type commands, Ctrl+D)
at now + 2 hours    # run 2 hours from now
at 10:00 tomorrow   # specific time
atq                 # list pending at jobs (q = queue)
atrm 3              # remove at job number 3 (rm = remove)
at -f script.sh 5pm # -f (file): run a script file at a time

# WHO CAN USE AT
#   /etc/at.allow   /etc/at.deny   (same logic as cron.allow/cron.deny)

# ===== SYSTEMD TIMERS (modern alternative) =====
systemctl list-timers                                      # show active timers
systemd-run --on-active=10m mycommand                      # run once, 10 min from now
systemd-run --on-calendar="20:00" /usr/bin/touch /tmp/test # run at 20:00
# timer units use OnCalendar= for schedules (like cron)

systemd-run --user --on-active=2m /bin/bash -c 'echo "systemd timerll!" > /home/amranich/test/timer' # --user: as your user, in 2 min
systemctl --user list-timers                                                                         # --user: show your own timers

# Once you have created the new timer, you can enable it and start it by running the following commands as root:
systemctl enable foobar.timer # start the timer at boot
systemctl start foobar.timer  # start the timer now

107.3 Localisation and internationalisation

# ===== TIMEZONE =====
# timezone = your time difference from a reference (UTC)
# servers/cloud often use UTC to avoid confusion

date        # show current date/time
cal         # show calendar
timedatectl # show time, timezone, UTC, sync status (systemd)

# tzselect - interactive, asks location, OUTPUTS the TZ name (doesn't set it)
tzselect

# TZ variable - set YOUR OWN timezone (not the system's)
TZ='America/New_York'; export TZ # put in ~/.profile for a personal timezone

# CONFIGURING SYSTEM TIMEZONE
# /etc/localtime  - the file Linux reads for system time
#                   symlink OR copy of a zoneinfo file
ln -s /usr/share/zoneinfo/America/New_York /etc/localtime # link method (-s = symbolic)
cp /usr/share/zoneinfo/America/New_York /etc/localtime    # copy method

# /etc/timezone   - holds timezone NAME (Debian based)
# /etc/sysconfig/clock - same, on RHEL based
# /usr/share/zoneinfo/ - database of all timezone files

timedatectl set-timezone Europe/Amsterdam # systemd way
dpkg-reconfigure tzdata                   # Debian interactive menu

# ===== LANGUAGES / LOCALE =====
# environment variables tell the system which language/format to use
# LANG=en_US.UTF-8  = English, US variant, UTF-8 encoding

locale    # show current locale settings
locale -a # -a (all): list installed locales

dpkg-reconfigure locales # Debian interactive menu for locales

# LC_* variables (each controls one category)
# LANG        default for everything (fallback)
# LC_ALL      OVERRIDES all (highest priority)
# LC_TIME     time format (e.g. en_GB.UTF-8 = British time format)
# LC_NUMERIC, LC_MONETARY, LC_MESSAGES, LC_CTYPE ...
# priority:  LC_ALL > LC_* > LANG

# LANG=C - plain defaults, English, predictable. used in SCRIPTS

# Good script (uses LANG=C)
export LANG=C
date | grep "Thu Sep" # works the same on every machine, because output is always English

localectl # systemd: show/set locale and keyboard layout

# /etc/timezone       → stores system timezone
# /etc/default/locale → stores system locale

# ===== ENCODING =====
# ASCII      basic English, 7-bit
# ISO-8859   (Latin-1) western european, 8-bit
# UTF-8      Unicode, all languages (modern default)
# Unicode    the standard; UTF-8 is an encoding of it

iconv -f ISO-8859-1 -t UTF-8 file.txt # convert encoding (-f from, -t to)


iconv -f UTF-8 -t ASCII//TRANSLIT test.txt > ascii.txt # TRANSLIT: replace special letters (é -> e)

108.1 Maintain system time

# System clock = kernel, runs while ON.   Hardware clock (RTC = Real Time Clock) = battery, runs while OFF.
# Keep hardware clock in UTC (Coordinated Universal Time). Local time = UTC + timezone.
#   --systohc : system -> hardware      --hctosys : hardware -> system

# ===== DISPLAY =====
date         # local time
date -u      # -u (UTC): Coordinated Universal Time
date +%s     # Unix time (seconds since 1970 epoch, overflows 2038 on 32-bit)
sudo hwclock # hardware clock (needs root)
timedatectl  # local + UTC + RTC + timezone + NTP (Network Time Protocol) sync status

# ===== SET (systemd way) =====
timedatectl set-time '2011-11-25 14:00:00' # date+time (or HH:MM:SS)
timedatectl set-timezone Africa/Cairo      # exact name, case matters
timedatectl list-timezones                 # grep this, it is long
timedatectl set-ntp true                   # network sync on/off

# ===== SET (legacy) =====
date -s "11 Nov 2011 11:11:11"   ; hwclock --systohc          # -s (set) system, push to hardware
hwclock --set --date "4/12/2019 11:15:19" ; hwclock --hctosys # set hardware, pull to system

# ===== TIMEZONE FILES =====
# /usr/share/zoneinfo/   all zone files
# /etc/localtime         file Linux reads (symlink/copy of a zoneinfo file)
# /etc/timezone          zone NAME, Debian only
ln -s /usr/share/zoneinfo/Canada/Eastern /etc/localtime # -s (symbolic): link localtime to the zone file

# ===== NTP (Network Time Protocol) =====
# Stratum: 0 = reference clocks, 1 = attached to them (private), 2 = public (pool.ntp.org).
# Offset=gap to NTP time | Step=big jump (>128ms) | Slew=slow fix (<128ms) | Insane=>17min, no change

# THREE WAYS TO SYNC (only run ONE at a time):
#   timesyncd = systemd built-in, light, syncs my clock only (cannot serve others)
#   ntpd      = full daemon, can also SERVE time to other machines
#   chrony    = modern alternative to ntpd
# timedatectl set-ntp controls ONLY timesyncd. If ntpd/chrony is installed:
#   -> set-ntp shows "NTP not supported" and timedatectl shows "NTP service: n/a"
#   -> this is normal. check "System clock synchronized: yes" instead.

# --- timesyncd (systemd built-in) ---
timedatectl set-ntp true           # turn timesyncd sync on/off
systemctl status systemd-timesyncd # is it running?

# --- ntpd (NTP daemon, full server, can also serve time) ---
# NOTE: on Debian/Ubuntu the package + service are named "ntp", the program is "ntpd"
systemctl enable ntpd && systemctl start ntpd # start at boot, and start now
# /etc/ntp.conf :  server 0.centos.pool.ntp.org iburst   (iburst = faster first sync)
# pool.ntp.org = free volunteer server pool, DNS (Domain Name System) gives a random one (spreads load)
# NTP is UDP (User Datagram Protocol) port 123
ntpdate pool.ntp.org                          # one-time manual sync (stop ntpd first; used when offset >17min)
ntpq -p                                       # ntpq (NTP query) -p (peers): show servers; * = server in use, -n (numeric) = show IPs

# --- chrony (modern alternative) ---
# chronyd = daemon,  chronyc = client (c = command line)
# config: /etc/chrony.conf (RHEL) | /etc/chrony/chrony.conf (Debian/Ubuntu). "!" = disabled line
chronyc tracking # how well synced (offset, stratum, drift)
chronyc sources  # which servers it uses
chronyc makestep # force an immediate step

108.2 System logging

# Logging = collect messages from the kernel, services and apps, and store them (usually /var/log).
# TWO systems:
#   rsyslog          = classic logging daemon, writes plain TEXT files in /var/log
#   systemd-journald = modern systemd logging, writes ONE BINARY journal (read with journalctl)
# They can run together. rsyslog can read from the journal.

# ============================================================
# LESSON 1 - rsyslog (classic)
# ============================================================

# LOGGING DAEMONS
#
#   syslog  (the original, now dead)
#      │
#      ├──> syslog-ng   (syslog new generation) ─┐
#      │                                          ├─ two SEPARATE competitors,
#      └──> rsyslog     (rocket-fast)  ──────────┘   both replaced old syslog
#
#   TODAY:
#     rsyslog     = most common (default on Debian, Ubuntu, RHEL)
#     syslog-ng   = still used, less common
#     syslog      = dead
#
#   ON systemd SYSTEMS (two layers):
#     systemd-journald   = base layer, always runs, binary journal
#           │
#           └──> rsyslog = often added on top, writes /var/log text files
#
# rsyslogd = the daemon    klogd = handles kernel messages



# ===== LOG LOCATIONS (/var/log) =====
#   /var/log/auth.log     (Debian)   logins, sudo, ssh, failed logins
#   /var/log/syslog       (Debian)   main log, almost everything
#   /var/log/messages     (RHEL)     main log, non-kernel messages
#   /var/log/kern.log     kernel messages
#   /var/log/daemon.log   background services
#   /var/log/mail.log     mail server
#   /var/log/boot.log     boot messages

# binary logs (need special tools, NOT less/cat):
#   /var/log/wtmp    -> last                 (successful logins)
#   /var/log/btmp    -> last -f / utmpdump   (failed logins, e.g. ssh brute force)
#   /var/log/faillog -> faillog              (failed auth)
#   /var/log/lastlog -> lastlog              (last login per user)

# ===== READING LOGS =====
less /var/log/auth.log       # page through
zless /var/log/auth.log.3.gz # same but for gzip-compressed rotated logs (also zmore)
tail -f /var/log/syslog      # -f (follow): show new lines live
head -5 /var/log/mail.log    # -5: first 5 lines
grep "sshd" /var/log/syslog  # filter

# log line format:  timestamp  hostname  program[PID]:  message   (PID = Process ID)

# ===== rsyslog.conf : FACILITY . PRIORITY  ACTION =====
# /etc/rsyslog.conf   (extra files in /etc/rsyslog.d/)
# 3 sections: MODULES, GLOBAL DIRECTIVES, RULES

# FACILITY = which subsystem made the message
#   kern, user, mail, daemon, auth/authpriv, syslog, lpr, news, cron, ftp, ntp, local0-local7 ...

# PRIORITY = how important (lower number = worse). 8 levels:
#   0 emerg    system unusable
#   1 alert    act now
#   2 crit     critical
#   3 err      error
#   4 warning  warning
#   5 notice   normal but important
#   6 info     informational
#   7 debug    debug
# a priority matches that level AND higher (mail.err = err + crit + alert + emerg)

# RULE format:  <facility>.<priority>   <action = where to send it>
auth,authpriv.*         /var/log/auth.log # all priorities (*) from auth -> auth.log
*.*;auth,authpriv.none  -/var/log/syslog  # everything EXCEPT auth (.none). - = less disk writes
mail.err                /var/log/mail.err # mail, err or worse
#  ; splits selectors   , joins facilities   .none excludes   .=debug means that ONE priority only

# ===== logger (write your own log line, for scripts/testing) =====
logger "this goes into /var/log/syslog"
logger -t backup "done" # -t (tag): a name you can grep later
tail -1 /var/log/syslog # -1: last line. see it

# ===== dmesg (kernel ring buffer) =====
# kernel logs to an in-memory ring buffer at boot, before rsyslog is ready
dmesg | grep usb # print kernel messages

# ===== logrotate (stop logs growing forever) =====
# renames, compresses, and finally deletes old logs. run daily by /etc/cron.daily/logrotate
# config: /etc/logrotate.conf (global) + /etc/logrotate.d/ (per package, overrides global)
# rotation:  messages -> messages.1 -> messages.2.gz -> ... -> deleted
# key directives:
#   rotate 4       keep 4 old copies
#   weekly         rotate every week (also daily, monthly)
#   compress       gzip old logs
#   delaycompress  compress one cycle later (log still being written)
#   create         make a new empty log after rotating
#   missingok      no error if log missing
#   notifempty     skip rotation if log is empty
#   postrotate / endscript   run a command after rotating

# ============================================================
# LESSON 2 - systemd-journald (modern)
# ============================================================

# systemd-journald = systemd's logging service. One central, indexed, BINARY journal.
# no log rotation needed. config: /etc/systemd/journald.conf
# binary = cannot use less/cat, must use journalctl

# ===== journalctl (read the journal, needs root/sudo) =====
journalctl                                       # whole journal, oldest first
journalctl -r                                    # -r (reverse): newest first
journalctl -f                                    # -f (follow): live (like tail -f)
journalctl -e                                    # -e (end): jump to end
journalctl -n 5                                  # -n (number): last 5 lines
journalctl -k                                    # -k (kernel): kernel messages only (= dmesg), also --dmesg
journalctl -b                                    # -b (boot): this boot   (-b -1 = previous boot, needs persistent storage)
journalctl -b -0 -p err                          # -p (priority): err or worse, for current boot
journalctl --since "19:00:00" --until "19:01:00" # time range (YYYY-MM-DD HH:MM:SS)
journalctl --since "2 minutes ago"               # also: yesterday, today, now
journalctl -u ssh.service                        # -u (unit): by systemd unit
journalctl /usr/sbin/sshd                        # by program path
# by field:  journalctl PRIORITY=3   SYSLOG_FACILITY=1   _PID=1
#   two fields together = AND    |    field + field = OR

# ===== systemd-cat (send command output INTO the journal) =====
# like logger, but for the journal. sends stdin, stdout, stderr to journald.
systemd-cat                          # no args: reads stdin, type lines, Ctrl+C to stop
echo "hello" | systemd-cat           # send a piped command's output to the journal
systemd-cat echo "hello too"         # run a command, send its output (and stderr) to journal
systemd-cat -p emerg echo "not real" # -p (priority): set a priority level
journalctl -n 4                      # see the last lines you added

# ===== STORAGE: persistent vs volatile =====
# /var/log/journal/   exists -> logs saved on DISK (survive reboot)
# /run/log/journal/   used when the above is missing -> RAM only, LOST on reboot
# set in /etc/systemd/journald.conf with Storage= :
#   Storage=persistent   disk, /var/log/journal (created if needed)
#   Storage=volatile     RAM only, /run/log/journal
#   Storage=auto         like persistent but does NOT create the dir (this is the DEFAULT)
#   Storage=none         throw logs away
# turn on persistent:  set Storage=persistent (or mkdir /var/log/journal), then restart: sudo systemctl restart systemd-journald

# ===== SIZE / DELETE OLD DATA =====
journalctl --disk-usage          # how much space the journal uses
# limits in journald.conf:  SystemMaxUse=500M  (max space, default 10% of filesystem, cap 4GiB)
#                           SystemKeepFree= , SystemMaxFileSize= , SystemMaxFiles= (default 100)
# manual clean (vacuum), only touches ARCHIVED files:
journalctl --vacuum-time=1months # delete older than 1 month
journalctl --vacuum-size=100M    # keep only 100M
journalctl --vacuum-files=10     # keep only 10 files

# ===== rsyslog <-> journald =====
# in journald.conf:  ForwardToSyslog=yes   -> journald also sends logs to rsyslog
# (also ForwardToKMsg, ForwardToConsole, ForwardToWall)


# ===== journalctl -D (read a journal from another location) =====
# -D (directory) <dir>, --directory=<dir> : read journal files from a given folder, not the default
# real use: a machine is broken. you boot a rescue USB, mount its disk,
#           and read ITS journal to see why it failed.
journalctl -D /mnt/broken/var/log/journal/

108.3 Mail Transfer Agent (MTA) basics

# MTA = moves mail (server)   |   MUA (Mail User Agent) = mail client (mail, Thunderbird)

# ===== MTAs =====
#   sendmail   oldest, huge, hard to configure, not security oriented -> few use it as default
#   exim       general and flexible, strong checks on incoming mail (ACLs, authentication)
#   postfix    newer alternative to sendmail, easy config files, multi-domain, encryption
#              -> default MTA on most distros
#   qmail      another MTA, just know the name
# most desktop distros install NO MTA by default. good choice: postfix + mailx (or bsd-mailx)

# ===== sendmail EMULATION LAYER =====
# every MTA copies sendmail's commands -> sendmail, mailq, newaliases work on ANY MTA

# ===== /etc/aliases (root) =====
postmaster:  root      # <alias>: <destination>
www:         webmaster # aliases can chain
test:        /dev/null # throw away
support:     ali, sara # several destinations
newaliases             # MUST run after editing (= sendmail -bi / -I)

# ===== mail =====
mail user                              # send: Subject, body, Ctrl+D to end
mail                                   # read inbox: p print, d delete, r reply, q quit
echo "body" | mail -s "subj" user@host # -s (subject)
mail -a file.gz user@host              # -a (attach)

# ===== ~/.forward (normal user) =====
# user forwards OWN mail: put a user or email address inside
# NO newaliases needed | owner-writable only | hidden file

# ===== queue =====
mailq       # show stuck mail + reason (= sendmail -bp)
sendmail -q # -q (queue): retry now

# ===== EXTRAS =====
# SMTP = TCP port 25
# queue: /var/spool/mqueue/ (sendmail) | /var/spool/postfix/
# inbox: /var/spool/mail/<user> | /var/mail/<user>
# sendmail: "." alone ends message  |  mail: Ctrl+D

108.4 Manage printers and printing

# CUPS (Common Unix Printing System) = the printing system on most distros. daemon: cupsd

# ===== INSTALL / START =====
sudo apt install cups             # (dnf install cups on RHEL)
sudo systemctl start cups.service # start CUPS now

# ===== CONFIG FILES (/etc/cups/) =====
# /etc/cups/cupsd.conf      main config.  Listen localhost:631 = listen on port 631
# /etc/cups/printers.conf   all printers. written by cupsd, DO NOT edit while cupsd runs
# /etc/cups/ppd/            PPD files (PostScript Printer Description) = text file describing each printer's features
# /var/log/cups/            access_log, error_log, page_log

# ===== WEB INTERFACE =====
# cupsd.conf: WebInterface Yes  ->  http://localhost:631
#   Administration = add printers, manage jobs, configure CUPS
#   Jobs           = active, pending, completed jobs
#   Printers       = list installed printers
# users can VIEW. changes need admin (set by <Limit ...> blocks at bottom of cupsd.conf)

# ===== LPD LEGACY INTERFACE (may need package: cups-bsd) =====
# LPD (Line Printer Daemon) = old BSD printing system, before CUPS.
# CUPS still accepts its commands, so old scripts keep working. CUPS does the real work.
lpr -PMyPrinter file.txt # -P (printer): print. no printer = default printer
lpq                      # show queue (q = queue). -a (all) printers | -PMyPrinter one printer
lprm 2                   # remove job ID 2 (rm = remove). only root removes others' jobs
lprm -                   # remove ALL your jobs
lpc status               # printer status (c = control)
#   NOTE: no space after -P  ->  -PMyPrinter
#   lpc status output:
#     queuing is enabled   -> queue ACCEPTS new jobs
#     printing is enabled  -> printer really PRINTS on paper

# ===== CONTROL QUEUE / PRINTING =====
cupsaccept  MyPrinter                      # queue accepts new jobs
cupsreject  MyPrinter                      # queue refuses new jobs
cupsenable  MyPrinter                      # physical printing ON
cupsdisable MyPrinter -r "need more paper" # printing OFF. -r (reason)

109.1 Fundamentals of internet protocols

# TCP/IP = the protocol stack of the Internet. includes TCP, UDP, ICMP, DNS ...

# ===== IPv4 =====
# format A.B.C.D, each part = octet (8 bits, 0-255). total 32 bits
# about 4.3 billion addresses -> not enough -> NAT and IPv6

# CLASSES (first octet)
#   A   1-126     255.0.0.0     /8
#   B   128-191   255.255.0.0   /16
#   C   192-223   255.255.255.0 /24
#   127.x.x.x = loopback (127.0.0.1)
#   224+      = multicast, not for hosts

# PRIVATE RANGES (not routed on the Internet)
#   10.0.0.0      - 10.255.255.255     (/8,  16M IPs)
#   172.16.0.0    - 172.31.255.255     (/12, 1M IPs)
#   192.168.0.0   - 192.168.255.255    (/16, 65K IPs)
# NAT (Network Address Translation) = many private IPs go out through ONE public IP

# ===== SUBNETTING =====
# netmask splits the IP: NETWORK bits (left) | HOST bits (right)
# CIDR (Classless Inter-Domain Routing) = number of network bits
#   /8  = 255.0.0.0     /16 = 255.255.0.0     /24 = 255.255.255.0
# usable hosts = 2^(host bits) - 2      (/24 -> 2^8 - 2 = 254)

# NETWORK + BROADCAST
#   network   = IP AND mask
#   broadcast = network OR flipped mask
# 192.168.4.12/24 -> network 192.168.4.0 | broadcast 192.168.4.255
ipcalc 192.168.4.12/24 # calculates it for you

# BINARY:  128 64 32 16 8 4 2 1   ->  11000000 = 128+64 = 192
# ===== PROTOCOLS =====
#   TCP   reliable, checks every packet     -> web, ssh, downloads
#   UDP   fast, no checks, can lose packets -> video calls, DNS
#   ICMP  diagnostics (ping), no user data

# ===== PORTS =====
# port = which program gets the packet. 0-65535
#   1-1023 = services (privileged)   |   1024+ = clients
# full list: /etc/services
#   20,21 FTP      53  DNS      139 NetBIOS    389 LDAP    636 LDAPS
#   22    SSH      80  HTTP     143 IMAP       443 HTTPS   993 IMAPS
#   23    Telnet   110 POP3     161,162 SNMP   465 SMTPS   995 POP3S
#   25    SMTP     123 NTP      514 Syslog
# tip: above 400 + ends in S = Secure

# ===== /etc/services =====
# text file: maps service names to port numbers + protocol
grep -w 22 /etc/services  # -w (word): exact word only. ssh   22/tcp
grep -w ssh /etc/services # find the port of a service

# ===== IPv6 =====
# 128 bits, 8 hex groups:  2001:0db8:0000:0000:0000:0000:0000:7344
# short: drop leading 0s, "::" for zero groups (only once) -> 2001:db8::7344
#
# unicast = one machine | multicast = all in group | anycast = nearest in group
#
# ===== IPv4 vs IPv6 =====
#   Feature          IPv4          IPv6
#   ---------------  ------------  ------------------------
#   size             32 bits       128 bits
#   send to all      broadcast     no broadcast -> multicast ff02::1
#   packet counter   TTL           Hop Limit
#   find neighbors   ARP           NDP
#   local address    -             fe80:: (link-local)

109.2 Persistent network configuration

# ===== NETWORK INTERFACES =====
# NIC (Network Interface Card) = the network hardware
# old names: eth0, eth1, wlan0      new names: eno1, ens1, enp3s5, wlp3s0
# lo = loopback, always there, = 127.0.0.1
ip link show # list interfaces

# ===== ifconfig (LEGACY, deprecated) =====
ifconfig                                          # show active interfaces   (-a (all) = even down)
ifconfig eth0 192.168.42.42 netmask 255.255.255.0 # set IP (root)
ifconfig eth0 down                                # turn off  (up = on)

# ===== ifup / ifdown (use saved config) =====
ifup eth0   # bring interface up using its config file
ifdown eth0 # bring it down
# config files:
#   Debian: /etc/network/interfaces        (all interfaces in ONE file)
#   RHEL:   /etc/sysconfig/network-scripts/ifcfg-eth0   (+ gateway in /etc/sysconfig/network)

# /etc/network/interfaces example:
# auto eth0                      # "auto" = bring up at boot
# iface eth0 inet static         # or: iface eth0 inet dhcp
# address 192.168.1.10
# netmask 255.255.255.0
# gateway 192.168.1.1

# ===== ip (modern, TEMPORARY changes) =====
ip addr add 172.19.1.10/24 dev eth2  # add IP (dev = device: which card)
ip addr show eth2                    # show IPs of eth2
ip addr del 172.19.1.10/24 dev eth2  # delete IP
ip link set eth2 up                  # turn on
ip route show                        # show routing table
ip route add default via 192.168.1.1 # add default gateway (via = through this gateway)

# ===== NetworkManager + nmcli =====
# NetworkManager = daemon that manages networks (auto wifi, DHCP)
# it manages interfaces NOT listed in /etc/network/interfaces
# DHCP (Dynamic Host Configuration Protocol) = get IP, mask, gateway, DNS automatically
# frontends: GUI applet | nmtui (text menu) | nmcli (command line)
nmcli general                                    # overall status
nmcli device                                     # list devices
nmcli device wifi                                # list wifi networks (= wifi list)
nmcli device wifi connect MyWifi password MyPass # connect to a wifi network

# ===== HOSTNAME =====
# /etc/hostname = the machine's name (static hostname)
hostnamectl set-hostname mycoolmachine          # sets all 3 types
hostnamectl --pretty set-hostname "LAN Storage" # nice name, spaces allowed
hostnamectl --transient set-hostname temp       # temporary
hostnamectl --static set-hostname firewall      # ONLY static
hostnamectl                                     # show status
# only the STATIC name is saved in /etc/hostname

# ===== /etc/hosts =====
# local list: IP -> name (checked before DNS by default)
127.0.0.1      localhost
::1            localhost
192.168.1.10   foo.mydomain.org  foo # extra names = aliases

# ===== /etc/resolv.conf (DNS) =====
nameserver 192.168.1.1        # DNS server. up to 3
nameserver 4.2.2.4            # fallback
domain nagato.net             # local domain -> short names work
search nagato.net company.com # domains to try for short names
# NOTE: the file is resolv.conf (no e), not resolve.conf

# ===== /etc/nsswitch.conf =====
# says WHERE and in WHICH ORDER to look up names, users, groups
hosts: files dns # first /etc/hosts, then DNS
# hosts: dns files             # DNS first, /etc/hosts only if DNS does not know

# ============================================================
# EXTRAS
# ============================================================
# NAME PREFIXES:  en = Ethernet | wl = WLAN (wifi) | ww = WWAN | ib = InfiniBand | sl = serial
# NAMING ORDER (Linux uses the first rule that works):
#   1. eno1    o = onboard card, number from BIOS
#   2. ens1    s = PCIe slot number
#   3. enp3s5  p = bus 3, slot 5 (see lspci)
#   4. enx...  x = MAC address (e.g. USB adapters)
#   5. eth0    old style, nothing else worked

# MORE nmcli
nmcli connection show                    # saved connections
nmcli connection up|down MyWifi          # activate / deactivate
nmcli connection delete "Hotel Internet" # delete a saved connection
nmcli device disconnect wlo1             # (connect = reconnect)
nmcli device wifi rescan                 # scan now (root)
nmcli radio wifi off                     # turn wifi off  (on = back)
# nmcli general CONNECTIVITY = portal -> needs web login (hotel wifi)

# systemd-networkd (awareness)
# systemd-networkd = manages interfaces | systemd-resolved = manages DNS
# /etc/systemd/network   -> your config files go here (highest priority)

# NOTE: ifup/ifdown + /etc/network/interfaces = legacy. modern Ubuntu/Debian use netplan
#   (/etc/netplan/*.yaml).

109.3 Basic network troubleshooting

# ===== TROUBLESHOOTING STEPS ("I cannot open webpages") =====
#   1. interface UP + has IP?     ip addr
#   2. can I reach the gateway?   ping <gateway>
#   3. can I reach the Internet?  ping 4.2.2.4    (IP, no DNS needed)
#   4. does DNS work?             ping google.com / dig google.com
#   5. where does it break?       traceroute

# ===== ifconfig & ip (check IP) =====
ip addr show   # needs correct IP + netmask
ifconfig       # legacy
man ip-address # help for one ip subcommand

# ===== ping & ping6 =====
ping 192.168.70.1       # gateway: should always answer (unless ICMP blocked)
ping 4.2.2.4            # Internet by IP
ping google.com         # "unknown host" = DNS problem -> check /etc/resolv.conf
ping -c 3 192.168.50.2  # -c (count): send 3 then stop (else Ctrl+C)
ping6 -c 3 2001:db8::10 # IPv6

# ===== ROUTING (temporary, lost at reboot) =====
# "Network is unreachable" + gateway pings OK = default gateway MISSING
ip route show                              # "default via 192.168.70.1" = default gateway
sudo ip route del default                  # delete the default gateway
sudo ip route add default via 192.168.70.1 # add it back (via = through this gateway)
netstat -nr                                # routing table, legacy (-n numeric, -r routes)

# ===== traceroute & tracepath =====
traceroute 4.2.2.4 # each router (hop) on the way. * * * = hop blocks ICMP
tracepath 4.2.2.4  # same idea (for LPIC-1 "essentially the same")

# ===== ss & netstat (ports and connections) =====
# ss = new, netstat = legacy. same options mostly
ss -na | grep LISTEN # -n numeric, -a all
ss -tulpn            # t tcp | u udp | l listening | p process | n numeric
netstat -tulpn       # same, legacy

# ===== netcat (nc) =====
nc -l 1337        # -l (listen): listen on port 1337
nc localhost 1337 # connect, type text -> shows on the listener

# ===== dig =====
dig google.com # SERVER: line shows which DNS answered

# ============================================================
# EXTRAS
# ============================================================
# LEGACY (net-tools)  ->  MODERN (iproute2)
#   ifconfig          ->  ip addr / ip link
#   route             ->  ip route
#   netstat           ->  ss
#   arp               ->  ip neighbour

# ROUTES
ip route save > backup  |  ip route restore < backup # save the routing table / load it back
ip neighbour                                         # ARP / neighbor table

109.4 Configure client side DNS

# DNS (Domain Name System) = turns names into IPs  (yahoo.com -> 206.190.36.45)

# ===== /etc/resolv.conf (which DNS server to use) =====
nameserver 192.168.1.1
nameserver 4.2.2.4
# often "# Generated by NetworkManager" -> hand edits get OVERWRITTEN (temporary)

# ===== host (simple lookup) =====
host kernel.org      # A (IPv4), AAAA (IPv6), MX (mail) records
host -t A kernel.org # -t (type): only one record type
host 208.80.154.224  # IP -> name (reverse lookup, PTR record)

# ===== dig (detailed lookup, for troubleshooting) =====
dig x.org               # ANSWER section: x.org. 1625 IN A 131.252.210.176
#   1625 = TTL: seconds this answer stays in cache
#   SERVER: 192.168.1.1#53 = which DNS answered (port 53)
dig @8.8.8.8 google.com # @ = ask THIS DNS server, not the one in resolv.conf
dig -t MX lpi.org       # -t (type): record type

# ===== /etc/hosts (static, local names) =====
192.168.59.231  mass1        # works even if DNS does not know "mass1"
127.0.0.1       facebook.com # block a site: name points to your own machine
# dig ignores /etc/hosts (asks DNS only) -> dig mass1 fails, ping mass1 works

# ===== /etc/nsswitch.conf (lookup ORDER) =====
hosts: files mdns4_minimal [NOTFOUND=return] dns
#   files = /etc/hosts first -> then mdns -> then dns
#   [NOTFOUND=return] = stop here if the service answered "not found"

# ===== getent (lookup like a real program, follows nsswitch) =====
getent hosts              # all hosts entries
getent hosts dns1.lpi.org # one name

# ===== systemd-resolved (awareness) =====
# systemd's local DNS service, listens on 127.0.0.53
# asks the real servers from /etc/systemd/resolved.conf or /etc/resolv.conf

# ============================================================
# EXTRAS
# ============================================================
# resolv.conf limits:
#   max 3 nameserver | max 6 search domains
#   domain and search: use ONE. if both, the LAST one wins
options timeout:3 # seconds to wait for a DNS answer

# nsswitch actions:
#   [NOTFOUND=return]   service answered "not found" -> stop
#   [!UNAVAIL=return]   DNS is reachable -> stop, even if no answer
#   [SUCCESS=continue]  found, but keep going (later source wins)

# record types (use with -t):
#   A = IPv4 | AAAA = IPv6 | MX = mail | NS = name servers | SOA = zone info | PTR = IP -> name
host -t MX lpi.org dns1.easydns.com # last argument = which DNS server to ask
dig +short lpi.org                  # +short: only the IP, no extra text
# ~/.digrc = your default dig options

getent -s files hosts learning.lpi.org # -s (source): force one source (files or dns)
getent group openldap                  # works for users/groups too, not just hosts

# KEY DIFFERENCE:
#   getent, ping, ssh, curl -> follow nsswitch (files, dns, ...) = what programs really see
#   host / dig / nslookup   -> ask DNS ONLY (ignore /etc/hosts)

110.1 Perform security administration tasks

# ===== su vs sudo =====
su -       # become root. asks ROOT's password. "-" = load target's environment
su - carol # become carol. asks CAROL's password
su         # no "-" -> keeps your old environment (stays in /home/you)
sudo ls    # run ONE command as root. asks YOUR password
sudo su -  # become root using your own password
# sudo is safer: no root password shared, only single commands

sudo -u carol ping 8.8.8.8 # -u (user): run as carol

# ===== /etc/sudoers (edit ONLY with visudo) =====
root    ALL=(ALL:ALL) ALL   # user  host=(as_user:as_group)  commands
%sudo   ALL=(ALL:ALL) ALL   # %  = a group
%admin  ALL=(ALL) ALL
nagato  ALL=(ALL) /bin/ping # nagato can run ONLY ping as root
#includedir /etc/sudoers.d  # extra files, preferred place for your rules
visudo                      # checks syntax before saving. a broken sudoers = no sudo

# ===== WHO IS / WAS LOGGED IN =====
w                     # logged in now + what they are doing (+ uptime, load)
who                   # logged in now (user, tty, time, host)
last                  # past logins, newest first. reads /var/log/wtmp
last -f /var/log/btmp # -f (file): FAILED logins (same as: lastb)

# ===== passwd =====
passwd             # change your own password
sudo passwd nagato # change another user's password
passwd -S          # -S (Status): nagato P 2023-09-14 0 99999 7 -1
#   P = has password | L = locked | NP = no password
passwd -l nagato # -l (lock)    (-u unlock, -e expire)
# shell, home... -> use usermod, not passwd

# ===== chage (password aging) =====
chage -l nagato   # -l (list): list aging info
chage nagato      # interactive mode (root)
chage -m 7 nagato # -m (min): min days between changes  (-M = max days)

# ===== SUID / SGID =====
# SUID (s in user part) = runs as the file OWNER, not as the runner
ls -l /usr/bin/passwd  # -l (long). -rwsr-xr-x root  -> passwd can edit /etc/shadow for normal users
# danger: SUID on vi = anyone edits any file as root. audit regularly:
sudo find / -perm -u+s # -perm (permissions): all SUID files
# SGID = same idea, runs with the file's GROUP

# ===== LIMITS =====
ulimit -a   # -a (all): show all limits
ulimit -t 1 # -t (time): CPU time max 1 second. TEMPORARY (this shell only)
# permanent, system-wide: /etc/security/limits.conf
#   <domain>   <type>  <item>     <value>
#   @student   hard    nproc      20        # group student: max 20 processes
#   @student   -       maxlogins  4         # "-" = soft and hard
#   domain: user | @group | * (default)    type: soft | hard
# soft = user can change it | hard = the real maximum

# ===== OPEN PORTS =====
netstat -tuna        # t tcp | u udp | n numeric | a all  ("tuna" sandwich)
#   LISTEN = server waiting | ESTABLISHED = active connection | 0.0.0.0 = any address
ss -tuna             # modern
lsof -i              # -i (internet): open network connections + command, PID, user
sudo fuser -v 22/tcp # -v (verbose): which process uses port 22

# ===== nmap =====
nmap localhost # scan ports 1-1000, show open ones

# ============================================================
# EXTRAS
# ============================================================
# find -perm, the 3 forms:
find . -perm 4000         # ONLY SUID, exactly
find /usr/bin -perm -4000 # SUID + any other perms   (= -perm -u+s)
find /usr/bin -perm -2000 # SGID                      (= -perm -g+s)
find /usr/bin -perm /6000 # SUID OR SGID              (4 + 2 = 6)

# lock also with usermod
usermod -L carol            # -L (Lock)   (-U Unlock)
usermod -f 3 carol          # -f: disable account 3 days after password expires (= chage -I)
usermod -e 2050-12-13 carol # -e (expire): account expire date (= chage -E)

# chage options
#   -m min | -M max | -d last change (0 = force change at login)
#   -I inactive days | -E account expire date | -W warn days

# lsof / fuser
lsof -i@192.168.1.7 # -i (internet): connections to one host
lsof -i :22         # one port
fuser -vn tcp 80    # -v (verbose) -n (namespace) tcp: who uses tcp port 80
fuser -k 80/tcp     # -k (kill): KILL the processes using it

# nmap
nmap -p 22 localhost    # -p (port): one port (= -p ssh)
nmap -p 22-80 localhost # range
nmap -p- localhost      # ALL 65535 ports
nmap -F localhost       # -F (fast): top 100 ports
nmap 192.168.1.0/24     # whole subnet (--exclude 192.168.1.7)

# ulimit soft / hard
ulimit -Ha     # -H (hard) + -a (all): all HARD limits (-a alone = soft)
ulimit -Sf 200 # -S (soft) + -f (file size): set only soft file size
ulimit -f 500  # no -S/-H = sets BOTH
# normal user: can LOWER hard, raise soft only up to hard

# who / w / last
who -b     # -b (boot): last boot time  (-r runlevel, -H headings)
last carol # one user only

# sudo
sudo -u carol cmd                                               # -u (user): run as another user
carol ALL=(ALL:ALL) NOPASSWD: /usr/bin/systemctl status apache2 # no password asked
# sudo remembers your password 15 min. change: Defaults timestamp_timeout=1
# aliases: Host_Alias | User_Alias | Cmnd_Alias | Runas_Alias
User_Alias ADMINS = carol, %sudo, !john # ! = exclude
Cmnd_Alias SERVICES = /usr/bin/systemctl *
ADMINS ALL = SERVICES

110.2 Setup host security

# ===== SHADOW PASSWORDS =====
# problem: /etc/passwd must be readable by ALL users -> hashes would be visible
# fix: hash moves to /etc/shadow, passwd shows only "x"
ls -l /etc/passwd       # -rw-r--r--  root root     everyone can read
ls -l /etc/shadow       # -rw-r-----  root shadow   only root (and group shadow)
grep nagato /etc/passwd # nagato:x:1000:1000:nagato,,,:/home/nagato:/bin/bash
grep nagato /etc/shadow # Permission denied  -> needs sudo

# ===== /etc/nologin (maintenance) =====
# file exists -> nobody can log in, its text is shown to them. delete it -> logins work again
# root CAN still log in
sudo usermod -s /sbin/nologin baduser # -s (shell): this user has no shell, but mail/ftp still work

# ===== SUPER-SERVERS (inetd, xinetd) =====
# one daemon listens for many services, starts the real service ONLY when a request comes
# old, rarely used today. modern replacement: systemd .socket units
# /etc/xinetd.conf   main config (includedir /etc/xinetd.d)
# /etc/xinetd.d/     one file per service
service telnet
{
    disable      = no                   # no = ACTIVE, yes = off
    socket_type  = stream               # stream = TCP, dgram = UDP
    wait         = no                   # no = handle many connections at once
    user         = root
    server       = /usr/sbin/in.telnetd # full path of the real service
    no_access    = 10.0.1.0/24          # blocked network
    access_times = 09:45-16:15          # allowed hours
}

# systemd .socket = modern xinetd: systemd waits on the port, starts the service on demand
sudo systemctl stop ssh.service # stop the always-running sshd
sudo systemctl start ssh.socket # systemd now watches port 22
sudo lsof -i :22 -P             # -P: show port numbers. listener = systemd, not sshd

# ===== TCP WRAPPERS: /etc/hosts.allow & /etc/hosts.deny =====
# work only for programs linked with libwrap:
ldd /usr/sbin/vsftpd | grep libwrap # ldd = list the libraries a program uses
# format:  service: hosts
vsftpd: 10.10.100.                  # in hosts.allow -> only 10.10.100.* may use vsftpd
sshd: ALL                           # in hosts.deny  -> block everyone
sshd: LOCAL                         # in hosts.allow -> except local network
# ALL = all services or all hosts

# ===== REMOVE UNUSED SERVICES =====
sudo service --status-all                          # SysV list: [+] running, [-] stopped
sudo chkconfig vsftpd off                          # RedHat, old
sudo update-rc.d vsftpd remove                     # Debian, old
systemctl list-units --state active --type service # list running services
sudo systemctl disable vsftpd.service --now        # systemd: stop now + off at boot
ss -ltu    /  netstat -ltu                         # -l listening -t tcp -u udp: listening services

# ===== /etc/inittab (SysV, old) =====
# format:  id:runlevel:action:process
1:2345:respawn:/sbin/mingetty tty1 # runlevels 2-5: start getty, restart if killed
id:3:initdefault:                  # boot into runlevel 3
# /etc/init.d/  = old init scripts

110.3 Securing data with encryption

# ===== KEY PAIRS =====
# symmetric  = one shared password encrypts AND decrypts
# asymmetric = key PAIR: what one key locks, only the other opens
#   public key  -> give to everyone      private key -> keep secret
#   encrypt: others use YOUR public key -> only your private key opens it
#   sign:    you use YOUR private key   -> anyone checks it with your public key

# ===== SSH HOST KEYS (server identity) =====
ssh 192.168.70.2           # 1st time: "authenticity can't be established" + fingerprint -> yes
# saved in ~/.ssh/known_hosts (per user) | /etc/ssh/ssh_known_hosts (system-wide)
# key changed -> "REMOTE HOST IDENTIFICATION HAS CHANGED!" (maybe man-in-the-middle)
ssh-keygen -R 192.168.70.2 # -R (remove): remove old key from known_hosts (after checking it's safe)
# server keys: /etc/ssh/ssh_host_{rsa,dsa,ecdsa,ed25519}_key (+ .pub)

# ===== YOUR OWN KEYS =====
ssh-keygen          # default: rsa -> ~/.ssh/id_rsa + id_rsa.pub
ssh-keygen -t ecdsa # -t (type): rsa | dsa | ecdsa | ed25519 -> ~/.ssh/id_ecdsa(.pub)
# passphrase = password on the private key (asked on every use)

# ===== KEY-BASED LOGIN (no password) =====
ssh-copy-id 192.168.70.2 # copies your PUBLIC key into server's ~/.ssh/authorized_keys
# server needs in /etc/ssh/sshd_config:  PubkeyAuthentication yes

# ===== ssh-agent / ssh-add =====
ssh-agent /bin/bash # start a shell with the agent
ssh-add             # load your keys -> passphrase asked ONCE, then remembered

# ===== SSH TUNNELS =====
ssh -L 5433:localhost:5432 admin@ec2
#   -L (local): my localhost:5433 -> through ec2 -> ec2's Postgres (localhost:5432)
#   use: open a server DB that only listens on localhost, from my PC

ssh -R 8000:localhost:3000 admin@ec2
#   -R (remote): ec2's port 8000 -> back to MY localhost:3000
#   use: show my local dev site to someone through the server

ssh -R 0.0.0.0:8000:localhost:3000 admin@ec2
#   same, but port 8000 opens on ALL ec2 interfaces (reachable from internet)
#   needs GatewayPorts in the server's sshd_config (default: localhost only)

ssh -D 1080 192.168.70.2 # -D (dynamic): localhost:1080 becomes a SOCKS proxy
ssh -X 192.168.70.2      # -X: X11 forwarding: remote GUI apps show on my screen
#   needs X11Forwarding yes in sshd_config

# -L  LOCAL:  bring a remote port to my machine
#     ssh -L 5433:localhost:5432 admin@ec2
#
#     [devbox]                              [EC2]
#     DBeaver -> :5433 ===== SSH =====> localhost:5432 (Postgres)
#                ▲ port opens here
#
# -R  REMOTE: send my port to the remote machine
#     ssh -R 8000:localhost:3000 admin@ec2
#
#     [devbox]                              [EC2]                    [friend]
#     my site :3000 <==== SSH ===== :8000 <---------------------- browser
#                                   ▲ port opens here
#
# -D  DYNAMIC: remote machine becomes my proxy
#     ssh -D 1080 admin@ec2
#
#     [devbox]                              [EC2]               [internet]
#     browser -> :1080 ===== SSH =====> EC2 ------------------> google.com
#                ▲ port opens here             ├--------------> youtube.com
#                                              └--------------> any site
#
# =====  inside the SSH tunnel (encrypted)
# ---->  normal traffic
# ▲      where the listening port opens


# ===== GPG =====
gpg --gen-key                                                  # create key pair in ~/.gnupg/
gpg --list-keys                                                # list the keys in your keyring
gpg --export nagato > nagato.pub.key                           # share your public key (-a (armor) = ASCII text)
gpg --import nagato.pub.key                                    # import someone's public key
gpg --output nagato.revoke.asc --gen-revoke nagato@example.com # revoke if key is stolen

# encrypt / decrypt
gpg --out file.txt.encrypted --recipient nagato@example.com --encrypt file.txt # encrypt with nagato's public key
gpg --out out.txt --decrypt file.txt.encrypted                                 # decrypt with your private key

# sign / verify
gpg --output msg.sig --sign msg.txt # sign with MY private key (binary)
gpg --verify msg.sig                # check signature with sender's public key
gpg --output msg --decrypt msg.sig  # verify + get the content
gpg --clearsign msg.txt             # -> msg.txt.asc: readable text + signature

# gpg-agent = like ssh-agent, keeps gpg key passphrases in memory

# ============================================================
# EXTRAS
# ============================================================
ssh-keygen -t ecdsa -b 521 # -b (bits) = key size in bits