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 }