diff --git a/mantle/kola/tests/fips/fips.go b/mantle/kola/tests/fips/fips.go index bc6ebff0e3..151a00c3be 100644 --- a/mantle/kola/tests/fips/fips.go +++ b/mantle/kola/tests/fips/fips.go @@ -129,6 +129,70 @@ func init() { } }`), }) + // Test that using TLS works in FIPS mode by having Ignition fetch + // a remote resource over HTTPS with FIPS compatible algorithms. + // See https://issues.redhat.com/browse/COS-3487 + // Note that 34.172.244.189, running RHCOS 9.6 (build 20260312-0) on + // Google Cloud Platform, provides HTTPS services using nginx-126:10.1. + register.RegisterTest(®ister.Test{ + Run: fipsEnableTestTLS, + ClusterSize: 1, + Name: `fips.enable.tls`, + Description: "Verify that fips enabled works if fetching a remote resource over HTTPS with FIPS compatible algorithms.", + Flags: []register.Flag{}, + Tags: []string{kola.NeedsInternetTag}, + Distros: []string{"rhcos"}, + Platforms: []string{"qemu"}, + ExcludeArchitectures: []string{"s390x", "ppc64le", "aarch64"}, // only test on x86_64 + UserData: conf.Ignition(`{ + "ignition": { + "config": { + "replace": { + "source": null, + "verification": {} + } + }, + "security": { + "tls": { + "certificateAuthorities": [ + { + "compression": "gzip", + "source": "data:;base64,H4sIAAAAAAAC/2SUTdN7yhbF5z7FnaduIUEY/AfdNFoQ2luYeUkkSLwk0fj0t57nnsk5Zw9/u2rV3lVrrf/+DEQGdv+jIhJiHasgRL+UcTBGVqOqoJdqQDEENY4q7G3jjqz64bkgmSjY2p8qKY3Gm0w1P7VOfYbvc+kCH9kM9AE1N1Q58G0APkKA0oi0nR+tMAp1p46NeKtUGGYXa59dMDXvpes0KXU2tHc0Z2PcBgjJD9x+4fYDf1kDbg7hqEFTLfZ9TYPECGIYYp04P+LZxeWw7nZM+cq68ueFHP/zPAh9oNW14QFNVUHaq3VtQHBWT5t93FhwWUnrFkyamb0KudwI1bgZ+jV86+TQaPrgrd/RvQRx+Xqx6lfffdnAi9FQTRw82PZ8HHM7Gt6IeaTV10vepwcr0iU/QLV250dMAquRsMi/rIJevXRV71auDPfnyVnHiD9UX4ukfUm5cGJkdr29FkGZ/DU58SQxrnjd9p/yAu+cgNL7JXq3j0oxd16NyiU3z+5bYyH5HK6NhTg5Y9J7labtJrhbee+zXBdD+7LGb42PLOOCbqoyfBp/93jvZBIBj6gGXwvCEsjE6q5o2iSGNe/EjLESWAdPw71i2EmRnCO22Y1k0i41HjGZaQLzaiM8r4kbDH3xewJdNBjhfh0ZGpTmt67ZfuLOYdjxwPJt4dh9RN/3Ba9LV78JzkjK0+rrV97xHamGieTUaRcqNVNxYdgPp5tRKtgt0PSg+wglmZ7obbN+cfNuR1JkJPLMJSx2tO1Pb+jtxnCJCpR3W8EOiGWaC8LlXZ9nT0nEJyeJm2iwTzRW/Km6Yrn189YdH5Y3Fy2K1OJT+VgDPoA939NEA2cG1m5s+jIENxlB4KgwB1SvUy0mXAd8k4WAUrVO8YmmEPqRCSiiGv3dEwhqysC+RHjcuDkE1f/FBKTXfpSRh97WU19wD/tV3W6NMRK2z0HSvf/mWOYnUX9ZFgI7MvEKKldoWtHGxSRxfLc9bbQrzSORUfLm2t2J3Bw42QVnTblbl4ww4DnmpaGN6OOuxKL79dTj7LqlPtcfH3vqK2XBC93VVzwavjTN+hnskvauGroj3Z4z442peDVjPXB4urDVFX8qsv924b7x7fuXaytdrbqyy53RL0r5my2udheyLWPJcHh6t5yZi5zjnEAxTRgeh+oRDpwkhnOunL/pQRjchO3LaPwaSuLkDs/HhqodOno6JA9BZsPYZ9BgdPPgKB9l1z/H9X3L8uU11Hv/6gSTZXpsGxm4Ts/24SbbT1pehxgCjh7XYY1Dc5KYqyh5tBYH/bk/vtbDHLgW/+yt83gTr1McTwczY08UWFIvfchVldSHYixka6XAN4lyEZinOkjjINfRNRpa3iuUs77t94NcgU8U7j6FtV0TCWTp7Qym5jn1jvupgkCKYuqEyhlhpig1gy0fs7mEuCi8ERNxt1R5+hHP5SZPX1AH1evyXCuzfZeP5uRVbNLbXbZ8UBvIw5tZtt1wqIKDRXA81H/+ML9ljVzt3wX+vwAAAP//bBEEnd0FAAA=" + } + ] + } + }, + "timeouts": {}, + "version": "3.4.0" + }, + "passwd": {}, + "storage": { + "files": [ + { + "group": { + "name": "root" + }, + "overwrite": true, + "path": "/etc/ignition-machine-config-encapsulated.json", + "user": { + "name": "root" + }, + "contents": { + "source": "data:,%7B%22metadata%22%3A%7B%22name%22%3A%22rendered-worker-1cc576110e0cf8396831ce4016f63900%22%2C%22selfLink%22%3A%22%2Fapis%2Fmachineconfiguration.openshift.io%2Fv1%2Fmachineconfigs%2Frendered-worker-1cc576110e0cf8396831ce4016f63900%22%2C%22uid%22%3A%2248871c03-899d-4332-a5f5-bef94e54b23f%22%2C%22resourceVersion%22%3A%224168%22%2C%22generation%22%3A1%2C%22creationTimestamp%22%3A%222019-11-04T15%3A54%3A08Z%22%2C%22annotations%22%3A%7B%22machineconfiguration.openshift.io%2Fgenerated-by-controller-version%22%3A%22bd846958bc95d049547164046a962054fca093df%22%7D%2C%22ownerReferences%22%3A%5B%7B%22apiVersion%22%3A%22machineconfiguration.openshift.io%2Fv1%22%2C%22kind%22%3A%22MachineConfigPool%22%2C%22name%22%3A%22worker%22%2C%22uid%22%3A%223d0dee9e-c9d6-4656-a4a9-81785b9ab01a%22%2C%22controller%22%3Atrue%2C%22blockOwnerDeletion%22%3Atrue%7D%5D%7D%2C%22spec%22%3A%7B%22osImageURL%22%3A%22registry.svc.ci.openshift.org%2Focp%2F4.3-2019-11-04-125204%40sha256%3A8a344c5b157bd01c3ca1abfcef0004fc39f5d69cac1cdaad0fd8dd332ad8e272%22%2C%22config%22%3A%7B%22ignition%22%3A%7B%22config%22%3A%7B%7D%2C%22security%22%3A%7B%22tls%22%3A%7B%7D%7D%2C%22timeouts%22%3A%7B%7D%2C%22version%22%3A%223.0.0%22%7D%2C%22networkd%22%3A%7B%7D%2C%22passwd%22%3A%7B%7D%2C%22storage%22%3A%7B%7D%2C%22systemd%22%3A%7B%7D%7D%2C%22kernelArguments%22%3A%5B%5D%2C%22fips%22%3Atrue%7D%7D", + "verification": {} + }, + "mode": 420 + }, + { + "path": "/var/resource/https-fips", + "contents": { + "source": "https://34.172.244.189:8443/index.html" + } + } + ] + } + }`), + }) } // Test: Run basic FIPS test @@ -137,3 +201,9 @@ func fipsEnableTest(c cluster.TestCluster) { c.AssertCmdOutputContains(m, `cat /proc/sys/crypto/fips_enabled`, "1") c.AssertCmdOutputContains(m, `update-crypto-policies --show`, "FIPS") } + +func fipsEnableTestTLS(c cluster.TestCluster) { + fipsEnableTest(c) + m := c.Machines()[0] + c.AssertCmdOutputContains(m, `cat /var/resource/https-fips`, "This file was served from an RHCOS FIPS-hardened server.") +} diff --git a/tests/containers/fips-nginx/Containerfile b/tests/containers/fips-nginx/Containerfile new file mode 100644 index 0000000000..3aced82647 --- /dev/null +++ b/tests/containers/fips-nginx/Containerfile @@ -0,0 +1,13 @@ +FROM registry.redhat.io/rhel10/nginx-126:10.1 + +ADD nginx.conf "${NGINX_CONF_PATH}" + +COPY index.html /usr/share/nginx/html/index.html + +# TLS material +USER 0 +COPY tls/ /etc/nginx/tls/ +RUN chown -R 1001:0 /etc/nginx/tls +USER 1001 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/tests/containers/fips-nginx/README.md b/tests/containers/fips-nginx/README.md new file mode 100644 index 0000000000..f728376a6e --- /dev/null +++ b/tests/containers/fips-nginx/README.md @@ -0,0 +1,28 @@ +# fips-nginx Container + +This is used by the `fips.enable.https` test to verify that using +TLS works in FIPS mode by having Ignition fetch a remote resource +over HTTPS with FIPS compatible algorithms. + +See https://catalog.redhat.com/en/software/containers/rhel10/nginx-126/677d3718e58b5a1ae5598058#overview + +To build the container using command: +`./build.sh ` + +To run the container image using command: +`podman run -d -p 8443:8443 --name fips-nginx fips-nginx` + +Remember to create firewall-rules to allow port 8443: +``` +gcloud compute firewall-rules create allow-nginx-fips-8443 \ + --action ALLOW \ + --direction INGRESS \ + --rules tcp:8443 \ + --source-ranges 0.0.0.0/0 \ + --target-tags nginx-fips-server \ + --description "Allow FIPS test access to nginx on port 8443" + +gcloud compute instances add-tags rhcos-fips-test \ + --zone us-central1-a \ + --tags nginx-fips-server +``` diff --git a/tests/containers/fips-nginx/build.sh b/tests/containers/fips-nginx/build.sh new file mode 100755 index 0000000000..0ccdb4a66b --- /dev/null +++ b/tests/containers/fips-nginx/build.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash + +# Run the image using command: +# podman run -d --name fips-nginx -p 8443:8443 fips-nginx +set -euo pipefail + +# Check if argument is provided +if [ $# -eq 0 ]; then + echo "Error: Missing IP address argument" + echo "Usage: $0 " + exit 1 +fi + +ip="$1" + +tmpdir="$(mktemp -d)" +cp Containerfile ${tmpdir} +cd ${tmpdir} + +# Prepare index.html +cat < index.html +This file was served from an RHCOS FIPS-hardened server. +EOF + +# Prepare nginx.conf +cat < nginx.conf +events {} + +http { + server { + listen 8443 ssl; + server_name _; + + # ---- FIPS-only TLS ---- + ssl_protocols TLSv1.2; + ssl_prefer_server_ciphers on; + + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256; + + ssl_certificate /etc/nginx/tls/fips-server.crt; + ssl_certificate_key /etc/nginx/tls/fips-server.key; + + location / { + root /usr/share/nginx/html; + index index.html; + } + } +} +EOF + +mkdir -p tls +pushd tls/ +# Prepare openssl.cnf +# The IP must point to an nginx server configured with FIPS-compliant ciphers +cat < openssl.cnf +[ req ] +default_bits = 3072 +distinguished_name = dn +prompt = no +string_mask = utf8only +req_extensions = req_ext + +[ dn ] +CN = FIPS TLS Test Server + +[ req_ext ] +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = critical, serverAuth +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = ${ip} +SSLEOF + +# Prepare key and crt +## Generate the private key (FIPS-approved) +openssl genpkey \ + -algorithm RSA \ + -pkeyopt rsa_keygen_bits:3072 \ + -out fips-server.key + +## Generate CSR (still FIPS-only) +openssl req -new -key fips-server.key -out fips-server.csr -config openssl.cnf + +## Self-sign the certificate (TLS-compatible + FIPS) +openssl x509 -req \ + -in fips-server.csr \ + -signkey fips-server.key \ + -out fips-server.crt \ + -days 3650 \ + -sha256 \ + -extfile openssl.cnf \ + -extensions req_ext + +# Verify SAN present +openssl x509 -in fips-server.crt -noout -text | grep -A2 "Subject Alternative Name" + +openssl verify \ + -provider fips \ + -CAfile fips-server.crt \ + fips-server.crt + +rm fips-server.csr openssl.cnf + +popd + +podman build -t fips-nginx .