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.


