| 3468 | } |
| 3469 | |
| 3470 | func TestTLSHandshakeFirstEOFAfterHandshake(t *testing.T) { |
| 3471 | // Simulate a proxy (like nginx) that completes the TLS handshake |
| 3472 | // but then closes the connection because the client certificate |
| 3473 | // is not trusted. The client should get a descriptive error |
| 3474 | // instead of a bare EOF. |
| 3475 | |
| 3476 | tc := &server.TLSConfigOpts{ |
| 3477 | CertFile: "./configs/certs/server.pem", |
| 3478 | KeyFile: "./configs/certs/key.pem", |
| 3479 | } |
| 3480 | tlsConf, err := server.GenTLSConfig(tc) |
| 3481 | if err != nil { |
| 3482 | t.Fatalf("Can't build TLSConfig: %v", err) |
| 3483 | } |
| 3484 | tlsConf.ServerName = "localhost" |
| 3485 | |
| 3486 | l, err := net.Listen("tcp", "127.0.0.1:0") |
| 3487 | if err != nil { |
| 3488 | t.Fatalf("Could not listen: %v", err) |
| 3489 | } |
| 3490 | defer l.Close() |
| 3491 | |
| 3492 | addr := l.Addr().(*net.TCPAddr) |
| 3493 | |
| 3494 | // Mock server: complete TLS handshake then immediately close, |
| 3495 | // simulating nginx rejecting an untrusted client cert. |
| 3496 | go func() { |
| 3497 | conn, err := l.Accept() |
| 3498 | if err != nil { |
| 3499 | return |
| 3500 | } |
| 3501 | tlsConn := tls.Server(conn, tlsConf) |
| 3502 | tlsConn.Handshake() |
| 3503 | // Close without sending INFO — this is what nginx does |
| 3504 | // when it rejects the client certificate. |
| 3505 | tlsConn.Close() |
| 3506 | }() |
| 3507 | |
| 3508 | _, err = nats.Connect( |
| 3509 | fmt.Sprintf("tls://localhost:%d", addr.Port), |
| 3510 | nats.RootCAs("./configs/certs/ca.pem"), |
| 3511 | nats.TLSHandshakeFirst(), |
| 3512 | ) |
| 3513 | if err == nil { |
| 3514 | t.Fatal("Expected error, got nil") |
| 3515 | } |
| 3516 | // Should be detectable as a TLS error. |
| 3517 | if !errors.Is(err, nats.ErrTLS) { |
| 3518 | t.Fatalf("Expected error to wrap nats.ErrTLS, got: %v", err) |
| 3519 | } |
| 3520 | // Should still wrap io.EOF for backwards compatibility. |
| 3521 | if !errors.Is(err, io.EOF) { |
| 3522 | t.Fatalf("Expected error to wrap io.EOF, got: %v", err) |
| 3523 | } |
| 3524 | } |
| 3525 | |
| 3526 | func TestTLSHandshakeFirstMTLSReject(t *testing.T) { |
| 3527 | // Test that when the NATS server itself does mTLS verification |