Files
higress/registry/proxy/factory.go
2025-09-21 14:29:07 +08:00

244 lines
7.0 KiB
Go

// 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/v2/api/networking/v1"
"github.com/alibaba/higress/v2/pkg/common"
ingress "github.com/alibaba/higress/v2/pkg/ingress/kube/common"
"github.com/alibaba/higress/v2/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
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)
}