99819b70ff
Build and Push Docker Images (Trusted) / build-and-push (., docker/caddy/Dockerfile, caddy) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/l4-port-manager/Dockerfile, l4-port-manager) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/web/Dockerfile, web) (push) Has been cancelled
Tests / test (push) Has been cancelled
187 lines
6.1 KiB
TypeScript
Executable File
187 lines
6.1 KiB
TypeScript
Executable File
/**
|
|
* Unit tests for the new cert-based mTLS model (leaf override / trusted_client_cert_ids).
|
|
*
|
|
* Tests buildClientAuthentication with the mTlsDomainLeafOverride parameter
|
|
* to ensure the new "trust user X" model works correctly alongside the legacy CA model.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import { buildClientAuthentication } from '../../src/lib/caddy-mtls';
|
|
|
|
function makeCaPem(label: string): string {
|
|
return `-----BEGIN CERTIFICATE-----\n${label}\n-----END CERTIFICATE-----`;
|
|
}
|
|
|
|
function makeCaCertMap(...entries: [number, string][]) {
|
|
return new Map(entries.map(([id, label]) => [id, { id, certificatePem: makeCaPem(label) }]));
|
|
}
|
|
|
|
describe('buildClientAuthentication with leaf override (new cert-based model)', () => {
|
|
it('uses leaf override PEMs when mTlsDomainLeafOverride is provided', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [1]]]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
const leafOverride = new Map([['app.example.com', [makeCaPem('USER_CERT_1'), makeCaPem('USER_CERT_2')]]]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
leafOverride
|
|
);
|
|
|
|
expect(result).not.toBeNull();
|
|
expect(result!.mode).toBe('require_and_verify');
|
|
expect(result!.trusted_ca_certs).toEqual(['CA_A']);
|
|
expect(result!.trusted_leaf_certs).toEqual(['USER_CERT_1', 'USER_CERT_2']);
|
|
});
|
|
|
|
it('ignores the legacy managed/unmanaged CA logic when leaf override is present', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [1]]]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
// CA 1 is managed with active certs, but leaf override should take precedence
|
|
const issuedClientCertMap = new Map([[1, [makeCaPem('OTHER_CERT')]]]);
|
|
const cAsWithAnyIssuedCerts = new Set([1]);
|
|
const leafOverride = new Map([['app.example.com', [makeCaPem('SPECIFIC_USER')]]]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
issuedClientCertMap,
|
|
cAsWithAnyIssuedCerts,
|
|
leafOverride
|
|
);
|
|
|
|
expect(result).not.toBeNull();
|
|
// Should only have the override leaf, not OTHER_CERT from the managed CA
|
|
expect(result!.trusted_leaf_certs).toEqual(['SPECIFIC_USER']);
|
|
expect(result!.trusted_leaf_certs).not.toContain('OTHER_CERT');
|
|
});
|
|
|
|
it('falls back to legacy CA logic when no leaf override exists for domain', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [1]]]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
// No leaf override for this domain
|
|
const leafOverride = new Map([['other.example.com', [makeCaPem('OTHER')]]]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
leafOverride
|
|
);
|
|
|
|
// Falls back to unmanaged CA: no leaf certs
|
|
expect(result).not.toBeNull();
|
|
expect(result!.trusted_ca_certs).toEqual(['CA_A']);
|
|
expect(result!.trusted_leaf_certs).toBeUndefined();
|
|
});
|
|
|
|
it('includes CAs from multiple domains in leaf override', () => {
|
|
const mTlsDomainMap = new Map([
|
|
['app.example.com', [1, 2]],
|
|
]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A'], [2, 'CA_B']);
|
|
const leafOverride = new Map([['app.example.com', [makeCaPem('USER_1')]]]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
leafOverride
|
|
);
|
|
|
|
expect(result).not.toBeNull();
|
|
expect(result!.trusted_ca_certs).toContain('CA_A');
|
|
expect(result!.trusted_ca_certs).toContain('CA_B');
|
|
expect(result!.trusted_leaf_certs).toEqual(['USER_1']);
|
|
});
|
|
|
|
it('deduplicates leaf PEMs from multiple domains in same group', () => {
|
|
const sharedPem = makeCaPem('SHARED_CERT');
|
|
const mTlsDomainMap = new Map([
|
|
['a.example.com', [1]],
|
|
['b.example.com', [1]],
|
|
]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
const leafOverride = new Map([
|
|
['a.example.com', [sharedPem]],
|
|
['b.example.com', [sharedPem]],
|
|
]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['a.example.com', 'b.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
leafOverride
|
|
);
|
|
|
|
expect(result).not.toBeNull();
|
|
// The Set-based dedup in the function should handle this
|
|
// Actually looking at the code, it uses a Set for PEMs
|
|
const leafCerts = result!.trusted_leaf_certs as string[];
|
|
// pemToBase64Der('SHARED_CERT') appears once since we used a Set
|
|
expect(leafCerts.length).toBe(1);
|
|
});
|
|
|
|
it('returns null when leaf override has PEMs but no CA exists in caCertMap', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [99]]]);
|
|
const caCertMap = new Map<number, { id: number; certificatePem: string }>(); // CA 99 not found
|
|
const leafOverride = new Map([['app.example.com', [makeCaPem('USER')]]]);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
leafOverride
|
|
);
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('handles empty leaf override map (same as no override)', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [1]]]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
new Map()
|
|
);
|
|
|
|
// No override → unmanaged CA logic
|
|
expect(result).not.toBeNull();
|
|
expect(result!.trusted_ca_certs).toEqual(['CA_A']);
|
|
expect(result!.trusted_leaf_certs).toBeUndefined();
|
|
});
|
|
|
|
it('handles undefined leaf override (backward compat)', () => {
|
|
const mTlsDomainMap = new Map([['app.example.com', [1]]]);
|
|
const caCertMap = makeCaCertMap([1, 'CA_A']);
|
|
|
|
const result = buildClientAuthentication(
|
|
['app.example.com'],
|
|
mTlsDomainMap,
|
|
caCertMap,
|
|
new Map(),
|
|
new Set(),
|
|
undefined
|
|
);
|
|
|
|
expect(result).not.toBeNull();
|
|
expect(result!.trusted_ca_certs).toEqual(['CA_A']);
|
|
});
|
|
});
|