2023-03-28 19:12:30 +00:00
// Copyright (c) HashiCorp, Inc.
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-28 19:12:30 +00:00
2018-10-03 19:37:53 +00:00
package envoy
import (
2018-10-05 20:08:01 +00:00
"errors"
2018-10-03 19:37:53 +00:00
"flag"
"fmt"
"net"
"os"
"os/exec"
2022-12-20 17:58:19 +00:00
"strconv"
2018-10-03 19:37:53 +00:00
"strings"
2022-12-19 19:37:27 +00:00
"time"
2018-10-03 19:37:53 +00:00
2023-01-31 21:30:20 +00:00
"github.com/hashicorp/go-hclog"
2023-01-09 20:16:00 +00:00
"github.com/hashicorp/go-version"
2020-03-26 14:19:21 +00:00
"github.com/mitchellh/cli"
2019-04-29 16:27:57 +00:00
"github.com/mitchellh/mapstructure"
2023-01-11 18:40:09 +00:00
"google.golang.org/protobuf/encoding/protojson"
2019-04-29 16:27:57 +00:00
2023-01-16 17:31:56 +00:00
"github.com/hashicorp/consul/acl"
2020-01-17 14:54:17 +00:00
"github.com/hashicorp/consul/agent/structs"
2018-10-03 19:37:53 +00:00
"github.com/hashicorp/consul/agent/xds"
2023-01-09 20:16:00 +00:00
"github.com/hashicorp/consul/agent/xds/accesslogs"
2023-03-06 15:32:06 +00:00
"github.com/hashicorp/consul/api"
2018-10-03 19:37:53 +00:00
proxyCmd "github.com/hashicorp/consul/command/connect/proxy"
"github.com/hashicorp/consul/command/flags"
2023-02-06 17:14:35 +00:00
"github.com/hashicorp/consul/envoyextensions/xdscommon"
2019-06-18 00:52:01 +00:00
"github.com/hashicorp/consul/ipaddr"
2020-09-08 10:16:16 +00:00
"github.com/hashicorp/consul/tlsutil"
2018-10-03 19:37:53 +00:00
)
func New ( ui cli . Ui ) * cmd {
2021-04-07 19:22:52 +00:00
c := & cmd { UI : ui }
2018-10-03 19:37:53 +00:00
c . init ( )
return c
}
2023-07-17 16:10:07 +00:00
const DefaultAdminAccessLogPath = os . DevNull
2019-06-07 09:26:43 +00:00
2018-10-03 19:37:53 +00:00
type cmd struct {
UI cli . Ui
flags * flag . FlagSet
http * flags . HTTPFlags
help string
client * api . Client
2023-01-31 21:30:20 +00:00
logger hclog . Logger
2018-10-03 19:37:53 +00:00
// flags
2022-12-20 17:58:19 +00:00
meshGateway bool
gateway string
proxyID string
nodeName string
sidecarFor string
adminAccessLogPath string
adminBind string
envoyBin string
bootstrap bool
disableCentralConfig bool
grpcAddr string
grpcCAFile string
grpcCAPath string
envoyVersion string
prometheusBackendPort string
prometheusScrapePath string
prometheusCAFile string
prometheusCAPath string
prometheusCertFile string
prometheusKeyFile string
ignoreEnvoyCompatibility bool
2023-01-31 21:30:20 +00:00
enableLogging bool
2019-06-18 00:52:01 +00:00
// mesh gateway registration information
register bool
2020-03-26 14:19:21 +00:00
lanAddress ServiceAddressValue
wanAddress ServiceAddressValue
2019-06-18 00:52:01 +00:00
deregAfterCritical string
2020-03-26 14:19:21 +00:00
bindAddresses ServiceAddressMapValue
2020-03-09 20:59:02 +00:00
exposeServers bool
2020-11-16 23:37:19 +00:00
omitDeprecatedTags bool
2019-06-18 00:52:01 +00:00
2023-01-19 21:54:11 +00:00
envoyReadyBindAddress string
envoyReadyBindPort int
2020-03-26 16:20:56 +00:00
gatewaySvcName string
gatewayKind api . ServiceKind
2022-12-19 19:37:27 +00:00
dialFunc func ( network string , address string ) ( net . Conn , error )
2018-10-03 19:37:53 +00:00
}
2020-07-31 20:52:49 +00:00
const meshGatewayVal = "mesh"
2023-02-06 17:14:35 +00:00
var defaultEnvoyVersion = xdscommon . EnvoyVersions [ 0 ]
2020-03-26 16:20:56 +00:00
var supportedGateways = map [ string ] api . ServiceKind {
2023-02-09 20:28:04 +00:00
"api" : api . ServiceKindAPIGateway ,
2020-03-26 16:20:56 +00:00
"mesh" : api . ServiceKindMeshGateway ,
"terminating" : api . ServiceKindTerminatingGateway ,
2020-04-14 14:48:02 +00:00
"ingress" : api . ServiceKindIngressGateway ,
2020-03-26 16:20:56 +00:00
}
2020-03-23 20:38:14 +00:00
2018-10-03 19:37:53 +00:00
func ( c * cmd ) init ( ) {
c . flags = flag . NewFlagSet ( "" , flag . ContinueOnError )
2020-03-23 14:49:50 +00:00
c . flags . StringVar ( & c . proxyID , "proxy-id" , os . Getenv ( "CONNECT_PROXY_ID" ) ,
2018-10-03 19:37:53 +00:00
"The proxy's ID on the local agent." )
2022-06-06 16:23:08 +00:00
c . flags . StringVar ( & c . nodeName , "node-name" , "" ,
"[Experimental] The node name where the proxy service is registered. It requires proxy-id to be specified. " )
2020-03-26 16:20:56 +00:00
// Deprecated in favor of `gateway`
2019-06-18 00:52:01 +00:00
c . flags . BoolVar ( & c . meshGateway , "mesh-gateway" , false ,
2020-02-11 20:48:58 +00:00
"Configure Envoy as a Mesh Gateway." )
2019-06-18 00:52:01 +00:00
2020-03-26 16:20:56 +00:00
c . flags . StringVar ( & c . gateway , "gateway" , "" ,
2020-05-13 17:56:53 +00:00
"The type of gateway to register. One of: terminating, ingress, or mesh" )
2020-03-26 16:20:56 +00:00
2020-03-23 14:49:50 +00:00
c . flags . StringVar ( & c . sidecarFor , "sidecar-for" , os . Getenv ( "CONNECT_SIDECAR_FOR" ) ,
2018-10-03 19:37:53 +00:00
"The ID of a service instance on the local agent that this proxy should " +
"become a sidecar for. It requires that the proxy service is registered " +
"with the agent as a connect-proxy with Proxy.DestinationServiceID set " +
"to this value. If more than one such proxy is registered it will fail." )
c . flags . StringVar ( & c . envoyBin , "envoy-binary" , "" ,
"The full path to the envoy binary to run. By default will just search " +
"$PATH. Ignored if -bootstrap is used." )
2019-06-07 09:26:43 +00:00
c . flags . StringVar ( & c . adminAccessLogPath , "admin-access-log-path" , DefaultAdminAccessLogPath ,
2023-01-09 20:16:00 +00:00
"DEPRECATED: use proxy-defaults.accessLogs to set Envoy access logs." )
2019-06-07 09:26:43 +00:00
2018-10-03 19:37:53 +00:00
c . flags . StringVar ( & c . adminBind , "admin-bind" , "localhost:19000" ,
"The address:port to start envoy's admin server on. Envoy requires this " +
2019-04-29 16:27:57 +00:00
"but care must be taken to ensure it's not exposed to an untrusted network " +
2018-10-03 19:37:53 +00:00
"as it has full control over the secrets and config of the proxy." )
c . flags . BoolVar ( & c . bootstrap , "bootstrap" , false ,
2018-10-05 20:08:01 +00:00
"Generate the bootstrap.json but don't exec envoy" )
2018-10-03 19:37:53 +00:00
2019-04-29 16:27:57 +00:00
c . flags . BoolVar ( & c . disableCentralConfig , "no-central-config" , false ,
"By default the proxy's bootstrap configuration can be customized " +
"centrally. This requires that the command run on the same agent as the " +
"proxy will and that the agent is reachable when the command is run. In " +
"cases where either assumption is violated this flag will prevent the " +
"command attempting to resolve config from the local agent." )
2020-03-23 14:49:50 +00:00
c . flags . StringVar ( & c . grpcAddr , "grpc-addr" , os . Getenv ( api . GRPCAddrEnvName ) ,
2018-10-03 19:37:53 +00:00
"Set the agent's gRPC address and port (in http(s)://host:port format). " +
"Alternatively, you can specify CONSUL_GRPC_ADDR in ENV." )
2022-11-18 20:36:20 +00:00
c . flags . StringVar ( & c . grpcCAFile , "grpc-ca-file" , os . Getenv ( api . GRPCCAFileEnvName ) ,
"Path to a CA file to use for TLS when communicating with the Consul agent through xDS. This " +
"can also be specified via the CONSUL_GRPC_CACERT environment variable." )
c . flags . StringVar ( & c . grpcCAPath , "grpc-ca-path" , os . Getenv ( api . GRPCCAPathEnvName ) ,
"Path to a directory of CA certificates to use for TLS when communicating " +
"with the Consul agent through xDS. This can also be specified via the " +
"CONSUL_GRPC_CAPATH environment variable." )
2021-10-12 02:18:56 +00:00
// Deprecated, no longer needed, keeping it around to not break back compat
2020-03-23 20:38:14 +00:00
c . flags . StringVar ( & c . envoyVersion , "envoy-version" , defaultEnvoyVersion ,
2021-10-12 02:18:56 +00:00
"This is a legacy flag that is currently not used but was formerly used to set the " +
"version for the envoy binary that gets invoked by Consul. This is no longer " +
"necessary as Consul will invoke the binary at a path set by -envoy-binary " +
"or whichever envoy binary it finds in $PATH" )
2020-02-10 19:53:04 +00:00
2019-06-18 00:52:01 +00:00
c . flags . BoolVar ( & c . register , "register" , false ,
2020-04-02 18:52:11 +00:00
"Register a new gateway service before configuring and starting Envoy" )
2019-06-18 00:52:01 +00:00
2020-03-26 14:19:21 +00:00
c . flags . Var ( & c . lanAddress , "address" ,
2020-04-02 18:52:11 +00:00
"LAN address to advertise in the gateway service registration" )
2019-06-18 00:52:01 +00:00
2023-01-19 21:54:11 +00:00
c . flags . StringVar ( & c . envoyReadyBindAddress , "envoy-ready-bind-address" , "" ,
"The address on which Envoy's readiness probe is available." )
c . flags . IntVar ( & c . envoyReadyBindPort , "envoy-ready-bind-port" , 0 ,
"The port on which Envoy's readiness probe is available." )
2020-03-26 14:19:21 +00:00
c . flags . Var ( & c . wanAddress , "wan-address" ,
2020-05-21 14:08:12 +00:00
"WAN address to advertise in the gateway service registration. For ingress gateways, " +
"only an IP address (without a port) is required." )
2019-06-18 00:52:01 +00:00
2020-03-26 14:19:21 +00:00
c . flags . Var ( & c . bindAddresses , "bind-address" , "Bind " +
2019-07-12 16:57:31 +00:00
"address to use instead of the default binding rules given as `<name>=<ip>:<port>` " +
"pairs. This flag may be specified multiple times to add multiple bind addresses." )
2020-03-26 16:20:56 +00:00
c . flags . StringVar ( & c . gatewaySvcName , "service" , "" ,
2019-06-18 00:52:01 +00:00
"Service name to use for the registration" )
2020-03-09 20:59:02 +00:00
c . flags . BoolVar ( & c . exposeServers , "expose-servers" , false ,
"Expose the servers for WAN federation via this mesh gateway" )
2019-06-18 00:52:01 +00:00
c . flags . StringVar ( & c . deregAfterCritical , "deregister-after-critical" , "6h" ,
"The amount of time the gateway services health check can be failing before being deregistered" )
2020-11-16 23:37:19 +00:00
c . flags . BoolVar ( & c . omitDeprecatedTags , "omit-deprecated-tags" , false ,
"In Consul 1.9.0 the format of metric tags for Envoy clusters was updated from consul.[service|dc|...] to " +
"consul.destination.[service|dc|...]. The old tags were preserved for backward compatibility," +
"but can be disabled with this flag." )
2021-03-04 22:15:47 +00:00
c . flags . StringVar ( & c . prometheusBackendPort , "prometheus-backend-port" , "" ,
"Sets the backend port for the 'prometheus_backend' cluster that envoy_prometheus_bind_addr will point to. " +
"Without this flag, envoy_prometheus_bind_addr would point to the 'self_admin' cluster where Envoy metrics are exposed. " +
"The metrics merging feature in consul-k8s uses this to point to the merged metrics endpoint combining Envoy and service metrics. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
c . flags . StringVar ( & c . prometheusScrapePath , "prometheus-scrape-path" , "/metrics" ,
"Sets the path where Envoy will expose metrics on envoy_prometheus_bind_addr listener. " +
"For example, if envoy_prometheus_bind_addr is 0.0.0.0:20200, and this flag is " +
"set to /scrape-metrics, prometheus metrics would be scrapeable at " +
"0.0.0.0:20200/scrape-metrics. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
2022-06-17 00:18:37 +00:00
c . flags . StringVar ( & c . prometheusCAFile , "prometheus-ca-file" , "" ,
"Path to a CA file for Envoy to use when serving TLS on the Prometheus metrics endpoint. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
c . flags . StringVar ( & c . prometheusCAPath , "prometheus-ca-path" , "" ,
"Path to a directory of CA certificates for Envoy to use when serving the Prometheus metrics endpoint. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
c . flags . StringVar ( & c . prometheusCertFile , "prometheus-cert-file" , "" ,
"Path to a certificate file for Envoy to use when serving TLS on the Prometheus metrics endpoint. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
c . flags . StringVar ( & c . prometheusKeyFile , "prometheus-key-file" , "" ,
"Path to a private key file for Envoy to use when serving TLS on the Prometheus metrics endpoint. " +
"Only applicable when envoy_prometheus_bind_addr is set in proxy config." )
2022-12-20 17:58:19 +00:00
c . flags . BoolVar ( & c . ignoreEnvoyCompatibility , "ignore-envoy-compatibility" , false ,
"If set to `true`, this flag ignores the Envoy version compatibility check. We recommend setting this " +
"flag to `false` to ensure compatibility with Envoy and prevent potential issues. " +
"Default is `false`." )
2022-06-17 00:18:37 +00:00
2023-01-31 21:30:20 +00:00
c . flags . BoolVar ( & c . enableLogging , "enable-config-gen-logging" , false ,
"Output debug log messages during config generation" )
2018-10-03 19:37:53 +00:00
c . http = & flags . HTTPFlags { }
flags . Merge ( c . flags , c . http . ClientFlags ( ) )
2021-07-21 19:45:24 +00:00
flags . Merge ( c . flags , c . http . MultiTenancyFlags ( ) )
2018-10-03 19:37:53 +00:00
c . help = flags . Usage ( help , c . flags )
2022-12-19 19:37:27 +00:00
c . dialFunc = func ( network string , address string ) ( net . Conn , error ) {
return net . DialTimeout ( network , address , 3 * time . Second )
}
2018-10-03 19:37:53 +00:00
}
2019-07-30 13:56:56 +00:00
// canBindInternal is here mainly so we can unit test this with a constant net.Addr list
func canBindInternal ( addr string , ifAddrs [ ] net . Addr ) bool {
2019-07-12 16:57:31 +00:00
if addr == "" {
return false
}
ip := net . ParseIP ( addr )
if ip == nil {
return false
}
2019-07-30 13:56:56 +00:00
ipStr := ip . String ( )
2019-07-12 16:57:31 +00:00
for _ , addr := range ifAddrs {
2019-07-30 13:56:56 +00:00
switch v := addr . ( type ) {
case * net . IPNet :
if v . IP . String ( ) == ipStr {
return true
}
default :
if addr . String ( ) == ipStr {
return true
}
2019-07-12 16:57:31 +00:00
}
}
return false
}
2020-03-26 14:19:21 +00:00
func canBind ( addr api . ServiceAddress ) bool {
2019-07-30 13:56:56 +00:00
ifAddrs , err := net . InterfaceAddrs ( )
if err != nil {
return false
}
2020-03-26 14:19:21 +00:00
return canBindInternal ( addr . Address , ifAddrs )
2019-07-30 13:56:56 +00:00
}
2018-10-03 19:37:53 +00:00
func ( c * cmd ) Run ( args [ ] string ) int {
if err := c . flags . Parse ( args ) ; err != nil {
return 1
}
// Setup Consul client
2020-04-07 22:02:56 +00:00
var err error
c . client , err = c . http . APIClient ( )
2018-10-03 19:37:53 +00:00
if err != nil {
c . UI . Error ( fmt . Sprintf ( "Error connecting to Consul agent: %s" , err ) )
return 1
}
2023-01-31 21:30:20 +00:00
2020-04-07 22:02:56 +00:00
// TODO: refactor
return c . run ( c . flags . Args ( ) )
}
2018-10-03 19:37:53 +00:00
2020-04-07 22:02:56 +00:00
func ( c * cmd ) run ( args [ ] string ) int {
2023-02-03 23:12:02 +00:00
opts := hclog . LoggerOptions { Level : hclog . Off }
if c . enableLogging {
opts . Level = hclog . Debug
}
c . logger = hclog . New ( & opts )
c . logger . Debug ( "Starting Envoy config generation" )
2022-06-06 16:23:08 +00:00
if c . nodeName != "" && c . proxyID == "" {
c . UI . Error ( "'-node-name' requires '-proxy-id'" )
return 1
}
2020-03-26 16:20:56 +00:00
// Fixup for deprecated mesh-gateway flag
if c . meshGateway && c . gateway != "" {
c . UI . Error ( "The mesh-gateway flag is deprecated and cannot be used alongside the gateway flag" )
return 1
}
2020-04-14 14:48:02 +00:00
2020-03-26 16:20:56 +00:00
if c . meshGateway {
c . gateway = meshGatewayVal
}
2020-03-09 20:59:02 +00:00
if c . exposeServers {
2020-03-26 16:20:56 +00:00
if c . gateway != meshGatewayVal {
2020-03-09 20:59:02 +00:00
c . UI . Error ( "'-expose-servers' can only be used for mesh gateways" )
return 1
}
if ! c . register {
c . UI . Error ( "'-expose-servers' requires '-register'" )
return 1
}
}
2020-04-02 18:52:11 +00:00
// Gateway kind is set so that it is available even if not auto-registering the gateway
if c . gateway != "" {
2020-03-26 16:20:56 +00:00
kind , ok := supportedGateways [ c . gateway ]
if ! ok {
2023-02-09 20:28:04 +00:00
c . UI . Error ( "Gateway must be one of: api, terminating, mesh, or ingress" )
2019-06-18 00:52:01 +00:00
return 1
}
2020-03-26 16:20:56 +00:00
c . gatewayKind = kind
2020-05-13 17:56:53 +00:00
if c . gatewaySvcName == "" {
c . gatewaySvcName = string ( c . gatewayKind )
}
}
2023-08-10 18:00:44 +00:00
var svcForSidecar api . AgentService
2020-05-13 17:56:53 +00:00
if c . proxyID == "" {
switch {
case c . sidecarFor != "" :
2023-08-10 18:00:44 +00:00
svcForSidecar , err := proxyCmd . LookupServiceForSidecar ( c . client , c . sidecarFor )
2020-05-13 17:56:53 +00:00
if err != nil {
c . UI . Error ( err . Error ( ) )
return 1
}
2023-08-10 18:00:44 +00:00
c . proxyID = svcForSidecar . ID
2020-05-13 17:56:53 +00:00
case c . gateway != "" && ! c . register :
gatewaySvc , err := proxyCmd . LookupGatewayProxy ( c . client , c . gatewayKind )
if err != nil {
c . UI . Error ( err . Error ( ) )
return 1
}
c . proxyID = gatewaySvc . ID
c . gatewaySvcName = gatewaySvc . Service
case c . gateway != "" && c . register :
c . proxyID = c . gatewaySvcName
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Set Proxy ID" , "proxy-id" , c . proxyID )
2020-05-13 17:56:53 +00:00
}
if c . proxyID == "" {
c . UI . Error ( "No proxy ID specified. One of -proxy-id, -sidecar-for, or -gateway is " +
"required" )
return 1
2020-04-02 18:52:11 +00:00
}
2022-06-17 00:18:37 +00:00
// If any of CA/Cert/Key are specified, make sure they are all present.
if c . prometheusKeyFile != "" || c . prometheusCertFile != "" || ( c . prometheusCAFile != "" || c . prometheusCAPath != "" ) {
if c . prometheusKeyFile == "" || c . prometheusCertFile == "" || ( c . prometheusCAFile == "" && c . prometheusCAPath == "" ) {
c . UI . Error ( "Must provide a CA (-prometheus-ca-file or -prometheus-ca-path) as well as " +
"-prometheus-cert-file and -prometheus-key-file to enable TLS for prometheus metrics" )
return 1
}
}
2020-04-02 18:52:11 +00:00
if c . register {
2022-06-06 16:23:08 +00:00
if c . nodeName != "" {
c . UI . Error ( "'-register' cannot be used with '-node-name'" )
return 1
}
2020-04-02 18:52:11 +00:00
if c . gateway == "" {
c . UI . Error ( "Auto-Registration can only be used for gateways" )
return 1
}
2020-03-26 16:20:56 +00:00
2023-08-10 18:00:44 +00:00
svc , err := c . proxyRegistration ( & svcForSidecar )
if err != nil {
c . UI . Error ( err . Error ( ) )
2019-07-12 16:57:31 +00:00
return 1
2019-06-18 00:52:01 +00:00
}
2023-08-10 18:00:44 +00:00
if err := c . client . Agent ( ) . ServiceRegister ( svc ) ; err != nil {
2019-06-18 00:52:01 +00:00
c . UI . Error ( fmt . Sprintf ( "Error registering service %q: %s" , svc . Name , err ) )
return 1
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Proxy registration complete" )
2019-06-18 00:52:01 +00:00
2021-04-07 19:22:52 +00:00
if ! c . bootstrap {
// We need stdout to be reserved exclusively for the JSON blob, so
// we omit logging this to Info which also writes to stdout.
c . UI . Info ( fmt . Sprintf ( "Registered service: %s" , svc . Name ) )
}
2019-06-18 00:52:01 +00:00
}
2023-01-09 20:16:00 +00:00
if c . adminAccessLogPath != DefaultAdminAccessLogPath {
c . UI . Warn ( "-admin-access-log-path is deprecated and will be removed in a future version of Consul. " +
"Configure access logging with proxy-defaults.accessLogs." )
}
2018-10-03 19:37:53 +00:00
// Generate config
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Generating bootstrap config" )
2018-10-05 20:08:01 +00:00
bootstrapJson , err := c . generateConfig ( )
2018-10-03 19:37:53 +00:00
if err != nil {
c . UI . Error ( err . Error ( ) )
return 1
}
if c . bootstrap {
// Just output it and we are done
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Outputting bootstrap config" )
2021-04-07 19:22:52 +00:00
c . UI . Output ( string ( bootstrapJson ) )
2018-10-03 19:37:53 +00:00
return 0
}
// Find Envoy binary
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Finding envoy binary" )
2018-10-03 19:37:53 +00:00
binary , err := c . findBinary ( )
if err != nil {
c . UI . Error ( "Couldn't find envoy binary: " + err . Error ( ) )
return 1
}
2022-12-20 17:58:19 +00:00
// Check if envoy version is supported
if ! c . ignoreEnvoyCompatibility {
v , err := execEnvoyVersion ( binary )
if err != nil {
c . UI . Warn ( "Couldn't get envoy version for compatibility check: " + err . Error ( ) )
return 1
}
2023-03-03 18:29:34 +00:00
ec , err := checkEnvoyVersionCompatibility ( v , xdscommon . UnsupportedEnvoyVersions )
2022-12-20 17:58:19 +00:00
if err != nil {
c . UI . Warn ( "There was an error checking the compatibility of the envoy version: " + err . Error ( ) )
2023-03-03 18:29:34 +00:00
} else if ! ec . isCompatible {
2022-12-20 17:58:19 +00:00
c . UI . Error ( fmt . Sprintf ( "Envoy version %s is not supported. If there is a reason you need to use " +
"this version of envoy use the ignore-envoy-compatibility flag. Using an unsupported version of Envoy " +
"is not recommended and your experience may vary. For more information on compatibility " +
2023-03-03 18:29:34 +00:00
"see https://developer.hashicorp.com/consul/docs/connect/proxies/envoy#envoy-and-consul-client-agent" , ec . versionIncompatible ) )
2022-12-20 17:58:19 +00:00
return 1
}
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Executing envoy binary" )
2020-04-07 22:02:56 +00:00
err = execEnvoy ( binary , nil , args , bootstrapJson )
2018-10-05 20:08:01 +00:00
if err == errUnsupportedOS {
c . UI . Error ( "Directly running Envoy is only supported on linux and macOS " +
"since envoy itself doesn't build on other platforms currently." )
c . UI . Error ( "Use the -bootstrap option to generate the JSON to use when running envoy " +
"on a supported OS or via a container or VM." )
return 1
} else if err != nil {
c . UI . Error ( err . Error ( ) )
2018-10-03 19:37:53 +00:00
return 1
}
2018-10-05 20:08:01 +00:00
2018-10-03 19:37:53 +00:00
return 0
}
2023-08-10 18:00:44 +00:00
func ( c * cmd ) proxyRegistration ( svcForSidecar * api . AgentService ) ( * api . AgentServiceRegistration , error ) {
taggedAddrs := make ( map [ string ] api . ServiceAddress )
lanAddr := c . lanAddress . Value ( )
if lanAddr . Address != "" {
taggedAddrs [ structs . TaggedAddressLAN ] = lanAddr
}
wanAddr := c . wanAddress . Value ( )
if wanAddr . Address != "" {
taggedAddrs [ structs . TaggedAddressWAN ] = wanAddr
}
tcpCheckAddr := lanAddr . Address
if tcpCheckAddr == "" {
// fallback to localhost as the gateway has to reside in the same network namespace
// as the agent
tcpCheckAddr = "127.0.0.1"
}
var proxyConf * api . AgentServiceConnectProxyConfig
if len ( c . bindAddresses . value ) > 0 {
// override all default binding rules and just bind to the user-supplied addresses
proxyConf = & api . AgentServiceConnectProxyConfig {
Config : map [ string ] interface { } {
"envoy_gateway_no_default_bind" : true ,
"envoy_gateway_bind_addresses" : c . bindAddresses . value ,
} ,
}
} else if canBind ( lanAddr ) && canBind ( wanAddr ) {
// when both addresses are bindable then we bind to the tagged addresses
// for creating the envoy listeners
proxyConf = & api . AgentServiceConnectProxyConfig {
Config : map [ string ] interface { } {
"envoy_gateway_no_default_bind" : true ,
"envoy_gateway_bind_tagged_addresses" : true ,
} ,
}
} else if ! canBind ( lanAddr ) && lanAddr . Address != "" {
return nil , fmt . Errorf ( "The LAN address %q will not be bindable. Either set a bindable address or override the bind addresses with -bind-address" , lanAddr . Address )
}
var meta map [ string ] string
if c . exposeServers {
meta = map [ string ] string { structs . MetaWANFederationKey : "1" }
}
// API gateways do not have a default listener or ready endpoint,
// so adding any check to the registration will fail
var check * api . AgentServiceCheck
if c . gatewayKind != api . ServiceKindAPIGateway {
check = & api . AgentServiceCheck {
Name : fmt . Sprintf ( "%s listening" , c . gatewayKind ) ,
TCP : ipaddr . FormatAddressPort ( tcpCheckAddr , lanAddr . Port ) ,
Interval : "10s" ,
DeregisterCriticalServiceAfter : c . deregAfterCritical ,
}
}
// If registering a sidecar for an existing service, inherit the
// locality of that service if it was explicitly configured.
var locality * api . Locality
if c . sidecarFor != "" {
locality = svcForSidecar . Locality
}
return & api . AgentServiceRegistration {
Kind : c . gatewayKind ,
Name : c . gatewaySvcName ,
ID : c . proxyID ,
Address : lanAddr . Address ,
Port : lanAddr . Port ,
Meta : meta ,
TaggedAddresses : taggedAddrs ,
Proxy : proxyConf ,
Check : check ,
Locality : locality ,
} , nil
}
2018-10-05 20:08:01 +00:00
var errUnsupportedOS = errors . New ( "envoy: not implemented on this operating system" )
2018-10-03 19:37:53 +00:00
func ( c * cmd ) findBinary ( ) ( string , error ) {
if c . envoyBin != "" {
return c . envoyBin , nil
}
return exec . LookPath ( "envoy" )
}
2019-04-29 16:27:57 +00:00
func ( c * cmd ) templateArgs ( ) ( * BootstrapTplArgs , error ) {
2018-10-03 19:37:53 +00:00
httpCfg := api . DefaultConfig ( )
c . http . MergeOntoConfig ( httpCfg )
2020-04-07 22:16:34 +00:00
// api.NewClient normalizes some values (Token, Scheme) on the Config.
2019-04-30 14:59:00 +00:00
if _ , err := api . NewClient ( httpCfg ) ; err != nil {
return nil , err
}
2022-11-18 20:36:20 +00:00
xdsAddr , err := c . xdsAddress ( )
2020-04-07 20:33:22 +00:00
if err != nil {
return nil , err
2018-10-03 19:37:53 +00:00
}
2023-01-06 18:34:49 +00:00
// Bootstrapping should not attempt to dial the address, since the template
// may be generated and passed to another host (Nomad is one example).
if ! c . bootstrap {
if err := checkDial ( xdsAddr , c . dialFunc ) ; err != nil {
c . UI . Warn ( "There was an error dialing the xDS address: " + err . Error ( ) )
}
2022-12-19 19:37:27 +00:00
}
2018-10-03 19:37:53 +00:00
adminAddr , adminPort , err := net . SplitHostPort ( c . adminBind )
if err != nil {
2018-10-05 20:08:01 +00:00
return nil , fmt . Errorf ( "Invalid Consul HTTP address: %s" , err )
2018-10-03 19:37:53 +00:00
}
// Envoy requires IP addresses to bind too when using static so resolve DNS or
// localhost here.
adminBindIP , err := net . ResolveIPAddr ( "ip" , adminAddr )
if err != nil {
2018-10-05 20:08:01 +00:00
return nil , fmt . Errorf ( "Failed to resolve admin bind address: %s" , err )
2018-10-03 19:37:53 +00:00
}
2019-04-29 16:27:57 +00:00
// Ideally the cluster should be the service name. We may or may not have that
// yet depending on the arguments used so make a best effort here. In the
// common case, even if the command was invoked with proxy-id and we don't
// know service name yet, we will after we resolve the proxy's config in a bit
// and will update this then.
cluster := c . proxyID
2020-11-16 23:37:19 +00:00
proxySourceService := ""
2019-04-29 16:27:57 +00:00
if c . sidecarFor != "" {
cluster = c . sidecarFor
2020-11-16 23:37:19 +00:00
proxySourceService = c . sidecarFor
2020-03-26 16:20:56 +00:00
} else if c . gateway != "" && c . gatewaySvcName != "" {
cluster = c . gatewaySvcName
2020-11-16 23:37:19 +00:00
proxySourceService = c . gatewaySvcName
2019-04-29 16:27:57 +00:00
}
2019-06-07 09:26:43 +00:00
adminAccessLogPath := c . adminAccessLogPath
if adminAccessLogPath == "" {
adminAccessLogPath = DefaultAdminAccessLogPath
}
2022-11-18 20:36:20 +00:00
// Fallback to the old certificate configuration, if none was defined.
if xdsAddr . AgentTLS && c . grpcCAFile == "" {
c . grpcCAFile = httpCfg . TLSConfig . CAFile
}
if xdsAddr . AgentTLS && c . grpcCAPath == "" {
c . grpcCAPath = httpCfg . TLSConfig . CAPath
}
2020-01-10 14:57:54 +00:00
var caPEM string
2022-11-18 20:36:20 +00:00
pems , err := tlsutil . LoadCAs ( c . grpcCAFile , c . grpcCAPath )
2020-09-08 10:16:16 +00:00
if err != nil {
return nil , err
2019-12-13 16:44:48 +00:00
}
2020-09-08 10:16:16 +00:00
caPEM = strings . Replace ( strings . Join ( pems , "" ) , "\n" , "\\n" , - 1 )
2019-12-13 16:44:48 +00:00
2019-04-29 16:27:57 +00:00
return & BootstrapTplArgs {
2021-07-09 19:17:29 +00:00
GRPC : xdsAddr ,
2019-04-29 16:27:57 +00:00
ProxyCluster : cluster ,
2018-10-03 19:37:53 +00:00
ProxyID : c . proxyID ,
2022-06-06 16:23:08 +00:00
NodeName : c . nodeName ,
2020-11-16 23:37:19 +00:00
ProxySourceService : proxySourceService ,
2019-12-13 16:44:48 +00:00
AgentCAPEM : caPEM ,
2019-06-07 09:26:43 +00:00
AdminAccessLogPath : adminAccessLogPath ,
2018-10-03 19:37:53 +00:00
AdminBindAddress : adminBindIP . String ( ) ,
AdminBindPort : adminPort ,
Token : httpCfg . Token ,
LocalAgentClusterName : xds . LocalAgentClusterName ,
2020-01-24 15:04:58 +00:00
Namespace : httpCfg . Namespace ,
2021-09-15 01:37:11 +00:00
Partition : httpCfg . Partition ,
2020-11-19 21:27:31 +00:00
Datacenter : httpCfg . Datacenter ,
2021-03-04 22:15:47 +00:00
PrometheusBackendPort : c . prometheusBackendPort ,
PrometheusScrapePath : c . prometheusScrapePath ,
2022-06-17 00:18:37 +00:00
PrometheusCAFile : c . prometheusCAFile ,
PrometheusCAPath : c . prometheusCAPath ,
PrometheusCertFile : c . prometheusCertFile ,
PrometheusKeyFile : c . prometheusKeyFile ,
2018-10-09 09:57:26 +00:00
} , nil
}
2018-10-03 19:37:53 +00:00
2018-10-09 09:57:26 +00:00
func ( c * cmd ) generateConfig ( ) ( [ ] byte , error ) {
args , err := c . templateArgs ( )
if err != nil {
return nil , err
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Generated template args" )
2019-04-29 16:27:57 +00:00
var bsCfg BootstrapConfig
2023-01-16 17:31:56 +00:00
// Make a call to an arbitrary ACL endpoint. If we get back an ErrNotFound
// (meaning ACLs are enabled) check that the token is not empty.
if _ , _ , err := c . client . ACL ( ) . TokenReadSelf (
& api . QueryOptions { Token : args . Token } ,
) ; acl . IsErrNotFound ( err ) {
if args . Token == "" {
c . UI . Warn ( "No ACL token was provided to Envoy. Because the ACL system is enabled, pass a suitable ACL token for this service to Envoy to avoid potential communication failure." )
}
}
2020-11-16 23:37:19 +00:00
// Fetch any customization from the registration
2022-06-06 16:23:08 +00:00
var svcProxyConfig * api . AgentServiceConnectProxyConfig
var serviceName , ns , partition , datacenter string
if c . nodeName == "" {
svc , _ , err := c . client . Agent ( ) . Service ( c . proxyID , nil )
if err != nil {
return nil , fmt . Errorf ( "failed fetch proxy config from local agent: %s" , err )
}
svcProxyConfig = svc . Proxy
serviceName = svc . Service
ns = svc . Namespace
partition = svc . Partition
datacenter = svc . Datacenter
} else {
filter := fmt . Sprintf ( "ID == %q" , c . proxyID )
2022-06-15 15:30:31 +00:00
svcList , _ , err := c . client . Catalog ( ) . NodeServiceList ( c . nodeName ,
& api . QueryOptions { Filter : filter , MergeCentralConfig : true } )
2022-06-06 16:23:08 +00:00
if err != nil {
return nil , fmt . Errorf ( "failed to fetch proxy config from catalog for node %q: %w" , c . nodeName , err )
}
2022-06-15 15:30:31 +00:00
if len ( svcList . Services ) == 0 {
return nil , fmt . Errorf ( "Proxy service with ID %q not found" , c . proxyID )
2022-06-06 16:23:08 +00:00
}
2022-06-15 15:30:31 +00:00
if len ( svcList . Services ) > 1 {
return nil , fmt . Errorf ( "Expected to find only one proxy service with ID %q, but more were found" , c . proxyID )
}
2022-06-06 16:23:08 +00:00
svcProxyConfig = svcList . Services [ 0 ] . Proxy
serviceName = svcList . Services [ 0 ] . Service
ns = svcList . Services [ 0 ] . Namespace
partition = svcList . Services [ 0 ] . Partition
datacenter = svcList . Node . Datacenter
c . gatewayKind = svcList . Services [ 0 ] . Kind
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Fetched registration info" )
2022-06-06 16:23:08 +00:00
if svcProxyConfig == nil {
2020-11-16 23:37:19 +00:00
return nil , errors . New ( "service is not a Connect proxy or gateway" )
}
2019-04-29 16:27:57 +00:00
2022-06-06 16:23:08 +00:00
if svcProxyConfig . DestinationServiceName != "" {
2020-11-16 23:37:19 +00:00
// Override cluster now we know the actual service name
2022-06-06 16:23:08 +00:00
args . ProxyCluster = svcProxyConfig . DestinationServiceName
args . ProxySourceService = svcProxyConfig . DestinationServiceName
2020-11-16 23:37:19 +00:00
} else {
// Set the source service name from the proxy's own registration
2022-06-06 16:23:08 +00:00
args . ProxySourceService = serviceName
2020-11-16 23:37:19 +00:00
}
2020-11-19 21:27:31 +00:00
2021-09-15 21:17:11 +00:00
// In most cases where namespaces and partitions are enabled they will already be set
// correctly because the http client that fetched this will provide them explicitly.
// However, if these arguments were not provided, they will be empty even
// though Namespaces and Partitions are actually being used.
// Overriding them ensures that we always set the Namespace and Partition args
// if the cluster is using them. This prevents us from defaulting to the "default"
// when a non-default partition or namespace was inferred from the ACL token.
2022-06-06 16:23:08 +00:00
if ns != "" {
args . Namespace = ns
2021-09-15 21:17:11 +00:00
}
2022-06-06 16:23:08 +00:00
if partition != "" {
args . Partition = partition
2021-09-15 21:17:11 +00:00
}
2022-06-06 16:23:08 +00:00
if datacenter != "" {
2020-11-19 21:27:31 +00:00
// The agent will definitely have the definitive answer here.
2022-06-06 16:23:08 +00:00
args . Datacenter = datacenter
}
2023-01-09 20:16:00 +00:00
if err := generateAccessLogs ( c , args ) ; err != nil {
return nil , err
}
2023-01-31 21:30:20 +00:00
c . logger . Debug ( "Generated access logs" )
2023-01-09 20:16:00 +00:00
2022-06-06 16:23:08 +00:00
// Setup ready listener for ingress gateway to pass healthcheck
if c . gatewayKind == api . ServiceKindIngressGateway {
lanAddr := c . lanAddress . String ( )
// Deal with possibility of address not being specified and defaulting to
// ":443"
if strings . HasPrefix ( lanAddr , ":" ) {
lanAddr = "127.0.0.1" + lanAddr
}
bsCfg . ReadyBindAddr = lanAddr
2020-11-16 23:37:19 +00:00
}
2019-04-29 16:27:57 +00:00
2023-01-19 21:54:11 +00:00
if c . envoyReadyBindAddress != "" && c . envoyReadyBindPort != 0 {
bsCfg . ReadyBindAddr = fmt . Sprintf ( "%s:%d" , c . envoyReadyBindAddress , c . envoyReadyBindPort )
}
2020-11-16 23:37:19 +00:00
if ! c . disableCentralConfig {
2019-04-29 16:27:57 +00:00
// Parse the bootstrap config
2022-06-06 16:23:08 +00:00
if err := mapstructure . WeakDecode ( svcProxyConfig . Config , & bsCfg ) ; err != nil {
2019-04-29 16:27:57 +00:00
return nil , fmt . Errorf ( "failed parsing Proxy.Config: %s" , err )
}
2018-10-03 19:37:53 +00:00
}
2019-04-29 16:27:57 +00:00
2020-11-16 23:37:19 +00:00
return bsCfg . GenerateJSON ( args , c . omitDeprecatedTags )
2018-10-03 19:37:53 +00:00
}
2023-01-09 20:16:00 +00:00
// generateAccessLogs checks if there is any access log customization from proxy-defaults.
// If available, access log parameters are marshaled to JSON and added to the bootstrap template args.
func generateAccessLogs ( c * cmd , args * BootstrapTplArgs ) error {
configEntry , _ , err := c . client . ConfigEntries ( ) . Get ( api . ProxyDefaults , api . ProxyConfigGlobal , & api . QueryOptions { } ) // Always assume the default partition
// We don't necessarily want to fail here if there isn't a proxy-defaults defined or if there
// is a server error.
var statusE api . StatusError
if err != nil && ! errors . As ( err , & statusE ) {
return fmt . Errorf ( "failed fetch proxy-defaults: %w" , err )
}
if configEntry != nil {
proxyDefaults , ok := configEntry . ( * api . ProxyConfigEntry )
if ! ok {
return fmt . Errorf ( "config entry %s is not a valid proxy-default" , configEntry . GetName ( ) )
}
if proxyDefaults . AccessLogs != nil {
2023-01-11 18:40:09 +00:00
AccessLogsConfig := & structs . AccessLogsConfig {
2023-01-09 20:16:00 +00:00
Enabled : proxyDefaults . AccessLogs . Enabled ,
DisableListenerLogs : false ,
Type : structs . LogSinkType ( proxyDefaults . AccessLogs . Type ) ,
JSONFormat : proxyDefaults . AccessLogs . JSONFormat ,
TextFormat : proxyDefaults . AccessLogs . TextFormat ,
Path : proxyDefaults . AccessLogs . Path ,
}
2023-01-11 18:40:09 +00:00
envoyLoggers , err := accesslogs . MakeAccessLogs ( AccessLogsConfig , false )
2023-01-09 20:16:00 +00:00
if err != nil {
return fmt . Errorf ( "failure generating Envoy access log configuration: %w" , err )
}
// Convert individual proto messages to JSON here
2023-01-11 18:40:09 +00:00
args . AdminAccessLogConfig = make ( [ ] string , 0 , len ( envoyLoggers ) )
2023-01-09 20:16:00 +00:00
2023-01-11 18:40:09 +00:00
for _ , msg := range envoyLoggers {
logConfig , err := protojson . Marshal ( msg )
2023-01-09 20:16:00 +00:00
if err != nil {
return fmt . Errorf ( "could not marshal Envoy access log configuration: %w" , err )
}
2023-01-11 18:40:09 +00:00
args . AdminAccessLogConfig = append ( args . AdminAccessLogConfig , string ( logConfig ) )
2023-01-09 20:16:00 +00:00
}
}
if proxyDefaults . AccessLogs != nil && c . adminAccessLogPath != DefaultAdminAccessLogPath {
c . UI . Warn ( "-admin-access-log-path and proxy-defaults.accessLogs both specify Envoy access log configuration. Ignoring the deprecated -admin-access-log-path flag." )
}
}
return nil
}
2022-11-18 20:36:20 +00:00
func ( c * cmd ) xdsAddress ( ) ( GRPC , error ) {
2020-04-07 20:33:22 +00:00
g := GRPC { }
addr := c . grpcAddr
if addr == "" {
2022-12-19 19:37:27 +00:00
// This lookup is a UX optimization and requires acl policy agent:read,
// which sidecars may not have.
2022-08-19 17:07:22 +00:00
port , protocol , err := c . lookupXDSPort ( )
2020-04-07 20:33:22 +00:00
if err != nil {
2022-12-19 19:37:27 +00:00
if strings . Contains ( err . Error ( ) , "Permission denied" ) {
2023-03-06 15:32:06 +00:00
// Token did not have agent:read. Suppress and proceed with defaults.
2022-12-19 19:37:27 +00:00
} else {
// If not a permission denied error, gRPC is explicitly disabled
// or something went fatally wrong.
return g , fmt . Errorf ( "Error looking up xDS port: %s" , err )
}
2020-04-07 20:33:22 +00:00
}
if port <= 0 {
// This is the dev mode default and recommended production setting if
// enabled.
port = 8502
2023-03-06 15:32:06 +00:00
c . UI . Warn ( "-grpc-addr not provided and unable to discover a gRPC address for xDS. Defaulting to localhost:8502" )
2020-04-07 20:33:22 +00:00
}
2022-08-19 17:07:22 +00:00
addr = fmt . Sprintf ( "%vlocalhost:%v" , protocol , port )
2020-04-07 20:33:22 +00:00
}
2020-04-07 22:16:34 +00:00
// TODO: parse addr as a url instead of strings.HasPrefix/TrimPrefix
2022-11-18 20:36:20 +00:00
if strings . HasPrefix ( strings . ToLower ( addr ) , "https://" ) {
2020-04-07 20:33:22 +00:00
g . AgentTLS = true
}
// We want to allow grpcAddr set as host:port with no scheme but if the host
// is an IP this will fail to parse as a URL with "parse 127.0.0.1:8500: first
// path segment in URL cannot contain colon". On the other hand we also
// support both http(s)://host:port and unix:///path/to/file.
if grpcAddr := strings . TrimPrefix ( addr , "unix://" ) ; grpcAddr != addr {
// Path to unix socket
g . AgentSocket = grpcAddr
2023-01-06 18:34:49 +00:00
// Configure unix sockets to encrypt traffic whenever a certificate is explicitly defined.
if c . grpcCAFile != "" || c . grpcCAPath != "" {
g . AgentTLS = true
}
2020-04-07 20:33:22 +00:00
} else {
// Parse as host:port with option http prefix
grpcAddr = strings . TrimPrefix ( addr , "http://" )
2020-05-14 18:41:20 +00:00
grpcAddr = strings . TrimPrefix ( grpcAddr , "https://" )
2020-04-07 20:33:22 +00:00
var err error
2020-04-07 22:16:34 +00:00
var host string
host , g . AgentPort , err = net . SplitHostPort ( grpcAddr )
2020-04-07 20:33:22 +00:00
if err != nil {
return g , fmt . Errorf ( "Invalid Consul HTTP address: %s" , err )
}
// We use STATIC for agent which means we need to resolve DNS names like
// `localhost` ourselves. We could use STRICT_DNS or LOGICAL_DNS with envoy
// but Envoy resolves `localhost` differently to go on macOS at least which
// causes paper cuts like default dev agent (which binds specifically to
// 127.0.0.1) isn't reachable since Envoy resolves localhost to `[::]` and
// can't connect.
2020-04-07 22:16:34 +00:00
agentIP , err := net . ResolveIPAddr ( "ip" , host )
2020-04-07 20:33:22 +00:00
if err != nil {
return g , fmt . Errorf ( "Failed to resolve agent address: %s" , err )
}
g . AgentAddress = agentIP . String ( )
}
return g , nil
}
2022-08-19 17:07:22 +00:00
func ( c * cmd ) lookupXDSPort ( ) ( int , string , error ) {
2019-08-01 16:53:34 +00:00
self , err := c . client . Agent ( ) . Self ( )
if err != nil {
2022-08-19 17:07:22 +00:00
return 0 , "" , err
2019-08-01 16:53:34 +00:00
}
2021-07-09 19:17:29 +00:00
type response struct {
XDS struct {
2022-09-01 17:32:11 +00:00
Ports struct {
Plaintext int
TLS int
}
2021-07-09 19:17:29 +00:00
}
}
var resp response
2022-09-01 17:32:11 +00:00
if err := mapstructure . Decode ( self , & resp ) ; err == nil {
2023-03-06 15:32:06 +00:00
// When we get rid of the 1.10 compatibility code below we can uncomment
// this check:
//
// if resp.XDS.Ports.TLS <= 0 && resp.XDS.Ports.Plaintext <= 0 {
// return 0, "", fmt.Errorf("agent has grpc disabled")
// }
2022-09-01 17:32:11 +00:00
if resp . XDS . Ports . TLS > 0 {
return resp . XDS . Ports . TLS , "https://" , nil
}
if resp . XDS . Ports . Plaintext > 0 {
return resp . XDS . Ports . Plaintext , "http://" , nil
2022-08-19 17:07:22 +00:00
}
2021-07-09 19:17:29 +00:00
}
2023-03-06 15:32:06 +00:00
// If above TLS and Plaintext ports are both 0, it could mean
// gRPC is disabled on the agent or we are using an older API.
// In either case, fallback to reading from the DebugConfig.
//
// Next major version we should get rid of this below code.
// It exists for compatibility reasons for 1.10 and below.
2019-08-01 16:53:34 +00:00
cfg , ok := self [ "DebugConfig" ]
if ! ok {
2022-08-19 17:07:22 +00:00
return 0 , "" , fmt . Errorf ( "unexpected agent response: no debug config" )
2019-08-01 16:53:34 +00:00
}
port , ok := cfg [ "GRPCPort" ]
if ! ok {
2022-08-19 17:07:22 +00:00
return 0 , "" , fmt . Errorf ( "agent does not have grpc port enabled" )
2019-08-01 16:53:34 +00:00
}
portN , ok := port . ( float64 )
if ! ok {
2022-08-19 17:07:22 +00:00
return 0 , "" , fmt . Errorf ( "invalid grpc port in agent response" )
2019-08-01 16:53:34 +00:00
}
2023-03-06 15:32:06 +00:00
// This works for both <1.10 and later but we should prefer
// reading from resp.XDS instead.
if portN < 0 {
return 0 , "" , fmt . Errorf ( "agent has grpc disabled" )
}
2022-08-19 17:07:22 +00:00
return int ( portN ) , "" , nil
2019-08-01 16:53:34 +00:00
}
2022-12-19 19:37:27 +00:00
func checkDial ( g GRPC , dial func ( string , string ) ( net . Conn , error ) ) error {
var (
conn net . Conn
err error
)
if g . AgentSocket != "" {
conn , err = dial ( "unix" , g . AgentSocket )
} else {
conn , err = dial ( "tcp" , fmt . Sprintf ( "%s:%s" , g . AgentAddress , g . AgentPort ) )
}
if err != nil {
return err
}
if conn != nil {
conn . Close ( )
}
return nil
}
2018-10-03 19:37:53 +00:00
func ( c * cmd ) Synopsis ( ) string {
return synopsis
}
func ( c * cmd ) Help ( ) string {
return c . help
}
2021-07-21 19:45:24 +00:00
const (
synopsis = "Runs or Configures Envoy as a Connect proxy"
help = `
2021-07-21 17:43:10 +00:00
Usage : consul connect envoy [ options ] [ -- pass - through options ]
2018-10-03 19:37:53 +00:00
Generates the bootstrap configuration needed to start an Envoy proxy instance
for use as a Connect sidecar for a particular service instance . By default it
will generate the config and then exec Envoy directly until it exits normally .
It will search $ PATH for the envoy binary but this can be overridden with
- envoy - binary .
2018-10-05 20:08:01 +00:00
It can instead only generate the bootstrap . json based on the current ENV and
2018-10-03 19:37:53 +00:00
arguments using - bootstrap .
The proxy requires service : write permissions for the service it represents .
2019-12-04 20:01:03 +00:00
The token may be passed via the CLI or the CONSUL_HTTP_TOKEN environment
2018-10-03 19:37:53 +00:00
variable .
The example below shows how to start a local proxy as a sidecar to a "web"
service instance . It assumes that the proxy was already registered with it ' s
Config for example via a sidecar_service block .
$ consul connect envoy - sidecar - for web
2021-07-21 17:43:10 +00:00
Additional arguments may be passed directly to Envoy by specifying a double
dash followed by a list of options .
$ consul connect envoy - sidecar - for web -- -- log - level debug
2018-10-03 19:37:53 +00:00
`
2021-07-21 19:45:24 +00:00
)
2022-12-20 17:58:19 +00:00
2023-03-03 18:29:34 +00:00
type envoyCompat struct {
isCompatible bool
versionIncompatible string
}
func checkEnvoyVersionCompatibility ( envoyVersion string , unsupportedList [ ] string ) ( envoyCompat , error ) {
2022-12-20 17:58:19 +00:00
v , err := version . NewVersion ( envoyVersion )
if err != nil {
2023-03-03 18:29:34 +00:00
return envoyCompat { } , err
2022-12-20 17:58:19 +00:00
}
var cs strings . Builder
2023-03-03 18:29:34 +00:00
// If there is a list of unsupported versions, build the constraint string,
// this will detect exactly unsupported versions
if len ( unsupportedList ) > 0 {
for i , s := range unsupportedList {
if i == 0 {
cs . WriteString ( fmt . Sprintf ( "!= %s" , s ) )
} else {
cs . WriteString ( fmt . Sprintf ( ", != %s" , s ) )
}
}
constraints , err := version . NewConstraint ( cs . String ( ) )
if err != nil {
return envoyCompat { } , err
}
if c := constraints . Check ( v ) ; ! c {
return envoyCompat {
isCompatible : c ,
versionIncompatible : envoyVersion ,
} , nil
}
}
// Next build the constraint string using the bounds, make sure that we are less than but not equal to
// maxSupported since we will add 1. Need to add one to the max minor version so that we accept all patches
2023-02-06 17:14:35 +00:00
splitS := strings . Split ( xdscommon . GetMaxEnvoyMinorVersion ( ) , "." )
2022-12-20 17:58:19 +00:00
minor , err := strconv . Atoi ( splitS [ 1 ] )
if err != nil {
2023-03-03 18:29:34 +00:00
return envoyCompat { } , err
2022-12-20 17:58:19 +00:00
}
minor ++
maxSupported := fmt . Sprintf ( "%s.%d" , splitS [ 0 ] , minor )
2023-03-03 18:29:34 +00:00
cs . Reset ( )
2023-02-06 17:14:35 +00:00
cs . WriteString ( fmt . Sprintf ( ">= %s, < %s" , xdscommon . GetMinEnvoyMinorVersion ( ) , maxSupported ) )
2022-12-20 17:58:19 +00:00
constraints , err := version . NewConstraint ( cs . String ( ) )
if err != nil {
2023-03-03 18:29:34 +00:00
return envoyCompat { } , err
2022-12-20 17:58:19 +00:00
}
2023-03-03 18:29:34 +00:00
if c := constraints . Check ( v ) ; ! c {
return envoyCompat {
isCompatible : c ,
versionIncompatible : replacePatchVersionWithX ( envoyVersion ) ,
} , nil
}
return envoyCompat { isCompatible : true } , nil
}
func replacePatchVersionWithX ( version string ) string {
// Strip off the patch and append x to convey that the constraint is on the minor version and not the patch
// itself
a := strings . Split ( version , "." )
return fmt . Sprintf ( "%s.%s.x" , a [ 0 ] , a [ 1 ] )
2022-12-20 17:58:19 +00:00
}