-
Notifications
You must be signed in to change notification settings - Fork 4
Closed as not planned
Description
This PR adds support for passing build secrets to the imageBuild method using BuildKit's --mount type=secret feature. Currently, the method lacks this capability, forcing users to rely on less secure alternatives like build arguments.
Problem
- Users cannot use Docker BuildKit secrets (
--mount type=secret) when building images through this library - Sensitive data (API keys, credentials, SSH keys) cannot be securely passed during build without exposing them in the image history
- The only current workaround is using
buildargs, which stores values in the image metadata
Solution
Add a new optional parameter secrets to the imageBuild method that accepts secret configurations and passes them to the Docker daemon via the appropriate API headers.
Changes
API Changes
Add new parameter to imageBuild options:
secrets?: Record<string, string>;Implementation Details
-
New parameter:
secrets- a Record mapping secret names to their values- Example:
{ "github_token": "ghp_...", "ssh_key": "..." }
- Example:
-
Header encoding: Secrets should be passed via
X-Registry-Configor a newX-BuildKit-Secretsheader (following Docker API conventions) -
Dockerfile usage: Allows the Dockerfile to mount secrets like:
RUN --mount=type=secret,id=github_token cat /run/secrets/github_token > ~/.credentials
Code Changes
public imageBuild(
buildContext: ReadableStream,
options?: {
dockerfile?: string;
tag?: string;
extrahosts?: string;
remote?: string;
quiet?: boolean;
nocache?: boolean;
cachefrom?: string;
pull?: string;
rm?: boolean;
forcerm?: boolean;
memory?: number;
memswap?: number;
cpushares?: number;
cpusetcpus?: string;
cpuperiod?: number;
cpuquota?: number;
buildargs?: string;
shmsize?: number;
squash?: boolean;
labels?: string;
networkmode?: string;
credentials?: Record<string, AuthConfig>;
platform?: string;
target?: string;
outputs?: string;
version?: '1' | '2';
secrets?: Record<string, string>; // NEW
},
): JSONMessages<JSONMessage, string> {
const headers: Record<string, string> = {};
headers['Content-Type'] = 'application/x-tar';
if (options?.credentials) {
headers['X-Registry-Config'] = this.authCredentials(
options.credentials,
);
}
// NEW: Handle secrets
if (options?.secrets) {
headers['X-BuildKit-Secrets'] = Buffer.from(
JSON.stringify(options.secrets)
).toString('base64');
}
const request = this.api.post(
'/build',
{
dockerfile: options?.dockerfile,
t: options?.tag,
extrahosts: options?.extrahosts,
remote: options?.remote,
q: options?.quiet,
nocache: options?.nocache,
cachefrom: options?.cachefrom,
pull: options?.pull,
rm: options?.rm,
forcerm: options?.forcerm,
memory: options?.memory,
memswap: options?.memswap,
cpushares: options?.cpushares,
cpusetcpus: options?.cpusetcpus,
cpuperiod: options?.cpuperiod,
cpuquota: options?.cpuquota,
buildargs: options?.buildargs,
shmsize: options?.shmsize,
squash: options?.squash,
labels: options?.labels,
networkmode: options?.networkmode,
platform: options?.platform,
target: options?.target,
outputs: options?.outputs,
version: options?.version || '2',
secrets: options?.secrets, // NEW
},
buildContext,
headers,
);
return {
messages: async function* (): AsyncGenerator<
types.JSONMessage,
void,
undefined
> {
const response = await request;
yield* jsonMessages<types.JSONMessage>(response);
},
wait: async function (): Promise<string> {
let id = '';
const response = await request;
for await (const message of jsonMessages<types.JSONMessage>(
response,
)) {
if (message.errorDetail) {
throw new Error(message.errorDetail?.message);
}
if (message.id === 'moby.image.id') {
id = message?.aux?.ID || '';
}
}
return id;
},
};
}Usage Example
const secrets = {
github_token: process.env.GITHUB_TOKEN,
npm_token: process.env.NPM_TOKEN,
ssh_key: fs.readFileSync('/path/to/id_rsa', 'utf-8'),
};
const build = docker.imageBuild(tarStream, {
tag: 'myapp:latest',
secrets: secrets,
version: '2', // BuildKit required
});
const imageId = await build.wait();Dockerfile:
FROM node:18
RUN --mount=type=secret,id=github_token \
--mount=type=secret,id=npm_token \
cat /run/secrets/github_token > /tmp/token && \
npm install
# Secrets are NOT included in the final image layerSecurity Benefits
- ✅ Secrets are NOT stored in image layers or history
- ✅ Secrets are only available during build time in
/run/secrets/ - ✅ No exposure in
docker historyor image inspection - ✅ Follows Docker/Moby API standards
Backwards Compatibility
- ✅ Fully backwards compatible -
secretsparameter is optional - ✅ Requires BuildKit (
version: '2') - will be ignored in BuildKit v1
Testing
it('should pass secrets to build', async () => {
const secrets = { 'github_token': 'test-token' };
docker.imageBuild(tarStream, {
tag: 'test:latest',
secrets: secrets,
version: '2',
});
// Verify headers contain encoded secrets
expect(mockRequest.headers['X-BuildKit-Secrets']).toBeDefined();
});Related
- Related: Docker API BuildKit documentation: https://docs.docker.com/build/building/secrets/
Metadata
Metadata
Assignees
Labels
No labels