Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,25 @@ await new Promise((resolve, reject) => {
// Build has finished
```

### BuildKit secrets

If the remote Docker daemon supports BuildKit, you can pass secrets to the build:

```js
const buildOpts = {
t: 'myimage',
dockerfile: 'Dockerfile',
version: '2',
secrets: {
mysecret: Buffer.from('supersecret')
}
};
docker.buildImage(tarStream, buildOpts, (err, stream) => { ... });
```

This uses the session API and requires BuildKit. Secrets are delivered via a small in-session secret provider.



### Creating a container:

Expand Down
7 changes: 6 additions & 1 deletion lib/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ Docker.prototype.buildImage = function(file, opts, callback) {
delete optsf.options.authconfig;
}

if (opts.secrets) {
// avoid sending secrets as parameters in the request.
delete optsf.options.secrets;
}

if (opts.cachefrom && Array.isArray(opts.cachefrom)) {
optsf.options.cachefrom = JSON.stringify(opts.cachefrom);
}
Expand All @@ -301,7 +306,7 @@ Docker.prototype.buildImage = function(file, opts, callback) {

function dialWithSession(callback) {
if (opts?.version === "2") {
withSession(self, optsf.authconfig,(err, sessionId, done) => {
withSession(self, {auth: optsf.authconfig, secrets: opts.secrets },(err, sessionId, done) => {
if (err) {
return callback(err);
}
Expand Down
19 changes: 19 additions & 0 deletions lib/proto/secrets.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";

package moby.buildkit.secrets.v1;

option go_package = "secrets";

service Secrets{
rpc GetSecret(GetSecretRequest) returns (GetSecretResponse);
}


message GetSecretRequest {
string ID = 1;
map<string, string> annotations = 2;
}

message GetSecretResponse {
bytes data = 1;
}
25 changes: 21 additions & 4 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var grpc = require("@grpc/grpc-js"),
path = require("path"),
uuid = require("uuid").v4;

function withSession(docker, auth, handler) {
function withSession(docker, { auth, secrets }, handler) {
const sessionId = uuid();

const opts = {
Expand Down Expand Up @@ -32,11 +32,15 @@ function withSession(docker, auth, handler) {
injector.injectConnection(socket);

const pkg = protoLoader.loadSync(
path.resolve(__dirname, "proto", "auth.proto")
[
path.resolve(__dirname, "proto", "auth.proto"),
path.resolve(__dirname, "proto", "secrets.proto"),
]
);
const service = grpc.loadPackageDefinition(pkg);

server.addService(service.moby.filesync.v1.Auth.service, {
const services = grpc.loadPackageDefinition(pkg);

server.addService(services.moby.filesync.v1.Auth.service, {
Credentials({ request }, callback) {
// We probably want to have the possibility to pass credentials per
// hots. The correct one could be returned based on `request.Host`
Expand All @@ -51,6 +55,19 @@ function withSession(docker, auth, handler) {
},
});

server.addService(services.moby.buildkit.secrets.v1.Secrets.service, {
GetSecret({ request }, callback) {
const found = secrets[request.ID];
if (!found) {
return callback({message: "No secret found."});
}

callback(null, {
data: found,
});
}
})

function done() {
server.forceShutdown();
socket.end();
Expand Down
5 changes: 5 additions & 0 deletions test/buildkit-secrets.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM alpine

RUN --mount=type=secret,id=s,target=/secret tail /secret

CMD ["bash"]
39 changes: 39 additions & 0 deletions test/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,45 @@ describe("#docker", function () {
handler
);
});

it("should build image with buildKit and support secrets", function (done) {
this.timeout(60000);
const randomId = Math.random().toString(36).substring(7);

function handler(err, stream) {
expect(err).to.be.null;
expect(stream).to.be.ok;

stream.pipe(process.stdout, {
end: true,
});

stream.on("end", function () {
docker.getImage(randomId).inspect(function (err, image) {
expect(err).to.be.null;
expect(image).to.exist;
done();
});
});
}

docker.buildImage(
{
context: __dirname,
src: ["buildkit-secrets.Dockerfile"],
},
{
dockerfile: "buildkit-secrets.Dockerfile",
secrets: {
s: Buffer.from("secret message.")
},
version: "2",
t: randomId,
pull: "true",
},
handler
);
});
});

describe("#loadImage", function () {
Expand Down
Loading