ISO/IEC 27001 is the international standard for an Information Security Management System (ISMS). Its 2022 revision reorganized Annex A into 93 controls in four themes: organizational (5.x), people (6.x), physical (7.x) and technological (8.x). In this tutorial you will implement the controls that apply directly to an Ubuntu 24.04 server and produce evidence an auditor can review. Each step names the Annex A control it supports.
NoteOrganizations certified against ISO 27001:2013 had until October 2025 to transition, so the old A.9, A.10 and A.12 numbering no longer applies. Use the 2022 control numbers in your Statement of Applicability.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS that is inside your ISMS scope, for example a CubePath VPS.
- A non-root user with
sudoprivileges and SSH key authentication already working. - A completed risk assessment and Statement of Applicability, so you know which controls are required.
- A second host reachable over SSH (SFTP) to store backups.
Step 1 - Registering users with expiry dates (5.16 and 5.18)
Control 5.16 (identity management) requires the full life cycle of identities to be managed, and 5.18 (access rights) requires access to be provisioned, reviewed and removed. Create each account with the person's name and an expiry date that matches their contract. Replace the values with your own:
sudo adduser --gecos "Alice Smith,Operations" alice
sudo chage -E 2027-06-30 alice
Check the account aging information:
sudo chage -l alice
Last password change : Sep 25, 2026
Password expires : never
Password inactive : never
Account expires : Jun 30, 2027
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
Keep a register (ticket or spreadsheet) with the business justification and approver for each account. The server shows who has access; the register shows why.
Step 2 - Restricting privileged access by role (8.2)
Control 8.2 (privileged access rights) requires privileges to be restricted and allocated per role. Instead of adding everyone to the sudo group, create a group per role and grant only the commands that role needs. Create the groups:
sudo groupadd ops
sudo groupadd dba
sudo usermod -aG ops alice
Create a sudoers file for the operations role. Always use visudo, which checks the syntax before saving:
sudo visudo -f /etc/sudoers.d/10-ops
%ops ALL=(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status *, /usr/bin/journalctl
And one for database administrators:
sudo visudo -f /etc/sudoers.d/20-dba
%dba ALL=(root) /usr/bin/systemctl restart mysql, /usr/bin/systemctl status mysql
Verify what a user is allowed to run:
sudo -l -U alice
User alice may run the following commands on server1:
(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status *, /usr/bin/journalctl
Review group membership at least quarterly with getent group sudo ops dba and record the review.
Step 3 - Securing log-on (8.5 and 5.17)
Control 8.5 (secure authentication) and 5.17 (authentication information) require strong authentication and protection of credentials. Enforce key-based SSH and a login banner. Create a drop-in file; the 10- prefix makes it take precedence over 50-cloud-init.conf, because the first value read wins:
sudo nano /etc/ssh/sshd_config.d/10-iso27001.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
LogLevel VERBOSE
Banner /etc/issue.net
Write the banner text:
sudo nano /etc/issue.net
Authorized users only. Activity on this system is logged and monitored
in accordance with the information security policy.
Validate and reload:
sudo sshd -t
sudo systemctl reload ssh
Passwords are still used for sudo, so enforce a quality policy:
sudo apt install libpam-pwquality
sudo nano /etc/security/pwquality.conf
minlen = 14
minclass = 3
dictcheck = 1
Test from a second terminal that key login still works before you close your session.
Step 4 - Applying cryptography to data in transit and at rest (8.24)
Control 8.24 (use of cryptography) requires rules for using cryptography, including key management. For web services, allow only TLS 1.2 and 1.3. In each Nginx server block listening on 443:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
Reload and test that old protocols are refused (replace your_domain):
sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect your_domain:443 -tls1_1 < /dev/null
The second command must fail with a protocol error. For data at rest, list block devices and look for crypto_LUKS:
lsblk -f
For each LUKS device, confirm it uses LUKS2 and back up its header, which is part of key management:
sudo cryptsetup luksDump /dev/vdb | head -n 3
sudo cryptsetup luksHeaderBackup /dev/vdb --header-backup-file /root/vdb-luks-header.img
LUKS header information
Version: 2
Epoch: 3
Move the header backup to offline, access-controlled storage and delete it from the server.
Step 5 - Logging events and synchronizing clocks (8.15, 8.16 and 8.17)
Control 8.15 (logging) requires logs of activities and exceptions, 8.16 (monitoring activities) requires them to be reviewed, and 8.17 (clock synchronization) requires consistent time so events can be correlated. Check time sync first:
timedatectl
System clock synchronized: yes
NTP service: active
If NTP service is inactive, run sudo timedatectl set-ntp true. Next, install auditd:
sudo apt install auditd
sudo nano /etc/audit/rules.d/iso27001.rules
## Identity and privilege changes
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k privilege
-w /etc/sudoers.d/ -p wa -k privilege
-w /etc/ssh/sshd_config.d/ -p wa -k sshd
## Administrator actions
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k admin_cmd
Load and verify:
sudo augenrules --load
sudo auditctl -l
Set retention in /etc/audit/auditd.conf according to your log policy, for example:
max_log_file = 100
num_logs = 12
max_log_file_action = ROTATE
Apply it with sudo service auditd restart. A monthly summary is useful evidence of review:
sudo aureport --summary --start this-month
sudo aureport -au --failed --start this-month | tail -n 10
For 8.15, logs must also be protected from tampering, so forward them to a central server your administrators cannot modify.
Step 6 - Managing vulnerabilities and malware (8.8 and 8.7)
Control 8.8 (management of technical vulnerabilities) requires timely patching. Enable automatic security updates:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Check what is pending and whether a reboot is required after kernel updates:
apt list --upgradable 2>/dev/null
ls /var/run/reboot-required 2>/dev/null && cat /var/run/reboot-required.pkgs
Control 8.7 (protection against malware) is usually met on Linux servers by hardening and integrity monitoring, but if the server accepts file uploads, scan them with ClamAV:
sudo apt install clamav clamav-freshclam
sudo systemctl status clamav-freshclam --no-pager
clamav-freshclam keeps signatures updated. Scan the upload directory and print only infected files:
sudo clamscan -ri /var/www/uploads
----------- SCAN SUMMARY -----------
Scanned files: 214
Infected files: 0
Step 7 - Backing up information (8.13)
Control 8.13 (information backup) requires backups to be taken and tested according to an agreed policy. Install restic and create a password file:
sudo apt install restic
sudo mkdir -p /etc/restic
sudo sh -c 'openssl rand -base64 32 > /etc/restic/password'
sudo chmod 600 /etc/restic/password
Store the password in your password manager. Initialize the repository on your backup host:
sudo restic -r sftp:backupuser@backup_host:/srv/restic/server1 --password-file /etc/restic/password init
Create a service that runs the backup, applies retention and verifies the repository:
sudo nano /etc/systemd/system/restic-backup.service
[Unit]
Description=restic backup
[Service]
Type=oneshot
Environment=RESTIC_REPOSITORY=sftp:backupuser@backup_host:/srv/restic/server1
Environment=RESTIC_PASSWORD_FILE=/etc/restic/password
ExecStart=/usr/bin/restic backup /etc /home /var/www
ExecStart=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
ExecStart=/usr/bin/restic check
Schedule it daily:
sudo nano /etc/systemd/system/restic-backup.timer
[Unit]
Description=Daily restic backup
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -n 5 --no-pager
Test a restore at least quarterly and record the result:
sudo restic -r sftp:backupuser@backup_host:/srv/restic/server1 --password-file /etc/restic/password restore latest --target /tmp/restore-test --include /etc
sudo rm -rf /tmp/restore-test
Step 8 - Securing the network (8.20 and 8.22)
Control 8.20 (networks security) and 8.22 (segregation of networks) require only necessary services to be reachable. Find services listening on all interfaces:
sudo ss -tlnp | grep -E '0\.0\.0\.0|\[::\]'
Internal services such as MySQL or Redis should listen on 127.0.0.1 or a private network only. For MySQL, set bind-address = 127.0.0.1 in /etc/mysql/mysql.conf.d/mysqld.cnf and restart it. Then close everything else at the firewall:
sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status numbered
Step 9 - Controlling supplier access (5.19 and 5.20)
Controls 5.19 and 5.20 cover information security in supplier relationships. Give vendors a separate, time-limited account that expires automatically, without sudo:
sudo adduser --gecos "Vendor Support,Acme Ltd,ticket 1234" vendor-acme
sudo chage -E 2026-10-31 vendor-acme
Because the audit rules log every command run as root, and LogLevel VERBOSE records which SSH key was used, vendor activity is traceable. Confirm the expiry date:
sudo chage -l vendor-acme | grep "Account expires"
Account expires : Oct 31, 2026
After that date the account can no longer log in, even if nobody remembers to remove it. Delete it during your next access review.
Step 10 - Collecting evidence for auditors (5.28 and 5.36)
Control 5.36 (compliance with policies and standards) requires regular review, and auditors will ask for proof. A small script that runs monthly is the right tool here. Create it:
sudo nano /usr/local/sbin/iso27001-evidence
#!/usr/bin/env bash
set -euo pipefail
outdir="/var/lib/iso27001-evidence/$(date +%Y-%m)"
install -d -m 700 "$outdir"
getent group sudo ops dba > "$outdir/privileged-groups.txt"
sudo -l -U alice > "$outdir/sudo-alice.txt" 2>&1 || true
sshd -T > "$outdir/sshd-effective.txt"
ufw status verbose > "$outdir/firewall.txt"
ss -tulpn > "$outdir/listening-ports.txt"
lsblk -f > "$outdir/block-devices.txt"
auditctl -l > "$outdir/audit-rules.txt"
aureport --summary --start this-month > "$outdir/audit-summary.txt"
apt list --upgradable > "$outdir/pending-updates.txt" 2>/dev/null
systemctl list-timers --all > "$outdir/timers.txt"
sha256sum "$outdir"/*.txt > "$outdir/SHA256SUMS"
echo "Evidence written to $outdir"
Adapt the sudo -l line to the users you want to document. Make it executable and run it once:
sudo chmod 750 /usr/local/sbin/iso27001-evidence
sudo /usr/local/sbin/iso27001-evidence
Evidence written to /var/lib/iso27001-evidence/2026-09
Schedule it on the first day of each month with a systemd timer:
sudo nano /etc/systemd/system/iso27001-evidence.service
[Unit]
Description=Collect ISO 27001 evidence
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/iso27001-evidence
sudo nano /etc/systemd/system/iso27001-evidence.timer
[Unit]
Description=Monthly ISO 27001 evidence collection
[Timer]
OnCalendar=monthly
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now iso27001-evidence.timer
systemctl list-timers iso27001-evidence.timer
For incidents (5.28, collection of evidence), preserve volatile data first: w, ps auxwwf and ss -tupan, then logs, and hash everything with sha256sum before copying it off the server.
Troubleshooting
visudo reports a syntax error. Do not save the file; visudo offers to re-edit it. A broken file in /etc/sudoers.d/ can disable sudo for everyone, which is why you should never edit these files with a normal editor.
The SSH banner does not appear. Run sudo sshd -T | grep banner. If it shows none, another file in /etc/ssh/sshd_config.d/ with an earlier name sets it first.
TLS 1.1 is still accepted. Another server block or an included file sets ssl_protocols. Search with sudo grep -rn ssl_protocols /etc/nginx/ and make all of them consistent.
Conclusion
You implemented the core ISO 27001:2022 technological controls on Ubuntu 24.04: role-based privileged access, secure authentication, cryptography, logging with synchronized time, patching and malware checks, tested backups, network restrictions, supplier accounts that expire, and a monthly evidence collection job. Next, reference each control and its evidence path in your Statement of Applicability, forward logs to a central SIEM, and schedule the quarterly access and restore reviews as recurring tasks.
