The security of a server intended for applications such as OpenClaw is not based solely on the software itself, but largely on the hardening of the underlying operating system. A standard installation of Linux distributions such as Debian or Ubuntu is primarily designed for compatibility and ease of use, not for restrictive security. This initially results in an unnecessarily large attack surface.
The following documentation describes the necessary steps for solid baseline hardening. Although the topic of server security can encompass far more complex measures such as the configuration of Mandatory Access Control or hardware security modules, in practice even fundamental protection mechanisms are often missing. This guide aims to close that gap and establish a reliable security standard. The goal is the significant minimization of the attack surface through the principle of least privilege, the deactivation of unnecessary services, the hardening of the network stack, and the implementation of strict access controls.
1. Initial Connection and System Update
Initial access to the system is usually performed via the default SSH port (22) using the root user. Immediately after taking over the system, a complete update of the package sources and the installed software is necessary in order to close known security vulnerabilities and establish a consistent system state.
Establishing the connection (client-side)
ssh -p 22 root@52.213.101.188
System update
The command sudo apt update updates the local database of available software packages and their versions. This is the prerequisite for the system to even learn about security patches. The command sudo apt full-upgrade -y performs the actual update of all installed packages to the latest versions. In contrast to a simple upgrade, full-upgrade also resolves complex dependency changes by installing new packages or removing obsolete ones if necessary.
With sudo apt autoremove –purge, packages are deleted that were originally installed as dependencies for other software, but are now no longer needed. The additional –purge flag also removes all associated configuration files. This minimizes the system’s attack surface, because unnecessary software and potentially insecure configuration remnants disappear completely from the disk.
sudo apt update && apt full-upgrade -y
sudo apt autoremove --purge
sudo reboot
Configuring automatic security updates
By enabling unattended-upgrades, the system is configured to download and install critical security patches in the background without manual intervention. For system hardening, this is essential, as the time span between a vulnerability becoming known and being closed is reduced to a minimum. The system therefore remains protected even if the administrator cannot intervene immediately.
In addition, the tool ensures that updates are obtained only from trusted security sources, which preserves the integrity of the installed software. Without this automation, you risk your system remaining vulnerable to known exploits for days or weeks due to forgotten maintenance intervals.
dpkg-reconfigure -plow unattended-upgrades
The file /etc/apt/apt.conf.d/20auto-upgrades controls the automation of maintenance tasks for the package manager and is a central component of system hardening through timely patches.
vi /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
- APT::Periodic::Update-Package-Lists “1”; causes the system to reconcile the local lists of available software packages with the repositories on a daily basis. Without this information, the system cannot find or download new security updates.
- APT::Periodic::Unattended-Upgrade “1”; enables the daily automatic installation of security updates without user interaction. This is essential for security, as the time span between a vulnerability becoming known and its remediation (time-to-patch) is drastically shortened.
- APT::Periodic::AutocleanInterval “7”; ensures that every seven days the local cache of outdated package files is cleaned up, i.e., files that can no longer be downloaded. This keeps the system lean and removes unnecessary data clutter from the disk.
The next command stops the timers for package updates as well as for security upgrades and immediately starts them again so that changed settings take effect without delay. This immediate adoption is crucial for system hardening because it ensures seamless automation of security fixes and minimizes the window for attacks.
sudo systemctl restart apt-daily.timer apt-daily-upgrade.timer
In addition, it is defined that the system restarts automatically if required (e.g., after kernel updates). To control availability, this restart is scheduled for a maintenance window (04:00 in the morning).
The file 50unattended-upgrades governs automated security updates. The number fifty determines the load order in the configuration directory, while the name describes the unattended function.
vi /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
2. User management and editor configuration
Disabling direct login for root is an essential security measure because this account is the primary target for attacks. Before disabling it, a new user such as aaron must be created to guarantee system access. The adduser command creates the account, while usermod assigns the user to a group with extended privileges. Only after the new user’s login and the use of vi have been successfully tested should direct access for root be blocked in the SSH configuration.
sudo adduser aaron
sudo usermod -aG sudo aaron
# OpenClaw user without root privileges
sudo adduser openclaw
After creation, a test of switching users is performed to verify functionality:
su - aaron
sudo -s
su - openclaw
Test the new user login:
ssh -p 22 aaron@52.213.101.188
sudo -s
Hardening the SSH service
The SSH service is the primary entry point for administration. In this step, the service is fully secured: the port is changed, root login is prohibited, and tunneling options are disabled.
Adjusting the configuration file
The file /etc/ssh/sshd_config defines the global security rules for the SSH daemon and must be edited with administrative privileges as root.
sudo vi /etc/ssh/sshd_config
The following values are adjusted or added:
# Change port to a high port (security through obscurity)
# Moving the service to port 40000 provides no real security against targeted attacks, because automated tools can quickly find an open service. This measure primarily serves to reduce baseline noise in the log files, since most simple scripts only probe standard ports. With fewer automated login attempts, the system logs remain significantly clearer, which makes the analysis of real security incidents easier.
Port 40000
# With PermitRootLogin no, direct access for the root account is completely blocked, which forces a login via a normal user such as aaron.
PermitRootLogin no
# The AllowUsers aaron directive creates a whitelist that restricts access exclusively to this single name.
AllowUsers aaron
# Harden brute-force protection
# MaxAuthTries 3 limits the allowed failed password attempts per connection, while MaxSessions 2 limits the number of parallel sessions.
MaxAuthTries 3
MaxSessions 2
# ClientAliveCountMax 2 defines the maximum number of keepalive checks that the server sends without a response from the remote participant before the connection is automatically terminated. This value works together with the interval for such checks. If no response is received to a sent check, an internal counter is increased. When this counter reaches 2, the session is terminated immediately. This protects the system from orphaned connections that show no activity anymore but still consume resources or could represent a security risk. This setting ensures that stuck or forgotten sessions do not persist indefinitely.
ClientAliveCountMax 2
# These parameters disable functions for data tunnels or graphical interfaces in order to minimize the system’s attack surface.
TCPKeepAlive no
AllowTcpForwarding no
X11Forwarding no
# Increase logging
LogLevel VERBOSE
After the baseline configuration, SSH access should be fully switched to public-key authentication. To do this, the following parameters are enabled. This configuration prevents any form of password-based login and reduces the attack surface to cryptographically secured keys.
PasswordAuthentication no
ChallengeResponseAuthentication no
Adjusting the systemd socket
Because we manually switched the SSH port to 40000, the socket port in systemd’s configuration must also be adjusted, as it serves as the primary receiving point for network traffic. If the socket is not switched to port 40000, the system will not recognize incoming requests as SSH traffic and will block access even though the service itself is already correctly configured.
sudo EDITOR=vi systemctl edit ssh.socket
# The empty ListenStream= directive is necessary to delete the default configuration.
# In this context, the empty assignment ListenStream= acts as a reset command. Without this line, the SSH socket would continue to listen on the standard port 22 and would only add port 40000 as an additional option.
[Socket]
ListenStream=
ListenStream=40000
Restarting the services
The configuration is checked and the services are restarted.
# Checking the configuration with the test command ensures that there are no syntax errors that could prevent the service from starting.
sshd -t
# Then the system manager is instructed to reload the unit configuration so that the manual socket changes are recognized by the system.
systemctl daemon-reload
# Restarting the socket activates listening on port 40000 for incoming requests, while restarting the service finalizes the entire migration.
systemctl restart ssh.socket
systemctl restart ssh.service
Verification
Access now occurs via the new port and user:
ssh -p 40000 aaron@52.213.101.188
Previously we had:
ssh -p 22 root@52.213.101.188
4. Firewall configuration (UFW)
A firewall serves as a digital protective wall that filters all data packets according to strict rules and repels unauthorized access attempts. By applying a whitelist strategy, it is ensured that only explicitly enabled services can communicate, while all other ports remain invisible. The firewall regulates traffic and applies the default-deny principle. It is important to enable the new SSH port 40000 before activating the firewall, otherwise administrative remote access is immediately blocked. Only after verifying all rules should protection be enabled to prevent locking yourself out of the system.
# Set default policies
ufw default deny outgoing
ufw default deny incoming
# Allow SSH on new port
ufw allow in 40000/tcp
ufw allow out 40000/tcp
# Allow web server traffic
ufw allow in 80/tcp
ufw allow out 80/tcp
ufw allow in 443/tcp
ufw allow out 443/tcp
# Allow DNS and NTP outbound
ufw allow out 53/udp
ufw allow out 53/tcp
ufw allow out 123/udp
Activate and verify the rules:
# Lists all rules that have already been added to the system, even if they are not yet active at the moment.
sudo ufw show added
# The filtering service is started and the protection mechanism is firmly anchored in the operating system so that it is automatically loaded on every future system boot.
sudo ufw enable
# Provides an overview of all currently active rules and labels each line with a consecutive number, which greatly simplifies targeted editing or deletion of individual entries.
sudo ufw status numbered
# ufw reload reloads the entire ruleset so that current changes take effect immediately without interrupting ongoing network connections.
sudo ufw reload
# Provides a comprehensive report on the overall operating state, including the basic security defaults for inbound and outbound traffic as well as the currently configured logging level.
ufw status verbose
5. Intrusion prevention with Fail2Ban
Fail2Ban acts as an automated protection mechanism that continuously scans system logs for signs of brute-force attacks or other malicious behavioral patterns. By immediately blocking attackers’ IP addresses, the server’s attack surface is drastically minimized and system stability against automated botnets is preserved. This service is indispensable for stopping relentless password-cracking attempts at the outset, before they can cause significant damage or overload system resources. Because the service intervenes directly in the firewall, it ensures that detected threats are immediately excluded from further access without requiring manual monitoring of the system.
Installation
sudo apt install fail2ban -y
Configuration
Create a local configuration file jail.local. The file jail.local is a copy for custom rules so that personal settings are not deleted during an update.
sudo vi /etc/fail2ban/jail.local
[DEFAULT]
# The line bantime 24h defines that a blocked IP address is not allowed to connect to the server for exactly 24 hours.
bantime = 24h
# The parameter findtime = 100m defines the time window within which the failed attempts are counted. Fail2Ban always considers a sliding window of the last 10 minutes from the most recent event.
findtime = 100m
# Only if the number of failed attempts (maxretry) is reached within this specific period will the IP address be banned.
maxretry = 3
[sshd]
# Enable protection
enabled = true
# Defines the port the service listens on (must match sshd_config)
port = 40000
# Refers to the filter file with the search patterns for SSH attacks
filter = sshd
# Path to the log file in which login attempts are recorded
logpath = /var/log/auth.log
# Uses the modern system journal to read data efficiently
backend = systemd
Enable the service and check status
# Ensures that the service starts automatically on every boot so that protection always remains active without manual intervention.
sudo systemctl enable fail2ban
# Restart to apply all configuration changes such as the new port 40000 or the ban duration to the running system.
sudo systemctl restart fail2ban
# Shows the current state of the filter as well as a list of all currently banned participants and the number of recorded attack attempts.
sudo fail2ban-client status sshd
6. Kernel and system hardening
The kernel is the heart of the operating system and takes full control over communication between the hardware and running applications. It manages access to the processor and memory and allocates the required resources to each process so that the system remains stable. Through its role as an intermediary, it ensures that software commands are translated into physical hardware actions without programs accessing sensitive components directly. It is therefore responsible for security and coordination of all processes within your machine.
In this section, kernel parameters are adjusted, file permissions are tightened, and rootkit scanners are installed.
Kernel parameters (sysctl)
Kernel parameters, also known as sysctl, are tuning knobs in the running operating system that control the kernel’s behavior and can directly influence it. These variables allow deep security features to be enabled or network protocols to be adjusted without restarting the system.
Install necessary tools:
sudo apt update && apt install auditd debsums apt-listchanges libpam-tmpdir -y
- The tool auditd takes on the task of logging all security-relevant events in the system without gaps and preserving them for later analysis.
- With debsums, the integrity of installed software packages is verified by comparing each individual file with its original fingerprint in order to detect tampering.
- The component apt-listchanges shows a list of all new features and bug fixes before an update so that system changes remain more traceable.
- libpam-tmpdir ensures that each user receives their own private directory for temporary files, which prevents unauthorized access by other accounts to sensitive intermediate data.
Creating the hardening configurations
This file stores persistent settings for kernel security that are automatically loaded on every system start in order to effectively reduce the machine’s attack surface.
sudo vi /etc/sysctl.d/99-hardening.conf
# No core dumps for services: a crash of SSH leaves no password in a memory dump.
fs.suid_dumpable = 0
# Hides kernel addresses: an attacker cannot see exactly where in memory a function is located that they want to exploit.
kernel.kptr_restrict = 2
# Hardens the BPF JIT: malicious code cannot be injected into the kernel via the network filter.
net.core.bpf_jit_harden = 2
# Blocks BPF for normal users: a regular user cannot sniff other data packets on the server.
kernel.unprivileged_bpf_disabled = 1
# Logs invalid packets: a packet from an internal address arriving from the outside is reported as a threat.
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Disallows redirects: a foreign machine cannot instruct your server to send data via an insecure detour.
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# No route specification by packets: a packet cannot decide its own path through the network to bypass filters.
net.ipv4.conf.default.accept_source_route = 0
Installing Lynis
Lynis is a specialized tool for performing security audits on Unix-based systems. It is installed to objectively assess server security and, through targeted optimizations, reach a hardening score of around 80. I use this high value as a benchmark for a professionally secured environment that is well protected against a wide range of digital threats.
# Installation
sudo apt install lynis
# Start audit
sudo lynis audit system
To further increase system security, an additional configuration file for kernel parameters is created in the next step. These settings are based on recommendations from the Lynis audit and aim to minimize potential attack surfaces at the kernel level.
sudo vi /etc/sysctl.d/99-lynis-hardening.conf
# This setting ensures that the process ID (PID) is appended to core dump names. This makes it easier to associate errors and prevents older reports from being overwritten easily.
kernel.core_uses_pid = 1
# This enables the “reverse path filter”. The system checks whether incoming packets arrive via the plausible interface through which the response would also be sent. This is an important protection against IP spoofing.
net.ipv4.conf.all.rp_filter = 1
# This line applies the reverse-path filter rule as the default value to all network interfaces on the system.
net.ipv4.conf.default.rp_filter = 1
# This option disables the so-called “Magic SysRq” key combinations. This prevents people with physical access to the server from triggering critical commands directly via the keyboard.
kernel.sysrq = 0
The configuration files are loaded and the parameters defined there are applied immediately as active rules in the running kernel without a system reboot.
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
sudo sysctl -p /etc/sysctl.d/99-lynis-hardening.conf
File permissions and service cleanup
The risk with automated schedules lies in the possible injection of commands that then run in the background with far-reaching privileges. The next steps shield sensitive directories as well as the remote-access configuration so that only a privileged user has access.
Assigning permission 600 to the central task-scheduling file ensures that only the system administrator (root) can read and edit the contents, while the regular user (e.g., aaron) is completely denied access. The value 700 for the corresponding directories also ensures that only the administrative layer can add new entries or execute scripts. This strict separation ensures that regular user accounts cannot start background services or manipulate system-wide processes without explicit permission.
# Protect cron and system files
sudo chmod 600 /etc/crontab
sudo chmod 700 /etc/cron.d
sudo chmod 700 /etc/cron.daily
sudo chmod 700 /etc/cron.hourly
sudo chmod 700 /etc/cron.weekly
sudo chmod 700 /etc/cron.monthly
sudo chmod 600 /etc/at.deny
sudo chmod 600 /etc/ssh/sshd_config
# Restricting tools for program creation such as compilers makes it significantly harder for attackers to build and run their own malware directly on the target system.
sudo chmod 700 /usr/bin/gcc
sudo chmod 700 /usr/bin/make
# Completely removing obsolete services such as Telnet also eliminates known security vulnerabilities that would arise from unencrypted data transmission.
sudo apt purge rsh-client rsh-redone-client telnet -y
System banner and legal hardening
Custom system banners minimize the risk of disclosing information about internal system characteristics and thus deprive potential attackers of valuable clues for targeted attacks. These explicit warnings also form the legal foundation for later prosecution because they unambiguously clarify that access is permitted only to authorized persons. By referencing active monitoring of all activities, a deterrent effect is achieved that warns unauthorized users about the consequences of their actions already when establishing the connection.
# Displays the warning text for a local login directly on the console
sudo vi /etc/issue
# Provides the legal text for login attempts over the network
sudo vi /etc/issue.net
By removing the standard greeting messages, information such as the distribution name or the current kernel version is prevented from leaking outward.
*****************************************************************************
AUTHORIZED USERS ONLY.
This system is restricted to authorized users for business purposes only.
All activities on this system are logged and monitored. Unauthorized
access or use is strictly prohibited and may be subject to criminal
and/or civil penalties. By continuing, you consent to these terms.
*****************************************************************************
Postfix hardening
Many common distributions such as Debian or Ubuntu often install a so-called Mail Transfer Agent already during the initial setup. This happens primarily so that the system is able to send internal status messages or warnings from background services to local administration.
If Postfix is installed, information is obscured.
sudo postconf -e "smtpd_banner = \$myhostname ESMTP"
sudo postconf -e "disable_vrfy_command = yes"
sudo systemctl restart postfix
7. Using Rootkit Hunter (rkhunter)
Rootkit Hunter is a specialized diagnostic tool that checks the entire software environment for hidden malware and backdoors. By comparing the digital fingerprints of system files with a secure reference database, suspicious changes to the integrity of the operating system can be detected immediately. Performing these checks regularly forms an essential pillar in detecting advanced attack methods that could embed themselves deeper in the system.
Installation
sudo apt install rkhunter -y
Configuration
sudo vi /etc/rkhunter.conf
UPDATE_MIRRORS=1
MIRRORS_MODE=0
WEB_CMD=/usr/bin/wget
sudo /etc/default/rkhunter
# Diese Einstellung sorgt für die tägliche und selbstständige Durchführung einer vollständigen Systemüberprüfung im Hintergrund. Damit wird sichergestellt dass potenzielle Manipulationen an Dateien zeitnah erkannt werden ohne dass ein manueller Start durch einen privilegierten Nutzer erforderlich ist.
CRON_DAILY_RUN="true"
# Mit dieser Option wird die regelmäßige Aktualisierung der internen Datenbank für Dateieigenschaften und Signaturen aktiviert. Dies ist entscheidend um die Erkennungsrate gegen neue Bedrohungen hoch zu halten da veraltete Informationen die Schutzwirkung des Programms erheblich mindern würden.
CRON_DB_UPDATE="true"
# Diese Zeile bewirkt die automatische Erneuerung der Dateireferenzen sobald Software über die Paketverwaltung des Systems installiert oder aktualisiert wird. Durch diesen Abgleich werden Fehlalarme vermieden die sonst entstehen würden wenn rechtmäßige Programmänderungen als unbefugte Eingriffe missverstanden werden.
APT_AUTOGEN="true"
Run a system check manually:
# This command updates the internal databases of the program. It downloads the latest signatures and information about known rootkits, backdoors, and other malicious software from the official servers to keep the system’s detection rate up to date.
sudo rkhunter --update
# This command performs a so-called “property update”. The program analyzes the current file properties (such as file size or hash values) of important system files and stores them as a trusted baseline in a database. This is necessary so that the tool can later determine whether files were modified without authorization. This step should always be executed after an intended system update or software installation to avoid false positives.
sudo rkhunter --propupd
# This is the actual check command that starts the scanning process. The tool compares the running processes and files of the system with the previously created database and the current signatures. It searches for suspicious directories, hidden files, and known indications of a compromise of the kernel or system applications.
sudo rkhunter --check
8. Audit Framework
The Linux Audit Framework is a powerful system monitoring tool originally developed by Red Hat and today included as an integral part of the kernel in almost all distributions. Its use is so important because it enables complete and audit-proof logging of all security-relevant activities that goes far beyond normal log files. It supports compliance with strict data-security requirements and enables precise forensic analysis of every single action on the system in the event of an incident. Because it is deeply integrated directly into the operating system kernel, even processes with elevated privileges can be monitored without gaps, which massively increases transparency and security across the entire environment.
Installation
sudo apt install auditd audispd-plugins
This file serves as the central instance for defining monitoring rules within the Audit Framework. It precisely defines which system events—file accesses or administrative activities—are to be captured by the kernel and permanently logged.
sudo vi /etc/audit/rules.d/audit.rules
# Records when user groups are created or changed in their permissions.
-w /etc/group -p wa -k identity
# Monitors the central file for user information in order to detect account manipulation
-w /etc/passwd -p wa -k identity
# Logs access to encrypted passwords, which is critical for detecting theft attempts.
-w /etc/shadow -p wa -k identity
# Every change to permissions for executing commands with elevated rights is recorded.
-w /etc/sudoers -p wa -k identity
# Prevents the system’s identity on the network from being modified unnoticed.
-w /etc/hostname -p wa -k system-locale
# Ensures that changes to interfaces or routing tables remain traceable.
-w /etc/network -p wa -k system-locale
# Monitors the file that stores information about users’ most recent logins.
-w /var/log/lastlog -p wa -k logins
# Records events related to failed login attempts and resulting lockouts.
-w /var/run/faillock -p wa -k logins
# This directive ensures that on systems with a 64-bit architecture, every single program start is recorded without gaps. The system call execve is responsible for executing applications and is logged for every invocation. This enables administrators to reconstruct exactly which user started which command on the console at what time. Even if a user such as aaron tries to cover their tracks, these actions remain permanently stored in the audit log.
-a always,exit -F arch=b64 -S execve -k command-execution
Load rules
# This command is used to compile the audit rules from the configuration files (usually under /etc/audit/rules.d/) and load them directly into the running kernel. This makes changes to the ruleset take effect immediately without requiring a restart of the service or the system.
sudo augenrules --load
# Lists all audit rules currently loaded in the kernel. This allows direct verification of whether the desired monitoring rules were successfully activated and which system events are currently being logged by the kernel.
sudo auditctl -l
