diff --git a/lib/patches/locationConstraints.ts b/lib/patches/locationConstraints.ts index f7b984fa5..a131db616 100644 --- a/lib/patches/locationConstraints.ts +++ b/lib/patches/locationConstraints.ts @@ -2,13 +2,10 @@ import { URL } from 'url'; import { decryptSecret } from '../executables/pensieveCreds/utils'; import { Logger } from 'werelogs'; -const CI_CEPH = process.env.CI_CEPH; - export type LocationType = | 'location-mem-v1' | 'location-file-v1' | 'location-azure-v1' - | 'location-ceph-radosgw-s3-v1' | 'location-scality-ring-s3-v1' | 'location-aws-s3-v1' | 'location-wasabi-v1' @@ -49,6 +46,17 @@ export type Location = { legacyAwsBehavior: boolean; }; +function isUnsupportedCephEndpoint(endpoint: unknown): boolean { + if (typeof endpoint !== 'string' || endpoint.length === 0) { + return false; + } + + const normalized = endpoint.toLowerCase(); + return /(?:^|[^a-z0-9])(ceph|radosgw|rgw)(?:[^a-z0-9]|$)/.test( + normalized + ); +} + export function patchLocations( overlayLocations: OverlayLocations | undefined | null, creds: any, @@ -68,7 +76,7 @@ export function patchLocations( legacyAwsBehavior: Boolean(l.legacyAwsBehavior), }; let supportsVersioning = false; - let pathStyle = CI_CEPH !== undefined; + let pathStyle = false; switch (l.locationType) { case 'location-mem-v1': @@ -94,13 +102,22 @@ export function patchLocations( }; } break; - case 'location-ceph-radosgw-s3-v1': case 'location-scality-ring-s3-v1': pathStyle = true; // fallthrough case 'location-aws-s3-v1': case 'location-wasabi-v1': supportsVersioning = true; // fallthrough case 'location-do-spaces-v1': + // Ceph support is deprecated/removed from Arsenal. + // Keeping this guard to prevent implicit compatibility through + // generic S3-compatible location types. + if (isUnsupportedCephEndpoint(l.details?.endpoint)) { + log.warn('deprecated ceph endpoint rejected for location type', { + locationType: l.locationType, + endpoint: l.details?.endpoint, + }); + return acc; + } location.type = 'aws_s3'; if (l.details.secretKey && l.details.secretKey.length > 0) { let https = true; diff --git a/tests/unit/patches/locationConstraints.spec.js b/tests/unit/patches/locationConstraints.spec.js index 4a58f131d..0c4faae58 100644 --- a/tests/unit/patches/locationConstraints.spec.js +++ b/tests/unit/patches/locationConstraints.spec.js @@ -201,41 +201,6 @@ const tests = [ objectId: 'httpsawsbackendtest', }, }, - { - locationType: 'location-ceph-radosgw-s3-v1', - locations: { - objectId: 'cephbackendtest', - details: { - bucketMatch: 'cephbucketmatch', - endpoint: 'https://secure.ceph.end.point', - accessKey: 'cephs3accesskey', - secretKey, - bucketName: 'cephbucketname', - region: 'us-west-1', - }, - }, - expected: { - details: { - awsEndpoint: 'secure.ceph.end.point', - bucketMatch: 'cephbucketmatch', - bucketName: 'cephbucketname', - credentials: { - accessKey: 'cephs3accesskey', - secretKey: decryptedSecretKey, - }, - https: true, - pathStyle: true, - region: 'us-west-1', - serverSideEncryption: false, - supportsVersioning: true, - }, - legacyAwsBehavior: false, - isTransient: false, - sizeLimitGB: null, - type: 'aws_s3', - objectId: 'cephbackendtest', - }, - }, { name: 'transient enabled', locationType: 'location-file-v1', @@ -295,6 +260,7 @@ const tests = [ describe('patch location constriants', () => { const mockLog = { info: () => {}, + warn: () => {}, }; tests.forEach(spec => { @@ -350,4 +316,103 @@ describe('patch location constriants', () => { {}, ); }); + + it('rejects ceph endpoint on aws location type', () => { + assert.deepStrictEqual( + patchLocations( + { + cephAws: { + name: 'cephAws', + objectId: 'ceph-aws-test', + locationType: 'location-aws-s3-v1', + details: { + endpoint: 'https://secure.ceph.end.point', + accessKey, + secretKey, + bucketName: 'bucket', + bucketMatch: true, + region: 'us-east-1', + }, + }, + }, + { privateKey }, + mockLog, + ), + {}, + ); + }); + + it('rejects radosgw endpoint on aws location type', () => { + assert.deepStrictEqual( + patchLocations( + { + rgwAws: { + name: 'rgwAws', + objectId: 'rgw-aws-test', + locationType: 'location-aws-s3-v1', + details: { + endpoint: 'http://radosgw.example.local', + accessKey, + secretKey, + bucketName: 'bucket', + bucketMatch: true, + region: 'us-east-1', + }, + }, + }, + { privateKey }, + mockLog, + ), + {}, + ); + }); + + it('does not reject endpoint with rgw as substring of a token', () => { + assert.deepStrictEqual( + patchLocations( + { + nonRgwToken: { + name: 'nonRgwToken', + objectId: 'non-rgw-token-test', + locationType: 'location-aws-s3-v1', + details: { + endpoint: 'https://forgwave.internal', + accessKey, + secretKey, + bucketName: 'bucket', + bucketMatch: true, + region: 'us-east-1', + }, + }, + }, + { privateKey }, + mockLog, + ), + { + nonRgwToken: { + type: 'aws_s3', + name: 'nonRgwToken', + objectId: 'non-rgw-token-test', + locationType: 'location-aws-s3-v1', + sizeLimitGB: null, + isTransient: false, + legacyAwsBehavior: false, + details: { + credentials: { + accessKey, + secretKey: decryptedSecretKey, + }, + bucketName: 'bucket', + bucketMatch: true, + serverSideEncryption: false, + region: 'us-east-1', + awsEndpoint: 'forgwave.internal', + supportsVersioning: true, + pathStyle: false, + https: true, + }, + }, + }, + ); + }); });