6
6
"errors"
7
7
"fmt"
8
8
"io"
9
+ "math/rand"
9
10
"net"
10
11
"sort"
11
12
"strconv"
@@ -52,7 +53,8 @@ type Broker struct {
52
53
brokerRequestsInFlight metrics.Counter
53
54
brokerThrottleTime metrics.Histogram
54
55
55
- kerberosAuthenticator GSSAPIKerberosAuth
56
+ kerberosAuthenticator GSSAPIKerberosAuth
57
+ clientSessionReauthenticationTimeMs int64
56
58
}
57
59
58
60
// SASLMechanism specifies the SASL mechanism the client uses to authenticate with the broker
@@ -923,6 +925,13 @@ func (b *Broker) sendWithPromise(rb protocolBody, promise *responsePromise) erro
923
925
return ErrNotConnected
924
926
}
925
927
928
+ if b .clientSessionReauthenticationTimeMs > 0 && currentUnixMilli () > b .clientSessionReauthenticationTimeMs {
929
+ err := b .authenticateViaSASL ()
930
+ if err != nil {
931
+ return err
932
+ }
933
+ }
934
+
926
935
if ! b .conf .Version .IsAtLeast (rb .requiredVersion ()) {
927
936
return ErrUnsupportedVersion
928
937
}
@@ -1263,7 +1272,7 @@ func (b *Broker) sendAndReceiveV1SASLPlainAuth() error {
1263
1272
1264
1273
// Will be decremented in updateIncomingCommunicationMetrics (except error)
1265
1274
b .addRequestInFlightMetrics (1 )
1266
- bytesWritten , err := b .sendSASLPlainAuthClientResponse (correlationID )
1275
+ bytesWritten , resVersion , err := b .sendSASLPlainAuthClientResponse (correlationID )
1267
1276
b .updateOutgoingCommunicationMetrics (bytesWritten )
1268
1277
1269
1278
if err != nil {
@@ -1274,7 +1283,8 @@ func (b *Broker) sendAndReceiveV1SASLPlainAuth() error {
1274
1283
1275
1284
b .correlationID ++
1276
1285
1277
- bytesRead , err := b .receiveSASLServerResponse (& SaslAuthenticateResponse {}, correlationID )
1286
+ res := & SaslAuthenticateResponse {}
1287
+ bytesRead , err := b .receiveSASLServerResponse (res , correlationID , resVersion )
1278
1288
b .updateIncomingCommunicationMetrics (bytesRead , time .Since (requestTime ))
1279
1289
1280
1290
// With v1 sasl we get an error message set in the response we can return
@@ -1288,6 +1298,10 @@ func (b *Broker) sendAndReceiveV1SASLPlainAuth() error {
1288
1298
return nil
1289
1299
}
1290
1300
1301
+ func currentUnixMilli () int64 {
1302
+ return time .Now ().UnixNano () / int64 (time .Millisecond )
1303
+ }
1304
+
1291
1305
// sendAndReceiveSASLOAuth performs the authentication flow as described by KIP-255
1292
1306
// https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=75968876
1293
1307
func (b * Broker ) sendAndReceiveSASLOAuth (provider AccessTokenProvider ) error {
@@ -1327,7 +1341,7 @@ func (b *Broker) sendClientMessage(message []byte) (bool, error) {
1327
1341
b .addRequestInFlightMetrics (1 )
1328
1342
correlationID := b .correlationID
1329
1343
1330
- bytesWritten , err := b .sendSASLOAuthBearerClientMessage (message , correlationID )
1344
+ bytesWritten , resVersion , err := b .sendSASLOAuthBearerClientMessage (message , correlationID )
1331
1345
b .updateOutgoingCommunicationMetrics (bytesWritten )
1332
1346
if err != nil {
1333
1347
b .addRequestInFlightMetrics (- 1 )
@@ -1337,7 +1351,7 @@ func (b *Broker) sendClientMessage(message []byte) (bool, error) {
1337
1351
b .correlationID ++
1338
1352
1339
1353
res := & SaslAuthenticateResponse {}
1340
- bytesRead , err := b .receiveSASLServerResponse (res , correlationID )
1354
+ bytesRead , err := b .receiveSASLServerResponse (res , correlationID , resVersion )
1341
1355
1342
1356
requestLatency := time .Since (requestTime )
1343
1357
b .updateIncomingCommunicationMetrics (bytesRead , requestLatency )
@@ -1464,7 +1478,7 @@ func (b *Broker) sendAndReceiveSASLSCRAMv1() error {
1464
1478
}
1465
1479
1466
1480
func (b * Broker ) sendSaslAuthenticateRequest (correlationID int32 , msg []byte ) (int , error ) {
1467
- rb := & SaslAuthenticateRequest { msg }
1481
+ rb := b . createSaslAuthenticateRequest ( msg )
1468
1482
req := & request {correlationID : correlationID , clientID : b .conf .ClientID , body : rb }
1469
1483
buf , err := encode (req , b .conf .MetricRegistry )
1470
1484
if err != nil {
@@ -1474,6 +1488,15 @@ func (b *Broker) sendSaslAuthenticateRequest(correlationID int32, msg []byte) (i
1474
1488
return b .write (buf )
1475
1489
}
1476
1490
1491
+ func (b * Broker ) createSaslAuthenticateRequest (msg []byte ) * SaslAuthenticateRequest {
1492
+ authenticateRequest := SaslAuthenticateRequest {SaslAuthBytes : msg }
1493
+ if b .conf .Version .IsAtLeast (V2_2_0_0 ) {
1494
+ authenticateRequest .Version = 1
1495
+ }
1496
+
1497
+ return & authenticateRequest
1498
+ }
1499
+
1477
1500
func (b * Broker ) receiveSaslAuthenticateResponse (correlationID int32 ) ([]byte , error ) {
1478
1501
buf := make ([]byte , responseLengthSize + correlationIDSize )
1479
1502
_ , err := b .readFull (buf )
@@ -1538,32 +1561,34 @@ func mapToString(extensions map[string]string, keyValSep string, elemSep string)
1538
1561
return strings .Join (buf , elemSep )
1539
1562
}
1540
1563
1541
- func (b * Broker ) sendSASLPlainAuthClientResponse (correlationID int32 ) (int , error ) {
1564
+ func (b * Broker ) sendSASLPlainAuthClientResponse (correlationID int32 ) (int , int16 , error ) {
1542
1565
authBytes := []byte (b .conf .Net .SASL .AuthIdentity + "\x00 " + b .conf .Net .SASL .User + "\x00 " + b .conf .Net .SASL .Password )
1543
- rb := & SaslAuthenticateRequest { authBytes }
1566
+ rb := b . createSaslAuthenticateRequest ( authBytes )
1544
1567
req := & request {correlationID : correlationID , clientID : b .conf .ClientID , body : rb }
1545
1568
buf , err := encode (req , b .conf .MetricRegistry )
1546
1569
if err != nil {
1547
- return 0 , err
1570
+ return 0 , rb . Version , err
1548
1571
}
1549
1572
1550
- return b .write (buf )
1573
+ write , err := b .write (buf )
1574
+ return write , rb .Version , err
1551
1575
}
1552
1576
1553
- func (b * Broker ) sendSASLOAuthBearerClientMessage (initialResp []byte , correlationID int32 ) (int , error ) {
1554
- rb := & SaslAuthenticateRequest { initialResp }
1577
+ func (b * Broker ) sendSASLOAuthBearerClientMessage (initialResp []byte , correlationID int32 ) (int , int16 , error ) {
1578
+ rb := b . createSaslAuthenticateRequest ( initialResp )
1555
1579
1556
1580
req := & request {correlationID : correlationID , clientID : b .conf .ClientID , body : rb }
1557
1581
1558
1582
buf , err := encode (req , b .conf .MetricRegistry )
1559
1583
if err != nil {
1560
- return 0 , err
1584
+ return 0 , rb . version (), err
1561
1585
}
1562
1586
1563
- return b .write (buf )
1587
+ write , err := b .write (buf )
1588
+ return write , rb .version (), err
1564
1589
}
1565
1590
1566
- func (b * Broker ) receiveSASLServerResponse (res * SaslAuthenticateResponse , correlationID int32 ) (int , error ) {
1591
+ func (b * Broker ) receiveSASLServerResponse (res * SaslAuthenticateResponse , correlationID int32 , resVersion int16 ) (int , error ) {
1567
1592
buf := make ([]byte , responseLengthSize + correlationIDSize )
1568
1593
bytesRead , err := b .readFull (buf )
1569
1594
if err != nil {
@@ -1587,7 +1612,7 @@ func (b *Broker) receiveSASLServerResponse(res *SaslAuthenticateResponse, correl
1587
1612
return bytesRead , err
1588
1613
}
1589
1614
1590
- if err := versionedDecode (buf , res , 0 ); err != nil {
1615
+ if err := versionedDecode (buf , res , resVersion ); err != nil {
1591
1616
return bytesRead , err
1592
1617
}
1593
1618
@@ -1599,6 +1624,21 @@ func (b *Broker) receiveSASLServerResponse(res *SaslAuthenticateResponse, correl
1599
1624
return bytesRead , err
1600
1625
}
1601
1626
1627
+ if res .SessionLifetimeMs > 0 {
1628
+ // Follows the Java Kafka implementation from SaslClientAuthenticator.ReauthInfo#setAuthenticationEndAndSessionReauthenticationTimes
1629
+ // pick a random percentage between 85% and 95% for session re-authentication
1630
+ positiveSessionLifetimeMs := res .SessionLifetimeMs
1631
+ authenticationEndMs := currentUnixMilli ()
1632
+ pctWindowFactorToTakeNetworkLatencyAndClockDriftIntoAccount := 0.85
1633
+ pctWindowJitterToAvoidReauthenticationStormAcrossManyChannelsSimultaneously := 0.10
1634
+ pctToUse := pctWindowFactorToTakeNetworkLatencyAndClockDriftIntoAccount + rand .Float64 ()* pctWindowJitterToAvoidReauthenticationStormAcrossManyChannelsSimultaneously
1635
+ sessionLifetimeMsToUse := int64 (float64 (positiveSessionLifetimeMs ) * pctToUse )
1636
+ DebugLogger .Printf ("Session expiration in %d ms and session re-authentication on or after %d ms" , positiveSessionLifetimeMs , sessionLifetimeMsToUse )
1637
+ b .clientSessionReauthenticationTimeMs = authenticationEndMs + sessionLifetimeMsToUse
1638
+ } else {
1639
+ b .clientSessionReauthenticationTimeMs = 0
1640
+ }
1641
+
1602
1642
return bytesRead , nil
1603
1643
}
1604
1644
0 commit comments