From 9261a765bd98745939ab23056aaa141dd87baa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:13:01 -0300 Subject: [PATCH 1/8] fix(httpd): harden the xCAT Apache configuration Add standard security response headers (X-Frame-Options, X-Content-Type- Options, Content-Security-Policy, X-Permitted-Cross-Domain-Policies) to the /install and /tftpboot directories, mask the server banner with "ServerTokens Prod", and drop the Includes (SSI) and MultiViews options from those file-serving directories. Indexes on /install/postscripts, /install/post and the doc directory are left intact so directory browsing still works where xCAT relies on it. The Header directives are wrapped in so a server whose mod_headers is not loaded still starts cleanly instead of failing on an unknown directive. On Debian/Ubuntu, where mod_headers is not enabled by default, the xCAT and xCATsn package postinst scripts run "a2enmod headers" before restarting Apache so the headers take effect there as well; on RHEL/SLES the module is loaded by default and needs no action. Recovered from the unmerged lenovobuild branch (originals 7ee0c129, 85c8bc09, d4d1783a), adapted: the deprecated X-XSS-Protection header and the mod_allowmethods-dependent AllowMethods directive are omitted, and the Header directives use "set" rather than "append". Co-authored-by: Jarrod Johnson <10814490+jjohnson42@users.noreply.github.com> --- xCAT/debian/postinst | 8 ++++++++ xCAT/xcat.conf | 18 ++++++++++++++++-- xCAT/xcat.conf.apach24 | 18 ++++++++++++++++-- xCATsn/debian/postinst | 8 ++++++++ xCATsn/xcat.conf | 18 ++++++++++++++++-- xCATsn/xcat.conf.apach24 | 18 ++++++++++++++++-- 6 files changed, 80 insertions(+), 8 deletions(-) diff --git a/xCAT/debian/postinst b/xCAT/debian/postinst index 299450e15..d47009379 100644 --- a/xCAT/debian/postinst +++ b/xCAT/debian/postinst @@ -45,6 +45,14 @@ case "$1" in ln -s -f /etc/apache2/conf-available/xcat.conf.apach24 /etc/apache2/conf-enabled/xcat.conf + # xcat.conf sets security response headers via mod_headers, which is + # not enabled by default on Debian/Ubuntu. Enable it so the headers + # take effect (the directives are wrapped in + # so a missing module never breaks the apache restart below). + if command -v a2enmod >/dev/null 2>&1; then + a2enmod -q headers >/dev/null 2>&1 || : + fi + /etc/init.d/apache2 restart # Let rsyslogd perform close of any open files diff --git a/xCAT/xcat.conf b/xCAT/xcat.conf index 6138086c4..c205a24f9 100644 --- a/xCAT/xcat.conf +++ b/xCAT/xcat.conf @@ -4,17 +4,31 @@ # http://localhost/xcat-doc/ # Updates to xCAT/xcat.conf should also be made to xCATsn/xcat.conf # +ServerTokens Prod + AliasMatch ^/install/(.*)$ "/install/$1" AliasMatch ^/tftpboot/(.*)$ "/tftpboot/$1" - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all diff --git a/xCAT/xcat.conf.apach24 b/xCAT/xcat.conf.apach24 index d1720149f..0b032f246 100644 --- a/xCAT/xcat.conf.apach24 +++ b/xCAT/xcat.conf.apach24 @@ -4,16 +4,30 @@ # http://localhost/xcat-doc/ # Updates to xCAT/xcat.conf should also be made to xCATsn/xcat.conf # +ServerTokens Prod + AliasMatch ^/install/(.*)$ "/install/$1" AliasMatch ^/tftpboot/(.*)$ "/tftpboot/$1" - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted diff --git a/xCATsn/debian/postinst b/xCATsn/debian/postinst index c7056f716..f674fb7eb 100644 --- a/xCATsn/debian/postinst +++ b/xCATsn/debian/postinst @@ -56,6 +56,14 @@ case "$1" in update-rc.d $apachedaemon enable + # xcat.conf sets security response headers via mod_headers, which is not + # enabled by default on Debian/Ubuntu. Enable it so the headers take + # effect (directives are wrapped in so a missing + # module never breaks the apache reload below). + if command -v a2enmod >/dev/null 2>&1; then + a2enmod -q headers >/dev/null 2>&1 || : + fi + if [ -f "/proc/cmdline" ]; then # prevent running it during install into chroot image if [ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1; then systemctl start xcatd.service diff --git a/xCATsn/xcat.conf b/xCATsn/xcat.conf index 6138086c4..c205a24f9 100644 --- a/xCATsn/xcat.conf +++ b/xCATsn/xcat.conf @@ -4,17 +4,31 @@ # http://localhost/xcat-doc/ # Updates to xCAT/xcat.conf should also be made to xCATsn/xcat.conf # +ServerTokens Prod + AliasMatch ^/install/(.*)$ "/install/$1" AliasMatch ^/tftpboot/(.*)$ "/tftpboot/$1" - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all diff --git a/xCATsn/xcat.conf.apach24 b/xCATsn/xcat.conf.apach24 index 0abfff396..251111903 100644 --- a/xCATsn/xcat.conf.apach24 +++ b/xCATsn/xcat.conf.apach24 @@ -4,16 +4,30 @@ # http://localhost/xcat-doc/ # Updates to xCAT/xcat.conf should also be made to xCATsn/xcat.conf # +ServerTokens Prod + AliasMatch ^/install/(.*)$ "/install/$1" AliasMatch ^/tftpboot/(.*)$ "/tftpboot/$1" - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted From f6ba29ef80173f8d7a3bfc2a9d00fe71b9197590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:50:58 -0300 Subject: [PATCH 2/8] build(spec): activate the hardened Apache headers on SUSE The security response headers added to xcat.conf are wrapped in , so they only take effect where mod_headers is loaded. EL loads it by default and the Debian postinst enables it with a2enmod, but SUSE apache2 does not load it by default, so the headers stayed inactive there. Enable it in the RPM %post for the apache2 case, guarded so it is a no-op on EL (where a2enmod is absent and mod_headers is already loaded). Verified on openSUSE Leap 15.6: mod_headers is off by default, the hardened config still starts, and after a2enmod the served response carries all four security headers. The need to load mod_headers on SUSE was identified in the lenovobuild branch (660df81379ce / xcat.conf.apach24.sles), here done via a2enmod rather than a separate config file. Co-authored-by: Jarrod Johnson <10814490+jjohnson42@users.noreply.github.com> --- xCAT/xCAT.spec | 7 +++++++ xCATsn/xCATsn.spec | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/xCAT/xCAT.spec b/xCAT/xCAT.spec index 57031a8b5..93fc46571 100644 --- a/xCAT/xCAT.spec +++ b/xCAT/xCAT.spec @@ -225,6 +225,13 @@ then cp /etc/%httpconfigdir/conf.orig/xcat.conf.apach24 /etc/apache2/conf.d/xcat.conf fi +# On SUSE apache2, mod_headers is not loaded by default; enable it so the +# security response headers in xcat.conf take effect (a no-op where a2enmod +# is absent, e.g. httpd on EL where mod_headers is already loaded). +if [ -e /etc/apache2/conf.d/xcat.conf ] && command -v a2enmod >/dev/null 2>&1; then + a2enmod headers >/dev/null 2>&1 || : +fi + # Let rsyslogd perform close of any open files if [ -e /var/run/rsyslogd.pid ]; then kill -HUP $(/dev/null 2>&1 || : diff --git a/xCATsn/xCATsn.spec b/xCATsn/xCATsn.spec index c69e43643..9735e958c 100644 --- a/xCATsn/xCATsn.spec +++ b/xCATsn/xCATsn.spec @@ -169,6 +169,13 @@ if [ -n "$version" ]; then fi fi +# On SUSE apache2, mod_headers is not loaded by default; enable it so the +# security response headers in xcat.conf take effect (a no-op where a2enmod +# is absent, e.g. httpd on EL where mod_headers is already loaded). +if [ -e /etc/apache2/conf.d/xcat.conf ] && command -v a2enmod >/dev/null 2>&1; then + a2enmod headers >/dev/null 2>&1 || : +fi + # Let rsyslogd perform close of any open files if [ -e /var/run/rsyslogd.pid ]; then From 06eced0951456b39e1e01b34ca01eca58fa575aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:17:19 -0300 Subject: [PATCH 3/8] test(httpd): verify security response headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com> --- xCAT-test/autotest/testcase/httpd/cases0 | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 xCAT-test/autotest/testcase/httpd/cases0 diff --git a/xCAT-test/autotest/testcase/httpd/cases0 b/xCAT-test/autotest/testcase/httpd/cases0 new file mode 100644 index 000000000..27d0c3ee7 --- /dev/null +++ b/xCAT-test/autotest/testcase/httpd/cases0 @@ -0,0 +1,30 @@ +start:httpd_security_headers_install +description:verify xcat.conf sets the security response headers on /install +label:mn_only,ci_test,httpd +cmd:curl -sSI --max-time 15 http://localhost/install/ 2>&1 +check:rc==0 +check:output=~X-Frame-Options:\s*SAMEORIGIN +check:output=~X-Content-Type-Options:\s*nosniff +check:output=~Content-Security-Policy:\s*script-src +check:output=~X-Permitted-Cross-Domain-Policies:\s*none +end + +start:httpd_security_headers_tftpboot +description:verify xcat.conf sets the security response headers on /tftpboot +label:mn_only,ci_test,httpd +cmd:curl -sSI --max-time 15 http://localhost/tftpboot/ 2>&1 +check:rc==0 +check:output=~X-Frame-Options:\s*SAMEORIGIN +check:output=~X-Content-Type-Options:\s*nosniff +check:output=~Content-Security-Policy:\s*script-src +check:output=~X-Permitted-Cross-Domain-Policies:\s*none +end + +start:httpd_server_banner_masked +description:verify ServerTokens Prod masks the Apache version in the Server header +label:mn_only,ci_test,httpd +cmd:curl -sSI --max-time 15 http://localhost/install/ 2>&1 +check:rc==0 +check:output=~Server:\s*Apache +check:output!~Apache/[0-9] +end From 4c82e02a7bce820b38d0f1a10c3f58cce5c610ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:45:52 -0300 Subject: [PATCH 4/8] fix(httpd): reload hardened config after RPM upgrades --- xCAT/xCAT.spec | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/xCAT/xCAT.spec b/xCAT/xCAT.spec index 93fc46571..4e563a876 100644 --- a/xCAT/xCAT.spec +++ b/xCAT/xCAT.spec @@ -265,6 +265,26 @@ fi mkdir -p /var/log/xcat date >> /var/log/xcat/upgrade.log $RPM_INSTALL_PREFIX0/sbin/xcatconfig -u -V >> /var/log/xcat/upgrade.log 2>&1 + +# The package replaces xcat.conf before this upgrade path runs. Reload an +# active web server so the new security headers and ServerTokens setting take +# effect immediately, but do not try to manage services while building an +# image in a chroot. +if [ -f "/proc/cmdline" ] && [ "x$(stat -c '%i %d' /)" == "x$(stat -c '%i %d' /proc/1/root/. 2>/dev/null)" ]; then + if [ -e "/etc/redhat-release" ]; then + apachedaemon='httpd' + else + apachedaemon='apache2' + fi + + if command -v systemctl >/dev/null 2>&1; then + if systemctl is-active --quiet "$apachedaemon"; then + systemctl reload "$apachedaemon" >/dev/null 2>&1 || : + fi + elif [ -x "/etc/init.d/$apachedaemon" ]; then + /etc/init.d/$apachedaemon reload >/dev/null 2>&1 || : + fi +fi fi exit 0 From f8267cb8f38e366f6ec35c84276fa7f227841b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:49:22 -0300 Subject: [PATCH 5/8] test(httpd): cover package activation across distros --- .../autotest/bundle/rhels_ppc_daily.bundle | 3 ++ .../autotest/bundle/rhels_ppcle_daily.bundle | 3 ++ .../autotest/bundle/rhels_x86_daily.bundle | 3 ++ .../autotest/bundle/sles_ppc_daily.bundle | 3 ++ .../autotest/bundle/sles_ppcle_daily.bundle | 3 ++ .../autotest/bundle/sles_x86_daily.bundle | 3 ++ .../autotest/bundle/ubuntu_ppcle_daily.bundle | 3 ++ .../autotest/bundle/ubuntu_x86_daily.bundle | 3 ++ xCAT-test/unit/httpd_security_packaging.t | 48 +++++++++++++++++++ 9 files changed, 72 insertions(+) create mode 100644 xCAT-test/unit/httpd_security_packaging.t diff --git a/xCAT-test/autotest/bundle/rhels_ppc_daily.bundle b/xCAT-test/autotest/bundle/rhels_ppc_daily.bundle index 50a0de914..d95863ff3 100644 --- a/xCAT-test/autotest/bundle/rhels_ppc_daily.bundle +++ b/xCAT-test/autotest/bundle/rhels_ppc_daily.bundle @@ -100,6 +100,9 @@ gettab_key_table getmacs_d getmacs_f_D getmacs_noderange +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked tabdump_servicenode lsdef_a lsdef_null diff --git a/xCAT-test/autotest/bundle/rhels_ppcle_daily.bundle b/xCAT-test/autotest/bundle/rhels_ppcle_daily.bundle index a39991440..5e1884cb6 100644 --- a/xCAT-test/autotest/bundle/rhels_ppcle_daily.bundle +++ b/xCAT-test/autotest/bundle/rhels_ppcle_daily.bundle @@ -106,6 +106,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/rhels_x86_daily.bundle b/xCAT-test/autotest/bundle/rhels_x86_daily.bundle index 608e862ee..a8a5e53fb 100644 --- a/xCAT-test/autotest/bundle/rhels_x86_daily.bundle +++ b/xCAT-test/autotest/bundle/rhels_x86_daily.bundle @@ -106,6 +106,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/sles_ppc_daily.bundle b/xCAT-test/autotest/bundle/sles_ppc_daily.bundle index fcb5b2d5c..7122a39ae 100644 --- a/xCAT-test/autotest/bundle/sles_ppc_daily.bundle +++ b/xCAT-test/autotest/bundle/sles_ppc_daily.bundle @@ -68,6 +68,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/sles_ppcle_daily.bundle b/xCAT-test/autotest/bundle/sles_ppcle_daily.bundle index db5fb3832..d24c5268c 100644 --- a/xCAT-test/autotest/bundle/sles_ppcle_daily.bundle +++ b/xCAT-test/autotest/bundle/sles_ppcle_daily.bundle @@ -72,6 +72,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/sles_x86_daily.bundle b/xCAT-test/autotest/bundle/sles_x86_daily.bundle index 33c28f32a..875c477d1 100644 --- a/xCAT-test/autotest/bundle/sles_x86_daily.bundle +++ b/xCAT-test/autotest/bundle/sles_x86_daily.bundle @@ -72,6 +72,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/ubuntu_ppcle_daily.bundle b/xCAT-test/autotest/bundle/ubuntu_ppcle_daily.bundle index b0182265c..f5aa4d2f2 100644 --- a/xCAT-test/autotest/bundle/ubuntu_ppcle_daily.bundle +++ b/xCAT-test/autotest/bundle/ubuntu_ppcle_daily.bundle @@ -48,6 +48,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/autotest/bundle/ubuntu_x86_daily.bundle b/xCAT-test/autotest/bundle/ubuntu_x86_daily.bundle index 154c66025..16681fb82 100644 --- a/xCAT-test/autotest/bundle/ubuntu_x86_daily.bundle +++ b/xCAT-test/autotest/bundle/ubuntu_x86_daily.bundle @@ -48,6 +48,9 @@ gettab_H gettab_err gettab_h gettab_key_table +httpd_security_headers_install +httpd_security_headers_tftpboot +httpd_server_banner_masked lsdef_a lsdef_null lsdef_t diff --git a/xCAT-test/unit/httpd_security_packaging.t b/xCAT-test/unit/httpd_security_packaging.t new file mode 100644 index 000000000..5e4679862 --- /dev/null +++ b/xCAT-test/unit/httpd_security_packaging.t @@ -0,0 +1,48 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Spec; +use FindBin; +use Test::More; + +my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' ); + +sub read_file { + my ($relative_path) = @_; + my $path = File::Spec->catfile( $repo_root, split( '/', $relative_path ) ); + open( my $fh, '<', $path ) or die "Unable to read $path: $!"; + my $contents = do { local $/; <$fh> }; + close($fh); + return $contents; +} + +my $xcat_spec = read_file('xCAT/xCAT.spec'); +like( $xcat_spec, qr{a2enmod headers}, + 'management RPM enables mod_headers where Apache requires it' ); +like( $xcat_spec, + qr{if \[ "\$1" = "1" \]; then.*?xcatconfig -i.*?else.*?xcatconfig -u -V.*?systemctl is-active --quiet "\$apachedaemon".*?systemctl reload "\$apachedaemon".*?/etc/init\.d/\$apachedaemon reload}s, + 'management RPM reloads an active Apache service after upgrades' ); +like( $xcat_spec, qr{apachedaemon='httpd'.*?apachedaemon='apache2'}s, + 'management RPM covers both EL and SUSE Apache service names' ); +like( $xcat_spec, + qr{/proc/1/root/\. 2>/dev/null.*?systemctl is-active --quiet "\$apachedaemon"}s, + 'management RPM skips service management inside image chroots' ); + +my $xcatsn_spec = read_file('xCATsn/xCATsn.spec'); +like( $xcatsn_spec, qr{a2enmod headers}, + 'service-node RPM enables mod_headers where Apache requires it' ); +like( $xcatsn_spec, + qr{# for install or upgrade restart the daemon.*?/etc/init\.d/\$apachedaemon reload.*?systemctl reload \$apacheserviceunit}s, + 'service-node RPM reloads Apache after installs and upgrades' ); + +my $xcat_postinst = read_file('xCAT/debian/postinst'); +like( $xcat_postinst, qr{a2enmod -q headers.*?/etc/init\.d/apache2 restart}s, + 'management Debian package enables mod_headers before restarting Apache' ); + +my $xcatsn_postinst = read_file('xCATsn/debian/postinst'); +like( $xcatsn_postinst, + qr{a2enmod -q headers.*?/etc/init\.d/\$apachedaemon reload}s, + 'service-node Debian package enables mod_headers before reloading Apache' ); + +done_testing(); From ddf4d3519412e8b7df7612aab9fb1ce809740c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:50:19 -0300 Subject: [PATCH 6/8] docs(httpd): align hardening guidance with defaults --- .../advanced/security/apache_hardening.rst | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/docs/source/advanced/security/apache_hardening.rst b/docs/source/advanced/security/apache_hardening.rst index 22787ffbc..a07ac58ef 100644 --- a/docs/source/advanced/security/apache_hardening.rst +++ b/docs/source/advanced/security/apache_hardening.rst @@ -23,13 +23,27 @@ provisioning scripts crawl recursively. **Apache 2.4** (RHEL 7+, SLES 12+, Ubuntu 16.04+):: # /etc/httpd/conf.d/xcat.conf + ServerTokens Prod + - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require all granted @@ -47,14 +61,28 @@ provisioning scripts crawl recursively. **Apache 2.2** (RHEL 6, SLES 11):: # /etc/httpd/conf.d/xcat.conf + ServerTokens Prod + - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Order allow,deny Allow from all @@ -79,6 +107,23 @@ provisioning scripts crawl recursively. download all files from these directories and depend on Apache directory listings to discover file paths. +Security Response Headers and Server Banner +------------------------------------------- + +xCAT configures ``ServerTokens Prod`` to omit the Apache version and operating +system from the HTTP ``Server`` header. It also applies the following response +headers to ``/install`` and ``/tftpboot``: + +* ``X-Frame-Options: SAMEORIGIN`` +* ``X-Content-Type-Options: nosniff`` +* ``Content-Security-Policy: script-src 'self' 'unsafe-eval'`` +* ``X-Permitted-Cross-Domain-Policies: none`` + +These directives require ``mod_headers``. The xCAT packages enable that module +on distributions where it is not loaded by default and activate the updated +configuration during installation or upgrade. Keep the ``Header always set`` +directives when adding local access-control rules to either directory. + Sensitive Directories --------------------- @@ -141,7 +186,13 @@ For additional protection, restrict access to the provisioning subnet:: # Apache 2.4+ - Options FollowSymLinks Includes MultiViews + Options FollowSymLinks + + Header always set X-Frame-Options SAMEORIGIN + Header always set X-Content-Type-Options nosniff + Header always set Content-Security-Policy "script-src 'self' 'unsafe-eval'" + Header always set X-Permitted-Cross-Domain-Policies none + AllowOverride None Require ip 10.0.0.0/16 From ae0d790adb79ce20c122c778574a63c5eceb6940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:01:46 -0300 Subject: [PATCH 7/8] fix(xcatsn): name SLES Apache service unit --- xCATsn/xCATsn.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/xCATsn/xCATsn.spec b/xCATsn/xCATsn.spec index 9735e958c..02a620e5a 100644 --- a/xCATsn/xCATsn.spec +++ b/xCATsn/xCATsn.spec @@ -226,6 +226,7 @@ if [ -e "/etc/redhat-release" ]; then apacheserviceunit='httpd.service' else # SuSE apachedaemon='apache2' + apacheserviceunit='apache2.service' fi # enable and reload the web server on linux [ -e "/etc/init.d/$apachedaemon" ] && chkconfig $apachedaemon on From 951562a45ab44dfa226a8b9e80277eeb1023329e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:02:40 -0300 Subject: [PATCH 8/8] test(httpd): require SLES service unit --- xCAT-test/unit/httpd_security_packaging.t | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xCAT-test/unit/httpd_security_packaging.t b/xCAT-test/unit/httpd_security_packaging.t index 5e4679862..6fc6f18d4 100644 --- a/xCAT-test/unit/httpd_security_packaging.t +++ b/xCAT-test/unit/httpd_security_packaging.t @@ -32,6 +32,9 @@ like( $xcat_spec, my $xcatsn_spec = read_file('xCATsn/xCATsn.spec'); like( $xcatsn_spec, qr{a2enmod headers}, 'service-node RPM enables mod_headers where Apache requires it' ); +like( $xcatsn_spec, + qr{apacheserviceunit='httpd\.service'.*?apacheserviceunit='apache2\.service'}s, + 'service-node RPM covers both EL and SUSE Apache service units' ); like( $xcatsn_spec, qr{# for install or upgrade restart the daemon.*?/etc/init\.d/\$apachedaemon reload.*?systemctl reload \$apacheserviceunit}s, 'service-node RPM reloads Apache after installs and upgrades' );