@@ -32,6 +32,8 @@ import (
32
32
const (
33
33
profileConfigMapLabelKey = "olm.openshift.io/pprof"
34
34
pprofSecretName = "pprof-cert"
35
+ validity = 24 * time .Hour
36
+ minValidity = 15 * time .Minute
35
37
)
36
38
37
39
var (
@@ -74,7 +76,7 @@ func newCmd() *cobra.Command {
74
76
Short : "Retrieves the pprof data from a URL and stores it in a configMap." ,
75
77
Long : `The collect-profiles command makes https requests against pprof URLs
76
78
provided as arguments and stores that information in immutable configMaps.
77
-
79
+
78
80
# Example command with multiple arguments
79
81
./collect-profiles -n - openshift-operator-lifecycle-manager \
80
82
- --config-mount-path \
@@ -143,15 +145,23 @@ func newCmd() *cobra.Command {
143
145
certPath := filepath .Join (certMountPath , corev1 .TLSCertKey )
144
146
keyPath := filepath .Join (certMountPath , corev1 .TLSPrivateKeyKey )
145
147
146
- if err := verifyCertAndKeyExist (certPath , keyPath ); err != nil {
148
+ var tlsCert * tls.Certificate
149
+ if tlsCert , err = verifyCertAndKey (certPath , keyPath ); err != nil {
147
150
logrus .Infof ("error verifying provided cert and key: %v" , err )
148
151
logrus .Info ("generating a new cert and key" )
149
- return populateServingCert (cmd .Context (), cfg .Client )
152
+ if tlsCert , err = populateServingCert (cmd .Context (), cfg .Client ); err != nil {
153
+ return err
154
+ }
155
+ // Continue with new certificate/keypair
150
156
}
151
157
152
- httpClient , err := getHttpClient (certPath , keyPath )
153
- if err != nil {
154
- return err
158
+ httpClient := & http.Client {
159
+ Transport : & http.Transport {
160
+ TLSClientConfig : & tls.Config {
161
+ InsecureSkipVerify : true ,
162
+ Certificates : []tls.Certificate {* tlsCert },
163
+ },
164
+ },
155
165
}
156
166
157
167
// Track successfully created configMaps by generateName for each endpoint being scrapped.
@@ -205,29 +215,49 @@ func newCmd() *cobra.Command {
205
215
return fmt .Errorf ("error deleting existing pprof configMaps: %v" , errs )
206
216
}
207
217
208
- // Update serving cert after a successful run
209
- return populateServingCert (cmd .Context (), cfg .Client )
218
+ return nil
210
219
},
211
220
}
212
221
}
213
222
214
- func verifyCertAndKeyExist (certPath , keyPath string ) error {
223
+ func verifyCertAndKey (certPath , keyPath string ) ( * tls. Certificate , error ) {
215
224
fi , err := os .Stat (certPath )
216
225
if err != nil {
217
- return err
226
+ return nil , err
218
227
}
219
228
if fi .Size () == 0 {
220
- return fmt .Errorf ("cert file should not be empty" )
229
+ return nil , fmt .Errorf ("cert file should not be empty" )
221
230
}
222
231
223
232
fi , err = os .Stat (keyPath )
224
233
if err != nil {
225
- return err
234
+ return nil , err
226
235
}
227
236
if fi .Size () == 0 {
228
- return fmt .Errorf ("key file should not be empty" )
237
+ return nil , fmt .Errorf ("key file should not be empty" )
238
+ }
239
+
240
+ tlsCert , err := tls .LoadX509KeyPair (certPath , keyPath )
241
+ if err != nil {
242
+ return nil , err
243
+ }
244
+
245
+ x509Cert , err := x509 .ParseCertificate (tlsCert .Certificate [0 ])
246
+ if err != nil {
247
+ return nil , err
248
+ }
249
+
250
+ now := time .Now ()
251
+ if x509Cert .NotAfter .Before (now ) {
252
+ return nil , fmt .Errorf ("certificate has expired" )
253
+ }
254
+ // Since this cron runs every 15 minutes, we assume that the job takes less
255
+ // than 15 minutes, so make sure there's still 15 minutes of validity left
256
+ if x509Cert .NotAfter .Before (now .Add (minValidity )) {
257
+ return nil , fmt .Errorf ("certificate expires in less than 15 minutes" )
229
258
}
230
- return nil
259
+
260
+ return & tlsCert , nil
231
261
}
232
262
233
263
func separateConfigMapsIntoNewestAndExpired (configMaps []corev1.ConfigMap ) (newestCMs []corev1.ConfigMap , expiredCMs []corev1.ConfigMap ) {
@@ -279,21 +309,6 @@ func newArgument(s string) (*argument, error) {
279
309
return arg , nil
280
310
}
281
311
282
- func getHttpClient (certPath , keyPath string ) (* http.Client , error ) {
283
- cert , err := tls .LoadX509KeyPair (certPath , keyPath )
284
- if err != nil {
285
- return nil , err
286
- }
287
- return & http.Client {
288
- Transport : & http.Transport {
289
- TLSClientConfig : & tls.Config {
290
- InsecureSkipVerify : true ,
291
- Certificates : []tls.Certificate {cert },
292
- },
293
- },
294
- }, nil
295
- }
296
-
297
312
func requestURLBody (httpClient * http.Client , u * url.URL ) ([]byte , error ) {
298
313
response , err := httpClient .Do (& http.Request {
299
314
Method : http .MethodGet ,
@@ -315,56 +330,67 @@ func requestURLBody(httpClient *http.Client, u *url.URL) ([]byte, error) {
315
330
return b .Bytes (), nil
316
331
}
317
332
318
- func populateServingCert (ctx context.Context , client client.Client ) error {
333
+ func populateServingCert (ctx context.Context , client client.Client ) ( * tls. Certificate , error ) {
319
334
secret := & corev1.Secret {}
320
335
err := client .Get (ctx , types.NamespacedName {Namespace : namespace , Name : pprofSecretName }, secret )
321
336
if err != nil {
322
- return err
337
+ return nil , err
323
338
}
324
339
325
- cert , privateKey , err := getCertAndKey ()
340
+ certPEMBytes , privKeyPEMBytes , err := generateCertAndKey ()
326
341
if err != nil {
327
- return err
342
+ return nil , err
328
343
}
329
344
330
- secret .Data [corev1 .TLSCertKey ] = cert
331
- secret .Data [corev1 .TLSPrivateKeyKey ] = privateKey
332
- return client .Update (ctx , secret )
345
+ secret .Data [corev1 .TLSCertKey ] = certPEMBytes
346
+ secret .Data [corev1 .TLSPrivateKeyKey ] = privKeyPEMBytes
347
+
348
+ if err = client .Update (ctx , secret ); err != nil {
349
+ return nil , err
350
+ }
351
+ // Create tlsCert for client use
352
+ tlsCert , err := tls .X509KeyPair (certPEMBytes , privKeyPEMBytes )
353
+ if err != nil {
354
+ return nil , err
355
+ }
356
+ return & tlsCert , nil
333
357
}
334
358
335
- func getCertAndKey () ([]byte , []byte , error ) {
359
+ func generateCertAndKey () ([]byte , []byte , error ) {
360
+ now := time .Now ()
336
361
cert := & x509.Certificate {
337
362
SerialNumber : big .NewInt (1658 ),
338
363
Subject : pkix.Name {
339
364
Organization : []string {"Red Hat, Inc." },
340
365
},
341
- NotBefore : time . Now () ,
342
- NotAfter : time . Now (). Add (time . Hour ),
366
+ NotBefore : now ,
367
+ NotAfter : now . Add (validity ),
343
368
ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
344
369
KeyUsage : x509 .KeyUsageDigitalSignature ,
345
370
}
346
371
347
- caPrivKey , err := rsa .GenerateKey (rand .Reader , 4096 )
372
+ keyPair , err := rsa .GenerateKey (rand .Reader , 4096 )
348
373
if err != nil {
349
374
return nil , nil , err
350
375
}
351
376
352
- caBytes , err := x509 .CreateCertificate (rand .Reader , cert , cert , & caPrivKey .PublicKey , caPrivKey )
377
+ // Generate a DER-encoded self-signed certificate
378
+ certDER , err := x509 .CreateCertificate (rand .Reader , cert , cert , & keyPair .PublicKey , keyPair )
353
379
if err != nil {
354
380
return nil , nil , err
355
381
}
356
382
357
- caPEM := new (bytes.Buffer )
358
- pem .Encode (caPEM , & pem.Block {
383
+ certPEM := new (bytes.Buffer )
384
+ pem .Encode (certPEM , & pem.Block {
359
385
Type : "CERTIFICATE" ,
360
- Bytes : caBytes ,
386
+ Bytes : certDER ,
361
387
})
362
388
363
- caPrivKeyPEM := new (bytes.Buffer )
364
- pem .Encode (caPrivKeyPEM , & pem.Block {
389
+ privKeyPEM := new (bytes.Buffer )
390
+ pem .Encode (privKeyPEM , & pem.Block {
365
391
Type : "RSA PRIVATE KEY" ,
366
- Bytes : x509 .MarshalPKCS1PrivateKey (caPrivKey ),
392
+ Bytes : x509 .MarshalPKCS1PrivateKey (keyPair ),
367
393
})
368
394
369
- return caPEM .Bytes (), caPrivKeyPEM .Bytes (), nil
395
+ return certPEM .Bytes (), privKeyPEM .Bytes (), nil
370
396
}
0 commit comments