Apache hardening profile
This article has last been updated at .
Introduction
This is a hardening profile to help securing the Apache web server by using a strict systemd unit configuration. The goal for this hardening profile is to limit what Apache can do, while still being able to serve HTTP requests.
Relevant FAQ: How to use systemctl edit to change a service?
Notes
Depending on external components such as PHP, temporary files may need to be made accessible. When possible, define an alternative directory and keep PrivateTmp enabled.
Possible errors
Apache no longer starts after applying the profile
Job for apache2.service failed because the control process exited with error code.
See "systemctl status apache2.service" and "journalctl -xeu apache2.service" for details.
This may happen due to a profile that is too strict for your system or environment. Perform the troubleshooting steps for failed systemd units to see what needs to be changed.
The journal shows errors
Depending on the Linux distribution, Apache might be started with a wrapper script. Specific binaries like /usr/bin/id and /usr/bin/rm might be needed.
Dec 15 22:20:44 test apachectl[37748]: /usr/sbin/apachectl: 155: rm: Permission denied
Hardening profile
Important notes
- Test this profile first on a non-production system
- Paths defined within this profile may differ between Linux distributions
- Additional ports may need to be added (SocketBindAllow)
- Additional paths might be needed when Apache logs are stored in a different location (ReadWritePaths)
- Reload and restart Apache a few times after applying the new settings
- Confirm that Apache is working and serving pages
Excluded settings for this profile
Some settings were considered too strict, too specific, or not compatible for this service unit. For this reason they are not applied to the service.
Setting | Reason |
---|---|
DeviceAllow | PrivateDevices is already defined, therefore access to devices is very restricted |
IPAccounting | IP accounting tracks traffic (packets and bytes) to and from the service. It strongly depends on the use-case if this setting is useful for this service. |
PrivateNetwork | This service requires networking connectivity |
PrivatePIDs | This service is defined with Type=forking and therefore can't be used |
ReadOnlyPaths | ProtectSystem is used, which already marks most of the file system read-only |
Settings to consider
There are some settings that can be considered adding. These might not be added yet as they require additional testing. Want to help improving this profile? Test the setting and let it know!
Setting | Reason or description |
---|---|
DevicePolicy | define level of access to devices in /dev |
RuntimeDirectoryMode | set the default file permissions for runtime directory, which is defined as RuntimeDirectory |
SecureBits | change the behavior of Linux capabilities by setting the securebits flag of the prctl(2) syscall |
RemoveIPC | defines if System V and POSIX IPC objects by the user and group are removed upon stopping the service |
PrivateUsers | Define a new user namespace for the process and its children |
KeyringMode | controls kernel session keyring and define what is available to the service |
PrivateMounts | provides a separated mount namespace to the service |
Applying this profile
Open the service unit for Apache with the editor function.
systemctl edit apache2.serviceCopy the following block into the editor. As the profile can change over time, it is suggested to include the link and version. This way changes can easily be compared in the future. Also add your own customizations at the top to simplify the comparison.
################################################################################
# Source: https://linux-audit.com/systemd/hardening-profiles/apache/
# Profile version: 0.2 [2024-12-15]
################################################################################
# Customizations:
# - Insert here the changes you made to the profile
################################################################################
[Service]
# ===============================
# Paths
# ===============================
# Deny access to /dev/shm directory, suggested when using MemoryDenyWriteExecute=yes
# Details: https://linux-audit.com/systemd/settings/units/inaccessiblepaths/
InaccessiblePaths=/dev/shm
# Do not allow execution of files, except those for Apache itself, such as its modules. Allow /bin/sh as apache2ctl is a shell script.
# Details: https://linux-audit.com/systemd/settings/units/noexecpaths/
NoExecPaths=/
# Apachectl is a script that uses /bin/sh and commands like id and rm
# Details: https://linux-audit.com/systemd/settings/units/execpaths/
ExecPaths=/bin/sh /usr/bin/id /usr/bin/rm /usr/sbin/apache2 /usr/sbin/apachectl /usr/sbin/apache2ctl /usr/lib/apache2 /usr/lib
# Allow creation of PID file and writing to log files
# Details: https://linux-audit.com/systemd/settings/units/readwritepaths/
ReadWritePaths=/var/log/apache2 /var/lock/apache2 /var/run/apache2
# ===============================
# Capabilities and system calls
# ===============================
# Define capabilities
# Details: https://linux-audit.com/systemd/settings/units/capabilityboundingset/
CapabilityBoundingSet=CAP_CHOWN CAP_DAC_OVERRIDE CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID
# Only allow the usage of system calls for own hardware platform. Advised when using RestrictAddressFamilies
# Details: https://linux-audit.com/systemd/settings/units/systemcallarchitectures/
SystemCallArchitectures=native
# Define an allow-list filter for system calls
# Details: https://linux-audit.com/systemd/settings/units/systemcallfilter/
SystemCallFilter=@system-service
# Explicit block memfd_create() due to using MemoryDenyWriteExecute=yes
SystemCallFilter=~memfd_create
# Block @mount, suggested as extension to PrivateTmp=, PrivateDevices=, ProtectSystem=, ProtectHome=, ProtectKernelTunables=, ProtectControlGroups=, ProtectKernelLogs=, ProtectClock=, ReadOnlyPaths=, InaccessiblePaths= and ReadWritePaths=.
SystemCallFilter=~@clock @mount @reboot
# ===============================
# Memory
# ===============================
# Do not allow memory segments to be writable+executable
# Details: https://linux-audit.com/systemd/settings/units/memorydenywriteexecute/
MemoryDenyWriteExecute=yes
# ===============================
# Privileges
# ===============================
# Do not gain new privileges
# Details: https://linux-audit.com/systemd/settings/units/nonewprivileges/
NoNewPrivileges=yes
# ===============================
# Personality
# ===============================
# Do not allow personality switch
# Details: https://linux-audit.com/systemd/settings/units/lockpersonality/
LockPersonality=yes
# ===============================
# Private
# ===============================
# Restrict access to devices in /dev, except for pseudo-devices like /dev/null, /dev/random, /dev/zero
# Details: https://linux-audit.com/systemd/settings/units/privatedevices/
PrivateDevices=yes
# Limit access to temporary directories such as /tmp and /var/tmp by creating a new namespace
# Details: https://linux-audit.com/systemd/settings/units/privatetmp/
PrivateTmp=yes
# ===============================
# Protect and Proc
# ===============================
# Do not allow changing the system clock
# Details: https://linux-audit.com/systemd/settings/units/protectclock/
ProtectClock=yes
# Details: https://linux-audit.com/systemd/settings/units/protectcontrolgroups/
ProtectControlGroups=yes
# No access to home directories
# Details: https://linux-audit.com/systemd/settings/units/protecthome/
ProtectHome=yes
# Do not allow changing the hostname
# Details: https://linux-audit.com/systemd/settings/units/protecthostname/
ProtectHostname=yes
# No access to kernel log ring buffer
# Details: https://linux-audit.com/systemd/settings/units/protectkernellogs/
ProtectKernelLogs=yes
# Do not allow loading kernel modules
# Details: https://linux-audit.com/systemd/settings/units/protectkernelmodules/
ProtectKernelModules=yes
# Don't allow changes to sysctl settings
# Details: https://linux-audit.com/systemd/settings/units/protectkerneltunables/
ProtectKernelTunables=yes
# No access to information about other users in /proc
# Details: https://linux-audit.com/systemd/settings/units/protectproc/
ProtectProc=invisible
# Most of the system paths will no longer be writable
# Details: https://linux-audit.com/systemd/settings/units/protectsystem/
ProtectSystem=strict
# Restricts information from /proc that not directly associated with process management and introspection
# Details: https://linux-audit.com/systemd/settings/units/procsubset/
ProcSubset=pid
# ===============================
# Restrictions
# ===============================
# Limit address families INET/INET6 for IPv4/IPv6, UNIX for local functions such as syslog
# Details: https://linux-audit.com/systemd/settings/units/restrictaddressfamilies/
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# No access to Linux namespace functionality
# Details: https://linux-audit.com/systemd/settings/units/restrictnamespaces/
RestrictNamespaces=yes
# Realtime scheduling not allowed
# Details: https://linux-audit.com/systemd/settings/units/restrictrealtime/
RestrictRealtime=yes
# Do not allow setting set-user-ID and set-group-ID
# Details: https://linux-audit.com/systemd/settings/units/restrictsuidsgid/
RestrictSUIDSGID=yes
# ===============================
# Sockets
# ===============================
# Block socket bind as the default, then use SocketBindAllow to create allow-list
# Details: https://linux-audit.com/systemd/settings/units/socketbinddeny/
SocketBindDeny=any
# Allow port 80 (HTTP)
# Details: https://linux-audit.com/systemd/settings/units/socketbindallow/
SocketBindAllow=tcp:80
# Allow port 443 (HTTPS)
SocketBindAllow=tcp:443
# Allow port 443 (HTTPS), HTTP/3 with QUIC
SocketBindAllow=udp:443
# ===============================
# Umask
# ===============================
# Create new directories and files with 700/600 file permissions
# Details: https://linux-audit.com/systemd/settings/units/umask/
UMask=0077
Restart the apache2 service
systemctl restart apache2.serviceAfter restarting the service, check the status.
systemctl status apache2.serviceLast step is checking the service and its log files.
Troubleshooting
All is working? Perfect!
- A file mentioned in the hardening profile does not exist on your system
- Adjust the path
- Add a hyphen before optional paths)
- Process can't start as it requires additional paths
- Add the paths
- Make sure that paths that needs to be written to are defined in ReadWritePaths
- Process can't bind to a port
- Specify port with SocketBindAllow
See troubleshooting a failed systemd unit to learn how to do a more in-depth check of a failing systemd unit.
Did the hardening profile work right away or still some issues? Send a quick message!