@@ -39,18 +39,21 @@ type UpstreamClientConfig struct {
39
39
type UpstreamClient struct {
40
40
c UpstreamClientConfig
41
41
42
- mintX509CAMtx sync.Mutex
43
- mintX509CAStream * streamState
44
- publishJWTKeyMtx sync.Mutex
45
- publishJWTKeyStream * streamState
42
+ mintX509CAMtx sync.Mutex
43
+ mintX509CAStream * streamState
44
+ publishJWTKeyMtx sync.Mutex
45
+ publishJWTKeyStream * streamState
46
+ subscribeToLocalBundleStreamMtx sync.Mutex
47
+ subscribeToLocalBundleStream * streamState
46
48
}
47
49
48
50
// NewUpstreamClient returns a new UpstreamAuthority plugin client.
49
51
func NewUpstreamClient (config UpstreamClientConfig ) * UpstreamClient {
50
52
return & UpstreamClient {
51
- c : config ,
52
- mintX509CAStream : newStreamState (),
53
- publishJWTKeyStream : newStreamState (),
53
+ c : config ,
54
+ mintX509CAStream : newStreamState (),
55
+ publishJWTKeyStream : newStreamState (),
56
+ subscribeToLocalBundleStream : newStreamState (),
54
57
}
55
58
}
56
59
@@ -67,6 +70,11 @@ func (u *UpstreamClient) Close() error {
67
70
defer u .publishJWTKeyMtx .Unlock ()
68
71
u .publishJWTKeyStream .Stop ()
69
72
}()
73
+ func () {
74
+ u .subscribeToLocalBundleStreamMtx .Lock ()
75
+ defer u .subscribeToLocalBundleStreamMtx .Unlock ()
76
+ u .subscribeToLocalBundleStream .Stop ()
77
+ }()
70
78
return nil
71
79
}
72
80
@@ -96,11 +104,6 @@ func (u *UpstreamClient) MintX509CA(ctx context.Context, csr []byte, ttl time.Du
96
104
}
97
105
}
98
106
99
- // WaitUntilMintX509CAStreamDone waits until the MintX509CA stream has stopped.
100
- func (u * UpstreamClient ) WaitUntilMintX509CAStreamDone (ctx context.Context ) error {
101
- return u .mintX509CAStream .WaitUntilStopped (ctx )
102
- }
103
-
104
107
// PublishJWTKey publishes the JWT key to the UpstreamAuthority. It maintains
105
108
// an open stream to the UpstreamAuthority plugin to receive and append JWT key
106
109
// updates to the bundle. The stream remains open until another call to
@@ -127,9 +130,26 @@ func (u *UpstreamClient) PublishJWTKey(ctx context.Context, jwtKey *common.Publi
127
130
}
128
131
}
129
132
130
- // WaitUntilPublishJWTKeyStreamDone waits until the MintX509CA stream has stopped.
131
- func (u * UpstreamClient ) WaitUntilPublishJWTKeyStreamDone (ctx context.Context ) error {
132
- return u .publishJWTKeyStream .WaitUntilStopped (ctx )
133
+ func (u * UpstreamClient ) SubscribeToLocalBundle (ctx context.Context ) (err error ) {
134
+ u .subscribeToLocalBundleStreamMtx .Lock ()
135
+ defer u .subscribeToLocalBundleStreamMtx .Unlock ()
136
+
137
+ firstResultCh := make (chan bundleUpdatesResult , 1 )
138
+ u .subscribeToLocalBundleStream .Start (func (streamCtx context.Context ) {
139
+ u .runSubscribeToLocalBundleStream (streamCtx , firstResultCh )
140
+ })
141
+ defer func () {
142
+ if err != nil {
143
+ u .subscribeToLocalBundleStream .Stop ()
144
+ }
145
+ }()
146
+
147
+ select {
148
+ case result := <- firstResultCh :
149
+ return result .err
150
+ case <- ctx .Done ():
151
+ return ctx .Err ()
152
+ }
133
153
}
134
154
135
155
func (u * UpstreamClient ) runMintX509CAStream (ctx context.Context , csr []byte , ttl time.Duration , validateX509CA ValidateX509CAFunc , firstResultCh chan <- mintX509CAResult ) {
@@ -223,6 +243,63 @@ func (u *UpstreamClient) runPublishJWTKeyStream(ctx context.Context, jwtKey *com
223
243
}
224
244
}
225
245
246
+ func (u * UpstreamClient ) runSubscribeToLocalBundleStream (ctx context.Context , firstResultCh chan <- bundleUpdatesResult ) {
247
+ x509CAs , jwtKeys , authorityStream , err := u .c .UpstreamAuthority .SubscribeToLocalBundle (ctx )
248
+ if err != nil {
249
+ firstResultCh <- bundleUpdatesResult {err : err }
250
+ return
251
+ }
252
+ defer authorityStream .Close ()
253
+
254
+ err = u .c .BundleUpdater .SyncX509Roots (ctx , x509CAs )
255
+ if err != nil {
256
+ firstResultCh <- bundleUpdatesResult {err : err }
257
+ return
258
+ }
259
+ updatedKeys , err := u .c .BundleUpdater .AppendJWTKeys (ctx , jwtKeys )
260
+ if err != nil {
261
+ firstResultCh <- bundleUpdatesResult {err : err }
262
+ return
263
+ }
264
+
265
+ x509CA := []* x509.Certificate {}
266
+ for _ , ca := range x509CAs {
267
+ x509CA = append (x509CA , ca .Certificate )
268
+ }
269
+
270
+ firstResultCh <- bundleUpdatesResult {
271
+ x509CA : x509CA ,
272
+ jwtKeys : updatedKeys ,
273
+ }
274
+
275
+ for {
276
+ x509CA , jwtKeys , err := authorityStream .RecvLocalBundleUpdate ()
277
+ if err != nil {
278
+ switch {
279
+ case errors .Is (err , io .EOF ):
280
+ // This is normal if the plugin does not support streaming
281
+ // bundle updates.
282
+ case status .Code (err ) == codes .Canceled :
283
+ // This is normal. This client cancels this stream when opening
284
+ // a new stream.
285
+ default :
286
+ u .c .BundleUpdater .LogError (err , "The upstream authority plugin stopped streaming authorities updates prematurely. Please report this bug. Will retry later." )
287
+ }
288
+ return
289
+ }
290
+
291
+ if err := u .c .BundleUpdater .SyncX509Roots (ctx , x509CA ); err != nil {
292
+ u .c .BundleUpdater .LogError (err , "Failed to store X.509 CAs received by the upstream authority plugin." )
293
+ continue
294
+ }
295
+
296
+ if _ , err := u .c .BundleUpdater .AppendJWTKeys (ctx , jwtKeys ); err != nil {
297
+ u .c .BundleUpdater .LogError (err , "Failed to store JWT keys received by the upstream authority plugin." )
298
+ continue
299
+ }
300
+ }
301
+ }
302
+
226
303
type mintX509CAResult struct {
227
304
x509CA []* x509.Certificate
228
305
err error
@@ -233,6 +310,12 @@ type publishJWTKeyResult struct {
233
310
err error
234
311
}
235
312
313
+ type bundleUpdatesResult struct {
314
+ x509CA []* x509.Certificate
315
+ jwtKeys []* common.PublicKey
316
+ err error
317
+ }
318
+
236
319
// streamState manages the state for open streams to the plugin that are
237
320
// receiving bundle updates. It is protected by the respective mutexes in
238
321
// the UpstreamClient.
0 commit comments