Skip to content

Commit 2bd5199

Browse files
authored
Merge pull request moby#3957 from vito/gw-ctr-secret-env
support passing SecretEnv to gateway containers
2 parents 2d91ddc + 6c4c1e0 commit 2bd5199

File tree

8 files changed

+358
-162
lines changed

8 files changed

+358
-162
lines changed

client/build_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func TestClientGatewayIntegration(t *testing.T) {
4444
testClientGatewayContainerPID1Fail,
4545
testClientGatewayContainerPID1Exit,
4646
testClientGatewayContainerMounts,
47+
testClientGatewayContainerSecretEnv,
4748
testClientGatewayContainerPID1Tty,
4849
testClientGatewayContainerCancelPID1Tty,
4950
testClientGatewayContainerExecTty,
@@ -843,6 +844,75 @@ func testClientGatewayContainerMounts(t *testing.T, sb integration.Sandbox) {
843844
checkAllReleasable(t, c, sb, true)
844845
}
845846

847+
func testClientGatewayContainerSecretEnv(t *testing.T, sb integration.Sandbox) {
848+
requiresLinux(t)
849+
850+
ctx := sb.Context()
851+
852+
c, err := New(ctx, sb.Address())
853+
require.NoError(t, err)
854+
defer c.Close()
855+
856+
product := "buildkit_test"
857+
858+
b := func(ctx context.Context, c client.Client) (*client.Result, error) {
859+
mounts := map[string]llb.State{
860+
"/": llb.Image("busybox:latest"),
861+
}
862+
863+
var containerMounts []client.Mount
864+
for mountpoint, st := range mounts {
865+
def, err := st.Marshal(ctx)
866+
if err != nil {
867+
return nil, errors.Wrap(err, "failed to marshal state")
868+
}
869+
870+
r, err := c.Solve(ctx, client.SolveRequest{
871+
Definition: def.ToPB(),
872+
})
873+
if err != nil {
874+
return nil, errors.Wrap(err, "failed to solve")
875+
}
876+
containerMounts = append(containerMounts, client.Mount{
877+
Dest: mountpoint,
878+
MountType: pb.MountType_BIND,
879+
Ref: r.Ref,
880+
})
881+
}
882+
883+
ctr, err := c.NewContainer(ctx, client.NewContainerRequest{Mounts: containerMounts})
884+
if err != nil {
885+
return nil, err
886+
}
887+
888+
pid, err := ctr.Start(ctx, client.StartRequest{
889+
Args: []string{"sh", "-c", "test $SOME_SECRET = foo-secret"},
890+
SecretEnv: []*pb.SecretEnv{
891+
{
892+
ID: "sekrit",
893+
Name: "SOME_SECRET",
894+
},
895+
},
896+
})
897+
require.NoError(t, err)
898+
err = pid.Wait()
899+
require.NoError(t, err)
900+
901+
return &client.Result{}, ctr.Release(ctx)
902+
}
903+
904+
_, err = c.Build(ctx, SolveOpt{
905+
Session: []session.Attachable{
906+
secretsprovider.FromMap(map[string][]byte{
907+
"sekrit": []byte("foo-secret"),
908+
}),
909+
},
910+
}, product, b, nil)
911+
require.NoError(t, err)
912+
913+
checkAllReleasable(t, c, sb, true)
914+
}
915+
846916
// testClientGatewayContainerPID1Tty is testing that we can get a tty via
847917
// a container pid1, executor.Run
848918
func testClientGatewayContainerPID1Tty(t *testing.T, sb integration.Sandbox) {

frontend/gateway/client/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type Container interface {
7171
type StartRequest struct {
7272
Args []string
7373
Env []string
74+
SecretEnv []*pb.SecretEnv
7475
User string
7576
Cwd string
7677
Tty bool

frontend/gateway/container.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sync"
1111
"syscall"
1212

13+
"github.com/moby/buildkit/session/secrets"
1314
"github.com/moby/buildkit/util/bklog"
1415

1516
"github.com/moby/buildkit/cache"
@@ -18,6 +19,7 @@ import (
1819
"github.com/moby/buildkit/session"
1920
"github.com/moby/buildkit/snapshot"
2021
"github.com/moby/buildkit/solver/llbsolver/mounts"
22+
"github.com/moby/buildkit/solver/pb"
2123
opspb "github.com/moby/buildkit/solver/pb"
2224
"github.com/moby/buildkit/util/stack"
2325
utilsystem "github.com/moby/buildkit/util/system"
@@ -61,6 +63,8 @@ func NewContainer(ctx context.Context, w worker.Worker, sm *session.Manager, g s
6163
extraHosts: req.ExtraHosts,
6264
platform: platform,
6365
executor: w.Executor(),
66+
sm: sm,
67+
group: g,
6468
errGroup: eg,
6569
ctx: ctx,
6670
cancel: cancel,
@@ -288,6 +292,8 @@ type gatewayContainer struct {
288292
rootFS executor.Mount
289293
mounts []executor.Mount
290294
executor executor.Executor
295+
sm *session.Manager
296+
group session.Group
291297
started bool
292298
errGroup *errgroup.Group
293299
mu sync.Mutex
@@ -296,7 +302,7 @@ type gatewayContainer struct {
296302
cancel func()
297303
}
298304

299-
func (gwCtr *gatewayContainer) Start(_ context.Context, req client.StartRequest) (client.ContainerProcess, error) {
305+
func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartRequest) (client.ContainerProcess, error) {
300306
resize := make(chan executor.WinSize)
301307
signal := make(chan syscall.Signal)
302308
procInfo := executor.ProcessInfo{
@@ -326,6 +332,12 @@ func (gwCtr *gatewayContainer) Start(_ context.Context, req client.StartRequest)
326332
procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "TERM", "xterm")
327333
}
328334

335+
secretEnv, err := gwCtr.loadSecretEnv(ctx, req.SecretEnv)
336+
if err != nil {
337+
return nil, err
338+
}
339+
procInfo.Meta.Env = append(procInfo.Meta.Env, secretEnv...)
340+
329341
// mark that we have started on the first call to execProcess for this
330342
// container, so that future calls will call Exec rather than Run
331343
gwCtr.mu.Lock()
@@ -365,6 +377,33 @@ func (gwCtr *gatewayContainer) Start(_ context.Context, req client.StartRequest)
365377
return gwProc, nil
366378
}
367379

380+
func (gwCtr *gatewayContainer) loadSecretEnv(ctx context.Context, secretEnv []*pb.SecretEnv) ([]string, error) {
381+
out := make([]string, 0, len(secretEnv))
382+
for _, sopt := range secretEnv {
383+
id := sopt.ID
384+
if id == "" {
385+
return nil, errors.Errorf("secret ID missing for %q environment variable", sopt.Name)
386+
}
387+
var dt []byte
388+
var err error
389+
err = gwCtr.sm.Any(ctx, gwCtr.group, func(ctx context.Context, _ string, caller session.Caller) error {
390+
dt, err = secrets.GetSecret(ctx, caller, id)
391+
if err != nil {
392+
if errors.Is(err, secrets.ErrNotFound) && sopt.Optional {
393+
return nil
394+
}
395+
return err
396+
}
397+
return nil
398+
})
399+
if err != nil {
400+
return nil, err
401+
}
402+
out = append(out, fmt.Sprintf("%s=%s", sopt.Name, string(dt)))
403+
}
404+
return out, nil
405+
}
406+
368407
func (gwCtr *gatewayContainer) Release(ctx context.Context) error {
369408
gwCtr.mu.Lock()
370409
defer gwCtr.mu.Unlock()

frontend/gateway/gateway.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,7 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e
13001300
proc, err := ctr.Start(initCtx, gwclient.StartRequest{
13011301
Args: init.Meta.Args,
13021302
Env: init.Meta.Env,
1303+
SecretEnv: init.Secretenv,
13031304
User: init.Meta.User,
13041305
Cwd: init.Meta.Cwd,
13051306
Tty: init.Tty,

frontend/gateway/grpcclient/client.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,13 +806,15 @@ func (c *grpcClient) NewContainer(ctx context.Context, req client.NewContainerRe
806806

807807
return &container{
808808
client: c.client,
809+
caps: c.caps,
809810
id: id,
810811
execMsgs: c.execMsgs,
811812
}, nil
812813
}
813814

814815
type container struct {
815816
client pb.LLBBridgeClient
817+
caps apicaps.CapSet
816818
id string
817819
execMsgs *messageForwarder
818820
}
@@ -821,6 +823,12 @@ func (ctr *container) Start(ctx context.Context, req client.StartRequest) (clien
821823
pid := fmt.Sprintf("%s:%s", ctr.id, identity.NewID())
822824
msgs := ctr.execMsgs.Register(pid)
823825

826+
if len(req.SecretEnv) > 0 {
827+
if err := ctr.caps.Supports(pb.CapGatewayExecSecretEnv); err != nil {
828+
return nil, err
829+
}
830+
}
831+
824832
init := &pb.InitMessage{
825833
ContainerID: ctr.id,
826834
Meta: &opspb.Meta{
@@ -829,8 +837,9 @@ func (ctr *container) Start(ctx context.Context, req client.StartRequest) (clien
829837
Cwd: req.Cwd,
830838
User: req.User,
831839
},
832-
Tty: req.Tty,
833-
Security: req.SecurityMode,
840+
Tty: req.Tty,
841+
Security: req.SecurityMode,
842+
Secretenv: req.SecretEnv,
834843
}
835844
init.Meta.RemoveMountStubsRecursive = req.RemoveMountStubsRecursive
836845
if req.Stdin != nil {

frontend/gateway/pb/caps.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const (
4444
// /etc/hosts for containers created via gateway exec.
4545
CapGatewayExecExtraHosts apicaps.CapID = "gateway.exec.extrahosts"
4646

47+
// CapGatewayExecExtraHosts is the capability to set secrets as env vars for
48+
// containers created via gateway exec.
49+
CapGatewayExecSecretEnv apicaps.CapID = "gateway.exec.secretenv"
50+
4751
// CapGatewayExecExtraHosts is the capability to send signals to a process
4852
// created via gateway exec.
4953
CapGatewayExecSignals apicaps.CapID = "gateway.exec.signals"
@@ -179,6 +183,13 @@ func init() {
179183
Status: apicaps.CapStatusExperimental,
180184
})
181185

186+
Caps.Init(apicaps.Cap{
187+
ID: CapGatewayExecSecretEnv,
188+
Name: "gateway exec secret env",
189+
Enabled: true,
190+
Status: apicaps.CapStatusExperimental,
191+
})
192+
182193
Caps.Init(apicaps.Cap{
183194
ID: CapGatewayExecSignals,
184195
Name: "gateway exec signals",

0 commit comments

Comments
 (0)