Apache 2.4

Introduction
This document lists the steps to be taken to install and configure Apache httpd 2.4 with HTTP/2 support, with particular attention to security aspects.

Sources Download
Download the latest available version of Apache httpd sources from the site https://httpd.apache.org/download.cgi and any patches for that version from https://archive.apache.org/dist/httpd/patches/.

We will use version 2.4.46, which currently has no patches: httpd-2.4.46.tar.gz httpd-2.4.46.tar.gz.sha256

Also download the latest available version of Apache Portable Runtime sources from https://apr.apache.org/download.cgi.

We will use version 1.7.0 of APR and version 1.6.1 of APR-util: apr-1.7.0.tar.gz apr-1.7.0.tar.gz.sha256 apr-util-1.6.1.tar.gz apr-util-1.6.1.tar.gz.sha256

To enable HTTP/2 support, download nghttp2 sources from https://www.nghttp2.org/.

We will use version 1.43.0 of nghttp2: nghttp2-1.43.0.tar.gz

Variables
For convenience, we will use these variables in the next steps: APACHE_SEC_DIR="/opt/apache_sec" HTTPD_VERSION="httpd-2.4.46" APR_VERSION="apr-1.7.0" APR_UTIL_VERSION="apr-util-1.6.1" NGHTTP2_VERSION="nghttp2-1.43.0"

Conventions
The binaries will be installed in: ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}

while conf, htdocs, logs and cgi-bins will be in: ${APACHE_SEC_DIR}/httpd/conf ${APACHE_SEC_DIR}/httpd/htdocs ${APACHE_SEC_DIR}/httpd/logs ${APACHE_SEC_DIR}/httpd/cgi-bin

Dedicated file system
Wherever possible, it is good to create a dedicated filesystem in which to put all the files related to an Apache installation. You can add a new volume like this: lvcreate -L 4g -n apache_lv system mkfs.xfs /dev/system/apache_lv echo "/dev/system/apache_lv ${APACHE_SEC_DIR} xfs defaults,nosuid 0 0" >> /etc/fstab mkdir -p ${APACHE_SEC_DIR} mount ${APACHE_SEC_DIR}

Compilation of nghttp2
Decompress the sources: tar xfz ${NGHTTP2_VERSION}.tar.gz cd ${NGHTTP2_VERSION}

Install the packages needed for the build: apt install build-essential pkg-config libssl-dev libxml2-dev libxml2

Use these parameters for configuration: ./configure \ --prefix=${APACHE_SEC_DIR}/bin/${NGHTTP2_VERSION} \ --enable-lib-only

Compile the sources: make -j4

Install the binaries: make install

Create the symbolic link: ln -fsn ${NGHTTP2_VERSION} ${APACHE_SEC_DIR}/bin/nghttp2

Compilation of Apache httpd
Verify with the sha256sum command that the hash of the .tar.gz files coincides with the hash of the .sha256 files: sha256sum --check *.sha256

Decompress the sources: tar xfz ${HTTPD_VERSION}.tar.gz tar xfz ${APR_VERSION}.tar.gz tar xfz ${APR_UTIL_VERSION}.tar.gz mv ${APR_VERSION} ${HTTPD_VERSION}/srclib/apr mv ${APR_UTIL_VERSION} ${HTTPD_VERSION}/srclib/apr-util cd ${HTTPD_VERSION}/

Apply any patches: patch -p0 < patch.diff

Install the packages needed for the build: apt install build-essential pkg-config libldap2-dev libpcre3-dev libpcre3 libssl-dev libxml2-dev libxml2

Use these parameters for configuration (if necessary, enable cgi and cgid): ./configure \ --prefix=${APACHE_SEC_DIR}/bin/${HTTPD_VERSION} \ --with-included-apr \ --with-ldap \ --with-mpm=event \ --with-nghttp2=${APACHE_SEC_DIR}/bin/nghttp2 \ --enable-so \ --enable-allowmethods \ --enable-authnz_ldap \ --enable-cache \ --enable-cache-disk \ --enable-deflate \ --enable-expires \ --enable-headers \ --enable-http2 \ --enable-ldap \ --enable-logio \ --enable-proxy \ --enable-proxy-ajp \ --enable-proxy-html \ --enable-proxy-http \ --enable-proxy-balancer \ --enable-lbmethod-bybusyness \ --enable-lbmethod-byrequests \ --enable-lbmethod-bytraffic \ --enable-remoteip \ --enable-reqtimeout \ --enable-rewrite \ --enable-ssl \ --enable-substitute \ --enable-unique-id \ --disable-asis \ --disable-autoindex \ --disable-negotiation \ --disable-userdir \ --disable-cgi --disable-cgid

Compile the sources: make -j4

Install the binaries: make install

Create the symbolic link: ln -fsn ${HTTPD_VERSION} ${APACHE_SEC_DIR}/bin/httpd

Dedicated user creation
For each Apache installation it is good to use a dedicated user and group. In this example we will use wwsrun: useradd --system --user-group --shell /usr/sbin/nologin --home-dir /dev/null --comment "WWW daemon apache" wwsrun

Permission settings
mkdir -p ${APACHE_SEC_DIR}/httpd/conf/extra mkdir -p ${APACHE_SEC_DIR}/httpd/conf/vhosts.d mkdir -p ${APACHE_SEC_DIR}/httpd/htdocs mkdir -p ${APACHE_SEC_DIR}/httpd/logs mkdir -p ${APACHE_SEC_DIR}/httpd/cgi-bin

chown -R root:root ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION} ${APACHE_SEC_DIR}/httpd chmod -R go-w ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION} ${APACHE_SEC_DIR}/httpd chmod -R go-rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf ${APACHE_SEC_DIR}/httpd/conf chmod -R o-rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/htdocs ${APACHE_SEC_DIR}/httpd/htdocs chmod -R o-rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/logs ${APACHE_SEC_DIR}/httpd/logs chmod -R o-rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/cgi-bin ${APACHE_SEC_DIR}/httpd/cgi-bin

chown -R root:wwsrun ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/htdocs ${APACHE_SEC_DIR}/httpd/htdocs chown -R root:wwsrun ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/logs ${APACHE_SEC_DIR}/httpd/logs chown -R root:wwsrun ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/cgi-bin ${APACHE_SEC_DIR}/httpd/cgi-bin

chmod -R g+rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/htdocs ${APACHE_SEC_DIR}/httpd/htdocs chmod -R g+rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/logs ${APACHE_SEC_DIR}/httpd/logs chmod -R g+rx ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/cgi-bin ${APACHE_SEC_DIR}/httpd/cgi-bin

Copy configuration files
cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/* ${APACHE_SEC_DIR}/httpd/conf/ cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/extra/httpd-default.conf ${APACHE_SEC_DIR}/httpd/conf/extra/ cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/extra/httpd-info.conf ${APACHE_SEC_DIR}/httpd/conf/extra/ cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/extra/httpd-mpm.conf ${APACHE_SEC_DIR}/httpd/conf/extra/ cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/extra/httpd-ssl.conf ${APACHE_SEC_DIR}/httpd/conf/extra/ cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/conf/extra/proxy-html.conf ${APACHE_SEC_DIR}/httpd/conf/extra/

File httpd-default.conf
Modify in the file ${APACHE_SEC_DIR}/httpd/conf/extra/httpd-default.conf the directive ServerTokens to Prod sed -i 's/^ServerTokens Full/ServerTokens Prod/g' ${APACHE_SEC_DIR}/httpd/conf/extra/httpd-default.conf

File httpd-info.conf
Modify in the file ${APACHE_SEC_DIR}/httpd/conf/extra/httpd-info.conf the directive Require host to localhost sed -i 's/Require host .example.com/Require host localhost/g' ${APACHE_SEC_DIR}/httpd/conf/extra/httpd-info.conf

Comment the section server-info: sed -i '/^$/,/^<\/Location>$/s/^/#/g' ${APACHE_SEC_DIR}/httpd/conf/extra/httpd-info.conf

File httpd.conf
Modify in the file ${APACHE_SEC_DIR}/httpd/conf/httpd.conf the various paths: sed -i "s+${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}+${APACHE_SEC_DIR}/httpd+g" ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i "s+_module modules/+_module ${APACHE_SEC_DIR}/bin/httpd/modules/+g" ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Specify the IP address used by the web server: sed -i 's/^Listen 80/Listen server_IP:80/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Enable the required modules: for MODULE in allowmethods deflate http2 remoteip rewrite slotmem_shm socache_shmcb ssl substitute unique_id do     sed -i "s/^#LoadModule ${MODULE}_module /LoadModule ${MODULE}_module /g" ${APACHE_SEC_DIR}/httpd/conf/httpd.conf done

Change user and group that will run the web server: sed -i 's/^User daemon/User wwsrun/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i 's/^Group daemon/Group wwsrun/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Set the email address of the web master: sed -i 's/^ServerAdmin you@example.com/ServerAdmin indirizzo_email@example.com/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Set the Options directive to None: sed -i 's/^\s*Options Indexes FollowSymLinks/   Options None/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Comment the section dir_module: sed -i '/^$/,/^<\/IfModule>$/s/^/#/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Extend the directive Files: sed -i 's/^$//g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i 's+^$++g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Modify the directive LogLevel to info: sed -i 's/^LogLevel warn/LogLevel info/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Extend the directive LogFormat combined: sed -i 's/^\s*LogFormat "%h %l %u %t \\"%r\\" %>s %b \\"%{Referer}i\\" \\"%{User-Agent}i\\"" combined/   LogFormat "%v %h %l %u %t \\"%r\\" %>s %b \\"%{Referer}i\\" \\"%{User-Agent}i\\" | %{UNIQUE_ID}e | %D" combined/g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Enable the directive CustomLog combined: sed -i 's+^\s*CustomLog "logs/access_log" common+#CustomLog "logs/access_log" common+g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i 's+^\s*#CustomLog "logs/access_log" combined+CustomLog "logs/access_log" combined+g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Include several extra configuration files: sed -i 's+^#Include conf/extra/httpd-mpm.conf+Include conf/extra/httpd-mpm.conf+g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i 's+^#Include conf/extra/httpd-info.conf+Include conf/extra/httpd-info.conf+g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf sed -i 's+^#Include conf/extra/httpd-default.conf+Include conf/extra/httpd-default.conf+g' ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Enable support for HTTP/2 protocol: echo 'Protocols h2 http/1.1' >> ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

Include the additional configuration files: echo ' IncludeOptional conf/httpd-sec.conf IncludeOptional conf/vhosts.d/*.conf ' >> ${APACHE_SEC_DIR}/httpd/conf/httpd.conf

File httpd-sec.conf
Create the new file ${APACHE_SEC_DIR}/httpd/conf/httpd-sec.conf:

echo '  ProxyRequests Off  TraceEnable off  AllowMethods GET POST  LimitRequestBody    102400 LimitRequestFields     100 LimitRequestFieldSize 8190 LimitRequestLine      8190 LimitXMLRequestBody  51200  Listen server_IP:443 SSLPassPhraseDialog    builtin SSLSessionCache        shmcb:logs/ssl_scache(512000) SSLSessionCacheTimeout 300 SSLProtocol            all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2 SSLHonorCipherOrder    off SSLSessionTickets      off SSLStaplingCache       shmcb:logs/ssl_stapling(128000)  ' >> ${APACHE_SEC_DIR}/httpd/conf/httpd-sec.conf
 * 1) When running a reverse proxy only,
 * 2) do not allow forward proxy requests
 * 1) Disable TRACE method
 * 1) Restrict HTTP methods
 * 1) Limits
 * 1) SSL

Setting the permissions
chmod -R go-rx ${APACHE_SEC_DIR}/httpd/conf

Init.d Script
Create the startup file: cp -p ${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/bin/apachectl /etc/init.d/apachectl_sec

and modify some paths: sed -i "s+${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}/bin/httpd+${APACHE_SEC_DIR}/bin/httpd/bin/httpd -f ${APACHE_SEC_DIR}/httpd/conf/httpd.conf+g" /etc/init.d/apachectl_sec sed -i "s+${APACHE_SEC_DIR}/bin/${HTTPD_VERSION}+${APACHE_SEC_DIR}/bin/httpd+g" /etc/init.d/apachectl_sec

Create the necessary symbolic links: chkconfig apachectl_sec on

Virtual Host
Create a separate file for each Virtual Host and save it in the directory ${APACHE_SEC_DIR}/httpd/conf/vhosts.d

 ServerName example.com ServerAlias www.example.com

# Document Root DocumentRoot "htdocs/example.com"

# Log ErrorLog logs/example.com-error_log CustomLog logs/example.com-access_log combined

# Force the use of HTTPS RewriteEngine On   RewriteCond %{REQUEST_URI} !^/\.well\-known/acme\-challenge/ RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]

# Security Headers Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests; script-src 'self'; style-src 'self'; object-src 'none'; worker-src 'self'; child-src 'self'; frame-src 'self'; img-src 'self'; connect-src 'self'; font-src 'self'; manifest-src 'self'; media-src 'self'; prefetch-src 'self'; form-action 'self'; report-uri https://${subdomain}.report-uri.com/r/d/csp/enforce" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Xss-Protection "1; mode=block" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "interest-cohort=, accelerometer=, ambient-light-sensor=, autoplay=, camera=, encrypted-media=, fullscreen=(self), geolocation=, gyroscope=, magnetometer=, microphone=, midi=, payment=, picture-in-picture=(*), speaker=, sync-xhr=(*), usb=, vr=" Header always set X-Permitted-Cross-Domain-Policies "none" Header always set Set-Cookie "HttpOnly; SameSite=Strict" 

 ServerName example.com ServerAlias www.example.com

# Document Root DocumentRoot "htdocs/example.com"

# Log ErrorLog logs/example.com-error_log CustomLog logs/example.com-access_log combined

# SSL SSLEngine on   SSLCertificateFile      /path/to/signed_certificate_and_intermediate_certificates SSLCertificateKeyFile  /path/to/private_key #SSLCACertificateFile   /path/to/all_ca_certs

# OCSP Stapling SSLUseStapling                  on    SSLStaplingResponderTimeout      5 SSLStaplingReturnResponderErrors off

# Security Headers Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains" env=HTTPS Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests; script-src 'self'; style-src 'self'; object-src 'none'; worker-src 'self'; child-src 'self'; frame-src 'self'; img-src 'self'; connect-src 'self'; font-src 'self'; manifest-src 'self'; media-src 'self'; prefetch-src 'self'; form-action 'self'; report-uri https://${subdomain}.report-uri.com/r/d/csp/enforce" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Xss-Protection "1; mode=block" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "interest-cohort=, accelerometer=, ambient-light-sensor=, autoplay=, camera=, encrypted-media=, fullscreen=(self), geolocation=, gyroscope=, magnetometer=, microphone=, midi=, payment=, picture-in-picture=(*), speaker=, sync-xhr=(*), usb=, vr=" Header always set X-Permitted-Cross-Domain-Policies "none" Header always set Set-Cookie "HttpOnly; Secure; SameSite=Strict" 

Sources Download
Download the latest available version of ModSecurity sources from https://github.com/SpiderLabs/ModSecurity.

We will use version 2.9.2: modsecurity-2.9.2.tar.gz modsecurity-2.9.2.tar.gz.sha256

Download the latest available version of OWASP's Core Rule Set (CRS): https://github.com/coreruleset/coreruleset/releases

Compilation and Configuration
Additional variables: MODSEC_VERSION="modsecurity-2.9.2"

Verify with the sha256sum command that the hash of the .tar.gz files matches the hash of the .sha256 files: sha256sum --check *.sha256

Decompress the sources: mkdir -p ${APACHE_SEC_DIR}/httpd/conf/crs tar xfz coreruleset-*.tar.gz --strip-components=1 -C ${APACHE_SEC_DIR}/httpd/conf/crs chown -R root:root ${APACHE_SEC_DIR}/httpd/conf/crs chmod -R go-rx ${APACHE_SEC_DIR}/httpd/conf/crs

tar xfz ${MODSEC_VERSION}.tar.gz cd ${MODSEC_VERSION}

Install the packages needed for the build: apt install build-essential pkg-config libssl-dev libxml2-dev libxml2 libexpat1-dev \ libpcre3-dev libpcre3 liblua5.3-0 liblua5.3-dev libcurl4-openssl-dev libcurl4

Use these parameters for configuration: ./configure \ --prefix=${APACHE_SEC_DIR}/bin/${MODSEC_VERSION} \ --with-apxs=${APACHE_SEC_DIR}/bin/httpd/bin/apxs \ --with-apr=${APACHE_SEC_DIR}/bin/httpd/bin \ --with-apu=${APACHE_SEC_DIR}/bin/httpd/bin \ --enable-pcre-jit \ --enable-lua-cache

Compile the sources: make -j4

Check the binaries: make CFLAGS=-DMSC_TEST test

Install the binaries: make install

Create the symbolic link: ln -fsn ${MODSEC_VERSION} ${APACHE_SEC_DIR}/bin/modsecurity

Configure ModSecurity: if [ ! -f "${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf" ]; then cp modsecurity.conf-recommended ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf cp unicode.mapping ${APACHE_SEC_DIR}/httpd/conf/ chown -R root:root ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf ${APACHE_SEC_DIR}/httpd/conf/unicode.mapping chmod -R go-rx ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf ${APACHE_SEC_DIR}/httpd/conf/unicode.mapping sed -i 's/^SecRuleEngine DetectionOnly$/SecRuleEngine On/g' ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf sed -i 's+^SecAuditLog /var/log/modsec_audit.log$+SecAuditLog logs/modsec_audit.log+g' ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf sed -i 's/^SecStatusEngine On$/SecStatusEngine Off/g' ${APACHE_SEC_DIR}/httpd/conf/modsecurity.conf fi

Configure Core Rule Set (CRS): if [ ! -f "${APACHE_SEC_DIR}/httpd/conf/crs/crs-setup.conf" ]; then cp -p ${APACHE_SEC_DIR}/httpd/conf/crs/crs-setup.conf.example ${APACHE_SEC_DIR}/httpd/conf/crs/crs-setup.conf fi

Load the ModSecurity module in Apache httpd: echo " LoadFile /usr/lib/x86_64-linux-gnu/libxml2.so LoadFile /usr/lib/x86_64-linux-gnu/liblua5.3.so LoadModule security2_module ${APACHE_SEC_DIR}/bin/modsecurity/lib/mod_security2.so Include conf/modsecurity.conf <IfModule security2_module>  Include conf/crs/crs-setup.conf   Include conf/crs/rules/*.conf </IfModule> " >> ${APACHE_SEC_DIR}/httpd/conf/httpd-sec.conf
 * 1) ModSecurity
 * 1) Core Rule Set (CRS)

Links

 * Mozilla SSL Configuration Generator
 * Qualys SSL Labs - SSL Server Test
 * Check HTTP(S) Security Headers
 * Check the Revocation Lists (CRL) and the OCSP status of an (SSL) Certificate
 * HTTP/2 Test
 * Content Security Policy (CSP) Scanner
 * HTTP/3 & QUIC