ISO 27001 Server Security Controls

ISO 27001 is the international standard for Information Security Management Systems (ISMS), requiring organizations to implement Annex A controls that protect information assets on Linux servers. This guide covers the practical implementation of ISO 27001 access control, cryptography, operations security, communications security, and incident management controls on Linux infrastructure.

Prerequisites

  • Linux server (Ubuntu 20.04+ or CentOS/Rocky 8+) with root access
  • ISO 27001 Annex A reference document
  • A designated Information Security Officer or ISMS owner
  • Existing ISMS scope definition and risk assessment completed

Access Control (A.9)

ISO 27001 Annex A.9 requires formal user registration, access provisioning, and privileged access management.

# A.9.2.1 - User registration and de-registration
# Document every account with a business justification

# Create accounts with full name in GECOS field for auditability
sudo useradd -m -c "Alice Smith - Developer - Ticket#2026-001" -s /bin/bash alice
sudo passwd alice

# Set expiry aligned with contract/employment period
sudo chage -E "2026-12-31" alice
sudo chage -l alice

Privileged access management (A.9.2.3):

# Restrict sudo to specific commands per role
sudo tee /etc/sudoers.d/iso27001-roles << 'EOF'
# Developer role: read-only system access
%developers ALL=(ALL) NOPASSWD: /usr/bin/journalctl, /usr/bin/systemctl status *

# Operations role: service management
%operations ALL=(ALL) PASSWD: /usr/bin/systemctl, /usr/bin/apt

# DBA role: only database commands
%dba ALL=(ALL) NOPASSWD: /usr/bin/mysql, /usr/bin/pg_dump
EOF

sudo chmod 440 /etc/sudoers.d/iso27001-roles

Enforce strong authentication (A.9.4.3):

# Password policy
sudo apt install libpam-pwquality   # Ubuntu

sudo tee /etc/security/pwquality.conf << 'EOF'
minlen = 12
dcredit = -1
ucredit = -1
ocredit = -1
lcredit = -1
maxrepeat = 3
usercheck = 1
gecoscheck = 1
EOF

# Account lockout after failed attempts (A.9.4.2)
# Ubuntu - add to /etc/pam.d/common-auth:
sudo sed -i '1s/^/auth required pam_tally2.so onerr=fail audit silent deny=5 unlock_time=1800\n/' \
  /etc/pam.d/common-auth

SSH configuration aligned with A.9.4:

sudo tee /etc/ssh/sshd_config.d/iso27001.conf << 'EOF'
# A.9.4.2 - Secure log-on procedures
PermitRootLogin no
PasswordAuthentication no
MaxAuthTries 3
LoginGraceTime 30
Banner /etc/issue.net

# A.9.4.3 - Password management
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# Session controls
ClientAliveInterval 600
ClientAliveCountMax 2
X11Forwarding no
EOF

sudo tee /etc/issue.net << 'EOF'
This system is the property of [Organization Name].
Unauthorized access is prohibited. All activity is monitored and logged.
Access to this system is restricted to authorized personnel only.
EOF

sudo systemctl reload sshd

Cryptography Controls (A.10)

A.10 requires a cryptography policy covering key management and approved algorithms.

# A.10.1.1 - Policy on cryptographic controls
# Verify TLS configuration
openssl s_client -connect yourdomain.com:443 2>/dev/null | grep -E "Protocol|Cipher"

# Generate strong certificate (minimum 2048-bit RSA or 256-bit ECC)
openssl ecparam -name secp256r1 -genkey -noout -out /etc/ssl/private/server.key
openssl req -new -x509 -key /etc/ssl/private/server.key \
  -out /etc/ssl/certs/server.crt -days 365 \
  -subj "/C=US/ST=State/O=Company/CN=server.example.com"
chmod 600 /etc/ssl/private/server.key

Disk encryption for data at rest (A.10.1):

# Verify LUKS encryption on data volumes
sudo cryptsetup luksDump /dev/sdb

# Check encryption headers
sudo cryptsetup luksDump /dev/sdb | grep -E "Cipher|Hash|Key"

# Add a recovery key (backup key for ISMS disaster scenarios)
sudo cryptsetup luksAddKey /dev/sdb /etc/keys/recovery.key

GPG for file-level encryption:

# Encrypt sensitive configuration files
gpg --batch --passphrase-file /etc/keys/config.pass \
    --symmetric --cipher-algo AES256 \
    /etc/app/secrets.conf

# A.10.1.2 - Key management: backup encryption keys securely
gpg --export-secret-keys --armor [email protected] > /secure/backup/gpg-private-key.asc
# Store this backup in a physically secure, offline location

Operations Security (A.12)

A.12 covers logging, monitoring, change management, and malware protection.

# A.12.4 - Logging and monitoring
sudo apt install auditd   # Ubuntu
sudo dnf install audit    # CentOS/Rocky
sudo systemctl enable --now auditd

sudo tee /etc/audit/rules.d/iso27001.rules << 'EOF'
# A.12.4.1 - Event logging
-w /var/log -p wa -k log_changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k privilege

# A.12.4.3 - Administrator and operator logs
-a always,exit -F arch=b64 -S execve -F euid=0 -k admin_commands

# A.12.6 - Management of technical vulnerabilities
-w /usr/bin/dpkg -p x -k package_install
-w /usr/bin/apt -p x -k package_install
-w /usr/bin/rpm -p x -k package_install
-w /usr/bin/yum -p x -k package_install
EOF

sudo augenrules --load

Anti-malware controls (A.12.2):

# Install ClamAV
sudo apt install clamav clamav-daemon   # Ubuntu
sudo dnf install clamav clamd           # CentOS/Rocky

# Update signatures
sudo freshclam

# Schedule daily scan of web/upload directories
sudo tee /etc/cron.daily/clamav-scan << 'EOF'
#!/bin/bash
clamscan -r /var/www /var/uploads --log=/var/log/clamav/daily-scan.log --quiet
EOF
sudo chmod +x /etc/cron.daily/clamav-scan

Backup procedures (A.12.3):

# Configure restic backup with ISO 27001-required retention
sudo apt install restic

restic -r sftp:backup@backup-server:/backups/$(hostname) init \
  --password-file /etc/restic/password

# Backup script with retention policy
sudo tee /usr/local/bin/iso27001-backup.sh << 'SCRIPT'
#!/bin/bash
export RESTIC_REPOSITORY="sftp:backup@backup-server:/backups/$(hostname)"
export RESTIC_PASSWORD_FILE="/etc/restic/password"

restic backup /etc /var/www /var/data --tag iso27001
restic check --read-data-subset=10%   # Verify 10% of data each run
restic forget --keep-daily 30 --keep-weekly 12 --keep-monthly 24 --prune
SCRIPT

sudo chmod +x /usr/local/bin/iso27001-backup.sh

Communications Security (A.13)

A.13 requires network controls and information transfer policies.

# A.13.1.1 - Network controls
# Firewall configuration
sudo ufw default deny incoming
sudo ufw default deny forward
sudo ufw allow ssh
sudo ufw allow 443/tcp
sudo ufw enable

# Network segmentation - ensure internal services only bind to loopback
# Check what's listening on 0.0.0.0 (all interfaces)
ss -tlnp | grep '0.0.0.0'

# Bind MySQL to loopback only
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
# Set: bind-address = 127.0.0.1

TLS enforcement for all services (A.13.2.1):

# Check TLS versions in use across services
for port in 443 8443 8080; do
    echo "Port $port:"
    openssl s_client -connect localhost:$port 2>/dev/null | grep "Protocol"
done

# Disable SSLv3 and TLS 1.0/1.1 in Nginx
sudo tee /etc/nginx/conf.d/ssl-policy.conf << 'EOF'
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=63072000" always;
EOF

Supplier and Third-Party Access (A.15)

Control third-party and vendor access to your servers:

# Create time-limited accounts for vendors
sudo useradd -m -e "2026-06-30" -c "Vendor: Acme Support - Contract#V2026-001" vendor-acme
sudo passwd vendor-acme

# Restrict to specific directory
sudo usermod -d /var/vendor/acme vendor-acme

# Log all vendor activity
sudo tee /etc/profile.d/vendor-logging.sh << 'EOF'
#!/bin/bash
if [[ "$USER" == vendor-* ]]; then
    LOGFILE="/var/log/vendor-sessions/$(date +%Y%m%d_%H%M%S)_${USER}.log"
    mkdir -p "$(dirname "$LOGFILE")"
    script -q -f "$LOGFILE"
fi
EOF

# Review vendor access quarterly
last -n 100 | grep vendor | tee /var/log/iso27001/vendor-access-review.txt

Information Security Incident Management (A.16)

# A.16.1.2 - Reporting information security events
sudo tee /usr/local/bin/iso27001-incident.sh << 'SCRIPT'
#!/bin/bash
INCIDENT_ID="ISO-$(date +%Y%m%d-%H%M%S)"
DIR="/var/log/iso27001/incidents/$INCIDENT_ID"
mkdir -p "$DIR"

cat > "$DIR/incident-report.txt" << EOF
ISO 27001 Incident Report
Incident ID: $INCIDENT_ID
Date/Time: $(date -u)
Reporter: ${SUDO_USER:-$USER}
Hostname: $(hostname)
Category: [Security Event / Data Breach / Availability / Other]
Severity: [Critical / High / Medium / Low]
Description: [To be filled in]

=== System State at Time of Detection ===
Active Sessions: $(who)
Failed Logins (last hour): $(sudo grep "Failed password" /var/log/auth.log | grep "$(date '+%b %e')" | wc -l)
Audit Events: $(sudo ausearch -ts "1 hour ago" 2>/dev/null | wc -l) events in last hour
EOF

# Collect technical evidence
ss -antp > "$DIR/connections.txt"
ps auxf > "$DIR/processes.txt"
sudo journalctl --since "2 hours ago" > "$DIR/journal.txt"

echo "Incident $INCIDENT_ID created: $DIR"
echo "Next: Notify ISMS manager within 24 hours per A.16.1.3"
SCRIPT

sudo chmod +x /usr/local/bin/iso27001-incident.sh

Evidence Collection for Auditors

ISO 27001 audits require documented evidence of control effectiveness:

sudo tee /usr/local/bin/iso27001-evidence-collect.sh << 'SCRIPT'
#!/bin/bash
MONTH=$(date +%Y-%m)
DIR="/var/log/iso27001/evidence/$MONTH"
mkdir -p "$DIR"

# Access control evidence
cat /etc/passwd > "$DIR/user-accounts.txt"
sudo cat /etc/sudoers.d/* > "$DIR/sudo-permissions.txt" 2>/dev/null
last -n 200 > "$DIR/login-history.txt"
sudo grep "Failed password" /var/log/auth.log | tail -200 > "$DIR/failed-logins.txt"
sudo pam_tally2 --quiet 2>/dev/null > "$DIR/lockouts.txt"

# Cryptography evidence
for dev in /dev/mapper/*; do
    sudo cryptsetup status "$dev" 2>/dev/null
done > "$DIR/encryption-status.txt"

# Operations security evidence
sudo apt list --upgradable 2>/dev/null > "$DIR/pending-updates.txt"
sudo ausearch -k admin_commands -ts "this-month" > "$DIR/admin-commands.txt" 2>/dev/null

# Network evidence
ss -tlnp > "$DIR/listening-services.txt"
sudo iptables -L -n > "$DIR/firewall-rules.txt"

# Backup evidence
restic -r sftp:backup@backup-server:/backups/$(hostname) \
  --password-file /etc/restic/password \
  snapshots 2>/dev/null > "$DIR/backup-snapshots.txt"

echo "Evidence collected to $DIR"
SCRIPT

sudo chmod +x /usr/local/bin/iso27001-evidence-collect.sh

# Run monthly
(sudo crontab -l 2>/dev/null; echo "0 3 1 * * /usr/local/bin/iso27001-evidence-collect.sh") | sudo crontab -

Troubleshooting

Audit logs not retained long enough (A.12.4.1 requires defined retention):

sudo nano /etc/audit/auditd.conf
# Set: max_log_file = 100          # MB per file
# Set: num_logs = 12               # Keep 12 rotated files
# Set: max_log_file_action = ROTATE
sudo systemctl restart auditd

pam_tally2 deprecated on newer systems:

# On Ubuntu 22.04+ and RHEL 9+ use pam_faillock instead
sudo nano /etc/security/faillock.conf
# Set: deny = 5
# Set: unlock_time = 1800

SSH banner not appearing:

# Verify Banner setting in sshd_config
grep Banner /etc/ssh/sshd_config
# Check file exists and is readable
ls -la /etc/issue.net
sudo systemctl reload sshd

TLS 1.1 still enabled after config change:

# Test what protocols are accepted
nmap --script ssl-enum-ciphers -p 443 localhost
# If old TLS still shown, check for other config files
grep -r "ssl_protocols" /etc/nginx/
sudo nginx -T | grep ssl_protocols

Conclusion

ISO 27001 Annex A controls on Linux translate into concrete configurations: locked-down SSH, role-based sudo access, AES-256 encryption at rest, TLS 1.2+ for communications, auditd-based event logging, and tested backup procedures. Collect evidence monthly and review controls quarterly against your ISMS risk register. When your auditor arrives, the evidence collection script provides the documentation trail needed to demonstrate control effectiveness and maintain certification.