@@ -334,6 +334,87 @@ func TestHandler_XForwardedFor(t *testing.T) {
334
334
t .Fatalf ("bad body: %v vs %v" , buf .String (), testcertificate )
335
335
}
336
336
})
337
+
338
+ // Test RFC 9440/8941 Structured Headers "byte sequence" format
339
+ t .Run ("pass_cert_rfc9440_format" , func (t * testing.T ) {
340
+ t .Parallel ()
341
+ testHandler := func (props * vault.HandlerProperties ) http.Handler {
342
+ origHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
343
+ w .WriteHeader (http .StatusOK )
344
+ w .Write ([]byte (base64 .StdEncoding .EncodeToString (r .TLS .PeerCertificates [0 ].Raw )))
345
+ })
346
+ listenerConfig := getListenerConfigForMarshalerTest (goodAddr )
347
+ listenerConfig .XForwardedForClientCertHeader = "X-Forwarded-Tls-Client-Cert"
348
+ listenerConfig .XForwardedForClientCertHeaderDecoders = "BASE64"
349
+ return WrapForwardedForHandler (origHandler , listenerConfig )
350
+ }
351
+
352
+ cluster := vault .NewTestCluster (t , nil , & vault.TestClusterOptions {
353
+ HandlerFunc : HandlerFunc (testHandler ),
354
+ })
355
+ cluster .Start ()
356
+ defer cluster .Cleanup ()
357
+ client := cluster .Cores [0 ].Client
358
+
359
+ req := client .NewRequest ("GET" , "/" )
360
+ req .Headers = make (http.Header )
361
+ req .Headers .Set ("x-forwarded-for" , "5.6.7.8" )
362
+ // Test certificate in RFC 9440/8941 format with leading and trailing colons
363
+ testcertificate := `:MIIDtTCCAp2gAwIBAgIUf+jhKTFBnqSs34II0WS1L4QsbbAwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzQxWhcNMjUwMTA1MTAyODExWjAbMRkwFwYDVQQDExBjZXJ0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxSTRAVnygAftetT8puHflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGnSgMld6ZWRhNheZhA6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmiYYMiIWplidMmMO5NTRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5donyqtnaHuIJGuUdy54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVGB+5+AAGF5iuHC3N2DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABo4H1MIHyMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUm++eHpyM3p708bgZJuRYEdX1o+UwHwYDVR0jBBgwFoAUncSzT/6HMexyuiU9/7EgHu+ok5swOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NhMCEGA1UdEQQaMBiCEGNlcnQuZXhhbXBsZS5jb22HBH8AAAEwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcNAQELBQADggEBABsuvmPSNjjKTVN6itWzdQy+SgMIrwfsX1Yb9Lefkkwmp9ovKFNQxa4DucuCuzXcQrbKwWTfHGgR8ct4rf30xCRoA7dbQWq4aYqNKFWrRaBRAaaYZ/O1ApRTOrXqRx9Eqr0H1BXLsoAq+mWassL8sf6siae+CpwAKqBko5G0dNXq5T4i2LQbmoQSVetIrCJEeMrU+idkuqfV2h1BQKgSEhFDABjFdTCNQDAHsEHsi2M4/jRW9fqEuhHSDfl2n7tkFUI8wTHUUCl7gXwweJ4qtaSXIwKXYzNjxqKHA8Purc1Yfybz4iE1JCROi9fInKlzr5xABq8nb9Qc/J9DIQM+Xmk=:`
364
+ req .Headers .Set ("x-forwarded-tls-client-cert" , testcertificate )
365
+ resp , err := client .RawRequest (req )
366
+ if err != nil {
367
+ t .Fatal (err )
368
+ }
369
+ defer resp .Body .Close ()
370
+ buf := bytes .NewBuffer (nil )
371
+ buf .ReadFrom (resp .Body )
372
+ // Strip the colons for comparison
373
+ expectedCert := testcertificate [1 : len (testcertificate )- 1 ]
374
+ if ! strings .Contains (buf .String (), expectedCert ) {
375
+ t .Fatalf ("bad body: %v vs %v" , buf .String (), expectedCert )
376
+ }
377
+ })
378
+
379
+ // Test that regular base64 without colons still works for compatibility
380
+ t .Run ("pass_cert_regular_base64" , func (t * testing.T ) {
381
+ t .Parallel ()
382
+ testHandler := func (props * vault.HandlerProperties ) http.Handler {
383
+ origHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
384
+ w .WriteHeader (http .StatusOK )
385
+ w .Write ([]byte (base64 .StdEncoding .EncodeToString (r .TLS .PeerCertificates [0 ].Raw )))
386
+ })
387
+ listenerConfig := getListenerConfigForMarshalerTest (goodAddr )
388
+ listenerConfig .XForwardedForClientCertHeader = "X-Forwarded-Tls-Client-Cert"
389
+ listenerConfig .XForwardedForClientCertHeaderDecoders = "BASE64"
390
+ return WrapForwardedForHandler (origHandler , listenerConfig )
391
+ }
392
+
393
+ cluster := vault .NewTestCluster (t , nil , & vault.TestClusterOptions {
394
+ HandlerFunc : HandlerFunc (testHandler ),
395
+ })
396
+ cluster .Start ()
397
+ defer cluster .Cleanup ()
398
+ client := cluster .Cores [0 ].Client
399
+
400
+ req := client .NewRequest ("GET" , "/" )
401
+ req .Headers = make (http.Header )
402
+ req .Headers .Set ("x-forwarded-for" , "5.6.7.8" )
403
+ // Regular base64 without URL encoding and without colons
404
+ testcertificate := `MIIDtTCCAp2gAwIBAgIUf+jhKTFBnqSs34II0WS1L4QsbbAwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzQxWhcNMjUwMTA1MTAyODExWjAbMRkwFwYDVQQDExBjZXJ0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxSTRAVnygAftetT8puHflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGnSgMld6ZWRhNheZhA6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmiYYMiIWplidMmMO5NTRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5donyqtnaHuIJGuUdy54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVGB+5+AAGF5iuHC3N2DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABo4H1MIHyMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUm++eHpyM3p708bgZJuRYEdX1o+UwHwYDVR0jBBgwFoAUncSzT/6HMexyuiU9/7EgHu+ok5swOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NhMCEGA1UdEQQaMBiCEGNlcnQuZXhhbXBsZS5jb22HBH8AAAEwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcNAQELBQADggEBABsuvmPSNjjKTVN6itWzdQy+SgMIrwfsX1Yb9Lefkkwmp9ovKFNQxa4DucuCuzXcQrbKwWTfHGgR8ct4rf30xCRoA7dbQWq4aYqNKFWrRaBRAaaYZ/O1ApRTOrXqRx9Eqr0H1BXLsoAq+mWassL8sf6siae+CpwAKqBko5G0dNXq5T4i2LQbmoQSVetIrCJEeMrU+idkuqfV2h1BQKgSEhFDABjFdTCNQDAHsEHsi2M4/jRW9fqEuhHSDfl2n7tkFUI8wTHUUCl7gXwweJ4qtaSXIwKXYzNjxqKHA8Purc1Yfybz4iE1JCROi9fInKlzr5xABq8nb9Qc/J9DIQM+Xmk=`
405
+ req .Headers .Set ("x-forwarded-tls-client-cert" , testcertificate )
406
+ resp , err := client .RawRequest (req )
407
+ if err != nil {
408
+ t .Fatal (err )
409
+ }
410
+ defer resp .Body .Close ()
411
+ buf := bytes .NewBuffer (nil )
412
+ buf .ReadFrom (resp .Body )
413
+ if ! strings .Contains (buf .String (), testcertificate ) {
414
+ t .Fatalf ("bad body: %v vs %v" , buf .String (), testcertificate )
415
+ }
416
+ })
417
+
337
418
t .Run ("reject invalid IP" , func (t * testing.T ) {
338
419
t .Parallel ()
339
420
testHandler := func (props * vault.HandlerProperties ) http.Handler {
0 commit comments