@@ -16,17 +16,22 @@ package integration
16
16
17
17
import (
18
18
"bytes"
19
+ "crypto/tls"
19
20
"fmt"
21
+ "io/ioutil"
20
22
"math/rand"
21
23
"os"
22
24
"reflect"
25
+ "strings"
23
26
"testing"
24
27
"time"
25
28
29
+ "github.com/coreos/etcd/clientv3"
26
30
"github.com/coreos/etcd/etcdserver/api/v3rpc"
27
31
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
28
32
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
29
33
"github.com/coreos/etcd/pkg/testutil"
34
+
30
35
"golang.org/x/net/context"
31
36
"google.golang.org/grpc"
32
37
"google.golang.org/grpc/metadata"
@@ -1374,6 +1379,204 @@ func TestTLSGRPCAcceptSecureAll(t *testing.T) {
1374
1379
}
1375
1380
}
1376
1381
1382
+ // TestTLSReloadAtomicReplace ensures server reloads expired/valid certs
1383
+ // when all certs are atomically replaced by directory renaming.
1384
+ // And expects server to reject client requests, and vice versa.
1385
+ func TestTLSReloadAtomicReplace (t * testing.T ) {
1386
+ defer testutil .AfterTest (t )
1387
+
1388
+ // clone valid,expired certs to separate directories for atomic renaming
1389
+ vDir , err := ioutil .TempDir (os .TempDir (), "fixtures-valid" )
1390
+ if err != nil {
1391
+ t .Fatal (err )
1392
+ }
1393
+ defer os .RemoveAll (vDir )
1394
+ ts , err := copyTLSFiles (testTLSInfo , vDir )
1395
+ if err != nil {
1396
+ t .Fatal (err )
1397
+ }
1398
+ eDir , err := ioutil .TempDir (os .TempDir (), "fixtures-expired" )
1399
+ if err != nil {
1400
+ t .Fatal (err )
1401
+ }
1402
+ defer os .RemoveAll (eDir )
1403
+ if _ , err = copyTLSFiles (testTLSInfoExpired , eDir ); err != nil {
1404
+ t .Fatal (err )
1405
+ }
1406
+
1407
+ tDir , err := ioutil .TempDir (os .TempDir (), "fixtures" )
1408
+ if err != nil {
1409
+ t .Fatal (err )
1410
+ }
1411
+ os .RemoveAll (tDir )
1412
+ defer os .RemoveAll (tDir )
1413
+
1414
+ // start with valid certs
1415
+ clus := NewClusterV3 (t , & ClusterConfig {Size : 1 , PeerTLS : & ts , ClientTLS : & ts })
1416
+ defer clus .Terminate (t )
1417
+
1418
+ // concurrent client dialing while certs transition from valid to expired
1419
+ errc := make (chan error , 1 )
1420
+ go func () {
1421
+ for {
1422
+ cc , err := ts .ClientConfig ()
1423
+ if err != nil {
1424
+ if os .IsNotExist (err ) {
1425
+ // from concurrent renaming
1426
+ continue
1427
+ }
1428
+ t .Fatal (err )
1429
+ }
1430
+ cli , cerr := clientv3 .New (clientv3.Config {
1431
+ Endpoints : []string {clus .Members [0 ].GRPCAddr ()},
1432
+ DialTimeout : time .Second ,
1433
+ TLS : cc ,
1434
+ })
1435
+ if cerr != nil {
1436
+ errc <- cerr
1437
+ return
1438
+ }
1439
+ cli .Close ()
1440
+ }
1441
+ }()
1442
+
1443
+ // replace certs directory with expired ones
1444
+ if err = os .Rename (vDir , tDir ); err != nil {
1445
+ t .Fatal (err )
1446
+ }
1447
+ if err = os .Rename (eDir , vDir ); err != nil {
1448
+ t .Fatal (err )
1449
+ }
1450
+
1451
+ // after rename,
1452
+ // 'vDir' contains expired certs
1453
+ // 'tDir' contains valid certs
1454
+ // 'eDir' does not exist
1455
+
1456
+ select {
1457
+ case err = <- errc :
1458
+ if err != grpc .ErrClientConnTimeout {
1459
+ t .Fatalf ("expected %v, got %v" , grpc .ErrClientConnTimeout , err )
1460
+ }
1461
+ case <- time .After (7 * time .Second ):
1462
+ t .Fatal ("failed to receive dial timeout error" )
1463
+ }
1464
+
1465
+ // now, replace expired certs back with valid ones
1466
+ if err = os .Rename (tDir , eDir ); err != nil {
1467
+ t .Fatal (err )
1468
+ }
1469
+ if err = os .Rename (vDir , tDir ); err != nil {
1470
+ t .Fatal (err )
1471
+ }
1472
+ if err = os .Rename (eDir , vDir ); err != nil {
1473
+ t .Fatal (err )
1474
+ }
1475
+
1476
+ // new incoming client request should trigger
1477
+ // listener to reload valid certs
1478
+ var tls * tls.Config
1479
+ tls , err = ts .ClientConfig ()
1480
+ if err != nil {
1481
+ t .Fatal (err )
1482
+ }
1483
+ var cl * clientv3.Client
1484
+ cl , err = clientv3 .New (clientv3.Config {
1485
+ Endpoints : []string {clus .Members [0 ].GRPCAddr ()},
1486
+ DialTimeout : time .Second ,
1487
+ TLS : tls ,
1488
+ })
1489
+ if err != nil {
1490
+ t .Fatalf ("expected no error, got %v" , err )
1491
+ }
1492
+ cl .Close ()
1493
+ }
1494
+
1495
+ // TestTLSReloadCopy ensures server reloads expired/valid certs
1496
+ // when new certs are copied over, one by one. And expects server
1497
+ // to reject client requests, and vice versa.
1498
+ func TestTLSReloadCopy (t * testing.T ) {
1499
+ defer testutil .AfterTest (t )
1500
+
1501
+ // clone certs directory, free to overwrite
1502
+ cDir , err := ioutil .TempDir (os .TempDir (), "fixtures-test" )
1503
+ if err != nil {
1504
+ t .Fatal (err )
1505
+ }
1506
+ defer os .RemoveAll (cDir )
1507
+ ts , err := copyTLSFiles (testTLSInfo , cDir )
1508
+ if err != nil {
1509
+ t .Fatal (err )
1510
+ }
1511
+
1512
+ // start with valid certs
1513
+ clus := NewClusterV3 (t , & ClusterConfig {Size : 1 , PeerTLS : & ts , ClientTLS : & ts })
1514
+ defer clus .Terminate (t )
1515
+
1516
+ // concurrent client dialing while certs transition from valid to expired
1517
+ errc := make (chan error , 1 )
1518
+ go func () {
1519
+ for {
1520
+ cc , err := ts .ClientConfig ()
1521
+ if err != nil {
1522
+ if strings .Contains (err .Error (), "tls: private key does not match public key" ) {
1523
+ // from concurrent certs overwriting
1524
+ continue
1525
+ }
1526
+ t .Fatal (err )
1527
+ }
1528
+ cli , cerr := clientv3 .New (clientv3.Config {
1529
+ Endpoints : []string {clus .Members [0 ].GRPCAddr ()},
1530
+ DialTimeout : time .Second ,
1531
+ TLS : cc ,
1532
+ })
1533
+ if cerr != nil {
1534
+ errc <- cerr
1535
+ return
1536
+ }
1537
+ cli .Close ()
1538
+ }
1539
+ }()
1540
+
1541
+ // overwrite valid certs with expired ones
1542
+ // (e.g. simulate cert expiration in practice)
1543
+ if _ , err = copyTLSFiles (testTLSInfoExpired , cDir ); err != nil {
1544
+ t .Fatal (err )
1545
+ }
1546
+
1547
+ select {
1548
+ case gerr := <- errc :
1549
+ if gerr != grpc .ErrClientConnTimeout {
1550
+ t .Fatalf ("expected %v, got %v" , grpc .ErrClientConnTimeout , gerr )
1551
+ }
1552
+ case <- time .After (7 * time .Second ):
1553
+ t .Fatal ("failed to receive dial timeout error" )
1554
+ }
1555
+
1556
+ // now, replace expired certs back with valid ones
1557
+ if _ , err = copyTLSFiles (testTLSInfo , cDir ); err != nil {
1558
+ t .Fatal (err )
1559
+ }
1560
+
1561
+ // new incoming client request should trigger
1562
+ // listener to reload valid certs
1563
+ var tls * tls.Config
1564
+ tls , err = ts .ClientConfig ()
1565
+ if err != nil {
1566
+ t .Fatal (err )
1567
+ }
1568
+ var cl * clientv3.Client
1569
+ cl , err = clientv3 .New (clientv3.Config {
1570
+ Endpoints : []string {clus .Members [0 ].GRPCAddr ()},
1571
+ DialTimeout : time .Second ,
1572
+ TLS : tls ,
1573
+ })
1574
+ if err != nil {
1575
+ t .Fatalf ("expected no error, got %v" , err )
1576
+ }
1577
+ cl .Close ()
1578
+ }
1579
+
1377
1580
func TestGRPCRequireLeader (t * testing.T ) {
1378
1581
defer testutil .AfterTest (t )
1379
1582
0 commit comments