feat(fips): fips inside, AWS patch-up and docker improvements

This commit is contained in:
Daniel Hougaard
2025-07-07 21:56:07 +04:00
parent ce88b0cbb1
commit 30e901c00c
17 changed files with 1048 additions and 59 deletions
+26
View File
@@ -73,6 +73,17 @@ RUN apt-get update && apt-get install -y \
# Configure ODBC # Configure ODBC
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini
# Build and install FIPS validated OpenSSL
WORKDIR /openssl-build
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
&& tar -xf openssl-3.1.2.tar.gz \
&& cd openssl-3.1.2 \
&& ./Configure enable-fips \
&& make \
&& make install_fips
COPY backend/package*.json ./ COPY backend/package*.json ./
RUN npm ci --only-production RUN npm ci --only-production
@@ -103,6 +114,15 @@ RUN apt-get update && apt-get install -y \
# Configure ODBC # Configure ODBC
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini
# Build and install FIPS validated OpenSSL
WORKDIR /openssl-build
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
&& tar -xf openssl-3.1.2.tar.gz \
&& cd openssl-3.1.2 \
&& ./Configure enable-fips \
&& make \
&& make install_fips
COPY backend/package*.json ./ COPY backend/package*.json ./
RUN npm ci --only-production RUN npm ci --only-production
@@ -173,6 +193,12 @@ ENV STANDALONE_MODE true
ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/ ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/
ENV NODE_OPTIONS="--max-old-space-size=1024" ENV NODE_OPTIONS="--max-old-space-size=1024"
# FIPS mode of operation:
ENV OPENSSL_CONF=/backend/nodejs.fips.cnf
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
ENV NODE_OPTIONS=--force-fips
ENV FIPS_ENABLED=true
WORKDIR /backend WORKDIR /backend
ENV TELEMETRY_ENABLED true ENV TELEMETRY_ENABLED true
+16 -3
View File
@@ -1,7 +1,5 @@
FROM node:20-slim FROM node:20-slim
RUN echo "RUNNING FIPS BUILD"
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver) # Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
build-essential \ build-essential \
@@ -29,11 +27,26 @@ RUN apt-get install -y \
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
# Build and install SoftHSM2
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
WORKDIR ${SOFTHSM2_SOURCES}
RUN git checkout ${SOFTHSM2_VERSION} -b ${SOFTHSM2_VERSION} \
&& sh autogen.sh \
&& ./configure --prefix=/usr/local --disable-gost \
&& make \
&& make install
WORKDIR /root WORKDIR /root
RUN rm -fr ${SOFTHSM2_SOURCES}
# Install pkcs11-tool # Install pkcs11-tool
RUN apt-get install -y opensc RUN apt-get install -y opensc
RUN mkdir -p /etc/softhsm2/tokens && \
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
# Build and install FIPS validated OpenSSL
WORKDIR /openssl-build WORKDIR /openssl-build
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
&& tar -xf openssl-3.1.2.tar.gz \ && tar -xf openssl-3.1.2.tar.gz \
@@ -59,7 +72,7 @@ RUN npm install
COPY . . COPY . .
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV OPENSSL_CONF=/app/nodejs.cnf ENV OPENSSL_CONF=/app/nodejs.fips.cnf
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
ENV NODE_OPTIONS=--force-fips ENV NODE_OPTIONS=--force-fips
ENV FIPS_ENABLED=true ENV FIPS_ENABLED=true
+881 -40
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -84,6 +84,7 @@
"@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-attributes": "^7.24.7",
"@babel/preset-env": "^7.18.10", "@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.24.7", "@babel/preset-react": "^7.24.7",
"@smithy/types": "^4.3.1",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/jmespath": "^0.15.2", "@types/jmespath": "^0.15.2",
@@ -12,6 +12,8 @@ import handlebars from "handlebars";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { crypto } from "@app/lib/crypto";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -39,8 +41,11 @@ type TDeleteElastiCacheUserInput = z.infer<typeof DeleteElasticCacheUserSchema>;
const ElastiCacheUserManager = (credentials: TBasicAWSCredentials, region: string) => { const ElastiCacheUserManager = (credentials: TBasicAWSCredentials, region: string) => {
const elastiCache = new ElastiCache({ const elastiCache = new ElastiCache({
region, region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials credentials
}); });
const infisicalGroup = "infisical-managed-group-elasticache"; const infisicalGroup = "infisical-managed-group-elasticache";
const ensureInfisicalGroupExists = async (clusterName: string) => { const ensureInfisicalGroupExists = async (clusterName: string) => {
@@ -19,6 +19,7 @@ import {
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { z } from "zod"; import { z } from "zod";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
@@ -49,6 +50,8 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
if (providerInputs.method === AwsIamAuthType.AssumeRole) { if (providerInputs.method === AwsIamAuthType.AssumeRole) {
const stsClient = new STSClient({ const stsClient = new STSClient({
region: providerInputs.region, region: providerInputs.region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: credentials:
appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID && appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY appCfg.DYNAMIC_SECRET_AWS_ACCESS_KEY_ID && appCfg.DYNAMIC_SECRET_AWS_SECRET_ACCESS_KEY
? { ? {
@@ -72,6 +75,8 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
} }
const client = new IAMClient({ const client = new IAMClient({
region: providerInputs.region, region: providerInputs.region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: { credentials: {
accessKeyId: assumeRes.Credentials?.AccessKeyId, accessKeyId: assumeRes.Credentials?.AccessKeyId,
secretAccessKey: assumeRes.Credentials?.SecretAccessKey, secretAccessKey: assumeRes.Credentials?.SecretAccessKey,
@@ -83,6 +88,8 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
const client = new IAMClient({ const client = new IAMClient({
region: providerInputs.region, region: providerInputs.region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: { credentials: {
accessKeyId: providerInputs.accessKey, accessKeyId: providerInputs.accessKey,
secretAccessKey: providerInputs.secretAccessKey secretAccessKey: providerInputs.secretAccessKey
@@ -1,6 +1,7 @@
import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, KMSClient } from "@aws-sdk/client-kms"; import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, KMSClient } from "@aws-sdk/client-kms";
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model"; import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model";
@@ -9,7 +10,9 @@ const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) { if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) {
const awsCredential = providerInputs.credential.data; const awsCredential = providerInputs.credential.data;
const stsClient = new STSClient({ const stsClient = new STSClient({
region: providerInputs.awsRegion region: providerInputs.awsRegion,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher
}); });
const command = new AssumeRoleCommand({ const command = new AssumeRoleCommand({
RoleArn: awsCredential.assumeRoleArn, RoleArn: awsCredential.assumeRoleArn,
@@ -23,6 +26,8 @@ const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
const kmsClient = new KMSClient({ const kmsClient = new KMSClient({
region: providerInputs.awsRegion, region: providerInputs.awsRegion,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: { credentials: {
accessKeyId: response.Credentials.AccessKeyId, accessKeyId: response.Credentials.AccessKeyId,
secretAccessKey: response.Credentials.SecretAccessKey, secretAccessKey: response.Credentials.SecretAccessKey,
@@ -35,6 +40,8 @@ const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
const awsCredential = providerInputs.credential.data; const awsCredential = providerInputs.credential.data;
const kmsClient = new KMSClient({ const kmsClient = new KMSClient({
region: providerInputs.awsRegion, region: providerInputs.awsRegion,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: { credentials: {
accessKeyId: awsCredential.accessKey, accessKeyId: awsCredential.accessKey,
secretAccessKey: awsCredential.secretKey secretAccessKey: awsCredential.secretKey
@@ -18,7 +18,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
environmentsUsed: 0, environmentsUsed: 0,
identityLimit: null, identityLimit: null,
identitiesUsed: 0, identitiesUsed: 0,
dynamicSecret: false, dynamicSecret: true,
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
ipAllowlisting: false, ipAllowlisting: false,
@@ -6,6 +6,7 @@ import {
} from "@aws-sdk/client-iam"; } from "@aws-sdk/client-iam";
import { SecretType } from "@app/db/schemas"; import { SecretType } from "@app/db/schemas";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates"; import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
@@ -226,6 +227,8 @@ export const secretRotationQueueFactory = ({
if (provider.template.type === TProviderFunctionTypes.AWS) { if (provider.template.type === TProviderFunctionTypes.AWS) {
if (provider.template.client === TAwsProviderSystems.IAM) { if (provider.template.client === TAwsProviderSystems.IAM) {
const client = new IAMClient({ const client = new IAMClient({
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
region: newCredential.inputs.manager_user_aws_region as string, region: newCredential.inputs.manager_user_aws_region as string,
credentials: { credentials: {
accessKeyId: newCredential.inputs.manager_user_access_key as string, accessKeyId: newCredential.inputs.manager_user_access_key as string,
+57
View File
@@ -0,0 +1,57 @@
/* eslint-disable no-underscore-dangle */
import type { SourceData } from "@smithy/types";
import { Hash, Hmac } from "crypto";
import { crypto } from "@app/lib/crypto";
export class CustomAWSHasher {
public algorithmIdentifier: string = "sha256";
public secret: SourceData | undefined;
public hash: Hash | Hmac | undefined;
private _hash: Hash | Hmac | undefined;
constructor(secret?: SourceData) {
this.secret = secret;
this.reset();
}
reset() {
if (this.secret) {
// Convert any secret type to Buffer
let secretBuffer = this.secret as Buffer;
if (this.secret instanceof ArrayBuffer) {
secretBuffer = Buffer.from(this.secret);
} else if (ArrayBuffer.isView && ArrayBuffer.isView(this.secret)) {
secretBuffer = Buffer.from(this.secret.buffer, this.secret.byteOffset, this.secret.byteLength);
}
this._hash = crypto.rawCrypto.createHmac(this.algorithmIdentifier, secretBuffer);
} else {
this._hash = crypto.rawCrypto.createHash(this.algorithmIdentifier);
}
return this;
}
update(data: SourceData) {
// Handle all possible data types
let buffer: Buffer = data as Buffer;
if (typeof data === "string") {
buffer = Buffer.from(data, "utf8");
} else if (data instanceof ArrayBuffer) {
buffer = Buffer.from(data);
} else if (ArrayBuffer.isView && ArrayBuffer.isView(data)) {
buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
}
this._hash?.update(buffer);
return this;
}
digest(): Promise<Uint8Array> {
const result = new Uint8Array(this._hash?.digest() || []);
this.reset();
return Promise.resolve(result);
}
}
+4 -4
View File
@@ -193,13 +193,12 @@ const encryptAsymmetricFipsValidated = (data: string, publicKey: string, private
format: "der" format: "der"
}); });
// Generate shared secret using X25519 // Generate shared secret using x25519 curve
const sharedSecret = crypto.diffieHellman({ const sharedSecret = crypto.diffieHellman({
privateKey: privKeyObj, privateKey: privKeyObj,
publicKey: pubKeyObj publicKey: pubKeyObj
}); });
// Generate 24-byte nonce (same as NaCl)
const nonce = crypto.randomBytes(24); const nonce = crypto.randomBytes(24);
// Derive 32-byte key from shared secret // Derive 32-byte key from shared secret
@@ -209,7 +208,7 @@ const encryptAsymmetricFipsValidated = (data: string, publicKey: string, private
const iv = nonce.subarray(0, 12); const iv = nonce.subarray(0, 12);
// Encrypt with AES-256-GCM // Encrypt with AES-256-GCM
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv); const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
const ciphertext = cipher.update(data, "utf8"); const ciphertext = cipher.update(data, "utf8");
cipher.final(); cipher.final();
@@ -665,7 +664,8 @@ const cryptographyFactory = () => {
}, },
constants: crypto.constants, constants: crypto.constants,
X509Certificate: crypto.X509Certificate, X509Certificate: crypto.X509Certificate,
KeyObject: crypto.KeyObject KeyObject: crypto.KeyObject,
Hash: crypto.Hash
} }
}; };
}; };
@@ -2,6 +2,7 @@ import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import AWS from "aws-sdk"; import AWS from "aws-sdk";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError, InternalServerError } from "@app/lib/errors";
@@ -35,6 +36,8 @@ export const getAwsConnectionConfig = async (appConnection: TAwsConnectionConfig
case AwsConnectionMethod.AssumeRole: { case AwsConnectionMethod.AssumeRole: {
const client = new STSClient({ const client = new STSClient({
region, region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: credentials:
appCfg.INF_APP_CONNECTION_AWS_ACCESS_KEY_ID && appCfg.INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY appCfg.INF_APP_CONNECTION_AWS_ACCESS_KEY_ID && appCfg.INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY
? { ? {
@@ -3,6 +3,7 @@ import * as x509 from "@peculiar/x509";
import acme from "acme-client"; import acme from "acme-client";
import { TableName } from "@app/db/schemas"; import { TableName } from "@app/db/schemas";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
@@ -102,6 +103,8 @@ export const route53InsertTxtRecord = async (
) => { ) => {
const config = await getAwsConnectionConfig(connection, AWSRegion.US_WEST_1); // REGION is irrelevant because Route53 is global const config = await getAwsConnectionConfig(connection, AWSRegion.US_WEST_1); // REGION is irrelevant because Route53 is global
const route53Client = new Route53Client({ const route53Client = new Route53Client({
sha256: CustomAWSHasher,
useFipsEndpoint: crypto.isFipsModeEnabled(),
credentials: config.credentials!, credentials: config.credentials!,
region: config.region region: config.region
}); });
@@ -30,6 +30,7 @@ import RE2 from "re2";
import { z } from "zod"; import { z } from "zod";
import { SecretType, TIntegrationAuths, TIntegrations } from "@app/db/schemas"; import { SecretType, TIntegrationAuths, TIntegrations } from "@app/db/schemas";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request"; import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
@@ -796,6 +797,8 @@ const syncSecretsAWSParameterStore = async ({
if (awsAssumeRoleArn) { if (awsAssumeRoleArn) {
const client = new STSClient({ const client = new STSClient({
region: integration.region as string, region: integration.region as string,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: credentials:
appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION
? { ? {
@@ -24,6 +24,8 @@ import {
Tag Tag
} from "aws-sdk/clients/secretsmanager"; } from "aws-sdk/clients/secretsmanager";
import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { crypto } from "@app/lib/crypto";
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns"; import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
import { AwsSecretsManagerSyncMappingBehavior } from "@app/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-enums"; import { AwsSecretsManagerSyncMappingBehavior } from "@app/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-enums";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
@@ -46,6 +48,8 @@ const getSecretsManagerClient = async (secretSync: TAwsSecretsManagerSyncWithCre
const secretsManagerClient = new SecretsManagerClient({ const secretsManagerClient = new SecretsManagerClient({
region: config.region, region: config.region,
useFipsEndpoint: crypto.isFipsModeEnabled(),
sha256: CustomAWSHasher,
credentials: config.credentials! credentials: config.credentials!
}); });
@@ -1,12 +1,14 @@
import { useCallback } from "react"; import { useCallback } from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Select, SelectItem } from "@app/components/v2"; import { Badge, Button, FormControl, Select, SelectItem, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context"; import { useServerConfig, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { import {
useGetServerRootKmsEncryptionDetails, useGetServerRootKmsEncryptionDetails,
@@ -29,6 +31,7 @@ export const EncryptionPageForm = () => {
const { data: rootKmsDetails } = useGetServerRootKmsEncryptionDetails(); const { data: rootKmsDetails } = useGetServerRootKmsEncryptionDetails();
const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy(); const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy();
const { config } = useServerConfig();
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const); const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
@@ -117,14 +120,27 @@ export const EncryptionPageForm = () => {
)} )}
</div> </div>
<Button <div className="flex w-full items-center justify-between">
className="mt-2" <Button
type="submit" className="mt-2"
isLoading={isSubmitting} type="submit"
isDisabled={isSubmitting || !isDirty} isLoading={isSubmitting}
> isDisabled={isSubmitting || !isDirty}
Save >
</Button> Save
</Button>
{config.fipsEnabled && (
<Tooltip content="FIPS mode of operation is enabled for your instance. All cryptographic operations within the FIPS boundaries are validated to be FIPS compliant.">
<div>
<Badge className="flex items-center gap-2" variant="primary">
FIPS Mode: Enabled
<FontAwesomeIcon icon={faInfoCircle} />
</Badge>
</div>
</Tooltip>
)}
</div>
</form> </form>
<UpgradePlanModal <UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen} isOpen={popUp.upgradePlan.isOpen}