From 877e8dd5ee893069f20d76b7dc81eb3e7f55e59b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 4 Mar 2026 11:29:20 -0300 Subject: [PATCH 01/66] feat: conditional fips mode --- ee/apps/ddp-streamer/Dockerfile | 12 +++++++++++- ee/apps/ddp-streamer/src/fips.ts | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 ee/apps/ddp-streamer/src/fips.ts diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index a1d430af04c90..3edfec0f6b0d7 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -90,7 +90,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -116,3 +116,13 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] + +FROM rocketchatfips140/dhi-node:22-alpine3.23 AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts new file mode 100644 index 0000000000000..db58817c919c3 --- /dev/null +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -0,0 +1,7 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); From 4bc2d5a7fc90d361b7b0743235ba23c8e4343965 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 09:10:21 -0300 Subject: [PATCH 02/66] ci: add fips lane --- .github/workflows/ci-test-e2e.yml | 27 ++++++++-- .github/workflows/ci.yml | 84 ++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index c8421db957b60..0ab7edc4f9afb 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -49,6 +49,10 @@ on: required: true CR_PAT: required: true + TEMP_DOCKERHUB_FIPS_USER: + required: false + TEMP_DOCKERHUB_FIPS_PASS: + required: false QASE_API_TOKEN: required: false REPORTER_ROCKETCHAT_URL: @@ -114,6 +118,13 @@ jobs: username: ${{ secrets.CR_USER }} password: ${{ secrets.CR_PAT }} + - name: Login to DockerHub for FIPS base images + if: inputs.release == 'fips' + uses: docker/login-action@v3 + with: + username: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + password: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup NodeJS @@ -187,20 +198,26 @@ jobs: DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d rocketchat --wait - name: Start containers for EE - if: inputs.release == 'ee' + if: inputs.release == 'ee' || inputs.release == 'fips' env: ENTERPRISE_LICENSE: ${{ inputs.enterprise-license }} TRANSPORTER: ${{ inputs.transporter }} COMPOSE_PROFILES: ${{ inputs.type == 'api' && 'api' || '' }} TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} + BUILD_TARGET: ${{ inputs.release == 'fips' && 'release-fips' || '' }} run: | + # Build only ddp-streamer locally when running FIPS to ensure the FIPS stage is used. + if [[ '${{ inputs.release }}' == 'fips' ]]; then + docker compose -f docker-compose-ci.yml build ddp-streamer-service + fi + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d --wait - uses: ./.github/actions/setup-playwright if: inputs.type == 'ui' - name: Wait services to start up - if: inputs.release == 'ee' + if: inputs.release == 'ee' || inputs.release == 'fips' run: | docker ps @@ -218,7 +235,7 @@ jobs: working-directory: ./apps/meteor env: WEBHOOK_TEST_URL: 'http://httpbin' - IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }} + IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace @@ -234,7 +251,7 @@ jobs: working-directory: ./apps/meteor env: WEBHOOK_TEST_URL: 'http://httpbin' - IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }} + IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace @@ -249,7 +266,7 @@ jobs: if: inputs.type == 'ui' env: E2E_COVERAGE: ${{ inputs.coverage == matrix.mongodb-version && 'true' || '' }} - IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }} + IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 596ebed8c9002..1755d14490776 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -679,6 +679,76 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }} + test-api-fips: + name: 🔨 Test API (FIPS) + needs: [checks, build-gh-docker-publish, release-versions] + + uses: ./.github/workflows/ci-test-e2e.yml + with: + type: api + release: fips + transporter: 'nats://nats:4222' + enterprise-license: ${{ needs.release-versions.outputs.enterprise-license }} + mongodb-version: "['8.0']" + node-version: ${{ needs.release-versions.outputs.node-version }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} + gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + secrets: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + + test-api-livechat-fips: + name: 🔨 Test API Livechat (FIPS) + needs: [checks, build-gh-docker-publish, release-versions] + + uses: ./.github/workflows/ci-test-e2e.yml + with: + type: api-livechat + release: fips + transporter: 'nats://nats:4222' + enterprise-license: ${{ needs.release-versions.outputs.enterprise-license }} + mongodb-version: "['8.0']" + node-version: ${{ needs.release-versions.outputs.node-version }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} + gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + secrets: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + + test-ui-fips: + name: 🔨 Test UI (FIPS) + needs: [checks, build-gh-docker-publish, release-versions] + + uses: ./.github/workflows/ci-test-e2e.yml + with: + type: ui + release: fips + transporter: 'nats://nats:4222' + enterprise-license: ${{ needs.release-versions.outputs.enterprise-license }} + shard: '[1, 2, 3, 4, 5]' + total-shard: 5 + mongodb-version: "['8.0']" + node-version: ${{ needs.release-versions.outputs.node-version }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + lowercase-repo: ${{ needs.release-versions.outputs.lowercase-repo }} + gh-docker-tag: ${{ needs.release-versions.outputs.gh-docker-tag }} + retries: ${{ (github.event_name == 'release' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && 2 || 0 }} + secrets: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} + REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} + REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} + REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + test-federation-matrix: name: 🔨 Test Federation Matrix needs: [checks, build-gh-docker-publish, packages-build, release-versions] @@ -829,7 +899,7 @@ jobs: tests-done: name: ✅ Tests Done runs-on: ubuntu-24.04-arm - needs: [checks, test-unit, test-api, test-ui, test-api-ee, test-ui-ee, test-api-livechat, test-api-livechat-ee, test-federation-matrix] + needs: [checks, test-unit, test-api, test-ui, test-api-ee, test-ui-ee, test-api-livechat, test-api-livechat-ee, test-api-fips, test-api-livechat-fips, test-ui-fips, test-federation-matrix] if: always() steps: - name: Test finish aggregation @@ -866,6 +936,18 @@ jobs: exit 1 fi + if [[ '${{ needs.test-api-fips.result }}' != 'success' ]]; then + exit 1 + fi + + if [[ '${{ needs.test-api-livechat-fips.result }}' != 'success' ]]; then + exit 1 + fi + + if [[ '${{ needs.test-ui-fips.result }}' != 'success' ]]; then + exit 1 + fi + if [[ '${{ needs.test-federation-matrix.result }}' != 'success' ]]; then exit 1 fi From c53c5d4b58737a294cfea466d0d04f61e2c9ace5 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 09:26:05 -0300 Subject: [PATCH 03/66] ci: build fips image in advance --- .github/actions/build-docker/action.yml | 17 ++++++++++++++- .github/workflows/ci-test-e2e.yml | 18 +--------------- .github/workflows/ci.yml | 28 +++++++++++++++++++------ docker-compose-ci.yml | 2 +- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 2d7b1fa736a18..228f89dc45a40 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -8,6 +8,12 @@ inputs: CR_PAT: required: true description: 'GitHub Container Registry Personal Access Token' + TEMP_DOCKERHUB_FIPS_USER: + required: false + description: 'Temporary DockerHub user for FIPS base image pulls' + TEMP_DOCKERHUB_FIPS_PASS: + required: false + description: 'Temporary DockerHub password/token for FIPS base image pulls' deno-version: required: true description: 'Deno version' @@ -28,7 +34,7 @@ inputs: default: 'true' type: required: false - description: 'production or coverage' + description: 'production, coverage, or fips' default: 'coverage' runs: @@ -43,6 +49,13 @@ runs: username: ${{ inputs.CR_USER }} password: ${{ inputs.CR_PAT }} + - name: Login to DockerHub for FIPS base images + if: inputs.type == 'fips' + uses: docker/login-action@v3 + with: + username: ${{ inputs.TEMP_DOCKERHUB_FIPS_USER }} + password: ${{ inputs.TEMP_DOCKERHUB_FIPS_PASS }} + - name: Restore meteor build if: inputs.service == 'rocketchat' uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 @@ -133,6 +146,8 @@ runs: SERVICE_SUFFIX='' if [[ "$INPUT_SERVICE" == 'rocketchat' && "$INPUT_TYPE" == 'coverage' ]] && [[ "$GITHUB_EVENT_NAME" == 'release' || "$GITHUB_REF" == 'refs/heads/develop' ]]; then SERVICE_SUFFIX='-cov' + elif [[ "$INPUT_SERVICE" == 'ddp-streamer-service' && "$INPUT_TYPE" == 'fips' ]]; then + SERVICE_SUFFIX='-fips' fi mkdir -p "/tmp/manifests/${INPUT_SERVICE}${SERVICE_SUFFIX}/${INPUT_ARCH}" diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 0ab7edc4f9afb..e3e9f8100972c 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -49,10 +49,6 @@ on: required: true CR_PAT: required: true - TEMP_DOCKERHUB_FIPS_USER: - required: false - TEMP_DOCKERHUB_FIPS_PASS: - required: false QASE_API_TOKEN: required: false REPORTER_ROCKETCHAT_URL: @@ -79,6 +75,7 @@ jobs: env: # if building for production on develop branch or release, add suffix for coverage images DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ inputs.coverage == matrix.mongodb-version && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} + DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ inputs.release == 'fips' && '-fips' || '' }} MONGODB_VERSION: ${{ matrix.mongodb-version }} COVERAGE_DIR: '/tmp/coverage/${{ startsWith(inputs.type, ''api'') && ''api'' || inputs.type }}' COVERAGE_FILE_NAME: '${{ inputs.type }}-${{ matrix.shard }}.json' @@ -118,13 +115,6 @@ jobs: username: ${{ secrets.CR_USER }} password: ${{ secrets.CR_PAT }} - - name: Login to DockerHub for FIPS base images - if: inputs.release == 'fips' - uses: docker/login-action@v3 - with: - username: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - password: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup NodeJS @@ -204,13 +194,7 @@ jobs: TRANSPORTER: ${{ inputs.transporter }} COMPOSE_PROFILES: ${{ inputs.type == 'api' && 'api' || '' }} TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} - BUILD_TARGET: ${{ inputs.release == 'fips' && 'release-fips' || '' }} run: | - # Build only ddp-streamer locally when running FIPS to ensure the FIPS stage is used. - if [[ '${{ inputs.release }}' == 'fips' ]]; then - docker compose -f docker-compose-ci.yml build ddp-streamer-service - fi - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d --wait - uses: ./.github/actions/setup-playwright diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1755d14490776..87b7ffdb0ed11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -328,6 +328,10 @@ jobs: - arch: arm64 service: [rocketchat] type: coverage + # build a dedicated FIPS ddp-streamer image for FIPS test lanes + - arch: amd64 + service: [ddp-streamer-service] + type: fips steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -341,9 +345,13 @@ jobs: env: # add suffix for the extra images with coverage if building for production DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} + DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} service: ${{ matrix.service[0] }} @@ -355,9 +363,13 @@ jobs: if: matrix.service[1] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} service: ${{ matrix.service[1] }} @@ -370,9 +382,13 @@ jobs: if: matrix.service[2] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} service: ${{ matrix.service[2] }} @@ -385,9 +401,13 @@ jobs: if: matrix.service[3] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} service: ${{ matrix.service[3] }} @@ -452,6 +472,8 @@ jobs: # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) if [ "$service" == "rocketchat-cov" ]; then IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "rocketchat" '.services[$s].image')-cov + elif [ "$service" == "ddp-streamer-service-fips" ]; then + IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "ddp-streamer-service" '.services[$s].image')-fips else IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "$service" '.services[$s].image') fi @@ -697,8 +719,6 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-api-livechat-fips: name: 🔨 Test API Livechat (FIPS) @@ -718,8 +738,6 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-ui-fips: name: 🔨 Test UI (FIPS) @@ -746,8 +764,6 @@ jobs: REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-federation-matrix: name: 🔨 Test Federation Matrix diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 305f0c81e2cc8..1f5a49417b643 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -114,7 +114,7 @@ services: - linux/arm64 args: SERVICE: ddp-streamer - image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG} + image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_DDP_STREAMER:-} environment: - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0 - 'TRANSPORTER=${TRANSPORTER:-}' From 3c41c2a316752cd3b201bad9a592d7824f4c825a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 10:01:24 -0300 Subject: [PATCH 04/66] ci: allow selecting test lanes to run --- .github/workflows/ci.yml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87b7ffdb0ed11..deaadc12be8c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,16 @@ name: CI on: + workflow_dispatch: + inputs: + test-scope: + description: Select test lanes to run + required: false + default: all + type: choice + options: + - all + - fips release: types: [published] pull_request: @@ -492,7 +502,7 @@ jobs: name: 📦 Track Image Sizes needs: [build-gh-docker-publish, release-versions] runs-on: ubuntu-24.04-arm - if: github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' + if: github.event_name != 'workflow_dispatch' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop') permissions: pull-requests: write contents: write @@ -550,6 +560,7 @@ jobs: test-storybook: name: 🔨 Test Storybook needs: [packages-build, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-storybook.yml with: @@ -561,6 +572,7 @@ jobs: test-unit: name: 🔨 Test Unit needs: [packages-build, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-unit.yml with: @@ -573,6 +585,7 @@ jobs: test-api: name: 🔨 Test API (CE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -590,6 +603,7 @@ jobs: test-api-livechat: name: 🔨 Test API Livechat (CE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -607,6 +621,7 @@ jobs: test-ui: name: 🔨 Test UI (CE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -633,6 +648,7 @@ jobs: test-api-ee: name: 🔨 Test API (EE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -654,6 +670,7 @@ jobs: test-api-livechat-ee: name: 🔨 Test API Livechat (EE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -675,6 +692,7 @@ jobs: test-ui-ee: name: 🔨 Test UI (EE) needs: [checks, build-gh-docker-publish, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -768,6 +786,7 @@ jobs: test-federation-matrix: name: 🔨 Test Federation Matrix needs: [checks, build-gh-docker-publish, packages-build, release-versions] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} runs-on: ubuntu-24.04 steps: @@ -864,6 +883,7 @@ jobs: name: 📊 Report Coverage runs-on: ubuntu-24.04 needs: [release-versions, test-api-ee, test-api-livechat-ee, test-ui-ee] + if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -916,7 +936,7 @@ jobs: name: ✅ Tests Done runs-on: ubuntu-24.04-arm needs: [checks, test-unit, test-api, test-ui, test-api-ee, test-ui-ee, test-api-livechat, test-api-livechat-ee, test-api-fips, test-api-livechat-fips, test-ui-fips, test-federation-matrix] - if: always() + if: ${{ always() && (github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips') }} steps: - name: Test finish aggregation run: | @@ -973,7 +993,7 @@ jobs: deploy: name: 🚀 Publish build assets runs-on: ubuntu-24.04-arm - if: github.event_name == 'release' || github.ref == 'refs/heads/develop' + if: github.event_name != 'workflow_dispatch' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') needs: [build-gh-docker-publish, release-versions] steps: From f505b624c7c141c9de1e8d1081073c38ccc25aab Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 11:18:35 -0300 Subject: [PATCH 05/66] fix: allow sha-1 for ws handshake --- ee/apps/ddp-streamer/Dockerfile | 7 ++++++- ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf | 14 ++++++++++++++ ee/apps/ddp-streamer/src/fips.ts | 10 ++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 3edfec0f6b0d7..cd3142ed296c3 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -121,8 +121,13 @@ FROM rocketchatfips140/dhi-node:22-alpine3.23 AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 + +# Keep provider behavior explicit for auditing: enable FIPS provider while allowing +# fallback to default provider for legacy algorithms required by dependencies. +COPY ./ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf /etc/ssl/openssl-ddp-streamer-fips.cnf + COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--openssl-config=/etc/ssl/openssl-ddp-streamer-fips.cnf", "--openssl-shared-config", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf b/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf new file mode 100644 index 0000000000000..f0d6c4f706647 --- /dev/null +++ b/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf @@ -0,0 +1,14 @@ +openssl_conf = openssl_init + +[openssl_init] +providers = provider_sect + +[provider_sect] +fips = fips_sect +default = default_sect + +[fips_sect] +activate = 1 + +[default_sect] +activate = 1 diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index db58817c919c3..8a31de51660f6 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -1,7 +1,13 @@ import crypto from 'crypto'; -crypto.setFips(true); +const OPENSSL_CONFIG_PATH = '/etc/ssl/openssl-ddp-streamer-fips.cnf'; +const hasOpenSSLConfigFlag = process.execArgv.some((arg) => arg.startsWith('--openssl-config=')); +const hasOpenSSLSharedConfigFlag = process.execArgv.includes('--openssl-shared-config'); console.log('========================================='); -console.log('FIPS COMPLIANCE CHECK: YES'); +console.log(`Node FIPS Mode Flag: ${crypto.getFips() === 1 ? 'ENABLED' : 'DISABLED'}`); +console.log(`OpenSSL Config Path: ${OPENSSL_CONFIG_PATH}`); +console.log(`OpenSSL Config Flag Present: ${hasOpenSSLConfigFlag ? 'YES' : 'NO'}`); +console.log(`OpenSSL Shared Config Flag Present: ${hasOpenSSLSharedConfigFlag ? 'YES' : 'NO'}`); +console.log('OpenSSL provider policy expected: fips + default fallback.'); console.log('========================================='); From 62b2cfa90b1f68022b6b5288bcf9a1f5d11fb68b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 11:57:43 -0300 Subject: [PATCH 06/66] Revert "ci: allow selecting test lanes to run" This reverts commit f2f6b9d75eb59ab677eb8c786e4b8e03bdcc19fa. --- .github/workflows/ci.yml | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index deaadc12be8c0..87b7ffdb0ed11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,6 @@ name: CI on: - workflow_dispatch: - inputs: - test-scope: - description: Select test lanes to run - required: false - default: all - type: choice - options: - - all - - fips release: types: [published] pull_request: @@ -502,7 +492,7 @@ jobs: name: 📦 Track Image Sizes needs: [build-gh-docker-publish, release-versions] runs-on: ubuntu-24.04-arm - if: github.event_name != 'workflow_dispatch' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop') + if: github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' permissions: pull-requests: write contents: write @@ -560,7 +550,6 @@ jobs: test-storybook: name: 🔨 Test Storybook needs: [packages-build, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-storybook.yml with: @@ -572,7 +561,6 @@ jobs: test-unit: name: 🔨 Test Unit needs: [packages-build, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-unit.yml with: @@ -585,7 +573,6 @@ jobs: test-api: name: 🔨 Test API (CE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -603,7 +590,6 @@ jobs: test-api-livechat: name: 🔨 Test API Livechat (CE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -621,7 +607,6 @@ jobs: test-ui: name: 🔨 Test UI (CE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -648,7 +633,6 @@ jobs: test-api-ee: name: 🔨 Test API (EE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -670,7 +654,6 @@ jobs: test-api-livechat-ee: name: 🔨 Test API Livechat (EE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -692,7 +675,6 @@ jobs: test-ui-ee: name: 🔨 Test UI (EE) needs: [checks, build-gh-docker-publish, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} uses: ./.github/workflows/ci-test-e2e.yml with: @@ -786,7 +768,6 @@ jobs: test-federation-matrix: name: 🔨 Test Federation Matrix needs: [checks, build-gh-docker-publish, packages-build, release-versions] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} runs-on: ubuntu-24.04 steps: @@ -883,7 +864,6 @@ jobs: name: 📊 Report Coverage runs-on: ubuntu-24.04 needs: [release-versions, test-api-ee, test-api-livechat-ee, test-ui-ee] - if: ${{ github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -936,7 +916,7 @@ jobs: name: ✅ Tests Done runs-on: ubuntu-24.04-arm needs: [checks, test-unit, test-api, test-ui, test-api-ee, test-ui-ee, test-api-livechat, test-api-livechat-ee, test-api-fips, test-api-livechat-fips, test-ui-fips, test-federation-matrix] - if: ${{ always() && (github.event_name != 'workflow_dispatch' || inputs.test-scope != 'fips') }} + if: always() steps: - name: Test finish aggregation run: | @@ -993,7 +973,7 @@ jobs: deploy: name: 🚀 Publish build assets runs-on: ubuntu-24.04-arm - if: github.event_name != 'workflow_dispatch' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') + if: github.event_name == 'release' || github.ref == 'refs/heads/develop' needs: [build-gh-docker-publish, release-versions] steps: From 406705f9b52b051faa9f3d0ff26c3ba2f46e17c5 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 5 Mar 2026 12:55:49 -0300 Subject: [PATCH 07/66] chore: streamline fips env vars --- .github/workflows/ci-test-e2e.yml | 2 +- .github/workflows/ci.yml | 8 ++++---- docker-compose-ci.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index e3e9f8100972c..631d1cbb20be5 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -75,7 +75,7 @@ jobs: env: # if building for production on develop branch or release, add suffix for coverage images DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ inputs.coverage == matrix.mongodb-version && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ inputs.release == 'fips' && '-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ inputs.release == 'fips' && '-fips' || '' }} MONGODB_VERSION: ${{ matrix.mongodb-version }} COVERAGE_DIR: '/tmp/coverage/${{ startsWith(inputs.type, ''api'') && ''api'' || inputs.type }}' COVERAGE_FILE_NAME: '${{ inputs.type }}-${{ matrix.shard }}.json' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87b7ffdb0ed11..6a2894e4258a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -345,7 +345,7 @@ jobs: env: # add suffix for the extra images with coverage if building for production DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} BUILD_TARGET: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} @@ -363,7 +363,7 @@ jobs: if: matrix.service[1] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} BUILD_TARGET: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} @@ -382,7 +382,7 @@ jobs: if: matrix.service[2] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} BUILD_TARGET: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} @@ -401,7 +401,7 @@ jobs: if: matrix.service[3] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_DDP_STREAMER: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} BUILD_TARGET: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 1f5a49417b643..4e65d50b016fc 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -114,7 +114,7 @@ services: - linux/arm64 args: SERVICE: ddp-streamer - image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_DDP_STREAMER:-} + image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_FIPS:-} environment: - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0 - 'TRANSPORTER=${TRANSPORTER:-}' From cb1aef2a3e5d926fc43ff4eeb2be1d3c2b2e20b4 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 6 Mar 2026 15:51:47 -0300 Subject: [PATCH 08/66] Revert "fix: allow sha-1 for ws handshake" This reverts commit 77fe51c04e63f4bb370c6e6e5859e7c035c7fb7b. --- ee/apps/ddp-streamer/Dockerfile | 7 +------ ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf | 14 -------------- ee/apps/ddp-streamer/src/fips.ts | 10 ++-------- 3 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index cd3142ed296c3..3edfec0f6b0d7 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -121,13 +121,8 @@ FROM rocketchatfips140/dhi-node:22-alpine3.23 AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 - -# Keep provider behavior explicit for auditing: enable FIPS provider while allowing -# fallback to default provider for legacy algorithms required by dependencies. -COPY ./ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf /etc/ssl/openssl-ddp-streamer-fips.cnf - COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--openssl-config=/etc/ssl/openssl-ddp-streamer-fips.cnf", "--openssl-shared-config", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf b/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf deleted file mode 100644 index f0d6c4f706647..0000000000000 --- a/ee/apps/ddp-streamer/openssl-ddp-streamer-fips.cnf +++ /dev/null @@ -1,14 +0,0 @@ -openssl_conf = openssl_init - -[openssl_init] -providers = provider_sect - -[provider_sect] -fips = fips_sect -default = default_sect - -[fips_sect] -activate = 1 - -[default_sect] -activate = 1 diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 8a31de51660f6..db58817c919c3 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -1,13 +1,7 @@ import crypto from 'crypto'; -const OPENSSL_CONFIG_PATH = '/etc/ssl/openssl-ddp-streamer-fips.cnf'; -const hasOpenSSLConfigFlag = process.execArgv.some((arg) => arg.startsWith('--openssl-config=')); -const hasOpenSSLSharedConfigFlag = process.execArgv.includes('--openssl-shared-config'); +crypto.setFips(true); console.log('========================================='); -console.log(`Node FIPS Mode Flag: ${crypto.getFips() === 1 ? 'ENABLED' : 'DISABLED'}`); -console.log(`OpenSSL Config Path: ${OPENSSL_CONFIG_PATH}`); -console.log(`OpenSSL Config Flag Present: ${hasOpenSSLConfigFlag ? 'YES' : 'NO'}`); -console.log(`OpenSSL Shared Config Flag Present: ${hasOpenSSLSharedConfigFlag ? 'YES' : 'NO'}`); -console.log('OpenSSL provider policy expected: fips + default fallback.'); +console.log('FIPS COMPLIANCE CHECK: YES'); console.log('========================================='); From b7baa5293c4d2fd623c5070dfd56f4a5fb3cf603 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 6 Mar 2026 15:56:11 -0300 Subject: [PATCH 09/66] fix(fips): use js implementation for ws handshake --- ee/apps/ddp-streamer/src/fips.ts | 98 ++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index db58817c919c3..b0e2a565607d0 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -5,3 +5,101 @@ crypto.setFips(true); console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); console.log('========================================='); + +// ========================================================================= +// FIPS 140-3 SHA-1 WORKAROUND +// Bypasses OpenSSL FIPS restrictions for WebSocket handshakes. +// ========================================================================= + +const generateWebSocketAccept = (message: string): string => { + let h0 = 0x67452301; + let h1 = 0xefcdab89; + let h2 = 0x98badcfe; + let h3 = 0x10325476; + let h4 = 0xc3d2e1f0; + const blocks = new Uint32Array(32); + for (let i = 0; i < 60; i++) blocks[i >> 2] |= message.charCodeAt(i) << (24 - (i % 4) * 8); + blocks[15] = 0x80000000; + blocks[31] = 480; + + const rotl = (n: number, b: number) => (n << b) | (n >>> (32 - b)); + + for (let chunk = 0; chunk < 2; chunk++) { + const w = new Uint32Array(80); + const offset = chunk * 16; + for (let i = 0; i < 16; i++) w[i] = blocks[offset + i]; + for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + let a = h0; + let b = h1; + let c = h2; + let d = h3; + let e = h4; + + for (let i = 0; i < 80; i++) { + let f; + let k; + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5a827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ed9eba1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8f1bbcdc; + } else { + f = b ^ c ^ d; + k = 0xca62c1d6; + } + + const temp = (rotl(a, 5) + f + e + k + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; + } + h0 = (h0 + a) >>> 0; + h1 = (h1 + b) >>> 0; + h2 = (h2 + c) >>> 0; + h3 = (h3 + d) >>> 0; + h4 = (h4 + e) >>> 0; + } + + const hashBuffer = Buffer.allocUnsafe(20); + hashBuffer.writeUInt32BE(h0, 0); + hashBuffer.writeUInt32BE(h1, 4); + hashBuffer.writeUInt32BE(h2, 8); + hashBuffer.writeUInt32BE(h3, 12); + hashBuffer.writeUInt32BE(h4, 16); + return hashBuffer.toString('base64'); +}; + +const originalCreateHash = crypto.createHash; + +crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { + if (algorithm.toLowerCase() === 'sha1') { + let payload = ''; + + return { + update(data: string | Buffer) { + payload += data.toString(); + return this; + }, + digest(encoding: crypto.BinaryToTextEncoding) { + // Route the exact 60-byte WebSocket handshake to our bypass + if (encoding === 'base64' && payload.length === 60) { + return generateWebSocketAccept(payload); + } + // Otherwise, fall back to native crypto + return originalCreateHash('sha1', options).update(payload).digest(encoding); + }, + } as crypto.Hash; + } + + return originalCreateHash(algorithm, options); +}; + +console.log('FIPS Workaround: WebSocket SHA-1 Monkey Patch applied successfully.'); +console.log('========================================='); From 5e4cf351abb8e5f10294d18cb138d27c82c4cc07 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 12:51:42 -0300 Subject: [PATCH 10/66] refactor: improve fips init script --- ee/apps/ddp-streamer/src/fips.ts | 226 ++++++++++++++++++------------- 1 file changed, 129 insertions(+), 97 deletions(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index b0e2a565607d0..dbe93b5f6d9a5 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -1,105 +1,137 @@ +/** + * ============================================================================== + * SECURITY AUDIT EXEMPTION / FIPS 140-3 WORKAROUND + * ============================================================================== + * Context: + * Node.js running in FIPS 140-3 mode strictly disables native SHA-1 execution. + * However, RFC 6455 (WebSockets) strictly requires SHA-1 to generate the + * Sec-WebSocket-Accept handshake header. + * * Justification: + * The WebSocket protocol uses SHA-1 purely for framing/handshake validation, + * NOT for cryptographic security. To allow the 'ws' library to function without + * crashing the Node process, we intercept SHA-1 calls specifically for the + * 60-byte WebSocket handshake and process them using a highly-optimized, + * zero-allocation, pure-JS implementation. + * ============================================================================== + */ import crypto from 'crypto'; -crypto.setFips(true); +try { + crypto.createHash('sha1').update('test').digest('hex'); + console.log('🔓 Native SHA-1 allowed. Skipping FIPS WebSocket patch.'); +} catch (err) { + console.log('🔒 FIPS 140-3 mode detected. Applying WebSocket Handshake Patch...'); -console.log('========================================='); -console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); + const blocks = new Uint32Array(32); + const w = new Uint32Array(80); + const hashBuffer = Buffer.alloc(20); -// ========================================================================= -// FIPS 140-3 SHA-1 WORKAROUND -// Bypasses OpenSSL FIPS restrictions for WebSocket handshakes. -// ========================================================================= + const generateWebSocketAccept = (message: string): string => { + if (message.length !== 60) { + throw new Error(`Expected 60-byte input for WS Accept, got ${message.length}`); + } -const generateWebSocketAccept = (message: string): string => { - let h0 = 0x67452301; - let h1 = 0xefcdab89; - let h2 = 0x98badcfe; - let h3 = 0x10325476; - let h4 = 0xc3d2e1f0; - const blocks = new Uint32Array(32); - for (let i = 0; i < 60; i++) blocks[i >> 2] |= message.charCodeAt(i) << (24 - (i % 4) * 8); - blocks[15] = 0x80000000; - blocks[31] = 480; - - const rotl = (n: number, b: number) => (n << b) | (n >>> (32 - b)); - - for (let chunk = 0; chunk < 2; chunk++) { - const w = new Uint32Array(80); - const offset = chunk * 16; - for (let i = 0; i < 16; i++) w[i] = blocks[offset + i]; - for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); - - let a = h0; - let b = h1; - let c = h2; - let d = h3; - let e = h4; - - for (let i = 0; i < 80; i++) { - let f; - let k; - if (i < 20) { - f = (b & c) | (~b & d); - k = 0x5a827999; - } else if (i < 40) { - f = b ^ c ^ d; - k = 0x6ed9eba1; - } else if (i < 60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8f1bbcdc; - } else { - f = b ^ c ^ d; - k = 0xca62c1d6; + blocks.fill(0); + + let h0 = 0x67452301; + let h1 = 0xefcdab89; + let h2 = 0x98badcfe; + let h3 = 0x10325476; + let h4 = 0xc3d2e1f0; + + for (let i = 0; i < 60; i++) blocks[i >> 2] |= message.charCodeAt(i) << (24 - (i % 4) * 8); + blocks[15] = 0x80000000; + blocks[31] = 480; + + const rotl = (n: number, b: number) => (n << b) | (n >>> (32 - b)); + + for (let chunk = 0; chunk < 2; chunk++) { + const offset = chunk * 16; + for (let i = 0; i < 16; i++) w[i] = blocks[offset + i]; + for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + let a = h0; + let b = h1; + let c = h2; + let d = h3; + let e = h4; + let temp; + + for (let i = 0; i < 20; i++) { + temp = (rotl(a, 5) + (d ^ (b & (c ^ d))) + e + 0x5a827999 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; } + for (let i = 20; i < 40; i++) { + temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0x6ed9eba1 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; + } + for (let i = 40; i < 60; i++) { + temp = (rotl(a, 5) + ((b & c) | (b & d) | (c & d)) + e + 0x8f1bbcdc + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; + } + for (let i = 60; i < 80; i++) { + temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0xca62c1d6 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; + } + + h0 = (h0 + a) >>> 0; + h1 = (h1 + b) >>> 0; + h2 = (h2 + c) >>> 0; + h3 = (h3 + d) >>> 0; + h4 = (h4 + e) >>> 0; + } + + hashBuffer.writeUInt32BE(h0, 0); + hashBuffer.writeUInt32BE(h1, 4); + hashBuffer.writeUInt32BE(h2, 8); + hashBuffer.writeUInt32BE(h3, 12); + hashBuffer.writeUInt32BE(h4, 16); + + return hashBuffer.toString('base64'); + }; + + const originalCreateHash = crypto.createHash; + + crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { + if (algorithm.toLowerCase() === 'sha1') { + let inputData = ''; - const temp = (rotl(a, 5) + f + e + k + w[i]) >>> 0; - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = temp; + return { + update(data) { + if (typeof data === 'string') { + inputData += data; + } else if (Buffer.isBuffer(data)) { + inputData += data.toString('utf8'); + } else { + inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8'); + } + return this; + }, + digest(encoding) { + if (encoding === 'base64' && inputData.length === 60) { + return generateWebSocketAccept(inputData); + } + // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) + return originalCreateHash(algorithm, options).update(inputData).digest(encoding); + }, + } as crypto.Hash; } - h0 = (h0 + a) >>> 0; - h1 = (h1 + b) >>> 0; - h2 = (h2 + c) >>> 0; - h3 = (h3 + d) >>> 0; - h4 = (h4 + e) >>> 0; - } - - const hashBuffer = Buffer.allocUnsafe(20); - hashBuffer.writeUInt32BE(h0, 0); - hashBuffer.writeUInt32BE(h1, 4); - hashBuffer.writeUInt32BE(h2, 8); - hashBuffer.writeUInt32BE(h3, 12); - hashBuffer.writeUInt32BE(h4, 16); - return hashBuffer.toString('base64'); -}; - -const originalCreateHash = crypto.createHash; - -crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { - if (algorithm.toLowerCase() === 'sha1') { - let payload = ''; - - return { - update(data: string | Buffer) { - payload += data.toString(); - return this; - }, - digest(encoding: crypto.BinaryToTextEncoding) { - // Route the exact 60-byte WebSocket handshake to our bypass - if (encoding === 'base64' && payload.length === 60) { - return generateWebSocketAccept(payload); - } - // Otherwise, fall back to native crypto - return originalCreateHash('sha1', options).update(payload).digest(encoding); - }, - } as crypto.Hash; - } - - return originalCreateHash(algorithm, options); -}; - -console.log('FIPS Workaround: WebSocket SHA-1 Monkey Patch applied successfully.'); -console.log('========================================='); + return originalCreateHash(algorithm, options); + }; +} From d0b0e2d1ede4b36365486016b6f847b88277049a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 13:03:22 -0300 Subject: [PATCH 11/66] ci: guard against missing creds --- .github/actions/build-docker/action.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 228f89dc45a40..34d1a9e813ee2 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -50,12 +50,11 @@ runs: password: ${{ inputs.CR_PAT }} - name: Login to DockerHub for FIPS base images - if: inputs.type == 'fips' + if: inputs.type == 'fips' && inputs.TEMP_DOCKERHUB_FIPS_USER != '' && inputs.TEMP_DOCKERHUB_FIPS_PASS != '' uses: docker/login-action@v3 with: username: ${{ inputs.TEMP_DOCKERHUB_FIPS_USER }} password: ${{ inputs.TEMP_DOCKERHUB_FIPS_PASS }} - - name: Restore meteor build if: inputs.service == 'rocketchat' uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 From 2dc8517353823e256702a04d6b58dcee7347ca5b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 13:13:36 -0300 Subject: [PATCH 12/66] feat(presence-service): fips mode --- .github/actions/build-docker/action.yml | 2 ++ .github/workflows/ci.yml | 22 ++++++++++++---------- docker-compose-ci.yml | 3 ++- ee/apps/presence-service/Dockerfile | 13 ++++++++++++- ee/apps/presence-service/src/fips.ts | 11 +++++++++++ ee/apps/presence-service/tsconfig.json | 2 +- 6 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 ee/apps/presence-service/src/fips.ts diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 34d1a9e813ee2..3270e98f2c9e0 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -147,6 +147,8 @@ runs: SERVICE_SUFFIX='-cov' elif [[ "$INPUT_SERVICE" == 'ddp-streamer-service' && "$INPUT_TYPE" == 'fips' ]]; then SERVICE_SUFFIX='-fips' + elif [[ "$INPUT_SERVICE" == 'presence-service' && "$INPUT_TYPE" == 'fips' ]]; then + SERVICE_SUFFIX='-fips' fi mkdir -p "/tmp/manifests/${INPUT_SERVICE}${SERVICE_SUFFIX}/${INPUT_ARCH}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a2894e4258a3..72a1cb6d1ec1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -328,9 +328,9 @@ jobs: - arch: arm64 service: [rocketchat] type: coverage - # build a dedicated FIPS ddp-streamer image for FIPS test lanes + # build dedicated FIPS images for FIPS test lanes - arch: amd64 - service: [ddp-streamer-service] + service: [ddp-streamer-service, presence-service] type: fips steps: @@ -345,8 +345,8 @@ jobs: env: # add suffix for the extra images with coverage if building for production DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ matrix.service[0] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[0] == 'ddp-streamer-service' || matrix.service[0] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ (matrix.service[0] == 'ddp-streamer-service' || matrix.service[0] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -363,8 +363,8 @@ jobs: if: matrix.service[1] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ matrix.service[1] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[1] == 'ddp-streamer-service' || matrix.service[1] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ (matrix.service[1] == 'ddp-streamer-service' || matrix.service[1] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -382,8 +382,8 @@ jobs: if: matrix.service[2] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ matrix.service[2] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[2] == 'ddp-streamer-service' || matrix.service[2] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ (matrix.service[2] == 'ddp-streamer-service' || matrix.service[2] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -401,8 +401,8 @@ jobs: if: matrix.service[3] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ matrix.service[3] == 'ddp-streamer-service' && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[3] == 'ddp-streamer-service' || matrix.service[3] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ (matrix.service[3] == 'ddp-streamer-service' || matrix.service[3] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -472,6 +472,8 @@ jobs: # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) if [ "$service" == "rocketchat-cov" ]; then IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "rocketchat" '.services[$s].image')-cov + elif [ "$service" == "presence-service-fips" ]; then + IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "presence-service" '.services[$s].image')-fips elif [ "$service" == "ddp-streamer-service-fips" ]; then IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "ddp-streamer-service" '.services[$s].image')-fips else diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 4e65d50b016fc..00a9199d74dd7 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -88,13 +88,14 @@ services: build: dockerfile: ee/apps/presence-service/Dockerfile context: . + target: ${BUILD_TARGET:-release-standard} x-bake: platforms: - linux/amd64 - linux/arm64 args: SERVICE: presence-service - image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG} + image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_FIPS:-} environment: - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0 - 'TRANSPORTER=${TRANSPORTER:-}' diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index e4d201aa24f64..78220ce2b7e2c 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -88,7 +88,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -114,3 +114,14 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] + +# FIPS RELEASE STAGE +FROM rocketchatfips140/dhi-node:22-fips AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/presence-service/src/fips.ts b/ee/apps/presence-service/src/fips.ts new file mode 100644 index 0000000000000..c232a6e586011 --- /dev/null +++ b/ee/apps/presence-service/src/fips.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +if (crypto.getFips() !== 1) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); diff --git a/ee/apps/presence-service/tsconfig.json b/ee/apps/presence-service/tsconfig.json index ff29a55af231c..34dfe4e779780 100644 --- a/ee/apps/presence-service/tsconfig.json +++ b/ee/apps/presence-service/tsconfig.json @@ -4,7 +4,7 @@ "strictPropertyInitialization": false, // TODO: Remove this line "outDir": "./dist/ee/apps/presence-service/src", }, - "files": ["./src/service.ts"], + "files": ["./src/service.ts", "./src/fips.ts"], "include": ["../../../apps/meteor/definition/externals/meteor"], "exclude": ["./dist"] } From 1bbd53122cb6434fe14b1f62313a217bed5e0258 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 14:05:53 -0300 Subject: [PATCH 13/66] ci: reduce duplication of fips checks --- .github/actions/build-docker/action.yml | 4 +--- .github/workflows/ci.yml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 3270e98f2c9e0..16db97a624cfb 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -145,9 +145,7 @@ runs: SERVICE_SUFFIX='' if [[ "$INPUT_SERVICE" == 'rocketchat' && "$INPUT_TYPE" == 'coverage' ]] && [[ "$GITHUB_EVENT_NAME" == 'release' || "$GITHUB_REF" == 'refs/heads/develop' ]]; then SERVICE_SUFFIX='-cov' - elif [[ "$INPUT_SERVICE" == 'ddp-streamer-service' && "$INPUT_TYPE" == 'fips' ]]; then - SERVICE_SUFFIX='-fips' - elif [[ "$INPUT_SERVICE" == 'presence-service' && "$INPUT_TYPE" == 'fips' ]]; then + elif [[ "$INPUT_TYPE" == 'fips' ]]; then SERVICE_SUFFIX='-fips' fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72a1cb6d1ec1e..f299484c0804a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,7 @@ concurrency: env: TOOL_NODE_FLAGS: ${{ vars.TOOL_NODE_FLAGS }} + FIPS_ENABLED_SERVICES: '["ddp-streamer-service","presence-service"]' jobs: release-versions: @@ -345,8 +346,8 @@ jobs: env: # add suffix for the extra images with coverage if building for production DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[0] == 'ddp-streamer-service' || matrix.service[0] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ (matrix.service[0] == 'ddp-streamer-service' || matrix.service[0] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[0]) && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[0]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -363,8 +364,8 @@ jobs: if: matrix.service[1] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[1] == 'ddp-streamer-service' || matrix.service[1] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ (matrix.service[1] == 'ddp-streamer-service' || matrix.service[1] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[1]) && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[1]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -382,8 +383,8 @@ jobs: if: matrix.service[2] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[2] == 'ddp-streamer-service' || matrix.service[2] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ (matrix.service[2] == 'ddp-streamer-service' || matrix.service[2] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[2]) && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[2]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -401,8 +402,8 @@ jobs: if: matrix.service[3] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ (matrix.service[3] == 'ddp-streamer-service' || matrix.service[3] == 'presence-service') && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ (matrix.service[3] == 'ddp-streamer-service' || matrix.service[3] == 'presence-service') && matrix.type == 'fips' && 'release-fips' || '' }} + DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[3]) && matrix.type == 'fips' && '-fips' || '' }} + BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[3]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -472,10 +473,9 @@ jobs: # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) if [ "$service" == "rocketchat-cov" ]; then IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "rocketchat" '.services[$s].image')-cov - elif [ "$service" == "presence-service-fips" ]; then - IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "presence-service" '.services[$s].image')-fips - elif [ "$service" == "ddp-streamer-service-fips" ]; then - IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "ddp-streamer-service" '.services[$s].image')-fips + elif [[ "$service" == *"-fips" ]]; then + base_service="${service%-fips}" + IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "$base_service" '.services[$s].image')-fips else IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "$service" '.services[$s].image') fi From 5a42430a2dfff44baeb88dbd8189ea066b3ab4b5 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 15:09:07 -0300 Subject: [PATCH 14/66] ci: fips docker compose overrides --- .github/actions/build-docker/action.yml | 18 ++++++++---- .github/workflows/ci-test-e2e.yml | 39 ++++++++++++++++++------- .github/workflows/ci.yml | 22 +++++++------- docker-compose-ci.fips.yml | 10 +++++++ docker-compose-ci.yml | 5 ++-- 5 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 docker-compose-ci.fips.yml diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 16db97a624cfb..b5235ef131365 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -97,6 +97,10 @@ runs: GITHUB_REF: ${{ github.ref }} run: | set -o xtrace + compose_files=(-f docker-compose-ci.yml) + if [[ "$INPUT_TYPE" == 'fips' ]]; then + compose_files+=(-f docker-compose-ci.fips.yml) + fi # Removes unnecessary swc cores and sharp binaries to reduce image size swc_arch='x64' @@ -120,11 +124,11 @@ runs: LOAD_OR_PUSH="--load" fi - # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) - IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') + # Get image name from compose config since rocketchat image is different from service name (rocket.chat) + IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') docker buildx bake \ - -f docker-compose-ci.yml \ + "${compose_files[@]}" \ ${LOAD_OR_PUSH} \ --allow=fs.read=/tmp/build \ --set "*.tags+=${IMAGE}-gha-run-${GITHUB_RUN_ID}" \ @@ -174,9 +178,13 @@ runs: TYPE: ${{ inputs.type }} run: | set -o xtrace + compose_files=(-f docker-compose-ci.yml) + if [[ "$TYPE" == 'fips' ]]; then + compose_files+=(-f docker-compose-ci.fips.yml) + fi - # Get image name from docker-compose-ci.yml - IMAGE=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "$SERVICE" '.services[$s].image') + # Get image name from compose config + IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$SERVICE" '.services[$s].image') # Create directory for image archives mkdir -p /tmp/docker-images diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 631d1cbb20be5..0ac8049a8e8f1 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -75,7 +75,6 @@ jobs: env: # if building for production on develop branch or release, add suffix for coverage images DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ inputs.coverage == matrix.mongodb-version && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ inputs.release == 'fips' && '-fips' || '' }} MONGODB_VERSION: ${{ matrix.mongodb-version }} COVERAGE_DIR: '/tmp/coverage/${{ startsWith(inputs.type, ''api'') && ''api'' || inputs.type }}' COVERAGE_FILE_NAME: '${{ inputs.type }}-${{ matrix.shard }}.json' @@ -90,6 +89,16 @@ jobs: name: MongoDB ${{ matrix.mongodb-version }}${{ inputs.coverage == matrix.mongodb-version && ' coverage' || '' }} (${{ matrix.shard }}/${{ inputs.total-shard }}) steps: + - name: Set compose files + run: | + if [[ '${{ inputs.release }}' == 'fips' ]]; then + echo 'COMPOSE_FILES=-f docker-compose-ci.yml -f docker-compose-ci.fips.yml' >> "$GITHUB_ENV" + echo 'COMPOSE_FILES_METEOR=-f ../../docker-compose-ci.yml -f ../../docker-compose-ci.fips.yml' >> "$GITHUB_ENV" + else + echo 'COMPOSE_FILES=-f docker-compose-ci.yml' >> "$GITHUB_ENV" + echo 'COMPOSE_FILES_METEOR=-f ../../docker-compose-ci.yml' >> "$GITHUB_ENV" + fi + - name: Collect Workflow Telemetry if: inputs.type == 'perf' uses: catchpoint/workflow-telemetry-action@94c3c3d9567a0205de6da68a76c428ce4e769af1 # v2.0.0 @@ -165,7 +174,8 @@ jobs: - name: Start httpbin container and wait for it to be ready if: inputs.type == 'api' || inputs.type == 'api-livechat' run: | - docker compose -f docker-compose-ci.yml up -d httpbin + read -r -a compose_files <<< "$COMPOSE_FILES" + docker compose "${compose_files[@]}" up -d httpbin - name: Prepare code coverage directory run: | @@ -185,7 +195,8 @@ jobs: TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} run: | # when we are testing CE, we only need to start the rocketchat container - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d rocketchat --wait + read -r -a compose_files <<< "$COMPOSE_FILES" + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d rocketchat --wait - name: Start containers for EE if: inputs.release == 'ee' || inputs.release == 'fips' @@ -195,7 +206,8 @@ jobs: COMPOSE_PROFILES: ${{ inputs.type == 'api' && 'api' || '' }} TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} run: | - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d --wait + read -r -a compose_files <<< "$COMPOSE_FILES" + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait - uses: ./.github/actions/setup-playwright if: inputs.type == 'ui' @@ -203,11 +215,12 @@ jobs: - name: Wait services to start up if: inputs.release == 'ee' || inputs.release == 'fips' run: | + read -r -a compose_files <<< "$COMPOSE_FILES" docker ps - until docker compose -f docker-compose-ci.yml logs ddp-streamer-service | grep -q "NetworkBroker started successfully"; do + until docker compose "${compose_files[@]}" logs ddp-streamer-service | grep -q "NetworkBroker started successfully"; do echo "Waiting 'ddp-streamer' to start up" - ((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs ddp-streamer-service && exit 1 + ((c++)) && ((c==10)) && docker compose "${compose_files[@]}" logs ddp-streamer-service && exit 1 sleep 10 done; @@ -222,10 +235,11 @@ jobs: IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace + read -r -a compose_files_meteor <<< "$COMPOSE_FILES_METEOR" npm run testapi || s=$? - docker compose -f ../../docker-compose-ci.yml stop + docker compose "${compose_files_meteor[@]}" stop ls -la "$COVERAGE_DIR" exit "${s:-0}" @@ -238,10 +252,11 @@ jobs: IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace + read -r -a compose_files_meteor <<< "$COMPOSE_FILES_METEOR" npm run testapi:livechat || s=$? - docker compose -f ../../docker-compose-ci.yml stop + docker compose "${compose_files_meteor[@]}" stop ls -la "$COVERAGE_DIR" exit "${s:-0}" @@ -292,11 +307,15 @@ jobs: - name: Show server logs if E2E test failed if: failure() - run: docker compose -f docker-compose-ci.yml logs rocketchat authorization-service queue-worker-service ddp-streamer-service account-service presence-service omnichannel-transcript-service + run: | + read -r -a compose_files <<< "$COMPOSE_FILES" + docker compose "${compose_files[@]}" logs rocketchat authorization-service queue-worker-service ddp-streamer-service account-service presence-service omnichannel-transcript-service - name: Show mongo logs if E2E test failed if: failure() - run: docker compose -f docker-compose-ci.yml logs mongo + run: | + read -r -a compose_files <<< "$COMPOSE_FILES" + docker compose "${compose_files[@]}" logs mongo - name: Store coverage if: inputs.coverage == matrix.mongodb-version diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f299484c0804a..8b1c034fe6a85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,6 @@ concurrency: env: TOOL_NODE_FLAGS: ${{ vars.TOOL_NODE_FLAGS }} - FIPS_ENABLED_SERVICES: '["ddp-streamer-service","presence-service"]' jobs: release-versions: @@ -346,8 +345,6 @@ jobs: env: # add suffix for the extra images with coverage if building for production DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop') && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[0]) && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[0]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -364,8 +361,6 @@ jobs: if: matrix.service[1] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[1]) && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[1]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -383,8 +378,6 @@ jobs: if: matrix.service[2] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[2]) && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[2]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -402,8 +395,6 @@ jobs: if: matrix.service[3] && github.actor != 'dependabot[bot]' env: DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - DOCKER_TAG_SUFFIX_FIPS: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[3]) && matrix.type == 'fips' && '-fips' || '' }} - BUILD_TARGET: ${{ contains(fromJSON(env.FIPS_ENABLED_SERVICES), matrix.service[3]) && matrix.type == 'fips' && 'release-fips' || '' }} with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} @@ -431,6 +422,7 @@ jobs: with: sparse-checkout: | docker-compose-ci.yml + docker-compose-ci.fips.yml sparse-checkout-cone-mode: false ref: ${{ github.ref }} @@ -1038,6 +1030,7 @@ jobs: with: sparse-checkout: | docker-compose-ci.yml + docker-compose-ci.fips.yml sparse-checkout-cone-mode: false ref: ${{ github.ref }} @@ -1120,8 +1113,15 @@ jobs: IMAGE_NAME="${{ needs.release-versions.outputs.lowercase-repo }}/${service}" fi - # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) - SRC=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "${service}" '.services[$s].image') + # Get image name from compose config since rocketchat image is different from service name (rocket.chat) + if [ "$service" == "rocketchat-cov" ]; then + SRC=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "rocketchat" '.services[$s].image')-cov + elif [[ "$service" == *"-fips" ]]; then + base_service="${service%-fips}" + SRC=$(docker compose -f docker-compose-ci.yml -f docker-compose-ci.fips.yml config --format json 2>/dev/null | jq -r --arg s "$base_service" '.services[$s].image') + else + SRC=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "${service}" '.services[$s].image') + fi DEST_REPO="docker.io/${IMAGE_NAME}" echo "Copying $SRC to ${DEST_REPO}:${PRIMARY}" diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml new file mode 100644 index 0000000000000..fc67bc966df93 --- /dev/null +++ b/docker-compose-ci.fips.yml @@ -0,0 +1,10 @@ +services: + presence-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG}-fips + + ddp-streamer-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 00a9199d74dd7..305f0c81e2cc8 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -88,14 +88,13 @@ services: build: dockerfile: ee/apps/presence-service/Dockerfile context: . - target: ${BUILD_TARGET:-release-standard} x-bake: platforms: - linux/amd64 - linux/arm64 args: SERVICE: presence-service - image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_FIPS:-} + image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG} environment: - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0 - 'TRANSPORTER=${TRANSPORTER:-}' @@ -115,7 +114,7 @@ services: - linux/arm64 args: SERVICE: ddp-streamer - image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}${DOCKER_TAG_SUFFIX_FIPS:-} + image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG} environment: - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0 - 'TRANSPORTER=${TRANSPORTER:-}' From aca2e1f0cee9071983a81441e6faf6e7bf62cf1d Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 15:43:46 -0300 Subject: [PATCH 15/66] ci: specify targets --- docker-compose-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 305f0c81e2cc8..228ba08afc5e0 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -88,6 +88,7 @@ services: build: dockerfile: ee/apps/presence-service/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 @@ -108,6 +109,7 @@ services: build: dockerfile: ee/apps/ddp-streamer/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 From e448a9354ee497c9620c0897066ff9d4b7790e0d Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 16:16:28 -0300 Subject: [PATCH 16/66] ci: enhance naming and streamline service matrix --- .github/workflows/ci.yml | 81 +++++++++------------------------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b1c034fe6a85..21c9e2c8c177c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -299,7 +299,7 @@ jobs: type: ${{ matrix.type }} build-gh-docker: - name: 🚢 Build Docker + name: 🚢 Build Docker (${{ matrix.service }}-${{ matrix.type }}-${{ matrix.arch }}) needs: [build, release-versions] runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }} @@ -311,26 +311,32 @@ jobs: fail-fast: false matrix: arch: [arm64, amd64] - service: - [ - [authorization-service, queue-worker-service, ddp-streamer-service], - [account-service, presence-service, omnichannel-transcript-service], - [rocketchat], - ] + service: [authorization-service, queue-worker-service, ddp-streamer-service, account-service, presence-service, omnichannel-transcript-service, rocketchat] type: # if running in a PR build with coverage - ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }} + exclude: + # avoid duplicate rocketchat coverage rows in PRs (the include rows below handle coverage) + - arch: amd64 + service: rocketchat + type: ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || '' }} + - arch: arm64 + service: rocketchat + type: ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || '' }} include: # if not, build with coverage for tests - arch: amd64 - service: [rocketchat] + service: rocketchat type: coverage - arch: arm64 - service: [rocketchat] + service: rocketchat type: coverage # build dedicated FIPS images for FIPS test lanes - arch: amd64 - service: [ddp-streamer-service, presence-service] + service: ddp-streamer-service + type: fips + - arch: amd64 + service: presence-service type: fips steps: @@ -339,7 +345,7 @@ jobs: - uses: ./.github/actions/restore-packages # we only build and publish the actual docker images if not a PR from a fork - - name: Image ${{ matrix.service[0] }} + - name: Image ${{ matrix.service }} uses: ./.github/actions/build-docker if: github.actor != 'dependabot[bot]' env: @@ -352,60 +358,9 @@ jobs: TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} - service: ${{ matrix.service[0] }} - type: ${{ matrix.type }} - publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} - - - name: Image ${{ matrix.service[1] || '"skipped"' }} - uses: ./.github/actions/build-docker - if: matrix.service[1] && github.actor != 'dependabot[bot]' - env: - DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - with: - CR_USER: ${{ secrets.CR_USER }} - CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} - deno-version: ${{ needs.release-versions.outputs.deno-version }} - arch: ${{ matrix.arch }} - service: ${{ matrix.service[1] }} - type: ${{ matrix.type }} - publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} - setup-docker: false - - - name: Image ${{ matrix.service[2] || '"skipped"' }} - uses: ./.github/actions/build-docker - if: matrix.service[2] && github.actor != 'dependabot[bot]' - env: - DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - with: - CR_USER: ${{ secrets.CR_USER }} - CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} - deno-version: ${{ needs.release-versions.outputs.deno-version }} - arch: ${{ matrix.arch }} - service: ${{ matrix.service[2] }} - type: ${{ matrix.type }} - publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} - setup-docker: false - - - name: Image ${{ matrix.service[3] || '"skipped"' }} - uses: ./.github/actions/build-docker - if: matrix.service[3] && github.actor != 'dependabot[bot]' - env: - DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} - with: - CR_USER: ${{ secrets.CR_USER }} - CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} - deno-version: ${{ needs.release-versions.outputs.deno-version }} - arch: ${{ matrix.arch }} - service: ${{ matrix.service[3] }} + service: ${{ matrix.service }} type: ${{ matrix.type }} publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} - setup-docker: false build-gh-docker-publish: name: 🚢 Publish Docker Images (ghcr.io) From c450817af79456dd041c2311c63384a3d03fdb88 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 17:08:24 -0300 Subject: [PATCH 17/66] fix: set fips mode on start --- ee/apps/ddp-streamer/src/fips.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index dbe93b5f6d9a5..32f59060e314f 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -16,6 +16,16 @@ */ import crypto from 'crypto'; +crypto.setFips(true); + +if (crypto.getFips() !== 1) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); + try { crypto.createHash('sha1').update('test').digest('hex'); console.log('🔓 Native SHA-1 allowed. Skipping FIPS WebSocket patch.'); From 54c4e7fdd2e8a0778dfee3f9539f71339137d319 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 17:09:36 -0300 Subject: [PATCH 18/66] fix: update artifact pattern --- .github/workflows/ci-test-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 0ac8049a8e8f1..991be406645f9 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -144,7 +144,7 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 if: github.event.pull_request.head.repo.full_name != github.repository && github.event_name != 'release' && github.ref != 'refs/heads/develop' with: - pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || 'docker-image-*-amd64-coverage' }} + pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || (inputs.release == 'fips' && 'docker-image-*-amd64-fips' || 'docker-image-*-amd64-coverage') }} path: /tmp/docker-images merge-multiple: true From 12ec8076bb8cfc8ab1a76ffbd4132d3c4520d6f2 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 9 Mar 2026 17:10:05 -0300 Subject: [PATCH 19/66] ci: matrix generation job --- .github/workflows/ci.yml | 77 ++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21c9e2c8c177c..4a45e003305cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,10 @@ concurrency: env: TOOL_NODE_FLAGS: ${{ vars.TOOL_NODE_FLAGS }} + DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' + DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' + DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["ddp-streamer-service","presence-service"]' jobs: release-versions: @@ -298,9 +302,48 @@ jobs: source-hash: ${{ needs.release-versions.outputs.packages-build-cache-key }}-${{ needs.release-versions.outputs.meteor-rc-cache-key }} type: ${{ matrix.type }} + build-gh-docker-matrix: + name: ⚙️ Build Docker Matrix + needs: [build] + runs-on: ubuntu-24.04-arm + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + steps: + - id: generate + env: + DOCKER_BUILD_ARCHES_JSON: ${{ env.DOCKER_BUILD_ARCHES_JSON }} + DOCKER_BUILD_SERVICES_JSON: ${{ env.DOCKER_BUILD_SERVICES_JSON }} + DOCKER_BUILD_EXTRA_COVERAGE_JSON: ${{ env.DOCKER_BUILD_EXTRA_COVERAGE_JSON }} + DOCKER_BUILD_FIPS_SERVICES_JSON: ${{ env.DOCKER_BUILD_FIPS_SERVICES_JSON }} + run: | + node <<'NODE' + const fs = require('node:fs'); + + const baseType = '${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}'; + const arches = JSON.parse(process.env.DOCKER_BUILD_ARCHES_JSON); + const services = JSON.parse(process.env.DOCKER_BUILD_SERVICES_JSON); + const extraCoverageRows = JSON.parse(process.env.DOCKER_BUILD_EXTRA_COVERAGE_JSON); + const fipsServices = JSON.parse(process.env.DOCKER_BUILD_FIPS_SERVICES_JSON); + + const include = []; + for (const arch of arches) { + for (const service of services) { + include.push({ arch, service, type: baseType }); + } + } + + if (baseType === 'production') { + include.push(...extraCoverageRows); + } + + include.push(...fipsServices.map((service) => ({ arch: 'amd64', service, type: 'fips' }))); + + fs.appendFileSync(process.env.GITHUB_OUTPUT, `matrix=${JSON.stringify({ include })}\n`); + NODE + build-gh-docker: - name: 🚢 Build Docker (${{ matrix.service }}-${{ matrix.type }}-${{ matrix.arch }}) - needs: [build, release-versions] + name: 🚢 Build Docker (${{ matrix.service }}-${{ matrix.arch }}-${{ matrix.type }}) + needs: [build, build-gh-docker-matrix, release-versions] runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }} env: @@ -309,35 +352,7 @@ jobs: strategy: fail-fast: false - matrix: - arch: [arm64, amd64] - service: [authorization-service, queue-worker-service, ddp-streamer-service, account-service, presence-service, omnichannel-transcript-service, rocketchat] - type: - # if running in a PR build with coverage - - ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }} - exclude: - # avoid duplicate rocketchat coverage rows in PRs (the include rows below handle coverage) - - arch: amd64 - service: rocketchat - type: ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || '' }} - - arch: arm64 - service: rocketchat - type: ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || '' }} - include: - # if not, build with coverage for tests - - arch: amd64 - service: rocketchat - type: coverage - - arch: arm64 - service: rocketchat - type: coverage - # build dedicated FIPS images for FIPS test lanes - - arch: amd64 - service: ddp-streamer-service - type: fips - - arch: amd64 - service: presence-service - type: fips + matrix: ${{ fromJSON(needs.build-gh-docker-matrix.outputs.matrix) }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 317807206cb87f8102c49915e2d5246c741c4bc6 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 09:06:59 -0300 Subject: [PATCH 20/66] feat(authorization-service): fips --- .github/workflows/ci.yml | 2 +- docker-compose-ci.fips.yml | 5 +++++ docker-compose-ci.yml | 1 + ee/apps/authorization-service/Dockerfile | 13 ++++++++++++- ee/apps/authorization-service/src/fips.ts | 11 +++++++++++ ee/apps/authorization-service/tsconfig.json | 2 +- 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ee/apps/authorization-service/src/fips.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a45e003305cc..810aab29cc3df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["ddp-streamer-service","presence-service"]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","ddp-streamer-service","presence-service"]' jobs: release-versions: diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index fc67bc966df93..4cfc6ec0964ca 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -1,4 +1,9 @@ services: + authorization-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/authorization-service:${DOCKER_TAG}-fips + presence-service: build: target: release-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 228ba08afc5e0..536c61d8ecd4d 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -48,6 +48,7 @@ services: build: dockerfile: ee/apps/authorization-service/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index cf81d8593e2a0..e893179195343 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -93,7 +93,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -119,3 +119,14 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] + +# FIPS RELEASE STAGE +FROM rocketchatfips140/dhi-node:22-fips AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/authorization-service/src/fips.ts b/ee/apps/authorization-service/src/fips.ts new file mode 100644 index 0000000000000..c232a6e586011 --- /dev/null +++ b/ee/apps/authorization-service/src/fips.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +if (crypto.getFips() !== 1) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); diff --git a/ee/apps/authorization-service/tsconfig.json b/ee/apps/authorization-service/tsconfig.json index 04f7fc0034d1c..7c04aabd6bb1b 100644 --- a/ee/apps/authorization-service/tsconfig.json +++ b/ee/apps/authorization-service/tsconfig.json @@ -4,7 +4,7 @@ "strictPropertyInitialization": false, // TODO: Remove this line "outDir": "./dist" }, - "files": ["./src/service.ts"], + "files": ["./src/service.ts", "./src/fips.ts"], "include": ["../../../apps/meteor/definition/externals/meteor"], "exclude": ["./dist"] } From 6764e0823ee803010c3fd28773019b8bb281d211 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 09:28:23 -0300 Subject: [PATCH 21/66] fix(ddp-streamer): remove flaky check --- ee/apps/ddp-streamer/src/fips.ts | 227 +++++++++++++++---------------- 1 file changed, 111 insertions(+), 116 deletions(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 32f59060e314f..1767ed946a7e4 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -26,122 +26,117 @@ console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); console.log('========================================='); -try { - crypto.createHash('sha1').update('test').digest('hex'); - console.log('🔓 Native SHA-1 allowed. Skipping FIPS WebSocket patch.'); -} catch (err) { - console.log('🔒 FIPS 140-3 mode detected. Applying WebSocket Handshake Patch...'); - - const blocks = new Uint32Array(32); - const w = new Uint32Array(80); - const hashBuffer = Buffer.alloc(20); - - const generateWebSocketAccept = (message: string): string => { - if (message.length !== 60) { - throw new Error(`Expected 60-byte input for WS Accept, got ${message.length}`); +console.log('🔒 FIPS 140-3 mode detected. Applying WebSocket Handshake Patch...'); + +const blocks = new Uint32Array(32); +const w = new Uint32Array(80); +const hashBuffer = Buffer.alloc(20); + +const generateWebSocketAccept = (message: string): string => { + if (message.length !== 60) { + throw new Error(`Expected 60-byte input for WS Accept, got ${message.length}`); + } + + blocks.fill(0); + + let h0 = 0x67452301; + let h1 = 0xefcdab89; + let h2 = 0x98badcfe; + let h3 = 0x10325476; + let h4 = 0xc3d2e1f0; + + for (let i = 0; i < 60; i++) blocks[i >> 2] |= message.charCodeAt(i) << (24 - (i % 4) * 8); + blocks[15] = 0x80000000; + blocks[31] = 480; + + const rotl = (n: number, b: number) => (n << b) | (n >>> (32 - b)); + + for (let chunk = 0; chunk < 2; chunk++) { + const offset = chunk * 16; + for (let i = 0; i < 16; i++) w[i] = blocks[offset + i]; + for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + let a = h0; + let b = h1; + let c = h2; + let d = h3; + let e = h4; + let temp; + + for (let i = 0; i < 20; i++) { + temp = (rotl(a, 5) + (d ^ (b & (c ^ d))) + e + 0x5a827999 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; } - - blocks.fill(0); - - let h0 = 0x67452301; - let h1 = 0xefcdab89; - let h2 = 0x98badcfe; - let h3 = 0x10325476; - let h4 = 0xc3d2e1f0; - - for (let i = 0; i < 60; i++) blocks[i >> 2] |= message.charCodeAt(i) << (24 - (i % 4) * 8); - blocks[15] = 0x80000000; - blocks[31] = 480; - - const rotl = (n: number, b: number) => (n << b) | (n >>> (32 - b)); - - for (let chunk = 0; chunk < 2; chunk++) { - const offset = chunk * 16; - for (let i = 0; i < 16; i++) w[i] = blocks[offset + i]; - for (let i = 16; i < 80; i++) w[i] = rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); - - let a = h0; - let b = h1; - let c = h2; - let d = h3; - let e = h4; - let temp; - - for (let i = 0; i < 20; i++) { - temp = (rotl(a, 5) + (d ^ (b & (c ^ d))) + e + 0x5a827999 + w[i]) >>> 0; - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = temp; - } - for (let i = 20; i < 40; i++) { - temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0x6ed9eba1 + w[i]) >>> 0; - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = temp; - } - for (let i = 40; i < 60; i++) { - temp = (rotl(a, 5) + ((b & c) | (b & d) | (c & d)) + e + 0x8f1bbcdc + w[i]) >>> 0; - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = temp; - } - for (let i = 60; i < 80; i++) { - temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0xca62c1d6 + w[i]) >>> 0; - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = temp; - } - - h0 = (h0 + a) >>> 0; - h1 = (h1 + b) >>> 0; - h2 = (h2 + c) >>> 0; - h3 = (h3 + d) >>> 0; - h4 = (h4 + e) >>> 0; + for (let i = 20; i < 40; i++) { + temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0x6ed9eba1 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; } - - hashBuffer.writeUInt32BE(h0, 0); - hashBuffer.writeUInt32BE(h1, 4); - hashBuffer.writeUInt32BE(h2, 8); - hashBuffer.writeUInt32BE(h3, 12); - hashBuffer.writeUInt32BE(h4, 16); - - return hashBuffer.toString('base64'); - }; - - const originalCreateHash = crypto.createHash; - - crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { - if (algorithm.toLowerCase() === 'sha1') { - let inputData = ''; - - return { - update(data) { - if (typeof data === 'string') { - inputData += data; - } else if (Buffer.isBuffer(data)) { - inputData += data.toString('utf8'); - } else { - inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8'); - } - return this; - }, - digest(encoding) { - if (encoding === 'base64' && inputData.length === 60) { - return generateWebSocketAccept(inputData); - } - // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) - return originalCreateHash(algorithm, options).update(inputData).digest(encoding); - }, - } as crypto.Hash; + for (let i = 40; i < 60; i++) { + temp = (rotl(a, 5) + ((b & c) | (b & d) | (c & d)) + e + 0x8f1bbcdc + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; } - return originalCreateHash(algorithm, options); - }; -} + for (let i = 60; i < 80; i++) { + temp = (rotl(a, 5) + (b ^ c ^ d) + e + 0xca62c1d6 + w[i]) >>> 0; + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = temp; + } + + h0 = (h0 + a) >>> 0; + h1 = (h1 + b) >>> 0; + h2 = (h2 + c) >>> 0; + h3 = (h3 + d) >>> 0; + h4 = (h4 + e) >>> 0; + } + + hashBuffer.writeUInt32BE(h0, 0); + hashBuffer.writeUInt32BE(h1, 4); + hashBuffer.writeUInt32BE(h2, 8); + hashBuffer.writeUInt32BE(h3, 12); + hashBuffer.writeUInt32BE(h4, 16); + + return hashBuffer.toString('base64'); +}; + +const originalCreateHash = crypto.createHash; + +crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { + if (algorithm.toLowerCase() === 'sha1') { + let inputData = ''; + + return { + update(data) { + if (typeof data === 'string') { + inputData += data; + } else if (Buffer.isBuffer(data)) { + inputData += data.toString('utf8'); + } else { + inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8'); + } + return this; + }, + digest(encoding) { + if (encoding === 'base64' && inputData.length === 60) { + return generateWebSocketAccept(inputData); + } + // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) + return originalCreateHash(algorithm, options).update(inputData).digest(encoding); + }, + } as crypto.Hash; + } + return originalCreateHash(algorithm, options); +}; From 265085898889cfc0337b226bcfd12f6d1170467b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 10:37:00 -0300 Subject: [PATCH 22/66] feat(queue-worker): fips --- .github/workflows/ci.yml | 2 +- docker-compose-ci.fips.yml | 5 +++++ docker-compose-ci.yml | 1 + ee/apps/queue-worker/Dockerfile | 13 ++++++++++++- ee/apps/queue-worker/src/fips.ts | 11 +++++++++++ ee/apps/queue-worker/tsconfig.json | 2 +- 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ee/apps/queue-worker/src/fips.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 810aab29cc3df..1d4a084b78b7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","ddp-streamer-service","presence-service"]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","presence-service"]' jobs: release-versions: diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index 4cfc6ec0964ca..995d074cf456c 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -13,3 +13,8 @@ services: build: target: release-fips image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG}-fips + + queue-worker-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/queue-worker-service:${DOCKER_TAG}-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 536c61d8ecd4d..60d1241ef4b0a 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -137,6 +137,7 @@ services: build: dockerfile: ee/apps/queue-worker/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 9b8e8b19a8abc..3c6ccacff7240 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -100,7 +100,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -126,3 +126,14 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] + +# FIPS RELEASE STAGE +FROM rocketchatfips140/dhi-node:22-fips AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/queue-worker/src/fips.ts b/ee/apps/queue-worker/src/fips.ts new file mode 100644 index 0000000000000..c232a6e586011 --- /dev/null +++ b/ee/apps/queue-worker/src/fips.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +if (crypto.getFips() !== 1) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); diff --git a/ee/apps/queue-worker/tsconfig.json b/ee/apps/queue-worker/tsconfig.json index c12ebd48bcdc0..c36303f2794d1 100644 --- a/ee/apps/queue-worker/tsconfig.json +++ b/ee/apps/queue-worker/tsconfig.json @@ -4,7 +4,7 @@ "strictPropertyInitialization": false, "outDir": "./dist/ee/apps/queue-worker/src", }, - "files": ["./src/service.ts"], + "files": ["./src/service.ts", "./src/fips.ts"], "include": ["../../../apps/meteor/definition/externals/meteor"], "exclude": ["./dist"] } From d8dc0bf592c59681a8c02dae5659596c31cd3871 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 11:06:24 -0300 Subject: [PATCH 23/66] feat(account-service): fips --- .github/workflows/ci.yml | 2 +- docker-compose-ci.fips.yml | 5 +++++ docker-compose-ci.yml | 1 + ee/apps/account-service/Dockerfile | 13 ++++++++++++- ee/apps/account-service/src/fips.ts | 11 +++++++++++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 ee/apps/account-service/src/fips.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d4a084b78b7a..0427346080f9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","presence-service"]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service"]' jobs: release-versions: diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index 995d074cf456c..9d49cf28af538 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -1,4 +1,9 @@ services: + account-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/account-service:${DOCKER_TAG}-fips + authorization-service: build: target: release-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 60d1241ef4b0a..6fe4bfbcc2edf 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -69,6 +69,7 @@ services: build: dockerfile: ee/apps/account-service/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index f89df4181a69f..8345d7be41773 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -87,7 +87,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -113,3 +113,14 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] + +# FIPS RELEASE STAGE +FROM rocketchatfips140/dhi-node:22-fips AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/account-service/src/fips.ts b/ee/apps/account-service/src/fips.ts new file mode 100644 index 0000000000000..60cf98ffbb3d4 --- /dev/null +++ b/ee/apps/account-service/src/fips.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +if (!crypto.getFips()) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); From d75e00a83da2a3c6f34e857b526a05a715b5449a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 12:36:45 -0300 Subject: [PATCH 24/66] fix(ci): compose build fallback creds --- .github/workflows/ci-test-e2e.yml | 14 ++++++++++++++ .github/workflows/ci.yml | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 991be406645f9..be424daba2c9c 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -49,6 +49,10 @@ on: required: true CR_PAT: required: true + TEMP_DOCKERHUB_FIPS_USER: + required: false + TEMP_DOCKERHUB_FIPS_PASS: + required: false QASE_API_TOKEN: required: false REPORTER_ROCKETCHAT_URL: @@ -124,6 +128,16 @@ jobs: username: ${{ secrets.CR_USER }} password: ${{ secrets.CR_PAT }} + - name: Login to DockerHub for FIPS base images + if: inputs.release == 'fips' && env.TEMP_DOCKERHUB_FIPS_USER != '' && env.TEMP_DOCKERHUB_FIPS_PASS != '' + uses: docker/login-action@v3 + env: + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + with: + username: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + password: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup NodeJS diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0427346080f9c..730a12f2913dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -683,6 +683,8 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-api-livechat-fips: name: 🔨 Test API Livechat (FIPS) @@ -702,6 +704,8 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-ui-fips: name: 🔨 Test UI (FIPS) @@ -724,6 +728,8 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} From 652b0332554925d136815bb501c25cfbea08a45c Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 13:17:42 -0300 Subject: [PATCH 25/66] fix(ci): retry docker buildx with backoff --- .github/actions/build-docker/action.yml | 55 ++++++++++++++++++------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index b5235ef131365..987b97ed94eea 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -124,23 +124,48 @@ runs: LOAD_OR_PUSH="--load" fi + export DOCKER_CLIENT_TIMEOUT=300 + export COMPOSE_HTTP_TIMEOUT=300 + # Get image name from compose config since rocketchat image is different from service name (rocket.chat) - IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') - - docker buildx bake \ - "${compose_files[@]}" \ - ${LOAD_OR_PUSH} \ - --allow=fs.read=/tmp/build \ - --set "*.tags+=${IMAGE}-gha-run-${GITHUB_RUN_ID}" \ - --set "*.labels.org.opencontainers.image.description=Build run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ - --set "*.labels.org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \ - --set "*.platform=linux/${INPUT_ARCH}" \ - --set *.cache-from=type=gha \ - --set *.cache-to=type=gha,mode=max \ - --provenance=false \ - --sbom=false \ - --metadata-file "/tmp/meta.json" \ + IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') + + buildx_bake_cmd=( + docker buildx bake + "${compose_files[@]}" + "$LOAD_OR_PUSH" + "--allow=fs.read=/tmp/build" + "--set" + "*.tags+=${IMAGE}-gha-run-${GITHUB_RUN_ID}" + "--set" + "*.labels.org.opencontainers.image.description=Build run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + "--set" + "*.labels.org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" + "--set" + "*.platform=linux/${INPUT_ARCH}" + "--set" + "*.cache-from=type=gha" + "--set" + "*.cache-to=type=gha,mode=max" + "--provenance=false" + "--sbom=false" + --metadata-file "/tmp/meta.json" "$INPUT_SERVICE" + ) + + attempts=1 + max_attempts=3 + until "${buildx_bake_cmd[@]}"; do + if [[ "$INPUT_PUBLISH_IMAGE" != 'true' || $attempts -ge $max_attempts ]]; then + echo "docker buildx bake failed after ${attempts} attempt(s)." + exit 1 + fi + + attempts=$((attempts + 1)) + sleep_seconds=$((15 * attempts)) + echo "docker buildx bake failed (likely transient push error). Retrying in ${sleep_seconds}s... (attempt ${attempts}/${max_attempts})" + sleep "${sleep_seconds}" + done echo "Contents of /tmp/meta.json:" cat /tmp/meta.json From a3b305b79633fda7d97305c7bb346e50cab067e2 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 10 Mar 2026 14:13:28 -0300 Subject: [PATCH 26/66] feat(ci): fips image pull step service readiness checks --- .github/workflows/ci-test-e2e.yml | 42 ++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index be424daba2c9c..a39e52f7ee771 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -198,6 +198,12 @@ jobs: mkdir -p "$COVERAGE_DIR" chmod 777 "$COVERAGE_DIR" + - name: Pull FIPS images + if: inputs.release == 'fips' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') + run: | + read -r -a compose_files <<< "$COMPOSE_FILES" + docker compose "${compose_files[@]}" pull + - name: Start containers for CE if: inputs.release == 'ce' env: @@ -221,7 +227,11 @@ jobs: TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} run: | read -r -a compose_files <<< "$COMPOSE_FILES" - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait + if [[ '${{ inputs.release }}' == 'fips' ]]; then + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait --no-build + else + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait + fi - uses: ./.github/actions/setup-playwright if: inputs.type == 'ui' @@ -232,11 +242,31 @@ jobs: read -r -a compose_files <<< "$COMPOSE_FILES" docker ps - until docker compose "${compose_files[@]}" logs ddp-streamer-service | grep -q "NetworkBroker started successfully"; do - echo "Waiting 'ddp-streamer' to start up" - ((c++)) && ((c==10)) && docker compose "${compose_files[@]}" logs ddp-streamer-service && exit 1 - sleep 10 - done; + wait_for_service() { + local service="$1" + local retries=18 + local delay=10 + + for attempt in $(seq 1 "$retries"); do + if docker compose "${compose_files[@]}" logs "$service" | grep -q "NetworkBroker started successfully"; then + echo "Service '$service' is ready" + return 0 + fi + + echo "Waiting '$service' to start up (attempt ${attempt}/${retries})" + sleep "$delay" + done + + echo "Service '$service' did not become ready in time" + docker compose "${compose_files[@]}" logs "$service" + return 1 + } + + wait_for_service ddp-streamer-service + wait_for_service account-service + wait_for_service authorization-service + wait_for_service queue-worker-service + wait_for_service presence-service - name: Remove unused Docker images run: docker system prune -af From b3914662c9eae3b44b0cf06bcab41cd390a04f74 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 09:20:48 -0300 Subject: [PATCH 27/66] feat(omnichannel-transcript): fips --- .github/workflows/ci-test-e2e.yml | 1 + .github/workflows/ci.yml | 2 +- docker-compose-ci.fips.yml | 5 +++++ docker-compose-ci.yml | 1 + ee/apps/omnichannel-transcript/Dockerfile | 12 +++++++++++- ee/apps/omnichannel-transcript/src/fips.ts | 11 +++++++++++ ee/apps/omnichannel-transcript/tsconfig.json | 2 +- 7 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 ee/apps/omnichannel-transcript/src/fips.ts diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index a39e52f7ee771..a26d9cde96381 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -267,6 +267,7 @@ jobs: wait_for_service authorization-service wait_for_service queue-worker-service wait_for_service presence-service + wait_for_service omnichannel-transcript-service - name: Remove unused Docker images run: docker system prune -af diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 730a12f2913dc..979f777480954 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service"]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service"]' jobs: release-versions: diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index 9d49cf28af538..69bafb63e434f 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -23,3 +23,8 @@ services: build: target: release-fips image: ghcr.io/${LOWERCASE_REPOSITORY}/queue-worker-service:${DOCKER_TAG}-fips + + omnichannel-transcript-service: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/omnichannel-transcript-service:${DOCKER_TAG}-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 6fe4bfbcc2edf..8722ef84d408c 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -159,6 +159,7 @@ services: build: dockerfile: ee/apps/omnichannel-transcript/Dockerfile context: . + target: release-standard x-bake: platforms: - linux/amd64 diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 9b8e8b19a8abc..5f9ad7c381640 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -100,7 +100,7 @@ WORKDIR /app/ee/apps/${SERVICE} RUN yarn workspaces focus --production -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard ARG SERVICE @@ -126,3 +126,13 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] +# FIPS RELEASE STAGE +FROM rocketchatfips140/dhi-node:22-fips AS release-fips +ARG SERVICE +ENV NODE_ENV=production \ + PORT=3000 +COPY --chown=node:node --from=builder /app /app +WORKDIR /app/ee/apps/${SERVICE} +USER node +EXPOSE 3000 9458 +CMD ["node", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/omnichannel-transcript/src/fips.ts b/ee/apps/omnichannel-transcript/src/fips.ts new file mode 100644 index 0000000000000..60cf98ffbb3d4 --- /dev/null +++ b/ee/apps/omnichannel-transcript/src/fips.ts @@ -0,0 +1,11 @@ +import crypto from 'crypto'; + +crypto.setFips(true); + +if (!crypto.getFips()) { + throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); +} + +console.log('========================================='); +console.log('FIPS COMPLIANCE CHECK: YES'); +console.log('========================================='); diff --git a/ee/apps/omnichannel-transcript/tsconfig.json b/ee/apps/omnichannel-transcript/tsconfig.json index 6c7f2d916a732..253de00013a22 100644 --- a/ee/apps/omnichannel-transcript/tsconfig.json +++ b/ee/apps/omnichannel-transcript/tsconfig.json @@ -4,7 +4,7 @@ "strictPropertyInitialization": false, "outDir": "./dist/ee/apps/omnichannel-transcript/src", }, - "files": ["./src/service.ts"], + "files": ["./src/service.ts", "./src/fips.ts"], "include": ["../../../apps/meteor/definition/externals/meteor"], "exclude": ["./dist"] } From 050cc12ae8b9406aa450c69eaab592cf5ab8d778 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 10:40:44 -0300 Subject: [PATCH 28/66] ci: improve readiness checks --- .github/workflows/ci-test-e2e.yml | 59 +++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index a26d9cde96381..5d421ea4cabca 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -242,12 +242,52 @@ jobs: read -r -a compose_files <<< "$COMPOSE_FILES" docker ps + wait_for_mongo_primary() { + local retries=36 + local delay=5 + + for attempt in $(seq 1 "$retries"); do + local is_primary + is_primary=$(docker compose "${compose_files[@]}" exec -T mongo mongosh --quiet --eval "try { const hello = db.hello(); print((hello.isWritablePrimary || hello.ismaster) ? '1' : '0'); } catch (e) { print('0'); }" 2>/dev/null | tail -n1) + + if [[ "$is_primary" == '1' ]]; then + echo "Mongo replica set primary is ready" + return 0 + fi + + echo "Waiting for Mongo primary (attempt ${attempt}/${retries})" + sleep "$delay" + done + + echo "Mongo primary was not ready in time" + docker compose "${compose_files[@]}" logs mongo + return 1 + } + wait_for_service() { local service="$1" local retries=18 local delay=10 for attempt in $(seq 1 "$retries"); do + local container_id + container_id=$(docker compose "${compose_files[@]}" ps -q "$service") + + if [[ -z "$container_id" ]]; then + echo "Service '$service' has no container ID" + docker compose "${compose_files[@]}" ps + return 1 + fi + + local container_state + container_state=$(docker inspect -f '{{.State.Status}}' "$container_id" 2>/dev/null || echo "unknown") + + if [[ "$container_state" != 'running' ]]; then + echo "Service '$service' is not running (state=$container_state)" + docker compose "${compose_files[@]}" logs "$service" + return 1 + fi + if docker compose "${compose_files[@]}" logs "$service" | grep -q "NetworkBroker started successfully"; then echo "Service '$service' is ready" return 0 @@ -262,12 +302,19 @@ jobs: return 1 } - wait_for_service ddp-streamer-service - wait_for_service account-service - wait_for_service authorization-service - wait_for_service queue-worker-service - wait_for_service presence-service - wait_for_service omnichannel-transcript-service + mapfile -t services_to_wait < <( + docker compose "${compose_files[@]}" config --format json \ + | jq -r '.services | keys[] | select(endswith("-service"))' \ + | sort + ) + + wait_for_mongo_primary + + echo "Waiting for services: ${services_to_wait[*]}" + + for service in "${services_to_wait[@]}"; do + wait_for_service "$service" + done - name: Remove unused Docker images run: docker system prune -af From 8a31b91ca8c0d909cc51a51d9132b3cdbc9b4e39 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 11:36:27 -0300 Subject: [PATCH 29/66] fix(ci): update readiness check for omnichannel-transcript service --- .github/workflows/ci-test-e2e.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 5d421ea4cabca..1549ccd66b933 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -268,6 +268,13 @@ jobs: local service="$1" local retries=18 local delay=10 + local ready_pattern="NetworkBroker started successfully" + + case "$service" in + omnichannel-transcript-service) + ready_pattern="Service 'omnichannel-transcript' started." + ;; + esac for attempt in $(seq 1 "$retries"); do local container_id @@ -288,7 +295,7 @@ jobs: return 1 fi - if docker compose "${compose_files[@]}" logs "$service" | grep -q "NetworkBroker started successfully"; then + if docker compose "${compose_files[@]}" logs "$service" | grep -q "$ready_pattern"; then echo "Service '$service' is ready" return 0 fi From ae161b8e9c4fc98883d24eb98c07e66f6f0919f6 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 12:21:06 -0300 Subject: [PATCH 30/66] fix(ci): update image pull step to support both EE and FIPS releases --- .github/workflows/ci-test-e2e.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 1549ccd66b933..12d459431b20b 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -198,8 +198,8 @@ jobs: mkdir -p "$COVERAGE_DIR" chmod 777 "$COVERAGE_DIR" - - name: Pull FIPS images - if: inputs.release == 'fips' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') + - name: Pull EE/FIPS images + if: (inputs.release == 'ee' || inputs.release == 'fips') && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') run: | read -r -a compose_files <<< "$COMPOSE_FILES" docker compose "${compose_files[@]}" pull @@ -227,11 +227,7 @@ jobs: TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} run: | read -r -a compose_files <<< "$COMPOSE_FILES" - if [[ '${{ inputs.release }}' == 'fips' ]]; then - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait --no-build - else - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait - fi + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait --no-build - uses: ./.github/actions/setup-playwright if: inputs.type == 'ui' From 5f51175c8dcc939130818e1563e114efc8ffa521 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:45:31 -0300 Subject: [PATCH 31/66] ci: make readiness check more robust --- .github/workflows/ci-test-e2e.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 12d459431b20b..1a5eb3328b5c9 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -264,13 +264,8 @@ jobs: local service="$1" local retries=18 local delay=10 - local ready_pattern="NetworkBroker started successfully" - - case "$service" in - omnichannel-transcript-service) - ready_pattern="Service 'omnichannel-transcript' started." - ;; - esac + local broker_service_name="${service%-service}" + local ready_pattern="NetworkBroker started successfully|ServiceBroker with [0-9]+ service\(s\) started successfully|Service '${broker_service_name}' started\." for attempt in $(seq 1 "$retries"); do local container_id @@ -284,6 +279,8 @@ jobs: local container_state container_state=$(docker inspect -f '{{.State.Status}}' "$container_id" 2>/dev/null || echo "unknown") + local health_state + health_state=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container_id" 2>/dev/null || echo "unknown") if [[ "$container_state" != 'running' ]]; then echo "Service '$service' is not running (state=$container_state)" @@ -291,7 +288,18 @@ jobs: return 1 fi - if docker compose "${compose_files[@]}" logs "$service" | grep -q "$ready_pattern"; then + if [[ "$health_state" == 'unhealthy' ]]; then + echo "Service '$service' is unhealthy" + docker compose "${compose_files[@]}" logs "$service" + return 1 + fi + + if [[ "$health_state" == 'healthy' ]]; then + echo "Service '$service' is healthy" + return 0 + fi + + if docker compose "${compose_files[@]}" logs "$service" | grep -Eq "$ready_pattern"; then echo "Service '$service' is ready" return 0 fi From ba886eab0ce08e7f38d7dbd7ca29dba348b10e99 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:46:51 -0300 Subject: [PATCH 32/66] ci: do not skip rocketchat image in fork runs --- .github/workflows/ci-test-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 1a5eb3328b5c9..9d4c7468fa391 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -158,7 +158,7 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 if: github.event.pull_request.head.repo.full_name != github.repository && github.event_name != 'release' && github.ref != 'refs/heads/develop' with: - pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || (inputs.release == 'fips' && 'docker-image-*-amd64-fips' || 'docker-image-*-amd64-coverage') }} + pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || (inputs.release == 'fips' && 'docker-image-*-amd64-*' || 'docker-image-*-amd64-coverage') }} path: /tmp/docker-images merge-multiple: true From e6e39421f04312055dd32ff36bc9646d75b7c205 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:50:08 -0300 Subject: [PATCH 33/66] fix(fips): enhance SHA-1 handling in FIPS mode for WebSocket handshake --- ee/apps/ddp-streamer/src/fips.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 1767ed946a7e4..ec96ac44aedc6 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -119,22 +119,27 @@ crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { let inputData = ''; return { - update(data) { + update(data: string | Buffer | NodeJS.ArrayBufferView, inputEncoding?: crypto.Encoding) { if (typeof data === 'string') { - inputData += data; + if (inputEncoding) { + inputData += Buffer.from(data, inputEncoding as BufferEncoding).toString('latin1'); + } else { + inputData += data; + } } else if (Buffer.isBuffer(data)) { - inputData += data.toString('utf8'); + inputData += data.toString('latin1'); } else { - inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('utf8'); + inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('latin1'); } return this; }, - digest(encoding) { + digest(encoding?: crypto.BinaryToTextEncoding) { if (encoding === 'base64' && inputData.length === 60) { return generateWebSocketAccept(inputData); } // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) - return originalCreateHash(algorithm, options).update(inputData).digest(encoding); + const hash = originalCreateHash(algorithm, options).update(Buffer.from(inputData, 'latin1')); + return encoding ? hash.digest(encoding) : hash.digest(); }, } as crypto.Hash; } From af0f8a15d1da34da7cc47897b56a2f4f0906c7aa Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:50:19 -0300 Subject: [PATCH 34/66] fix(fips): enhance SHA-1 handling in FIPS mode with error handling for unsupported methods --- ee/apps/ddp-streamer/src/fips.ts | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index ec96ac44aedc6..d143875b0e575 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -114,11 +114,23 @@ const generateWebSocketAccept = (message: string): string => { const originalCreateHash = crypto.createHash; +const createUnsupportedSha1MethodError = (method: string): Error => + new Error( + `Unsupported SHA-1 hash API in FIPS mode: crypto.createHash('sha1').${method}. ` + + `Only update() and digest('base64') for the WebSocket handshake are supported.`, + ); + +const createUnsupportedSha1Method = (method: string) => { + return () => { + throw createUnsupportedSha1MethodError(method); + }; +}; + crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { if (algorithm.toLowerCase() === 'sha1') { let inputData = ''; - return { + const mockHash = { update(data: string | Buffer | NodeJS.ArrayBufferView, inputEncoding?: crypto.Encoding) { if (typeof data === 'string') { if (inputEncoding) { @@ -141,7 +153,23 @@ crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { const hash = originalCreateHash(algorithm, options).update(Buffer.from(inputData, 'latin1')); return encoding ? hash.digest(encoding) : hash.digest(); }, - } as crypto.Hash; + copy: createUnsupportedSha1Method('copy'), + on: createUnsupportedSha1Method('on'), + once: createUnsupportedSha1Method('once'), + emit: createUnsupportedSha1Method('emit'), + pipe: createUnsupportedSha1Method('pipe'), + } as Record; + + const guardedHash = new Proxy(mockHash, { + get(target, prop, receiver) { + if (typeof prop === 'string' && !(prop in target)) { + throw createUnsupportedSha1MethodError(prop); + } + return Reflect.get(target, prop, receiver); + }, + }); + + return guardedHash as unknown as crypto.Hash; } return originalCreateHash(algorithm, options); }; From 12bfc5a810894e0fa12e63985e839733a6f3e879 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:51:57 -0300 Subject: [PATCH 35/66] fix(ci): remove exclusion of rocketchat-cov service from promotion process --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 979f777480954..04478ee43b643 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1077,10 +1077,6 @@ jobs: [[ -d "$service_dir" ]] || continue service="$(basename "$service_dir")" - if [ "$service" == "rocketchat-cov" ]; then - continue - fi - echo "Promoting $service" if [[ "${service}" == 'rocketchat' ]]; then From 7ce95a2c78ab2d8f86d51584bc87d66d749917fa Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 13:54:03 -0300 Subject: [PATCH 36/66] fix(fips): improve SHA-1 handling in FIPS mode by storing updates for WebSocket handshake --- ee/apps/ddp-streamer/src/fips.ts | 40 +++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index d143875b0e575..87294d0c34d1b 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -126,31 +126,49 @@ const createUnsupportedSha1Method = (method: string) => { }; }; +type Sha1UpdateCall = { type: 'string'; chunk: string; encoding?: BufferEncoding } | { type: 'buffer'; chunk: Buffer }; + crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { if (algorithm.toLowerCase() === 'sha1') { - let inputData = ''; + const updates: Sha1UpdateCall[] = []; + let wsProbeInput = ''; const mockHash = { update(data: string | Buffer | NodeJS.ArrayBufferView, inputEncoding?: crypto.Encoding) { if (typeof data === 'string') { - if (inputEncoding) { - inputData += Buffer.from(data, inputEncoding as BufferEncoding).toString('latin1'); - } else { - inputData += data; - } + const encoding = inputEncoding as BufferEncoding | undefined; + updates.push({ type: 'string', chunk: data, encoding }); + wsProbeInput += Buffer.from(data, encoding ?? 'utf8').toString('latin1'); } else if (Buffer.isBuffer(data)) { - inputData += data.toString('latin1'); + const chunk = Buffer.from(data); + updates.push({ type: 'buffer', chunk }); + wsProbeInput += chunk.toString('latin1'); } else { - inputData += Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('latin1'); + const chunk = Buffer.from(data.buffer, data.byteOffset, data.byteLength); + updates.push({ type: 'buffer', chunk: Buffer.from(chunk) }); + wsProbeInput += chunk.toString('latin1'); } return this; }, digest(encoding?: crypto.BinaryToTextEncoding) { - if (encoding === 'base64' && inputData.length === 60) { - return generateWebSocketAccept(inputData); + if (encoding === 'base64' && wsProbeInput.length === 60) { + return generateWebSocketAccept(wsProbeInput); } // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) - const hash = originalCreateHash(algorithm, options).update(Buffer.from(inputData, 'latin1')); + const hash = originalCreateHash(algorithm, options); + + for (const updateCall of updates) { + if (updateCall.type === 'string') { + if (updateCall.encoding) { + hash.update(updateCall.chunk, updateCall.encoding); + } else { + hash.update(updateCall.chunk); + } + } else { + hash.update(updateCall.chunk); + } + } + return encoding ? hash.digest(encoding) : hash.digest(); }, copy: createUnsupportedSha1Method('copy'), From c6ebacfdb20e27eb4e3089f7e7f63d75e8e19b26 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 11 Mar 2026 15:07:36 -0300 Subject: [PATCH 37/66] chore: consistent fips checks --- ee/apps/authorization-service/src/fips.ts | 2 +- ee/apps/ddp-streamer/src/fips.ts | 2 +- ee/apps/presence-service/src/fips.ts | 2 +- ee/apps/queue-worker/src/fips.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ee/apps/authorization-service/src/fips.ts b/ee/apps/authorization-service/src/fips.ts index c232a6e586011..60cf98ffbb3d4 100644 --- a/ee/apps/authorization-service/src/fips.ts +++ b/ee/apps/authorization-service/src/fips.ts @@ -2,7 +2,7 @@ import crypto from 'crypto'; crypto.setFips(true); -if (crypto.getFips() !== 1) { +if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 87294d0c34d1b..fb645599eda15 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -18,7 +18,7 @@ import crypto from 'crypto'; crypto.setFips(true); -if (crypto.getFips() !== 1) { +if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } diff --git a/ee/apps/presence-service/src/fips.ts b/ee/apps/presence-service/src/fips.ts index c232a6e586011..60cf98ffbb3d4 100644 --- a/ee/apps/presence-service/src/fips.ts +++ b/ee/apps/presence-service/src/fips.ts @@ -2,7 +2,7 @@ import crypto from 'crypto'; crypto.setFips(true); -if (crypto.getFips() !== 1) { +if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } diff --git a/ee/apps/queue-worker/src/fips.ts b/ee/apps/queue-worker/src/fips.ts index c232a6e586011..60cf98ffbb3d4 100644 --- a/ee/apps/queue-worker/src/fips.ts +++ b/ee/apps/queue-worker/src/fips.ts @@ -2,7 +2,7 @@ import crypto from 'crypto'; crypto.setFips(true); -if (crypto.getFips() !== 1) { +if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } From 8855f9af42c34a9afdd0a55065c03d4a98831460 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 12 Mar 2026 11:24:26 -0300 Subject: [PATCH 38/66] fix(fips): add WebSocket handshake validation for SHA-1 input in FIPS mode --- ee/apps/ddp-streamer/src/fips.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index fb645599eda15..0188b3bd8f59d 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -31,6 +31,8 @@ console.log('🔒 FIPS 140-3 mode detected. Applying WebSocket Handshake Patch.. const blocks = new Uint32Array(32); const w = new Uint32Array(80); const hashBuffer = Buffer.alloc(20); +const WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; +const SEC_WEBSOCKET_KEY_BASE64_PATTERN = /^[A-Za-z0-9+/]{22}==$/; const generateWebSocketAccept = (message: string): string => { if (message.length !== 60) { @@ -128,6 +130,17 @@ const createUnsupportedSha1Method = (method: string) => { type Sha1UpdateCall = { type: 'string'; chunk: string; encoding?: BufferEncoding } | { type: 'buffer'; chunk: Buffer }; +const isWebSocketHandshakeSha1Input = (input: string): boolean => { + if (input.length !== 60) { + return false; + } + + const secWebSocketKey = input.slice(0, 24); + const guid = input.slice(24); + + return guid === WEBSOCKET_GUID && SEC_WEBSOCKET_KEY_BASE64_PATTERN.test(secWebSocketKey); +}; + crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { if (algorithm.toLowerCase() === 'sha1') { const updates: Sha1UpdateCall[] = []; @@ -151,7 +164,7 @@ crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { return this; }, digest(encoding?: crypto.BinaryToTextEncoding) { - if (encoding === 'base64' && wsProbeInput.length === 60) { + if (encoding === 'base64' && isWebSocketHandshakeSha1Input(wsProbeInput)) { return generateWebSocketAccept(wsProbeInput); } // If it's not the exact WS handshake, pass it back to native (which will throw FIPS error) From c396eeb3788ee24673231ce95f237a095db2dadd Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 12 Mar 2026 11:24:47 -0300 Subject: [PATCH 39/66] fix(fips): prevent multiple calls to digest in SHA-1 mock implementation for WebSocket handshake --- ee/apps/ddp-streamer/src/fips.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 0188b3bd8f59d..087647ba557e9 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -145,9 +145,14 @@ crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { if (algorithm.toLowerCase() === 'sha1') { const updates: Sha1UpdateCall[] = []; let wsProbeInput = ''; + let finalized = false; const mockHash = { update(data: string | Buffer | NodeJS.ArrayBufferView, inputEncoding?: crypto.Encoding) { + if (finalized) { + throw new Error('Digest already called'); + } + if (typeof data === 'string') { const encoding = inputEncoding as BufferEncoding | undefined; updates.push({ type: 'string', chunk: data, encoding }); @@ -164,6 +169,12 @@ crypto.createHash = function (algorithm: string, options?: crypto.HashOptions) { return this; }, digest(encoding?: crypto.BinaryToTextEncoding) { + if (finalized) { + throw new Error('Digest already called'); + } + + finalized = true; + if (encoding === 'base64' && isWebSocketHandshakeSha1Input(wsProbeInput)) { return generateWebSocketAccept(wsProbeInput); } From a97d28a1cd0e6f639ad9aaa75436a74a3b643b78 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 12 Mar 2026 11:25:55 -0300 Subject: [PATCH 40/66] fix(fips): add TEMP_DOCKERHUB_FIPS_USER and TEMP_DOCKERHUB_FIPS_PASS to environment variables for DockerHub login --- .github/workflows/ci-test-e2e.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 9d4c7468fa391..1a7d836e9cce6 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -83,6 +83,8 @@ jobs: COVERAGE_DIR: '/tmp/coverage/${{ startsWith(inputs.type, ''api'') && ''api'' || inputs.type }}' COVERAGE_FILE_NAME: '${{ inputs.type }}-${{ matrix.shard }}.json' COVERAGE_REPORTER: ${{ inputs.coverage == matrix.mongodb-version && 'json' || '' }} + TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} + TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} strategy: fail-fast: false @@ -131,9 +133,6 @@ jobs: - name: Login to DockerHub for FIPS base images if: inputs.release == 'fips' && env.TEMP_DOCKERHUB_FIPS_USER != '' && env.TEMP_DOCKERHUB_FIPS_PASS != '' uses: docker/login-action@v3 - env: - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} with: username: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} password: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} From 73b21e23c897b7b7beb4b6c56fa41d3cd9158d2d Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 12 Mar 2026 15:26:30 -0300 Subject: [PATCH 41/66] fix(ci): improve service wait logic by checking for running containers before proceeding --- .github/workflows/ci-test-e2e.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 1a7d836e9cce6..f3dac1b1dbd6d 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -313,13 +313,19 @@ jobs: } mapfile -t services_to_wait < <( - docker compose "${compose_files[@]}" config --format json \ - | jq -r '.services | keys[] | select(endswith("-service"))' \ + docker compose "${compose_files[@]}" ps --services --status running \ + | grep -- '-service$' \ | sort ) wait_for_mongo_primary + if (( ${#services_to_wait[@]} == 0 )); then + echo "No running -service containers found to wait for" + docker compose "${compose_files[@]}" ps + exit 1 + fi + echo "Waiting for services: ${services_to_wait[*]}" for service in "${services_to_wait[@]}"; do From 36a257c35e9fed0652d24519d5b1fae2b66bb867 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 13 Mar 2026 12:05:28 -0300 Subject: [PATCH 42/66] fix(ci): semgrep findings --- .github/actions/build-docker/action.yml | 64 +++++++++++-------------- .github/workflows/ci-test-e2e.yml | 8 ++-- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 987b97ed94eea..1b6523c9eefd9 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -85,18 +85,20 @@ runs: - name: Build Docker images shell: bash env: - DENO_VERSION: ${{ inputs.deno-version }} + INPUT_DENO_VERSION: ${{ inputs.deno-version }} INPUT_ARCH: ${{ inputs.arch }} INPUT_SERVICE: ${{ inputs.service }} INPUT_PUBLISH_IMAGE: ${{ inputs.publish-image }} INPUT_TYPE: ${{ inputs.type }} - GITHUB_RUN_ID: ${{ github.run_id }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_EVENT_NAME: ${{ github.event_name }} - GITHUB_REF: ${{ github.ref }} + GH_RUN_ID: ${{ github.run_id }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + GH_EVENT_NAME: ${{ github.event_name }} + GH_REF: ${{ github.ref }} + SERVICE_SUFFIX_FROM_CONTEXT: ${{ (inputs.service == 'rocketchat' && inputs.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop')) && '-cov' || (inputs.type == 'fips' && '-fips' || '') }} run: | set -o xtrace + export DENO_VERSION="$INPUT_DENO_VERSION" compose_files=(-f docker-compose-ci.yml) if [[ "$INPUT_TYPE" == 'fips' ]]; then compose_files+=(-f docker-compose-ci.fips.yml) @@ -130,25 +132,22 @@ runs: # Get image name from compose config since rocketchat image is different from service name (rocket.chat) IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') + BUILD_RUN_URL="${GH_SERVER_URL}/${GH_REPOSITORY}/actions/runs/${GH_RUN_ID}" + BUILD_SOURCE_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + buildx_bake_cmd=( docker buildx bake "${compose_files[@]}" - "$LOAD_OR_PUSH" - "--allow=fs.read=/tmp/build" - "--set" - "*.tags+=${IMAGE}-gha-run-${GITHUB_RUN_ID}" - "--set" - "*.labels.org.opencontainers.image.description=Build run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" - "--set" - "*.labels.org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" - "--set" - "*.platform=linux/${INPUT_ARCH}" - "--set" - "*.cache-from=type=gha" - "--set" - "*.cache-to=type=gha,mode=max" - "--provenance=false" - "--sbom=false" + ${LOAD_OR_PUSH} + --allow=fs.read=/tmp/build + --set "*.tags+=${IMAGE}-gha-run-${GH_RUN_ID}" + --set "*.labels.org.opencontainers.image.description=Build run: ${BUILD_RUN_URL}" + --set "*.labels.org.opencontainers.image.source=${BUILD_SOURCE_URL}" + --set "*.platform=linux/${INPUT_ARCH}" + --set *.cache-from=type=gha + --set *.cache-to=type=gha,mode=max + --provenance=false + --sbom=false --metadata-file "/tmp/meta.json" "$INPUT_SERVICE" ) @@ -171,12 +170,7 @@ runs: cat /tmp/meta.json if [[ "$INPUT_PUBLISH_IMAGE" == 'true' ]]; then - SERVICE_SUFFIX='' - if [[ "$INPUT_SERVICE" == 'rocketchat' && "$INPUT_TYPE" == 'coverage' ]] && [[ "$GITHUB_EVENT_NAME" == 'release' || "$GITHUB_REF" == 'refs/heads/develop' ]]; then - SERVICE_SUFFIX='-cov' - elif [[ "$INPUT_TYPE" == 'fips' ]]; then - SERVICE_SUFFIX='-fips' - fi + SERVICE_SUFFIX="$SERVICE_SUFFIX_FROM_CONTEXT" mkdir -p "/tmp/manifests/${INPUT_SERVICE}${SERVICE_SUFFIX}/${INPUT_ARCH}" @@ -198,26 +192,26 @@ runs: if: inputs.publish-image == 'false' && inputs.arch == 'amd64' shell: bash env: - SERVICE: ${{ inputs.service }} - ARCH: ${{ inputs.arch }} - TYPE: ${{ inputs.type }} + INPUT_TYPE: ${{ inputs.type }} + INPUT_SERVICE: ${{ inputs.service }} + INPUT_ARCH: ${{ inputs.arch }} run: | set -o xtrace compose_files=(-f docker-compose-ci.yml) - if [[ "$TYPE" == 'fips' ]]; then + if [[ "$INPUT_TYPE" == 'fips' ]]; then compose_files+=(-f docker-compose-ci.fips.yml) fi # Get image name from compose config - IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$SERVICE" '.services[$s].image') + IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') # Create directory for image archives mkdir -p /tmp/docker-images # Save the image to a tar file - docker save "${IMAGE}" -o "/tmp/docker-images/${SERVICE}-${ARCH}-${TYPE}.tar" + docker save "${IMAGE}" -o "/tmp/docker-images/${INPUT_SERVICE}-${INPUT_ARCH}-${INPUT_TYPE}.tar" - echo "Saved image to /tmp/docker-images/${SERVICE}-${ARCH}-${TYPE}.tar" + echo "Saved image to /tmp/docker-images/${INPUT_SERVICE}-${INPUT_ARCH}-${INPUT_TYPE}.tar" ls -lh /tmp/docker-images/ - name: Upload Docker image artifact diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index f3dac1b1dbd6d..528dfbcdda109 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -96,8 +96,10 @@ jobs: steps: - name: Set compose files + env: + INPUT_RELEASE: ${{ inputs.release }} run: | - if [[ '${{ inputs.release }}' == 'fips' ]]; then + if [[ "$INPUT_RELEASE" == 'fips' ]]; then echo 'COMPOSE_FILES=-f docker-compose-ci.yml -f docker-compose-ci.fips.yml' >> "$GITHUB_ENV" echo 'COMPOSE_FILES_METEOR=-f ../../docker-compose-ci.yml -f ../../docker-compose-ci.fips.yml' >> "$GITHUB_ENV" else @@ -372,6 +374,8 @@ jobs: - name: E2E Test UI (${{ matrix.shard }}/${{ inputs.total-shard }}) if: inputs.type == 'ui' env: + E2E_SHARD: ${{ matrix.shard }} + E2E_TOTAL_SHARD: ${{ inputs.total-shard }} E2E_COVERAGE: ${{ inputs.coverage == matrix.mongodb-version && 'true' || '' }} IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} @@ -389,8 +393,6 @@ jobs: QASE_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} CI: true PLAYWRIGHT_RETRIES: ${{ inputs.retries }} - E2E_SHARD: ${{ matrix.shard }} - E2E_TOTAL_SHARD: ${{ inputs.total-shard }} working-directory: ./apps/meteor run: | set -o xtrace From 42645350251e5f3e0167892f8eff5bedc3abd142 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 16 Mar 2026 15:43:34 -0300 Subject: [PATCH 43/66] chore: reduce logs --- ee/apps/account-service/src/fips.ts | 2 -- ee/apps/authorization-service/src/fips.ts | 2 -- ee/apps/ddp-streamer/src/fips.ts | 4 ---- ee/apps/omnichannel-transcript/src/fips.ts | 2 -- ee/apps/presence-service/src/fips.ts | 2 -- ee/apps/queue-worker/src/fips.ts | 2 -- 6 files changed, 14 deletions(-) diff --git a/ee/apps/account-service/src/fips.ts b/ee/apps/account-service/src/fips.ts index 60cf98ffbb3d4..fab82191a20c8 100644 --- a/ee/apps/account-service/src/fips.ts +++ b/ee/apps/account-service/src/fips.ts @@ -6,6 +6,4 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); diff --git a/ee/apps/authorization-service/src/fips.ts b/ee/apps/authorization-service/src/fips.ts index 60cf98ffbb3d4..fab82191a20c8 100644 --- a/ee/apps/authorization-service/src/fips.ts +++ b/ee/apps/authorization-service/src/fips.ts @@ -6,6 +6,4 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); diff --git a/ee/apps/ddp-streamer/src/fips.ts b/ee/apps/ddp-streamer/src/fips.ts index 087647ba557e9..37651fc8bf1a6 100644 --- a/ee/apps/ddp-streamer/src/fips.ts +++ b/ee/apps/ddp-streamer/src/fips.ts @@ -22,11 +22,7 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); - -console.log('🔒 FIPS 140-3 mode detected. Applying WebSocket Handshake Patch...'); const blocks = new Uint32Array(32); const w = new Uint32Array(80); diff --git a/ee/apps/omnichannel-transcript/src/fips.ts b/ee/apps/omnichannel-transcript/src/fips.ts index 60cf98ffbb3d4..fab82191a20c8 100644 --- a/ee/apps/omnichannel-transcript/src/fips.ts +++ b/ee/apps/omnichannel-transcript/src/fips.ts @@ -6,6 +6,4 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); diff --git a/ee/apps/presence-service/src/fips.ts b/ee/apps/presence-service/src/fips.ts index 60cf98ffbb3d4..fab82191a20c8 100644 --- a/ee/apps/presence-service/src/fips.ts +++ b/ee/apps/presence-service/src/fips.ts @@ -6,6 +6,4 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); diff --git a/ee/apps/queue-worker/src/fips.ts b/ee/apps/queue-worker/src/fips.ts index 60cf98ffbb3d4..fab82191a20c8 100644 --- a/ee/apps/queue-worker/src/fips.ts +++ b/ee/apps/queue-worker/src/fips.ts @@ -6,6 +6,4 @@ if (!crypto.getFips()) { throw new Error('FIPS mode was not enabled after crypto.setFips(true)'); } -console.log('========================================='); console.log('FIPS COMPLIANCE CHECK: YES'); -console.log('========================================='); From 2c0be06c9e001b9181c582f5880eb24d4bf7a328 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 16 Mar 2026 15:50:01 -0300 Subject: [PATCH 44/66] chore: use --force-fips --- ee/apps/account-service/Dockerfile | 3 +-- ee/apps/authorization-service/Dockerfile | 3 +-- ee/apps/ddp-streamer/Dockerfile | 2 +- ee/apps/omnichannel-transcript/Dockerfile | 4 ++-- ee/apps/presence-service/Dockerfile | 3 +-- ee/apps/queue-worker/Dockerfile | 3 +-- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index 8345d7be41773..2c85a283ec599 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -114,7 +114,6 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -# FIPS RELEASE STAGE FROM rocketchatfips140/dhi-node:22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ @@ -123,4 +122,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index e893179195343..92137d0c89e84 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -120,7 +120,6 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -# FIPS RELEASE STAGE FROM rocketchatfips140/dhi-node:22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ @@ -129,4 +128,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 3edfec0f6b0d7..6227367ebe79e 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -125,4 +125,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 5f9ad7c381640..2ddf7916874aa 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -126,7 +126,7 @@ USER rocketchat EXPOSE 3000 9458 CMD ["node", "src/service.js"] -# FIPS RELEASE STAGE + FROM rocketchatfips140/dhi-node:22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ @@ -135,4 +135,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index 78220ce2b7e2c..843a459e81379 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -115,7 +115,6 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -# FIPS RELEASE STAGE FROM rocketchatfips140/dhi-node:22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ @@ -124,4 +123,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 3c6ccacff7240..2ddf7916874aa 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -127,7 +127,6 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -# FIPS RELEASE STAGE FROM rocketchatfips140/dhi-node:22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ @@ -136,4 +135,4 @@ COPY --chown=node:node --from=builder /app /app WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 -CMD ["node", "--require", "./src/fips.js", "src/service.js"] +CMD ["node", "--force-fips", "--require", "./src/fips.js", "src/service.js"] From 62d927585cb8ad7af2a2e4bc5196a740656bc770 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 17 Mar 2026 10:09:43 -0300 Subject: [PATCH 45/66] chore: reduce diff --- .github/workflows/ci-test-e2e.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 528dfbcdda109..07823da400f96 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -374,8 +374,6 @@ jobs: - name: E2E Test UI (${{ matrix.shard }}/${{ inputs.total-shard }}) if: inputs.type == 'ui' env: - E2E_SHARD: ${{ matrix.shard }} - E2E_TOTAL_SHARD: ${{ inputs.total-shard }} E2E_COVERAGE: ${{ inputs.coverage == matrix.mongodb-version && 'true' || '' }} IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} @@ -393,6 +391,8 @@ jobs: QASE_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} CI: true PLAYWRIGHT_RETRIES: ${{ inputs.retries }} + E2E_SHARD: ${{ matrix.shard }} + E2E_TOTAL_SHARD: ${{ inputs.total-shard }} working-directory: ./apps/meteor run: | set -o xtrace From 00810bede64a92591dfa43bfb9e40fe709fdfed4 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 27 Mar 2026 10:47:13 -0300 Subject: [PATCH 46/66] feat(ddp-streamer): use alpine for fips --- ee/apps/ddp-streamer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 6227367ebe79e..6fcdee4395815 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -117,7 +117,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-alpine3.23 AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From 60b622390ac3013a2d7cc6b6ddb5e6c05a2ba3a5 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 27 Mar 2026 11:51:38 -0300 Subject: [PATCH 47/66] feat(omnichannel-transcript): use alpine for fips --- ee/apps/omnichannel-transcript/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 2ddf7916874aa..516fc3494990a 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-fips AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From 82666484c37633ebdd63440d5a5aae049ad8b904 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 27 Mar 2026 11:56:33 -0300 Subject: [PATCH 48/66] feat(account-service): use alpine for fips --- ee/apps/account-service/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index 2c85a283ec599..c1588d9a409f0 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -114,11 +114,12 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-fips AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 COPY --chown=node:node --from=builder /app /app + WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 From 2a11fe038684127438b3037db2fdf3d7750cbda1 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 27 Mar 2026 12:12:47 -0300 Subject: [PATCH 49/66] feat(presence-service): use alpine for fips --- ee/apps/presence-service/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index 843a459e81379..8b6e26e8f32a8 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -115,7 +115,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-fips AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From 69d4717a1d1c39b8c2bb5417b3fcc5cf44f50487 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 27 Mar 2026 12:13:11 -0300 Subject: [PATCH 50/66] feat(queue-worker): use alpine for fips --- ee/apps/queue-worker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 2ddf7916874aa..516fc3494990a 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-fips AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From f0788bed190bab316b4f8e07d9fd709f4e4147ff Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 30 Mar 2026 12:43:19 -0300 Subject: [PATCH 51/66] fix(ci): skip publish of rocketchat-cov --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 04478ee43b643..1db92344acada 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1077,6 +1077,11 @@ jobs: [[ -d "$service_dir" ]] || continue service="$(basename "$service_dir")" + if [[ "$service" == 'rocketchat-cov' ]]; then + echo "Skipping $service for DockerHub publish" + continue + fi + echo "Promoting $service" if [[ "${service}" == 'rocketchat' ]]; then From 7e7b0b4235f006d3a3a974531ad27e895d9afa0d Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 30 Mar 2026 12:45:01 -0300 Subject: [PATCH 52/66] ci: fix retry loop --- .github/workflows/ci-test-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 07823da400f96..320e61200e4f7 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -245,7 +245,7 @@ jobs: for attempt in $(seq 1 "$retries"); do local is_primary - is_primary=$(docker compose "${compose_files[@]}" exec -T mongo mongosh --quiet --eval "try { const hello = db.hello(); print((hello.isWritablePrimary || hello.ismaster) ? '1' : '0'); } catch (e) { print('0'); }" 2>/dev/null | tail -n1) + is_primary=$(docker compose "${compose_files[@]}" exec -T mongo mongosh --quiet --eval "try { const hello = db.hello(); print((hello.isWritablePrimary || hello.ismaster) ? '1' : '0'); } catch (e) { print('0'); }" 2>/dev/null | tail -n1 || true) if [[ "$is_primary" == '1' ]]; then echo "Mongo replica set primary is ready" From 46c3db0446faaf7cd17527df21d06925a73d28d7 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 5 May 2026 10:44:05 -0300 Subject: [PATCH 53/66] chore: normalize dockerfiles --- ee/apps/account-service/Dockerfile | 1 - ee/apps/authorization-service/Dockerfile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index c1588d9a409f0..a87be1da134c0 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -119,7 +119,6 @@ ARG SERVICE ENV NODE_ENV=production \ PORT=3000 COPY --chown=node:node --from=builder /app /app - WORKDIR /app/ee/apps/${SERVICE} USER node EXPOSE 3000 9458 diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index 92137d0c89e84..d0df0ddd399b5 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -120,7 +120,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22-fips AS release-fips +FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From 599a1425aab27e239c9144156e9ef3a7b9702e1a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 7 May 2026 14:43:07 -0300 Subject: [PATCH 54/66] ci(fips): build and publish rocketchat-fips image with temporary production artifact mapping --- .github/actions/build-docker/action.yml | 4 +++- .github/workflows/ci.yml | 2 +- apps/meteor/.docker/Dockerfile.alpine | 6 +++++- docker-compose-ci.fips.yml | 5 +++++ docker-compose-ci.yml | 1 + ee/packages/federation-matrix/docker-compose.test.yml | 1 + 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 1b6523c9eefd9..ba063a83b7e49 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -59,7 +59,9 @@ runs: if: inputs.service == 'rocketchat' uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: - name: build-${{ inputs.type }} + # Temporary: rocketchat fips reuses existing Meteor artifacts until build-fips exists. + # PR/merge queue runs only produce build-coverage, while release/develop produce build-production. + name: build-${{ inputs.type == 'fips' && ((github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production') || inputs.type }} path: /tmp/build - name: Unpack meteor build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1db92344acada..546caa338297d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service"]' + DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' jobs: release-versions: diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 2bee7b45f36e3..3f2c3a277b246 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -20,7 +20,7 @@ RUN cd /app/bundle/programs/server \ && find /app/bundle/programs/server/npm/node_modules -type f -name '*.map' -delete \ && find /app/bundle/programs/web.browser -type f -name '*.map' -delete -FROM node:22.22.3-alpine3.23 +FROM node:22.22.3-alpine3.23 AS release-standard LABEL maintainer="buildmaster@rocket.chat" @@ -65,3 +65,7 @@ WORKDIR /app/bundle EXPOSE 3000 CMD ["node", "main.js"] + +FROM release-standard AS release-fips + +CMD ["node", "main.js"] diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index 69bafb63e434f..bd3f55c510edb 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -1,4 +1,9 @@ services: + rocketchat: + build: + target: release-fips + image: ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${DOCKER_TAG}-fips + account-service: build: target: release-fips diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index 8722ef84d408c..963a6614e3229 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -5,6 +5,7 @@ services: build: dockerfile: ${GITHUB_WORKSPACE:-}/apps/meteor/.docker/Dockerfile.alpine context: /tmp/build + target: release-standard x-bake: platforms: - linux/amd64 diff --git a/ee/packages/federation-matrix/docker-compose.test.yml b/ee/packages/federation-matrix/docker-compose.test.yml index ad06faeb0ef38..4c9fc6bf44390 100644 --- a/ee/packages/federation-matrix/docker-compose.test.yml +++ b/ee/packages/federation-matrix/docker-compose.test.yml @@ -82,6 +82,7 @@ services: build: context: ${ROCKETCHAT_BUILD_CONTEXT:-./test/dist} dockerfile: ${ROCKETCHAT_DOCKERFILE:-../../../apps/meteor/.docker/Dockerfile.alpine} + target: release-standard image: ${ROCKETCHAT_IMAGE:-rocketchat/rocket.chat:latest} profiles: - test From e0798a4ea06584a067c7252689074bb43bb97615 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 11 May 2026 10:24:20 -0300 Subject: [PATCH 55/66] ci: hash pin docker/login-action --- .github/actions/build-docker/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index ba063a83b7e49..05f14b0ee79f6 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -51,7 +51,7 @@ runs: - name: Login to DockerHub for FIPS base images if: inputs.type == 'fips' && inputs.TEMP_DOCKERHUB_FIPS_USER != '' && inputs.TEMP_DOCKERHUB_FIPS_PASS != '' - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: username: ${{ inputs.TEMP_DOCKERHUB_FIPS_USER }} password: ${{ inputs.TEMP_DOCKERHUB_FIPS_PASS }} From bfe467855f2ef8a65fdf501ef9af19dba55fb734 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Mon, 11 May 2026 14:19:44 -0300 Subject: [PATCH 56/66] fix(fips): remove temporary DockerHub org --- .github/actions/build-docker/action.yml | 15 ++++++++------- .github/workflows/ci-test-e2e.yml | 13 ------------- .github/workflows/ci.yml | 10 ++-------- ee/apps/account-service/Dockerfile | 2 +- ee/apps/authorization-service/Dockerfile | 2 +- ee/apps/ddp-streamer/Dockerfile | 2 +- ee/apps/omnichannel-transcript/Dockerfile | 2 +- ee/apps/presence-service/Dockerfile | 2 +- ee/apps/queue-worker/Dockerfile | 2 +- 9 files changed, 16 insertions(+), 34 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 05f14b0ee79f6..ca8a14b5f3e5a 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -8,12 +8,12 @@ inputs: CR_PAT: required: true description: 'GitHub Container Registry Personal Access Token' - TEMP_DOCKERHUB_FIPS_USER: + DOCKER_USER: required: false - description: 'Temporary DockerHub user for FIPS base image pulls' - TEMP_DOCKERHUB_FIPS_PASS: + description: 'DockerHub username for private base image pulls' + DOCKER_PASS: required: false - description: 'Temporary DockerHub password/token for FIPS base image pulls' + description: 'DockerHub password/token for private base image pulls' deno-version: required: true description: 'Deno version' @@ -50,11 +50,12 @@ runs: password: ${{ inputs.CR_PAT }} - name: Login to DockerHub for FIPS base images - if: inputs.type == 'fips' && inputs.TEMP_DOCKERHUB_FIPS_USER != '' && inputs.TEMP_DOCKERHUB_FIPS_PASS != '' + if: inputs.type == 'fips' && inputs.DOCKER_USER != '' && inputs.DOCKER_PASS != '' uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: - username: ${{ inputs.TEMP_DOCKERHUB_FIPS_USER }} - password: ${{ inputs.TEMP_DOCKERHUB_FIPS_PASS }} + username: ${{ inputs.DOCKER_USER }} + password: ${{ inputs.DOCKER_PASS }} + - name: Restore meteor build if: inputs.service == 'rocketchat' uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 320e61200e4f7..a7b597bdd406f 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -49,10 +49,6 @@ on: required: true CR_PAT: required: true - TEMP_DOCKERHUB_FIPS_USER: - required: false - TEMP_DOCKERHUB_FIPS_PASS: - required: false QASE_API_TOKEN: required: false REPORTER_ROCKETCHAT_URL: @@ -83,8 +79,6 @@ jobs: COVERAGE_DIR: '/tmp/coverage/${{ startsWith(inputs.type, ''api'') && ''api'' || inputs.type }}' COVERAGE_FILE_NAME: '${{ inputs.type }}-${{ matrix.shard }}.json' COVERAGE_REPORTER: ${{ inputs.coverage == matrix.mongodb-version && 'json' || '' }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} strategy: fail-fast: false @@ -132,13 +126,6 @@ jobs: username: ${{ secrets.CR_USER }} password: ${{ secrets.CR_PAT }} - - name: Login to DockerHub for FIPS base images - if: inputs.release == 'fips' && env.TEMP_DOCKERHUB_FIPS_USER != '' && env.TEMP_DOCKERHUB_FIPS_PASS != '' - uses: docker/login-action@v3 - with: - username: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - password: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup NodeJS diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 546caa338297d..b3d19945fd97d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -369,8 +369,8 @@ jobs: with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} + DOCKER_USER: ${{ secrets.DOCKER_USER }} + DOCKER_PASS: ${{ secrets.DOCKER_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} service: ${{ matrix.service }} @@ -683,8 +683,6 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-api-livechat-fips: name: 🔨 Test API Livechat (FIPS) @@ -704,8 +702,6 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} test-ui-fips: name: 🔨 Test UI (FIPS) @@ -728,8 +724,6 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} - TEMP_DOCKERHUB_FIPS_USER: ${{ secrets.TEMP_DOCKERHUB_FIPS_USER }} - TEMP_DOCKERHUB_FIPS_PASS: ${{ secrets.TEMP_DOCKERHUB_FIPS_PASS }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index a87be1da134c0..402b4de7893de 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -114,7 +114,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index d0df0ddd399b5..5a2621e53b8da 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -120,7 +120,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 6fcdee4395815..843b32430a03f 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -117,7 +117,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 516fc3494990a..1b85bda9e8c2b 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index 8b6e26e8f32a8..e4230bab8b620 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -115,7 +115,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 516fc3494990a..1b85bda9e8c2b 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchatfips140/dhi-node:22.22.2-alpine3.22-fips AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From 4100536db9113aed65174415b51cc1a823426e55 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 12 May 2026 16:21:48 -0300 Subject: [PATCH 57/66] feat: use fips base image for rocketchat service --- apps/meteor/.docker/Dockerfile.alpine | 23 ++++++++++++++++++++++- docker-compose-ci.fips.yml | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 3f2c3a277b246..5e4638e7ae202 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -66,6 +66,27 @@ EXPOSE 3000 CMD ["node", "main.js"] -FROM release-standard AS release-fips +FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips + +LABEL maintainer="buildmaster@rocket.chat" + +# needs a mongo instance - defaults to container linking with alias 'mongo' +ENV DEPLOY_METHOD=docker \ + NODE_ENV=production \ + MONGO_URL=mongodb://mongo:27017/rocketchat \ + HOME=/tmp \ + PORT=3000 \ + ROOT_URL=http://localhost:3000 \ + Accounts_AvatarStorePath=/app/uploads + +USER node + +COPY --from=builder --chown=node:node /app /app + +VOLUME /app/uploads + +WORKDIR /app/bundle + +EXPOSE 3000 CMD ["node", "main.js"] diff --git a/docker-compose-ci.fips.yml b/docker-compose-ci.fips.yml index bd3f55c510edb..22781fac4ed3b 100644 --- a/docker-compose-ci.fips.yml +++ b/docker-compose-ci.fips.yml @@ -3,6 +3,8 @@ services: build: target: release-fips image: ghcr.io/${LOWERCASE_REPOSITORY}/rocket.chat:${DOCKER_TAG}-fips + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:3000/livez', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); }).on('error', () => process.exit(1));"] account-service: build: From d9eb6e573caf7dab2828e4308badbcf42eb1ce48 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Wed, 13 May 2026 13:59:50 -0300 Subject: [PATCH 58/66] feat: force fips in monolith --- apps/meteor/.docker/Dockerfile.alpine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 5e4638e7ae202..5d368081dc534 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -89,4 +89,4 @@ WORKDIR /app/bundle EXPOSE 3000 -CMD ["node", "main.js"] +CMD ["node", "--force-fips", "main.js"] From e2112a5e944718d5e91f459a3f8709b805202f9a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 14 May 2026 12:23:25 -0300 Subject: [PATCH 59/66] feat: show fips mode in logs --- apps/meteor/server/startup/serverRunning.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/meteor/server/startup/serverRunning.ts b/apps/meteor/server/startup/serverRunning.ts index 9408895221357..799852e119bae 100644 --- a/apps/meteor/server/startup/serverRunning.ts +++ b/apps/meteor/server/startup/serverRunning.ts @@ -1,3 +1,4 @@ +import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; @@ -49,6 +50,8 @@ Meteor.startup(async () => { ` Platform: ${process.platform}`, ` Process Port: ${process.env.PORT}`, ` Site URL: ${settings.get('Site_Url')}`, + ` OpenSSL Version: ${process.versions.openssl}`, + ` FIPS Provider: ${crypto.getFips() ? 'Enabled' : 'Disabled'}`, ]; if (Info.commit?.hash) { From 6a881bcb1400617cb66f5fed109a8738d5a8bd6b Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 14 May 2026 13:44:19 -0300 Subject: [PATCH 60/66] feat: show fips mode in statistics --- apps/meteor/app/statistics/server/lib/statistics.ts | 3 +++ packages/core-typings/src/IStats.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 8c810acaad70b..91bd03d468606 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -1,4 +1,5 @@ import { log } from 'console'; +import crypto from 'node:crypto'; import os from 'node:os'; import { Analytics, Team, VideoConf, Presence } from '@rocket.chat/core-services'; @@ -345,6 +346,8 @@ export const statistics = { platform: process.env.DEPLOY_PLATFORM || 'selfinstall', }; + statistics.fips = !!crypto.getFips(); + statistics.readReceiptsEnabled = settings.get('Message_Read_Receipt_Enabled'); statistics.readReceiptsDetailed = settings.get('Message_Read_Receipt_Store_Users'); diff --git a/packages/core-typings/src/IStats.ts b/packages/core-typings/src/IStats.ts index 37280c86b5cf0..939b3558c8d72 100644 --- a/packages/core-typings/src/IStats.ts +++ b/packages/core-typings/src/IStats.ts @@ -273,4 +273,5 @@ export interface IStats extends IRocketChatRecord { abacTotalAttributeValues?: number; abacRoomsEnrolled?: number; allowUnsafeQueryAndFieldsApiParamsEnabled?: boolean; + fips?: boolean; } From 3bd03e1a8b277a87188685bf185e83d7078acc1f Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Tue, 19 May 2026 08:41:18 -0300 Subject: [PATCH 61/66] chore(deps): update fips base images for node 22.22.3 --- apps/meteor/.docker/Dockerfile.alpine | 2 +- ee/apps/account-service/Dockerfile | 2 +- ee/apps/authorization-service/Dockerfile | 2 +- ee/apps/ddp-streamer/Dockerfile | 2 +- ee/apps/omnichannel-transcript/Dockerfile | 2 +- ee/apps/presence-service/Dockerfile | 2 +- ee/apps/queue-worker/Dockerfile | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index 5d368081dc534..cb002e9b3756d 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -66,7 +66,7 @@ EXPOSE 3000 CMD ["node", "main.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips LABEL maintainer="buildmaster@rocket.chat" diff --git a/ee/apps/account-service/Dockerfile b/ee/apps/account-service/Dockerfile index 402b4de7893de..50c0f423ea984 100644 --- a/ee/apps/account-service/Dockerfile +++ b/ee/apps/account-service/Dockerfile @@ -114,7 +114,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/authorization-service/Dockerfile b/ee/apps/authorization-service/Dockerfile index 5a2621e53b8da..b706524fd7d64 100644 --- a/ee/apps/authorization-service/Dockerfile +++ b/ee/apps/authorization-service/Dockerfile @@ -120,7 +120,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/ddp-streamer/Dockerfile b/ee/apps/ddp-streamer/Dockerfile index 843b32430a03f..4aa4e5f9b1ac8 100644 --- a/ee/apps/ddp-streamer/Dockerfile +++ b/ee/apps/ddp-streamer/Dockerfile @@ -117,7 +117,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/omnichannel-transcript/Dockerfile b/ee/apps/omnichannel-transcript/Dockerfile index 1b85bda9e8c2b..8734cc6d1b49a 100644 --- a/ee/apps/omnichannel-transcript/Dockerfile +++ b/ee/apps/omnichannel-transcript/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/presence-service/Dockerfile b/ee/apps/presence-service/Dockerfile index e4230bab8b620..a0c1caf519058 100644 --- a/ee/apps/presence-service/Dockerfile +++ b/ee/apps/presence-service/Dockerfile @@ -115,7 +115,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 diff --git a/ee/apps/queue-worker/Dockerfile b/ee/apps/queue-worker/Dockerfile index 1b85bda9e8c2b..8734cc6d1b49a 100644 --- a/ee/apps/queue-worker/Dockerfile +++ b/ee/apps/queue-worker/Dockerfile @@ -127,7 +127,7 @@ EXPOSE 3000 9458 CMD ["node", "src/service.js"] -FROM rocketchat/dhi-node:22.22.2-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips ARG SERVICE ENV NODE_ENV=production \ PORT=3000 From abede3be0d8e5ee45e975ad19c75035072e82e93 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 21 May 2026 14:33:43 -0300 Subject: [PATCH 62/66] fix!(fips): use sha256 instead of md5 for 2fa fingerprint --- apps/meteor/app/2fa/server/code/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/2fa/server/code/index.ts b/apps/meteor/app/2fa/server/code/index.ts index 8242b1c66185e..a9934e6fe7e27 100644 --- a/apps/meteor/app/2fa/server/code/index.ts +++ b/apps/meteor/app/2fa/server/code/index.ts @@ -60,7 +60,7 @@ function getFingerprintFromConnection(connection: IMethodConnection): string { clientAddress: connection.clientAddress, }); - return crypto.createHash('md5').update(data).digest('hex'); + return crypto.createHash('sha256').update(data).digest('hex'); } function getRememberDate(from: Date = new Date()): Date | undefined { From baa3a316f4c03dab498bf853d7614aa73b8b221a Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Thu, 21 May 2026 14:35:09 -0300 Subject: [PATCH 63/66] fix(fips): use sha256 for meteor method rate limiting --- apps/meteor/app/api/server/v1/misc.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/api/server/v1/misc.ts b/apps/meteor/app/api/server/v1/misc.ts index d7b0be701a3f5..8a7dbabc5c7e6 100644 --- a/apps/meteor/app/api/server/v1/misc.ts +++ b/apps/meteor/app/api/server/v1/misc.ts @@ -632,7 +632,7 @@ API.v1.post( const connectionId = this.token || crypto - .createHash('md5') + .createHash('sha256') .update((this.requestIp ?? '') + this.user._id) .digest('hex'); @@ -693,7 +693,7 @@ API.v1.post( const connectionId = this.token || crypto - .createHash('md5') + .createHash('sha256') .update(this.requestIp ?? '') .digest('hex'); From b96e696dbff5ef4350a093849adcb1396df1ab99 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 22 May 2026 11:10:45 -0300 Subject: [PATCH 64/66] fix(fips): install deno in monolith image --- apps/meteor/.docker/Dockerfile.alpine | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/meteor/.docker/Dockerfile.alpine b/apps/meteor/.docker/Dockerfile.alpine index cb002e9b3756d..333f2f026fbf8 100644 --- a/apps/meteor/.docker/Dockerfile.alpine +++ b/apps/meteor/.docker/Dockerfile.alpine @@ -66,7 +66,9 @@ EXPOSE 3000 CMD ["node", "main.js"] -FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips AS release-fips +FROM rocketchat/dhi-node:22.22.3-alpine3.23-fips-dev AS release-fips + +RUN apk add --no-cache deno LABEL maintainer="buildmaster@rocket.chat" From 2bf121ecb82a0019caceb532398eebf72c89c44c Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 22 May 2026 12:59:05 -0300 Subject: [PATCH 65/66] fix(federation-matrix): change from md5 to sha256 for ETag for FIPS compliance --- ee/packages/federation-matrix/src/api/.well-known/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/packages/federation-matrix/src/api/.well-known/server.ts b/ee/packages/federation-matrix/src/api/.well-known/server.ts index 9bb991ab0d7e9..262afa5d3249e 100644 --- a/ee/packages/federation-matrix/src/api/.well-known/server.ts +++ b/ee/packages/federation-matrix/src/api/.well-known/server.ts @@ -32,7 +32,7 @@ export const getWellKnownRoutes = () => { async (c) => { const responseData = federationSDK.getWellKnownHostData(); - const etag = createHash('md5').update(JSON.stringify(responseData)).digest('hex'); + const etag = createHash('sha256').update(JSON.stringify(responseData)).digest('hex'); c.header('ETag', etag); c.header('Content-Type', 'application/json'); From 3869643aaebbf9b047c54a7a475f4f6e709529a9 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Date: Fri, 22 May 2026 13:31:46 -0300 Subject: [PATCH 66/66] chore(fips): reduce ci diff --- .github/actions/build-docker/action.yml | 108 +++++++--------- .github/workflows/ci-test-e2e.yml | 145 +++------------------- .github/workflows/ci.yml | 156 ++++++++++++++++-------- 3 files changed, 162 insertions(+), 247 deletions(-) diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index ca8a14b5f3e5a..264fec9d129c4 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -10,10 +10,10 @@ inputs: description: 'GitHub Container Registry Personal Access Token' DOCKER_USER: required: false - description: 'DockerHub username for private base image pulls' + description: 'DockerHub username (required for FIPS builds to pull private base image)' DOCKER_PASS: required: false - description: 'DockerHub password/token for private base image pulls' + description: 'DockerHub password (required for FIPS builds to pull private base image)' deno-version: required: true description: 'Deno version' @@ -49,8 +49,9 @@ runs: username: ${{ inputs.CR_USER }} password: ${{ inputs.CR_PAT }} - - name: Login to DockerHub for FIPS base images - if: inputs.type == 'fips' && inputs.DOCKER_USER != '' && inputs.DOCKER_PASS != '' + - name: Login to DockerHub + # FIPS base image (rocketchat/dhi-node) lives in a private DockerHub repo and requires auth to pull. + if: inputs.type == 'fips' && github.actor != 'dependabot[bot]' && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: username: ${{ inputs.DOCKER_USER }} @@ -88,23 +89,21 @@ runs: - name: Build Docker images shell: bash env: - INPUT_DENO_VERSION: ${{ inputs.deno-version }} + DENO_VERSION: ${{ inputs.deno-version }} INPUT_ARCH: ${{ inputs.arch }} INPUT_SERVICE: ${{ inputs.service }} INPUT_PUBLISH_IMAGE: ${{ inputs.publish-image }} INPUT_TYPE: ${{ inputs.type }} - GH_RUN_ID: ${{ github.run_id }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - GH_EVENT_NAME: ${{ github.event_name }} - GH_REF: ${{ github.ref }} - SERVICE_SUFFIX_FROM_CONTEXT: ${{ (inputs.service == 'rocketchat' && inputs.type == 'coverage' && (github.event_name == 'release' || github.ref == 'refs/heads/develop')) && '-cov' || (inputs.type == 'fips' && '-fips' || '') }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_REF: ${{ github.ref }} run: | set -o xtrace - export DENO_VERSION="$INPUT_DENO_VERSION" - compose_files=(-f docker-compose-ci.yml) + compose_fips_override='' if [[ "$INPUT_TYPE" == 'fips' ]]; then - compose_files+=(-f docker-compose-ci.fips.yml) + compose_fips_override='-f docker-compose-ci.fips.yml' fi # Removes unnecessary swc cores and sharp binaries to reduce image size @@ -129,51 +128,34 @@ runs: LOAD_OR_PUSH="--load" fi - export DOCKER_CLIENT_TIMEOUT=300 - export COMPOSE_HTTP_TIMEOUT=300 - - # Get image name from compose config since rocketchat image is different from service name (rocket.chat) - IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') - - BUILD_RUN_URL="${GH_SERVER_URL}/${GH_REPOSITORY}/actions/runs/${GH_RUN_ID}" - BUILD_SOURCE_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - buildx_bake_cmd=( - docker buildx bake - "${compose_files[@]}" - ${LOAD_OR_PUSH} - --allow=fs.read=/tmp/build - --set "*.tags+=${IMAGE}-gha-run-${GH_RUN_ID}" - --set "*.labels.org.opencontainers.image.description=Build run: ${BUILD_RUN_URL}" - --set "*.labels.org.opencontainers.image.source=${BUILD_SOURCE_URL}" - --set "*.platform=linux/${INPUT_ARCH}" - --set *.cache-from=type=gha - --set *.cache-to=type=gha,mode=max - --provenance=false - --sbom=false - --metadata-file "/tmp/meta.json" + # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) + IMAGE=$(docker compose -f docker-compose-ci.yml $compose_fips_override config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') + + docker buildx bake \ + -f docker-compose-ci.yml $compose_fips_override \ + ${LOAD_OR_PUSH} \ + --allow=fs.read=/tmp/build \ + --set "*.tags+=${IMAGE}-gha-run-${GITHUB_RUN_ID}" \ + --set "*.labels.org.opencontainers.image.description=Build run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ + --set "*.labels.org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \ + --set "*.platform=linux/${INPUT_ARCH}" \ + --set *.cache-from=type=gha \ + --set *.cache-to=type=gha,mode=max \ + --provenance=false \ + --sbom=false \ + --metadata-file "/tmp/meta.json" \ "$INPUT_SERVICE" - ) - - attempts=1 - max_attempts=3 - until "${buildx_bake_cmd[@]}"; do - if [[ "$INPUT_PUBLISH_IMAGE" != 'true' || $attempts -ge $max_attempts ]]; then - echo "docker buildx bake failed after ${attempts} attempt(s)." - exit 1 - fi - - attempts=$((attempts + 1)) - sleep_seconds=$((15 * attempts)) - echo "docker buildx bake failed (likely transient push error). Retrying in ${sleep_seconds}s... (attempt ${attempts}/${max_attempts})" - sleep "${sleep_seconds}" - done echo "Contents of /tmp/meta.json:" cat /tmp/meta.json if [[ "$INPUT_PUBLISH_IMAGE" == 'true' ]]; then - SERVICE_SUFFIX="$SERVICE_SUFFIX_FROM_CONTEXT" + SERVICE_SUFFIX='' + if [[ "$INPUT_SERVICE" == 'rocketchat' && "$INPUT_TYPE" == 'coverage' ]] && [[ "$GITHUB_EVENT_NAME" == 'release' || "$GITHUB_REF" == 'refs/heads/develop' ]]; then + SERVICE_SUFFIX='-cov' + elif [[ "$INPUT_TYPE" == 'fips' ]]; then + SERVICE_SUFFIX='-fips' + fi mkdir -p "/tmp/manifests/${INPUT_SERVICE}${SERVICE_SUFFIX}/${INPUT_ARCH}" @@ -195,26 +177,26 @@ runs: if: inputs.publish-image == 'false' && inputs.arch == 'amd64' shell: bash env: - INPUT_TYPE: ${{ inputs.type }} - INPUT_SERVICE: ${{ inputs.service }} - INPUT_ARCH: ${{ inputs.arch }} + SERVICE: ${{ inputs.service }} + ARCH: ${{ inputs.arch }} + TYPE: ${{ inputs.type }} run: | set -o xtrace - compose_files=(-f docker-compose-ci.yml) - if [[ "$INPUT_TYPE" == 'fips' ]]; then - compose_files+=(-f docker-compose-ci.fips.yml) + compose_fips_override='' + if [[ "$TYPE" == 'fips' ]]; then + compose_fips_override='-f docker-compose-ci.fips.yml' fi - # Get image name from compose config - IMAGE=$(docker compose "${compose_files[@]}" config --format json 2>/dev/null | jq -r --arg s "$INPUT_SERVICE" '.services[$s].image') + # Get image name from docker-compose-ci.yml + IMAGE=$(docker compose -f docker-compose-ci.yml $compose_fips_override config --format json 2>/dev/null | jq -r --arg s "$SERVICE" '.services[$s].image') # Create directory for image archives mkdir -p /tmp/docker-images # Save the image to a tar file - docker save "${IMAGE}" -o "/tmp/docker-images/${INPUT_SERVICE}-${INPUT_ARCH}-${INPUT_TYPE}.tar" + docker save "${IMAGE}" -o "/tmp/docker-images/${SERVICE}-${ARCH}-${TYPE}.tar" - echo "Saved image to /tmp/docker-images/${INPUT_SERVICE}-${INPUT_ARCH}-${INPUT_TYPE}.tar" + echo "Saved image to /tmp/docker-images/${SERVICE}-${ARCH}-${TYPE}.tar" ls -lh /tmp/docker-images/ - name: Upload Docker image artifact diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index a7b597bdd406f..d8e7a95c81e8c 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -89,18 +89,6 @@ jobs: name: MongoDB ${{ matrix.mongodb-version }}${{ inputs.coverage == matrix.mongodb-version && ' coverage' || '' }} (${{ matrix.shard }}/${{ inputs.total-shard }}) steps: - - name: Set compose files - env: - INPUT_RELEASE: ${{ inputs.release }} - run: | - if [[ "$INPUT_RELEASE" == 'fips' ]]; then - echo 'COMPOSE_FILES=-f docker-compose-ci.yml -f docker-compose-ci.fips.yml' >> "$GITHUB_ENV" - echo 'COMPOSE_FILES_METEOR=-f ../../docker-compose-ci.yml -f ../../docker-compose-ci.fips.yml' >> "$GITHUB_ENV" - else - echo 'COMPOSE_FILES=-f docker-compose-ci.yml' >> "$GITHUB_ENV" - echo 'COMPOSE_FILES_METEOR=-f ../../docker-compose-ci.yml' >> "$GITHUB_ENV" - fi - - name: Collect Workflow Telemetry if: inputs.type == 'perf' uses: catchpoint/workflow-telemetry-action@94c3c3d9567a0205de6da68a76c428ce4e769af1 # v2.0.0 @@ -146,7 +134,7 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 if: github.event.pull_request.head.repo.full_name != github.repository && github.event_name != 'release' && github.ref != 'refs/heads/develop' with: - pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || (inputs.release == 'fips' && 'docker-image-*-amd64-*' || 'docker-image-*-amd64-coverage') }} + pattern: ${{ inputs.release == 'ce' && 'docker-image-rocketchat-amd64-coverage' || (inputs.release == 'fips' && 'docker-image-*-amd64-fips' || 'docker-image-*-amd64-coverage') }} path: /tmp/docker-images merge-multiple: true @@ -176,8 +164,7 @@ jobs: - name: Start httpbin container and wait for it to be ready if: inputs.type == 'api' || inputs.type == 'api-livechat' run: | - read -r -a compose_files <<< "$COMPOSE_FILES" - docker compose "${compose_files[@]}" up -d httpbin + docker compose -f docker-compose-ci.yml up -d httpbin - name: Prepare code coverage directory run: | @@ -186,12 +173,6 @@ jobs: mkdir -p "$COVERAGE_DIR" chmod 777 "$COVERAGE_DIR" - - name: Pull EE/FIPS images - if: (inputs.release == 'ee' || inputs.release == 'fips') && (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') - run: | - read -r -a compose_files <<< "$COMPOSE_FILES" - docker compose "${compose_files[@]}" pull - - name: Start containers for CE if: inputs.release == 'ce' env: @@ -203,8 +184,7 @@ jobs: TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} run: | # when we are testing CE, we only need to start the rocketchat container - read -r -a compose_files <<< "$COMPOSE_FILES" - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d rocketchat --wait + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml up -d rocketchat --wait - name: Start containers for EE if: inputs.release == 'ee' || inputs.release == 'fips' @@ -213,9 +193,10 @@ jobs: TRANSPORTER: ${{ inputs.transporter }} COMPOSE_PROFILES: ${{ inputs.type == 'api' && 'api' || '' }} TEST_MODE: ${{ (inputs.type == 'api' || inputs.type == 'api-livechat') && 'api' || 'true' }} + FIPS_OVERRIDE: ${{ inputs.release == 'fips' && '-f docker-compose-ci.fips.yml' || '' }} run: | - read -r -a compose_files <<< "$COMPOSE_FILES" - DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose "${compose_files[@]}" up -d --wait --no-build + read -r -a fips_override <<< "$FIPS_OVERRIDE" + DEBUG_LOG_LEVEL=${DEBUG_LOG_LEVEL:-0} docker compose -f docker-compose-ci.yml "${fips_override[@]}" up -d --wait - uses: ./.github/actions/setup-playwright if: inputs.type == 'ui' @@ -223,103 +204,13 @@ jobs: - name: Wait services to start up if: inputs.release == 'ee' || inputs.release == 'fips' run: | - read -r -a compose_files <<< "$COMPOSE_FILES" docker ps - wait_for_mongo_primary() { - local retries=36 - local delay=5 - - for attempt in $(seq 1 "$retries"); do - local is_primary - is_primary=$(docker compose "${compose_files[@]}" exec -T mongo mongosh --quiet --eval "try { const hello = db.hello(); print((hello.isWritablePrimary || hello.ismaster) ? '1' : '0'); } catch (e) { print('0'); }" 2>/dev/null | tail -n1 || true) - - if [[ "$is_primary" == '1' ]]; then - echo "Mongo replica set primary is ready" - return 0 - fi - - echo "Waiting for Mongo primary (attempt ${attempt}/${retries})" - sleep "$delay" - done - - echo "Mongo primary was not ready in time" - docker compose "${compose_files[@]}" logs mongo - return 1 - } - - wait_for_service() { - local service="$1" - local retries=18 - local delay=10 - local broker_service_name="${service%-service}" - local ready_pattern="NetworkBroker started successfully|ServiceBroker with [0-9]+ service\(s\) started successfully|Service '${broker_service_name}' started\." - - for attempt in $(seq 1 "$retries"); do - local container_id - container_id=$(docker compose "${compose_files[@]}" ps -q "$service") - - if [[ -z "$container_id" ]]; then - echo "Service '$service' has no container ID" - docker compose "${compose_files[@]}" ps - return 1 - fi - - local container_state - container_state=$(docker inspect -f '{{.State.Status}}' "$container_id" 2>/dev/null || echo "unknown") - local health_state - health_state=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container_id" 2>/dev/null || echo "unknown") - - if [[ "$container_state" != 'running' ]]; then - echo "Service '$service' is not running (state=$container_state)" - docker compose "${compose_files[@]}" logs "$service" - return 1 - fi - - if [[ "$health_state" == 'unhealthy' ]]; then - echo "Service '$service' is unhealthy" - docker compose "${compose_files[@]}" logs "$service" - return 1 - fi - - if [[ "$health_state" == 'healthy' ]]; then - echo "Service '$service' is healthy" - return 0 - fi - - if docker compose "${compose_files[@]}" logs "$service" | grep -Eq "$ready_pattern"; then - echo "Service '$service' is ready" - return 0 - fi - - echo "Waiting '$service' to start up (attempt ${attempt}/${retries})" - sleep "$delay" - done - - echo "Service '$service' did not become ready in time" - docker compose "${compose_files[@]}" logs "$service" - return 1 - } - - mapfile -t services_to_wait < <( - docker compose "${compose_files[@]}" ps --services --status running \ - | grep -- '-service$' \ - | sort - ) - - wait_for_mongo_primary - - if (( ${#services_to_wait[@]} == 0 )); then - echo "No running -service containers found to wait for" - docker compose "${compose_files[@]}" ps - exit 1 - fi - - echo "Waiting for services: ${services_to_wait[*]}" - - for service in "${services_to_wait[@]}"; do - wait_for_service "$service" - done + until docker compose -f docker-compose-ci.yml logs ddp-streamer-service | grep -q "NetworkBroker started successfully"; do + echo "Waiting 'ddp-streamer' to start up" + ((c++)) && ((c==10)) && docker compose -f docker-compose-ci.yml logs ddp-streamer-service && exit 1 + sleep 10 + done; - name: Remove unused Docker images run: docker system prune -af @@ -332,11 +223,10 @@ jobs: IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace - read -r -a compose_files_meteor <<< "$COMPOSE_FILES_METEOR" npm run testapi || s=$? - docker compose "${compose_files_meteor[@]}" stop + docker compose -f ../../docker-compose-ci.yml stop ls -la "$COVERAGE_DIR" exit "${s:-0}" @@ -349,11 +239,10 @@ jobs: IS_EE: ${{ (inputs.release == 'ee' || inputs.release == 'fips') && 'true' || '' }} run: | set -o xtrace - read -r -a compose_files_meteor <<< "$COMPOSE_FILES_METEOR" npm run testapi:livechat || s=$? - docker compose "${compose_files_meteor[@]}" stop + docker compose -f ../../docker-compose-ci.yml stop ls -la "$COVERAGE_DIR" exit "${s:-0}" @@ -404,15 +293,11 @@ jobs: - name: Show server logs if E2E test failed if: failure() - run: | - read -r -a compose_files <<< "$COMPOSE_FILES" - docker compose "${compose_files[@]}" logs rocketchat authorization-service queue-worker-service ddp-streamer-service account-service presence-service omnichannel-transcript-service + run: docker compose -f docker-compose-ci.yml logs rocketchat authorization-service queue-worker-service ddp-streamer-service account-service presence-service omnichannel-transcript-service - name: Show mongo logs if E2E test failed if: failure() - run: | - read -r -a compose_files <<< "$COMPOSE_FILES" - docker compose "${compose_files[@]}" logs mongo + run: docker compose -f docker-compose-ci.yml logs mongo - name: Store coverage if: inputs.coverage == matrix.mongodb-version diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3d19945fd97d..359b4491e1be1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,6 @@ concurrency: env: TOOL_NODE_FLAGS: ${{ vars.TOOL_NODE_FLAGS }} - DOCKER_BUILD_ARCHES_JSON: '["arm64","amd64"]' - DOCKER_BUILD_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' - DOCKER_BUILD_EXTRA_COVERAGE_JSON: '[{"arch":"amd64","service":"rocketchat","type":"coverage"},{"arch":"arm64","service":"rocketchat","type":"coverage"}]' - DOCKER_BUILD_FIPS_SERVICES_JSON: '["authorization-service","queue-worker-service","ddp-streamer-service","account-service","presence-service","omnichannel-transcript-service","rocketchat"]' jobs: release-versions: @@ -302,48 +298,9 @@ jobs: source-hash: ${{ needs.release-versions.outputs.packages-build-cache-key }}-${{ needs.release-versions.outputs.meteor-rc-cache-key }} type: ${{ matrix.type }} - build-gh-docker-matrix: - name: ⚙️ Build Docker Matrix - needs: [build] - runs-on: ubuntu-24.04-arm - outputs: - matrix: ${{ steps.generate.outputs.matrix }} - steps: - - id: generate - env: - DOCKER_BUILD_ARCHES_JSON: ${{ env.DOCKER_BUILD_ARCHES_JSON }} - DOCKER_BUILD_SERVICES_JSON: ${{ env.DOCKER_BUILD_SERVICES_JSON }} - DOCKER_BUILD_EXTRA_COVERAGE_JSON: ${{ env.DOCKER_BUILD_EXTRA_COVERAGE_JSON }} - DOCKER_BUILD_FIPS_SERVICES_JSON: ${{ env.DOCKER_BUILD_FIPS_SERVICES_JSON }} - run: | - node <<'NODE' - const fs = require('node:fs'); - - const baseType = '${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}'; - const arches = JSON.parse(process.env.DOCKER_BUILD_ARCHES_JSON); - const services = JSON.parse(process.env.DOCKER_BUILD_SERVICES_JSON); - const extraCoverageRows = JSON.parse(process.env.DOCKER_BUILD_EXTRA_COVERAGE_JSON); - const fipsServices = JSON.parse(process.env.DOCKER_BUILD_FIPS_SERVICES_JSON); - - const include = []; - for (const arch of arches) { - for (const service of services) { - include.push({ arch, service, type: baseType }); - } - } - - if (baseType === 'production') { - include.push(...extraCoverageRows); - } - - include.push(...fipsServices.map((service) => ({ arch: 'amd64', service, type: 'fips' }))); - - fs.appendFileSync(process.env.GITHUB_OUTPUT, `matrix=${JSON.stringify({ include })}\n`); - NODE - build-gh-docker: - name: 🚢 Build Docker (${{ matrix.service }}-${{ matrix.arch }}-${{ matrix.type }}) - needs: [build, build-gh-docker-matrix, release-versions] + name: 🚢 Build Docker + needs: [build, release-versions] runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }} env: @@ -352,7 +309,47 @@ jobs: strategy: fail-fast: false - matrix: ${{ fromJSON(needs.build-gh-docker-matrix.outputs.matrix) }} + matrix: + arch: [arm64, amd64] + service: + [ + [authorization-service, queue-worker-service, ddp-streamer-service], + [account-service, presence-service, omnichannel-transcript-service], + [rocketchat], + ] + type: + # if running in a PR build with coverage + - ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }} + include: + # if not, build with coverage for tests + - arch: amd64 + service: [rocketchat] + type: coverage + - arch: arm64 + service: [rocketchat] + type: coverage + # FIPS images: amd64 only, includes all microservices + - arch: amd64 + service: [authorization-service] + type: fips + - arch: amd64 + service: [queue-worker-service] + type: fips + - arch: amd64 + service: [ddp-streamer-service] + type: fips + - arch: amd64 + service: [account-service] + type: fips + - arch: amd64 + service: [presence-service] + type: fips + - arch: amd64 + service: [omnichannel-transcript-service] + type: fips + - arch: amd64 + service: [rocketchat] + type: fips steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -360,7 +357,7 @@ jobs: - uses: ./.github/actions/restore-packages # we only build and publish the actual docker images if not a PR from a fork - - name: Image ${{ matrix.service }} + - name: Image ${{ matrix.service[0] }} uses: ./.github/actions/build-docker if: github.actor != 'dependabot[bot]' env: @@ -373,9 +370,60 @@ jobs: DOCKER_PASS: ${{ secrets.DOCKER_PASS }} deno-version: ${{ needs.release-versions.outputs.deno-version }} arch: ${{ matrix.arch }} - service: ${{ matrix.service }} + service: ${{ matrix.service[0] }} + type: ${{ matrix.type }} + publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} + + - name: Image ${{ matrix.service[1] || '"skipped"' }} + uses: ./.github/actions/build-docker + if: matrix.service[1] && github.actor != 'dependabot[bot]' + env: + DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + DOCKER_USER: ${{ secrets.DOCKER_USER }} + DOCKER_PASS: ${{ secrets.DOCKER_PASS }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + arch: ${{ matrix.arch }} + service: ${{ matrix.service[1] }} + type: ${{ matrix.type }} + publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} + setup-docker: false + + - name: Image ${{ matrix.service[2] || '"skipped"' }} + uses: ./.github/actions/build-docker + if: matrix.service[2] && github.actor != 'dependabot[bot]' + env: + DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + DOCKER_USER: ${{ secrets.DOCKER_USER }} + DOCKER_PASS: ${{ secrets.DOCKER_PASS }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + arch: ${{ matrix.arch }} + service: ${{ matrix.service[2] }} + type: ${{ matrix.type }} + publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} + setup-docker: false + + - name: Image ${{ matrix.service[3] || '"skipped"' }} + uses: ./.github/actions/build-docker + if: matrix.service[3] && github.actor != 'dependabot[bot]' + env: + DOCKER_TAG_SUFFIX_ROCKETCHAT: ${{ matrix.type == 'coverage' && '-cov' || '' }} + with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + DOCKER_USER: ${{ secrets.DOCKER_USER }} + DOCKER_PASS: ${{ secrets.DOCKER_PASS }} + deno-version: ${{ needs.release-versions.outputs.deno-version }} + arch: ${{ matrix.arch }} + service: ${{ matrix.service[3] }} type: ${{ matrix.type }} publish-image: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop' }} + setup-docker: false build-gh-docker-publish: name: 🚢 Publish Docker Images (ghcr.io) @@ -683,6 +731,7 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} test-api-livechat-fips: name: 🔨 Test API Livechat (FIPS) @@ -702,6 +751,7 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} test-ui-fips: name: 🔨 Test UI (FIPS) @@ -724,6 +774,7 @@ jobs: secrets: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} @@ -1071,8 +1122,7 @@ jobs: [[ -d "$service_dir" ]] || continue service="$(basename "$service_dir")" - if [[ "$service" == 'rocketchat-cov' ]]; then - echo "Skipping $service for DockerHub publish" + if [ "$service" == "rocketchat-cov" ]; then continue fi @@ -1084,10 +1134,8 @@ jobs: IMAGE_NAME="${{ needs.release-versions.outputs.lowercase-repo }}/${service}" fi - # Get image name from compose config since rocketchat image is different from service name (rocket.chat) - if [ "$service" == "rocketchat-cov" ]; then - SRC=$(docker compose -f docker-compose-ci.yml config --format json 2>/dev/null | jq -r --arg s "rocketchat" '.services[$s].image')-cov - elif [[ "$service" == *"-fips" ]]; then + # Get image name from docker-compose-ci.yml since rocketchat image is different from service name (rocket.chat) + if [[ "$service" == *"-fips" ]]; then base_service="${service%-fips}" SRC=$(docker compose -f docker-compose-ci.yml -f docker-compose-ci.fips.yml config --format json 2>/dev/null | jq -r --arg s "$base_service" '.services[$s].image') else