Backport of ext-authz Envoy extension: support `localhost` as a valid target URI. into release/1.16.x (#17837)
* backport of commit 391db7e58b501b3ed7561fec352f2f3f5004a29f * backport of commit f204d5b52ab80836128882a65d7d7c5e53b2fa3d --------- Co-authored-by: Chris Thain <chris.m.thain@gmail.com>
This commit is contained in:
parent
b4afa2bbff
commit
e949c3fccc
|
@ -59,7 +59,7 @@ func TestConstructor(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
errMsg: `invalid host for Target.URI "foo.bar.com:9191": expected 'localhost' or '127.0.0.1'`,
|
errMsg: `invalid host for Target.URI "foo.bar.com:9191": expected "localhost", "127.0.0.1", or "::1"`,
|
||||||
},
|
},
|
||||||
"non-loopback address": {
|
"non-loopback address": {
|
||||||
args: map[string]any{
|
args: map[string]any{
|
||||||
|
@ -72,7 +72,34 @@ func TestConstructor(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
errMsg: `invalid host for Target.URI "10.0.0.1:9191": expected 'localhost' or '127.0.0.1'`,
|
errMsg: `invalid host for Target.URI "10.0.0.1:9191": expected "localhost", "127.0.0.1", or "::1"`,
|
||||||
|
},
|
||||||
|
"invalid target port": {
|
||||||
|
args: map[string]any{
|
||||||
|
"ProxyType": "connect-proxy",
|
||||||
|
"Config": map[string]any{
|
||||||
|
"GrpcService": map[string]any{
|
||||||
|
"Target": map[string]any{
|
||||||
|
"URI": "localhost:zero",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errMsg: `invalid format for Target.URI "localhost:zero": expected host:port`,
|
||||||
|
},
|
||||||
|
"invalid target timeout": {
|
||||||
|
args: map[string]any{
|
||||||
|
"ProxyType": "connect-proxy",
|
||||||
|
"Config": map[string]any{
|
||||||
|
"GrpcService": map[string]any{
|
||||||
|
"Target": map[string]any{
|
||||||
|
"URI": "localhost:9191",
|
||||||
|
"Timeout": "one",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errMsg: `failed to parse Target.Timeout "one" as a duration`,
|
||||||
},
|
},
|
||||||
"no uri or service target": {
|
"no uri or service target": {
|
||||||
args: map[string]any{
|
args: map[string]any{
|
||||||
|
|
|
@ -34,6 +34,9 @@ const (
|
||||||
defaultMetadataNS = "consul"
|
defaultMetadataNS = "consul"
|
||||||
defaultStatPrefix = "response"
|
defaultStatPrefix = "response"
|
||||||
defaultStatusOnError = 403
|
defaultStatusOnError = 403
|
||||||
|
localhost = "localhost"
|
||||||
|
localhostIPv4 = "127.0.0.1"
|
||||||
|
localhostIPv6 = "::1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type extAuthzConfig struct {
|
type extAuthzConfig struct {
|
||||||
|
@ -185,6 +188,12 @@ func (c *extAuthzConfig) toEnvoyCluster(_ *cmn.RuntimeConfig) (*envoy_cluster_v3
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clusterType := &envoy_cluster_v3.Cluster_Type{Type: envoy_cluster_v3.Cluster_STATIC}
|
||||||
|
if host == localhost {
|
||||||
|
// If the host is "localhost" use a STRICT_DNS cluster type to perform DNS lookup.
|
||||||
|
clusterType = &envoy_cluster_v3.Cluster_Type{Type: envoy_cluster_v3.Cluster_STRICT_DNS}
|
||||||
|
}
|
||||||
|
|
||||||
var typedExtProtoOpts map[string]*anypb.Any
|
var typedExtProtoOpts map[string]*anypb.Any
|
||||||
if c.isGRPC() {
|
if c.isGRPC() {
|
||||||
// By default HTTP/1.1 is used for the transport protocol. gRPC requires that we explicitly configure HTTP/2
|
// By default HTTP/1.1 is used for the transport protocol. gRPC requires that we explicitly configure HTTP/2
|
||||||
|
@ -205,7 +214,7 @@ func (c *extAuthzConfig) toEnvoyCluster(_ *cmn.RuntimeConfig) (*envoy_cluster_v3
|
||||||
|
|
||||||
return &envoy_cluster_v3.Cluster{
|
return &envoy_cluster_v3.Cluster{
|
||||||
Name: LocalExtAuthzClusterName,
|
Name: LocalExtAuthzClusterName,
|
||||||
ClusterDiscoveryType: &envoy_cluster_v3.Cluster_Type{Type: envoy_cluster_v3.Cluster_STATIC},
|
ClusterDiscoveryType: clusterType,
|
||||||
ConnectTimeout: target.timeoutDurationPB(),
|
ConnectTimeout: target.timeoutDurationPB(),
|
||||||
LoadAssignment: &envoy_endpoint_v3.ClusterLoadAssignment{
|
LoadAssignment: &envoy_endpoint_v3.ClusterLoadAssignment{
|
||||||
ClusterName: LocalExtAuthzClusterName,
|
ClusterName: LocalExtAuthzClusterName,
|
||||||
|
@ -645,18 +654,13 @@ func (t *Target) validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.isURI() {
|
if t.isURI() {
|
||||||
// Strip the protocol if one was provided
|
t.host, t.port, err = parseAddr(t.URI)
|
||||||
if _, addr, hasProto := strings.Cut(t.URI, "://"); hasProto {
|
if err == nil {
|
||||||
t.URI = addr
|
switch t.host {
|
||||||
}
|
case localhost, localhostIPv4, localhostIPv6:
|
||||||
addr := strings.Split(t.URI, ":")
|
default:
|
||||||
if len(addr) == 2 {
|
resultErr = multierror.Append(resultErr,
|
||||||
t.host = addr[0]
|
fmt.Errorf("invalid host for Target.URI %q: expected %q, %q, or %q", t.URI, localhost, localhostIPv4, localhostIPv6))
|
||||||
if t.host != "localhost" && t.host != "127.0.0.1" {
|
|
||||||
resultErr = multierror.Append(resultErr, fmt.Errorf("invalid host for Target.URI %q: expected 'localhost' or '127.0.0.1'", t.URI))
|
|
||||||
}
|
|
||||||
if t.port, err = strconv.Atoi(addr[1]); err != nil {
|
|
||||||
resultErr = multierror.Append(resultErr, fmt.Errorf("invalid port for Target.URI %q", addr[1]))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resultErr = multierror.Append(resultErr, fmt.Errorf("invalid format for Target.URI %q: expected host:port", t.URI))
|
resultErr = multierror.Append(resultErr, fmt.Errorf("invalid format for Target.URI %q: expected host:port", t.URI))
|
||||||
|
@ -672,3 +676,22 @@ func (t *Target) validate() error {
|
||||||
}
|
}
|
||||||
return resultErr
|
return resultErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseAddr(s string) (host string, port int, err error) {
|
||||||
|
// Strip the protocol if one was provided
|
||||||
|
if _, addr, hasProto := strings.Cut(s, "://"); hasProto {
|
||||||
|
s = addr
|
||||||
|
}
|
||||||
|
idx := strings.LastIndex(s, ":")
|
||||||
|
switch idx {
|
||||||
|
case -1, len(s) - 1:
|
||||||
|
err = fmt.Errorf("invalid input format %q: expected host:port", s)
|
||||||
|
case 0:
|
||||||
|
host = localhost
|
||||||
|
port, err = strconv.Atoi(s[idx+1:])
|
||||||
|
default:
|
||||||
|
host = s[:idx]
|
||||||
|
port, err = strconv.Atoi(s[idx+1:])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -676,6 +676,7 @@ end`,
|
||||||
ns.Proxy.EnvoyExtensions = makeExtAuthzEnvoyExtension(
|
ns.Proxy.EnvoyExtensions = makeExtAuthzEnvoyExtension(
|
||||||
"http",
|
"http",
|
||||||
"dest=local",
|
"dest=local",
|
||||||
|
"target-uri=localhost:9191",
|
||||||
"insert=AfterLastMatch:envoy.filters.http.header_to_metadata",
|
"insert=AfterLastMatch:envoy.filters.http.header_to_metadata",
|
||||||
)
|
)
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
|
@ -50,6 +50,13 @@ func makeExtAuthzEnvoyExtension(svc string, opts ...string) []structs.EnvoyExten
|
||||||
"FilterName": filterName,
|
"FilterName": filterName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "target-uri":
|
||||||
|
target = map[string]any{"URI": v}
|
||||||
|
configMap = map[string]any{
|
||||||
|
serviceKey: map[string]any{
|
||||||
|
"Target": target,
|
||||||
|
},
|
||||||
|
}
|
||||||
case "config-type":
|
case "config-type":
|
||||||
if v == "full" {
|
if v == "full" {
|
||||||
target["Timeout"] = "2s"
|
target["Timeout"] = "2s"
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
{
|
{
|
||||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
"name": "local_ext_authz",
|
"name": "local_ext_authz",
|
||||||
"type": "STATIC",
|
"type": "STRICT_DNS",
|
||||||
"loadAssignment": {
|
"loadAssignment": {
|
||||||
"clusterName": "local_ext_authz",
|
"clusterName": "local_ext_authz",
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
"endpoint": {
|
"endpoint": {
|
||||||
"address": {
|
"address": {
|
||||||
"socketAddress": {
|
"socketAddress": {
|
||||||
"address": "127.0.0.1",
|
"address": "localhost",
|
||||||
"portValue": 9191
|
"portValue": 9191
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,7 +348,7 @@ The following table describes how to configure parameters for the `Service` fiel
|
||||||
|
|
||||||
### `Arguments.Config.GrpcService.Target.Uri`
|
### `Arguments.Config.GrpcService.Target.Uri`
|
||||||
|
|
||||||
Specifies the URI of the external authorization service. Configure this field when you must provide an explicit URI to the external authorization service, such as cases in which the authorization service is running on the same host or pod. If set, the value of this field must be either `localhost:<port>` or `127.0.0.1:<port>`
|
Specifies the URI of the external authorization service. Configure this field when you must provide an explicit URI to the external authorization service, such as cases in which the authorization service is running on the same host or pod. If set, the value of this field must be one of `localhost:<port>`, `127.0.0.1:<port>`, or `::1:<port>`.
|
||||||
|
|
||||||
Configure either the `Uri` field or the [`Service`](#arguments-config-grpcservice-target-service) field, but not both.
|
Configure either the `Uri` field or the [`Service`](#arguments-config-grpcservice-target-service) field, but not both.
|
||||||
|
|
||||||
|
@ -434,7 +434,7 @@ The following table describes how to configure parameters for the `Service` fiel
|
||||||
|
|
||||||
### `Arguments{}.Config{}.HttpService{}.Target{}.Uri`
|
### `Arguments{}.Config{}.HttpService{}.Target{}.Uri`
|
||||||
|
|
||||||
Specifies the URI of the external authorization service. Configure this field when you must provide an explicit URI to the external authorization service, such as cases in which the authorization service is running on the same host or pod.
|
Specifies the URI of the external authorization service. Configure this field when you must provide an explicit URI to the external authorization service, such as cases in which the authorization service is running on the same host or pod. If set, the value of this field must be one of `localhost:<port>`, `127.0.0.1:<port>`, or `::1:<port>`.
|
||||||
|
|
||||||
Configure either the `Uri` field or the [`Service`](#arguments-config-httpservice-target-service) field, but not both.
|
Configure either the `Uri` field or the [`Service`](#arguments-config-httpservice-target-service) field, but not both.
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ The following Envoy configurations are not supported:
|
||||||
| `failure_mode_allow` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
| `failure_mode_allow` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
||||||
| `filter_enabled` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
| `filter_enabled` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
||||||
| `filter_enabled_metadata` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
| `filter_enabled_metadata` | Set the `EnvoyExtension.Required` field to `true` in the [service defaults configuration entry](/consul/docs/connect/config-entries/service-defaults#envoyextensions) or [proxy defaults configuration entry](/consul/docs/connect/config-entries/proxy-defaults#envoyextensions). |
|
||||||
| `transport_api_version` | Consul only supports v3 of the transport API. As a result, there is no workaround for implement the behavior of this field. |
|
| `transport_api_version` | Consul only supports v3 of the transport API. As a result, there is no workaround for implementing the behavior of this field. |
|
||||||
|
|
||||||
## Apply the configuration entry
|
## Apply the configuration entry
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue