Linux Operating Systems Fundamentals · Lesson 10

Securing Your System

Auditing and disabling network services, legacy access controls, password and root-access security, user auditing and limits, cryptography basics, SSH, and GPG.

  • linux
  • security
  • sudo
  • ssh
  • gpg
  • encryption
  • auditing
  • lpic-1

Lesson purpose

Security is something an administrator practices every day, not a product you install once. This lesson applies the skills from earlier lessons to three jobs:

  • Shrinking the network attack surface: find and disable services you don’t need, and control access to the ones you keep.
  • Tightening local security: protect passwords, limit root access, audit who logs in, set resource limits, and hunt for dangerous permissions.
  • Protecting data with cryptography: use SSH for secure remote access and file transfer, and GPG to encrypt and sign files.

Learning objectives

  • Explain attack surface and audit listening services with nmap, netstat, ss, systemctl list-sockets, lsof, and fuser.
  • Disable unneeded services on systemd and SysVinit systems.
  • Describe how super servers (xinetd) and TCP Wrappers restrict access to services, and how they compare with firewalls.
  • Explain how Linux stores and checks passwords, and troubleshoot common login problems.
  • Limit root access with su and sudo, configure sudo safely, and explain nonrepudiation.
  • Audit user sessions with who, w, and last, set limits with ulimit and limits.conf, and find SUID and SGID files.
  • Explain symmetric and asymmetric encryption, hashing, and digital signatures.
  • Use SSH for logins, remote commands, scp, key-based authentication, ssh-agent, and tunneling, and harden sshd.
  • Use GPG to generate, export, and import keys, encrypt and decrypt files, sign and verify files, and revoke a key.

LPIC-1 exam focus

  • 110.1 Perform security administration tasks
  • 110.2 Set up host security
  • 110.3 Securing data with encryption

1. Attack surface

An attack surface is every point where an attacker could try to get in. Every listening service is like a door to the outside. Every outdated or poorly configured service is a door with a bad lock.

The basic process is always the same:

  1. Audit what’s running and listening.
  2. Compare it to a list of required services.
  3. Disable or remove everything else.
  4. Restrict and patch what remains.

2. Auditing listening services

nmap: the view from the network

nmap (Network Mapper) scans ports the way an attacker would. Scanning from the inside (loopback) and the outside (the server’s real address) shows what the firewall lets through.

$ nmap -sT 127.0.0.1                  # TCP ports, inside the firewall
$ nmap -sT 192.168.0.103              # TCP ports, as the network sees them
$ sudo nmap -sU 127.0.0.1             # UDP ports (requires root)
$ sudo nmap -sU -6 ::1                # IPv6
$ nmap -p 1-1024 192.168.0.103        # limit the port range
PORT    STATE SERVICE
22/tcp  open  ssh
25/tcp  open  smtp
111/tcp open  rpcbind
631/tcp open  ipp
  • PORT is the port number and protocol, STATE is open, closed, or filtered, and SERVICE is the name from /etc/services.
  • If a port is open on 127.0.0.1 but not on the external address, it’s either bound only to loopback or blocked by the firewall.
  • Legal and ethical caution: scan only systems you own or have written permission to test. Scanning other networks can violate law and school policy.

netstat and ss: the view from inside

$ netstat --tcp --listening           # deprecated; -u for UDP
$ ss -ltu                             # listening TCP and UDP sockets
$ sudo ss -ltunp                      # add numeric ports and owning processes
  • A port is a number that identifies a service, such as 22 for SSH or 53 for DNS.
  • A socket is one end of a connection: an IP address plus a port on the local system.
  • netstat is deprecated, and ss replaces it.
  • A Local Address of 0.0.0.0 or * means the service listens on every interface. 127.0.0.1 means local connections only.

systemd socket units

systemd can listen on a socket itself and start the service only when a connection arrives (socket activation). These sockets need auditing too.

$ systemctl list-sockets --all --no-pager --full
$ systemctl list-unit-files --type=socket
$ systemctl cat rpcbind.socket         # ListenStream=0.0.0.0:111 ...
$ man systemd.socket
  • ListenStream= is a TCP (or stream) socket, and ListenDatagram= is a UDP socket.
  • A socket unit that shows disabled only means systemd isn’t handling that socket. The service itself may still be running and listening on its own.

lsof and fuser: open files and processes

Linux treats network sockets as files, so file tools can find them. Both commands need sudo to see every process.

$ sudo lsof -iUDP                      # all UDP sockets
$ sudo lsof -iTCP -sTCP:LISTEN         # listening TCP sockets
$ sudo lsof -i tcp:22                  # everything using TCP port 22, including established sessions
$ sudo fuser -vn tcp 22                # PIDs and users using TCP port 22
$ sudo fuser -vn udp 822

Services that start at boot

$ systemctl list-unit-files --type=service --state=enabled    # systemd
$ chkconfig --list                                            # SysVinit, Red Hat
$ service --status-all                                        # SysVinit, Debian
$ ls /etc/init.d/                                             # SysVinit scripts

On very old systems, also check /etc/inittab.

3. Disabling unneeded services

systemd:

$ sudo systemctl stop cups
$ sudo systemctl disable cups          # or: disable --now to do both at once
$ systemctl is-enabled cups            # should print "disabled"
$ sudo systemctl mask cups             # optional: block it from being started at all

SysVinit:

# service cups stop ; service cups status
# chkconfig cups off ; chkconfig --list cups     # Red Hat: off at every runlevel
# update-rc.d -f cups remove                     # Debian

The best option is to remove a service you’ll never need by uninstalling its package (Lesson 2). A package that isn’t installed can’t be attacked or started by accident.

4. Super servers and TCP Wrappers (legacy access controls)

Super servers: inetd and xinetd

A super server listens on ports for several services and starts each service only when a connection arrives. The original was inetd; xinetd (the extended super daemon) added access control and logging. Most services now run as their own daemons or use systemd socket activation. You’ll mainly see xinetd on older systems, but it’s on the exam.

  • /etc/xinetd.conf holds the global defaults { } settings and ends with includedir /etc/xinetd.d.
  • /etc/xinetd.d/SERVICE has one file per service. Settings there override the defaults.
service echo
{
    disable      = yes          # yes = off; set to no to enable the service
    id           = echo-stream
    type         = INTERNAL
    wait         = no
    socket_type  = stream
    only_from    = 192.168.0.0/24
    instances    = 10
}
Directive Meaning
disable yes turns the service off
only_from / no_access Hosts or subnets allowed / denied
instances Maximum simultaneous processes for the service (UNLIMITED for no limit)
per_source Maximum processes per client IP address
cps Connection rate limit: 50 10 means pause for 10 seconds after 50 connections per second
max_load Refuse connections above this 1-minute load average
log_type SYSLOG facility severity or FILE path
log_on_success / log_on_failure Extra details to log (PID, HOST, USERID, DURATION, EXIT, ATTEMPT)

Restart xinetd after making changes.

TCP Wrappers

Programs linked with the libwrap library check two files before accepting a connection. Check whether a program supports it with ldd /usr/sbin/sshd | grep libwrap.

# /etc/hosts.allow
sshd: 172.243.24.15, 172.243.24.16
sshd: 172.243.24.            # trailing dot = the whole 172.243.24.0/24 subnet

# /etc/hosts.deny
ALL: ALL

Evaluation order:

  1. If a match is found in hosts.allow, access is allowed and nothing else is checked.
  2. Otherwise, if a match is found in hosts.deny, access is denied.
  3. If neither file matches, access is allowed.

Because of rule 3, a secure setup puts ALL: ALL in hosts.deny (default deny) and lists only approved clients in hosts.allow. PARANOID is a special pattern that matches hosts whose name and address don’t agree in DNS.

Current practice: TCP Wrappers are deprecated. Red Hat removed them in RHEL 8, and newer OpenSSH builds don’t include libwrap. Enforce network access with a firewall (firewalld, nftables, or ufw) and with each service’s own configuration, such as AllowUsers in sshd_config. Learn TCP Wrappers for the exam.

5. How Linux protects passwords

Hashing and salting

  • Passwords are stored as hashes, the output of a one-way function. Nobody can reverse a hash back into the password.
  • At login, the system hashes what you type with the same salt and compares the result to the stored hash.
  • Salt is random data added to each password before hashing. Because every account has a different salt, two identical passwords produce different hashes, and precomputed rainbow tables (lists of hashes for common passwords) become useless. The book implies rainbow tables still defeat salted hashes; the salt is what stops them.
  • The remaining threat is offline guessing against the hash. That’s why the hashes must stay unreadable and passwords must be long.

Protecting the hashes

$ ls -l /etc/passwd /etc/shadow
-rw-r--r-- 1 root root   2631 ... /etc/passwd     # everyone can read it; no hashes
-rw-r----- 1 root shadow 1425 ... /etc/shadow     # only root (and the shadow group) can read it
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root  59640 ... /usr/bin/passwd # s = SUID
  • Hashes are stored in /etc/shadow, not the world-readable /etc/passwd. If you find hashes in /etc/passwd, run pwconv.
  • Ordinary users can change their own passwords because passwd is SUID root (Lesson 4): it runs with root’s authority, only for that job. Changing another user’s password still requires sudo.
  • passwd doesn’t echo anything as you type, not even asterisks.

Troubleshooting a user who can’t log in

Check Command What to look for
Does the account exist? getent passwd JKirk A record with a valid login shell
Is a password set? sudo getent shadow JKirk ! or !! alone means no password has been set
Is it locked? sudo passwd -S KJaneway L or LK, and a ! in front of the hash in shadow
Has the account expired? sudo chage -l JArcher “Account expires” date in the past. Fix with chage -E.
Has the password expired? sudo chage -l “Password expires” and inactive dates
Is the user typing it correctly? — Usernames are case-sensitive. Check Caps Lock and the keyboard layout.
  • Unlock with usermod -U or passwd -u.
  • Follow current guidance (NIST SP 800-63B): favor length over complicated composition rules, and check new passwords against lists of known-compromised passwords.
  • Linux enforces password rules through PAM modules such as pam_pwquality (strength), pam_pwhistory (reuse), and pam_faillock (lock after failed attempts).

6. Limiting root access

Good account practice:

  • Don’t log in directly as root.
  • Give each person their own account and never share accounts.
  • Set expiration dates on temporary accounts (Lesson 7).
  • Remove accounts that are no longer used.

su (switch user)

$ su -                      # become root with a full login environment (needs root's password)
$ su - rich                 # become rich (needs rich's password)
$ su -c "passwd rich"       # run one command as root; quote a command that has spaces
$ exit
  • The - gives you the target user’s complete login environment (PATH, home directory, and startup files). Plain su keeps your own environment, which can cause subtle mistakes.
  • su requires the target account’s password, so root’s password has to be shared. On Ubuntu the root account is locked by default, so su - doesn’t work there.

sudo (run a command with elevated privileges)

sudo asks for your own password, checks your permissions in the sudoers policy, and logs every command. Because each action is tied to a named person, you get nonrepudiation: nobody can credibly deny what they did. When several administrators share the root password, no one can prove who ran a command, which is a repudiation environment.

$ sudo chage -l BCrusher
[sudo] password for Christine:
$ journalctl -r | grep sudo     # also /var/log/auth.log or /var/log/secure
... sudo[3609]: Christine : TTY=pts/0 ; PWD=/home/Christine ; USER=root ; COMMAND=/usr/bin/chage -l BCrusher

The sudoers policy

# /etc/sudoers  -- ALWAYS edit with visudo
root    ALL=(ALL:ALL) ALL
%sudo   ALL=(ALL:ALL) ALL          # Debian/Ubuntu admin group
%wheel  ALL=(ALL)     ALL          # Red Hat admin group
@includedir /etc/sudoers.d         # older files write this as #includedir

The format is WHO HOST=(RUN-AS-USER:RUN-AS-GROUP) COMMANDS:

  • WHO: a username, or %group for a group.
  • HOST: which hosts the rule applies to (ALL for any host).
  • (RUN-AS): which users and groups the person may run commands as.
  • COMMANDS: which commands, using full paths or ALL.
# /etc/sudoers.d/helpdesk  -- least privilege: only what the job requires
%helpdesk  ALL=(root) /usr/bin/passwd, /usr/bin/chage -l *
  • Always use visudo (or visudo -f /etc/sudoers.d/file). It locks the file and checks the syntax before saving. A syntax error in sudoers can lock every administrator out of sudo.
  • Grant full sudo by adding the person to the admin group: usermod -aG sudo alice (Debian/Ubuntu) or usermod -aG wheel alice (Red Hat).
  • Put custom rules in /etc/sudoers.d/ rather than editing the main file.
  • sudo -l lists what you’re allowed to run. sudo -i or sudo -s gives a root shell (still logged), but use it sparingly.

7. Auditing user access

$ who                     # who is logged in, on which terminal, since when, from where
$ who am i                # your own session (so does "who mom likes")
$ w
 09:58:31 up 49 min,  5 users,  load average: 0.81, 0.37, 0.27
USER     TTY    LOGIN@  IDLE   JCPU   PCPU  WHAT
rich     tty3   09:56   1:35   0.85s  0.81s top
$ last                    # login and logout history, newest first (from /var/log/wtmp)
$ last -f /var/log/wtmp.1 # a rotated wtmp file
$ sudo lastb              # failed login attempts (from /var/log/btmp)
$ lastlog                 # each account's most recent login
  • The first line of w shows the current time, how long the system has been up, the number of users, and the 1-, 5-, and 15-minute load averages.
  • The w columns:
    • USER is the account name.
    • TTY is the terminal.
    • LOGIN@ is when the user logged in.
    • IDLE is how long since the user last typed anything.
    • JCPU is total CPU time used by the terminal’s processes.
    • PCPU is CPU time used by the current process.
    • WHAT is the command the user is running now.
  • Data sources: who and w read /var/run/utmp (current sessions). last reads /var/log/wtmp (history), and lastb reads /var/log/btmp (failures). These files are rotated (Lesson 7).
  • Removing an intruder: lock the account (usermod -L), end its processes (pkill -KILL -u username), and create /etc/nologin (sudo touch /etc/nologin, optionally with a message inside) to block all non-root logins until you remove the file.

8. Resource limits

Limits keep a runaway program, or a malicious user, from using up every process slot, all the memory, or every file handle on the system.

$ ulimit -a                 # show all limits for this shell
$ ulimit -u 200             # at most 200 processes
$ ulimit -n 4096            # at most 4096 open files
$ ulimit -c 0               # no core dump files
$ ulimit -Sn / ulimit -Hn   # soft / hard limit for open files
Option Limit
-a Show all limits
-c Core file size
-d Data segment size
-e Scheduling priority
-f Size of files the shell can write
-i Pending signals
-l Locked memory
-m Resident set size
-n Open file descriptors
-p Pipe size (in 512-byte blocks)
-r Real-time priority
-s Stack size
-t CPU time in seconds
-u Number of user processes
-v Virtual memory
-x File locks
  • Soft vs. hard: a user can raise their soft limit up to the hard limit. Only root can raise a hard limit.
  • ulimit affects only the current shell and the programs it starts.
  • Persistent limits for users and groups belong in /etc/security/limits.conf or /etc/security/limits.d/*.conf, which PAM applies at login. The book suggests putting ulimit in startup files; limits.conf is the standard way.
# /etc/security/limits.conf
# domain     type   item      value
@students    hard   nproc     100
@students    hard   maxlogins 2
*            soft   core      0

The book’s option table says -p counts 512k blocks and lists a -k option. On Linux, -p counts 512-byte blocks, and -k (kqueues) exists only on BSD.

9. Finding SUID and SGID files

SUID and SGID programs run with the authority of the file’s owner or group (Lesson 4). A root-owned SUID file that shouldn’t exist is a classic backdoor. Build a baseline, then compare it regularly.

$ sudo find / -perm /6000 -type f 2>/dev/null > SUID-SGID_baseline.txt
$ sudo find / -perm /6000 -type f 2>/dev/null > SUID-SGID_today.txt
$ diff SUID-SGID_baseline.txt SUID-SGID_today.txt
> /usr/bin/threat-actor
$ ls -l /usr/bin/threat-actor
-rwsrwsrwx 1 root root 0 Jun 13 17:25 /usr/bin/threat-actor
  • /6000 means “SUID (4000) or SGID (2000) is set.” The leading / means any of these bits. The older +6000 form is obsolete.
  • -type f limits the search to regular files.
  • 2>/dev/null hides “Permission denied” noise.
  • Protect the baseline file (make it root-owned with mode 600), and automate the comparison with a script run by cron (Lesson 9).

10. Cryptography concepts

Encryption turns plaintext into ciphertext using an algorithm and a key. Decryption reverses it.

Symmetric (secret key) Asymmetric (public/private key pair)
Keys One shared key encrypts and decrypts A public key (shared freely) and a private key (kept secret, usually protected by a passphrase)
Speed Fast Slower
Main problem You must share the key securely Proving whose public key you really have
Examples AES RSA, ECDSA, Ed25519

The book calls symmetric keys “private keys.” Call them secret keys in class, so students don’t confuse them with the private half of a key pair.

Rule of thumb:

  • To send someone a secret, encrypt with the recipient’s public key. Only their private key can decrypt it.
  • To prove you wrote something, sign with your own private key. Anyone with your public key can verify it.

Real protocols such as SSH and TLS combine both types: asymmetric keys authenticate the parties and agree on a random symmetric session key, which then encrypts all the traffic quickly.

Hashing

  • A hash (message digest, fingerprint) is a fixed-length, one-way summary of data. The same input always produces the same hash, so comparing hashes shows whether two files are identical or whether a download was corrupted or altered.
  • The algorithm must be collision-resistant: two different inputs mustn’t produce the same hash. MD5 and SHA-1 aren’t collision-resistant anymore. Use SHA-256 or stronger.
$ sha256sum ubuntu.iso           # compare with the published checksum
  • A keyed message digest (HMAC) mixes a secret key into the hash. It proves the data wasn’t changed and that it came from someone who has the key. SSH uses HMACs to protect every packet.

Digital signatures

  1. The sender hashes the message and encrypts the hash with the sender’s private key. That’s the signature.
  2. The receiver decrypts the signature with the sender’s public key and hashes the received message.
  3. If the two hashes match, the message is authentic (it came from the owner of that private key) and unchanged.

Signatures defend against a man-in-the-middle who substitutes their own message or key. You still have to be sure that the public key really belongs to the sender, which is why SSH shows host key fingerprints and GPG uses key trust.

11. SSH: secure remote access

SSH (Secure Shell) encrypts logins, commands, and file transfers, replacing insecure tools such as telnet, rsh, and ftp. OpenSSH is the standard implementation.

  • Packages: openssh-clients and openssh-server (Red Hat), or openssh-client and openssh-server (Debian and Ubuntu).
  • Service: sshd (called ssh on Ubuntu). The server must be running and TCP port 22 open in the firewall.

Connecting and host keys

$ ssh Christine@192.168.0.105
The authenticity of host '192.168.0.105' can't be established.
ED25519 key fingerprint is SHA256:BnaCbm+ensyrkflKk1rRSVwxHi4NrBWOOSOdU+14m7w.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.105' (ED25519) to the list of known hosts.
  • The syntax is ssh [options] [user@]host. Leave out user@ if your username is the same on both systems.
  • The first time you connect, SSH shows the server’s host key fingerprint. Verify it through a separate, trusted channel before typing yes. The key is then saved in ~/.ssh/known_hosts.
  • “WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED” means the server’s key doesn’t match the saved one. It may be a reinstalled server, or it may be an attack. Investigate before continuing. Remove the old entry with ssh-keygen -R host only after confirming the change is legitimate.
  • /etc/ssh/ssh_known_hosts is a system-wide list of trusted host keys. Pre-loading it means users see a warning only when something has really changed, which keeps them from learning to ignore warnings.

Remote commands and file copies

$ ssh Christine@192.168.0.104 "ls Project42.txt"      # run one command remotely
$ scp Project42.txt Christine@192.168.0.104:~          # copy to the remote home directory
$ scp Christine@192.168.0.104:/etc/hosts .             # copy from the remote system
$ scp -r labs/ Christine@192.168.0.104:backup/         # copy a directory
$ sftp Christine@192.168.0.104                         # interactive transfers
$ ssh -p 1138 192.168.0.104                            # non-default port

The colon matters. scp file user@host: copies to the remote host. Without the colon, scp makes a local copy named user@host.

Configuration files

File Purpose
~/.ssh/config Your personal client settings (overrides the system client file)
/etc/ssh/ssh_config Client settings for everyone on this machine
/etc/ssh/sshd_config Server settings (restart or reload sshd after changes)

Command-line options override ~/.ssh/config, which overrides /etc/ssh/ssh_config.

# ~/.ssh/config
Host lab
    HostName 192.168.0.104
    User Christine
    Port 1138

With that entry, ssh lab is all you need to type.

Hardening sshd_config

Directive Recommended setting and reason
PermitRootLogin no. Root is the first account attackers guess. Log in as yourself and use sudo. (The current OpenSSH default is prohibit-password, which allows root to log in with a key only. The book gives the default as yes.)
PasswordAuthentication no once everyone uses keys. This stops password-guessing attacks.
AllowUsers / AllowGroups Only the listed accounts may log in over SSH
Port Changing it from 22 reduces automated scanning noise, but it isn’t real protection. A firewall and keys matter more.
X11Forwarding no unless people need it
AllowTcpForwarding no unless people need tunnels
  • Protocol 1: SSH protocol 1 is broken. Current OpenSSH supports only protocol 2, and the old Protocol directive has been removed. The book’s advice to set Protocol 2 applies only to very old systems.
  • Test before you disconnect. Check the file with sudo sshd -t, reload the service, and confirm that a second session can log in before closing the one you’re using.
  • ForwardX11 is a client option in ssh_config. The server’s option is X11Forwarding.

Host keys

$ ls /etc/ssh/*key*
/etc/ssh/ssh_host_ecdsa_key      /etc/ssh/ssh_host_ecdsa_key.pub
/etc/ssh/ssh_host_ed25519_key    /etc/ssh/ssh_host_ed25519_key.pub
/etc/ssh/ssh_host_rsa_key        /etc/ssh/ssh_host_rsa_key.pub
$ sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key    # regenerate one type
$ sudo ssh-keygen -A                                             # create any missing host keys
  • The files are named ssh_host_TYPE_key (private) and ssh_host_TYPE_key.pub (public).
  • Key types:
    • ed25519 is the modern default.
    • ecdsa is also current.
    • rsa is widely supported (use 3072 bits or more).
    • dsa is obsolete and removed from current OpenSSH.
  • Permissions: private host keys must be readable only by root (0600, or 0640 with a special group on some distributions). Public keys are world-readable. sshd refuses to use a private key with loose permissions.

Key-based user authentication

$ ssh-keygen -t ed25519                     # creates ~/.ssh/id_ed25519 and id_ed25519.pub; set a passphrase
$ ssh-copy-id -n Christine@192.168.0.104    # dry run: show which keys would be installed
$ ssh-copy-id Christine@192.168.0.104       # append the public key to the server's ~/.ssh/authorized_keys
$ ssh Christine@192.168.0.104               # now authenticates with the key
  1. Generate a key pair on the client.
  2. Install the public key in the server account’s ~/.ssh/authorized_keys. ssh-copy-id does this securely in one step. Without it, copy the .pub file with scp and append it with cat id_ed25519.pub >> ~/.ssh/authorized_keys.
  3. The private key never leaves the client.
  • Permissions: ~/.ssh must be 700, and the private key and authorized_keys should be 600. OpenSSH ignores keys whose permissions are too open.
  • Default file names: id_ed25519, id_ecdsa, or id_rsa in ~/.ssh/.
  • Key files without a passphrase are convenient, but anyone who copies the file can use it. Use a passphrase together with an agent.

ssh-agent

The agent holds your decrypted private key in memory, so you type the passphrase once per session.

$ eval "$(ssh-agent)"                 # start an agent in this shell (the book uses: ssh-agent /bin/bash)
$ ssh-add ~/.ssh/id_ed25519           # enter the passphrase once
$ ssh-add -l                          # list the loaded keys
$ ssh Christine@192.168.0.104         # no password or passphrase prompt
$ ssh-add -d ~/.ssh/id_ed25519        # remove the key from the agent

Most desktop sessions start an agent automatically. When the agent session ends, you have to add the key again.

Tunneling (port forwarding)

SSH can carry other traffic through its encrypted connection:

$ ssh -X user@remote-host                         # X11 forwarding: run remote GUI programs (Lesson 6)
$ ssh -L 5901:localhost:5901 user@server          # local forward: localhost:5901 -> server's 5901 (VNC)
$ ssh -R 8080:localhost:80 user@server            # remote forward: server's 8080 -> your port 80
  • X11 forwarding requires X11Forwarding yes on the server. The book’s listing caption calls this “AllowTCPForwarding,” but the directive checked is X11Forwarding.
  • Avoid ssh -Y (trusted X11). It gives the remote host broad access to your display.
  • Port forwarding requires AllowTcpForwarding yes on the server.

12. GPG: encrypting and signing files

GNU Privacy Guard (GPG, GnuPG) implements the OpenPGP standard (based on PGP). It encrypts and signs individual files and email. The command is gpg, or gpg2 on some older systems.

Keys

$ gpg --gen-key                      # or --full-generate-key to choose type, size, and expiration
$ gpg --list-keys                    # public keys on your keyring
$ gpg --list-secret-keys             # your private (secret) keys
  • GPG asks for your name, email address, and a passphrase. The email address identifies the key, and the passphrase protects the private key.
  • Keys are stored on your keyring in ~/.gnupg/.
  • gpg-agent starts automatically to cache your unlocked private key, so no service needs to be enabled.
  • In --list-keys output, pub is the primary key ([SC] = sign and certify) and sub is a subkey ([E] = encrypt). The uid line shows the owner and your trust level ([ultimate] for your own key, [unknown] for others).
  • Key generation needs random data. Modern kernels provide plenty. On old systems, the book suggests running rngd to speed this up.

Sharing public keys

$ gpg --export cb1234@example.edu > CB_gpg.pub            # binary format
$ gpg --export --armor cb1234@example.edu > CB_gpg.asc    # ASCII, easy to email or post
$ gpg --import RB_gpg.pub                                 # add someone else's public key
$ gpg --fingerprint rb4242@example.com                    # compare with the owner by phone or in person

Public keys aren’t secret. You can email them or post them on a web page or key server. Verify the fingerprint before trusting an imported key; otherwise, you might be using an attacker’s key.

Encrypting and decrypting

$ gpg --out encryptfile --recipient rb4242@example.com --encrypt secretfile.txt
$ gpg --out message.txt --decrypt encryptfile              # the recipient enters their passphrase
  • “It’s all about the recipient.” To send someone a secret file, encrypt it with their public key (--recipient). They decrypt it with their private key.
  • Add --armor for ASCII output that can be pasted into email.
  • gpg -c file is symmetric encryption with a passphrase only, for when both people know the passphrase.

Signing and verifying

$ gpg --sign file.txt                        # binary signed file (file.txt.gpg)
$ gpg --clearsign note.txt                   # readable text with an ASCII signature block
$ gpg --detach-sign release.tar.gz           # separate signature file: release.tar.gz.sig
$ gpg --verify release.tar.gz.sig release.tar.gz
$ gpg --sign --encrypt -r rb4242@example.com report.pdf   # sign and encrypt in one step
$ gpg --decrypt report.pdf.gpg > report.pdf               # decrypts and checks the signature
  • A signature uses your private key (you’ll be asked for your passphrase). Verifying it needs your public key on the recipient’s keyring. Otherwise they see “Can’t check signature: No public key.”
  • “Good signature from …” means the file is authentic and unchanged.
  • The book encrypts and signs in two separate steps and verifies in two steps. The single --sign --encrypt command above, followed by --decrypt, is the normal practice.
  • Detached signatures are how Linux distributions sign packages and ISO images. The file itself stays readable.

Revoking a key

If your private key is lost or stolen:

$ gpg --output key-revocation.asc --gen-revoke cb1234@example.edu   # choose the reason: 1 = compromised
$ gpg --import key-revocation.asc                                    # revoke it on your own keyring

Then distribute the certificate to everyone who has your public key: upload it to the key server or web page where you published the key. They import it the same way.

Best practice: create a revocation certificate when you create the key, and store it offline, so you can still revoke the key if you lose the passphrase. Modern GnuPG does this automatically in ~/.gnupg/openpgp-revocs.d/.

Classroom application

  1. Attack-surface audit. On a lab VM, compare nmap -sT 127.0.0.1 with a scan of the VM’s own address from a second VM, sudo ss -ltunp, and systemctl list-sockets. Build a table of each listening service, whether it’s needed, and what to do about it. Disable one unneeded service and show that it no longer appears.
  2. sudo policy. Create a helpdesk group, write /etc/sudoers.d/helpdesk with visudo -f so it allows only passwd and chage -l, test allowed and denied commands with sudo -l, and find the log entries in the journal.
  3. Login detective. Create an account without a password, a locked account, and an expired account. Have a partner diagnose each one using only getent, passwd -S, and chage -l.
  4. SUID baseline. Create a SUID baseline, then (on a lab VM) set SUID on a copy of a harmless binary, rerun the scan, and find the difference with diff. Remove the test file afterward.
  5. SSH keys and hardening. Generate an ed25519 key with a passphrase, install it with ssh-copy-id, load it into ssh-agent, and connect without a password. Then set PermitRootLogin no and PasswordAuthentication no, check the file with sshd -t, reload, and confirm key login still works from a new terminal before closing the old one.
  6. GPG exchange. Partners generate keys, exchange public keys as ASCII files, compare fingerprints aloud, send each other an encrypted and signed file, verify and decrypt it, and then change one byte of a signed file to watch verification fail.

Common misconceptions

  • “Disabling a service in systemd removes the risk.” disable only stops it from starting at boot. Stop it now, consider mask, and uninstall it if it’s never needed.
  • “An empty hosts.deny is secure.” When no rule matches, TCP Wrappers allow access. Use ALL: ALL in hosts.deny. Better yet, use a firewall.
  • “Salted hashes can be cracked with rainbow tables.” Salt is what defeats rainbow tables. The remaining risk is brute-force guessing against stolen hashes.
  • “su and sudo are equally secure.” su requires the shared root password and gives no per-person accountability. sudo uses your own password and logs every command.
  • “Edit /etc/sudoers with any editor.” Use visudo. A syntax error can lock out every administrator.
  • “Changing the SSH port secures SSH.” It only reduces noise. Keys, PermitRootLogin no, AllowUsers, and a firewall provide the real protection.
  • “Ignore the ‘host identification has changed’ warning.” It may be a man-in-the-middle attack. Verify the fingerprint first.
  • “Encrypt with your own public key to send a secret.” Encrypt with the recipient’s public key. Sign with your own private key.
  • “Encryption alone proves who sent a file.” Only a signature proves the sender and that the file wasn’t changed.
  • “MD5 is fine for integrity checks.” MD5 and SHA-1 have known collisions. Use SHA-256 or stronger.

Lesson summary

  • Network security: audit with nmap, ss/netstat, systemctl list-sockets, lsof, and fuser. Disable or remove unneeded services. xinetd and TCP Wrappers (hosts.allow is checked before hosts.deny, and ALL: ALL makes access default-deny) are legacy access controls; firewalls are the modern replacement.
  • Local security: salted hashes in root-only /etc/shadow, with the SUID passwd program for self-service changes. Troubleshoot logins with getent, passwd -S, and chage -l. Use sudo (edited with visudo, rules in /etc/sudoers.d) for accountable root access. Audit sessions with who, w, last, and lastb, block logins with /etc/nologin, set limits with ulimit and limits.conf, and compare SUID/SGID scans (find / -perm /6000 -type f) against a baseline.
  • Cryptography: symmetric keys are fast but must be shared. Asymmetric key pairs use public keys for encryption and verification and private keys for decryption and signing. Hashes show integrity, HMACs add a shared key, and signatures prove authenticity.
  • SSH: check host keys in known_hosts, use scp and remote commands, understand the client and server configuration files, harden sshd_config, manage host keys, use key authentication with ssh-keygen, ssh-copy-id, and authorized_keys, use ssh-agent with ssh-add, and forward ports and X11.
  • GPG: generate, list, export, and import keys; encrypt with the recipient’s public key and decrypt with your private key; sign, clearsign, detach-sign, and verify; and revoke compromised keys.

This lesson completes the course’s coverage of the LPIC-1 topics. Its tools and habits (least privilege, auditing against a baseline, verifying before trusting, and encrypting in transit) apply to everything students learned in Lessons 1 through 9.