mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
228 lines
7.6 KiB
JavaScript
228 lines
7.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const SELECTION_DIRECTORY =
|
|
'deploy/kubernetes/ql3-cluster/operators/cert-manager';
|
|
const BARMAN_LOCK =
|
|
'deploy/kubernetes/ql3-cluster/operators/barman-cloud/plugin-lock.json';
|
|
const RELEASE_BLOCKERS = Object.freeze([
|
|
'live-cert-manager-api-and-plugin-mtls-rotation-evidence',
|
|
]);
|
|
const RELEASE_MANIFEST_SHA256 =
|
|
'7ee74ba06845213e96d8ceaff3d20dd51e682765c1418eddda4e8780ba082261';
|
|
const IMAGES = Object.freeze([
|
|
Object.freeze({
|
|
name: 'controller',
|
|
manifestReference: 'quay.io/jetstack/cert-manager-controller:v1.20.3',
|
|
image:
|
|
'quay.io/jetstack/cert-manager-controller:v1.20.3@sha256:6c13d61e0348a5bc3477f8ea9a928624300b30d19b1c72a7d2b90372fc713db4',
|
|
platforms: Object.freeze({
|
|
'linux/amd64':
|
|
'sha256:1e4af57beb469cc3bb0fb48b9201caea2723819b9ffd3c3ea98568f55b4dd38b',
|
|
'linux/arm64':
|
|
'sha256:af62a025ae4f8fd03209b5e0760868296bad5a9370aab0c91ad3b5476bcb282d',
|
|
}),
|
|
}),
|
|
Object.freeze({
|
|
name: 'cainjector',
|
|
manifestReference: 'quay.io/jetstack/cert-manager-cainjector:v1.20.3',
|
|
image:
|
|
'quay.io/jetstack/cert-manager-cainjector:v1.20.3@sha256:06ad347fe0dc2eb84cc355c26f6752e05e87dceb6447f5cd29b963dd66dfd8bd',
|
|
platforms: Object.freeze({
|
|
'linux/amd64':
|
|
'sha256:a2b12d27950d1603d2c8168c3ccd95d07b93ce6ec4b530316196a31db592a9c0',
|
|
'linux/arm64':
|
|
'sha256:3c052c134ad1b93122b957f4d214aaa9d85a37b5ff15acc5b4d86f50e3ed822e',
|
|
}),
|
|
}),
|
|
Object.freeze({
|
|
name: 'webhook',
|
|
manifestReference: 'quay.io/jetstack/cert-manager-webhook:v1.20.3',
|
|
image:
|
|
'quay.io/jetstack/cert-manager-webhook:v1.20.3@sha256:a61e817632cebed3bb59a189327e786fa3fdd7597167d994a1848d98fd55848f',
|
|
platforms: Object.freeze({
|
|
'linux/amd64':
|
|
'sha256:953a97df613f7da7eda8ce4b1c8d8e6b50963db0800fab595d040db6eb5cb060',
|
|
'linux/arm64':
|
|
'sha256:7c510875e038f79f7fba707b5f86d8736777a4dfefcd42179b08844ee75e685b',
|
|
}),
|
|
}),
|
|
]);
|
|
|
|
function finding(code, detail) {
|
|
return Object.freeze({ code, detail });
|
|
}
|
|
|
|
function exactJson(actual, expected) {
|
|
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
}
|
|
|
|
function auditCertManagerSelection(options = {}) {
|
|
const root = path.resolve(options.root ?? path.join(__dirname, '..'));
|
|
const readFile = options.readFile ?? fs.readFileSync;
|
|
const readDirectory = options.readDirectory ?? fs.readdirSync;
|
|
const findings = [];
|
|
|
|
try {
|
|
const directory = path.join(root, SELECTION_DIRECTORY);
|
|
const entries = [...readDirectory(directory)].sort();
|
|
if (!exactJson(entries, ['selection-lock.json'])) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_INSTALLER_UNVERIFIED',
|
|
'the selection directory must remain a lock-only input; installers are fetched and digest verified at deployment time',
|
|
),
|
|
);
|
|
}
|
|
|
|
const selection = JSON.parse(
|
|
readFile(path.join(directory, 'selection-lock.json'), 'utf8'),
|
|
);
|
|
if (
|
|
selection?.schemaVersion !== 1 ||
|
|
selection?.observedAt !== '2026-08-03' ||
|
|
selection?.certManager?.version !== '1.20.3' ||
|
|
selection?.certManager?.releaseManifest !==
|
|
'https://github.com/cert-manager/cert-manager/releases/download/v1.20.3/cert-manager.yaml' ||
|
|
selection?.certManager?.status !== 'supply-chain-verified'
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_SELECTION',
|
|
'cert-manager must remain fixed to the reviewed v1.20.3 selection until a new Kubernetes compatibility review',
|
|
),
|
|
);
|
|
}
|
|
if (
|
|
selection?.compatibility?.reviewedKubernetes !== '1.32.8' ||
|
|
selection?.compatibility?.supportedKubernetesMin !== '1.32' ||
|
|
selection?.compatibility?.supportedKubernetesMax !== '1.35' ||
|
|
selection?.compatibility?.newerMinorRejected !==
|
|
'1.21.0 requires Kubernetes 1.33 or newer'
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_KUBERNETES_COMPATIBILITY',
|
|
'the certificate controller selection must stay compatible with the locked Kubernetes 1.32.8 live gate',
|
|
),
|
|
);
|
|
}
|
|
if (
|
|
selection?.certManager?.releaseManifestSha256 !==
|
|
RELEASE_MANIFEST_SHA256 ||
|
|
!exactJson(selection?.certManager?.images, IMAGES) ||
|
|
selection?.releaseReady !== false ||
|
|
!exactJson(selection?.releaseBlockers, RELEASE_BLOCKERS)
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_PREMATURE_RELEASE',
|
|
'the exact release SHA and OCI platform digests must stay locked while live API and rotation evidence remains an explicit release blocker',
|
|
),
|
|
);
|
|
}
|
|
if (
|
|
selection?.scope?.profile !== 'cluster-only' ||
|
|
selection?.scope?.installNamespace !== 'cert-manager' ||
|
|
selection?.scope?.consumerNamespace !== 'cnpg-system' ||
|
|
selection?.scope?.requiredBy !== 'barman-cloud.cloudnative-pg.io@0.13.0'
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_SCOPE',
|
|
'cert-manager must remain cluster-only and separate from the Barman plugin namespace',
|
|
),
|
|
);
|
|
}
|
|
|
|
const pluginTls = selection?.pluginTls;
|
|
const certificates = pluginTls?.certificates;
|
|
if (
|
|
!exactJson(pluginTls?.issuer, {
|
|
apiVersion: 'cert-manager.io/v1',
|
|
kind: 'Issuer',
|
|
name: 'selfsigned-issuer',
|
|
namespace: 'cnpg-system',
|
|
type: 'SelfSigned',
|
|
}) ||
|
|
!exactJson(certificates, [
|
|
{
|
|
name: 'barman-cloud-client',
|
|
secretName: 'barman-cloud-client-tls',
|
|
commonName: 'barman-cloud-client',
|
|
usages: ['client auth'],
|
|
duration: '2160h',
|
|
renewBefore: '360h',
|
|
},
|
|
{
|
|
name: 'barman-cloud-server',
|
|
secretName: 'barman-cloud-server-tls',
|
|
commonName: 'barman-cloud',
|
|
dnsNames: ['barman-cloud'],
|
|
usages: ['server auth'],
|
|
duration: '2160h',
|
|
renewBefore: '360h',
|
|
},
|
|
])
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_PLUGIN_TLS',
|
|
'the Barman client/server certificate identities, usages and rotation window must match the reviewed release contract',
|
|
),
|
|
);
|
|
}
|
|
|
|
const barman = JSON.parse(readFile(path.join(root, BARMAN_LOCK), 'utf8'));
|
|
if (
|
|
!exactJson(barman?.certificateAuthority, {
|
|
mode: 'cert-manager',
|
|
version: '1.20.3',
|
|
selectionLock:
|
|
'deploy/kubernetes/ql3-cluster/operators/cert-manager/selection-lock.json',
|
|
releaseManifestUses: 'cert-manager.io/v1',
|
|
requiredSecrets: ['barman-cloud-client-tls', 'barman-cloud-server-tls'],
|
|
status: 'supply-chain-verified',
|
|
})
|
|
) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_BARMAN_BINDING',
|
|
'the Barman candidate must bind the exact supply-chain-verified certificate authority without claiming live readiness',
|
|
),
|
|
);
|
|
}
|
|
} catch (error) {
|
|
findings.push(
|
|
finding(
|
|
'QL3_CERT_MANAGER_SELECTION_AUDIT_UNAVAILABLE',
|
|
error instanceof Error ? error.message : 'unknown audit failure',
|
|
),
|
|
);
|
|
}
|
|
|
|
return Object.freeze({
|
|
schemaVersion: 1,
|
|
certManagerVersion: '1.20.3',
|
|
kubernetesVersion: '1.32.8',
|
|
releaseReady: false,
|
|
releaseBlockers: RELEASE_BLOCKERS,
|
|
findings: Object.freeze(findings),
|
|
compatible: findings.length === 0,
|
|
});
|
|
}
|
|
|
|
if (require.main === module) {
|
|
const report = auditCertManagerSelection();
|
|
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
if (!report.compatible) process.exitCode = 1;
|
|
}
|
|
|
|
module.exports = {
|
|
auditCertManagerSelection,
|
|
};
|