github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/nomad/structs/config/tls.go (about)

     1  package config
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"sync"
     7  )
     8  
     9  // TLSConfig provides TLS related configuration
    10  type TLSConfig struct {
    11  
    12  	// EnableHTTP enabled TLS for http traffic to the Nomad server and clients
    13  	EnableHTTP bool `mapstructure:"http"`
    14  
    15  	// EnableRPC enables TLS for RPC and Raft traffic to the Nomad servers
    16  	EnableRPC bool `mapstructure:"rpc"`
    17  
    18  	// VerifyServerHostname is used to enable hostname verification of servers. This
    19  	// ensures that the certificate presented is valid for server.<region>.nomad
    20  	// This prevents a compromised client from being restarted as a server, and then
    21  	// intercepting request traffic as well as being added as a raft peer. This should be
    22  	// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
    23  	// existing clients.
    24  	VerifyServerHostname bool `mapstructure:"verify_server_hostname"`
    25  
    26  	// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
    27  	// or VerifyOutgoing to verify the TLS connection.
    28  	CAFile string `mapstructure:"ca_file"`
    29  
    30  	// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
    31  	// Must be provided to serve TLS connections.
    32  	CertFile string `mapstructure:"cert_file"`
    33  
    34  	// KeyLoader is a helper to dynamically reload TLS configuration
    35  	KeyLoader *KeyLoader
    36  
    37  	keyloaderLock sync.Mutex
    38  
    39  	// KeyFile is used to provide a TLS key that is used for serving TLS connections.
    40  	// Must be provided to serve TLS connections.
    41  	KeyFile string `mapstructure:"key_file"`
    42  
    43  	// RPCUpgradeMode should be enabled when a cluster is being upgraded
    44  	// to TLS. Allows servers to accept both plaintext and TLS connections and
    45  	// should only be a temporary state.
    46  	RPCUpgradeMode bool `mapstructure:"rpc_upgrade_mode"`
    47  
    48  	// Verify connections to the HTTPS API
    49  	VerifyHTTPSClient bool `mapstructure:"verify_https_client"`
    50  }
    51  
    52  type KeyLoader struct {
    53  	cacheLock   sync.Mutex
    54  	certificate *tls.Certificate
    55  }
    56  
    57  // LoadKeyPair reloads the TLS certificate based on the specified certificate
    58  // and key file. If successful, stores the certificate for further use.
    59  func (k *KeyLoader) LoadKeyPair(certFile, keyFile string) (*tls.Certificate, error) {
    60  	k.cacheLock.Lock()
    61  	defer k.cacheLock.Unlock()
    62  
    63  	// Allow downgrading
    64  	if certFile == "" && keyFile == "" {
    65  		k.certificate = nil
    66  		return nil, nil
    67  	}
    68  
    69  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
    70  	if err != nil {
    71  		return nil, fmt.Errorf("Failed to load cert/key pair: %v", err)
    72  	}
    73  
    74  	k.certificate = &cert
    75  	return k.certificate, nil
    76  }
    77  
    78  // GetOutgoingCertificate fetches the currently-loaded certificate when
    79  // accepting a TLS connection. This currently does not consider information in
    80  // the ClientHello and only returns the certificate that was last loaded.
    81  func (k *KeyLoader) GetOutgoingCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
    82  	k.cacheLock.Lock()
    83  	defer k.cacheLock.Unlock()
    84  	return k.certificate, nil
    85  }
    86  
    87  // GetClientCertificate fetches the currently-loaded certificate when the Server
    88  // requests a certificate from the caller. This currently does not consider
    89  // information in the ClientHello and only returns the certificate that was last
    90  // loaded.
    91  func (k *KeyLoader) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
    92  	k.cacheLock.Lock()
    93  	defer k.cacheLock.Unlock()
    94  	return k.certificate, nil
    95  }
    96  
    97  func (k *KeyLoader) Copy() *KeyLoader {
    98  	if k == nil {
    99  		return nil
   100  	}
   101  
   102  	new := KeyLoader{}
   103  	new.certificate = k.certificate
   104  	return &new
   105  }
   106  
   107  // GetKeyLoader returns the keyloader for a TLSConfig object. If the keyloader
   108  // has not been initialized, it will first do so.
   109  func (t *TLSConfig) GetKeyLoader() *KeyLoader {
   110  	t.keyloaderLock.Lock()
   111  	defer t.keyloaderLock.Unlock()
   112  
   113  	// If the keyloader has not yet been initialized, do it here
   114  	if t.KeyLoader == nil {
   115  		t.KeyLoader = &KeyLoader{}
   116  	}
   117  	return t.KeyLoader
   118  }
   119  
   120  // Copy copies the fields of TLSConfig to another TLSConfig object. Required as
   121  // to not copy mutexes between objects.
   122  func (t *TLSConfig) Copy() *TLSConfig {
   123  	if t == nil {
   124  		return t
   125  	}
   126  
   127  	new := &TLSConfig{}
   128  	new.EnableHTTP = t.EnableHTTP
   129  	new.EnableRPC = t.EnableRPC
   130  	new.VerifyServerHostname = t.VerifyServerHostname
   131  	new.CAFile = t.CAFile
   132  	new.CertFile = t.CertFile
   133  
   134  	t.keyloaderLock.Lock()
   135  	new.KeyLoader = t.KeyLoader.Copy()
   136  	t.keyloaderLock.Unlock()
   137  
   138  	new.KeyFile = t.KeyFile
   139  	new.RPCUpgradeMode = t.RPCUpgradeMode
   140  	new.VerifyHTTPSClient = t.VerifyHTTPSClient
   141  	return new
   142  }
   143  
   144  func (t *TLSConfig) IsEmpty() bool {
   145  	if t == nil {
   146  		return true
   147  	}
   148  
   149  	return t.EnableHTTP == false &&
   150  		t.EnableRPC == false &&
   151  		t.VerifyServerHostname == false &&
   152  		t.CAFile == "" &&
   153  		t.CertFile == "" &&
   154  		t.KeyFile == "" &&
   155  		t.VerifyHTTPSClient == false
   156  }
   157  
   158  // Merge is used to merge two TLS configs together
   159  func (t *TLSConfig) Merge(b *TLSConfig) *TLSConfig {
   160  	result := t.Copy()
   161  
   162  	if b.EnableHTTP {
   163  		result.EnableHTTP = true
   164  	}
   165  	if b.EnableRPC {
   166  		result.EnableRPC = true
   167  	}
   168  	if b.VerifyServerHostname {
   169  		result.VerifyServerHostname = true
   170  	}
   171  	if b.CAFile != "" {
   172  		result.CAFile = b.CAFile
   173  	}
   174  	if b.CertFile != "" {
   175  		result.CertFile = b.CertFile
   176  	}
   177  	if b.KeyFile != "" {
   178  		result.KeyFile = b.KeyFile
   179  	}
   180  	if b.VerifyHTTPSClient {
   181  		result.VerifyHTTPSClient = true
   182  	}
   183  	if b.RPCUpgradeMode {
   184  		result.RPCUpgradeMode = true
   185  	}
   186  	return result
   187  }
   188  
   189  // Equals compares the fields of two TLS configuration objects, returning a
   190  // boolean indicating if they are the same.
   191  // It is possible for either the calling TLSConfig to be nil, or the TLSConfig
   192  // that it is being compared against, so we need to handle both places. See
   193  // server.go Reload for example.
   194  func (t *TLSConfig) Equals(newConfig *TLSConfig) bool {
   195  	if t == nil || newConfig == nil {
   196  		return t == newConfig
   197  	}
   198  
   199  	return t.EnableRPC == newConfig.EnableRPC &&
   200  		t.CAFile == newConfig.CAFile &&
   201  		t.CertFile == newConfig.CertFile &&
   202  		t.KeyFile == newConfig.KeyFile &&
   203  		t.RPCUpgradeMode == newConfig.RPCUpgradeMode &&
   204  		t.VerifyHTTPSClient == newConfig.VerifyHTTPSClient
   205  }