Linux for DevOps · Hands-on lab

Security Hardening, AppArmor & TLS

Make a fresh server harder to break into and easier to investigate. Reduce what's exposed, set up a default-deny firewall, audit sensitive files, read AppArmor and SELinux denials instead of switching them off, and check TLS certificates and time sync. Then fix a server where every HTTPS call suddenly fails.

Module: 17 of 25 Level: Linux for DevOps · Hands-on lab Time: 15 min read Published:
All modules in this track (25)

In this module, you'll learn to:

  • Harden a fresh server against a short, practical checklist
  • Record who changes sensitive files with auditd, and read AppArmor and SELinux denials
  • Inspect TLS certificates with openssl, and recognise problems caused by a wrong clock

Before you start: finish Users, sudo & SSH and DNS, Firewalls & Packet Capture. Open a shell on your lab VM with multipass shell lab.

Principle

Smaller, patched, and watched

Hardening isn't one setting. It's a habit built on four ideas:

  • Least privilege: every user and service gets only the access it needs (Users, sudo & SSH, systemd).
  • Smaller attack surface: if a service isn't needed, it isn't running, and if a port doesn't need to be open, it isn't.
  • Patched: security updates go on quickly and automatically (Packages).
  • Watched: changes to sensitive files are recorded, and logs leave the server (Logs & Monitoring).

Published baselines, such as the CIS Benchmarks, turn these ideas into hundreds of specific checks. You rarely apply all of them by hand; automation and audit tools do that. But you should understand the important ones.

Checklist

Harden a fresh server

Work through this on the lab VM. Every step is safe for your SSH session.

# 1. What's exposed? Every listening port should have a reason.
sudo ss -tlnp

# 2. Default-deny firewall. Allow SSH FIRST, or you lock yourself out.
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw enable
sudo ufw status verbose

# 3. Security updates install automatically?
systemctl is-enabled unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades

# 4. SSH: keys only, no root sign-in (see Module 04)
sudo sshd -T | grep -Ei '^(passwordauthentication|permitrootlogin)'

# 5. Who can become root?
getent group sudo
sudo ls /etc/sudoers.d/

# 6. Programs that run with their owner's privileges (setuid): know every one
sudo find / -xdev -perm -4000 -type f 2>/dev/null

# 7. An automated audit for everything else, with suggestions
sudo apt install -y lynis
sudo lynis audit system --quick | tail -30
Scroll sideways to see the full diagram →

Lynis prints a hardening index and a list of suggestions. Treat it as a to-do list to think about, not a score to maximise: some suggestions won't apply to your server.

Audit

Who changed that file?

auditd records security-relevant events from the kernel: who changed a file, who ran a command as root, who changed a user account. Unlike application logs, a user can't simply skip it.

sudo apt install -y auditd
sudo auditctl -w /etc/passwd -p wa -k identity       # watch writes and attribute changes
sudo auditctl -w /etc/sudoers.d/ -p wa -k sudoers

sudo useradd audit-test                              # a change to /etc/passwd
sudo ausearch -k identity -i | tail -20              # who, when, which command
sudo userdel audit-test
Scroll sideways to see the full diagram →

auditctl rules last until reboot. Permanent rules go in a file under /etc/audit/rules.d/.

MAC

Read denials, don't disable the system

On top of normal permissions, mandatory access control confines programs to what their policy allows, even when they run as root. Ubuntu uses AppArmor, and RHEL family systems use SELinux. When one blocks something, the program sees "Permission denied" even though the file permissions look fine, which is the case Files, Inodes & Permissions warned about.

# AppArmor (Ubuntu)
sudo aa-status                                   # which profiles are loaded, enforcing or complaining
sudo journalctl -k | grep 'apparmor="DENIED"'    # what was blocked, for which program

# SELinux (RHEL, Rocky, Amazon Linux)
getenforce                                       # Enforcing, Permissive, or Disabled
sudo ausearch -m avc -ts recent                  # recent denials
sudo restorecon -Rv /srv/www                     # fix wrong file labels, the most common cause
Scroll sideways to see the full diagram →

A denial message names the program, the action, and the file. The usual fix is small: a file in an unusual location needs the right label (restorecon, semanage fcontext), or the profile needs one extra rule. Setting SELinux to Permissive while you investigate is fine. Turning it off for good removes a layer of protection, and it's rarely needed.

TLS

Certificates and the clock

Most "HTTPS is broken" incidents are an expired certificate, a missing intermediate certificate, the wrong name on the certificate, or a server whose clock is wrong. openssl checks each of these:

# The certificate a server presents: who it's for, who issued it, valid from and to
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

# Will it still be valid in 30 days? (exit code 0 = yes)
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend $((30*24*3600))

# Is this server's clock right, and is it synchronised?
timedatectl
Scroll sideways to see the full diagram →

Certificate validity is checked against the local clock. A server whose clock has drifted months ahead sees every certificate on the internet as expired, and one that's behind sees new certificates as "not yet valid". Time sync (here systemd-timesyncd, or chrony) isn't optional.

Break it, fix it

Every HTTPS call fails

After maintenance work on this server, every HTTPS request it makes fails, and apt update complains too. From your laptop, the same sites work perfectly. Reproduce it:

sudo timedatectl set-ntp false
sudo date -s "2031-01-01 09:00"
curl -sS https://example.com -o /dev/null
sudo apt update 2>&1 | tail -3

Your job: find out why trusted certificates are being rejected, and fix it so it can't drift again. Try it before opening a hint.

Hint 1 · Where to look
  • Read the exact error from curl. The same certificates are accepted on your laptop, so ask what's different about how this server judges them.
Hint 2 · Which tool
  • Compare the certificate's dates (openssl s_client ... | openssl x509 -noout -dates) with this server's idea of the date: date and timedatectl. Look at the "synchronized" and "NTP service" lines.
Hint 3 · The cause and the fix
  • Cause: the server's clock is set years ahead, and time sync is turned off. Every certificate's "valid until" date is in the past by this server's clock, so curl reports "certificate has expired", and apt rejects the repositories' signed files for the same reason.
  • Fix: turn time sync back on: sudo timedatectl set-ntp true and sudo systemctl restart systemd-timesyncd. Within a few seconds timedatectl shows "System clock synchronized: yes", and curl works again.
  • Prevent it: never set the clock by hand on a server. Monitor clock offset and sync status, and alert on certificates that expire within 30 days, so a real expiry is renewed long before it causes an outage.
Implementation notes
  • Allow SSH before enabling a firewall, every time, and keep your current session open while you test a new connection.
  • Defaults beat heroics. Automatic security updates, keys-only SSH, and a default-deny firewall stop more real attacks than any exotic setting.
  • Automate certificate renewal (for example with ACME and Let's Encrypt, or a managed certificate service) instead of tracking expiry dates in a spreadsheet.
  • Harden with automation, then audit. A baseline applied by Ansible or a golden image is consistent; one applied by hand drifts.
Recap

Key terms

  • Attack surface: everything an attacker could interact with: open ports, services, accounts.
  • Default-deny firewall: block all incoming traffic except what you explicitly allow.
  • auditd: the kernel audit system, which records who changed sensitive files.
  • AppArmor and SELinux: mandatory access control that confines programs, even ones running as root.
  • Denial: a log entry showing what AppArmor or SELinux blocked, for which program.
  • Certificate chain and expiry: who vouches for a certificate, and the dates it's valid between.
  • Time sync: keeping the clock right with NTP, which certificate checks depend on.
Check yourself

Pop quiz

Five questions: three on the ideas in this module, and two scenarios where you apply them. The order changes every time you take it, and 4 out of 5 passes.

5 questions. Decide on your answer to each, then check it.

  1. Concept

    You're about to enable ufw on a remote server you reach over SSH. What must come first?

    1. sudo ufw default allow incoming
    2. sudo ufw allow OpenSSH
    3. A reboot
    4. Disabling AppArmor
    Show answer

    sudo ufw allow OpenSSH

    With a default-deny policy, SSH is blocked too unless it's allowed first. Enabling the firewall without that rule cuts off the session you're using.

  2. Concept

    A web server running as root gets "Permission denied" reading a file that's 644. Ubuntu, AppArmor enforcing. Where do you look?

    1. /etc/passwd
    2. The kernel log, for an apparmor="DENIED" entry naming the program and file
    3. The file's inode number
    4. The firewall rules
    Show answer

    The kernel log, for an apparmor="DENIED" entry naming the program and file

    AppArmor confines programs regardless of normal permissions, and even root. The denial entry tells you exactly what was blocked, so you can fix the profile or the file's location.

  3. Concept

    Why does a server's clock matter for HTTPS?

    1. TLS encrypts with the current time
    2. Certificate validity is checked against the local clock, so a wrong clock makes valid certificates look expired or not yet valid
    3. The clock sets the TCP port
    4. It doesn't; only the certificate matters
    Show answer

    Certificate validity is checked against the local clock, so a wrong clock makes valid certificates look expired or not yet valid

    Every certificate has a "not before" and "not after" date. A server with a drifted clock judges them wrongly, and every TLS connection it makes fails.

  4. Scenario

    An auditor asks who modified /etc/sudoers.d/ last week. What gives you a reliable answer?

    1. The bash history of each admin
    2. auditd records, from a watch rule on that directory, searched with ausearch
    3. The file's modification time
    4. last, which lists sign-ins
    Show answer

    auditd records, from a watch rule on that directory, searched with ausearch

    Bash history can be edited or skipped, and a modification time doesn't say who. An audit rule records the user, time, and command for every change.

  5. Scenario

    A colleague's fix for an SELinux denial is setenforce 0 in a boot script. What's a better approach?

    1. Read the denial with ausearch -m avc, then fix the cause, often a wrong file label with restorecon
    2. Uninstall SELinux
    3. Run the service as a different user
    4. Ignore it; permissive is the default anyway
    Show answer

    Read the denial with ausearch -m avc, then fix the cause, often a wrong file label with restorecon

    Most denials have a small, specific fix. Turning SELinux off for good removes the protection for every service on the server, to fix one.

Questions about this learning path?

I'm happy to go into more detail on any of this: how to practise troubleshooting safely, which fundamentals matter most early on, or how a break-fix lab is put together.

anupojuprudhvi@gmail.com