Doublecheck if real FIPS host available in GH actions

Closes https://github.com/keycloak/keycloak/issues/15069
This commit is contained in:
rmartinc 2023-02-09 10:14:20 +01:00 committed by Marek Posolda
parent 22e256149c
commit 5b626231d9
6 changed files with 142 additions and 15 deletions

6
.github/fake_fips/Makefile vendored Normal file
View file

@ -0,0 +1,6 @@
obj-m = fake_fips.o
KVERSION = $(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

72
.github/fake_fips/fake_fips.c vendored Normal file
View file

@ -0,0 +1,72 @@
/*
* Copyright 2023 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* https://github.com/torvalds/linux/blob/master/crypto/fips.c
* https://pointer-overloading.blogspot.com/2013/09/linux-creating-entry-in-proc-file.html
*/
#include <linux/module.h>
#include <linux/sysctl.h>
int fips_enabled = 1;
static struct ctl_table crypto_sysctl_table[] = {
{
.procname = "fips_enabled",
.data = &fips_enabled,
.maxlen = sizeof(int),
.mode = 0444,
.proc_handler = proc_dointvec
},
{}
};
static struct ctl_table crypto_dir_table[] = {
{
.procname = "crypto",
.mode = 0555,
.child = crypto_sysctl_table
},
{}
};
static struct ctl_table_header *crypto_sysctls;
static void crypto_proc_fips_init(void)
{
crypto_sysctls = register_sysctl_table(crypto_dir_table);
}
static void crypto_proc_fips_exit(void)
{
unregister_sysctl_table(crypto_sysctls);
}
static int __init fips_init(void)
{
crypto_proc_fips_init();
return 0;
}
static void __exit fips_exit(void)
{
crypto_proc_fips_exit();
}
MODULE_LICENSE("GPL");
subsys_initcall(fips_init);
module_exit(fips_exit);

17
.github/scripts/run-fips-it.sh vendored Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
dnf install -y java-17-openjdk-devel crypto-policies-scripts
fips-mode-setup --enable --no-bootcfg
fips-mode-setup --is-enabled
if [ $? -ne 0 ]; then
exit 1
fi
STRICT_OPTIONS=""
if [ "$1" = "strict" ]; then
STRICT_OPTIONS="-Dauth.server.fips.mode=strict -Dauth.server.supported.keystore.types=BCFKS -Dauth.server.keystore.type=bcfks -Dauth.server.supported.rsa.key.sizes=2048,4096"
fi
echo "STRICT_OPTIONS: $STRICT_OPTIONS"
TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh fips`
echo "Tests: $TESTS"
export JAVA_HOME=/etc/alternatives/java_sdk_17
./mvnw test -Dsurefire.rerunFailingTestsCount=$SUREFIRE_RERUN_FAILING_COUNT -nsu -B -Pauth-server-quarkus,auth-server-fips140-2 -Dcom.redhat.fips=false $STRICT_OPTIONS -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh

15
.github/scripts/run-fips-ut.sh vendored Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
dnf install -y java-17-openjdk-devel crypto-policies-scripts
fips-mode-setup --enable --no-bootcfg
fips-mode-setup --is-enabled
if [ $? -ne 0 ]; then
exit 1
fi
echo "fips.provider.7=XMLDSig" >>/etc/alternatives/java_sdk_17/conf/security/java.security
export JAVA_HOME=/etc/alternatives/java_sdk_17
./mvnw test -nsu -B -am -pl crypto/default,crypto/fips1402 -Dcom.redhat.fips=true
if [ $? -ne 0 ]; then
exit 1
fi
./mvnw test -nsu -B -am -pl crypto/default,crypto/fips1402 -Dcom.redhat.fips=true -Dorg.bouncycastle.fips.approved_only=true

View file

@ -390,15 +390,20 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Fake fips
run: |
cd .github/fake_fips
make
sudo insmod fake_fips.ko
- id: unit-test-setup
name: Unit test setup
uses: ./.github/actions/unit-test-setup
with:
jdk-version: 17
- name: Run crypto tests (BCFIPS non-approved mode)
run: ./mvnw test -nsu -B -am -pl crypto/default,crypto/fips1402,crypto/elytron -Dcom.redhat.fips=true
- name: Run crypto tests (BCFIPS approved mode)
run: ./mvnw test -nsu -B -am -pl crypto/default,crypto/fips1402,crypto/elytron -Dcom.redhat.fips=true -Dorg.bouncycastle.fips.approved_only=true
- name: Run crypto tests
run: docker run --rm --workdir /github/workspace -v "${{ github.workspace }}":"/github/workspace" -v "$HOME/.m2":"/root/.m2" registry.access.redhat.com/ubi8/ubi:latest .github/scripts/run-fips-ut.sh
- name: Upload JVM Heapdumps
if: always()
@ -424,21 +429,23 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Fake fips
run: |
cd .github/fake_fips
make
sudo insmod fake_fips.ko
- id: integration-test-setup
name: Integration test setup
uses: ./.github/actions/integration-test-setup
with:
jdk-version: 17
- name: Prepare Quarkus distribution with BCFIPS
run: ./mvnw install -nsu -B -e -pl testsuite/integration-arquillian/servers/auth-server/quarkus -Pauth-server-quarkus,auth-server-fips140-2
- name: Run base tests
run: |
declare -A PARAMS
PARAMS["non-strict"]=""
PARAMS["strict"]="-Dauth.server.fips.mode=strict -Dauth.server.supported.keystore.types=BCFKS -Dauth.server.keystore.type=bcfks -Dauth.server.supported.rsa.key.sizes=2048,4096"
TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh fips`
echo "Tests: $TESTS"
./mvnw test -Dsurefire.rerunFailingTestsCount=${{ env.SUREFIRE_RERUN_FAILING_COUNT }} -nsu -B -Pauth-server-quarkus,auth-server-fips140-2 ${PARAMS["${{ matrix.mode }}"]} -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
run: docker run --rm --workdir /github/workspace -e "SUREFIRE_RERUN_FAILING_COUNT" -v "${{ github.workspace }}":"/github/workspace" -v "$HOME/.m2":"/root/.m2" registry.access.redhat.com/ubi8/ubi:latest .github/scripts/run-fips-it.sh ${{ matrix.mode }}
- name: Upload JVM Heapdumps
if: always()

View file

@ -11,13 +11,14 @@ import org.keycloak.common.profile.ProfileException;
import org.keycloak.common.profile.PropertiesFileProfileConfigResolver;
import org.keycloak.common.profile.PropertiesProfileConfigResolver;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
@ -69,7 +70,12 @@ public class ProfileTest {
}
Assert.assertEquals(Profile.ProfileName.DEFAULT, profile.getName());
assertEquals(profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Profile.Feature.CLIENT_SECRET_ROTATION, Profile.Feature.UPDATE_EMAIL);
Set<Profile.Feature> disabledFeatutes = new HashSet<>(Arrays.asList(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Profile.Feature.CLIENT_SECRET_ROTATION, Profile.Feature.UPDATE_EMAIL));
// KERBEROS can be disabled (i.e. FIPS mode disables SunJGSS provider)
if (Profile.Feature.KERBEROS.getType() == Profile.Feature.Type.DISABLED_BY_DEFAULT) {
disabledFeatutes.add(Profile.Feature.KERBEROS);
}
assertEquals(profile.getDisabledFeatures(), disabledFeatutes);
assertEquals(profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.DECLARATIVE_USER_PROFILE, Profile.Feature.CLIENT_SECRET_ROTATION, Profile.Feature.UPDATE_EMAIL);
}
@ -226,8 +232,12 @@ public class ProfileTest {
Assert.assertTrue(Profile.isFeatureEnabled(PREVIEW_FEATURE));
}
public static void assertEquals(Set<Profile.Feature> actual, Collection<Profile.Feature> expected) {
assertEquals(actual, expected.toArray(new Profile.Feature[0]));
}
public static void assertEquals(Set<Profile.Feature> actual, Profile.Feature... expected) {
Profile.Feature[] a = actual.toArray(new Profile.Feature[actual.size()]);
Profile.Feature[] a = actual.toArray(new Profile.Feature[0]);
Arrays.sort(a, new FeatureComparator());
Arrays.sort(expected, new FeatureComparator());
Assert.assertArrayEquals(a, expected);