Skip to content

Commit a815ace

Browse files
committed
Add BinaryURLResolver to ConfidentialModule for presigned URL support
The enclave needs an authenticated URL to download WASM binaries from the CRE storage service. BinaryURLResolver resolves the raw on-chain URL into a presigned/ephemeral URL per execution. Nil-safe: falls back to the raw URL when no resolver is set. PR 5/5 (#21642) wires this to the storage service retriever.
1 parent 587f754 commit a815ace

3 files changed

Lines changed: 143 additions & 30 deletions

File tree

core/services/workflows/syncer/v2/handler.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -898,15 +898,16 @@ func (h *eventHandler) confidentialEngineFactory(
898898
lggr := logger.Named(h.lggr, "WorkflowEngine.ConfidentialModule")
899899
lggr = logger.With(lggr, "workflowID", spec.WorkflowID, "workflowName", spec.WorkflowName, "workflowOwner", spec.WorkflowOwner)
900900

901+
// nil resolver: raw binaryURL is passed to the enclave as-is.
902+
// PR 5/5 (#21642) wires this to the storage service retriever
903+
// so the enclave receives a presigned URL.
901904
module := v2.NewConfidentialModule(
902905
h.capRegistry,
903906
spec.BinaryURL,
904907
binaryHash,
905-
spec.WorkflowID,
906-
spec.WorkflowOwner,
907-
workflowName.String(),
908-
spec.WorkflowTag,
908+
spec.WorkflowID, spec.WorkflowOwner, workflowName.String(), spec.WorkflowTag,
909909
attrs.VaultDonSecrets,
910+
nil,
910911
lggr,
911912
)
912913

core/services/workflows/v2/confidential_module.go

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,26 @@ func IsConfidential(data []byte) (bool, error) {
5757
return attrs.Confidential, nil
5858
}
5959

60+
// BinaryURLResolver resolves a raw binary URL into an ephemeral/presigned
61+
// URL that the enclave can fetch without authentication. In production this
62+
// calls the CRE storage service; nil means the raw URL is used as-is.
63+
// PR 5/5 (#21642) wires this to the storage service retriever.
64+
type BinaryURLResolver func(ctx context.Context, workflowID string) (string, error)
65+
6066
// ConfidentialModule implements host.ModuleV2 for confidential workflows.
6167
// Instead of running WASM locally, it delegates execution to the
6268
// confidential-workflows capability via the CapabilitiesRegistry.
6369
type ConfidentialModule struct {
64-
capRegistry core.CapabilitiesRegistry
65-
binaryURL string
66-
binaryHash []byte
67-
workflowID string
68-
workflowOwner string
69-
workflowName string
70-
workflowTag string
71-
vaultDonSecrets []SecretIdentifier
72-
lggr logger.Logger
70+
capRegistry core.CapabilitiesRegistry
71+
binaryURL string
72+
binaryHash []byte
73+
workflowID string
74+
workflowOwner string
75+
workflowName string
76+
workflowTag string
77+
vaultDonSecrets []SecretIdentifier
78+
binaryURLResolver BinaryURLResolver
79+
lggr logger.Logger
7380
}
7481

7582
var _ host.ModuleV2 = (*ConfidentialModule)(nil)
@@ -78,23 +85,22 @@ func NewConfidentialModule(
7885
capRegistry core.CapabilitiesRegistry,
7986
binaryURL string,
8087
binaryHash []byte,
81-
workflowID string,
82-
workflowOwner string,
83-
workflowName string,
84-
workflowTag string,
88+
workflowID, workflowOwner, workflowName, workflowTag string,
8589
vaultDonSecrets []SecretIdentifier,
90+
binaryURLResolver BinaryURLResolver,
8691
lggr logger.Logger,
8792
) *ConfidentialModule {
8893
return &ConfidentialModule{
89-
capRegistry: capRegistry,
90-
binaryURL: binaryURL,
91-
binaryHash: binaryHash,
92-
workflowID: workflowID,
93-
workflowOwner: workflowOwner,
94-
workflowName: workflowName,
95-
workflowTag: workflowTag,
96-
vaultDonSecrets: vaultDonSecrets,
97-
lggr: lggr,
94+
capRegistry: capRegistry,
95+
binaryURL: binaryURL,
96+
binaryHash: binaryHash,
97+
workflowID: workflowID,
98+
workflowOwner: workflowOwner,
99+
workflowName: workflowName,
100+
workflowTag: workflowTag,
101+
vaultDonSecrets: vaultDonSecrets,
102+
binaryURLResolver: binaryURLResolver,
103+
lggr: lggr,
98104
}
99105
}
100106

@@ -125,11 +131,20 @@ func (m *ConfidentialModule) Execute(
125131
}
126132
}
127133

134+
binaryURL := m.binaryURL
135+
if m.binaryURLResolver != nil {
136+
resolved, resolveErr := m.binaryURLResolver(ctx, m.workflowID)
137+
if resolveErr != nil {
138+
return nil, fmt.Errorf("failed to resolve binary URL: %w", resolveErr)
139+
}
140+
binaryURL = resolved
141+
}
142+
128143
capInput := &confworkflowtypes.ConfidentialWorkflowRequest{
129144
VaultDonSecrets: protoSecrets,
130145
Execution: &confworkflowtypes.WorkflowExecution{
131146
WorkflowId: m.workflowID,
132-
BinaryUrl: m.binaryURL,
147+
BinaryUrl: binaryURL,
133148
BinaryHash: m.binaryHash,
134149
ExecuteRequest: execReqBytes,
135150
Owner: m.workflowOwner,

core/services/workflows/v2/confidential_module_test.go

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
172172
{Key: "API_KEY"},
173173
{Key: "SIGNING_KEY", Namespace: "custom-ns"},
174174
},
175+
nil,
175176
lggr,
176177
)
177178

@@ -205,6 +206,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
205206
[]byte("hash"),
206207
"wf-1", "owner", "name", "tag",
207208
[]SecretIdentifier{{Key: "SECRET_A"}}, // no namespace
209+
nil,
208210
lggr,
209211
)
210212

@@ -225,7 +227,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
225227
Return(nil, errors.New("capability not found")).Once()
226228

227229
mod := NewConfidentialModule(
228-
capReg, "", nil, "wf", "owner", "name", "tag", nil, lggr,
230+
capReg, "", nil, "wf", "owner", "name", "tag", nil, nil, lggr,
229231
)
230232

231233
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{})
@@ -243,7 +245,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
243245
Return(capabilities.CapabilityResponse{}, errors.New("enclave unavailable")).Once()
244246

245247
mod := NewConfidentialModule(
246-
capReg, "", nil, "wf", "owner", "name", "tag", nil, lggr,
248+
capReg, "", nil, "wf", "owner", "name", "tag", nil, nil, lggr,
247249
)
248250

249251
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{})
@@ -261,7 +263,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
261263
Return(capabilities.CapabilityResponse{Payload: nil}, nil).Once()
262264

263265
mod := NewConfidentialModule(
264-
capReg, "", nil, "wf", "owner", "name", "tag", nil, lggr,
266+
capReg, "", nil, "wf", "owner", "name", "tag", nil, nil, lggr,
265267
)
266268

267269
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{})
@@ -296,6 +298,7 @@ func TestConfidentialModule_Execute(t *testing.T) {
296298
{Key: "K1", Namespace: "ns1"},
297299
{Key: "K2"},
298300
},
301+
nil,
299302
lggr,
300303
)
301304

@@ -333,6 +336,100 @@ func TestConfidentialModule_Execute(t *testing.T) {
333336
})
334337
}
335338

339+
func TestConfidentialModule_BinaryURLResolver(t *testing.T) {
340+
ctx := context.Background()
341+
lggr := logger.Nop()
342+
343+
execReq := &sdkpb.ExecuteRequest{Config: []byte("cfg")}
344+
345+
expectedResult := &sdkpb.ExecutionResult{
346+
Result: &sdkpb.ExecutionResult_Value{
347+
Value: valuespb.NewStringValue("ok"),
348+
},
349+
}
350+
resultBytes, err := proto.Marshal(expectedResult)
351+
require.NoError(t, err)
352+
confResp := &confworkflowtypes.ConfidentialWorkflowResponse{ExecutionResult: resultBytes}
353+
respPayload, err := anypb.New(confResp)
354+
require.NoError(t, err)
355+
356+
t.Run("resolver replaces binary URL", func(t *testing.T) {
357+
capReg := regmocks.NewCapabilitiesRegistry(t)
358+
execCap := capmocks.NewExecutableCapability(t)
359+
360+
capReg.EXPECT().GetExecutable(matches.AnyContext, confidentialWorkflowsCapabilityID).
361+
Return(execCap, nil).Once()
362+
363+
var capturedReq capabilities.CapabilityRequest
364+
execCap.EXPECT().Execute(matches.AnyContext, mock.Anything).
365+
Run(func(_ context.Context, req capabilities.CapabilityRequest) {
366+
capturedReq = req
367+
}).
368+
Return(capabilities.CapabilityResponse{Payload: respPayload}, nil).Once()
369+
370+
resolver := func(_ context.Context, wfID string) (string, error) {
371+
return "https://presigned.example.com/" + wfID + "?token=abc", nil
372+
}
373+
374+
mod := NewConfidentialModule(
375+
capReg, "https://storage.example.com/raw", []byte("hash"),
376+
"wf-1", "owner", "name", "tag", nil, resolver, lggr,
377+
)
378+
379+
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{executionID: "exec-1"})
380+
require.NoError(t, err)
381+
382+
var confReq confworkflowtypes.ConfidentialWorkflowRequest
383+
require.NoError(t, capturedReq.Payload.UnmarshalTo(&confReq))
384+
assert.Equal(t, "https://presigned.example.com/wf-1?token=abc", confReq.Execution.BinaryUrl)
385+
})
386+
387+
t.Run("nil resolver uses raw URL", func(t *testing.T) {
388+
capReg := regmocks.NewCapabilitiesRegistry(t)
389+
execCap := capmocks.NewExecutableCapability(t)
390+
391+
capReg.EXPECT().GetExecutable(matches.AnyContext, confidentialWorkflowsCapabilityID).
392+
Return(execCap, nil).Once()
393+
394+
var capturedReq capabilities.CapabilityRequest
395+
execCap.EXPECT().Execute(matches.AnyContext, mock.Anything).
396+
Run(func(_ context.Context, req capabilities.CapabilityRequest) {
397+
capturedReq = req
398+
}).
399+
Return(capabilities.CapabilityResponse{Payload: respPayload}, nil).Once()
400+
401+
mod := NewConfidentialModule(
402+
capReg, "https://storage.example.com/raw", []byte("hash"),
403+
"wf-1", "owner", "name", "tag", nil, nil, lggr,
404+
)
405+
406+
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{executionID: "exec-1"})
407+
require.NoError(t, err)
408+
409+
var confReq confworkflowtypes.ConfidentialWorkflowRequest
410+
require.NoError(t, capturedReq.Payload.UnmarshalTo(&confReq))
411+
assert.Equal(t, "https://storage.example.com/raw", confReq.Execution.BinaryUrl)
412+
})
413+
414+
t.Run("resolver error propagates", func(t *testing.T) {
415+
capReg := regmocks.NewCapabilitiesRegistry(t)
416+
417+
resolver := func(_ context.Context, _ string) (string, error) {
418+
return "", errors.New("storage service unavailable")
419+
}
420+
421+
mod := NewConfidentialModule(
422+
capReg, "https://storage.example.com/raw", []byte("hash"),
423+
"wf-1", "owner", "name", "tag", nil, resolver, lggr,
424+
)
425+
426+
_, err := mod.Execute(ctx, execReq, &stubExecutionHelper{executionID: "exec-1"})
427+
require.Error(t, err)
428+
assert.Contains(t, err.Error(), "failed to resolve binary URL")
429+
assert.Contains(t, err.Error(), "storage service unavailable")
430+
})
431+
}
432+
336433
func TestConfidentialModule_InterfaceMethods(t *testing.T) {
337434
mod := &ConfidentialModule{}
338435

0 commit comments

Comments
 (0)