mirror of
https://github.com/alibaba/higress.git
synced 2026-02-25 05:01:19 +08:00
244 lines
7.0 KiB
Go
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)
|
|
}
|