@@ -15,8 +15,10 @@ package config
15
15
16
16
import (
17
17
"context"
18
+ "crypto/md5"
18
19
"crypto/tls"
19
20
"crypto/x509"
21
+ "encoding/hex"
20
22
"encoding/json"
21
23
"errors"
22
24
"fmt"
@@ -77,7 +79,7 @@ var invalidHTTPClientConfigs = []struct {
77
79
},
78
80
{
79
81
httpClientConfigFile : "testdata/http.conf.empty.bad.yml" ,
80
- errMsg : "at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured" ,
82
+ errMsg : "at most one of basic_auth, digest_auth, oauth2, bearer_token & bearer_token_file must be configured" ,
81
83
},
82
84
{
83
85
httpClientConfigFile : "testdata/http.conf.basic-auth.too-much.bad.yaml" ,
@@ -97,11 +99,15 @@ var invalidHTTPClientConfigs = []struct {
97
99
},
98
100
{
99
101
httpClientConfigFile : "testdata/http.conf.basic-auth-and-auth-creds.too-much.bad.yaml" ,
100
- errMsg : "at most one of basic_auth, oauth2 & authorization must be configured" ,
102
+ errMsg : "at most one of basic_auth, digest_auth, oauth2 & authorization must be configured" ,
101
103
},
102
104
{
103
105
httpClientConfigFile : "testdata/http.conf.basic-auth-and-oauth2.too-much.bad.yaml" ,
104
- errMsg : "at most one of basic_auth, oauth2 & authorization must be configured" ,
106
+ errMsg : "at most one of basic_auth, digest_auth, oauth2 & authorization must be configured" ,
107
+ },
108
+ {
109
+ httpClientConfigFile : "testdata/http.conf.basic-auth-and-digest.too-much.bad.yaml" ,
110
+ errMsg : "at most one of basic_auth, digest_auth, oauth2 & authorization must be configured" ,
105
111
},
106
112
{
107
113
httpClientConfigFile : "testdata/http.conf.auth-creds-no-basic.bad.yaml" ,
@@ -312,6 +318,31 @@ func TestNewClientFromConfig(t *testing.T) {
312
318
fmt .Fprint (w , ExpectedMessage )
313
319
}
314
320
},
321
+ }, {
322
+ clientConfig : HTTPClientConfig {
323
+ BasicAuth : & BasicAuth {
324
+ Username : ExpectedUsername ,
325
+ Password : ExpectedPassword ,
326
+ },
327
+ TLSConfig : TLSConfig {
328
+ CAFile : TLSCAChainPath ,
329
+ CertFile : ClientCertificatePath ,
330
+ KeyFile : ClientKeyNoPassPath ,
331
+ ServerName : "" ,
332
+ InsecureSkipVerify : false },
333
+ },
334
+ handler : func (w http.ResponseWriter , r * http.Request ) {
335
+ username , password , ok := r .BasicAuth ()
336
+ if ! ok {
337
+ fmt .Fprintf (w , "The Authorization header wasn't set" )
338
+ } else if ExpectedUsername != username {
339
+ fmt .Fprintf (w , "The expected username (%s) differs from the obtained username (%s)." , ExpectedUsername , username )
340
+ } else if ExpectedPassword != password {
341
+ fmt .Fprintf (w , "The expected password (%s) differs from the obtained password (%s)." , ExpectedPassword , password )
342
+ } else {
343
+ fmt .Fprint (w , ExpectedMessage )
344
+ }
345
+ },
315
346
}, {
316
347
clientConfig : HTTPClientConfig {
317
348
Authorization : & Authorization {
@@ -335,7 +366,7 @@ func TestNewClientFromConfig(t *testing.T) {
335
366
},
336
367
}, {
337
368
clientConfig : HTTPClientConfig {
338
- BasicAuth : & BasicAuth {
369
+ DigestAuth : & DigestAuth {
339
370
Username : ExpectedUsername ,
340
371
Password : ExpectedPassword ,
341
372
},
@@ -347,14 +378,61 @@ func TestNewClientFromConfig(t *testing.T) {
347
378
InsecureSkipVerify : false },
348
379
},
349
380
handler : func (w http.ResponseWriter , r * http.Request ) {
350
- username , password , ok := r .BasicAuth ()
351
- if ! ok {
352
- fmt .Fprintf (w , "The Authorization header wasn't set" )
353
- } else if ExpectedUsername != username {
354
- fmt .Fprintf (w , "The expected username (%s) differs from the obtained username (%s)." , ExpectedUsername , username )
355
- } else if ExpectedPassword != password {
356
- fmt .Fprintf (w , "The expected password (%s) differs from the obtained password (%s)." , ExpectedPassword , password )
381
+ // Example server response header:
382
+ // WWW-Authenticate: Digest realm="prometheus", nonce="43568ca162f46c3bcc57ecae193b3159", qop="auth", opaque="3bc9f19d8195721e24469ff255750f8c", algorithm=MD5, stale=FALSE
383
+ //
384
+ // Example client request header:
385
+ // Authorization: Digest username="foo", realm="prometheus", nonce="43568ca162f46c3bcc57ecae193b3159", uri="/", cnonce="NDA2M2JmYzQ2YTQ4OTQ0OTQ1NzE0NmI3ZmYyY2YyNzU=", nc=00000001, qop=auth, response="fe543d7eeb2d2f0aba8d100a1f076909", opaque="3bc9f19d8195721e24469ff255750f8c", algorithm=MD5
386
+
387
+ const (
388
+ nonce = "43568ca162f46c3bcc57ecae193b3159"
389
+ realm = "prometheus"
390
+ )
391
+
392
+ if authHeader := r .Header .Get ("Authorization" ); authHeader == "" {
393
+ // first request
394
+ w .Header ().Set ("www-authenticate" , "Digest realm=\" " + realm + "\" , nonce=\" " + nonce + "\" , qop=\" auth\" , opaque=\" 3bc9f19d8195721e24469ff255750f8c\" , algorithm=MD5, stale=FALSE" )
395
+ w .WriteHeader (401 )
357
396
} else {
397
+ // second, authenticated request
398
+ if ! strings .HasPrefix (authHeader , "Digest" ) {
399
+ fmt .Fprint (w , "Request does not contain a valid digest auth header" )
400
+ return
401
+ }
402
+
403
+ digestComponents := make (map [string ]string )
404
+ for _ , p := range strings .Split (authHeader , ", " )[1 :] {
405
+ kvParts := strings .Split (p , "=" )
406
+ digestComponents [kvParts [0 ]] = strings .TrimSpace (strings .Trim (kvParts [1 ], "\" " ))
407
+ }
408
+
409
+ if v := digestComponents ["realm" ]; v != realm {
410
+ fmt .Fprintf (w , "Digest auth with wrong realm (%s)" , v )
411
+ return
412
+ }
413
+ if v := digestComponents ["nonce" ]; v != nonce {
414
+ fmt .Fprintf (w , "Digest auth with wrong nonce (%s)" , v )
415
+ return
416
+ }
417
+
418
+ hashMD5 := func (s string ) string {
419
+ hasher := md5 .New ()
420
+ hasher .Write ([]byte (s ))
421
+ return hex .EncodeToString (hasher .Sum (nil ))
422
+ }
423
+
424
+ hash1Str := fmt .Sprintf ("%s:%s:%s" , ExpectedUsername , realm , ExpectedPassword )
425
+ hash1 := hashMD5 (hash1Str )
426
+ hash2Str := fmt .Sprintf ("GET:%s" , digestComponents ["uri" ])
427
+ hash2 := hashMD5 (hash2Str )
428
+ responseStr := fmt .Sprintf ("%s:%s:%s:%s:%s:%s" , hash1 , nonce , digestComponents ["nc" ], digestComponents ["cnonce" ], digestComponents ["qop" ], hash2 )
429
+ response := hashMD5 (responseStr )
430
+
431
+ if response != digestComponents ["response" ] {
432
+ fmt .Fprintf (w , "Digest auth failed, response hashes didn't match" )
433
+ return
434
+ }
435
+
358
436
fmt .Fprint (w , ExpectedMessage )
359
437
}
360
438
},
0 commit comments