Merge branch 'main' into msiege2/docs-day
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
ingress: allow setting TLS min version and cipher suites in ingress gateway config entries
|
||||
```
|
|
@ -0,0 +1,4 @@
|
|||
```release-note:bug
|
||||
ui: Ensure partition query parameter is passed through to all OIDC related API
|
||||
requests
|
||||
```
|
|
@ -122,7 +122,10 @@ func (s *HTTPHandlers) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request
|
|||
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
|
||||
}
|
||||
|
||||
policyID := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/")
|
||||
policyID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/policy/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policyID == "" && req.Method != "PUT" {
|
||||
return nil, BadRequestError{Reason: "Missing policy ID"}
|
||||
}
|
||||
|
@ -167,7 +170,10 @@ func (s *HTTPHandlers) ACLPolicyReadByName(resp http.ResponseWriter, req *http.R
|
|||
return nil, aclDisabled
|
||||
}
|
||||
|
||||
policyName := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/name/")
|
||||
policyName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/policy/name/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policyName == "" {
|
||||
return nil, BadRequestError{Reason: "Missing policy Name"}
|
||||
}
|
||||
|
@ -302,7 +308,10 @@ func (s *HTTPHandlers) ACLTokenCRUD(resp http.ResponseWriter, req *http.Request)
|
|||
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
|
||||
}
|
||||
|
||||
tokenID := strings.TrimPrefix(req.URL.Path, "/v1/acl/token/")
|
||||
tokenID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/token/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.HasSuffix(tokenID, "/clone") && req.Method == "PUT" {
|
||||
tokenID = tokenID[:len(tokenID)-6]
|
||||
fn = s.ACLTokenClone
|
||||
|
@ -521,7 +530,10 @@ func (s *HTTPHandlers) ACLRoleCRUD(resp http.ResponseWriter, req *http.Request)
|
|||
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
|
||||
}
|
||||
|
||||
roleID := strings.TrimPrefix(req.URL.Path, "/v1/acl/role/")
|
||||
roleID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/role/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if roleID == "" && req.Method != "PUT" {
|
||||
return nil, BadRequestError{Reason: "Missing role ID"}
|
||||
}
|
||||
|
@ -534,7 +546,10 @@ func (s *HTTPHandlers) ACLRoleReadByName(resp http.ResponseWriter, req *http.Req
|
|||
return nil, aclDisabled
|
||||
}
|
||||
|
||||
roleName := strings.TrimPrefix(req.URL.Path, "/v1/acl/role/name/")
|
||||
roleName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/role/name/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if roleName == "" {
|
||||
return nil, BadRequestError{Reason: "Missing role Name"}
|
||||
}
|
||||
|
@ -685,7 +700,10 @@ func (s *HTTPHandlers) ACLBindingRuleCRUD(resp http.ResponseWriter, req *http.Re
|
|||
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
|
||||
}
|
||||
|
||||
bindingRuleID := strings.TrimPrefix(req.URL.Path, "/v1/acl/binding-rule/")
|
||||
bindingRuleID, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/binding-rule/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bindingRuleID == "" && req.Method != "PUT" {
|
||||
return nil, BadRequestError{Reason: "Missing binding rule ID"}
|
||||
}
|
||||
|
@ -829,7 +847,10 @@ func (s *HTTPHandlers) ACLAuthMethodCRUD(resp http.ResponseWriter, req *http.Req
|
|||
return nil, MethodNotAllowedError{req.Method, []string{"GET", "PUT", "DELETE"}}
|
||||
}
|
||||
|
||||
methodName := strings.TrimPrefix(req.URL.Path, "/v1/acl/auth-method/")
|
||||
methodName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/acl/auth-method/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if methodName == "" && req.Method != "PUT" {
|
||||
return nil, BadRequestError{Reason: "Missing auth method name"}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package agent
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/armon/go-metrics/prometheus"
|
||||
|
@ -362,7 +361,11 @@ func (s *HTTPHandlers) catalogServiceNodes(resp http.ResponseWriter, req *http.R
|
|||
}
|
||||
|
||||
// Pull out the service name
|
||||
args.ServiceName = strings.TrimPrefix(req.URL.Path, pathPrefix)
|
||||
var err error
|
||||
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, pathPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.ServiceName == "" {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(resp, "Missing service name")
|
||||
|
@ -435,7 +438,11 @@ func (s *HTTPHandlers) CatalogNodeServices(resp http.ResponseWriter, req *http.R
|
|||
}
|
||||
|
||||
// Pull out the node name
|
||||
args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node/")
|
||||
var err error
|
||||
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/node/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.Node == "" {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(resp, "Missing node name")
|
||||
|
@ -498,7 +505,11 @@ func (s *HTTPHandlers) CatalogNodeServiceList(resp http.ResponseWriter, req *htt
|
|||
}
|
||||
|
||||
// Pull out the node name
|
||||
args.Node = strings.TrimPrefix(req.URL.Path, "/v1/catalog/node-services/")
|
||||
var err error
|
||||
args.Node, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/node-services/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.Node == "" {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(resp, "Missing node name")
|
||||
|
@ -547,7 +558,11 @@ func (s *HTTPHandlers) CatalogGatewayServices(resp http.ResponseWriter, req *htt
|
|||
}
|
||||
|
||||
// Pull out the gateway's service name
|
||||
args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/catalog/gateway-services/")
|
||||
var err error
|
||||
args.ServiceName, err = getPathSuffixUnescaped(req.URL.Path, "/v1/catalog/gateway-services/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.ServiceName == "" {
|
||||
resp.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprint(resp, "Missing gateway name")
|
||||
|
|
|
@ -32,7 +32,11 @@ func (s *HTTPHandlers) configGet(resp http.ResponseWriter, req *http.Request) (i
|
|||
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
||||
return nil, nil
|
||||
}
|
||||
pathArgs := strings.SplitN(strings.TrimPrefix(req.URL.Path, "/v1/config/"), "/", 2)
|
||||
kindAndName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/config/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pathArgs := strings.SplitN(kindAndName, "/", 2)
|
||||
|
||||
switch len(pathArgs) {
|
||||
case 2:
|
||||
|
@ -79,7 +83,11 @@ func (s *HTTPHandlers) configDelete(resp http.ResponseWriter, req *http.Request)
|
|||
var args structs.ConfigEntryRequest
|
||||
s.parseDC(req, &args.Datacenter)
|
||||
s.parseToken(req, &args.Token)
|
||||
pathArgs := strings.SplitN(strings.TrimPrefix(req.URL.Path, "/v1/config/"), "/", 2)
|
||||
kindAndName, err := getPathSuffixUnescaped(req.URL.Path, "/v1/config/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pathArgs := strings.SplitN(kindAndName, "/", 2)
|
||||
|
||||
if len(pathArgs) != 2 {
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
@ -103,7 +102,10 @@ func (s *HTTPHandlers) CoordinateNode(resp http.ResponseWriter, req *http.Reques
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
node := strings.TrimPrefix(req.URL.Path, "/v1/coordinate/node/")
|
||||
node, err := getPathSuffixUnescaped(req.URL.Path, "/v1/coordinate/node/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args := structs.NodeSpecificRequest{Node: node}
|
||||
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
||||
return nil, nil
|
||||
|
|
|
@ -3,7 +3,6 @@ package agent
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
@ -19,7 +18,11 @@ func (s *HTTPHandlers) DiscoveryChainRead(resp http.ResponseWriter, req *http.Re
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
args.Name = strings.TrimPrefix(req.URL.Path, "/v1/discovery-chain/")
|
||||
var err error
|
||||
args.Name, err = getPathSuffixUnescaped(req.URL.Path, "/v1/discovery-chain/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if args.Name == "" {
|
||||
return nil, BadRequestError{Reason: "Missing chain name"}
|
||||
}
|
||||
|
|
|
@ -1082,34 +1082,31 @@ func (l *State) updateSyncState() error {
|
|||
continue
|
||||
}
|
||||
|
||||
// to avoid a data race with the service struct,
|
||||
// We copy the Service struct, mutate it and replace the pointer
|
||||
svc := *ls.Service
|
||||
|
||||
// If our definition is different, we need to update it. Make a
|
||||
// copy so that we don't retain a pointer to any actual state
|
||||
// store info for in-memory RPCs.
|
||||
if svc.EnableTagOverride {
|
||||
svc.Tags = make([]string, len(rs.Tags))
|
||||
copy(svc.Tags, rs.Tags)
|
||||
if ls.Service.EnableTagOverride {
|
||||
ls.Service.Tags = make([]string, len(rs.Tags))
|
||||
copy(ls.Service.Tags, rs.Tags)
|
||||
}
|
||||
|
||||
// Merge any tagged addresses with the consul- prefix (set by the server)
|
||||
// back into the local state.
|
||||
if !reflect.DeepEqual(svc.TaggedAddresses, rs.TaggedAddresses) {
|
||||
if svc.TaggedAddresses == nil {
|
||||
svc.TaggedAddresses = make(map[string]structs.ServiceAddress)
|
||||
if !reflect.DeepEqual(ls.Service.TaggedAddresses, rs.TaggedAddresses) {
|
||||
// Make a copy of TaggedAddresses to prevent races when writing
|
||||
// since other goroutines may be reading from the map
|
||||
m := make(map[string]structs.ServiceAddress)
|
||||
for k, v := range ls.Service.TaggedAddresses {
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range rs.TaggedAddresses {
|
||||
if strings.HasPrefix(k, structs.MetaKeyReservedPrefix) {
|
||||
svc.TaggedAddresses[k] = v
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
ls.Service.TaggedAddresses = m
|
||||
}
|
||||
ls.InSync = svc.IsSame(rs)
|
||||
|
||||
// replace the service pointer to the new mutated struct
|
||||
ls.Service = &svc
|
||||
ls.InSync = ls.Service.IsSame(rs)
|
||||
}
|
||||
|
||||
// Check which checks need syncing
|
||||
|
|
|
@ -373,36 +373,41 @@ func TestAgentAntiEntropy_Services_ConnectProxy(t *testing.T) {
|
|||
// We should have 5 services (consul included)
|
||||
assert.Len(services.NodeServices.Services, 5)
|
||||
|
||||
// All the services should match
|
||||
// Check that virtual IPs have been set
|
||||
vips := make(map[string]struct{})
|
||||
srv1.TaggedAddresses = nil
|
||||
srv2.TaggedAddresses = nil
|
||||
for id, serv := range services.NodeServices.Services {
|
||||
serv.CreateIndex, serv.ModifyIndex = 0, 0
|
||||
for _, serv := range services.NodeServices.Services {
|
||||
if serv.TaggedAddresses != nil {
|
||||
serviceVIP := serv.TaggedAddresses[structs.TaggedAddressVirtualIP].Address
|
||||
assert.NotEmpty(serviceVIP)
|
||||
vips[serviceVIP] = struct{}{}
|
||||
}
|
||||
serv.TaggedAddresses = nil
|
||||
switch id {
|
||||
case "mysql-proxy":
|
||||
assert.Equal(srv1, serv)
|
||||
case "redis-proxy":
|
||||
assert.Equal(srv2, serv)
|
||||
case "web-proxy":
|
||||
assert.Equal(srv3, serv)
|
||||
case "cache-proxy":
|
||||
assert.Equal(srv5, serv)
|
||||
case structs.ConsulServiceID:
|
||||
// ignore
|
||||
default:
|
||||
t.Fatalf("unexpected service: %v", id)
|
||||
}
|
||||
}
|
||||
|
||||
assert.Len(vips, 4)
|
||||
assert.Nil(servicesInSync(a.State, 4, structs.DefaultEnterpriseMetaInDefaultPartition()))
|
||||
|
||||
// All the services should match
|
||||
// Retry to mitigate data races between local and remote state
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
require.NoError(r, a.State.SyncFull())
|
||||
for id, serv := range services.NodeServices.Services {
|
||||
serv.CreateIndex, serv.ModifyIndex = 0, 0
|
||||
switch id {
|
||||
case "mysql-proxy":
|
||||
require.Equal(r, srv1, serv)
|
||||
case "redis-proxy":
|
||||
require.Equal(r, srv2, serv)
|
||||
case "web-proxy":
|
||||
require.Equal(r, srv3, serv)
|
||||
case "cache-proxy":
|
||||
require.Equal(r, srv5, serv)
|
||||
case structs.ConsulServiceID:
|
||||
// ignore
|
||||
default:
|
||||
r.Fatalf("unexpected service: %v", id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
assert.NoError(servicesInSync(a.State, 4, structs.DefaultEnterpriseMetaInDefaultPartition()))
|
||||
|
||||
// Remove one of the services
|
||||
a.State.RemoveService(structs.NewServiceID("cache-proxy", nil))
|
||||
|
@ -415,12 +420,6 @@ func TestAgentAntiEntropy_Services_ConnectProxy(t *testing.T) {
|
|||
// All the services should match
|
||||
for id, serv := range services.NodeServices.Services {
|
||||
serv.CreateIndex, serv.ModifyIndex = 0, 0
|
||||
if serv.TaggedAddresses != nil {
|
||||
serviceVIP := serv.TaggedAddresses[structs.TaggedAddressVirtualIP].Address
|
||||
assert.NotEmpty(serviceVIP)
|
||||
vips[serviceVIP] = struct{}{}
|
||||
}
|
||||
serv.TaggedAddresses = nil
|
||||
switch id {
|
||||
case "mysql-proxy":
|
||||
assert.Equal(srv1, serv)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/lib/stringslice"
|
||||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
// IngressGatewayConfigEntry manages the configuration for an ingress service
|
||||
|
@ -99,6 +100,13 @@ type GatewayTLSConfig struct {
|
|||
|
||||
// SDS allows configuring TLS certificate from an SDS service.
|
||||
SDS *GatewayTLSSDSConfig `json:",omitempty"`
|
||||
|
||||
TLSMinVersion types.TLSVersion `json:",omitempty" alias:"tls_min_version"`
|
||||
TLSMaxVersion types.TLSVersion `json:",omitempty" alias:"tls_max_version"`
|
||||
|
||||
// Define a subset of cipher suites to restrict
|
||||
// Only applicable to connections negotiated via TLS 1.2 or earlier
|
||||
CipherSuites []types.TLSCipherSuite `json:",omitempty" alias:"cipher_suites"`
|
||||
}
|
||||
|
||||
type GatewayServiceTLSConfig struct {
|
||||
|
@ -231,11 +239,49 @@ func (e *IngressGatewayConfigEntry) validateServiceSDS(lis IngressListener, svc
|
|||
return nil
|
||||
}
|
||||
|
||||
func validateGatewayTLSConfig(tlsCfg GatewayTLSConfig) error {
|
||||
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
|
||||
if err := types.ValidateTLSVersion(tlsCfg.TLSMinVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tlsCfg.TLSMaxVersion != types.TLSVersionUnspecified {
|
||||
if err := types.ValidateTLSVersion(tlsCfg.TLSMaxVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
|
||||
if err, maxLessThanMin := tlsCfg.TLSMaxVersion.LessThan(tlsCfg.TLSMinVersion); err == nil && maxLessThanMin {
|
||||
return fmt.Errorf("configuring max version %s less than the configured min version %s is invalid", tlsCfg.TLSMaxVersion, tlsCfg.TLSMinVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(tlsCfg.CipherSuites) != 0 {
|
||||
if _, ok := types.TLSVersionsWithConfigurableCipherSuites[tlsCfg.TLSMinVersion]; !ok {
|
||||
return fmt.Errorf("configuring CipherSuites is only applicable to conncetions negotiated with TLS 1.2 or earlier, TLSMinVersion is set to %s", tlsCfg.TLSMinVersion)
|
||||
}
|
||||
|
||||
// NOTE: it would be nice to emit a warning but not return an error from
|
||||
// here if TLSMaxVersion is unspecified, TLS_AUTO or TLSv1_3
|
||||
if err := types.ValidateEnvoyCipherSuites(tlsCfg.CipherSuites); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *IngressGatewayConfigEntry) Validate() error {
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateGatewayTLSConfig(e.TLS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validProtocols := map[string]bool{
|
||||
"tcp": true,
|
||||
"http": true,
|
||||
|
@ -264,6 +310,12 @@ func (e *IngressGatewayConfigEntry) Validate() error {
|
|||
listener.Port)
|
||||
}
|
||||
|
||||
if listener.TLS != nil {
|
||||
if err := validateGatewayTLSConfig(*listener.TLS); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
declaredHosts := make(map[string]bool)
|
||||
serviceNames := make(map[ServiceID]struct{})
|
||||
for _, s := range listener.Services {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/cache"
|
||||
"github.com/hashicorp/consul/sdk/testutil"
|
||||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
func TestConfigEntries_ACLs(t *testing.T) {
|
||||
|
@ -1107,6 +1108,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
// TODO(rb): test SDS stuff here in both places (global/service)
|
||||
name: "ingress-gateway: kitchen sink",
|
||||
snake: `
|
||||
kind = "ingress-gateway"
|
||||
|
@ -1118,6 +1120,12 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
|
||||
tls {
|
||||
enabled = true
|
||||
tls_min_version = "TLSv1_1"
|
||||
tls_max_version = "TLSv1_2"
|
||||
cipher_suites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
}
|
||||
|
||||
listeners = [
|
||||
|
@ -1181,6 +1189,12 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
}
|
||||
TLS {
|
||||
Enabled = true
|
||||
TLSMinVersion = "TLSv1_1"
|
||||
TLSMaxVersion = "TLSv1_2"
|
||||
CipherSuites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
}
|
||||
Listeners = [
|
||||
{
|
||||
|
@ -1242,7 +1256,13 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
"gir": "zim",
|
||||
},
|
||||
TLS: GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
Enabled: true,
|
||||
TLSMinVersion: types.TLSv1_1,
|
||||
TLSMaxVersion: types.TLSv1_2,
|
||||
CipherSuites: []types.TLSCipherSuite{
|
||||
types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
types.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
},
|
||||
},
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
|
|
|
@ -186,7 +186,7 @@ func makePassthroughClusters(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message,
|
|||
ConnectTimeout: ptypes.DurationProto(5 * time.Second),
|
||||
}
|
||||
|
||||
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf())
|
||||
commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
|
||||
err := injectSANMatcher(commonTLSContext, passthrough.SpiffeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", passthrough.SNI, err)
|
||||
|
@ -550,7 +550,7 @@ func (s *ResourceGenerator) makeUpstreamClusterForPreparedQuery(upstream structs
|
|||
}
|
||||
|
||||
// Enable TLS upstream with the configured client certificate.
|
||||
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf())
|
||||
commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
|
||||
err = injectSANMatcher(commonTLSContext, spiffeIDs...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err)
|
||||
|
@ -728,7 +728,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
|||
c.Http2ProtocolOptions = &envoy_core_v3.Http2ProtocolOptions{}
|
||||
}
|
||||
|
||||
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf())
|
||||
commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
|
||||
err = injectSANMatcher(commonTLSContext, spiffeIDs...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err)
|
||||
|
|
|
@ -740,7 +740,7 @@ func injectHTTPFilterOnFilterChains(
|
|||
func (s *ResourceGenerator) injectConnectTLSOnFilterChains(cfgSnap *proxycfg.ConfigSnapshot, listener *envoy_listener_v3.Listener) error {
|
||||
for idx := range listener.FilterChains {
|
||||
tlsContext := &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
|
||||
CommonTlsContext: makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf()),
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: true},
|
||||
}
|
||||
transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext)
|
||||
|
@ -1062,7 +1062,7 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway(
|
|||
protocol string,
|
||||
) (*envoy_listener_v3.FilterChain, error) {
|
||||
tlsContext := &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]),
|
||||
CommonTlsContext: makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]),
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: true},
|
||||
}
|
||||
transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext)
|
||||
|
@ -1536,7 +1536,11 @@ func makeEnvoyHTTPFilter(name string, cfg proto.Message) (*envoy_http_v3.HttpFil
|
|||
}, nil
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoy_tls_v3.CommonTlsContext {
|
||||
func makeCommonTLSContextFromLeafWithoutParams(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoy_tls_v3.CommonTlsContext {
|
||||
return makeCommonTLSContextFromLeaf(cfgSnap, leaf, nil)
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert, tlsParams *envoy_tls_v3.TlsParameters) *envoy_tls_v3.CommonTlsContext {
|
||||
// Concatenate all the root PEMs into one.
|
||||
if cfgSnap.Roots == nil {
|
||||
return nil
|
||||
|
@ -1547,8 +1551,12 @@ func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *struct
|
|||
rootPEMS += ca.EnsureTrailingNewline(root.RootCert)
|
||||
}
|
||||
|
||||
if tlsParams == nil {
|
||||
tlsParams = &envoy_tls_v3.TlsParameters{}
|
||||
}
|
||||
|
||||
return &envoy_tls_v3.CommonTlsContext{
|
||||
TlsParams: &envoy_tls_v3.TlsParameters{},
|
||||
TlsParams: tlsParams,
|
||||
TlsCertificates: []*envoy_tls_v3.TlsCertificate{
|
||||
{
|
||||
CertificateChain: &envoy_core_v3.DataSource{
|
||||
|
|
|
@ -8,43 +8,26 @@ import (
|
|||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes/duration"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
|
||||
"github.com/hashicorp/consul/agent/proxycfg"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
||||
var resources []proto.Message
|
||||
|
||||
for listenerKey, upstreams := range cfgSnap.IngressGateway.Upstreams {
|
||||
var tlsContext *envoy_tls_v3.DownstreamTlsContext
|
||||
|
||||
listenerCfg, ok := cfgSnap.IngressGateway.Listeners[listenerKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no listener config found for listener on port %d", listenerKey.Port)
|
||||
}
|
||||
// Enable connect TLS if it is enabled at the Gateway or specific listener
|
||||
// level.
|
||||
connectTLSEnabled := cfgSnap.IngressGateway.TLSConfig.Enabled ||
|
||||
(listenerCfg.TLS != nil && listenerCfg.TLS.Enabled)
|
||||
|
||||
sdsCfg, err := resolveListenerSDSConfig(cfgSnap, listenerCfg)
|
||||
tlsContext, err := makeDownstreamTLSContextFromSnapshotListenerConfig(cfgSnap, listenerCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sdsCfg != nil {
|
||||
// Set up listener TLS from SDS
|
||||
tlsContext = &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: makeCommonTLSContextFromSDS(*sdsCfg),
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: false},
|
||||
}
|
||||
} else if connectTLSEnabled {
|
||||
tlsContext = &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: false},
|
||||
}
|
||||
}
|
||||
|
||||
if listenerKey.Protocol == "tcp" {
|
||||
// We rely on the invariant of upstreams slice always having at least 1
|
||||
// member, because this key/value pair is created only when a
|
||||
|
@ -154,21 +137,116 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap
|
|||
return resources, nil
|
||||
}
|
||||
|
||||
func resolveListenerSDSConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*structs.GatewayTLSSDSConfig, error) {
|
||||
var mergedCfg structs.GatewayTLSSDSConfig
|
||||
func makeDownstreamTLSContextFromSnapshotListenerConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*envoy_tls_v3.DownstreamTlsContext, error) {
|
||||
var downstreamContext *envoy_tls_v3.DownstreamTlsContext
|
||||
|
||||
gwSDS := cfgSnap.IngressGateway.TLSConfig.SDS
|
||||
if gwSDS != nil {
|
||||
mergedCfg.ClusterName = gwSDS.ClusterName
|
||||
mergedCfg.CertResource = gwSDS.CertResource
|
||||
tlsContext, err := makeCommonTLSContextFromSnapshotListenerConfig(cfgSnap, listenerCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if listenerCfg.TLS != nil && listenerCfg.TLS.SDS != nil {
|
||||
if listenerCfg.TLS.SDS.ClusterName != "" {
|
||||
mergedCfg.ClusterName = listenerCfg.TLS.SDS.ClusterName
|
||||
if tlsContext != nil {
|
||||
downstreamContext = &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: tlsContext,
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: false},
|
||||
}
|
||||
if listenerCfg.TLS.SDS.CertResource != "" {
|
||||
mergedCfg.CertResource = listenerCfg.TLS.SDS.CertResource
|
||||
}
|
||||
|
||||
return downstreamContext, nil
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromSnapshotListenerConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*envoy_tls_v3.CommonTlsContext, error) {
|
||||
var tlsContext *envoy_tls_v3.CommonTlsContext
|
||||
|
||||
// Enable connect TLS if it is enabled at the Gateway or specific listener
|
||||
// level.
|
||||
gatewayTLSCfg := cfgSnap.IngressGateway.TLSConfig
|
||||
|
||||
// It is not possible to explicitly _disable_ TLS on a listener if it's
|
||||
// enabled on the gateway, because false is the zero-value of the struct field
|
||||
// and therefore indistinguishable from it being unspecified.
|
||||
connectTLSEnabled := gatewayTLSCfg.Enabled ||
|
||||
(listenerCfg.TLS != nil && listenerCfg.TLS.Enabled)
|
||||
|
||||
tlsCfg, err := resolveListenerTLSConfig(&gatewayTLSCfg, listenerCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tlsCfg.SDS != nil {
|
||||
// Set up listener TLS from SDS
|
||||
tlsContext = makeCommonTLSContextFromGatewayTLSConfig(*tlsCfg)
|
||||
} else if connectTLSEnabled {
|
||||
tlsContext = makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf(), makeTLSParametersFromGatewayTLSConfig(*tlsCfg))
|
||||
}
|
||||
|
||||
return tlsContext, nil
|
||||
}
|
||||
|
||||
func resolveListenerTLSConfig(gatewayTLSCfg *structs.GatewayTLSConfig, listenerCfg structs.IngressListener) (*structs.GatewayTLSConfig, error) {
|
||||
var mergedCfg structs.GatewayTLSConfig
|
||||
|
||||
resolvedSDSCfg, err := resolveListenerSDSConfig(gatewayTLSCfg.SDS, listenerCfg.TLS, listenerCfg.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mergedCfg.SDS = resolvedSDSCfg
|
||||
|
||||
if gatewayTLSCfg != nil {
|
||||
mergedCfg.TLSMinVersion = gatewayTLSCfg.TLSMinVersion
|
||||
mergedCfg.TLSMaxVersion = gatewayTLSCfg.TLSMaxVersion
|
||||
mergedCfg.CipherSuites = gatewayTLSCfg.CipherSuites
|
||||
}
|
||||
|
||||
if listenerCfg.TLS != nil {
|
||||
if listenerCfg.TLS.TLSMinVersion != types.TLSVersionUnspecified {
|
||||
mergedCfg.TLSMinVersion = listenerCfg.TLS.TLSMinVersion
|
||||
}
|
||||
if listenerCfg.TLS.TLSMaxVersion != types.TLSVersionUnspecified {
|
||||
mergedCfg.TLSMaxVersion = listenerCfg.TLS.TLSMaxVersion
|
||||
}
|
||||
if len(listenerCfg.TLS.CipherSuites) != 0 {
|
||||
mergedCfg.CipherSuites = listenerCfg.TLS.CipherSuites
|
||||
}
|
||||
}
|
||||
|
||||
var TLSVersionsWithConfigurableCipherSuites = map[types.TLSVersion]struct{}{
|
||||
// Remove these two if Envoy ever sets TLS 1.3 as default minimum
|
||||
types.TLSVersionUnspecified: {},
|
||||
types.TLSVersionAuto: {},
|
||||
|
||||
types.TLSv1_0: {},
|
||||
types.TLSv1_1: {},
|
||||
types.TLSv1_2: {},
|
||||
}
|
||||
|
||||
// Validate. Configuring cipher suites is only applicable to connections negotiated
|
||||
// via TLS 1.2 or earlier. Other cases shouldn't be possible as we validate them at
|
||||
// input but be resilient to bugs later.
|
||||
if len(mergedCfg.CipherSuites) != 0 {
|
||||
if _, ok := TLSVersionsWithConfigurableCipherSuites[mergedCfg.TLSMinVersion]; !ok {
|
||||
return nil, fmt.Errorf("configuring CipherSuites is only applicable to connections negotiated with TLS 1.2 or earlier, TLSMinVersion is set to %s in listener or gateway config", mergedCfg.TLSMinVersion)
|
||||
}
|
||||
}
|
||||
|
||||
return &mergedCfg, nil
|
||||
}
|
||||
|
||||
func resolveListenerSDSConfig(gatewaySDSCfg *structs.GatewayTLSSDSConfig, listenerTLSCfg *structs.GatewayTLSConfig, listenerPort int) (*structs.GatewayTLSSDSConfig, error) {
|
||||
var mergedCfg structs.GatewayTLSSDSConfig
|
||||
|
||||
if gatewaySDSCfg != nil {
|
||||
mergedCfg.ClusterName = gatewaySDSCfg.ClusterName
|
||||
mergedCfg.CertResource = gatewaySDSCfg.CertResource
|
||||
}
|
||||
|
||||
if listenerTLSCfg != nil && listenerTLSCfg.SDS != nil {
|
||||
if listenerTLSCfg.SDS.ClusterName != "" {
|
||||
mergedCfg.ClusterName = listenerTLSCfg.SDS.ClusterName
|
||||
}
|
||||
if listenerTLSCfg.SDS.CertResource != "" {
|
||||
mergedCfg.CertResource = listenerTLSCfg.SDS.CertResource
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,10 +261,10 @@ func resolveListenerSDSConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg stru
|
|||
return &mergedCfg, nil
|
||||
|
||||
case mergedCfg.ClusterName == "" && mergedCfg.CertResource != "":
|
||||
return nil, fmt.Errorf("missing SDS cluster name for listener on port %d", listenerCfg.Port)
|
||||
return nil, fmt.Errorf("missing SDS cluster name for listener on port %d", listenerPort)
|
||||
|
||||
case mergedCfg.ClusterName != "" && mergedCfg.CertResource == "":
|
||||
return nil, fmt.Errorf("missing SDS cert resource for listener on port %d", listenerCfg.Port)
|
||||
return nil, fmt.Errorf("missing SDS cert resource for listener on port %d", listenerPort)
|
||||
}
|
||||
|
||||
return &mergedCfg, nil
|
||||
|
@ -260,7 +338,7 @@ func makeSDSOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
|
|||
}
|
||||
|
||||
tlsContext := &envoy_tls_v3.DownstreamTlsContext{
|
||||
CommonTlsContext: makeCommonTLSContextFromSDS(*svc.TLS.SDS),
|
||||
CommonTlsContext: makeCommonTLSContextFromGatewayServiceTLSConfig(*svc.TLS),
|
||||
RequireClientCertificate: &wrappers.BoolValue{Value: false},
|
||||
}
|
||||
|
||||
|
@ -284,33 +362,71 @@ func makeSDSOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
|
|||
return chains, nil
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromSDS(sdsCfg structs.GatewayTLSSDSConfig) *envoy_tls_v3.CommonTlsContext {
|
||||
var envoyTLSVersions = map[types.TLSVersion]envoy_tls_v3.TlsParameters_TlsProtocol{
|
||||
types.TLSVersionAuto: envoy_tls_v3.TlsParameters_TLS_AUTO,
|
||||
types.TLSv1_0: envoy_tls_v3.TlsParameters_TLSv1_0,
|
||||
types.TLSv1_1: envoy_tls_v3.TlsParameters_TLSv1_1,
|
||||
types.TLSv1_2: envoy_tls_v3.TlsParameters_TLSv1_2,
|
||||
types.TLSv1_3: envoy_tls_v3.TlsParameters_TLSv1_3,
|
||||
}
|
||||
|
||||
func makeTLSParametersFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *envoy_tls_v3.TlsParameters {
|
||||
tlsParams := envoy_tls_v3.TlsParameters{}
|
||||
|
||||
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
|
||||
if minVersion, ok := envoyTLSVersions[tlsCfg.TLSMinVersion]; ok {
|
||||
tlsParams.TlsMinimumProtocolVersion = minVersion
|
||||
}
|
||||
}
|
||||
if tlsCfg.TLSMaxVersion != types.TLSVersionUnspecified {
|
||||
if maxVersion, ok := envoyTLSVersions[tlsCfg.TLSMaxVersion]; ok {
|
||||
tlsParams.TlsMaximumProtocolVersion = maxVersion
|
||||
}
|
||||
}
|
||||
if len(tlsCfg.CipherSuites) != 0 {
|
||||
tlsParams.CipherSuites = types.MarshalEnvoyTLSCipherSuiteStrings(tlsCfg.CipherSuites)
|
||||
}
|
||||
|
||||
return &tlsParams
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *envoy_tls_v3.CommonTlsContext {
|
||||
return &envoy_tls_v3.CommonTlsContext{
|
||||
TlsParams: &envoy_tls_v3.TlsParameters{},
|
||||
TlsCertificateSdsSecretConfigs: []*envoy_tls_v3.SdsSecretConfig{
|
||||
{
|
||||
Name: sdsCfg.CertResource,
|
||||
SdsConfig: &envoy_core_v3.ConfigSource{
|
||||
ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_ApiConfigSource{
|
||||
ApiConfigSource: &envoy_core_v3.ApiConfigSource{
|
||||
ApiType: envoy_core_v3.ApiConfigSource_GRPC,
|
||||
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
|
||||
// Note ClusterNames can't be set here - that's only for REST type
|
||||
// we need a full GRPC config instead.
|
||||
GrpcServices: []*envoy_core_v3.GrpcService{
|
||||
{
|
||||
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
|
||||
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
|
||||
ClusterName: sdsCfg.ClusterName,
|
||||
},
|
||||
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
|
||||
TlsCertificateSdsSecretConfigs: makeTLSCertificateSdsSecretConfigsFromSDS(*tlsCfg.SDS),
|
||||
}
|
||||
}
|
||||
|
||||
func makeCommonTLSContextFromGatewayServiceTLSConfig(tlsCfg structs.GatewayServiceTLSConfig) *envoy_tls_v3.CommonTlsContext {
|
||||
return &envoy_tls_v3.CommonTlsContext{
|
||||
TlsParams: &envoy_tls_v3.TlsParameters{},
|
||||
TlsCertificateSdsSecretConfigs: makeTLSCertificateSdsSecretConfigsFromSDS(*tlsCfg.SDS),
|
||||
}
|
||||
}
|
||||
func makeTLSCertificateSdsSecretConfigsFromSDS(sdsCfg structs.GatewayTLSSDSConfig) []*envoy_tls_v3.SdsSecretConfig {
|
||||
return []*envoy_tls_v3.SdsSecretConfig{
|
||||
{
|
||||
Name: sdsCfg.CertResource,
|
||||
SdsConfig: &envoy_core_v3.ConfigSource{
|
||||
ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_ApiConfigSource{
|
||||
ApiConfigSource: &envoy_core_v3.ApiConfigSource{
|
||||
ApiType: envoy_core_v3.ApiConfigSource_GRPC,
|
||||
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
|
||||
// Note ClusterNames can't be set here - that's only for REST type
|
||||
// we need a full GRPC config instead.
|
||||
GrpcServices: []*envoy_core_v3.GrpcService{
|
||||
{
|
||||
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
|
||||
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
|
||||
ClusterName: sdsCfg.ClusterName,
|
||||
},
|
||||
Timeout: &duration.Duration{Seconds: 5},
|
||||
},
|
||||
Timeout: &duration.Duration{Seconds: 5},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
|
||||
},
|
||||
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -519,6 +519,30 @@ func TestListenersFromSnapshot(t *testing.T) {
|
|||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: nil,
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-listener-min-version",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_3
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-listener-max-version",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.IngressGateway.TLSConfig.TLSMaxVersion = types.TLSv1_2
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-listener-cipher-suites",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.IngressGateway.TLSConfig.CipherSuites = []types.TLSCipherSuite{
|
||||
types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-mixed-listeners",
|
||||
// Use SDS helper even though we aren't testing SDS since it already sets
|
||||
|
@ -572,6 +596,215 @@ func TestListenersFromSnapshot(t *testing.T) {
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-min-version-listeners-gateway-defaults",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2
|
||||
|
||||
// One listener disables TLS, one inherits TLS minimum version from the gateway
|
||||
// config, two others set different versions
|
||||
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
{
|
||||
DestinationName: "s1",
|
||||
LocalBindPort: 8080,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
{
|
||||
DestinationName: "s2",
|
||||
LocalBindPort: 8081,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8082}: {
|
||||
{
|
||||
DestinationName: "s3",
|
||||
LocalBindPort: 8082,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8083}: {
|
||||
{
|
||||
DestinationName: "s4",
|
||||
LocalBindPort: 8083,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8084}: {
|
||||
{
|
||||
DestinationName: "s4",
|
||||
LocalBindPort: 8084,
|
||||
},
|
||||
},
|
||||
}
|
||||
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
|
||||
// Omits listener TLS config, should default to gateway TLS config
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
Port: 8080,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s1",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Explicitly sets listener TLS config to nil, should default to gateway TLS config
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
Port: 8081,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s2",
|
||||
},
|
||||
},
|
||||
TLS: nil,
|
||||
},
|
||||
// Explicitly enables TLS config, but with no listener default TLS params,
|
||||
// should default to gateway TLS config
|
||||
{Protocol: "http", Port: 8082}: {
|
||||
Port: 8082,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s3",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
// Explicitly unset gateway default TLS min version in favor of proxy default
|
||||
{Protocol: "http", Port: 8083}: {
|
||||
Port: 8083,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s3",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
TLSMinVersion: types.TLSVersionAuto,
|
||||
},
|
||||
},
|
||||
// Disables listener TLS
|
||||
{Protocol: "http", Port: 8084}: {
|
||||
Port: 8084,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s4",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-single-tls-listener",
|
||||
create: proxycfg.TestConfigSnapshotIngress,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
// One listener should inherit non-TLS gateway config, another
|
||||
// listener configures TLS with an explicit minimum version
|
||||
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
{
|
||||
DestinationName: "s1",
|
||||
LocalBindPort: 8080,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
{
|
||||
DestinationName: "s2",
|
||||
LocalBindPort: 8081,
|
||||
},
|
||||
},
|
||||
}
|
||||
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
Port: 8080,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
Port: 8081,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s2",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
TLSMinVersion: types.TLSv1_2,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-tls-mixed-min-version-listeners",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
|
||||
setup: func(snap *proxycfg.ConfigSnapshot) {
|
||||
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2
|
||||
|
||||
// One listener should inherit TLS minimum version from the gateway config,
|
||||
// two others each set explicit TLS minimum versions
|
||||
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
{
|
||||
DestinationName: "s1",
|
||||
LocalBindPort: 8080,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
{
|
||||
DestinationName: "s2",
|
||||
LocalBindPort: 8081,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8082}: {
|
||||
{
|
||||
DestinationName: "s3",
|
||||
LocalBindPort: 8082,
|
||||
},
|
||||
},
|
||||
}
|
||||
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
|
||||
{Protocol: "http", Port: 8080}: {
|
||||
Port: 8080,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8081}: {
|
||||
Port: 8081,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s2",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
TLSMinVersion: types.TLSv1_0,
|
||||
},
|
||||
},
|
||||
{Protocol: "http", Port: 8082}: {
|
||||
Port: 8082,
|
||||
Services: []structs.IngressService{
|
||||
{
|
||||
Name: "s3",
|
||||
},
|
||||
},
|
||||
TLS: &structs.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
TLSMinVersion: types.TLSv1_3,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ingress-with-sds-listener-gw-level",
|
||||
create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS,
|
||||
|
@ -1208,7 +1441,7 @@ func TestResolveListenerSDSConfig(t *testing.T) {
|
|||
listenerCfg = lisCfg
|
||||
}
|
||||
|
||||
got, err := resolveListenerSDSConfig(snap, listenerCfg)
|
||||
got, err := resolveListenerSDSConfig(snap.IngressGateway.TLSConfig.SDS, listenerCfg.TLS, listenerCfg.Port)
|
||||
if tc.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.wantErr)
|
||||
|
|
120
agent/xds/testdata/listeners/ingress-with-single-tls-listener.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8080",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8080
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8080",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8080"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8081",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8081
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8081",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8081"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
62
agent/xds/testdata/listeners/ingress-with-tls-listener-cipher-suites.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "db:1.2.3.4:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.db.default.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"cipherSuites": [
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256",
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305"
|
||||
]
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
59
agent/xds/testdata/listeners/ingress-with-tls-listener-max-version.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "db:1.2.3.4:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.db.default.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMaximumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
59
agent/xds/testdata/listeners/ingress-with-tls-listener-min-version.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "db:1.2.3.4:9191",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 9191
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.tcp_proxy",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"statPrefix": "upstream.db.default.default.dc1",
|
||||
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_3"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8080",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8080
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8080",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8080"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8081",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8081
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8081",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8081"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8082",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8082
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8082",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8082"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8083",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8083
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8083",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8083"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8084",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8084
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8084",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8084"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
217
agent/xds/testdata/listeners/ingress-with-tls-mixed-min-version-listeners.envoy-1-20-x.golden
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8080",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8080
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8080",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8080"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_2"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8081",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8081
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8081",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8081"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_0"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8082",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8082
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_8082",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {
|
||||
|
||||
},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8082"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router"
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {
|
||||
"tlsMinimumProtocolVersion": "TLSv1_3"
|
||||
},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"validationContext": {
|
||||
"trustedCa": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -43,6 +43,13 @@ type GatewayTLSConfig struct {
|
|||
|
||||
// SDS allows configuring TLS certificate from an SDS service.
|
||||
SDS *GatewayTLSSDSConfig `json:",omitempty"`
|
||||
|
||||
TLSMinVersion string `json:",omitempty" alias:"tls_min_version"`
|
||||
TLSMaxVersion string `json:",omitempty" alias:"tls_max_version"`
|
||||
|
||||
// Define a subset of cipher suites to restrict
|
||||
// Only applicable to connections negotiated via TLS 1.2 or earlier
|
||||
CipherSuites []string `json:",omitempty" alias:"cipher_suites"`
|
||||
}
|
||||
|
||||
type GatewayServiceTLSConfig struct {
|
||||
|
|
|
@ -26,7 +26,8 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
|
|||
Kind: IngressGateway,
|
||||
Name: "bar",
|
||||
TLS: GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
Enabled: true,
|
||||
TLSMinVersion: "TLSv1_2",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -945,6 +945,7 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
// TODO(rb): test SDS stuff here in both places (global/service)
|
||||
name: "ingress-gateway",
|
||||
body: `
|
||||
{
|
||||
|
@ -955,7 +956,13 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
"gir": "zim"
|
||||
},
|
||||
"Tls": {
|
||||
"Enabled": true
|
||||
"Enabled": true,
|
||||
"TLSMinVersion": "TLSv1_1",
|
||||
"TLSMaxVersion": "TLSv1_2",
|
||||
"CipherSuites": [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
},
|
||||
"Listeners": [
|
||||
{
|
||||
|
@ -992,7 +999,13 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
"gir": "zim",
|
||||
},
|
||||
TLS: GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
Enabled: true,
|
||||
TLSMinVersion: "TLSv1_1",
|
||||
TLSMaxVersion: "TLSv1_2",
|
||||
CipherSuites: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
},
|
||||
},
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
|
|
|
@ -2096,6 +2096,7 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
// TODO(rb): test SDS stuff here in both places (global/service)
|
||||
name: "ingress-gateway: kitchen sink",
|
||||
snake: `
|
||||
kind = "ingress-gateway"
|
||||
|
@ -2106,6 +2107,12 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
}
|
||||
tls {
|
||||
enabled = true
|
||||
tls_min_version = "TLSv1_1"
|
||||
tls_max_version = "TLSv1_2"
|
||||
cipher_suites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
}
|
||||
listeners = [
|
||||
{
|
||||
|
@ -2133,6 +2140,12 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
}
|
||||
Tls {
|
||||
Enabled = true
|
||||
TLSMinVersion = "TLSv1_1"
|
||||
TLSMaxVersion = "TLSv1_2"
|
||||
CipherSuites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
}
|
||||
Listeners = [
|
||||
{
|
||||
|
@ -2160,7 +2173,13 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
"gir": "zim"
|
||||
},
|
||||
"tls": {
|
||||
"enabled": true
|
||||
"enabled": true,
|
||||
"tls_min_version": "TLSv1_1",
|
||||
"tls_max_version": "TLSv1_2",
|
||||
"cipher_suites": [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
},
|
||||
"listeners": [
|
||||
{
|
||||
|
@ -2188,8 +2207,14 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
},
|
||||
"Tls": {
|
||||
"Enabled": true
|
||||
"TLS": {
|
||||
"Enabled": true,
|
||||
"TLSMinVersion": "TLSv1_1",
|
||||
"TLSMaxVersion": "TLSv1_2",
|
||||
"CipherSuites": [
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
},
|
||||
"Listeners": [
|
||||
{
|
||||
|
@ -2217,7 +2242,13 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
"gir": "zim",
|
||||
},
|
||||
TLS: api.GatewayTLSConfig{
|
||||
Enabled: true,
|
||||
Enabled: true,
|
||||
TLSMinVersion: "TLSv1_1",
|
||||
TLSMaxVersion: "TLSv1_2",
|
||||
CipherSuites: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
},
|
||||
},
|
||||
Listeners: []api.IngressListener{
|
||||
{
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"lockfileVersion": 1
|
||||
}
|
240
types/tls.go
|
@ -1,43 +1,42 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TLSVersion is a strongly-typed int used for relative comparison
|
||||
// (minimum, maximum, greater than, less than) of TLS versions
|
||||
type TLSVersion int
|
||||
// TLSVersion is a strongly-typed string for TLS versions
|
||||
type TLSVersion string
|
||||
|
||||
const (
|
||||
// Error value, excluded from lookup maps
|
||||
TLSVersionInvalid TLSVersion = iota - 1
|
||||
TLSVersionInvalid TLSVersion = "TLS_INVALID"
|
||||
|
||||
// Explicit unspecified zero-value to avoid overwriting parent defaults
|
||||
TLSVersionUnspecified
|
||||
TLSVersionUnspecified TLSVersion = ""
|
||||
|
||||
// Explictly allow implementation to select TLS version
|
||||
// May be useful to supercede defaults specified at a higher layer
|
||||
TLSVersionAuto
|
||||
TLSVersionAuto TLSVersion = "TLS_AUTO"
|
||||
|
||||
_ // Placeholder for SSLv3, hopefully we won't have to add this
|
||||
|
||||
// TLS versions
|
||||
TLSv1_0
|
||||
TLSv1_1
|
||||
TLSv1_2
|
||||
TLSv1_3
|
||||
TLSv1_0 TLSVersion = "TLSv1_0"
|
||||
TLSv1_1 TLSVersion = "TLSv1_1"
|
||||
TLSv1_2 TLSVersion = "TLSv1_2"
|
||||
TLSv1_3 TLSVersion = "TLSv1_3"
|
||||
)
|
||||
|
||||
var (
|
||||
TLSVersions = map[string]TLSVersion{
|
||||
"TLS_AUTO": TLSVersionAuto,
|
||||
"TLSv1_0": TLSv1_0,
|
||||
"TLSv1_1": TLSv1_1,
|
||||
"TLSv1_2": TLSv1_2,
|
||||
"TLSv1_3": TLSv1_3,
|
||||
tlsVersions = map[TLSVersion]struct{}{
|
||||
TLSVersionAuto: {},
|
||||
TLSv1_0: {},
|
||||
TLSv1_1: {},
|
||||
TLSv1_2: {},
|
||||
TLSv1_3: {},
|
||||
}
|
||||
// NOTE: This interface is deprecated in favor of TLSVersions
|
||||
// NOTE: This interface is deprecated in favor of tlsVersions
|
||||
// and should be eventually removed in a future release.
|
||||
DeprecatedConsulAgentTLSVersions = map[string]TLSVersion{
|
||||
"": TLSVersionAuto,
|
||||
|
@ -46,24 +45,10 @@ var (
|
|||
"tls12": TLSv1_2,
|
||||
"tls13": TLSv1_3,
|
||||
}
|
||||
HumanTLSVersionStrings = map[TLSVersion]string{
|
||||
TLSVersionAuto: "Allow implementation to select TLS version",
|
||||
TLSv1_0: "TLS 1.0",
|
||||
TLSv1_1: "TLS 1.1",
|
||||
TLSv1_2: "TLS 1.2",
|
||||
TLSv1_3: "TLS 1.3",
|
||||
}
|
||||
ConsulConfigTLSVersionStrings = func() map[TLSVersion]string {
|
||||
inverted := make(map[TLSVersion]string, len(TLSVersions))
|
||||
for k, v := range TLSVersions {
|
||||
inverted[v] = k
|
||||
}
|
||||
return inverted
|
||||
}()
|
||||
// NOTE: these currently map to the deprecated config strings to support the
|
||||
// deployment pattern of upgrading servers first. This map should eventually
|
||||
// be removed and any lookups updated to use ConsulConfigTLSVersionStrings
|
||||
// with newer config strings instead in a future release.
|
||||
// be removed and any lookups updated to instead use the TLSVersion string
|
||||
// values directly in a future release.
|
||||
ConsulAutoConfigTLSVersionStrings = map[TLSVersion]string{
|
||||
TLSVersionAuto: "",
|
||||
TLSv1_0: "tls10",
|
||||
|
@ -71,33 +56,51 @@ var (
|
|||
TLSv1_2: "tls12",
|
||||
TLSv1_3: "tls13",
|
||||
}
|
||||
TLSVersionsWithConfigurableCipherSuites = map[TLSVersion]struct{}{
|
||||
// NOTE: these two are implementation-dependent, but it is not expected that
|
||||
// either Go or Envoy would default to TLS 1.3 as a minimum version in the
|
||||
// near future
|
||||
TLSVersionUnspecified: {},
|
||||
TLSVersionAuto: {},
|
||||
|
||||
TLSv1_0: {},
|
||||
TLSv1_1: {},
|
||||
TLSv1_2: {},
|
||||
}
|
||||
)
|
||||
|
||||
func (v TLSVersion) String() string {
|
||||
return ConsulConfigTLSVersionStrings[v]
|
||||
func (v *TLSVersion) String() string {
|
||||
return string(*v)
|
||||
}
|
||||
|
||||
func (v TLSVersion) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.String())
|
||||
var tlsVersionComparison = map[TLSVersion]uint{
|
||||
TLSv1_0: 1,
|
||||
TLSv1_1: 2,
|
||||
TLSv1_2: 3,
|
||||
TLSv1_3: 4,
|
||||
}
|
||||
|
||||
func (v *TLSVersion) UnmarshalJSON(bytes []byte) error {
|
||||
versionStr := string(bytes)
|
||||
|
||||
if n := len(versionStr); n > 1 && versionStr[0] == '"' && versionStr[n-1] == '"' {
|
||||
versionStr = versionStr[1 : n-1] // trim surrounding quotes
|
||||
// Will only return true for concrete versions and won't catch
|
||||
// implementation-dependent conflicts with TLSVersionAuto or unspecified values
|
||||
func (a TLSVersion) LessThan(b TLSVersion) (error, bool) {
|
||||
for _, v := range []TLSVersion{a, b} {
|
||||
if _, ok := tlsVersionComparison[v]; !ok {
|
||||
return fmt.Errorf("can't compare implementation-dependent values"), false
|
||||
}
|
||||
}
|
||||
|
||||
if version, ok := TLSVersions[versionStr]; ok {
|
||||
*v = version
|
||||
return nil
|
||||
}
|
||||
|
||||
*v = TLSVersionInvalid
|
||||
return fmt.Errorf("no matching TLS Version found for %s", versionStr)
|
||||
return nil, tlsVersionComparison[a] < tlsVersionComparison[b]
|
||||
}
|
||||
|
||||
// IANA cipher suite constants and values as defined at
|
||||
func ValidateTLSVersion(v TLSVersion) error {
|
||||
if _, ok := tlsVersions[v]; !ok {
|
||||
return fmt.Errorf("no matching TLS version found for %s", v.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IANA cipher suite string constants as defined at
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
|
||||
// This is the total list of TLS 1.2-style cipher suites
|
||||
// which are currently supported by either Envoy 1.21 or the Consul agent
|
||||
|
@ -106,67 +109,49 @@ func (v *TLSVersion) UnmarshalJSON(bytes []byte) error {
|
|||
// and as supported cipher suites in the Go runtime change.
|
||||
//
|
||||
// The naming convention for cipher suites changed in TLS 1.3
|
||||
// but constant values should still be globally unqiue
|
||||
// Handling validation on a subset of TLSCipherSuite constants
|
||||
// would be a future exercise if cipher suites for TLS 1.3 ever
|
||||
// become configurable in BoringSSL, Envoy, or other implementation
|
||||
type TLSCipherSuite uint16
|
||||
// but constant values should still be globally unqiue.
|
||||
//
|
||||
// Handling validation on distinct sets of TLS 1.3 and TLS 1.2 TLSCipherSuite
|
||||
// constants would be a future exercise if cipher suites for TLS 1.3 ever
|
||||
// become configurable in BoringSSL, Envoy, or other implementation.
|
||||
type TLSCipherSuite string
|
||||
|
||||
const (
|
||||
// Envoy cipher suites also used by Consul agent
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLSCipherSuite = 0xc02b
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca9 // Not used by Consul agent yet
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xc02f
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca8 // Not used by Consul agent yet
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xc009
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xc013
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xc02c
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xc030
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xc014
|
||||
// Cipher suites used by both Envoy and Consul agent
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
|
||||
|
||||
// Older cipher suites not supported for Consul agent TLS, will eventually be removed from Envoy defaults
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009c
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002f
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009d
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035
|
||||
|
||||
// Additional cipher suites used by Consul agent but not Envoy
|
||||
// TODO: these are both explicitly listed as insecure and disabled in the Go source, should they be removed?
|
||||
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/crypto/tls/cipher_suites.go;l=329-330
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0x0023
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xc027
|
||||
// Older cipher suites not supported for Consul agent TLS,
|
||||
// will eventually be removed from Envoy defaults
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 = "TLS_RSA_WITH_AES_128_GCM_SHA256"
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = "TLS_RSA_WITH_AES_128_CBC_SHA"
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 = "TLS_RSA_WITH_AES_256_GCM_SHA384"
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = "TLS_RSA_WITH_AES_256_CBC_SHA"
|
||||
)
|
||||
|
||||
var (
|
||||
TLSCipherSuites = map[string]TLSCipherSuite{
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
consulAgentTLSCipherSuites = map[TLSCipherSuite]struct{}{
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: {},
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: {},
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: {},
|
||||
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: {},
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {},
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: {},
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: {},
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: {},
|
||||
}
|
||||
HumanTLSCipherSuiteStrings = func() map[TLSCipherSuite]string {
|
||||
inverted := make(map[TLSCipherSuite]string, len(TLSCipherSuites))
|
||||
for k, v := range TLSCipherSuites {
|
||||
inverted[v] = k
|
||||
}
|
||||
return inverted
|
||||
}()
|
||||
EnvoyTLSCipherSuiteStrings = map[TLSCipherSuite]string{
|
||||
envoyTLSCipherSuiteStrings = map[TLSCipherSuite]string{
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256",
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: "ECDHE-ECDSA-CHACHA20-POLY1305",
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256",
|
||||
|
@ -183,3 +168,50 @@ var (
|
|||
TLS_RSA_WITH_AES_256_CBC_SHA: "AES256-SHA",
|
||||
}
|
||||
)
|
||||
|
||||
func (c *TLSCipherSuite) String() string {
|
||||
return string(*c)
|
||||
}
|
||||
|
||||
func ValidateConsulAgentCipherSuites(cipherSuites []TLSCipherSuite) error {
|
||||
var unmatched []string
|
||||
|
||||
for _, c := range cipherSuites {
|
||||
if _, ok := consulAgentTLSCipherSuites[c]; !ok {
|
||||
unmatched = append(unmatched, c.String())
|
||||
}
|
||||
}
|
||||
|
||||
if len(unmatched) > 0 {
|
||||
return fmt.Errorf("no matching Consul Agent TLS cipher suite found for %s", strings.Join(unmatched, ","))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateEnvoyCipherSuites(cipherSuites []TLSCipherSuite) error {
|
||||
var unmatched []string
|
||||
|
||||
for _, c := range cipherSuites {
|
||||
if _, ok := envoyTLSCipherSuiteStrings[c]; !ok {
|
||||
unmatched = append(unmatched, c.String())
|
||||
}
|
||||
}
|
||||
|
||||
if len(unmatched) > 0 {
|
||||
return fmt.Errorf("no matching Envoy TLS cipher suite found for %s", strings.Join(unmatched, ","))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MarshalEnvoyTLSCipherSuiteStrings(cipherSuites []TLSCipherSuite) []string {
|
||||
cipherSuiteStrings := []string{}
|
||||
|
||||
for _, c := range cipherSuites {
|
||||
if s, ok := envoyTLSCipherSuiteStrings[c]; ok {
|
||||
cipherSuiteStrings = append(cipherSuiteStrings, s)
|
||||
}
|
||||
}
|
||||
|
||||
return cipherSuiteStrings
|
||||
}
|
||||
|
|
|
@ -7,14 +7,12 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTLSVersion_PartialEq(t *testing.T) {
|
||||
require.Greater(t, TLSv1_3, TLSv1_2)
|
||||
require.Greater(t, TLSv1_2, TLSv1_1)
|
||||
require.Greater(t, TLSv1_1, TLSv1_0)
|
||||
|
||||
require.Less(t, TLSv1_2, TLSv1_3)
|
||||
require.Less(t, TLSv1_1, TLSv1_2)
|
||||
require.Less(t, TLSv1_0, TLSv1_1)
|
||||
func TestTLSVersion_Valid(t *testing.T) {
|
||||
require.NoError(t, ValidateTLSVersion("TLS_AUTO"))
|
||||
require.NoError(t, ValidateTLSVersion("TLSv1_0"))
|
||||
require.NoError(t, ValidateTLSVersion("TLSv1_1"))
|
||||
require.NoError(t, ValidateTLSVersion("TLSv1_2"))
|
||||
require.NoError(t, ValidateTLSVersion("TLSv1_3"))
|
||||
}
|
||||
|
||||
func TestTLSVersion_Invalid(t *testing.T) {
|
||||
|
@ -33,16 +31,19 @@ func TestTLSVersion_Zero(t *testing.T) {
|
|||
|
||||
func TestTLSVersion_ToJSON(t *testing.T) {
|
||||
var tlsVersion TLSVersion
|
||||
err := tlsVersion.UnmarshalJSON([]byte(`"foo"`))
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tlsVersion, TLSVersionInvalid)
|
||||
|
||||
for str, version := range TLSVersions {
|
||||
// Unmarshalling won't catch invalid version strings,
|
||||
// must be checked in config or config entry validation
|
||||
err := json.Unmarshal([]byte(`"foo"`), &tlsVersion)
|
||||
require.NoError(t, err)
|
||||
|
||||
for version := range tlsVersions {
|
||||
str := version.String()
|
||||
versionJSON, err := json.Marshal(version)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, versionJSON, []byte(`"`+str+`"`))
|
||||
|
||||
err = tlsVersion.UnmarshalJSON([]byte(`"` + str + `"`))
|
||||
err = json.Unmarshal([]byte(`"`+str+`"`), &tlsVersion)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tlsVersion, version)
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
|
|||
@nspace={{or this.value.Namespace @nspace}}
|
||||
@partition={{or this.value.Partition @partition}}
|
||||
@type={{if this.value.Name 'oidc' 'secret'}}
|
||||
@value={{if this.value.Name this.value.Name this.value}}
|
||||
@value={{this.value}}
|
||||
@onchange={{queue (action dispatch "RESET") @onsubmit}}
|
||||
@onerror={{queue (action (mut this.error) value="error.errors.firstObject") (action dispatch "ERROR")}}
|
||||
/>
|
||||
|
|
|
@ -21,6 +21,15 @@ This component **does not store the resulting token**, it only emits it via
|
|||
its `onchange` argument/event handler. Errors are emitted via the `onerror`
|
||||
argument/event handler.
|
||||
|
||||
## Potential improvements
|
||||
|
||||
We could decide to remove the `@type` argument and always require an object
|
||||
passed to `@value` instead of a `String|Object`. Alternatively we could still
|
||||
allow `String|Object`. Then inside the component we could decide whether to
|
||||
use the Consul or SSO depending on the shape of the `@value` argument. All in
|
||||
all this means we can remove the `@type` argument making a slimmer component
|
||||
API.
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>Provide a widget to login with</figcaption>
|
||||
|
@ -75,7 +84,7 @@ argument/event handler.
|
|||
| `nspace` | `String` | | The name of the current namespace |
|
||||
| `partition` | `String` | | The name of the current partition |
|
||||
| `type` | `String` | | `secret` or `oidc`. `secret` is just traditional login, whereas `oidc` uses the users OIDC provider |
|
||||
| `value` | `String` | | When `type` is `secret` this should be the users secret. When `type` is `oidc` this should be the name of the `AuthMethod` to use for authentication |
|
||||
| `value` | `String|Object` | | When `type` is `secret` this should be the users secret. When `type` is `oidc` this should be object returned by Consul's AuthMethod HTTP API endpoint |
|
||||
| `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the jwt data, in this case the autorizationCode and the status |
|
||||
| `onerror` | `Function` | | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. |
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@ as |State Guard Action dispatch state|>
|
|||
@cond={{this.isSecret}}
|
||||
/>
|
||||
{{#let
|
||||
(uri '/${partition}/{$nspace}/${dc}'
|
||||
(uri '/${partition}/${nspace}/${dc}'
|
||||
(hash
|
||||
partition=@partition
|
||||
nspace=@nspace
|
||||
partition=(or @value.Partition @partition)
|
||||
nspace=(or @value.Namespace @nspace)
|
||||
dc=@dc
|
||||
)
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ as |State Guard Action dispatch state|>
|
|||
<DataSource
|
||||
@src={{uri (concat path '/oidc/provider/${value}')
|
||||
(hash
|
||||
value=@value
|
||||
value=@value.Name
|
||||
)
|
||||
}}
|
||||
@onchange={{queue (action (mut this.provider) value="data") (action dispatch "SUCCESS")}}
|
||||
|
|
|
@ -29,18 +29,35 @@
|
|||
<TopologyMetrics::SourceType @source='routing-config' />
|
||||
{{/if}}
|
||||
<div class="details">
|
||||
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (not-eq @item.Namespace @service.Namespace))}}
|
||||
<dl class="nspace">
|
||||
<dt>
|
||||
<Tooltip>
|
||||
Namespace
|
||||
</Tooltip>
|
||||
</dt>
|
||||
<dd>
|
||||
{{@item.Namespace}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
{{#if (and (can 'use partitions') (can 'use nspaces'))}}
|
||||
<div class={{if (not-eq @item.Partition @service.Partition) 'group'}}>
|
||||
{{#if (not-eq @item.Partition @service.Partition)}}
|
||||
<dl class="partition">
|
||||
<dt>
|
||||
<Tooltip>
|
||||
Admin Partition
|
||||
</Tooltip>
|
||||
</dt>
|
||||
<dd>
|
||||
{{@item.Partition}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
<span></span>
|
||||
{{#if (or (not-eq @item.Partition @service.Partition) (not-eq @item.Namespace @service.Namespace))}}
|
||||
<dl class="nspace">
|
||||
<dt>
|
||||
<Tooltip>
|
||||
Namespace
|
||||
</Tooltip>
|
||||
</dt>
|
||||
<dd>
|
||||
{{@item.Namespace}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (not-eq @item.Source 'routing-config')}}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
padding: 12px !important;
|
||||
}
|
||||
div {
|
||||
display: inline-flex;
|
||||
dl {
|
||||
display: inline-flex;
|
||||
margin-right: 8px;
|
||||
|
@ -42,11 +41,8 @@
|
|||
.health dt::before {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.nspace dt::before {
|
||||
@extend %with-folder-outline-mask, %as-pseudo;
|
||||
}
|
||||
.health dt::before {
|
||||
@extend %with-help-circle-outline-mask, %as-pseudo;
|
||||
.partition dt::before {
|
||||
@extend %with-user-team-mask, %as-pseudo;
|
||||
}
|
||||
.nspace dt::before {
|
||||
@extend %with-folder-outline-mask, %as-pseudo;
|
||||
|
@ -54,6 +50,10 @@
|
|||
.health dt::before {
|
||||
@extend %with-help-circle-outline-mask, %as-pseudo;
|
||||
}
|
||||
.health dt::before {
|
||||
@extend %with-help-circle-outline-mask, %as-pseudo;
|
||||
}
|
||||
.partition dt::before,
|
||||
.nspace dt::before,
|
||||
.health dt::before {
|
||||
background-color: rgb(var(--tone-gray-500));
|
||||
|
@ -77,5 +77,34 @@
|
|||
}
|
||||
.details {
|
||||
padding: 0 12px 12px 12px;
|
||||
> *:not(:last-child) {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.group {
|
||||
display: grid;
|
||||
grid-template-columns: 20px 1fr;
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
grid-template-areas:
|
||||
'partition partition'
|
||||
'union namespace';
|
||||
span {
|
||||
display: inline-block;
|
||||
grid-area: union;
|
||||
padding-left: 7px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
span::before {
|
||||
margin-right: 0px;
|
||||
@extend %with-union-mask, %as-pseudo;
|
||||
background-color: rgb(var(--tone-gray-500));
|
||||
}
|
||||
dl:first-child {
|
||||
grid-area: partition;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
dl:nth-child(2) {
|
||||
grid-area: namespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,13 +39,6 @@
|
|||
#upstream-container {
|
||||
padding: 12px;
|
||||
}
|
||||
#downstream-container div:first-child {
|
||||
display: inline-flex;
|
||||
span::before {
|
||||
@extend %with-info-circle-outline-mask, %as-pseudo;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
#upstream-column #upstream-container:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#upstream-container {
|
||||
background-color: rgb(var(--tone-gray-100));
|
||||
}
|
||||
#downstream-container div:first-child {
|
||||
#downstream-container > div:first-child {
|
||||
display: inline-flex;
|
||||
span::before {
|
||||
@extend %with-info-circle-outline-mask, %as-pseudo;
|
||||
|
|
|
@ -22,6 +22,7 @@ export default class OidcSerializer extends Serializer {
|
|||
cb(headers, {
|
||||
Name: query.id,
|
||||
Namespace: query.ns,
|
||||
Partition: query.partition,
|
||||
...body,
|
||||
})
|
||||
),
|
||||
|
|
|
@ -40,7 +40,7 @@ export default class OidcProviderService extends RepositoryService {
|
|||
// with an empty `ns=` Consul will use the namespace that is assigned to
|
||||
// the token, and when we get the response we can pick that back off the
|
||||
// responses `Namespace` property. As we don't receive a `Namespace`
|
||||
// property here, we have to figure this out ourselves. Biut we also want
|
||||
// property here, we have to figure this out ourselves. But we also want
|
||||
// to make this completely invisible to 'the application engineer/a
|
||||
// template engineer'. This feels like the best place/way to do it as we
|
||||
// are already in a asynchronous method, and we avoid adding extra 'just
|
||||
|
@ -54,6 +54,7 @@ export default class OidcProviderService extends RepositoryService {
|
|||
const token = (await this.settings.findBySlug('token')) || {};
|
||||
return super.findBySlug({
|
||||
ns: params.ns || token.Namespace || 'default',
|
||||
partition: params.partition || token.Partition || 'default',
|
||||
dc: params.dc,
|
||||
id: params.id,
|
||||
});
|
||||
|
|
|
@ -9448,6 +9448,16 @@
|
|||
mask-image: var(--unfold-open-24-svg);
|
||||
}
|
||||
|
||||
%with-union-icon {
|
||||
@extend %with-icon, %union-svg;
|
||||
background-image: var(--union-svg);
|
||||
}
|
||||
%with-union-mask {
|
||||
@extend %with-mask, %union-svg;
|
||||
-webkit-mask-image: var(--union-svg);
|
||||
mask-image: var(--union-svg);
|
||||
}
|
||||
|
||||
%with-unlock-16-icon {
|
||||
@extend %with-icon, %unlock-16-svg-prop;
|
||||
background-image: var(--unlock-16-svg);
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
typeof location.search.ns !== 'undefined' ? location.search.ns :
|
||||
typeof http.body.Namespace !== 'undefined' ? http.body.Namespace : 'default'
|
||||
}",
|
||||
"Partition": "${
|
||||
typeof location.search.partition !== 'undefined' ?
|
||||
location.search.partition :
|
||||
typeof http.body.Partition !== 'undefined' ? http.body.Partition : 'default'
|
||||
}",
|
||||
"Local": false,
|
||||
"Description": "AuthMethod: ${http.body.AuthMethod}; Code: ${http.body.Code}; State: ${http.body.State}; - ${fake.lorem.sentence()}",
|
||||
"Policies": [
|
||||
|
|
|
@ -16,8 +16,8 @@ return `
|
|||
"Name": "${name.split(' ').join('-').toLowerCase()}",
|
||||
"DisplayName": "${name}",
|
||||
"Kind": "${fake.helpers.randomize(['no-icon', 'google', 'okta', 'auth0', 'microsoft'])}",
|
||||
"Namespace": "default",
|
||||
"Partition": "default"
|
||||
"Namespace": "${typeof location.search.ns !== 'undefined' ? location.search.ns : 'default'}",
|
||||
"Partition": "${typeof location.search.partition !== 'undefined' ? location.search.partition : 'default'}"
|
||||
}
|
||||
`})
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ ${
|
|||
fake.seed(num);
|
||||
return range(num).map(i => {
|
||||
const nspace = i === 0 ? `default` : `${fake.hacker.noun()}-ns-${i}`;
|
||||
const partition = i === 0 ? `default` : `${fake.hacker.noun()}-partition`;
|
||||
return {
|
||||
Name: `service-${fake.random.number({min:0, max:99})}`,
|
||||
Datacenter: `${dc}`,
|
||||
Namespace: `${nspace}`
|
||||
Namespace: `${nspace}`,
|
||||
Partition: `${partition}`
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -72,6 +74,7 @@ ${(Math.random(1) > 0.3) ? `
|
|||
"Name": "${item.Name}",
|
||||
"Datacenter": "${item.Datacenter}",
|
||||
"Namespace": "${item.Namespace}",
|
||||
"Partition": "${item.Partition}",
|
||||
"ChecksPassing":${fake.random.number({min: 1, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
"ChecksWarning":${fake.random.number({min: 0, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
"ChecksCritical":${fake.random.number({min: 0, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
|
@ -101,6 +104,7 @@ ${(Math.random(1) > 0.3) ? `
|
|||
"Name": "${item.Name}",
|
||||
"Datacenter": "${item.Datacenter}",
|
||||
"Namespace": "${item.Namespace}",
|
||||
"Partition": "${item.Partition}",
|
||||
"ChecksPassing":${fake.random.number({min: 1, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
"ChecksWarning":${fake.random.number({min: 0, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
"ChecksCritical":${fake.random.number({min: 0, max: env('CONSUL_CHECK_COUNT', fake.random.number(10))})},
|
||||
|
|
|
@ -19,3 +19,33 @@ Feature: login
|
|||
headers:
|
||||
X-Consul-Token: something
|
||||
---
|
||||
@onlyNamespaceable
|
||||
Scenario: Logging in via SSO
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And SSO is enabled
|
||||
And partitions are enabled
|
||||
And 1 oidcProvider model from yaml
|
||||
---
|
||||
- DisplayName: Okta
|
||||
Name: okta
|
||||
Kind: okta
|
||||
---
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
And the "okta" oidcProvider responds with from yaml
|
||||
---
|
||||
state: state-123456789/abcdefghijklmnopqrstuvwxyz
|
||||
code: code-abcdefghijklmnopqrstuvwxyz/123456789
|
||||
---
|
||||
And I click login on the navigation
|
||||
And I click "[data-test-tab=tab_sso] button"
|
||||
And I type "partition" into "[name=partition]"
|
||||
And I click ".oidc-select button"
|
||||
Then a GET request was made to "/v1/internal/ui/oidc-auth-methods?dc=dc-1&ns=@namespace&partition=partition"
|
||||
And I click ".okta-oidc-provider"
|
||||
Then a POST request was made to "/v1/acl/oidc/auth-url?dc=dc-1&ns=@!namespace&partition=partition"
|
||||
And a POST request was made to "/v1/acl/oidc/callback?dc=dc-1&ns=@!namespace&partition=partition"
|
||||
And "[data-notification]" has the "notification-authorize" class
|
||||
And "[data-notification]" has the "success" class
|
||||
|
|
|
@ -43,6 +43,9 @@ export default function(type, value, doc = document) {
|
|||
case 'authMethod':
|
||||
key = 'CONSUL_AUTH_METHOD_COUNT';
|
||||
break;
|
||||
case 'oidcProvider':
|
||||
key = 'CONSUL_OIDC_PROVIDER_COUNT';
|
||||
break;
|
||||
case 'nspace':
|
||||
key = 'CONSUL_NSPACE_COUNT';
|
||||
break;
|
||||
|
|
|
@ -40,6 +40,9 @@ export default function(type) {
|
|||
case 'authMethod':
|
||||
requests = ['/v1/acl/auth-methods', '/v1/acl/auth-method/'];
|
||||
break;
|
||||
case 'oidcProvider':
|
||||
requests = ['/v1/internal/ui/oidc-auth-methods'];
|
||||
break;
|
||||
case 'nspace':
|
||||
requests = ['/v1/namespaces', '/v1/namespace/'];
|
||||
break;
|
||||
|
|
|
@ -103,9 +103,16 @@ export default function({
|
|||
let location = context.owner.lookup(`location:${locationType}`);
|
||||
return location.getURLFrom();
|
||||
};
|
||||
const oidcProvider = function(name, response) {
|
||||
const context = helpers.getContext();
|
||||
const provider = context.owner.lookup('torii-provider:oidc-with-url');
|
||||
provider.popup.open = async function() {
|
||||
return response;
|
||||
};
|
||||
};
|
||||
|
||||
models(library, create, setCookie);
|
||||
http(library, respondWith, setCookie);
|
||||
http(library, respondWith, setCookie, oidcProvider);
|
||||
visit(library, pages, utils.setCurrentPage, reset);
|
||||
click(library, utils.find, helpers.click);
|
||||
form(library, utils.find, helpers.fillIn, helpers.triggerKeyEvent, utils.getCurrentPage);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default function(scenario, respondWith, set) {
|
||||
export default function(scenario, respondWith, set, oidc) {
|
||||
// respondWith should set the url to return a certain response shape
|
||||
scenario
|
||||
.given(['the url "$endpoint" responds with a $status status'], function(url, status) {
|
||||
|
@ -12,6 +12,9 @@ export default function(scenario, respondWith, set) {
|
|||
}
|
||||
respondWith(url, data);
|
||||
})
|
||||
.given(['the "$provider" oidcProvider responds with from yaml\n$yaml'], function(name, data) {
|
||||
oidc(name, data);
|
||||
})
|
||||
.given('a network latency of $number', function(number) {
|
||||
set('CONSUL_LATENCY', number);
|
||||
});
|
||||
|
|
|
@ -38,6 +38,14 @@ export default function(scenario, create, set, win = window, doc = document) {
|
|||
.given(['ACLs are disabled'], function() {
|
||||
doc.cookie = `CONSUL_ACLS_ENABLE=0`;
|
||||
})
|
||||
.given(['SSO is enabled'], function() {
|
||||
doc.cookie = `CONSUL_SSO_ENABLE=1`;
|
||||
set('CONSUL_SSO_ENABLE', 1);
|
||||
})
|
||||
.given(['partitions are enabled'], function() {
|
||||
doc.cookie = `CONSUL_PARTITIONS_ENABLE=1`;
|
||||
set('CONSUL_PARTITIONS_ENABLE', 1);
|
||||
})
|
||||
.given(['the default ACL policy is "$policy"'], function(policy) {
|
||||
set('CONSUL_ACL_POLICY', policy);
|
||||
})
|
||||
|
|
|
@ -128,7 +128,8 @@ $ curl \
|
|||
"ReplicatedIndex": 1976,
|
||||
"ReplicatedTokenIndex": 2018,
|
||||
"LastSuccess": "2018-11-03T06:28:58Z",
|
||||
"LastError": "2016-11-03T06:28:28Z"
|
||||
"LastError": "2016-11-03T06:28:28Z",
|
||||
"LastErrorMessage": "failed to retrieve ACL policy updates: RPC rate limit exceeded"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -179,6 +180,9 @@ $ curl \
|
|||
replication process is not in a good state. A zero value of
|
||||
"0001-01-01T00:00:00Z" will be present if no sync has resulted in an error.
|
||||
|
||||
- `LastErrorMessage` - The last error message produced at the time of `LastError`.
|
||||
An empty string indicates that no sync has resulted in an error.
|
||||
|
||||
## Translate Rules
|
||||
|
||||
-> **Deprecated** - This endpoint was removed in Consul 1.11.0.
|
||||
|
|
|
@ -18,14 +18,16 @@ replication between datacenters, please view the
|
|||
|
||||
~> Values in the KV store cannot be larger than 512kb.
|
||||
|
||||
For multi-key updates, please consider using [transaction](/api/txn).
|
||||
In order to perform atomic operations on multiple KV pairs (up to a limit of 64)
|
||||
please consider using [transactions](/api/txn) instead.
|
||||
|
||||
## Read Key
|
||||
|
||||
This endpoint returns the specified key. If no key exists at the given path, a
|
||||
404 is returned instead of a 200 response.
|
||||
|
||||
For multi-key reads, please consider using [transaction](/api/txn).
|
||||
For multi-key reads (up to a limit of 64 KV operations) please consider using
|
||||
[transactions](/api/txn) instead.
|
||||
|
||||
| Method | Path | Produces |
|
||||
| ------ | ---------- | ------------------ |
|
||||
|
|
|
@ -14,6 +14,12 @@ store at the given key name. If no key exists with that name, an error is
|
|||
returned. If a key exists with that name but has no data, nothing is returned.
|
||||
A key name or prefix is required.
|
||||
|
||||
-> **Note**: When reading many entries under a given prefix, it may be worth
|
||||
considering [`kv export`](/commands/kv/export) instead. The kv export output
|
||||
can be used with [`kv import`](/commands/kv/import) to move entire trees between
|
||||
Consul clusters. Alternatively, the [transaction API](/api-docs/txn) provides
|
||||
support for performing up to 64 KV operations atomically.
|
||||
|
||||
The table below shows this command's [required ACLs](/api#authentication). Configuration of
|
||||
[blocking queries](/api/features/blocking) and [agent caching](/api/features/caching)
|
||||
are not supported from commands, but may be from the corresponding HTTP endpoint.
|
||||
|
@ -63,17 +69,27 @@ Usage: `consul kv get [options] [KEY_OR_PREFIX]`
|
|||
To retrieve the value for the key named "redis/config/connections" in the
|
||||
KV store:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get redis/config/connections
|
||||
5
|
||||
```
|
||||
|
||||
This will return the original, raw value stored in Consul. To view detailed
|
||||
information about the key, specify the "-detailed" flag. This will output all
|
||||
known metadata about the key including ModifyIndex and any user-supplied
|
||||
flags:
|
||||
This will return the original raw value stored in Consul.
|
||||
|
||||
```shell-session
|
||||
If the key with the given name does not exist, an error is returned.
|
||||
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get not-a-real-key
|
||||
Error! No key exists at: not-a-real-key
|
||||
```
|
||||
|
||||
### Detailed Output
|
||||
|
||||
To view detailed information about the key, specify the `-detailed` flag.
|
||||
This will output all known metadata about the key including `ModifyIndex`
|
||||
and any user-supplied flags:
|
||||
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -detailed redis/config/connections
|
||||
CreateIndex 336
|
||||
Flags 0
|
||||
|
@ -84,26 +100,22 @@ Session -
|
|||
Value 5
|
||||
```
|
||||
|
||||
If the key with the given name does not exist, an error is returned:
|
||||
### Recursively Reading By Prefix
|
||||
|
||||
```shell-session
|
||||
$ consul kv get not-a-real-key
|
||||
Error! No key exists at: not-a-real-key
|
||||
```
|
||||
To treat the path as a prefix and list all entries which start with the given
|
||||
prefix, specify the `-recurse` flag:
|
||||
|
||||
To treat the path as a prefix and list all keys which start with the given
|
||||
prefix, specify the "-recurse" flag:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -recurse redis/
|
||||
redis/config/connections:5
|
||||
redis/config/cpu:128
|
||||
redis/config/memory:512
|
||||
```
|
||||
|
||||
Or list detailed information about all pairs under a prefix:
|
||||
Alternatively, combine with the `-detailed` flag to list detailed information
|
||||
about all entries under a prefix:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -recurse -detailed redis
|
||||
CreateIndex 336
|
||||
Flags 0
|
||||
|
@ -130,10 +142,12 @@ Session -
|
|||
Value 512
|
||||
```
|
||||
|
||||
To just list the keys which start with the specified prefix, use the "-keys"
|
||||
### Listing Keys
|
||||
|
||||
To just list the keys which start with the specified prefix, use the `-keys`
|
||||
option instead. This is more performant and results in a smaller payload:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -keys redis/config/
|
||||
redis/config/connections
|
||||
redis/config/cpu
|
||||
|
@ -144,7 +158,7 @@ By default, the `-keys` operation uses a separator of "/", meaning it will not
|
|||
recurse beyond that separator. You can choose a different separator by setting
|
||||
`-separator="<string>"`.
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -keys -separator="c" redis
|
||||
redis/c
|
||||
```
|
||||
|
@ -152,7 +166,7 @@ redis/c
|
|||
Alternatively, you can disable the separator altogether by setting it to the
|
||||
empty string:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -keys -separator="" redis
|
||||
redis/config/connections
|
||||
redis/config/cpu
|
||||
|
@ -161,7 +175,7 @@ redis/config/memory
|
|||
|
||||
To list all keys at the root, simply omit the prefix parameter:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -keys
|
||||
memcached/
|
||||
redis/
|
||||
|
|
|
@ -11,6 +11,11 @@ Corresponding HTTP API Endpoint: [\[PUT\] /v1/kv/:key](/api-docs/kv#create-updat
|
|||
|
||||
The `kv put` command writes the data to the given path in the KV store.
|
||||
|
||||
-> **Note**: When writing multiple entries at once, consider using
|
||||
[`kv import`](/commands/kv/import) instead. Alternatively, the
|
||||
[transaction API](/api-docs/txn) provides support for performing up to
|
||||
64 KV operations atomically.
|
||||
|
||||
The table below shows this command's [required ACLs](/api#authentication). Configuration of
|
||||
[blocking queries](/api/features/blocking) and [agent caching](/api/features/caching)
|
||||
are not supported from commands, but may be from the corresponding HTTP endpoint.
|
||||
|
@ -67,57 +72,76 @@ Usage: `consul kv put [options] KEY [DATA]`
|
|||
To insert a value of "5" for the key named "redis/config/connections" in the
|
||||
KV store:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put redis/config/connections 5
|
||||
Success! Data written to: redis/config/connections
|
||||
```
|
||||
|
||||
If no data is specified, the key will be created with empty data:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put redis/config/connections
|
||||
Success! Data written to: redis/config/connections
|
||||
```
|
||||
|
||||
If the `-base64` flag is set, the data will be decoded before writing:
|
||||
!> **Be careful of overwriting data!** The above operation would overwrite
|
||||
any existing value at the key to the empty value.
|
||||
|
||||
```shell-session
|
||||
### Base64 Encoded Values
|
||||
|
||||
If the `-base64` flag is set, the given data will be Base64-decoded before writing:
|
||||
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put -base64 foo/encoded aGVsbG8gd29ybGQK
|
||||
Success! Data written to: foo/encoded
|
||||
```
|
||||
|
||||
!> **Be careful when overwriting data!** The above operation would overwrite
|
||||
the value at the key to the empty value.
|
||||
### Longer or Sensitive Values
|
||||
|
||||
For longer or sensitive values, it is possible to read from a file by prefixing
|
||||
with the `@` symbol:
|
||||
For longer or sensitive values, it is possible to read from a file by
|
||||
supplying its path prefixed with the `@` symbol:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put redis/config/password @password.txt
|
||||
Success! Data written to: redis/config/connections
|
||||
Success! Data written to: redis/config/password
|
||||
```
|
||||
|
||||
Or read values from stdin by specifying the `-` symbol:
|
||||
|
||||
```shell-session
|
||||
$ echo "5" | consul kv put redis/config/password -
|
||||
```shell-session hideClipboard
|
||||
$ echo "5" | consul kv put redis/config/connections -
|
||||
Success! Data written to: redis/config/connections
|
||||
```
|
||||
|
||||
$ consul kv put redis/config/password -
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put redis/config/connections -
|
||||
5
|
||||
<CTRL+D>
|
||||
Success! Data written to: redis/config/connections
|
||||
```
|
||||
|
||||
~> For secret and sensitive values, you should consider using a secret
|
||||
management solution like **[HashiCorp's Vault](https://www.vaultproject.io/)**.
|
||||
While it is possible to secure values in Consul's KV store, Vault provides a
|
||||
more robust interface for secret management.
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put leaderboard/scores - <<EOF
|
||||
{
|
||||
"user-a": 100,
|
||||
"user-b": 250,
|
||||
"user-c": 75
|
||||
}
|
||||
EOF
|
||||
Success! Data written to: leaderboard/scores
|
||||
```
|
||||
|
||||
~> **Warning**: For secret and sensitive values, you should consider using a
|
||||
secret management solution like **[HashiCorp's Vault](https://learn.hashicorp.com/tutorials/vault/static-secrets?in=vault/secrets-management)**.
|
||||
While it is possible to encrpyt data before writing it to Consul's KV store,
|
||||
Consul provides no built-in support for encryption at-rest.
|
||||
|
||||
### Atomic Check-And-Set (CAS)
|
||||
|
||||
To only update a key if it has not been modified since a given index, specify
|
||||
the `-cas` and `-modify-index` flags:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv get -detailed redis/config/connections | grep ModifyIndex
|
||||
ModifyIndex 456
|
||||
|
||||
|
@ -128,24 +152,18 @@ $ consul kv put -cas -modify-index=456 redis/config/connections 10
|
|||
Success! Data written to: redis/config/connections
|
||||
```
|
||||
|
||||
To specify flags on the key, use the `-flags` option. These flags are completely
|
||||
controlled by the user:
|
||||
|
||||
```shell-session
|
||||
$ consul kv put -flags=42 redis/config/password s3cr3t
|
||||
Success! Data written to: redis/config/password
|
||||
```
|
||||
### Locking Primitives
|
||||
|
||||
To create or tune a lock, use the `-acquire` and `-session` flags. The session must already exist (this command will not create it or manage it):
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put -acquire -session=abc123 redis/lock/update
|
||||
Success! Lock acquired on: redis/lock/update
|
||||
```
|
||||
|
||||
When you are finished, release the lock:
|
||||
|
||||
```shell-session
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put -release -session=acb123 redis/lock/update
|
||||
Success! Lock released on: redis/lock/update
|
||||
```
|
||||
|
@ -154,3 +172,13 @@ Success! Lock released on: redis/lock/update
|
|||
low-level primitives, you may want to look at the [<tt>consul
|
||||
lock</tt>](/commands/lock) command. It provides higher-level
|
||||
functionality without exposing the internal APIs of Consul.
|
||||
|
||||
### Flags
|
||||
|
||||
To set user-defined flags on the entry, use the `-flags` option. These flags
|
||||
are completely controlled by the user and have no special meaning to Consul:
|
||||
|
||||
```shell-session hideClipboard
|
||||
$ consul kv put -flags=42 redis/config/password s3cr3t
|
||||
Success! Data written to: redis/config/password
|
||||
```
|
||||
|
|
|
@ -51,7 +51,7 @@ See [Kubernetes Custom Resource Definitions](/docs/k8s/crds).
|
|||
Configuration entries outside of Kubernetes should be managed with the Consul
|
||||
[CLI](/commands/config) or [API](/api/config). Additionally, as a
|
||||
convenience for initial cluster bootstrapping, configuration entries can be
|
||||
specified in all of the Consul servers's
|
||||
specified in the Consul servers agent's
|
||||
[configuration files](/docs/agent/options#config_entries_bootstrap)
|
||||
|
||||
### Managing Configuration Entries with the CLI
|
||||
|
|
|
@ -136,7 +136,7 @@ $ consul agent -data-dir=/tmp/consul
|
|||
- **Server**: This indicates whether the agent is running in server or client
|
||||
mode.
|
||||
Running an agent in server mode requires additional overhead. This is because they participate in the consensus quorum, store cluster state, and handle queries. A server may also be
|
||||
in ["bootstrap"](/docs/agent/options#_bootstrap_expect) mode, which enables the server to elect itselft as the Raft leader. Multiple servers cannot be in bootstrap mode because it would put the cluster in an inconsistent state.
|
||||
in ["bootstrap"](/docs/agent/options#_bootstrap_expect) mode, which enables the server to elect itself as the Raft leader. Multiple servers cannot be in bootstrap mode because it would put the cluster in an inconsistent state.
|
||||
|
||||
- **Client Addr**: This is the address used for client interfaces to the agent.
|
||||
This includes the ports for the HTTP and DNS interfaces. By default, this
|
||||
|
@ -193,11 +193,9 @@ The following settings are commonly used in the configuration file (also called
|
|||
The following example configuration is for a server agent named "`consul-server`". The server is [bootstrapped](/docs/agent/options#_bootstrap) and the Consul GUI is enabled.
|
||||
The reason this server agent is configured for a service mesh is that the `connect` configuration is enabled. Connect is Consul's service mesh component that provides service-to-service connection authorization and encryption using mutual Transport Layer Security (TLS). Applications can use sidecar proxies in a service mesh configuration to establish TLS connections for inbound and outbound connections without being aware of Connect at all. See [Connect](/docs/connect) for details.
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="HCL">
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
|
||||
node_name = "consul-server"
|
||||
server = true
|
||||
bootstrap = true
|
||||
|
@ -215,8 +213,6 @@ connect {
|
|||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="JSON">
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -238,19 +234,16 @@ connect {
|
|||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</CodeTabs>
|
||||
|
||||
### Server Node with Encryption Enabled
|
||||
|
||||
The following example shows a server node configured with encryption enabled.
|
||||
Refer to the [Security](/docs/security) chapter for additional information about how to configure security options for Consul.
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="HCL">
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
|
||||
node_name = "consul-server"
|
||||
server = true
|
||||
ui_config {
|
||||
|
@ -274,8 +267,6 @@ key_file = "/consul/config/certs/dc1-server-consul-0-key.pem"
|
|||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="JSON">
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -299,19 +290,16 @@ key_file = "/consul/config/certs/dc1-server-consul-0-key.pem"
|
|||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</CodeTabs>
|
||||
|
||||
### Client Node Registering a Service
|
||||
|
||||
Using Consul as a central service registry is a common use case.
|
||||
The following example configuration includes common settings to register a service with a Consul agent and enable health checks (see [Checks](/docs/discovery/checks) to learn more about health checks):
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="HCL">
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
|
||||
node_name = "consul-client"
|
||||
server = false
|
||||
datacenter = "dc1"
|
||||
|
@ -335,9 +323,6 @@ service {
|
|||
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="JSON">
|
||||
|
||||
```json
|
||||
{
|
||||
"node_name": "consul-client",
|
||||
|
@ -363,8 +348,58 @@ service {
|
|||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</CodeTabs>
|
||||
|
||||
## Client Node with Multiple Interfaces or IP addresses
|
||||
|
||||
The following example shows how to configure Consul to listen on multiple interfaces or IP addresses using a [go-sockaddr template].
|
||||
|
||||
The `bind_addr` is used for internal RPC and Serf communication ([read the Agent Configuration for more information](/docs/agent/options#bind_addr)).
|
||||
|
||||
The `client_addr` configuration specifies IP addresses used for HTTP, HTTPS, DNS and gRPC servers. ([read the Agent Configuration for more information](/docs/agent/options#client_addr)).
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
node_name = "consul-server"
|
||||
server = true
|
||||
bootstrap = true
|
||||
ui_config {
|
||||
enabled = true
|
||||
}
|
||||
datacenter = "dc1"
|
||||
data_dir = "consul/data"
|
||||
log_level = "INFO"
|
||||
|
||||
# used for internal RPC and Serf
|
||||
bind_addr = "0.0.0.0"
|
||||
|
||||
# Used for HTTP, HTTPS, DNS, and gRPC addresses.
|
||||
# loopback is not included in GetPrivateInterfaces because it is not routable.
|
||||
client_addr = "{{ GetPrivateInterfaces | exclude \"type\" \"ipv6\" | join \"address\" \" \" }} {{ GetAllInterfaces | include \"flags\" \"loopback\" | join \"address\" \" \" }}"
|
||||
|
||||
# advertises gossip and RPC interface to other nodes
|
||||
advertise_addr = "{{ GetInterfaceIP \"en0\" }}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"node_name": "consul-server",
|
||||
"server": true,
|
||||
"bootstrap": true,
|
||||
"ui_config": {
|
||||
"enabled": true
|
||||
},
|
||||
"datacenter": "dc1",
|
||||
"data_dir": "consul/data",
|
||||
"log_level": "INFO",
|
||||
"bind_addr": "{{ GetPrivateIP }}",
|
||||
"client_addr": "{{ GetPrivateInterfaces | exclude \"type\" \"ipv6\" | join \"address\" \" \" }} {{ GetAllInterfaces | include \"flags\" \"loopback\" | join \"address\" \" \" }}",
|
||||
"advertise_addr": "{{ GetInterfaceIP \"en0\"}}"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
## Stopping an Agent
|
||||
|
||||
|
@ -402,3 +437,7 @@ from the load balancer pool.
|
|||
The [`skip_leave_on_interrupt`](/docs/agent/options#skip_leave_on_interrupt) and
|
||||
[`leave_on_terminate`](/docs/agent/options#leave_on_terminate) configuration
|
||||
options allow you to adjust this behavior.
|
||||
|
||||
<!-- list of reference-style links -->
|
||||
|
||||
[go-sockaddr template]: https://godoc.org/github.com/hashicorp/go-sockaddr/template
|
||||
|
|
|
@ -54,6 +54,8 @@ information.
|
|||
|
||||
## Command-line Options ((#commandline_options))
|
||||
|
||||
-> **Note:** Some CLI arguments may be different from HCL keys. See [Configuration Key Reference](#config_key_reference) for equivalent HCL Keys.
|
||||
|
||||
The options below are all specified on the command-line.
|
||||
|
||||
- `-advertise` ((#\_advertise)) - The advertise address is used to change
|
||||
|
@ -64,6 +66,15 @@ The options below are all specified on the command-line.
|
|||
state as other nodes will treat the non-routability as a failure. In Consul 1.1.0 and later this can be dynamically defined with a [go-sockaddr]
|
||||
template that is resolved at runtime.
|
||||
|
||||
<CodeBlockConfig>
|
||||
|
||||
```shell
|
||||
# Using a static network interface name
|
||||
$ consul agent -advertise '{{ GetInterfaceIP "eth0" }}'
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
- `-advertise-wan` ((#\_advertise-wan)) - The advertise WAN address is used
|
||||
to change the address that we advertise to server nodes joining through the WAN.
|
||||
This can also be set on client agents when used in combination with the [`translate_wan_addrs`](#translate_wan_addrs) configuration option. By default, the [`-advertise`](#_advertise) address
|
||||
|
@ -137,11 +148,35 @@ The options below are all specified on the command-line.
|
|||
capture, it is possible to use [`discard_check_output`](#discard_check_output).
|
||||
|
||||
- `-client` ((#\_client)) - The address to which Consul will bind client
|
||||
interfaces, including the HTTP and DNS servers. By default, this is "127.0.0.1",
|
||||
interfaces, including the HTTP, HTTPS, gRPC and DNS servers. By default, this is "127.0.0.1",
|
||||
allowing only loopback connections. In Consul 1.0 and later this can be set to
|
||||
a space-separated list of addresses to bind to, or a [go-sockaddr]
|
||||
template that can potentially resolve to multiple addresses.
|
||||
|
||||
<CodeBlockConfig hideClipboard heading="Bind consul client interfaces to private IPv4 interfaces">
|
||||
|
||||
```shell
|
||||
$ consul agent -dev -client '{{ GetPrivateInterfaces | exclude "type" "ipv6" | join "address" " " }}'
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
<CodeBlockConfig hideClipboard heading="Bind consul client interfaces to private IP addresses and loopback">
|
||||
|
||||
```shell
|
||||
$ consul agent -dev -client '{{ GetPrivateInterfaces | join "address" " " }} {{ GetAllInterfaces | include "flags" "loopback" | join "address" " " }}'
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
<CodeBlockConfig hideClipboard heading="Exclude private interfaces that start with 'br-'">
|
||||
|
||||
```shell
|
||||
$ consul agent -dev -client '{{ GetPrivateInterfaces | exclude "name" "br.*" | join "address" " " }}'
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
- `-config-file` ((#\_config_file)) - A configuration file to load. For
|
||||
more information on the format of this file, read the [Configuration Files](#configuration_files)
|
||||
section. This option can be specified multiple times to load multiple configuration
|
||||
|
@ -566,7 +601,7 @@ definitions support being updated during a reload.
|
|||
}
|
||||
```
|
||||
|
||||
#### Configuration Key Reference
|
||||
#### Configuration Key Reference ((#config_key_reference))
|
||||
|
||||
-> **Note:** All the TTL values described below are parsed by Go's `time` package, and have the following
|
||||
[formatting specification](https://golang.org/pkg/time/#ParseDuration): "A
|
||||
|
|
|
@ -350,8 +350,8 @@ These metrics are used to monitor the health of the Consul servers.
|
|||
| `consul.raft.boltdb.logSize` | Measures the size of logs being written to the db. | bytes | sample |
|
||||
| `consul.raft.boltdb.numFreePages` | Represents the number of free pages within the raft.db file. | pages | gauge |
|
||||
| `consul.raft.boltdb.numPendingPages` | Represents the number of pending pages within the raft.db that will soon become free. | pages | gauge |
|
||||
| `consul.raft.boltdb.openReadTxn` | Represents the number of open read transactions against the db | transactions | guage |
|
||||
| `consul.raft.boltdb.totalReadTxn` | Represents the total number of started read transactions against the db | transactions | guage |
|
||||
| `consul.raft.boltdb.openReadTxn` | Represents the number of open read transactions against the db | transactions | gauge |
|
||||
| `consul.raft.boltdb.totalReadTxn` | Represents the total number of started read transactions against the db | transactions | gauge |
|
||||
| `consul.raft.boltdb.storeLogs` | Measures the amount of time spent writing logs to the db. | ms | timer |
|
||||
| `consul.raft.boltdb.txstats.cursorCount` | Counts the number of cursors created since Consul was started. | cursors | counter |
|
||||
| `consul.raft.boltdb.txstats.nodeCount` | Counts the number of node allocations within the db since Consul was started. | allocations | counter |
|
||||
|
@ -398,7 +398,7 @@ These metrics are used to monitor the health of the Consul servers.
|
|||
| `consul.raft.state.leader` | Increments whenever a Consul server becomes a leader. If there are frequent leadership changes this may be indication that the servers are overloaded and aren't meeting the soft real-time requirements for Raft, or that there are networking problems between the servers. | leadership transitions / interval | counter |
|
||||
| `consul.raft.state.follower` | Counts the number of times an agent has entered the follower mode. This happens when a new agent joins the cluster or after the end of a leader election. | follower state entered / interval | counter |
|
||||
| `consul.raft.transition.heartbeat_timeout` | The number of times an agent has transitioned to the Candidate state, after receive no heartbeat messages from the last known leader. | timeouts / interval | counter |
|
||||
| `consul.raft.verify_leader` | Counts the number of times an agent checks whether it is still the leader or not | checks / interval | Counter |
|
||||
| `consul.raft.verify_leader` | This metric doesn't have a direct correlation to the leader change. It just counts the number of times an agent checks if it is still the leader or not. For example, during every consistent read, the check is done. Depending on the load in the system, this metric count can be high as it is incremented each time a consistent read is completed. | checks / interval | Counter |
|
||||
| `consul.rpc.accept_conn` | Increments when a server accepts an RPC connection. | connections | counter |
|
||||
| `consul.catalog.register` | Measures the time it takes to complete a catalog register operation. | ms | timer |
|
||||
| `consul.catalog.deregister` | Measures the time it takes to complete a catalog deregister operation. | ms | timer |
|
||||
|
|
|
@ -48,14 +48,20 @@ Computing the estimated network round trip time between any two nodes is simple
|
|||
once you have their coordinates. Here's a sample coordinate, as returned from the
|
||||
[Coordinate endpoint](/api/coordinate).
|
||||
|
||||
<CodeBlockConfig heading="Sample coordinate from Coordinate endpoint" hideClipboard>
|
||||
|
||||
```json
|
||||
...
|
||||
"Coord": {
|
||||
"Adjustment": 0.1,
|
||||
"Error": 1.5,
|
||||
"Height": 0.02,
|
||||
"Vec": [0.34,0.68,0.003,0.01,0.05,0.1,0.34,0.06]
|
||||
}
|
||||
...
|
||||
```
|
||||
"Coord": {
|
||||
"Adjustment": 0.1,
|
||||
"Error": 1.5,
|
||||
"Height": 0.02,
|
||||
"Vec": [0.34,0.68,0.003,0.01,0.05,0.1,0.34,0.06]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
All values are floating point numbers in units of seconds, except for the error
|
||||
term which isn't used for distance calculations.
|
||||
|
@ -63,7 +69,9 @@ term which isn't used for distance calculations.
|
|||
Here's a complete example in Go showing how to compute the distance between two
|
||||
coordinates:
|
||||
|
||||
```
|
||||
<CodeBlockConfig heading="Computing distance between two coordinates with Go">
|
||||
|
||||
```go
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
@ -97,3 +105,5 @@ func dist(a *coordinate.Coordinate, b *coordinate.Coordinate) time.Duration {
|
|||
return time.Duration(rtt * secondsToNanoseconds)
|
||||
}
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
|
|
@ -23,6 +23,12 @@ and discovery terminates.
|
|||
- Service resolver config entries are a component of [L7 Traffic
|
||||
Management](/docs/connect/l7-traffic-management).
|
||||
|
||||
## UI
|
||||
|
||||
Once a `service-resolver` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the *routing* tab.
|
||||
|
||||
![screenshot of service resolver in the UI](/img/l7-routing/Resolver.png)
|
||||
|
||||
## Sample Config Entries
|
||||
|
||||
### Filter on service version
|
||||
|
@ -116,9 +122,9 @@ spec:
|
|||
|
||||
</CodeTabs>
|
||||
|
||||
### Datacenter failover
|
||||
### Failover
|
||||
|
||||
Enable failover for subset 'v2' to 'dc2', and all other subsets to dc3 or dc4:
|
||||
Enable failover for subset `v2` to `dc2`, and all other subsets to `dc3` or `dc4`:
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
|
@ -168,6 +174,104 @@ spec:
|
|||
|
||||
</CodeTabs>
|
||||
|
||||
<EnterpriseAlert product="consul">
|
||||
Failover to another datacenter and namespace for all service subsets.
|
||||
</EnterpriseAlert>
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
```hcl
|
||||
Kind = "service-resolver"
|
||||
Name = "product-api"
|
||||
Namespace = "primary"
|
||||
ConnectTimeout = "0s"
|
||||
Failover = {
|
||||
"*" = {
|
||||
Datacenters = ["dc2"]
|
||||
Namespace = "secondary"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: consul.hashicorp.com/v1alpha1
|
||||
kind: ServiceResolver
|
||||
metadata:
|
||||
name: product-api
|
||||
namespace: primary
|
||||
spec:
|
||||
connectTimeout: 0s
|
||||
failover:
|
||||
namespace: 'secondary'
|
||||
datacenters: ['dc2']
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "service-resolver",
|
||||
"Name": "product-api",
|
||||
"Namespace": "primary",
|
||||
"ConnectTimeout": "0s",
|
||||
"Failover": {
|
||||
"*": {
|
||||
"Datacenters": ["dc2"],
|
||||
"Namespace": "secondary"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
<EnterpriseAlert product="consul">
|
||||
Failover within a datacenter and a different namespace.
|
||||
</EnterpriseAlert>
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
```hcl
|
||||
Kind = "service-resolver"
|
||||
Name = "product-api"
|
||||
Namespace = "primary"
|
||||
ConnectTimeout = "0s"
|
||||
Failover = {
|
||||
"*" = {
|
||||
Service = "product-api-backup"
|
||||
Namespace = "secondary"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: consul.hashicorp.com/v1alpha1
|
||||
kind: ServiceResolver
|
||||
metadata:
|
||||
name: product-api
|
||||
namespace: primary
|
||||
spec:
|
||||
connectTimeout: 0s
|
||||
failover:
|
||||
service: 'product-api-backup'
|
||||
namespace: 'secondary'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "service-resolver",
|
||||
"Name": "product-api",
|
||||
"Namespace": "primary",
|
||||
"ConnectTimeout": "0s",
|
||||
"Failover": {
|
||||
"*": {
|
||||
"Service": "product-api-backup",
|
||||
"Namespace": "secondary"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
### Consistent load balancing
|
||||
|
||||
Apply consistent load balancing for requests based on `x-user-id` header:
|
||||
|
|
|
@ -36,6 +36,13 @@ service of the same name.
|
|||
to any configured
|
||||
[`service-resolver`](/docs/connect/config-entries/service-resolver).
|
||||
|
||||
## UI
|
||||
|
||||
|
||||
Once a `service-router` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the *routing* tab.
|
||||
|
||||
![screenshot of service router in the UI](/img/l7-routing/Router.png)
|
||||
|
||||
## Sample Config Entries
|
||||
|
||||
### Path prefix matching
|
||||
|
|
|
@ -39,6 +39,12 @@ resolution stage.
|
|||
to any configured
|
||||
[`service-resolver`](/docs/connect/config-entries/service-resolver).
|
||||
|
||||
## UI
|
||||
|
||||
Once a `service-splitter` is successfully entered, you can view it in the UI. Service routers, service splitters, and service resolvers can all be viewed by clicking on your service then switching to the *routing* tab.
|
||||
|
||||
![screenshot of service splitter in the UI](/img/l7-routing/Splitter.png)
|
||||
|
||||
## Sample Config Entries
|
||||
|
||||
### Two subsets of same service
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
---
|
||||
layout: docs
|
||||
page_title: Distributed Tracing
|
||||
description: >-
|
||||
Distributed tracing is a way to track and correlate requests across microservices.
|
||||
---
|
||||
|
||||
# Distributed Tracing
|
||||
|
||||
Distributed tracing is a way to track and correlate requests across microservices. Distributed tracing must first
|
||||
be implemented in each application, it cannot be added by Consul. Once implemented in your applications, adding
|
||||
distributed tracing to Consul will add the sidecar proxies as spans in the request path.
|
||||
|
||||
## Application Changes
|
||||
|
||||
Consul alone cannot implement distributed tracing for your applications. Each application must propagate the required
|
||||
headers. Typically this is done using a tracing library such as:
|
||||
|
||||
- https://github.com/opentracing/opentracing-go
|
||||
- https://github.com/DataDog/dd-trace-go
|
||||
- https://github.com/openzipkin/zipkin-go
|
||||
|
||||
## Configuration
|
||||
|
||||
Once your applications have been instrumented with a tracing library, you are ready to configure Consul to add sidecar
|
||||
proxy spans to the trace. Your eventual config will look something like:
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
```hcl
|
||||
Kind = "proxy-defaults"
|
||||
Name = "global"
|
||||
Config {
|
||||
protocol = "http"
|
||||
envoy_tracing_json = <<EOF
|
||||
{
|
||||
"http":{
|
||||
"name":"envoy.tracers.zipkin",
|
||||
"typedConfig":{
|
||||
"@type":"type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
|
||||
"collector_cluster":"collector_cluster_name",
|
||||
"collector_endpoint_version":"HTTP_JSON",
|
||||
"collector_endpoint":"/api/v2/spans",
|
||||
"shared_span_context":false
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
envoy_extra_static_clusters_json = <<EOF
|
||||
{
|
||||
"connect_timeout":"3.000s",
|
||||
"dns_lookup_family":"V4_ONLY",
|
||||
"lb_policy":"ROUND_ROBIN",
|
||||
"load_assignment":{
|
||||
"cluster_name":"collector_cluster_name",
|
||||
"endpoints":[
|
||||
{
|
||||
"lb_endpoints":[
|
||||
{
|
||||
"endpoint":{
|
||||
"address":{
|
||||
"socket_address":{
|
||||
"address":"collector-url",
|
||||
"port_value":9411,
|
||||
"protocol":"TCP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name":"collector_cluster_name",
|
||||
"type":"STRICT_DNS"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: consul.hashicorp.com/v1alpha1
|
||||
kind: ProxyDefaults
|
||||
metadata:
|
||||
name: global
|
||||
spec:
|
||||
config:
|
||||
protocol: http
|
||||
envoy_tracing_json: |
|
||||
{
|
||||
"http":{
|
||||
"name":"envoy.tracers.zipkin",
|
||||
"typedConfig":{
|
||||
"@type":"type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
|
||||
"collector_cluster":"collector_cluster_name",
|
||||
"collector_endpoint_version":"HTTP_JSON",
|
||||
"collector_endpoint":"/api/v2/spans",
|
||||
"shared_span_context":false
|
||||
}
|
||||
}
|
||||
}
|
||||
envoy_extra_static_clusters_json: |
|
||||
{
|
||||
"connect_timeout":"3.000s",
|
||||
"dns_lookup_family":"V4_ONLY",
|
||||
"lb_policy":"ROUND_ROBIN",
|
||||
"load_assignment":{
|
||||
"cluster_name":"collector_cluster_name",
|
||||
"endpoints":[
|
||||
{
|
||||
"lb_endpoints":[
|
||||
{
|
||||
"endpoint":{
|
||||
"address":{
|
||||
"socket_address":{
|
||||
"address":"collector-url",
|
||||
"port_value":9411,
|
||||
"protocol":"TCP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name":"collector_cluster_name",
|
||||
"type":"STRICT_DNS"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "ProxyDefaults",
|
||||
"Name": "global",
|
||||
"Config": {
|
||||
"protocol": "http",
|
||||
"envoy_tracing_json": "{\"http\":{\"name\":\"envoy.tracers.zipkin\",\"typedConfig\":{\"@type\":\"type.googleapis.com/envoy.config.trace.v3.ZipkinConfig\",\"collector_cluster\":\"collector_cluster_name\",\"collector_endpoint_version\":\"HTTP_JSON\",\"collector_endpoint\":\"/api/v2/spans\",\"shared_span_context\":false}}}",
|
||||
"envoy_extra_static_clusters_json": "{\"connect_timeout\":\"3.000s\",\"dns_lookup_family\":\"V4_ONLY\",\"lb_policy\":\"ROUND_ROBIN\",\"load_assignment\":{\"cluster_name\":\"collector_cluster_name\",\"endpoints\":[{\"lb_endpoints\":[{\"endpoint\":{\"address\":{\"socket_address\":{\"address\":\"collector-url\",\"port_value\":9411,\"protocol\":\"TCP\"}}}}]}]},\"name\":\"collector_cluster_name\",\"type\":\"STRICT_DNS\"}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
-> **NOTE:** This example uses a [proxy defaults](/docs/connect/config-entries/proxy-defaults) config entry which will apply to all proxies
|
||||
but you can also apply this config in the
|
||||
[proxy service registration](/docs/connect/registration/service-registration#proxy-parameters) (not supported on Kubernetes).
|
||||
|
||||
Within the config there are two keys you need to customize:
|
||||
|
||||
1. [`envoy_tracing_json`](/docs/connect/proxies/envoy#envoy_tracing_json): Sets the tracing configuration for your specific tracing type.
|
||||
See the [Envoy tracers documentation](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/trace/trace) for your
|
||||
specific collector's configuration. This configuration will reference the cluster name defined in `envoy_extra_static_clusters_json`.
|
||||
1. [`envoy_extra_static_clusters_json`](/docs/connect/proxies/envoy#envoy_extra_static_clusters_json): Defines the address
|
||||
of your tracing collector where Envoy will send its spans. In this example the URL was `collector-url:9411`.
|
||||
|
||||
## Applying the configuration
|
||||
|
||||
This configuration only applies when proxies are _restarted_ since it changes the _bootstrap_ config for Envoy
|
||||
which can only be applied on startup. This means you must restart all your proxies for changes to this
|
||||
config to take effect.
|
||||
|
||||
-> **Note:** On Kubernetes this is a matter of restarting your deployments, e.g. `kubectl rollout restart deploy/deploy-name`.
|
||||
|
||||
## Considerations
|
||||
|
||||
1. Distributed tracing is only supported for HTTP and gRPC services. You must specify the protocol either globally
|
||||
via a proxy defaults config entry:
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
```hcl
|
||||
Kind = "proxy-defaults"
|
||||
Name = "global"
|
||||
Config {
|
||||
protocol = "http"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: consul.hashicorp.com/v1alpha1
|
||||
kind: ProxyDefaults
|
||||
metadata:
|
||||
name: global
|
||||
spec:
|
||||
config:
|
||||
protocol: http
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "proxy-defaults",
|
||||
"Name": "global",
|
||||
"Config": {
|
||||
"protocol": "http"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
Or via a service defaults config entry for each service:
|
||||
|
||||
<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>
|
||||
|
||||
```hcl
|
||||
Kind = "service-defaults"
|
||||
Name = "service-name"
|
||||
Protocol = "http"
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: consul.hashicorp.com/v1alpha1
|
||||
kind: ProxyDefaults
|
||||
metadata:
|
||||
name: service-name
|
||||
spec:
|
||||
protocol: http
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "service-defaults",
|
||||
"Name": "service-name",
|
||||
"Protocol": "http"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
1. Requests through [Ingress Gateways](/docs/connect/gateways/ingress-gateway) will not be traced unless the header
|
||||
`x-client-trace-id: 1` is set (see [hashicorp/consul#6645](https://github.com/hashicorp/consul/issues/6645)).
|
||||
|
||||
1. Consul does not currently support interoperation with [OpenTelemetry](https://opentelemetry.io/) libraries due to
|
||||
Envoy not yet having support.
|
||||
|
||||
1. Tracing is only supported with Envoy proxies, not the built-in proxy.
|
|
@ -13,21 +13,23 @@ This topic provides an overview of the gateway features shipped with Consul. Gat
|
|||
- [Ingress gateways](#ingress-gateways) enable services to accept traffic from outside the Consul service mesh.
|
||||
- [Terminating gateways](#terminating-gateways) enable you to route traffic from services in the Consul service mesh to external services.
|
||||
|
||||
[![Gateway Architecture](/img/consul-connect/svgs/consul_gateway_overview_wide.svg)](/img/consul-connect/svgs/consul_gateway_overview_wide.svg)
|
||||
|
||||
## Mesh Gateways
|
||||
|
||||
-> **1.6.0+:** This feature is available in Consul versions 1.6.0 and newer.
|
||||
|
||||
Mesh gateways enable service mesh traffic to be routed between different Consul datacenters and admin partitions. The datacenters or partitions can reside
|
||||
in different clouds or runtime environments where general interconnectivity between all services in all datacenters
|
||||
isn't feasible.
|
||||
isn't feasible.
|
||||
|
||||
They operate by sniffing and extracting the server name indication (SNI) header from the service mesh session and routing the connection to the appropriate destination based on the server name requested. The gateway does not decrypt the data within the mTLS session.
|
||||
|
||||
Mesh gateways enable the following scenarios:
|
||||
Mesh gateways enable the following scenarios:
|
||||
|
||||
* **Federate multiple datacenters across a WAN**. Since Consul 1.8.0, mesh gateways can forward gossip and RPC traffic between Consul servers. See [WAN federation via mesh gateways](/docs/connect/gateways/wan-federation-via-mesh-gateways) for additional information.
|
||||
* **Service-to-service communication across datacenters**. Refer to [Enabling Service-to-service Traffic Accross Datacenters](/docs/connect/gateways/mesh-gateway/service-to-service-traffic-datacenters) for additional information.
|
||||
* **Service-to-service communication across admin partitions**. Since Consul 1.11.0, you can create administrative boundaries for single Consul deployements called "admin partitions". You can use mesh gateways to facilitate cross-partition communication. Refer to [Enabling Service-to-service Traffic Accross Admin Partitions](/docs/connect/gateways/mesh-gateway/service-to-service-traffic-partitions) for additional information.
|
||||
- **Federate multiple datacenters across a WAN**. Since Consul 1.8.0, mesh gateways can forward gossip and RPC traffic between Consul servers. See [WAN federation via mesh gateways](/docs/connect/gateways/wan-federation-via-mesh-gateways) for additional information.
|
||||
- **Service-to-service communication across datacenters**. Refer to [Enabling Service-to-service Traffic Across Datacenters](/docs/connect/gateways/mesh-gateway/service-to-service-traffic-datacenters) for additional information.
|
||||
- **Service-to-service communication across admin partitions**. Since Consul 1.11.0, you can create administrative boundaries for single Consul deployments called "admin partitions". You can use mesh gateways to facilitate cross-partition communication. Refer to [Enabling Service-to-service Traffic Across Admin Partitions](/docs/connect/gateways/mesh-gateway/service-to-service-traffic-partitions) for additional information.
|
||||
|
||||
-> **Mesh gateway tutorial**: Follow the [mesh gateway tutorial](https://learn.hashicorp.com/tutorials/consul/service-mesh-gateways) to learn concepts associated with mesh gateways.
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ description: >-
|
|||
|
||||
-> **Consul Enterprise 1.11.0+:** Admin partitions are supported in Consul Enterprise versions 1.11.0 and newer.
|
||||
|
||||
Mesh gateways enable you to route service mesh traffic between different Consul [admin partitions](/docs/enteprise/admin-partitions).
|
||||
Mesh gateways enable you to route service mesh traffic between different Consul [admin partitions](/docs/enterprise/admin-partitions).
|
||||
Partitions can reside in different clouds or runtime environments where general interconnectivity between all services
|
||||
in all partitions isn't feasible.
|
||||
|
||||
|
@ -20,7 +20,7 @@ Mesh gateways operate by sniffing and extracting the server name indication (SNI
|
|||
|
||||
Ensure that your Consul environment meets the following requirements.
|
||||
|
||||
### Consul
|
||||
### Consul
|
||||
|
||||
* Consul Enterprise version 1.11.0 or newer.
|
||||
* A local Consul agent is required to manage its configuration.
|
||||
|
@ -170,7 +170,7 @@ service:
|
|||
|
||||
### Enabling Gateways for a Proxy Upstream
|
||||
|
||||
The following service definition will enable gateways in `local` mode for three different partitions. Note that each service exists in the same namepace, but are separated by admin partition.
|
||||
The following service definition will enable gateways in `local` mode for three different partitions. Note that each service exists in the same namespace, but are separated by admin partition.
|
||||
|
||||
<CodeTabs heading="Example: Enabling gateways for a proxy upstream.">
|
||||
|
||||
|
@ -241,4 +241,4 @@ service:
|
|||
mesh_gateway:
|
||||
- mode: local
|
||||
```
|
||||
</CodeTabs>
|
||||
</CodeTabs>
|
||||
|
|
|
@ -49,26 +49,17 @@ target destination. After verifying the TLS client certificate, the cached
|
|||
intentions should be consulted for each incoming connection/request to
|
||||
determine if it should be accepted or rejected.
|
||||
|
||||
The default intention behavior is defined by the default [ACL
|
||||
policy](/docs/agent/options#acl_default_policy). If the default ACL policy is
|
||||
"allow all", then all Connect connections are allowed by default. If the
|
||||
default ACL policy is "deny all", then all Connect connections or requests are
|
||||
denied by default.
|
||||
The default intention behavior is defined by the [`default_policy`](/docs/agent/options#acl_default_policy) configuration.
|
||||
If the configuration is set `allow`, then all service mesh Connect connections will be allowed by default.
|
||||
If is set to `deny`, then all connections or requests will be denied by default.
|
||||
|
||||
## Intention Basics
|
||||
|
||||
Intentions are managed primarily via
|
||||
[`service-intentions`](/docs/connect/config-entries/service-intentions) config
|
||||
entries or the UI. Some simpler tasks can also be achieved with the older
|
||||
[API](/api-docs/connect/intentions) or [CLI](/commands/intention). Please see
|
||||
the respective documentation for each for full details on options, flags, etc.
|
||||
You can define a [`service-intentions`](/docs/connect/config-entries/service-intentions) configuration entry to create and manage intentions, as well as manage intentions through the Consul UI. You can also perform some intention-related tasks using the API and CLI commands. Refer to the [API](/api-docs/connect/intentions) and [CLI](/commands/intention) documentation for details.
|
||||
|
||||
Below is an example of a basic
|
||||
[`service-intentions`](/docs/connect/config-entries/service-intentions) config
|
||||
entry representing two simple intentions. The full data model complete with
|
||||
more examples can be found in the
|
||||
[`service-intentions`](/docs/connect/config-entries/service-intentions) config
|
||||
entry documentation.
|
||||
The following example shows a `service-intentions` configuration entry that specifies two intentions. Refer to the [`service-intentions`](/docs/connect/config-entries/service-intentions) documentation for the full data model and additional examples.
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
Kind = "service-intentions"
|
||||
|
@ -85,20 +76,41 @@ Sources = [
|
|||
]
|
||||
```
|
||||
|
||||
This config entry defines two intentions with a common destination of "db". The
|
||||
first intention above is a deny intention with a source of "web". This says
|
||||
```json
|
||||
{
|
||||
"Kind": "service-intentions",
|
||||
"Name": "db",
|
||||
"Sources": [
|
||||
{
|
||||
"Action": "deny",
|
||||
"Name": "web"
|
||||
},
|
||||
{
|
||||
"Action": "allow",
|
||||
"Name": "api"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
This configuration entry defines two intentions with a common destination of `db`. The
|
||||
first intention above is a deny intention with a source of `web`. This says
|
||||
that connections from web to db are not allowed and the connection will be
|
||||
rejected. The second intention is an allow intention with a source of "api".
|
||||
rejected. The second intention is an allow intention with a source of `api`.
|
||||
This says that connections from api to db are allowed and connections will be
|
||||
accepted.
|
||||
|
||||
### Wildcard Intentions
|
||||
|
||||
You can use the `*` wildcard when defining an intention source or destination. The wildcard matches _any_ value and can serve as a "catch-all" entry for intentions that should have a wide scope.
|
||||
You can use the `*` wildcard to match service names when defining an intention source or destination. The wildcard matches _any_ value, which enables you to set a wide initial scope when configuring intentions.
|
||||
|
||||
You can use a wildcard to match service names. If you are using Consul Enterprise, you can also use a wildcard to match a namespace.
|
||||
The wildcard is supported in Consul Enterprise `namespace` fields (see [Namespaces](/docs/enterprise/namespaces) for additional information), but it _is not supported_ in `partition` fields (see [Admin Partitions](/docs/enterprise/admin-partitions) for additional information).
|
||||
|
||||
This example says that the "web" service cannot connect to _any_ service:
|
||||
In the following example, the `web` service cannot connect to _any_ service:
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
Kind = "service-intentions"
|
||||
|
@ -111,7 +123,24 @@ Sources = [
|
|||
]
|
||||
```
|
||||
|
||||
And this example says that _no_ service can connect to the "db" service:
|
||||
```json
|
||||
{
|
||||
"Kind": "service-intentions",
|
||||
"Name": "*",
|
||||
"Sources": [
|
||||
{
|
||||
"Action": "deny",
|
||||
"Name": "web"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
The `db` service is configured to deny all connection in the following example:
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
Kind = "service-intentions"
|
||||
|
@ -124,8 +153,25 @@ Sources = [
|
|||
]
|
||||
```
|
||||
|
||||
<EnterpriseAlert inline /> This example grants Prometheus
|
||||
access to any service in any namespace.
|
||||
```json
|
||||
{
|
||||
"Kind": "service-intentions",
|
||||
"Name": "db",
|
||||
"Sources": [
|
||||
{
|
||||
"Action": "deny",
|
||||
"Name": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
<EnterpriseAlert inline /> This example grants Prometheus access to any service in
|
||||
any namespace.
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
Kind = "service-intentions"
|
||||
|
@ -140,6 +186,23 @@ Sources = [
|
|||
]
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Kind": "service-intentions",
|
||||
"Name": "*",
|
||||
"Namespace": "*",
|
||||
"Sources": [
|
||||
{
|
||||
"Action": "allow",
|
||||
"Name": "prometheus",
|
||||
"Namespace": "monitoring"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
### Enforcement
|
||||
|
||||
For services that define their [protocol] as TCP, intentions mediate the
|
||||
|
@ -177,7 +240,7 @@ top to bottom, with larger numbers being evaluated first.
|
|||
|
||||
The precedence value can be read from a
|
||||
[field](/docs/connect/config-entries/service-intentions#precedence) on the
|
||||
`service-intentions` config entry after it is modified. Precedence cannot be
|
||||
`service-intentions` configuration entry after it is modified. Precedence cannot be
|
||||
manually overridden today.
|
||||
|
||||
The numbers in the table above are not stable. Their ordering will remain
|
||||
|
@ -201,12 +264,30 @@ for its own service name in order to know whether or not to authorize
|
|||
connections. The following ACL policy will implicitly grant `intentions:read`
|
||||
(note _read_) for service `web`.
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```hcl
|
||||
service "web" {
|
||||
policy = "write"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"service": [
|
||||
{
|
||||
"web": [
|
||||
{
|
||||
"policy": "write"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
It is possible to explicitly specify intention permissions. For example,
|
||||
the following policy will allow a service to be discovered without granting
|
||||
access to read intentions for it.
|
||||
|
|
|
@ -33,7 +33,7 @@ Connect proxy upstreams are discovered using a series of stages: routing,
|
|||
splitting, and resolution. These stages represent different ways of managing L7
|
||||
traffic.
|
||||
|
||||
![diagram showing l7 traffic discovery stages: routing to splitting to resolution](/img/l7-traffic-stages.svg)
|
||||
![screenshot of L7 traffic visualization in the UI](/img/l7-routing/full.png)
|
||||
|
||||
Each stage of this discovery process can be dynamically reconfigured via various
|
||||
[configuration entries](/docs/agent/config-entries). When a configuration
|
||||
|
@ -44,6 +44,8 @@ entry is missing, that stage will fall back on reasonable default behavior.
|
|||
A [`service-router`](/docs/connect/config-entries/service-router) config
|
||||
entry kind is the first configurable stage.
|
||||
|
||||
![screenshot of service router in the UI](/img/l7-routing/Router.png)
|
||||
|
||||
A router config entry allows for a user to intercept traffic using L7 criteria
|
||||
such as path prefixes or http headers, and change behavior such as by sending
|
||||
traffic to a different service or service subset.
|
||||
|
@ -59,6 +61,8 @@ can be found in the `service-router` documentation.
|
|||
A [`service-splitter`](/docs/connect/config-entries/service-splitter) config
|
||||
entry kind is the next stage after routing.
|
||||
|
||||
![screenshot of service splitter in the UI](/img/l7-routing/Splitter.png)
|
||||
|
||||
A splitter config entry allows for a user to choose to split incoming requests
|
||||
across different subsets of a single service (like during staged canary
|
||||
rollouts), or perhaps across different services (like during a v2 rewrite or
|
||||
|
@ -84,6 +88,8 @@ can be found in the `service-splitter` documentation.
|
|||
A [`service-resolver`](/docs/connect/config-entries/service-resolver) config
|
||||
entry kind is the last stage.
|
||||
|
||||
![screenshot of service resolver in the UI](/img/l7-routing/Resolver.png)
|
||||
|
||||
A resolver config entry allows for a user to define which instances of a
|
||||
service should satisfy discovery requests for the provided name.
|
||||
|
||||
|
@ -118,4 +124,4 @@ can be found in the `service-resolver` documentation.
|
|||
|
||||
-> **Note:** `service-resolver` config entries kinds can function at L4 (unlike
|
||||
`service-router` and `service-splitter` kinds). These can be created for
|
||||
services of any protocol such as `tcp`.
|
||||
services of any protocol such as `tcp`.
|
|
@ -27,7 +27,7 @@ URLs](#configuring-dashboard-urls).
|
|||
It is possible to configure the UI to fetch basic metrics from your metrics
|
||||
provider storage to augment the visualization as displayed below.
|
||||
|
||||
![Consul UI Service Mesh Visualization](/img/ui-service-topology-view.png)
|
||||
![Consul UI Service Mesh Visualization](/img/ui-service-topology-view-hover.png)
|
||||
|
||||
Consul has built-in support for overlaying metrics from a
|
||||
[Prometheus](https://prometheus.io) backend. Alternative metrics providers may
|
||||
|
|
|
@ -55,7 +55,7 @@ $ curl http://<host-ip>:8500/v1/agent/connect/ca/roots
|
|||
After validating the client certificate from the caller, the proxy can authorize the entire connection (L4) or each request (L7). Depending upon the [protocol] of the proxied service, authorization is performed either on a per-connection (L4) or per-request (L7) basis. Authentication is based on "service identity" (TLS), and is implemented at the
|
||||
transport layer.
|
||||
|
||||
-> **Note:** Some features, such as (local) rate limiting or max connections, are expected to be proxy-level configurations enforced separately when authorization calls are made. Proxies can enforce the configurations based on information about request rates and other states that should already be availabe.
|
||||
-> **Note:** Some features, such as (local) rate limiting or max connections, are expected to be proxy-level configurations enforced separately when authorization calls are made. Proxies can enforce the configurations based on information about request rates and other states that should already be available.
|
||||
|
||||
The proxy can authorize the connection by either calling the [`/v1/agent/connect/authorize`](/api/agent/connect) API endpoint or by querying the [intention match API](/api/connect/intentions#list-matching-intentions) endpoint.
|
||||
|
||||
|
|
|
@ -97,14 +97,14 @@ Sidecar proxies are co-located with the single service instance they represent a
|
|||
Specify the following parameters in the `proxy` code block to configure a sidecar proxy in its own service registration:
|
||||
|
||||
* `destination_service_id`: String value that specifies the ID of the service being proxied. Refer to the [proxy parameters reference](#destination-service-id) for details.
|
||||
* `local_service_port`: Integer value that specifes the port that the proxy should use to connect to the _local_ service instance. Refer to the [proxy parameters reference](#local-service-port) for details.
|
||||
* `local_service_address`: String value that specifies the IP address or hostname that the proxy should use to connect to the _local_ service. Refer to the [proxy parameters reference](#local-service-address) for details.
|
||||
* `local_service_port`: Integer value that specifies the port that the proxy should use to connect to the _local_ service instance. Refer to the [proxy parameters reference](#local-service-port) for details.
|
||||
* `local_service_address`: String value that specifies the IP address or hostname that the proxy should use to connect to the _local_ service. Refer to the [proxy parameters reference](#local-service-address) for details.
|
||||
|
||||
See (Sidecar Service Registration)[/docs/connect/registration/sidecar-service] for additional information about configuring service mesh proxies as sidecars.
|
||||
|
||||
### Complete Configuration Example
|
||||
|
||||
The following example includes values for all availble options when registering a proxy instance.
|
||||
The following example includes values for all available options when registering a proxy instance.
|
||||
|
||||
<CodeTabs heading="Example that includes all configuration options when registering a proxy instance">
|
||||
<CodeBlockConfig>
|
||||
|
@ -160,7 +160,7 @@ The following table describes all parameters that can be defined in the `proxy`
|
|||
| Parameter | Description | Required | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `destination_service_id` <a name="destination-service-id"/>| String value that specifies the ID of a single service instance represented by the proxy. <br/>This parameter is only applicable for sidecar proxies that run on the same node as the service. <br/>Consul checks for the proxied service on the same agent. <br/>The ID is unique and may differ from its `name` value. <br/>Specifying this parameter helps tools identify which sidecar proxy instances are associated with which application instance, as well as enable fine-grained analysis of the metrics coming from the proxy.| Required when registering proxy as a sidecar | None |
|
||||
| `local_service_port` <a name="local-service-port"/>| Integer value that specifes the port that a sidecar proxy should use to connect to the _local_ service instance. | Required when registering proxy as a sidecar | Port advertised by the service instance configured in `destination_service_id` |
|
||||
| `local_service_port` <a name="local-service-port"/>| Integer value that specifies the port that a sidecar proxy should use to connect to the _local_ service instance. | Required when registering proxy as a sidecar | Port advertised by the service instance configured in `destination_service_id` |
|
||||
| `local_service_address` <a name="local-service-address"/>| String value that specifies the IP address or hostname that a sidecar proxy should use to connect to the _local_ service. | Optional | `127.0.0.1` |
|
||||
| `destination_service_name` | String value that specifies the _name_ of the service the instance is proxying. The name is used during service discovery to route to the correct proxy instances for a given service name. | Required | None |
|
||||
| `local_service_socket_path` | String value that specifies the path of a Unix domain socket for connecting to the local application instance. <br/>This parameter value is created by the application and conflicts with `local_service_address` and `local_service_port`. <br/>Supported when using Envoy for the proxy. | Optional | None |
|
||||
|
@ -175,23 +175,23 @@ The following table describes all parameters that can be defined in the `proxy`
|
|||
|
||||
You can configure the service mesh proxy to create listeners for upstream services. The listeners enable the upstream service to accept requests. You can specify the following parameters to configure upstream service listeners.
|
||||
|
||||
| Parameter | Description | Required | Defautl |
|
||||
| Parameter | Description | Required | Default |
|
||||
| --- | --- | --- | --- |
|
||||
|`destination_name` | String value that specifies the name of the service or prepared query to route the service mesh to. The prepared query should be the name or the ID of the prepared query. | Required | None |
|
||||
| `destination_namespace` | String value that specifies the namespace containing the upstream service. <EnterpriseAlert inline /> | Optional | `default` |
|
||||
| `destination_partition` | String value that specifies the name of the admin partition containing the upstream service. | Optional | `default` |
|
||||
| `local_bind_port` | Integer value that specifies the port to bind a local listener to. The application will make outbound connections to the upstream from the local port. | Required | None |
|
||||
| `local_bind_address` | String value that specifies the address to bind a local listener to. The application will make outbound connecttions to the upstream service from the local bind address. | Optional | `127.0.0.1` |
|
||||
| `local_bind_address` | String value that specifies the address to bind a local listener to. The application will make outbound connections to the upstream service from the local bind address. | Optional | `127.0.0.1` |
|
||||
| `local_bind_socket_path` | String value that specifies the path at which to bind a Unix domain socket listener. The application will make outbound connections to the upstream from the local bind socket path. <br/>This parameter conflicts with the `local_bind_port` or `local_bind_address` parameters. <br/>Supported when using Envoy as a proxy. | Optional | None|
|
||||
| `local_bind_socket_mode` | String value that specifies a Unix octal that configures file permissions for the socket. | Optional | None |
|
||||
| `destination_type` | String value that specifies the type of discovery query the proxy should use for finding service mesh instances. The following values are supported: <li>`service`: Queries for upstream `service` types. </li><li> `prepared_query`: Queries for upstream prepared queries.</li> | Optional | `service` |
|
||||
| `datacenter` | String value that specifies the datacenter to issue the discovery query to. | Optional | Defaults to the local datacenter. |
|
||||
| `config` | Object value that specifies opaque configuration options that will be provided to the proxy instance for the upstream. <br/>Valid JSON objects are also suppported. <br/>The `config` parameter can specify timeouts, retries, and other proxy-specific features for the given upstream. <br/>See the [built-in proxy configuration reference](/docs/connect/proxies/built-in#proxy-upstream-config-key-reference) for configuration options when using the built-in proxy. <br/>If using Envoy as a proxy, see [Envoy configuration reference](/docs/connect/proxies/envoy#proxy-upstream-config-options) | Optional | None |
|
||||
| `config` | Object value that specifies opaque configuration options that will be provided to the proxy instance for the upstream. <br/>Valid JSON objects are also supported. <br/>The `config` parameter can specify timeouts, retries, and other proxy-specific features for the given upstream. <br/>See the [built-in proxy configuration reference](/docs/connect/proxies/built-in#proxy-upstream-config-key-reference) for configuration options when using the built-in proxy. <br/>If using Envoy as a proxy, see [Envoy configuration reference](/docs/connect/proxies/envoy#proxy-upstream-config-options) | Optional | None |
|
||||
| `mesh_gateway` | Object that defines the mesh gateway configuration for the proxy. Refer to the [Mesh Gateway Configuration Reference](#mesh-gateway-configuration-reference) for configuration details. | Optional | None |
|
||||
|
||||
### Upstream Configuration Examples
|
||||
|
||||
Upstreams support multiple destination types. The following examples include information about each implmentation.
|
||||
Upstreams support multiple destination types. The following examples include information about each implementation.
|
||||
|
||||
-> **Snake case**: The examples in this topic use `snake_case` because the syntax is supported in configuration files and API registrations. See [Service Definition Parameter Case](/docs/agent/services#service-definition-parameter-case) for additional information.
|
||||
|
||||
|
|
|
@ -142,6 +142,19 @@ There are several different kinds of checks:
|
|||
|
||||
A script check:
|
||||
|
||||
<CodeTabs heading="Script Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "mem-util"
|
||||
name = "Memory utilization"
|
||||
args = ["/usr/local/bin/check_mem.py", "-limit", "256MB"]
|
||||
interval = "10s"
|
||||
timeout = "1s"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -154,8 +167,29 @@ A script check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A HTTP check:
|
||||
|
||||
<CodeTabs heading="HTTP Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "api"
|
||||
name = "HTTP API on port 5000"
|
||||
http = "https://localhost:5000/health"
|
||||
tls_server_name = ""
|
||||
tls_skip_verify = false
|
||||
method = "POST"
|
||||
header = {
|
||||
Content-Type = ["application/json"]
|
||||
}
|
||||
body = "{\"method\":\"health\"}"
|
||||
interval = "10s"
|
||||
timeout = "1s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -173,8 +207,23 @@ A HTTP check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A TCP check:
|
||||
|
||||
<CodeTabs heading="TCP Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "ssh"
|
||||
name = "SSH TCP on port 22"
|
||||
tcp = "localhost:22"
|
||||
interval = "10s"
|
||||
timeout = "1s"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -187,8 +236,21 @@ A TCP check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A TTL check:
|
||||
|
||||
<CodeTabs heading="TTL Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "web-app"
|
||||
name = "Web App Status"
|
||||
notes = "Web app does a curl internally every 10 seconds"
|
||||
ttl = "30s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -200,8 +262,23 @@ A TTL check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A Docker check:
|
||||
|
||||
<CodeTabs heading="Docker Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "mem-util"
|
||||
name = "Memory utilization"
|
||||
docker_container_id = "f972c95ebf0e"
|
||||
shell = "/bin/bash"
|
||||
args = ["/usr/local/bin/check_mem.py"]
|
||||
interval = "10s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -215,8 +292,22 @@ A Docker check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A gRPC check for the whole application:
|
||||
|
||||
<CodeTabs heading="gRPC Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "mem-util"
|
||||
name = "Service health status"
|
||||
grpc = "127.0.0.1:12345"
|
||||
grpc_use_tls = true
|
||||
interval = "10s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -229,8 +320,22 @@ A gRPC check for the whole application:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A gRPC check for the specific `my_service` service:
|
||||
|
||||
<CodeTabs heading="gRPC Specific Service Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "mem-util"
|
||||
name = "Service health status"
|
||||
grpc = "127.0.0.1:12345/my_service"
|
||||
grpc_use_tls = true
|
||||
interval = "10s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -243,8 +348,22 @@ A gRPC check for the specific `my_service` service:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
A h2ping check:
|
||||
|
||||
<CodeTabs heading="H2ping Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "h2ping-check"
|
||||
name = "h2ping"
|
||||
h2ping = "localhost:22222"
|
||||
interval = "10s"
|
||||
h2ping_use_tls = false
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -257,8 +376,19 @@ A h2ping check:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
An alias check for a local service:
|
||||
|
||||
<CodeTabs heading="Alias Check">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "web-alias"
|
||||
alias_service = "web"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -268,6 +398,8 @@ An alias check for a local service:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
~> Configuration info: The alias check configuration expects the alias to be
|
||||
registered on the same agent as the one you are aliasing. If the service is
|
||||
not registered with the same agent, `"alias_node": "<node_id>"` must also be
|
||||
|
@ -342,6 +474,17 @@ to be healthy. In certain cases, it may be desirable to specify the initial
|
|||
state of a health check. This can be done by specifying the `status` field in a
|
||||
health check definition, like so:
|
||||
|
||||
<CodeTabs heading="Status Field Example">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
"id": "mem",
|
||||
"args": ["/bin/check_mem", "-limit", "256MB"]
|
||||
"interval": "10s"
|
||||
"status": "passing"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -353,6 +496,8 @@ health check definition, like so:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
The above service definition would cause the new "mem" check to be
|
||||
registered with its initial state set to "passing".
|
||||
|
||||
|
@ -363,6 +508,17 @@ that the status of the health check will only affect the health status of the
|
|||
given service instead of the entire node. Service-bound health checks may be
|
||||
provided by adding a `service_id` field to a check configuration:
|
||||
|
||||
<CodeTabs heading="Status Field Example">
|
||||
|
||||
```hcl
|
||||
check = {
|
||||
id = "web-app"
|
||||
name = "Web App Status"
|
||||
service_id = "web-app"
|
||||
ttl = "30s"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"check": {
|
||||
|
@ -374,6 +530,8 @@ provided by adding a `service_id` field to a check configuration:
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
In the above configuration, if the web-app health check begins failing, it will
|
||||
only affect the availability of the web-app service. All other services
|
||||
provided by the node will remain unchanged.
|
||||
|
@ -389,6 +547,32 @@ to use the agent's credentials when configured for TLS.
|
|||
Multiple check definitions can be defined using the `checks` (plural)
|
||||
key in your configuration file.
|
||||
|
||||
<CodeTabs heading="Multiple Checks Example">
|
||||
|
||||
```hcl
|
||||
checks = [
|
||||
{
|
||||
id = "chk1"
|
||||
name = "mem"
|
||||
args = ["/bin/check_mem", "-limit", "256MB"]
|
||||
interval = "5s"
|
||||
},
|
||||
{
|
||||
id = "chk2"
|
||||
name = "/health"
|
||||
http = "http://localhost:5000/health"
|
||||
interval = "15s"
|
||||
},
|
||||
{
|
||||
id = "chk3"
|
||||
name = "cpu"
|
||||
args = ["/bin/check_cpu"]
|
||||
interval = "10s"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"checks": [
|
||||
|
@ -415,6 +599,8 @@ key in your configuration file.
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
## Success/Failures before passing/warning/critical
|
||||
|
||||
To prevent flapping health checks, and limit the load they cause on the cluster,
|
||||
|
@ -427,7 +613,7 @@ The status will not transition states until the configured threshold is reached.
|
|||
- `failures_before_warning` - Number of consecutive unsuccessful results required
|
||||
before check status transitions to warning. Defaults to the same value as that of
|
||||
`failures_before_critical` to maintain the expected behavior of not changing the
|
||||
status of serivce checks to `warning` before `critical` unless configured to do so.
|
||||
status of service checks to `warning` before `critical` unless configured to do so.
|
||||
Values higher than `failures_before_critical` are invalid. Added in Consul 1.11.0.
|
||||
- `failures_before_critical` - Number of consecutive unsuccessful results required
|
||||
before check status transitions to critical. Defaults to `0`. Added in Consul 1.7.0.
|
||||
|
@ -436,6 +622,22 @@ This feature is available for HTTP, TCP, gRPC, Docker & Monitor checks.
|
|||
By default, both passing and critical thresholds will be set to 0 so the check
|
||||
status will always reflect the last check result.
|
||||
|
||||
<CodeTabs heading="Flapping Prevention Example">
|
||||
|
||||
```hcl
|
||||
checks = [
|
||||
{
|
||||
name = "HTTP TCP on port 80"
|
||||
tcp = "localhost:80"
|
||||
interval = "10s"
|
||||
timeout = "1s"
|
||||
success_before_passing = 3
|
||||
failures_before_warning = 1
|
||||
failures_before_critical = 3
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"checks": [
|
||||
|
@ -451,3 +653,5 @@ status will always reflect the last check result.
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
|
|
@ -32,7 +32,91 @@ Send a `SIGHUP` to the running agent or use [`consul reload`](/commands/reload)
|
|||
update existing services. Alternatively, the service can be [registered dynamically](/api-docs/agent/service#register-service)
|
||||
using the [HTTP API](/api).
|
||||
|
||||
A service definition contains a set of parameters that specify various aspects of the service, including how it is discovered by other services in the network. All possible parameters are included in the following example, but only the top-level `service` parameter and its `name` parameter child are required by default.
|
||||
A service definition contains a set of parameters that specify various aspects of the service, including how it is discovered by other services in the network.
|
||||
All possible parameters are included in the following example, but only the top-level `service` parameter and its `name` parameter child are required by default.
|
||||
|
||||
<CodeTabs heading="Service Definition">
|
||||
|
||||
```hcl
|
||||
service {
|
||||
name = "redis"
|
||||
id = "redis"
|
||||
port = 80
|
||||
tags = ["primary"]
|
||||
|
||||
meta = {
|
||||
custom_meta_key = "custom_meta_value"
|
||||
}
|
||||
|
||||
tagged_addresses = {
|
||||
lan = {
|
||||
address = "192.168.0.55"
|
||||
port = 8000
|
||||
}
|
||||
|
||||
wan = {
|
||||
address = "198.18.0.23"
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
|
||||
port = 8000
|
||||
socket_path = "/tmp/redis.sock"
|
||||
enable_tag_override = false
|
||||
|
||||
checks = [
|
||||
{
|
||||
args = ["/usr/local/bin/check_redis.py"]
|
||||
interval = "10s"
|
||||
}
|
||||
]
|
||||
|
||||
kind = "connect-proxy"
|
||||
proxy_destination = "redis"
|
||||
|
||||
proxy = {
|
||||
destination_service_name = "redis"
|
||||
destination_service_id = "redis1"
|
||||
local_service_address = "127.0.0.1"
|
||||
local_service_port = 9090
|
||||
local_service_socket_path = "/tmp/redis.sock"
|
||||
mode = "transparent"
|
||||
|
||||
transparent_proxy {
|
||||
outbound_listener_port = 22500
|
||||
}
|
||||
|
||||
mesh_gateway = {
|
||||
mode = "local"
|
||||
}
|
||||
|
||||
expose = {
|
||||
checks = true
|
||||
|
||||
paths = [
|
||||
{
|
||||
path = "/healthz"
|
||||
local_path_port = 8080
|
||||
listener_port = 21500
|
||||
protocol = "http2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
connect = {
|
||||
native = false
|
||||
}
|
||||
|
||||
weights = {
|
||||
passing = 5
|
||||
warning = 1
|
||||
}
|
||||
|
||||
token = "233b604b-b92e-48c8-a253-5f11514e4b50"
|
||||
namespace = "foo"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -110,6 +194,8 @@ A service definition contains a set of parameters that specify various aspects o
|
|||
}
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
The following table describes the available parameters for service definitions.
|
||||
|
||||
### `service`
|
||||
|
@ -504,6 +590,51 @@ Multiple services definitions can be provided at once when registering services
|
|||
via the agent configuration by using the plural `services` key (registering
|
||||
multiple services in this manner is not supported using the HTTP API).
|
||||
|
||||
<CodeTabs heading="Multiple Service Definitions">
|
||||
|
||||
<CodeBlockConfig filename="redis-services.hcl">
|
||||
|
||||
```hcl
|
||||
services {
|
||||
id = "red0"
|
||||
name = "redis"
|
||||
tags = [
|
||||
"primary"
|
||||
]
|
||||
address = ""
|
||||
port = 6000
|
||||
checks = [
|
||||
{
|
||||
args = ["/bin/check_redis", "-p", "6000"]
|
||||
interval = "5s"
|
||||
timeout = "20s"
|
||||
}
|
||||
]
|
||||
}
|
||||
services {
|
||||
id = "red1"
|
||||
name = "redis"
|
||||
tags = [
|
||||
"delayed",
|
||||
"secondary"
|
||||
]
|
||||
address = ""
|
||||
port = 7000
|
||||
checks = [
|
||||
{
|
||||
args = ["/bin/check_redis", "-p", "7000"]
|
||||
interval = "30s"
|
||||
timeout = "60s"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
<CodeBlockConfig filename="redis-services.json">
|
||||
|
||||
```json
|
||||
{
|
||||
"services": [
|
||||
|
@ -545,43 +676,8 @@ multiple services in this manner is not supported using the HTTP API).
|
|||
}
|
||||
```
|
||||
|
||||
In HCL you can specify the plural `services` key (although not `service`) multiple times:
|
||||
|
||||
```hcl
|
||||
services {
|
||||
id = "red0"
|
||||
name = "redis"
|
||||
tags = [
|
||||
"primary"
|
||||
]
|
||||
address = ""
|
||||
port = 6000
|
||||
checks = [
|
||||
{
|
||||
args = ["/bin/check_redis", "-p", "6000"]
|
||||
interval = "5s"
|
||||
timeout = "20s"
|
||||
}
|
||||
]
|
||||
}
|
||||
services {
|
||||
id = "red1"
|
||||
name = "redis"
|
||||
tags = [
|
||||
"delayed",
|
||||
"secondary"
|
||||
]
|
||||
address = ""
|
||||
port = 7000
|
||||
checks = [
|
||||
{
|
||||
args = ["/bin/check_redis", "-p", "7000"]
|
||||
interval = "30s"
|
||||
timeout = "60s"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeBlockConfig>
|
||||
</CodeTabs>
|
||||
|
||||
## Service and Tag Names with DNS
|
||||
|
||||
|
|
|
@ -15,10 +15,15 @@ From this page you can download various tools for Consul. These tools are mainta
|
|||
These Consul tools are created and managed by the dedicated engineers at HashiCorp:
|
||||
|
||||
- [Envconsul](https://github.com/hashicorp/envconsul) - Read and set environmental variables for processes from Consul.
|
||||
- [Consul API Gateway](https://github.com/hashicorp/consul-api-gateway/) - dedicated ingress solution for intelligently routing traffic to applications running on a Consul Service Mesh.
|
||||
- [Consul ESM](https://github.com/hashicorp/consul-esm) - Provides external service monitoring for Consul. A tutorial is available on [HashiCorp Learn](https://learn.hashicorp.com/tutorials/consul/service-registration-external-services).
|
||||
- [Consul Migrate](https://github.com/hashicorp/consul-migrate) - Data migration tool to handle Consul upgrades to 0.5.1+
|
||||
- [Consul Replicate](https://github.com/hashicorp/consul-replicate) - Consul cross-DC KV replication daemon.
|
||||
- [Consul Template](https://github.com/hashicorp/consul-template) - Generic template rendering and notifications with Consul. A step by step tutorial is available on [HashiCorp Learn](https://learn.hashicorp.com/tutorials/consul/consul-template).
|
||||
- [Consul-Terraform Sync](https://github.com/hashicorp/consul-terraform-sync) -
|
||||
enables dynamic updates to network infrastructure devices triggered by service
|
||||
changes. A tutorial is available on [HashiCorp
|
||||
Learn](https://learn.hashicorp.com/collections/consul/network-infrastructure-automation?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS)
|
||||
|
||||
## Community Tools
|
||||
|
||||
|
|
|
@ -25,6 +25,6 @@ There are several ways to get started with Consul with ECS.
|
|||
* The [Serverless Consul Service Mesh with ECS and HCP](https://learn.hashicorp.com/tutorials/cloud/consul-ecs-hcp?in=consul/cloud-integrations) learn guide shows how to use Terraform to run Consul service mesh applications on ECS with managed Consul servers running in HashiCorp Cloud Platform (HCP).
|
||||
* The [Service Mesh with ECS and Consul on EC2](https://learn.hashicorp.com/tutorials/consul/consul-ecs-ec2?in=consul/cloud-integrations) learn guide shows how to use Terraform to run Consul service mesh applications on ECS with Consul servers running on EC2 instances.
|
||||
* The [Consul with Dev Server on Fargate](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-fargate) example installation deploys a sample application in ECS using the Fargate launch type.
|
||||
* The [Consul with Dev Server on EC2](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-ec2) example installation deploys a sample applciation in ECS using the EC2 launch type.
|
||||
* The [Consul with Dev Server on EC2](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-ec2) example installation deploys a sample application in ECS using the EC2 launch type.
|
||||
|
||||
See the [Requirements](/docs/ecs/get-started/requirements) and the full [Install Guide](/docs/ecs/get-started/install) when you're ready to install Consul on an existing ECS cluster and add existing tasks to the service mesh.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
layout: docs
|
||||
page_title: Consul Enterprise Admin Partitions
|
||||
description: Consul Enterprise enables you to create paritions that can be administrated across namespaces.
|
||||
description: Consul Enterprise enables you to create partitions that can be administrated across namespaces.
|
||||
---
|
||||
|
||||
# Consul Enterprise Admin Partitions
|
||||
|
|
|
@ -112,7 +112,7 @@ Consul’s built-in UI has a topology visualization for services part of the Con
|
|||
|
||||
The diagram below illustrates how the UI displays service metrics for a sample application:
|
||||
|
||||
[![UI Topology View](/img/ui-topology.png)](/img/ui-topology.png)
|
||||
![UI Topology View](/img/ui-service-topology-view-hover.png)
|
||||
|
||||
The topology view is configured under `ui.metrics`. This will enable the Consul UI to query the provider specified by
|
||||
`ui.metrics.provider` at the URL of the Prometheus server `ui.metrics.baseURL` to display sidecar proxy metrics for the
|
||||
|
|
|
@ -441,7 +441,7 @@ to retrieve the bootstrap token mentioned in the UI documentation.
|
|||
With the UI open, you'll be able to switch between datacenters via the dropdown
|
||||
in the top left:
|
||||
|
||||
![Consul Datacenter Dropdown](/img/consul-datacenter-dropdown.png 'Consul Datacenter Dropdown')
|
||||
![Consul Datacenter Dropdown](/img/data-center-dropdown.png 'Consul Datacenter Dropdown')
|
||||
|
||||
## Next Steps
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ Prior to creating Vault auth roles for the Consul servers and clients, ensure th
|
|||
$ vault auth enable kubernetes
|
||||
```
|
||||
|
||||
After enabling the Kubernetes auth method, in Vault, ensure that you have configured the Kubernetes Auth method properly as described in [Kubernetes Auth Method Configuration](https://www.vaultproject.io/docs/auth/kubernetes#configuration). The command should look simliar to the following with a custom `kubernetes_host` config provided from the information provided via `kubectl cluster-info`.
|
||||
After enabling the Kubernetes auth method, in Vault, ensure that you have configured the Kubernetes Auth method properly as described in [Kubernetes Auth Method Configuration](https://www.vaultproject.io/docs/auth/kubernetes#configuration). The command should look similar to the following with a custom `kubernetes_host` config provided from the information provided via `kubectl cluster-info`.
|
||||
|
||||
```shell-session
|
||||
$ vault write auth/kubernetes/config \
|
||||
|
@ -52,7 +52,7 @@ $ vault write auth/kubernetes/config \
|
|||
|
||||
### Vault KV Secrets Engine - Version 2
|
||||
|
||||
In order to utlize Vault as a secrets backend, we must enable thne [Vault KV secrets engine - Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2).
|
||||
In order to utilize Vault as a secrets backend, we must enable the [Vault KV secrets engine - Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2).
|
||||
|
||||
```shell-session
|
||||
$ vault secrets enable -path=consul kv-v2
|
||||
|
|
|
@ -9,7 +9,7 @@ description: >-
|
|||
|
||||
To use Vault to issue Server TLS certificates the following will be needed:
|
||||
|
||||
1. Bootstrap the Vault PKI engine and boostrap it with any configuration required for your infrastructure.
|
||||
1. Bootstrap the Vault PKI engine and bootstrap it with any configuration required for your infrastructure.
|
||||
1. Create Vault Policies that will allow the Consul server to access the certificate issuing url.
|
||||
1. Create Vault Policies that will allow the Consul components, e.g. ingress gateways, controller, to access the CA url.
|
||||
1. Create Kubernetes auth roles that link these policies to the Kubernetes service accounts of the Consul components.
|
||||
|
@ -130,7 +130,7 @@ vault write auth/kubernetes/role/consul-ca \
|
|||
```
|
||||
|
||||
The above Vault Roles will now be your Helm values for `global.secretsBackend.vault.consulServerRole` and
|
||||
`global.secretsBAckend.vault.consulCARole` respectively.
|
||||
`global.secretsBackend.vault.consulCARole` respectively.
|
||||
|
||||
|
||||
## Deploying the Consul Helm chart
|
||||
|
|
|
@ -88,9 +88,9 @@ The following options are available.
|
|||
| `-config-file` | String value that specifies the path to a file containing custom installation configurations, e.g., Consul Helm chart values file. <br/> You can use the `-config-file` flag multiple times to specify multiple files. | none | Optional |
|
||||
| `-namespace` | String value that specifies the namespace of the Consul installation. | `consul` | Optional |
|
||||
| `-preset` | String value that installs Consul based on a preset configuration. You can specify the following values: <br/> `demo`: Installs a single replica server with sidecar injection enabled; useful for testing service mesh functionality. <br/> `secure`: Installs a single replica server with sidecar injection, ACLs, and TLS enabled; useful for testing service mesh functionality. | Configuration of the Consul Helm chart. | Optional |
|
||||
| `-set` | String value that enables you to set a customizeable value. This flag is comparable to the `helm install --set` flag. <br/> You can use the `-set` flag multiple times to set multiple values. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-file` | String value that specifies the name of an arbitrary config file. This flag is comparable to the `helm install --set-file` <br/> flag. The contents of the file will be used to set a customizeable value. You can use the `-set-file` flag multiple times to specify multiple files. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-string` | String value that enables you to set a customizeable string value. This flag is comparable to the `helm install --set-string` <br/> flag. You can use the `-set-string` flag multiple times to specify multiple strings. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set` | String value that enables you to set a customizable value. This flag is comparable to the `helm install --set` flag. <br/> You can use the `-set` flag multiple times to set multiple values. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-file` | String value that specifies the name of an arbitrary config file. This flag is comparable to the `helm install --set-file` <br/> flag. The contents of the file will be used to set a customizable value. You can use the `-set-file` flag multiple times to specify multiple files. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-string` | String value that enables you to set a customizable string value. This flag is comparable to the `helm install --set-string` <br/> flag. You can use the `-set-string` flag multiple times to specify multiple strings. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-timeout` | Specifies how long to wait for the installation process to complete before timing out. The value is specified with an integer and string value indicating a unit of time. <br/> The following units are supported: <br/> `ms` (milliseconds)<br/>`s` (seconds)<br/>`m` (minutes) <br/>In the following example, installation will timeout after one minute:<br/> `consul-k8s install -timeout 1m` | `10m` | Optional |
|
||||
| `-wait` | Boolean value that determines if Consul should wait for resources in the installation to be ready before exiting the command. | `true` | Optional |
|
||||
| `-verbose`, `-v` | Boolean value that specifies whether to output verbose logs from the install command with the status of resources being installed. | `false` | Optional |
|
||||
|
@ -133,7 +133,7 @@ The following options are available.
|
|||
| `-name` | String value for the name of the installation to remove. | none | Optional |
|
||||
| `-namespace` | String value that specifies the namespace of the Consul installation to remove. | `consul` | Optional |
|
||||
| `-timeout` | Specifies how long to wait for the removal process to complete before timing out. The value is specified with an integer and string value indicating a unit of time. <br/> The following units are supported: <br/> `ms` (milliseconds)<br/>`s` (seconds)<br/>`m` (minutes) <br/>`h` (hours) <br/>In the following example, removal will timeout after one minute:<br/> `consul-k8s uninstall -timeout 1m` | `10m` | Optional |
|
||||
| `-wipe-data` | Boolan value that deletes PVCs and secrets associated with the Consul installation during installation. <br/> Data will be removed without a verification prompt if the `-auto-approve` flag is set to `true`. | `false` <br/> Instructions for removing data will be printed to the console. | Optional |
|
||||
| `-wipe-data` | Boolean value that deletes PVCs and secrets associated with the Consul installation during installation. <br/> Data will be removed without a verification prompt if the `-auto-approve` flag is set to `true`. | `false` <br/> Instructions for removing data will be printed to the console. | Optional |
|
||||
| `--help` | Prints usage information for this option. | none | Optional |
|
||||
|
||||
See [Global Options](#global-options) for additional commands that you can use when uninstalling Consul from Kubernetes.
|
||||
|
@ -210,11 +210,11 @@ The following options are available.
|
|||
| `-config-file` | String value that specifies the path to a file containing custom upgrade configurations, e.g., Consul Helm chart values file. <br/> You can use the `-config-file` flag multiple times to specify multiple files. | none | Optional |
|
||||
| `-namespace` | String value that specifies the namespace of the Consul installation. | `consul` | Optional |
|
||||
| `-preset` | String value that upgrades Consul based on a preset configuration. | Configuration of the Consul Helm chart. | Optional |
|
||||
| `-set` | String value that enables you to set a customizeable value. This flag is comparable to the `helm upgrade --set` flag. <br/> You can use the `-set` flag multiple times to set multiple values. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-file` | String value that specifies the name of an arbitrary config file. This flag is comparable to the `helm upgrade --set-file` <br/> flag. The contents of the file will be used to set a customizeable value. You can use the `-set-file` flag multiple times to specify multiple files. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-string` | String value that enables you to set a customizeable string value. This flag is comparable to the `helm upgrade --set-string` <br/> flag. You can use the `-set-string` flag multiple times to specify multiple strings. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set` | String value that enables you to set a customizable value. This flag is comparable to the `helm upgrade --set` flag. <br/> You can use the `-set` flag multiple times to set multiple values. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-file` | String value that specifies the name of an arbitrary config file. This flag is comparable to the `helm upgrade --set-file` <br/> flag. The contents of the file will be used to set a customizable value. You can use the `-set-file` flag multiple times to specify multiple files. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-set-string` | String value that enables you to set a customizable string value. This flag is comparable to the `helm upgrade --set-string` <br/> flag. You can use the `-set-string` flag multiple times to specify multiple strings. <br/> Consul Helm chart values are supported. | none | Optional |
|
||||
| `-timeout` | Specifies how long to wait for the upgrade process to complete before timing out. The value is specified with an integer and string value indicating a unit of time. <br/> The following units are supported: <br/> `ms` (milliseconds)<br/>`s` (seconds)<br/>`m` (minutes) <br/>In the following example, the upgrade will timeout after one minute:<br/> `consul-k8s upgrade -timeout 1m` | `10m` | Optional |
|
||||
| `-wait` | Boolean value that determines if Consul should wait for resources in the upgtrade to be ready before exiting the command. | `true` | Optional |
|
||||
| `-wait` | Boolean value that determines if Consul should wait for resources in the upgrade to be ready before exiting the command. | `true` | Optional |
|
||||
| `-verbose`, `-v` | Boolean value that specifies whether to output verbose logs from the upgrade command with the status of resources being upgraded. | `false` | Optional |
|
||||
| `--help` | Prints usage information for this option. | none | Optional |
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ You can uninstall Consul using Helm commands or the Consul K8s CLI.
|
|||
|
||||
## Consul K8s CLI
|
||||
|
||||
Issue the `consul-k8s uninstall` command to remove Consul on Kubernetes. You can specify the installation name, namespace, and data retention behavior using the applicable options. By default, the uninstallation preserves the secrets and PVCs that are provisioned by Consul on Kubernetes.
|
||||
Issue the `consul-k8s uninstall` command to remove Consul on Kubernetes. You can specify the installation name, namespace, and data retention behavior using the applicable options. By default, the uninstall preserves the secrets and PVCs that are provisioned by Consul on Kubernetes.
|
||||
|
||||
```shell-session
|
||||
$ consul-k8s uninstall <OPTIONS>
|
||||
|
@ -29,12 +29,18 @@ Refer to the [Consul K8s CLI reference](/docs/k8s/k8s-cli#uninstall) topic for d
|
|||
|
||||
Run the `helm uninstall` **and** manually remove resources that Helm does not delete.
|
||||
|
||||
1. Although the Helm chart automates the deletion of CRDs upon the uninstallation of the Helm chart, sometimes the finalizers tied to those CRDs may not complete because the deletion of the CRDs rely on the Consul K8s controller running. Ensure that previously created CRDs for Consul on Kubernetes are deleted, so subsequent installs of Consul on Kubernetes on the same Kubernetes cluster do not get blocked.
|
||||
|
||||
```shell-session
|
||||
$ kubectl delete crd --selector app=consul
|
||||
```
|
||||
|
||||
1. (Optional) If Consul is installed in a dedicated namespace, set the kubeConfig context to the `consul` namespace. Otherwise, subsequent commands will need to include `-n consul`.
|
||||
|
||||
```
|
||||
kubectl config set-context --current --namespace=consul
|
||||
```
|
||||
|
||||
|
||||
1. Run the `helm uninstall <release-name>` command and specify the release name you've installed Consul with, e.g.,:
|
||||
|
||||
|
@ -42,6 +48,7 @@ Run the `helm uninstall` **and** manually remove resources that Helm does not de
|
|||
$ helm uninstall consul
|
||||
release "consul" uninstalled
|
||||
```
|
||||
|
||||
|
||||
1. After deleting the Helm release, you need to delete the `PersistentVolumeClaim`'s
|
||||
for the persistent volumes that store Consul's data. A [bug](https://github.com/helm/helm/issues/5156) in Helm prevents PVCs from being deleted. Issue the following commands:
|
||||
|
@ -102,3 +109,9 @@ Run the `helm uninstall` **and** manually remove resources that Helm does not de
|
|||
$ kubectl delete serviceaccount consul-tls-init
|
||||
serviceaccount "consul-tls-init" deleted
|
||||
```
|
||||
|
||||
1. (Optional) Delete the namespace (i.e. `consul` in the following example) that you have dedicated for installing Consul on Kubernetes.
|
||||
|
||||
```shell-session
|
||||
$ kubectl delete ns consul
|
||||
```
|
||||
|
|
|
@ -16,6 +16,8 @@ as first-class Kubernetes services. This functionality is provided by the
|
|||
automatically installed and configured using the
|
||||
[Consul Helm chart](/docs/k8s/installation/install).
|
||||
|
||||
![screenshot of a Kubernetes service in the UI](/img/k8s-service.png)
|
||||
|
||||
**Why sync Kubernetes services to Consul?** Kubernetes services synced to the
|
||||
Consul catalog enable Kubernetes services to be accessed by any node that
|
||||
is part of the Consul cluster, including other distinct Kubernetes clusters.
|
||||
|
|
|
@ -7,8 +7,21 @@ description: >-
|
|||
|
||||
# Consul-Terraform-Sync Enterprise
|
||||
|
||||
Consul-Terraform-Sync Enterprise features address organization complexities of collaboration, operations, scale, and governance. Please see the sidebar navigation on the left to view all enterprise features.
|
||||
Consul-Terraform-Sync (CTS) Enterprise is available with [Consul Enterprise](https://www.hashicorp.com/products/consul) and requires a Consul [license](/docs/nia/enterprise/license) to be applied.
|
||||
|
||||
Enterprise features require a Consul [license](/docs/nia/enterprise/license) to be applied.
|
||||
Enterprise features of CTS address organization complexities of collaboration, operations, scale, and governance. CTS Enterprise supports an official integration with [Terraform Cloud](https://www.terraform.io/cloud) and [Terraform Enterprise](https://www.terraform.io/enterprise), the self-hosted distribution, to extend insight into dynamic updates of your network infrastructure.
|
||||
|
||||
These features are part of Consul-Terraform-Sync Enterprise which is part of [Consul Enterprise](https://www.hashicorp.com/products/consul).
|
||||
| Features | Open Source | Enterprise |
|
||||
|----------|-------------|------------|
|
||||
| Consul Namespace | Default namespace only | Filter task triggers by any namespace |
|
||||
| Automation Driver | Terraform OSS | Terraform OSS, Terraform Cloud, or Terraform Enterprise |
|
||||
| Terraform Workspaces | Local | Local workspaces with the Terraform driver or [remote workspaces](https://www.terraform.io/cloud-docs/workspaces) with the Terraform Cloud driver |
|
||||
| Terraform Backend Options | [azurerm](https://www.terraform.io/docs/backends/types/azurerm.html), [consul](https://www.terraform.io/docs/backends/types/consul.html), [cos](https://www.terraform.io/docs/backends/types/cos.html), [gcs](https://www.terraform.io/docs/backends/types/gcs.html), [kubernetes](https://www.terraform.io/docs/backends/types/kubernetes.html), [local](https://www.terraform.io/docs/backends/types/local.html), [manta](https://www.terraform.io/docs/backends/types/manta.html), [pg](https://www.terraform.io/docs/backends/types/pg.html), and [s3](https://www.terraform.io/docs/backends/types/s3.html) with the Terraform driver | The supported backends for CTS with the Terraform driver or Terraform Cloud with the Terraform Cloud driver |
|
||||
| Terraform Version | One Terraform version for all tasks | Optional Terraform version per task when using the Terraform Cloud driver |
|
||||
| Terraform Run Output | CTS logs | CTS logs or Terraform output organized by Terraform Cloud remote workspaces |
|
||||
| Credentials and secrets | On disk as `.tfvars` files or in shell environment | Secured variables stored in remote workspace |
|
||||
| Audit | | Terraform audit logs ([Terraform Cloud](https://www.terraform.io/cloud-docs/api-docs/audit-trails) or [Terraform Enterprise](https://www.terraform.io/enterprise/admin/infrastructure/logging)) |
|
||||
| Collaboration | | Run [history](https://www.terraform.io/docs/cloud/run/manage.html), [triggers](https://www.terraform.io/docs/cloud/workspaces/run-triggers.html), and [notifications](https://www.terraform.io/docs/cloud/workspaces/notifications.html) supported on Terraform Cloud |
|
||||
| Governance | | [Sentinel](https://www.terraform.io/docs/cloud/sentinel/index.html) to enforce governance policies as code |
|
||||
|
||||
The [Terraform Cloud driver](/docs/nia/configuration#terraform-cloud-driver) enables CTS Enterprise to integrate with Terraform Cloud or Terraform Enterprise. The [Terraform Cloud driver](/docs/nia/network-drivers/terraform-cloud) page provides an overview of how the integration works within CTS.
|
||||
|
|
|
@ -11,6 +11,8 @@ Network Infrastructure Automation (NIA) enables dynamic updates to network infra
|
|||
|
||||
Consul-Terraform-Sync executes one or more automation tasks with the most recent service variable values from the Consul service catalog. Each task consists of a runbook automation written as a Consul-Terraform-Sync compatible Terraform module using resources and data sources for the underlying network infrastructure. The `consul-terraform-sync` daemon runs on the same node as a Consul agent.
|
||||
|
||||
CTS is available as an open source and enterprise distribution. Follow the [Network Infrastructure Automation introduction tutorial](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-intro?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) to get started with CTS OSS or read more about [CTS Enterprise](/docs/nia/enterprise).
|
||||
|
||||
## Use Cases
|
||||
|
||||
**Application teams must wait for manual changes in the network to release, scale up/down and re-deploy their applications.** This creates a bottleneck, especially in frequent workflows related to scaling up/down the application, breaking the DevOps goal of self-service enablement. Consul-Terraform-Sync automates this process, thus decreasing the possibility of human error in manually editing configuration files, as well as decreasing the overall time taken to push out configuration changes.
|
||||
|
@ -19,7 +21,7 @@ Consul-Terraform-Sync executes one or more automation tasks with the most recent
|
|||
|
||||
## Glossary
|
||||
|
||||
**Condition** - A task-level defined environmental requirement. When a task’s condition is met, Consul-Terraform-Sync executes that task to update network infrastructure. Depending on the condition type, the condition definition may also define and enable the source input that the task provides to the configured Terraform Module.
|
||||
**Condition** - A task-level defined environmental requirement. When a task’s condition is met, Consul-Terraform-Sync executes that task to update network infrastructure. Depending on the condition type, the condition definition may also define and enable the source input that the task provides to the configured Terraform module.
|
||||
|
||||
**Consul-Terraform-Sync** - [GitHub repo](https://github.com/hashicorp/consul-terraform-sync) and binary/CLI name for the project that is used to perform Network Infrastructure Automation.
|
||||
|
||||
|
@ -33,10 +35,12 @@ Consul-Terraform-Sync executes one or more automation tasks with the most recent
|
|||
|
||||
-> **Note:** The terminology "tasks" used throughout the documentation refers to all types of tasks except when specifically stated otherwise.
|
||||
|
||||
**Source Input** - A source input defines objects that provide values or metadata to the Terraform module. See [source input](/docs/nia/terraform-modules#source-input) for the supported metadata and values. For example, a user can configure a Consul KV source input to provide KV pairs as variables to their respective Terraform Module. The source input can be included in two ways. It can be specified as a parameter in a condition using `source_includes_var` and also by using the `source_input` block.
|
||||
**Source Input** - A source input defines objects that provide values or metadata to the Terraform module. See [source input](/docs/nia/terraform-modules#source-input) for the supported metadata and values. For example, a user can configure a Consul KV source input to provide KV pairs as variables to their respective Terraform module. The source input can be included in two ways. It can be specified as a parameter in a condition using `source_includes_var` and also by using the `source_input` block.
|
||||
|
||||
**Network Infrastructure Automation (NIA)** - Enables dynamic updates to network infrastructure devices triggered when specific conditions, such as service changes and registration, are met.
|
||||
|
||||
**Services** - A service in CTS represents a service that is registered with Consul for service discovery. Services are grouped by their service names. There may be more than one instance of a particular service, each with its own unique ID. CTS monitors services based on service names and can provide service instance details to a Terraform module for network automation.
|
||||
|
||||
**Tasks** - A task is the translation of dynamic service information from the Consul Catalog into network infrastructure changes downstream.
|
||||
|
||||
**Terraform Cloud** - Per the [Terraform documentation](https://www.terraform.io/docs/cloud/index.html), "Terraform Cloud" describes both Terraform Cloud and Terraform Enterprise, which are different distributions of the same application. Documentation will apply to both distributions unless specifically stated otherwise.
|
||||
|
|
|
@ -9,36 +9,88 @@ description: >-
|
|||
|
||||
Refer to the [introduction](https://learn.hashicorp.com/tutorials/consul/consul-terraform-sync-intro?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial for details about installing, configuring, and running Consul-Terraform-Sync on your local machine with the Terraform driver.
|
||||
|
||||
## Installing Consul-Terraform-Sync
|
||||
## Install Consul-Terraform-Sync
|
||||
|
||||
To install Consul-Terraform-Sync, find the [appropriate package](https://releases.hashicorp.com/consul-terraform-sync/) for your system and download it as a zip archive. Unzip the package to extract the binary named consul-terraform-sync. Move the consul-terraform-sync binary to a location available on your PATH.
|
||||
<Tabs>
|
||||
<Tab heading="Pre-compiled binary">
|
||||
|
||||
To install Consul-Terraform-Sync, find the [appropriate package](https://releases.hashicorp.com/consul-terraform-sync/) for your system and download it as a zip archive. For the CTS Enterprise binary, download a zip archive with the `+ent` metadata. [CTS Enterprise requires a Consul Enterpise license](/docs/nia/enterprise/license) to run.
|
||||
|
||||
Unzip the package to extract the binary named `consul-terraform-sync`. Move the `consul-terraform-sync` binary to a location available on your `PATH`.
|
||||
|
||||
Example:
|
||||
```shell-session
|
||||
$ mv ~/Downloads/consul-terraform-sync /usr/local/bin/consul-terraform-sync
|
||||
$ echo $PATH
|
||||
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
||||
$ mv ./consul-terraform-sync /usr/local/bin/consul-terraform-sync
|
||||
```
|
||||
|
||||
You can also install Consul-Terraform-Sync as a [Docker container](https://hub.docker.com/u/hashicorp/consul-terraform-sync) or build the binary from [source](https://github.com/hashicorp/consul-terraform-sync).
|
||||
|
||||
Once installed, verify the installation works by prompting the help option.
|
||||
Once installed, verify the installation works by prompting the `-version` or `-help` option. The version outputed for the CTS Enterpise binary includes the `+ent` metadata.
|
||||
|
||||
```shell-session
|
||||
$ consul-terraform-sync -h
|
||||
Usage of consul-terraform-sync:
|
||||
-config-dir value
|
||||
A directory to load files for configuring Sync. Configuration files
|
||||
require an .hcl or .json file extention in order to specify their format.
|
||||
This option can be specified multiple times to load different directories.
|
||||
-config-file value
|
||||
A file to load for configuring Sync. Configuration file requires an
|
||||
.hcl or .json extension in order to specify their format. This option can
|
||||
be specified multiple times to load different configuration files.
|
||||
-once
|
||||
Render templates and run tasks once. Does not run the process as a daemon
|
||||
and disables wait timers.
|
||||
-version
|
||||
Print the version of this daemon.
|
||||
$ consul-terraform-sync -version
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="Docker">
|
||||
|
||||
Install and run Consul-Terraform-Sync as a [Docker container](https://hub.docker.com/r/hashicorp/consul-terraform-sync).
|
||||
|
||||
For the CTS Enterprise, use the Docker image [`hashicorp/consul-terraform-sync-enterprise`](https://hub.docker.com/r/hashicorp/consul-terraform-sync-enterprise).
|
||||
|
||||
```shell-session
|
||||
$ docker pull hashicorp/consul-terraform-sync
|
||||
```
|
||||
|
||||
Once installed, verify the installation works by prompting the `-version` or `-help` option. The version outputed for the CTS Enterpise image includes the `+ent` metadata.
|
||||
|
||||
```shell-session
|
||||
$ docker run --rm hashicorp/consul-terraform-sync -version
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="Homewbrew on OS X">
|
||||
|
||||
The CTS OSS binary is available in the HashiCorp tap, which is a repository of all our Homebrew packages.
|
||||
|
||||
```shell-session
|
||||
$ brew tap hashicorp/tap
|
||||
$ brew install hashicorp/tap/consul-terraform-sync
|
||||
```
|
||||
|
||||
Run the following command to update to the latest version:
|
||||
|
||||
```shell-session
|
||||
$ brew upgrade hashicorp/tap/consul-terraform-sync
|
||||
```
|
||||
|
||||
Once installed, verify the installation works by prompting the `-version` or `-help` option.
|
||||
|
||||
```shell-session
|
||||
$ consul-terraform-sync -version
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="Build from source">
|
||||
|
||||
Clone the repository from GitHub [`hashicorp/consul-terraform-sync`](https://github.com/hashicorp/consul-terraform-sync) to build and install the CTS OSS binary in your path `$GOPATH/bin`. Building from source requires `git` and [Golang](https://go.dev/).
|
||||
|
||||
```shell-session
|
||||
$ git clone https://github.com/hashicorp/consul-terraform-sync.git
|
||||
$ cd consul-terraform-sync
|
||||
$ git checkout tags/<vX.Y.Z>
|
||||
$ go install
|
||||
```
|
||||
|
||||
Once installed, verify the installation works by prompting the `-version` or `-help` option.
|
||||
|
||||
```shell-session
|
||||
$ consul-terraform-sync -version
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Connect your Consul Cluster
|
||||
|
||||
Consul-Terraform-Sync connects with your Consul cluster in order to monitor the Consul catalog for service changes. These service changes lead to downstream updates to your network devices. You can configure your Consul cluster in Consul-Terraform-Sync with the [Consul block](/docs/nia/configuration#consul). Below is an example:
|
||||
|
|
|
@ -16,7 +16,7 @@ The following table highlights some of the additional features Terraform and Ter
|
|||
| Network Driver | Description | Features |
|
||||
| -------------- | ----------- | -------- |
|
||||
| [Terraform driver](/docs/nia/network-drivers/terraform) | Consul-Terraform-Sync automates a local installation of the [Terraform CLI](https://www.terraform.io/) | - Local Terraform execution <br/> - Local workspace directories <br/> - [Backend options](/docs/nia/configuration#backend) available for state storage <br/> |
|
||||
| [Terraform Cloud driver](/docs/nia/network-drivers/terraform-cloud) | Consul-Terraform-Sync Enterprise automates remote workspaces on [Terraform Cloud](https://www.terraform.io/docs/cloud/index.html) | - [Remote Terraform execution](https://www.terraform.io/docs/cloud/run/index.html) <br/> - Concurrent runs <br/> - [Secured variables](https://www.terraform.io/docs/cloud/workspaces/variables.html) <br/> - [State versions](https://www.terraform.io/docs/cloud/workspaces/state.html) <br/> - [Sentinel](https://www.terraform.io/docs/cloud/sentinel/index.html) to enforce governance policies as code <br/> - Audit [logs](https://www.terraform.io/docs/enterprise/admin/logging.html) and [trails](https://www.terraform.io/docs/cloud/api/audit-trails.html) <br/> - Run [history](https://www.terraform.io/docs/cloud/run/manage.html), [triggers](https://www.terraform.io/docs/cloud/workspaces/run-triggers.html) and [notifications](https://www.terraform.io/docs/cloud/workspaces/notifications.html) <br/> - [Terraform Cloud Agents](https://www.terraform.io/docs/cloud/agents/index.html) |
|
||||
| [Terraform Cloud driver](/docs/nia/network-drivers/terraform-cloud) | Consul-Terraform-Sync Enterprise automates remote workspaces on [Terraform Cloud](https://www.terraform.io/docs/cloud/index.html) | - [Remote Terraform execution](https://www.terraform.io/docs/cloud/run/index.html) <br/> - Concurrent runs <br/> - [Secured variables](https://www.terraform.io/docs/cloud/workspaces/variables.html) <br/> - [State versions](https://www.terraform.io/docs/cloud/workspaces/state.html) <br/> - [Sentinel](https://www.terraform.io/docs/cloud/sentinel/index.html) to enforce governance policies as code <br/> - Audit [logs](https://www.terraform.io/docs/enterprise/admin/logging.html) and [trails](https://www.terraform.io/docs/cloud/api/audit-trails.html) <br/> - Run [history](https://www.terraform.io/docs/cloud/run/manage.html), [triggers](https://www.terraform.io/docs/cloud/workspaces/run-triggers.html), and [notifications](https://www.terraform.io/docs/cloud/workspaces/notifications.html) <br/> - [Terraform Cloud Agents](https://www.terraform.io/docs/cloud/agents/index.html) |
|
||||
|
||||
## Understanding Terraform Automation
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ Once all workspaces are set up, Consul-Terraform-Sync monitors the Consul catalo
|
|||
|
||||
Within the Consul-Terraform-Sync configuration for a task, practitioners can select the desired module to run for the task as well as set the condition to execute the task. Each task executed by the Terraform driver corresponds to an automated root module that calls the selected module in an isolated Terraform environment. Consul-Terraform-Sync will manage concurrent execution of these tasks.
|
||||
|
||||
Autogenerated root modules for tasks are mantained in local subdirectories of the Consul-Terraform-Sync working directory. Each subdirectory represents the local workspace for a task. By default, the working directory `sync-tasks` is created in the current directory. To configure where Terraform configuration files are stored, set [`working_dir`](/docs/nia/configuration#working_dir) to the desired path or configure the [`task.working_dir`](/docs/nia/configuration#working_dir-1) individually.
|
||||
Autogenerated root modules for tasks are maintained in local subdirectories of the Consul-Terraform-Sync working directory. Each subdirectory represents the local workspace for a task. By default, the working directory `sync-tasks` is created in the current directory. To configure where Terraform configuration files are stored, set [`working_dir`](/docs/nia/configuration#working_dir) to the desired path or configure the [`task.working_dir`](/docs/nia/configuration#working_dir-1) individually.
|
||||
|
||||
~> **Note:** Although Terraform state files for task workspaces are independent, this does not guarantee the infrastructure changes from concurrent task executions are independent. Ensure that modules across all tasks are not modifying the same resource objects or have overlapping changes that may result in race conditions during automation.
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ The following syntax describes how to include a resource label in the rule:
|
|||
</CodeBlockConfig>
|
||||
</CodeTabs>
|
||||
|
||||
Labels provide operators with more granular control over access to the resouce, but the following resource types do not take a label:
|
||||
Labels provide operators with more granular control over access to the resource, but the following resource types do not take a label:
|
||||
|
||||
* `acl`
|
||||
* `keyring`
|
||||
|
@ -102,7 +102,7 @@ Use the `policy` keyword and one of the following access levels to set a policy
|
|||
- `write`: Allows the resource to be read and modified.
|
||||
- `deny`: Denies read and write access to the resource.
|
||||
|
||||
The special `list` access level provices access to all keys with the specified resource label in the Consul KV. The `list` access level can only be used with the `key_prefix` resource. The [`acl.enable_key_list_policy`](/docs/agent/options#acl_enable_key_list_policy) setting must be set to `true`.
|
||||
The special `list` access level provides access to all keys with the specified resource label in the Consul KV. The `list` access level can only be used with the `key_prefix` resource. The [`acl.enable_key_list_policy`](/docs/agent/options#acl_enable_key_list_policy) setting must be set to `true`.
|
||||
|
||||
### Matching and Prefix Values
|
||||
|
||||
|
@ -295,7 +295,7 @@ $ curl \
|
|||
}' http://127.0.0.1:8500/v1/acl/policy?token=<management token>
|
||||
```
|
||||
|
||||
The policy configuration is returned when the call is succesfully performaed:
|
||||
The policy configuration is returned when the call is successfully performed:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ description: >-
|
|||
|
||||
This guide explains how to best upgrade a single Consul Enterprise datacenter to v1.10.0
|
||||
from a version of Consul that is forward compatible with v1.10. If you are on a major
|
||||
version of Consul prior to 1.8, you will need to complete and [upgrade to 1.8.13](/docs/upgrading/instructions)
|
||||
version of Consul prior to 1.8, you will need to complete and [upgrade to 1.8.13](/docs/upgrading/instructions/upgrade-to-1-8-x)
|
||||
or higher before continuing with this guide. If you are already on a major version of 1.8 or 1.9, then
|
||||
this guide will go over the procedures required for upgrading to v1.10. This process
|
||||
will require intermediate version upgrades to a forward-compatible release of v1.8 or v1.9,
|
||||
|
@ -210,7 +210,7 @@ to the latest 1.8.x or 1.9.x before the next steps. Otherwise, this step can be
|
|||
|
||||
**6.** Update the value of `global.image` in the values file to the latest 1.8.x or 1.9.x image.
|
||||
|
||||
Additionally, ensure that the Kuberenetes secret with the license is specified in the
|
||||
Additionally, ensure that the Kubernetes secret with the license is specified in the
|
||||
values `server.enterpriseLicense.secretName` and `server.enterpriseLicense.secretKey`.
|
||||
|
||||
**7.** Upgrade the cluster.
|
||||
|
|
|
@ -16,6 +16,13 @@ upgrade flow.
|
|||
|
||||
## Consul 1.11.0
|
||||
|
||||
### 1.10 Compatibility <EnterpriseAlert inline />
|
||||
Consul Enterprise versions 1.10.0 through 1.10.4 contain a latent bug that
|
||||
causes those client or server agents to deregister their own services or health
|
||||
checks when some of the servers have been upgraded to 1.11. Before upgrading Consul Enterprise servers to 1.11, all Consul agents should first
|
||||
be upgraded to 1.10.6 or higher to ensure forward compatibility and prevent
|
||||
flapping of catalog registrations.
|
||||
|
||||
### Deprecated Agent Config Options
|
||||
|
||||
Consul 1.11.0 is compiled with Go 1.17 and now the ordering of
|
||||
|
|
|
@ -269,6 +269,10 @@
|
|||
"title": "Connectivity Tasks",
|
||||
"path": "connect/connectivity-tasks"
|
||||
},
|
||||
{
|
||||
"title": "Distributed Tracing",
|
||||
"path": "connect/distributed-tracing"
|
||||
},
|
||||
{
|
||||
"title": "Gateways",
|
||||
"routes": [
|
||||
|
|
After Width: | Height: | Size: 334 KiB |
After Width: | Height: | Size: 334 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 170 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 150 KiB |