Skip to content

Commit e3fcf9a

Browse files
committed
fix: introduce per-job cert volume with %gcl% token
Jobs using docker:dind as a service share TLS client certificates via a named volume. The previous approach relied on a static volume name (e.g. `certs`) configured in .gitlab-ci-local-env, which caused race conditions when concurrent jobs wrote to and cleaned up the same volume. A new `%gcl%:` prefix in VOLUME entries is now resolved at runtime to a per-job unique volume name (`gcl-<job>-<id>-cert`), matching the naming pattern of the existing build/tmp volumes. The `%gcl%` token was chosen because Docker hard-rejects it if it ever reaches the daemon unsubstituted (invalid volume name character), rather than silently bind-mounting an unintended host path. Changes: - get certVolumeName() getter returning a per-job unique name - Cert volume is created and registered for cleanup alongside build/tmp volumes when any %gcl%: entry is present in argv.volume - %gcl%: prefix is resolved to certVolumeName in both the job container and service container volume loops
1 parent 98c6573 commit e3fcf9a

1 file changed

Lines changed: 15 additions & 4 deletions

File tree

src/job.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ If you know what you're doing and would like to suppress this warning, use one o
468468
return this.jobData["needs"] ?? null;
469469
}
470470

471+
get certVolumeName (): string {
472+
return `gcl-${this.safeJobName}-${this.jobId}-cert`;
473+
}
474+
471475
get buildVolumeName (): string {
472476
return `gcl-${this.safeJobName}-${this.jobId}-build`;
473477
}
@@ -689,10 +693,15 @@ If you know what you're doing and would like to suppress this warning, use one o
689693
const fileVariablesDir = this.fileVariablesDir;
690694

691695
this._containerVolumeNames.push(buildVolumeName, tmpVolumeName);
692-
await Promise.all([
696+
const volumeCreatePromises = [
693697
Utils.spawn([this.argv.containerExecutable, "volume", "create", `${buildVolumeName}`], argv.cwd),
694698
Utils.spawn([this.argv.containerExecutable, "volume", "create", `${tmpVolumeName}`], argv.cwd),
695-
]);
699+
];
700+
if (this.argv.volume.some(v => v.startsWith("%gcl%:"))) {
701+
this._containerVolumeNames.push(this.certVolumeName);
702+
volumeCreatePromises.push(Utils.spawn([this.argv.containerExecutable, "volume", "create", this.certVolumeName], argv.cwd));
703+
}
704+
await Promise.all(volumeCreatePromises);
696705

697706
const time = process.hrtime();
698707
this.refreshLongRunningSilentTimeout(writeStreams);
@@ -1017,7 +1026,8 @@ If you know what you're doing and would like to suppress this warning, use one o
10171026
dockerCmd += `--workdir ${this.ciProjectDir} `;
10181027

10191028
for (const volume of this.argv.volume) {
1020-
dockerCmd += `--volume ${volume} `;
1029+
const v = volume.startsWith("%gcl%:") ? `${this.certVolumeName}${volume.slice("%gcl%".length)}` : volume;
1030+
dockerCmd += `--volume ${v} `;
10211031
}
10221032

10231033
for (const extraHost of this.argv.extraHost) {
@@ -1580,7 +1590,8 @@ If you know what you're doing and would like to suppress this warning, use one o
15801590
}
15811591

15821592
for (const volume of this.argv.volume) {
1583-
dockerCmd += `--volume ${volume} `;
1593+
const v = volume.startsWith("%gcl%:") ? `${this.certVolumeName}${volume.slice("%gcl%".length)}` : volume;
1594+
dockerCmd += `--volume ${v} `;
15841595
}
15851596

15861597
for (const extraHost of this.argv.extraHost) {

0 commit comments

Comments
 (0)