-
Notifications
You must be signed in to change notification settings - Fork 361
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: huabing zhao <[email protected]>
- Loading branch information
1 parent
d3110e3
commit 6711663
Showing
4 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// Copyright Envoy Gateway Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// The full text of the Apache license is available in the LICENSE file at | ||
// the root of the repo. | ||
|
||
package translator | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" | ||
corsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" | ||
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" | ||
matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" | ||
"github.com/golang/protobuf/ptypes/wrappers" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
|
||
"github.com/envoyproxy/gateway/internal/ir" | ||
) | ||
|
||
const ( | ||
corsFilter = "envoy.filters.http.cors" | ||
) | ||
|
||
// patchHCMWithCorsFilter builds and appends the Cors Filter to the HTTP | ||
// Connection Manager if applicable, and it does not already exist. | ||
func patchHCMWithCorsFilter(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { | ||
if mgr == nil { | ||
return errors.New("hcm is nil") | ||
} | ||
|
||
if irListener == nil { | ||
return errors.New("ir listener is nil") | ||
} | ||
|
||
if !listenerContainsCorsPolicy(irListener) { | ||
return nil | ||
} | ||
|
||
// Return early if filter already exists. | ||
for _, httpFilter := range mgr.HttpFilters { | ||
if httpFilter.Name == corsFilter { | ||
return nil | ||
} | ||
} | ||
|
||
corsFilter, err := buildHCMCorsFilter(irListener) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Ensure the cors filter is the first one in the chain. | ||
mgr.HttpFilters = append([]*hcmv3.HttpFilter{corsFilter}, mgr.HttpFilters...) | ||
|
||
return nil | ||
} | ||
|
||
// buildHCMCorsFilter returns a Cors filter from the provided IR listener. | ||
func buildHCMCorsFilter(irListener *ir.HTTPListener) (*hcmv3.HttpFilter, error) { | ||
corsProto := &corsv3.Cors{} | ||
|
||
corsAny, err := anypb.New(corsProto) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &hcmv3.HttpFilter{ | ||
Name: corsFilter, | ||
ConfigType: &hcmv3.HttpFilter_TypedConfig{ | ||
TypedConfig: corsAny, | ||
}, | ||
}, nil | ||
} | ||
|
||
// listenerContainsCorsPolicy returns true if the provided listener has Cors | ||
// policies attached to its routes. | ||
func listenerContainsCorsPolicy(irListener *ir.HTTPListener) bool { | ||
if irListener == nil { | ||
return false | ||
} | ||
|
||
for _, route := range irListener.Routes { | ||
if route.CorsPolicy != nil { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// patchRouteWithCorsConfig patches the provided route with the Cors config if | ||
// applicable. | ||
func patchRouteWithCorsConfig(route *routev3.Route, irRoute *ir.HTTPRoute) error { | ||
if route == nil { | ||
return errors.New("xds route is nil") | ||
} | ||
if irRoute == nil { | ||
return errors.New("ir route is nil") | ||
} | ||
if irRoute.CorsPolicy == nil { | ||
return nil | ||
} | ||
|
||
filterCfg := route.GetTypedPerFilterConfig() | ||
if _, ok := filterCfg[corsFilter]; ok { | ||
return fmt.Errorf("route already contains cors config: %+v", route) | ||
} | ||
|
||
var ( | ||
allowOrigins []*matcherv3.StringMatcher | ||
allowMethods string | ||
allowHeaders string | ||
exposeHeaders string | ||
maxAge string | ||
allowCredentials *wrappers.BoolValue | ||
allowPrivateNetworkAccess *wrappers.BoolValue | ||
) | ||
|
||
for _, origin := range irRoute.CorsPolicy.AllowOrigins { | ||
if origin.Exact != nil { | ||
allowOrigins = append(allowOrigins, &matcherv3.StringMatcher{ | ||
MatchPattern: &matcherv3.StringMatcher_Exact{ | ||
Exact: *origin.Exact, | ||
}, | ||
}) | ||
} else if origin.Prefix != nil { | ||
allowOrigins = append(allowOrigins, &matcherv3.StringMatcher{ | ||
MatchPattern: &matcherv3.StringMatcher_Prefix{ | ||
Prefix: *origin.Prefix, | ||
}, | ||
}) | ||
} else if origin.SafeRegex != nil { | ||
allowOrigins = append(allowOrigins, &matcherv3.StringMatcher{ | ||
MatchPattern: &matcherv3.StringMatcher_SafeRegex{ | ||
SafeRegex: &matcherv3.RegexMatcher{ | ||
Regex: *origin.SafeRegex, | ||
}, | ||
}, | ||
}) | ||
} else if origin.Suffix != nil { | ||
allowOrigins = append(allowOrigins, &matcherv3.StringMatcher{ | ||
MatchPattern: &matcherv3.StringMatcher_Suffix{ | ||
Suffix: *origin.Suffix, | ||
}, | ||
}) | ||
} | ||
} | ||
|
||
allowMethods = strings.Join(irRoute.CorsPolicy.AllowMethods, " ,") | ||
allowHeaders = strings.Join(irRoute.CorsPolicy.AllowedHeaders, " ,") | ||
exposeHeaders = strings.Join(irRoute.CorsPolicy.ExposedHeaders, " ,") | ||
maxAge = strconv.Itoa(int(irRoute.CorsPolicy.MaxAge.Seconds())) | ||
allowPrivateNetworkAccess = &wrappers.BoolValue{Value: irRoute.CorsPolicy.AllowPrivateNetworkAccess} | ||
allowCredentials = &wrappers.BoolValue{Value: irRoute.CorsPolicy.AllowCredentials} | ||
|
||
routeCfgProto := &corsv3.CorsPolicy{ | ||
AllowOriginStringMatch: allowOrigins, | ||
AllowMethods: allowMethods, | ||
AllowHeaders: allowHeaders, | ||
ExposeHeaders: exposeHeaders, | ||
MaxAge: maxAge, | ||
AllowCredentials: allowCredentials, | ||
AllowPrivateNetworkAccess: allowPrivateNetworkAccess, | ||
} | ||
|
||
routeCfgAny, err := anypb.New(routeCfgProto) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if filterCfg == nil { | ||
route.TypedPerFilterConfig = make(map[string]*anypb.Any) | ||
} | ||
|
||
route.TypedPerFilterConfig[corsFilter] = routeCfgAny | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters