github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/peer/config.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  // The 'viper' package for configuration handling is very flexible, but has
     8  // been found to have extremely poor performance when configuration values are
     9  // accessed repeatedly. The function CacheConfiguration() defined here caches
    10  // all configuration values that are accessed frequently.  These parameters
    11  // are now presented as function calls that access local configuration
    12  // variables.  This seems to be the most robust way to represent these
    13  // parameters in the face of the numerous ways that configuration files are
    14  // loaded and used (e.g, normal usage vs. test cases).
    15  
    16  // The CacheConfiguration() function is allowed to be called globally to
    17  // ensure that the correct values are always cached; See for example how
    18  // certain parameters are forced in 'ChaincodeDevMode' in main.go.
    19  
    20  package peer
    21  
    22  import (
    23  	"crypto/tls"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net"
    27  	"path/filepath"
    28  	"runtime"
    29  	"time"
    30  
    31  	"github.com/hyperledger/fabric/core/comm"
    32  	"github.com/hyperledger/fabric/core/config"
    33  	"github.com/pkg/errors"
    34  	"github.com/spf13/viper"
    35  )
    36  
    37  // ExternalBuilder represents the configuration structure of
    38  // a chaincode external builder
    39  type ExternalBuilder struct {
    40  	EnvironmentWhitelist []string `yaml:"environmentWhitelist"`
    41  	Name                 string   `yaml:"name"`
    42  	Path                 string   `yaml:"path"`
    43  }
    44  
    45  // Config is the struct that defines the Peer configurations.
    46  type Config struct {
    47  	// LocalMSPID is the identifier of the local MSP.
    48  	LocalMSPID string
    49  	// ListenAddress is the local address the peer will listen on. It must be
    50  	// formatted as [host | ipaddr]:port.
    51  	ListenAddress string
    52  	// PeerID provides a name for this peer instance. It is used when naming
    53  	// docker resources to segregate fabric networks and peers.
    54  	PeerID string
    55  	// PeerAddress is the address other peers and clients should use to
    56  	// communicate with the peer. It must be formatted as [host | ipaddr]:port.
    57  	// When used by the CLI, it represents the target peer endpoint.
    58  	PeerAddress string
    59  	// NetworkID specifies a name to use for logical separation of networks. It
    60  	// is used when naming docker resources to segregate fabric networks and
    61  	// peers.
    62  	NetworkID string
    63  	// ChaincodeListenAddress is the endpoint on which this peer will listen for
    64  	// chaincode connections. If omitted, it defaults to the host portion of
    65  	// PeerAddress and port 7052.
    66  	ChaincodeListenAddress string
    67  	// ChaincodeAddress specifies the endpoint chaincode launched by the peer
    68  	// should use to connect to the peer. If omitted, it defaults to
    69  	// ChaincodeListenAddress and falls back to ListenAddress.
    70  	ChaincodeAddress string
    71  	// ValidatorPoolSize indicates the number of goroutines that will execute
    72  	// transaction validation in parallel. If omitted, it defaults to number of
    73  	// hardware threads on the machine.
    74  	ValidatorPoolSize int
    75  
    76  	// ----- Peer Delivery Client Keepalive -----
    77  	// DeliveryClient Keepalive settings for communication with ordering nodes.
    78  	DeliverClientKeepaliveOptions comm.KeepaliveOptions
    79  
    80  	// ----- Profile -----
    81  	// TODO: create separate sub-struct for Profile config.
    82  
    83  	// ProfileEnabled determines if the go pprof endpoint is enabled in the peer.
    84  	ProfileEnabled bool
    85  	// ProfileListenAddress is the address the pprof server should accept
    86  	// connections on.
    87  	ProfileListenAddress string
    88  
    89  	// ----- Discovery -----
    90  
    91  	// The discovery service is used by clients to query information about peers,
    92  	// such as - which peers have joined a certain channel, what is the latest
    93  	// channel config, and most importantly - given a chaincode and a channel, what
    94  	// possible sets of peers satisfy the endorsement policy.
    95  	// TODO: create separate sub-struct for Discovery config.
    96  
    97  	// DiscoveryEnabled is used to enable the discovery service.
    98  	DiscoveryEnabled bool
    99  	// DiscoveryOrgMembersAllowed allows non-admins to perform non channel-scoped queries.
   100  	DiscoveryOrgMembersAllowed bool
   101  	// DiscoveryAuthCacheEnabled is used to enable the authentication cache.
   102  	DiscoveryAuthCacheEnabled bool
   103  	// DiscoveryAuthCacheMaxSize sets the maximum size of authentication cache.
   104  	DiscoveryAuthCacheMaxSize int
   105  	// DiscoveryAuthCachePurgeRetentionRatio set the proportion of entries remains in cache
   106  	// after overpopulation purge.
   107  	DiscoveryAuthCachePurgeRetentionRatio float64
   108  
   109  	// ----- Limits -----
   110  	// Limits is used to configure some internal resource limits.
   111  	// TODO: create separate sub-struct for Limits config.
   112  
   113  	// LimitsConcurrencyQSCC sets the limits for number of concurrently running
   114  	// qscc system chaincode requests.
   115  	LimitsConcurrencyQSCC int
   116  
   117  	// ----- TLS -----
   118  	// Require server-side TLS.
   119  	// TODO: create separate sub-struct for PeerTLS config.
   120  
   121  	// PeerTLSEnabled enables/disables Peer TLS.
   122  	PeerTLSEnabled bool
   123  
   124  	// ----- Authentication -----
   125  	// Authentication contains configuration parameters related to authenticating
   126  	// client messages.
   127  	// TODO: create separate sub-struct for Authentication config.
   128  
   129  	// AuthenticationTimeWindow sets the acceptable time duration for current
   130  	// server time and client's time as specified in a client request message.
   131  	AuthenticationTimeWindow time.Duration
   132  
   133  	// Endpoint of the vm management system. For docker can be one of the following in general
   134  	// unix:///var/run/docker.sock
   135  	// http://localhost:2375
   136  	// https://localhost:2376
   137  	VMEndpoint string
   138  
   139  	// ----- vm.docker.tls -----
   140  	// TODO: create separate sub-struct for VM.Docker.TLS config.
   141  
   142  	// VMDockerTLSEnabled enables/disables TLS for dockers.
   143  	VMDockerTLSEnabled   bool
   144  	VMDockerAttachStdout bool
   145  	// VMNetworkMode sets the networking mode for the container.
   146  	VMNetworkMode string
   147  
   148  	// ChaincodePull enables/disables force pulling of the base docker image.
   149  	ChaincodePull bool
   150  	// ExternalBuilders represents the builders and launchers for
   151  	// chaincode. The external builder detection processing will iterate over the
   152  	// builders in the order specified below.
   153  	ExternalBuilders []ExternalBuilder
   154  
   155  	// ----- Operations config -----
   156  	// TODO: create separate sub-struct for Operations config.
   157  
   158  	// OperationsListenAddress provides the host and port for the operations server
   159  	OperationsListenAddress string
   160  	// OperationsTLSEnabled enables/disables TLS for operations.
   161  	OperationsTLSEnabled bool
   162  	// OperationsTLSCertFile provides the path to PEM encoded server certificate for
   163  	// the operations server.
   164  	OperationsTLSCertFile string
   165  	// OperationsTLSKeyFile provides the path to PEM encoded server key for the
   166  	// operations server.
   167  	OperationsTLSKeyFile string
   168  	// OperationsTLSClientAuthRequired enables/disables the requirements for client
   169  	// certificate authentication at the TLS layer to access all resource.
   170  	OperationsTLSClientAuthRequired bool
   171  	// OperationsTLSClientRootCAs provides the path to PEM encoded ca certiricates to
   172  	// trust for client authentication.
   173  	OperationsTLSClientRootCAs []string
   174  
   175  	// ----- Metrics config -----
   176  	// TODO: create separate sub-struct for Metrics config.
   177  
   178  	// MetricsProvider provides the categories of metrics providers, which is one of
   179  	// statsd, prometheus, or disabled.
   180  	MetricsProvider string
   181  	// StatsdNetwork indicate the network type used by statsd metrics. (tcp or udp).
   182  	StatsdNetwork string
   183  	// StatsdAaddress provides the address for statsd server.
   184  	StatsdAaddress string
   185  	// StatsdWriteInterval set the time interval at which locally cached counters and
   186  	// gauges are pushed.
   187  	StatsdWriteInterval time.Duration
   188  	// StatsdPrefix provides the prefix that prepended to all emitted statsd metrics.
   189  	StatsdPrefix string
   190  
   191  	// ----- Docker config ------
   192  
   193  	// DockerCert is the path to the PEM encoded TLS client certificate required to access
   194  	// the docker daemon.
   195  	DockerCert string
   196  	// DockerKey is the path to the PEM encoded key required to access the docker daemon.
   197  	DockerKey string
   198  	// DockerCA is the path to the PEM encoded CA certificate for the docker daemon.
   199  	DockerCA string
   200  }
   201  
   202  // GlobalConfig obtains a set of configuration from viper, build and returns
   203  // the config struct.
   204  func GlobalConfig() (*Config, error) {
   205  	c := &Config{}
   206  	if err := c.load(); err != nil {
   207  		return nil, err
   208  	}
   209  	return c, nil
   210  }
   211  
   212  func (c *Config) load() error {
   213  	preeAddress, err := getLocalAddress()
   214  	if err != nil {
   215  		return err
   216  	}
   217  
   218  	configDir := filepath.Dir(viper.ConfigFileUsed())
   219  
   220  	c.PeerAddress = preeAddress
   221  	c.PeerID = viper.GetString("peer.id")
   222  	c.LocalMSPID = viper.GetString("peer.localMspId")
   223  	c.ListenAddress = viper.GetString("peer.listenAddress")
   224  
   225  	c.AuthenticationTimeWindow = viper.GetDuration("peer.authentication.timewindow")
   226  	if c.AuthenticationTimeWindow == 0 {
   227  		defaultTimeWindow := 15 * time.Minute
   228  		logger.Warningf("`peer.authentication.timewindow` not set; defaulting to %s", defaultTimeWindow)
   229  		c.AuthenticationTimeWindow = defaultTimeWindow
   230  	}
   231  
   232  	c.PeerTLSEnabled = viper.GetBool("peer.tls.enabled")
   233  	c.NetworkID = viper.GetString("peer.networkId")
   234  	c.LimitsConcurrencyQSCC = viper.GetInt("peer.limits.concurrency.qscc")
   235  	c.DiscoveryEnabled = viper.GetBool("peer.discovery.enabled")
   236  	c.ProfileEnabled = viper.GetBool("peer.profile.enabled")
   237  	c.ProfileListenAddress = viper.GetString("peer.profile.listenAddress")
   238  	c.DiscoveryOrgMembersAllowed = viper.GetBool("peer.discovery.orgMembersAllowedAccess")
   239  	c.DiscoveryAuthCacheEnabled = viper.GetBool("peer.discovery.authCacheEnabled")
   240  	c.DiscoveryAuthCacheMaxSize = viper.GetInt("peer.discovery.authCacheMaxSize")
   241  	c.DiscoveryAuthCachePurgeRetentionRatio = viper.GetFloat64("peer.discovery.authCachePurgeRetentionRatio")
   242  	c.ChaincodeListenAddress = viper.GetString("peer.chaincodeListenAddress")
   243  	c.ChaincodeAddress = viper.GetString("peer.chaincodeAddress")
   244  
   245  	c.ValidatorPoolSize = viper.GetInt("peer.validatorPoolSize")
   246  	if c.ValidatorPoolSize <= 0 {
   247  		c.ValidatorPoolSize = runtime.NumCPU()
   248  	}
   249  
   250  	c.DeliverClientKeepaliveOptions = comm.DefaultKeepaliveOptions
   251  	if viper.IsSet("peer.keepalive.deliveryClient.interval") {
   252  		c.DeliverClientKeepaliveOptions.ClientInterval = viper.GetDuration("peer.keepalive.deliveryClient.interval")
   253  	}
   254  	if viper.IsSet("peer.keepalive.deliveryClient.timeout") {
   255  		c.DeliverClientKeepaliveOptions.ClientTimeout = viper.GetDuration("peer.keepalive.deliveryClient.timeout")
   256  	}
   257  
   258  	c.VMEndpoint = viper.GetString("vm.endpoint")
   259  	c.VMDockerTLSEnabled = viper.GetBool("vm.docker.tls.enabled")
   260  	c.VMDockerAttachStdout = viper.GetBool("vm.docker.attachStdout")
   261  
   262  	c.VMNetworkMode = viper.GetString("vm.docker.hostConfig.NetworkMode")
   263  	if c.VMNetworkMode == "" {
   264  		c.VMNetworkMode = "host"
   265  	}
   266  
   267  	c.ChaincodePull = viper.GetBool("chaincode.pull")
   268  	var externalBuilders []ExternalBuilder
   269  	err = viper.UnmarshalKey("chaincode.externalBuilders", &externalBuilders)
   270  	if err != nil {
   271  		return err
   272  	}
   273  	for _, builder := range externalBuilders {
   274  		if builder.Path == "" {
   275  			return fmt.Errorf("invalid external builder configuration, path attribute missing in one or more builders")
   276  		}
   277  		if builder.Name == "" {
   278  			return fmt.Errorf("external builder at path %s has no name attribute", builder.Path)
   279  		}
   280  	}
   281  	c.ExternalBuilders = externalBuilders
   282  
   283  	c.OperationsListenAddress = viper.GetString("operations.listenAddress")
   284  	c.OperationsTLSEnabled = viper.GetBool("operations.tls.enabled")
   285  	c.OperationsTLSCertFile = config.GetPath("operations.tls.cert.file")
   286  	c.OperationsTLSKeyFile = config.GetPath("operations.tls.key.file")
   287  	c.OperationsTLSClientAuthRequired = viper.GetBool("operations.tls.clientAuthRequired")
   288  
   289  	for _, rca := range viper.GetStringSlice("operations.tls.clientRootCAs.files") {
   290  		c.OperationsTLSClientRootCAs = append(c.OperationsTLSClientRootCAs, config.TranslatePath(configDir, rca))
   291  	}
   292  
   293  	c.MetricsProvider = viper.GetString("metrics.provider")
   294  	c.StatsdNetwork = viper.GetString("metrics.statsd.network")
   295  	c.StatsdAaddress = viper.GetString("metrics.statsd.address")
   296  	c.StatsdWriteInterval = viper.GetDuration("metrics.statsd.writeInterval")
   297  	c.StatsdPrefix = viper.GetString("metrics.statsd.prefix")
   298  
   299  	c.DockerCert = config.GetPath("vm.docker.tls.cert.file")
   300  	c.DockerKey = config.GetPath("vm.docker.tls.key.file")
   301  	c.DockerCA = config.GetPath("vm.docker.tls.ca.file")
   302  
   303  	return nil
   304  }
   305  
   306  // getLocalAddress returns the address:port the local peer is operating on.  Affected by env:peer.addressAutoDetect
   307  func getLocalAddress() (string, error) {
   308  	peerAddress := viper.GetString("peer.address")
   309  	if peerAddress == "" {
   310  		return "", fmt.Errorf("peer.address isn't set")
   311  	}
   312  	host, port, err := net.SplitHostPort(peerAddress)
   313  	if err != nil {
   314  		return "", errors.Errorf("peer.address isn't in host:port format: %s", peerAddress)
   315  	}
   316  
   317  	localIP, err := comm.GetLocalIP()
   318  	if err != nil {
   319  		peerLogger.Errorf("local IP address not auto-detectable: %s", err)
   320  		return "", err
   321  	}
   322  	autoDetectedIPAndPort := net.JoinHostPort(localIP, port)
   323  	peerLogger.Info("Auto-detected peer address:", autoDetectedIPAndPort)
   324  	// If host is the IPv4 address "0.0.0.0" or the IPv6 address "::",
   325  	// then fallback to auto-detected address
   326  	if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() {
   327  		peerLogger.Info("Host is", host, ", falling back to auto-detected address:", autoDetectedIPAndPort)
   328  		return autoDetectedIPAndPort, nil
   329  	}
   330  
   331  	if viper.GetBool("peer.addressAutoDetect") {
   332  		peerLogger.Info("Auto-detect flag is set, returning", autoDetectedIPAndPort)
   333  		return autoDetectedIPAndPort, nil
   334  	}
   335  	peerLogger.Info("Returning", peerAddress)
   336  	return peerAddress, nil
   337  
   338  }
   339  
   340  // GetServerConfig returns the gRPC server configuration for the peer
   341  func GetServerConfig() (comm.ServerConfig, error) {
   342  	serverConfig := comm.ServerConfig{
   343  		ConnectionTimeout: viper.GetDuration("peer.connectiontimeout"),
   344  		SecOpts: comm.SecureOptions{
   345  			UseTLS: viper.GetBool("peer.tls.enabled"),
   346  		},
   347  	}
   348  	if serverConfig.SecOpts.UseTLS {
   349  		// get the certs from the file system
   350  		serverKey, err := ioutil.ReadFile(config.GetPath("peer.tls.key.file"))
   351  		if err != nil {
   352  			return serverConfig, fmt.Errorf("error loading TLS key (%s)", err)
   353  		}
   354  		serverCert, err := ioutil.ReadFile(config.GetPath("peer.tls.cert.file"))
   355  		if err != nil {
   356  			return serverConfig, fmt.Errorf("error loading TLS certificate (%s)", err)
   357  		}
   358  		serverConfig.SecOpts.Certificate = serverCert
   359  		serverConfig.SecOpts.Key = serverKey
   360  		serverConfig.SecOpts.RequireClientCert = viper.GetBool("peer.tls.clientAuthRequired")
   361  		if serverConfig.SecOpts.RequireClientCert {
   362  			var clientRoots [][]byte
   363  			for _, file := range viper.GetStringSlice("peer.tls.clientRootCAs.files") {
   364  				clientRoot, err := ioutil.ReadFile(
   365  					config.TranslatePath(filepath.Dir(viper.ConfigFileUsed()), file))
   366  				if err != nil {
   367  					return serverConfig,
   368  						fmt.Errorf("error loading client root CAs (%s)", err)
   369  				}
   370  				clientRoots = append(clientRoots, clientRoot)
   371  			}
   372  			serverConfig.SecOpts.ClientRootCAs = clientRoots
   373  		}
   374  		// check for root cert
   375  		if config.GetPath("peer.tls.rootcert.file") != "" {
   376  			rootCert, err := ioutil.ReadFile(config.GetPath("peer.tls.rootcert.file"))
   377  			if err != nil {
   378  				return serverConfig, fmt.Errorf("error loading TLS root certificate (%s)", err)
   379  			}
   380  			serverConfig.SecOpts.ServerRootCAs = [][]byte{rootCert}
   381  		}
   382  	}
   383  	// get the default keepalive options
   384  	serverConfig.KaOpts = comm.DefaultKeepaliveOptions
   385  	// check to see if interval is set for the env
   386  	if viper.IsSet("peer.keepalive.interval") {
   387  		serverConfig.KaOpts.ServerInterval = viper.GetDuration("peer.keepalive.interval")
   388  	}
   389  	// check to see if timeout is set for the env
   390  	if viper.IsSet("peer.keepalive.timeout") {
   391  		serverConfig.KaOpts.ServerTimeout = viper.GetDuration("peer.keepalive.timeout")
   392  	}
   393  	// check to see if minInterval is set for the env
   394  	if viper.IsSet("peer.keepalive.minInterval") {
   395  		serverConfig.KaOpts.ServerMinInterval = viper.GetDuration("peer.keepalive.minInterval")
   396  	}
   397  	return serverConfig, nil
   398  }
   399  
   400  // GetClientCertificate returns the TLS certificate to use for gRPC client
   401  // connections
   402  func GetClientCertificate() (tls.Certificate, error) {
   403  	cert := tls.Certificate{}
   404  
   405  	keyPath := viper.GetString("peer.tls.clientKey.file")
   406  	certPath := viper.GetString("peer.tls.clientCert.file")
   407  
   408  	if keyPath != "" || certPath != "" {
   409  		// need both keyPath and certPath to be set
   410  		if keyPath == "" || certPath == "" {
   411  			return cert, errors.New("peer.tls.clientKey.file and " +
   412  				"peer.tls.clientCert.file must both be set or must both be empty")
   413  		}
   414  		keyPath = config.GetPath("peer.tls.clientKey.file")
   415  		certPath = config.GetPath("peer.tls.clientCert.file")
   416  
   417  	} else {
   418  		// use the TLS server keypair
   419  		keyPath = viper.GetString("peer.tls.key.file")
   420  		certPath = viper.GetString("peer.tls.cert.file")
   421  
   422  		if keyPath != "" || certPath != "" {
   423  			// need both keyPath and certPath to be set
   424  			if keyPath == "" || certPath == "" {
   425  				return cert, errors.New("peer.tls.key.file and " +
   426  					"peer.tls.cert.file must both be set or must both be empty")
   427  			}
   428  			keyPath = config.GetPath("peer.tls.key.file")
   429  			certPath = config.GetPath("peer.tls.cert.file")
   430  		} else {
   431  			return cert, errors.New("must set either " +
   432  				"[peer.tls.key.file and peer.tls.cert.file] or " +
   433  				"[peer.tls.clientKey.file and peer.tls.clientCert.file]" +
   434  				"when peer.tls.clientAuthEnabled is set to true")
   435  		}
   436  	}
   437  	// get the keypair from the file system
   438  	clientKey, err := ioutil.ReadFile(keyPath)
   439  	if err != nil {
   440  		return cert, errors.WithMessage(err,
   441  			"error loading client TLS key")
   442  	}
   443  	clientCert, err := ioutil.ReadFile(certPath)
   444  	if err != nil {
   445  		return cert, errors.WithMessage(err,
   446  			"error loading client TLS certificate")
   447  	}
   448  	cert, err = tls.X509KeyPair(clientCert, clientKey)
   449  	if err != nil {
   450  		return cert, errors.WithMessage(err,
   451  			"error parsing client TLS key pair")
   452  	}
   453  	return cert, nil
   454  }