mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
fix(ql3): materialize projected runtime files
This commit is contained in:
@@ -846,18 +846,66 @@ function assertKubernetes(readFile, root, findings) {
|
||||
),
|
||||
);
|
||||
}
|
||||
const runtimeCaMount = namedEntry(
|
||||
container?.volumeMounts,
|
||||
'postgres-runtime-ca',
|
||||
const runtimeFileMaterializer = namedEntry(
|
||||
pod?.initContainers,
|
||||
'materialize-runtime-files',
|
||||
);
|
||||
const runtimeCaVolume = namedEntry(pod?.volumes, 'postgres-runtime-ca');
|
||||
const runtimePrivateMount = namedEntry(
|
||||
container?.volumeMounts,
|
||||
'postgres-runtime-private',
|
||||
);
|
||||
const projectedMount = namedEntry(
|
||||
runtimeFileMaterializer?.volumeMounts,
|
||||
'postgres-runtime-projected',
|
||||
);
|
||||
const materializedMount = namedEntry(
|
||||
runtimeFileMaterializer?.volumeMounts,
|
||||
'postgres-runtime-private',
|
||||
);
|
||||
const projectedVolume = namedEntry(
|
||||
pod?.volumes,
|
||||
'postgres-runtime-projected',
|
||||
);
|
||||
const privateVolume = namedEntry(pod?.volumes, 'postgres-runtime-private');
|
||||
const materializerSource = runtimeFileMaterializer?.command?.[2];
|
||||
if (
|
||||
runtimeCaMount?.mountPath !==
|
||||
runtimePrivateMount?.mountPath !==
|
||||
'/var/run/secrets/qinglong3/postgres-runtime' ||
|
||||
runtimeCaMount?.readOnly !== true ||
|
||||
runtimeCaVolume?.secret?.secretName !== 'ql3-cluster-control-runtime' ||
|
||||
runtimeCaVolume?.secret?.defaultMode !== 0o444 ||
|
||||
JSON.stringify(runtimeCaVolume?.secret?.items) !==
|
||||
runtimePrivateMount?.readOnly !== true ||
|
||||
container?.volumeMounts?.some(
|
||||
(mount) => mount?.name === 'postgres-runtime-projected',
|
||||
) ||
|
||||
runtimeFileMaterializer?.image !== container?.image ||
|
||||
runtimeFileMaterializer?.imagePullPolicy !== container?.imagePullPolicy ||
|
||||
JSON.stringify(runtimeFileMaterializer?.command?.slice(0, 2)) !==
|
||||
JSON.stringify(['node', '-e']) ||
|
||||
typeof materializerSource !== 'string' ||
|
||||
!materializerSource.includes(
|
||||
"realpathSync('/var/run/secrets/qinglong3/postgres-runtime-projected/..data')",
|
||||
) ||
|
||||
!materializerSource.includes('COPYFILE_EXCL') ||
|
||||
!materializerSource.includes('chmodSync(output, 0o400)') ||
|
||||
materializerSource.includes('console.') ||
|
||||
runtimeFileMaterializer?.securityContext?.allowPrivilegeEscalation !==
|
||||
false ||
|
||||
runtimeFileMaterializer?.securityContext?.readOnlyRootFilesystem !== true ||
|
||||
JSON.stringify(
|
||||
runtimeFileMaterializer?.securityContext?.capabilities?.drop,
|
||||
) !== JSON.stringify(['ALL']) ||
|
||||
runtimeFileMaterializer?.resources?.requests?.cpu !== '10m' ||
|
||||
runtimeFileMaterializer?.resources?.requests?.memory !== '32Mi' ||
|
||||
runtimeFileMaterializer?.resources?.limits?.cpu !== '100m' ||
|
||||
runtimeFileMaterializer?.resources?.limits?.memory !== '64Mi' ||
|
||||
runtimeFileMaterializer?.volumeMounts?.length !== 2 ||
|
||||
projectedMount?.mountPath !==
|
||||
'/var/run/secrets/qinglong3/postgres-runtime-projected' ||
|
||||
projectedMount?.readOnly !== true ||
|
||||
materializedMount?.mountPath !==
|
||||
'/var/run/secrets/qinglong3/postgres-runtime' ||
|
||||
materializedMount?.readOnly === true ||
|
||||
projectedVolume?.secret?.secretName !== 'ql3-cluster-control-runtime' ||
|
||||
projectedVolume?.secret?.defaultMode !== 0o444 ||
|
||||
JSON.stringify(projectedVolume?.secret?.items) !==
|
||||
JSON.stringify([
|
||||
{ key: 'postgres-ca.crt', path: 'ca.crt' },
|
||||
{
|
||||
@@ -865,12 +913,14 @@ function assertKubernetes(readFile, root, findings) {
|
||||
path: 'api-credential-pepper-keyring.json',
|
||||
},
|
||||
]) ||
|
||||
privateVolume?.emptyDir?.medium !== 'Memory' ||
|
||||
privateVolume?.emptyDir?.sizeLimit !== '1Mi' ||
|
||||
env.has('QL3_API_CREDENTIAL_PEPPER')
|
||||
) {
|
||||
findings.push(
|
||||
finding(
|
||||
'QL3_CLUSTER_KUBERNETES_POSTGRES_CA_BINDING',
|
||||
'runtime PostgreSQL trust must use the reviewed read-only projected CA file',
|
||||
'runtime trust and API credential keyring must be copied from one projected Secret generation into bounded Pod-private regular files before the control process starts',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ const IMAGE_NAMES = Object.freeze({
|
||||
worker: 'qinglong3-worker',
|
||||
});
|
||||
const EXPECTED_SOURCE_SURFACES = Object.freeze({
|
||||
control: 2,
|
||||
control: 3,
|
||||
'control-ai': 1,
|
||||
admin: 28,
|
||||
worker: 2,
|
||||
|
||||
@@ -731,6 +731,27 @@ async function runCustodyEvidence({
|
||||
}
|
||||
}
|
||||
|
||||
function runtimeFileMaterializationSource({
|
||||
postgresDirectory = '/var/run/secrets/qinglong3/postgres-projected',
|
||||
keyringDirectory = '/var/run/secrets/qinglong3/api-credential-projected',
|
||||
targetDirectory = '/var/run/secrets/qinglong3/runtime',
|
||||
} = {}) {
|
||||
for (const value of [
|
||||
postgresDirectory,
|
||||
keyringDirectory,
|
||||
targetDirectory,
|
||||
]) {
|
||||
assert.equal(path.isAbsolute(value), true);
|
||||
}
|
||||
return [
|
||||
"const fs=require('node:fs')",
|
||||
"const path=require('node:path')",
|
||||
`const target=${JSON.stringify(targetDirectory)}`,
|
||||
`const files=[[${JSON.stringify(postgresDirectory + '/..data')},'ca.crt'],[${JSON.stringify(keyringDirectory + '/..data')},'keyring.json']]`,
|
||||
"for(const [directory,name] of files){const source=fs.realpathSync(directory);const output=path.join(target,name);fs.copyFileSync(path.join(source,name),output,fs.constants.COPYFILE_EXCL);fs.chmodSync(output,0o400)}",
|
||||
].join(';');
|
||||
}
|
||||
|
||||
function clusterControlResources(controlImage) {
|
||||
const labels = Object.freeze({
|
||||
'app.kubernetes.io/name': CONTROL_NAME,
|
||||
@@ -803,7 +824,7 @@ function clusterControlResources(controlImage) {
|
||||
{ name: 'QL3_POSTGRES_TLS_MODE', value: 'verify-full' },
|
||||
{
|
||||
name: 'QL3_POSTGRES_TLS_CA_FILE',
|
||||
value: '/var/run/secrets/qinglong3/postgres/ca.crt',
|
||||
value: '/var/run/secrets/qinglong3/runtime/ca.crt',
|
||||
},
|
||||
{
|
||||
name: 'QL3_POSTGRES_TLS_SERVERNAME',
|
||||
@@ -834,7 +855,7 @@ function clusterControlResources(controlImage) {
|
||||
},
|
||||
{
|
||||
name: 'QL3_API_CREDENTIAL_PEPPER_KEYRING_FILE',
|
||||
value: '/var/run/secrets/qinglong3/api-credential/keyring.json',
|
||||
value: '/var/run/secrets/qinglong3/runtime/keyring.json',
|
||||
},
|
||||
],
|
||||
ports: [{ name: 'http', containerPort: 5800 }],
|
||||
@@ -857,15 +878,45 @@ function clusterControlResources(controlImage) {
|
||||
volumeMounts: [
|
||||
{ name: 'tmp', mountPath: '/tmp' },
|
||||
{
|
||||
name: 'postgres-ca',
|
||||
mountPath: '/var/run/secrets/qinglong3/postgres',
|
||||
name: 'runtime-private',
|
||||
mountPath: '/var/run/secrets/qinglong3/runtime',
|
||||
readOnly: true,
|
||||
},
|
||||
],
|
||||
}],
|
||||
initContainers: [{
|
||||
name: 'materialize-runtime-files',
|
||||
image: controlImage,
|
||||
imagePullPolicy: 'Never',
|
||||
command: [
|
||||
'node',
|
||||
'-e',
|
||||
runtimeFileMaterializationSource(),
|
||||
],
|
||||
securityContext: {
|
||||
allowPrivilegeEscalation: false,
|
||||
readOnlyRootFilesystem: true,
|
||||
capabilities: { drop: ['ALL'] },
|
||||
},
|
||||
resources: {
|
||||
requests: { cpu: '10m', memory: '32Mi' },
|
||||
limits: { cpu: '100m', memory: '64Mi' },
|
||||
},
|
||||
volumeMounts: [
|
||||
{
|
||||
name: 'postgres-ca-projected',
|
||||
mountPath: '/var/run/secrets/qinglong3/postgres-projected',
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'api-credential-keyring',
|
||||
mountPath: '/var/run/secrets/qinglong3/api-credential',
|
||||
name: 'api-credential-keyring-projected',
|
||||
mountPath: '/var/run/secrets/qinglong3/api-credential-projected',
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'runtime-private',
|
||||
mountPath: '/var/run/secrets/qinglong3/runtime',
|
||||
},
|
||||
],
|
||||
}],
|
||||
volumes: [
|
||||
@@ -874,7 +925,7 @@ function clusterControlResources(controlImage) {
|
||||
emptyDir: { medium: 'Memory', sizeLimit: '16Mi' },
|
||||
},
|
||||
{
|
||||
name: 'postgres-ca',
|
||||
name: 'postgres-ca-projected',
|
||||
secret: {
|
||||
secretName: 'ql3-postgres-ca',
|
||||
defaultMode: 292,
|
||||
@@ -882,7 +933,7 @@ function clusterControlResources(controlImage) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'api-credential-keyring',
|
||||
name: 'api-credential-keyring-projected',
|
||||
secret: {
|
||||
secretName: CONTROL_RUNTIME_SECRET,
|
||||
defaultMode: 292,
|
||||
@@ -892,6 +943,10 @@ function clusterControlResources(controlImage) {
|
||||
}],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'runtime-private',
|
||||
emptyDir: { medium: 'Memory', sizeLimit: '1Mi' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -2353,6 +2408,7 @@ if (require.main === module) {
|
||||
module.exports = {
|
||||
auditListCommand,
|
||||
clusterControlResources,
|
||||
runtimeFileMaterializationSource,
|
||||
controlRolloutFailureEvidence,
|
||||
controlTerminationFact,
|
||||
credentialAuthenticationProbeSource,
|
||||
|
||||
Reference in New Issue
Block a user