mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 12:47:28 +08:00
feat: Support adding a proxy server in between when forwarding requests to upstream (#2710)
Co-authored-by: 澄潭 <zty98751@alibaba-inc.com>
This commit is contained in:
243
registry/proxy/factory.go
Normal file
243
registry/proxy/factory.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"istio.io/api/networking/v1alpha3"
|
||||
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
"github.com/alibaba/higress/pkg/common"
|
||||
ingress "github.com/alibaba/higress/pkg/ingress/kube/common"
|
||||
"github.com/alibaba/higress/pkg/ingress/kube/util"
|
||||
)
|
||||
|
||||
const (
|
||||
proxyPortRangeStart uint32 = 50001
|
||||
proxyPortRangeEnd uint32 = 51000 // Exclusive
|
||||
|
||||
defaultProxyConnectTimeout = 1200
|
||||
|
||||
proxyClusterPatchTemplate = `{
|
||||
"name": "{{name}}",
|
||||
"connect_timeout": "{{connect_timeout}}ms",
|
||||
"type": "{{type}}",
|
||||
"dns_lookup_family": "V4_ONLY",
|
||||
"load_assignment": {
|
||||
"cluster_name": "{{name}}",
|
||||
"endpoints": [
|
||||
{
|
||||
"lb_endpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socket_address": {
|
||||
"address": "{{address}}",
|
||||
"port_value": {{port}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
proxyListenerPatchTemplate = `{
|
||||
"name": "istio-autogenerated-proxy-listener-{{proxy_name}}",
|
||||
"address": {
|
||||
"socket_address": {
|
||||
"address": "127.0.0.1",
|
||||
"port_value": {{port}}
|
||||
}
|
||||
},
|
||||
"listener_filters": [
|
||||
{
|
||||
"name": "envoy.filters.listener.tls_inspector",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filter_chains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "tcp",
|
||||
"typed_config": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||
"stat_prefix": "tcpproxy.{{proxy_name}}",
|
||||
"cluster": "{{cluster_name}}",
|
||||
"tunneling_config": {
|
||||
"hostname": "%REQUESTED_SERVER_NAME%:443"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
)
|
||||
|
||||
var (
|
||||
configPatchesBuilders = map[common.ProxyType]func(*apiv1.ProxyConfig) []*v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch{
|
||||
common.ProxyType_HTTP: buildConfigPatchesForHttpProxy,
|
||||
}
|
||||
|
||||
ipv4AddressRegexp = regexp.MustCompile("(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}")
|
||||
)
|
||||
|
||||
func NeedToFillProxyListenerPorts(proxies []*apiv1.ProxyConfig) bool {
|
||||
if proxies == nil || len(proxies) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, proxy := range proxies {
|
||||
if proxy.ListenerPort <= 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func FillProxyListenerPorts(proxies []*apiv1.ProxyConfig) bool {
|
||||
if proxies == nil || len(proxies) == 0 {
|
||||
return false
|
||||
}
|
||||
filled := false
|
||||
var usedPorts = make(map[uint32]bool)
|
||||
for _, proxy := range proxies {
|
||||
if proxy.ListenerPort > 0 {
|
||||
usedPorts[proxy.ListenerPort] = true
|
||||
}
|
||||
}
|
||||
for _, proxy := range proxies {
|
||||
if proxy.ListenerPort > 0 {
|
||||
continue
|
||||
}
|
||||
for port := proxyPortRangeStart; port < proxyPortRangeEnd; port++ {
|
||||
if !usedPorts[port] {
|
||||
proxy.ListenerPort = port
|
||||
usedPorts[port] = true
|
||||
filled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return filled
|
||||
}
|
||||
|
||||
func BuildProxyWrapper(config *apiv1.ProxyConfig) *ingress.ProxyWrapper {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
if len(config.ServerAddress) == 0 || config.ServerPort <= 0 || config.ServerPort > 65535 || config.ListenerPort <= 0 || config.ListenerPort > 65535 {
|
||||
return nil
|
||||
}
|
||||
|
||||
envoyFilter := buildEnvoyFilter(config)
|
||||
if envoyFilter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ingress.ProxyWrapper{
|
||||
ProxyName: config.Name,
|
||||
ListenerPort: config.ListenerPort,
|
||||
EnvoyFilter: envoyFilter,
|
||||
}
|
||||
}
|
||||
|
||||
func buildEnvoyFilter(config *apiv1.ProxyConfig) *v1alpha3.EnvoyFilter {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
configPatchesBuilder := configPatchesBuilders[common.ParseProxyType(config.Type)]
|
||||
if configPatchesBuilder == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
configPatches := configPatchesBuilder(config)
|
||||
return &v1alpha3.EnvoyFilter{ConfigPatches: configPatches}
|
||||
}
|
||||
|
||||
func buildConfigPatchesForHttpProxy(config *apiv1.ProxyConfig) []*v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch {
|
||||
if common.ParseProxyType(config.Type) != common.ProxyType_HTTP {
|
||||
return nil
|
||||
}
|
||||
|
||||
clusterName := buildClusterName(config)
|
||||
|
||||
var patches []*v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch
|
||||
|
||||
// Add a cluster for the proxy server
|
||||
proxyClusterPatchJson := proxyClusterPatchTemplate
|
||||
{
|
||||
clusterType := ""
|
||||
if ipv4AddressRegexp.MatchString(config.ServerAddress) {
|
||||
clusterType = "STATIC"
|
||||
} else {
|
||||
clusterType = "STRICT_DNS"
|
||||
}
|
||||
connectTimeout := config.ConnectTimeout
|
||||
if connectTimeout <= 0 {
|
||||
connectTimeout = defaultProxyConnectTimeout
|
||||
}
|
||||
proxyClusterPatchJson = strings.ReplaceAll(proxyClusterPatchJson, "{{name}}", clusterName)
|
||||
proxyClusterPatchJson = strings.ReplaceAll(proxyClusterPatchJson, "{{type}}", clusterType)
|
||||
proxyClusterPatchJson = strings.ReplaceAll(proxyClusterPatchJson, "{{connect_timeout}}", fmt.Sprintf("%d", connectTimeout))
|
||||
proxyClusterPatchJson = strings.ReplaceAll(proxyClusterPatchJson, "{{address}}", config.ServerAddress)
|
||||
proxyClusterPatchJson = strings.ReplaceAll(proxyClusterPatchJson, "{{port}}", fmt.Sprintf("%d", config.ServerPort))
|
||||
}
|
||||
proxyClusterPatch := &v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch{
|
||||
ApplyTo: v1alpha3.EnvoyFilter_CLUSTER,
|
||||
Match: &v1alpha3.EnvoyFilter_EnvoyConfigObjectMatch{
|
||||
Context: v1alpha3.EnvoyFilter_GATEWAY,
|
||||
},
|
||||
Patch: &v1alpha3.EnvoyFilter_Patch{
|
||||
Operation: v1alpha3.EnvoyFilter_Patch_ADD,
|
||||
Value: util.BuildPatchStruct(proxyClusterPatchJson),
|
||||
},
|
||||
}
|
||||
patches = append(patches, proxyClusterPatch)
|
||||
|
||||
// Add a listener to accept requests from the gateway itself
|
||||
proxyListenerPatchJson := proxyListenerPatchTemplate
|
||||
proxyListenerPatchJson = strings.ReplaceAll(proxyListenerPatchJson, "{{proxy_name}}", config.Name)
|
||||
proxyListenerPatchJson = strings.ReplaceAll(proxyListenerPatchJson, "{{port}}", fmt.Sprintf("%d", config.ListenerPort))
|
||||
proxyListenerPatchJson = strings.ReplaceAll(proxyListenerPatchJson, "{{cluster_name}}", clusterName)
|
||||
proxyListenerPatch := &v1alpha3.EnvoyFilter_EnvoyConfigObjectPatch{
|
||||
ApplyTo: v1alpha3.EnvoyFilter_LISTENER,
|
||||
Match: &v1alpha3.EnvoyFilter_EnvoyConfigObjectMatch{
|
||||
Context: v1alpha3.EnvoyFilter_GATEWAY,
|
||||
},
|
||||
Patch: &v1alpha3.EnvoyFilter_Patch{
|
||||
Operation: v1alpha3.EnvoyFilter_Patch_ADD,
|
||||
Value: util.BuildPatchStruct(proxyListenerPatchJson),
|
||||
},
|
||||
}
|
||||
patches = append(patches, proxyListenerPatch)
|
||||
|
||||
return patches
|
||||
}
|
||||
|
||||
func buildClusterName(config *apiv1.ProxyConfig) string {
|
||||
if config == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("outbound|%d||%s.proxy", config.ServerPort, config.Name)
|
||||
}
|
||||
Reference in New Issue
Block a user