@@ -1174,3 +1174,77 @@ func TestDialerChecksSubjectAlternativeNameAndFails(t *testing.T) {
11741174 t .Fatal ("want error containing `tls: failed to verify certificate`. Got: " , err )
11751175 }
11761176}
1177+
1178+ func TestDialerRefreshesAfterClientCertificateError (t * testing.T ) {
1179+ inst := mock .NewFakeCSQLInstanceWithSan (
1180+ "my-project" , "my-region" , "my-instance" , []string {"db.example.com" },
1181+ mock .WithDNS ("db.example.com" ),
1182+ mock .WithServerCAMode ("GOOGLE_MANAGED_CAS_CA" ),
1183+ )
1184+
1185+ d := setupDialer (t , setupConfig {
1186+ skipServer : true ,
1187+ testInstance : inst ,
1188+ reqs : []* mock.Request {
1189+ mock .InstanceGetSuccess (inst , 2 ),
1190+ mock .CreateEphemeralSuccess (inst , 2 ),
1191+ },
1192+ dialerOptions : []Option {
1193+ WithTokenSource (mock.EmptyTokenSource {}),
1194+ WithDebugLogger (& dialerTestLogger {t : t }),
1195+ //WithLazyRefresh(),
1196+ // Note: this succeeds with lazy refresh, but fails with lazy.
1197+ // because dialer.ForceRefresh does not block connections while the
1198+ // refresh is in progress.
1199+ },
1200+ })
1201+ cancel1 := mock .StartServerProxy (t , inst )
1202+ t .Log ("First attempt..." )
1203+ testSuccessfulDial (
1204+ context .Background (), t , d ,
1205+ "my-project:my-region:my-instance" ,
1206+ )
1207+ t .Log ("First attempt OK. Resetting client cert." )
1208+
1209+ // Close the server
1210+ cancel1 ()
1211+
1212+ mock .RotateClientCA (inst )
1213+ time .Sleep (2 * time .Second )
1214+
1215+ // Recreate the instance, which generates new server certificates
1216+ // Start the server with new certificates
1217+ cancel2 := mock .StartServerProxy (t , inst )
1218+ defer cancel2 ()
1219+
1220+ // Dial a second time. We expect no error on dial, but TLS error on read.
1221+ conn , err := d .Dial (context .Background (), "my-project:my-region:my-instance" )
1222+ if err != nil {
1223+ t .Fatal ("Should be no certificate error after, got " , err )
1224+ }
1225+
1226+ // Expect an error on read. This should trigger the dialer to refresh.
1227+ _ , err = io .ReadAll (conn )
1228+ if err != nil {
1229+ t .Log ("Got error on read as expected." , err )
1230+ } else {
1231+ t .Fatal ("Want read error, got no error" )
1232+ }
1233+
1234+ time .Sleep (2 * time .Second )
1235+ // Dial again. This should occur after the refresh has completed.
1236+ t .Log ("Third attempt..." )
1237+ testSuccessfulDial (
1238+ context .Background (), t , d ,
1239+ "my-project:my-region:my-instance" ,
1240+ )
1241+ t .Log ("Third attempt OK." )
1242+ }
1243+
1244+ type dialerTestLogger struct {
1245+ t * testing.T
1246+ }
1247+
1248+ func (l * dialerTestLogger ) Debugf (f string , args ... interface {}) {
1249+ l .t .Logf (f , args ... )
1250+ }
0 commit comments