github.com/hernad/nomad@v1.6.112/nomad/structs/config/consul.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package config
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"strings"
    10  	"time"
    11  
    12  	consul "github.com/hashicorp/consul/api"
    13  	"github.com/hashicorp/go-secure-stdlib/listenerutil"
    14  	"github.com/hernad/nomad/helper/pointer"
    15  )
    16  
    17  // ConsulConfig contains the configuration information necessary to
    18  // communicate with a Consul Agent in order to:
    19  //
    20  // - Register services and their checks with Consul
    21  //
    22  //   - Bootstrap this Nomad Client with the list of Nomad Servers registered
    23  //     with Consul
    24  //
    25  //   - Establish how this Nomad Client will resolve Envoy Connect Sidecar
    26  //     images.
    27  //
    28  // Both the Agent and the executor need to be able to import ConsulConfig.
    29  type ConsulConfig struct {
    30  	// ServerServiceName is the name of the service that Nomad uses to register
    31  	// servers with Consul
    32  	ServerServiceName string `hcl:"server_service_name"`
    33  
    34  	// ServerHTTPCheckName is the name of the health check that Nomad uses
    35  	// to register the server HTTP health check with Consul
    36  	ServerHTTPCheckName string `hcl:"server_http_check_name"`
    37  
    38  	// ServerSerfCheckName is the name of the health check that Nomad uses
    39  	// to register the server Serf health check with Consul
    40  	ServerSerfCheckName string `hcl:"server_serf_check_name"`
    41  
    42  	// ServerRPCCheckName is the name of the health check that Nomad uses
    43  	// to register the server RPC health check with Consul
    44  	ServerRPCCheckName string `hcl:"server_rpc_check_name"`
    45  
    46  	// ClientServiceName is the name of the service that Nomad uses to register
    47  	// clients with Consul
    48  	ClientServiceName string `hcl:"client_service_name"`
    49  
    50  	// ClientHTTPCheckName is the name of the health check that Nomad uses
    51  	// to register the client HTTP health check with Consul
    52  	ClientHTTPCheckName string `hcl:"client_http_check_name"`
    53  
    54  	// Tags are optional service tags that get registered with the service
    55  	// in Consul
    56  	Tags []string `hcl:"tags"`
    57  
    58  	// AutoAdvertise determines if this Nomad Agent will advertise its
    59  	// services via Consul.  When true, Nomad Agent will register
    60  	// services with Consul.
    61  	AutoAdvertise *bool `hcl:"auto_advertise"`
    62  
    63  	// ChecksUseAdvertise specifies that Consul checks should use advertise
    64  	// address instead of bind address
    65  	ChecksUseAdvertise *bool `hcl:"checks_use_advertise"`
    66  
    67  	// Addr is the HTTP endpoint address of the local Consul agent
    68  	//
    69  	// Uses Consul's default and env var.
    70  	Addr string `hcl:"address"`
    71  
    72  	// GRPCAddr is the gRPC endpoint address of the local Consul agent
    73  	GRPCAddr string `hcl:"grpc_address"`
    74  
    75  	// Timeout is used by Consul HTTP Client
    76  	Timeout    time.Duration `hcl:"-"`
    77  	TimeoutHCL string        `hcl:"timeout" json:"-"`
    78  
    79  	// Token is used to provide a per-request ACL token. This options overrides
    80  	// the agent's default token
    81  	Token string `hcl:"token"`
    82  
    83  	// AllowUnauthenticated allows users to submit jobs requiring Consul
    84  	// Service Identity tokens without providing a Consul token proving they
    85  	// have access to such policies.
    86  	AllowUnauthenticated *bool `hcl:"allow_unauthenticated"`
    87  
    88  	// Auth is the information to use for http access to Consul agent
    89  	Auth string `hcl:"auth"`
    90  
    91  	// EnableSSL sets the transport scheme to talk to the Consul agent as https
    92  	//
    93  	// Uses Consul's default and env var.
    94  	EnableSSL *bool `hcl:"ssl"`
    95  
    96  	// ShareSSL enables Consul Connect Native applications to use the TLS
    97  	// configuration of the Nomad Client for establishing connections to Consul.
    98  	//
    99  	// Does not include sharing of ACL tokens.
   100  	ShareSSL *bool `hcl:"share_ssl"`
   101  
   102  	// VerifySSL enables or disables SSL verification when the transport scheme
   103  	// for the consul api client is https
   104  	//
   105  	// Uses Consul's default and env var.
   106  	VerifySSL *bool `hcl:"verify_ssl"`
   107  
   108  	// GRPCCAFile is the path to the ca certificate used for Consul gRPC communication.
   109  	//
   110  	// Uses Consul's default and env var.
   111  	GRPCCAFile string `hcl:"grpc_ca_file"`
   112  
   113  	// CAFile is the path to the ca certificate used for Consul communication.
   114  	//
   115  	// Uses Consul's default and env var.
   116  	CAFile string `hcl:"ca_file"`
   117  
   118  	// CertFile is the path to the certificate for Consul communication
   119  	CertFile string `hcl:"cert_file"`
   120  
   121  	// KeyFile is the path to the private key for Consul communication
   122  	KeyFile string `hcl:"key_file"`
   123  
   124  	// ServerAutoJoin enables Nomad servers to find peers by querying Consul and
   125  	// joining them
   126  	ServerAutoJoin *bool `hcl:"server_auto_join"`
   127  
   128  	// ClientAutoJoin enables Nomad servers to find addresses of Nomad servers
   129  	// and register with them
   130  	ClientAutoJoin *bool `hcl:"client_auto_join"`
   131  
   132  	// ExtraKeysHCL is used by hcl to surface unexpected keys
   133  	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
   134  
   135  	// Namespace sets the Consul namespace used for all calls against the
   136  	// Consul API. If this is unset, then Nomad does not specify a consul namespace.
   137  	Namespace string `hcl:"namespace"`
   138  }
   139  
   140  // DefaultConsulConfig returns the canonical defaults for the Nomad
   141  // `consul` configuration. Uses Consul's default configuration which reads
   142  // environment variables.
   143  func DefaultConsulConfig() *ConsulConfig {
   144  	def := consul.DefaultConfig()
   145  	return &ConsulConfig{
   146  		ServerServiceName:    "nomad",
   147  		ServerHTTPCheckName:  "Nomad Server HTTP Check",
   148  		ServerSerfCheckName:  "Nomad Server Serf Check",
   149  		ServerRPCCheckName:   "Nomad Server RPC Check",
   150  		ClientServiceName:    "nomad-client",
   151  		ClientHTTPCheckName:  "Nomad Client HTTP Check",
   152  		AutoAdvertise:        pointer.Of(true),
   153  		ChecksUseAdvertise:   pointer.Of(false),
   154  		ServerAutoJoin:       pointer.Of(true),
   155  		ClientAutoJoin:       pointer.Of(true),
   156  		AllowUnauthenticated: pointer.Of(true),
   157  		Timeout:              5 * time.Second,
   158  
   159  		// From Consul api package defaults
   160  		Addr:      def.Address,
   161  		EnableSSL: pointer.Of(def.Scheme == "https"),
   162  		VerifySSL: pointer.Of(!def.TLSConfig.InsecureSkipVerify),
   163  		CAFile:    def.TLSConfig.CAFile,
   164  		Namespace: def.Namespace,
   165  	}
   166  }
   167  
   168  // AllowsUnauthenticated returns whether the config allows unauthenticated
   169  // creation of Consul Service Identity tokens for Consul Connect enabled Tasks.
   170  //
   171  // If allow_unauthenticated is false, the operator must provide a token on
   172  // job submission (i.e. -consul-token or $CONSUL_HTTP_TOKEN).
   173  func (c *ConsulConfig) AllowsUnauthenticated() bool {
   174  	return c.AllowUnauthenticated != nil && *c.AllowUnauthenticated
   175  }
   176  
   177  // Merge merges two Consul Configurations together.
   178  func (c *ConsulConfig) Merge(b *ConsulConfig) *ConsulConfig {
   179  	result := c.Copy()
   180  
   181  	if b.ServerServiceName != "" {
   182  		result.ServerServiceName = b.ServerServiceName
   183  	}
   184  	if b.ServerHTTPCheckName != "" {
   185  		result.ServerHTTPCheckName = b.ServerHTTPCheckName
   186  	}
   187  	if b.ServerSerfCheckName != "" {
   188  		result.ServerSerfCheckName = b.ServerSerfCheckName
   189  	}
   190  	if b.ServerRPCCheckName != "" {
   191  		result.ServerRPCCheckName = b.ServerRPCCheckName
   192  	}
   193  	if b.ClientServiceName != "" {
   194  		result.ClientServiceName = b.ClientServiceName
   195  	}
   196  	if b.ClientHTTPCheckName != "" {
   197  		result.ClientHTTPCheckName = b.ClientHTTPCheckName
   198  	}
   199  	result.Tags = append(result.Tags, b.Tags...)
   200  	if b.AutoAdvertise != nil {
   201  		result.AutoAdvertise = pointer.Of(*b.AutoAdvertise)
   202  	}
   203  	if b.Addr != "" {
   204  		result.Addr = b.Addr
   205  	}
   206  	if b.GRPCAddr != "" {
   207  		result.GRPCAddr = b.GRPCAddr
   208  	}
   209  	if b.Timeout != 0 {
   210  		result.Timeout = b.Timeout
   211  	}
   212  	if b.TimeoutHCL != "" {
   213  		result.TimeoutHCL = b.TimeoutHCL
   214  	}
   215  	if b.Token != "" {
   216  		result.Token = b.Token
   217  	}
   218  	if b.Auth != "" {
   219  		result.Auth = b.Auth
   220  	}
   221  	if b.EnableSSL != nil {
   222  		result.EnableSSL = pointer.Of(*b.EnableSSL)
   223  	}
   224  	if b.VerifySSL != nil {
   225  		result.VerifySSL = pointer.Of(*b.VerifySSL)
   226  	}
   227  	if b.ShareSSL != nil {
   228  		result.ShareSSL = pointer.Of(*b.ShareSSL)
   229  	}
   230  	if b.GRPCCAFile != "" {
   231  		result.GRPCCAFile = b.GRPCCAFile
   232  	}
   233  	if b.CAFile != "" {
   234  		result.CAFile = b.CAFile
   235  	}
   236  	if b.CertFile != "" {
   237  		result.CertFile = b.CertFile
   238  	}
   239  	if b.KeyFile != "" {
   240  		result.KeyFile = b.KeyFile
   241  	}
   242  	if b.ServerAutoJoin != nil {
   243  		result.ServerAutoJoin = pointer.Of(*b.ServerAutoJoin)
   244  	}
   245  	if b.ClientAutoJoin != nil {
   246  		result.ClientAutoJoin = pointer.Of(*b.ClientAutoJoin)
   247  	}
   248  	if b.ChecksUseAdvertise != nil {
   249  		result.ChecksUseAdvertise = pointer.Of(*b.ChecksUseAdvertise)
   250  	}
   251  	if b.AllowUnauthenticated != nil {
   252  		result.AllowUnauthenticated = pointer.Of(*b.AllowUnauthenticated)
   253  	}
   254  	if b.Namespace != "" {
   255  		result.Namespace = b.Namespace
   256  	}
   257  	return result
   258  }
   259  
   260  // ApiConfig returns a usable Consul config that can be passed directly to
   261  // hashicorp/consul/api.  NOTE: datacenter is not set
   262  func (c *ConsulConfig) ApiConfig() (*consul.Config, error) {
   263  	// Get the default config from consul to reuse things like the default
   264  	// http.Transport.
   265  	config := consul.DefaultConfig()
   266  	if c.Addr != "" {
   267  		ipStr, err := listenerutil.ParseSingleIPTemplate(c.Addr)
   268  		if err != nil {
   269  			return nil, fmt.Errorf("unable to parse address template %q: %v", c.Addr, err)
   270  		}
   271  		config.Address = ipStr
   272  	}
   273  	if c.Token != "" {
   274  		config.Token = c.Token
   275  	}
   276  	if c.Timeout != 0 {
   277  		// Create a custom Client to set the timeout
   278  		if config.HttpClient == nil {
   279  			config.HttpClient = &http.Client{}
   280  		}
   281  		config.HttpClient.Timeout = c.Timeout
   282  		config.HttpClient.Transport = config.Transport
   283  	}
   284  	if c.Auth != "" {
   285  		var username, password string
   286  		if strings.Contains(c.Auth, ":") {
   287  			split := strings.SplitN(c.Auth, ":", 2)
   288  			username = split[0]
   289  			password = split[1]
   290  		} else {
   291  			username = c.Auth
   292  		}
   293  
   294  		config.HttpAuth = &consul.HttpBasicAuth{
   295  			Username: username,
   296  			Password: password,
   297  		}
   298  	}
   299  	if c.EnableSSL != nil && *c.EnableSSL {
   300  		config.Scheme = "https"
   301  		config.TLSConfig = consul.TLSConfig{
   302  			Address:  config.Address,
   303  			CAFile:   c.CAFile,
   304  			CertFile: c.CertFile,
   305  			KeyFile:  c.KeyFile,
   306  		}
   307  		if c.VerifySSL != nil {
   308  			config.TLSConfig.InsecureSkipVerify = !*c.VerifySSL
   309  		}
   310  		tlsConfig, err := consul.SetupTLSConfig(&config.TLSConfig)
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  		config.Transport.TLSClientConfig = tlsConfig
   315  	}
   316  	if c.Namespace != "" {
   317  		config.Namespace = c.Namespace
   318  	}
   319  	return config, nil
   320  }
   321  
   322  // Copy returns a copy of this Consul config.
   323  func (c *ConsulConfig) Copy() *ConsulConfig {
   324  	if c == nil {
   325  		return nil
   326  	}
   327  
   328  	nc := new(ConsulConfig)
   329  	*nc = *c
   330  
   331  	// Copy the bools
   332  	if nc.AutoAdvertise != nil {
   333  		nc.AutoAdvertise = pointer.Of(*nc.AutoAdvertise)
   334  	}
   335  	if nc.ChecksUseAdvertise != nil {
   336  		nc.ChecksUseAdvertise = pointer.Of(*nc.ChecksUseAdvertise)
   337  	}
   338  	if nc.EnableSSL != nil {
   339  		nc.EnableSSL = pointer.Of(*nc.EnableSSL)
   340  	}
   341  	if nc.VerifySSL != nil {
   342  		nc.VerifySSL = pointer.Of(*nc.VerifySSL)
   343  	}
   344  	if nc.ShareSSL != nil {
   345  		nc.ShareSSL = pointer.Of(*nc.ShareSSL)
   346  	}
   347  	if nc.ServerAutoJoin != nil {
   348  		nc.ServerAutoJoin = pointer.Of(*nc.ServerAutoJoin)
   349  	}
   350  	if nc.ClientAutoJoin != nil {
   351  		nc.ClientAutoJoin = pointer.Of(*nc.ClientAutoJoin)
   352  	}
   353  	if nc.AllowUnauthenticated != nil {
   354  		nc.AllowUnauthenticated = pointer.Of(*nc.AllowUnauthenticated)
   355  	}
   356  
   357  	return nc
   358  }