@@ -3,6 +3,8 @@ package nomad
33import (
44 "context"
55 "crypto/tls"
6+ "crypto/x509"
7+ "errors"
68 "fmt"
79 "io/ioutil"
810 "net"
@@ -11,6 +13,7 @@ import (
1113 "path/filepath"
1214 "sort"
1315 "strconv"
16+ "strings"
1417 "sync"
1518 "sync/atomic"
1619 "time"
@@ -279,7 +282,7 @@ func NewServer(config *Config, consulCatalog consul.CatalogAPI) (*Server, error)
279282 if err != nil {
280283 return nil , err
281284 }
282- incomingTLS , tlsWrap , err := getTLSConf (config .TLSConfig .EnableRPC , tlsConf )
285+ incomingTLS , tlsWrap , err := getTLSConf (config .TLSConfig .EnableRPC , tlsConf , config . Region )
283286 if err != nil {
284287 return nil , err
285288 }
@@ -447,25 +450,70 @@ func (s *Server) createRPCListener() (*net.TCPListener, error) {
447450
448451// getTLSConf gets the server's TLS configuration based on the config supplied
449452// by the operator
450- func getTLSConf (enableRPC bool , tlsConf * tlsutil.Config ) (* tls.Config , tlsutil.RegionWrapper , error ) {
453+ func getTLSConf (enableRPC bool , tlsConf * tlsutil.Config , region string ) (* tls.Config , tlsutil.RegionWrapper , error ) {
451454 var tlsWrap tlsutil.RegionWrapper
452455 var incomingTLS * tls.Config
453- if enableRPC {
454- tw , err := tlsConf .OutgoingTLSWrapper ()
455- if err != nil {
456- return nil , nil , err
457- }
458- tlsWrap = tw
456+ if ! enableRPC {
457+ return incomingTLS , tlsWrap , nil
458+ }
459459
460- itls , err := tlsConf .IncomingTLSConfig ()
461- if err != nil {
462- return nil , nil , err
463- }
460+ tlsWrap , err := tlsConf .OutgoingTLSWrapper ()
461+ if err != nil {
462+ return nil , nil , err
463+ }
464+
465+ itls , err := tlsConf .IncomingTLSConfig ()
466+ if err != nil {
467+ return nil , nil , err
468+ }
469+
470+ if tlsConf .VerifyServerHostname {
471+ incomingTLS = itls .Clone ()
472+ incomingTLS .VerifyPeerCertificate = rpcNameAndRegionValidator (region )
473+ } else {
464474 incomingTLS = itls
465475 }
466476 return incomingTLS , tlsWrap , nil
467477}
468478
479+ // implements signature of tls.Config.VerifyPeerCertificate which is called
480+ // after the certs have been verified. We'll ignore the raw certs and only
481+ // check the verified certs.
482+ func rpcNameAndRegionValidator (region string ) func ([][]byte , [][]* x509.Certificate ) error {
483+ return func (_ [][]byte , certificates [][]* x509.Certificate ) error {
484+ if len (certificates ) > 0 && len (certificates [0 ]) > 0 {
485+ cert := certificates [0 ][0 ]
486+ for _ , dnsName := range cert .DNSNames {
487+ if validateRPCRegionPeer (dnsName , region ) {
488+ return nil
489+ }
490+ }
491+ if validateRPCRegionPeer (cert .Subject .CommonName , region ) {
492+ return nil
493+ }
494+ }
495+ return errors .New ("invalid role or region for certificate" )
496+ }
497+ }
498+
499+ func validateRPCRegionPeer (name , region string ) bool {
500+ parts := strings .Split (name , "." )
501+ if len (parts ) < 3 {
502+ // Invalid SAN
503+ return false
504+ }
505+ if parts [len (parts )- 1 ] != "nomad" {
506+ // Incorrect service
507+ return false
508+ }
509+ if parts [0 ] == "client" {
510+ // Clients may only connect to servers in their region
511+ return name == "client." + region + ".nomad"
512+ }
513+ // Servers may connect to any Nomad RPC service for federation.
514+ return parts [0 ] == "server"
515+ }
516+
469517// reloadTLSConnections updates a server's TLS configuration and reloads RPC
470518// connections.
471519func (s * Server ) reloadTLSConnections (newTLSConfig * config.TLSConfig ) error {
@@ -483,7 +531,7 @@ func (s *Server) reloadTLSConnections(newTLSConfig *config.TLSConfig) error {
483531 return err
484532 }
485533
486- incomingTLS , tlsWrap , err := getTLSConf (newTLSConfig .EnableRPC , tlsConf )
534+ incomingTLS , tlsWrap , err := getTLSConf (newTLSConfig .EnableRPC , tlsConf , s . config . Region )
487535 if err != nil {
488536 s .logger .Error ("unable to reset TLS context" , "error" , err )
489537 return err
0 commit comments