Skip to content

Commit

Permalink
TLS Passthrough support (#402)
Browse files Browse the repository at this point in the history
* TLS Passthrough support

This commit adds a tlsroute controller which is further used
to configure tls passthrough in envoy.

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* Adding tlsroute experimental crd in testdata

update gatewayclass/gateway/httproute experimental
CRDs to use standard schemas

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* keep other testdata changes out of this PR

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* added testcases for tlsroutes, include serviceport in irInfraPortName

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* lintfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* tlroute kubernetes provider test

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* added xds tls config validate test for passthrough

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* types test tlsroute

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* test fixes

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* xds config tests for tls passthrough

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* increase test coverage

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* testfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* separate xds tls listener

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

testfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* additional xds validate tests

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* tlsroute refgrant test

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* add rbac permissions for tlsroute

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* updates post rebase

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* add status updater, gateway watcher for tlsroute

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* add status update framework for tlsroute

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* lintfix, testfix, fix post rebase

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* yet another lintfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* refactor tlslistener/route -> tcplistener/route, xds updates

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* missed a file

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* lintfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* rebase, review comments

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* minor testfix

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* more

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* review comments, status deepcopy, check routes in ns

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* revert bad import, testfix, new test

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

* rev sort

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>

Signed-off-by: Shubham Chauhan <shubham@tetrate.io>
  • Loading branch information
chauhanshubham authored Oct 11, 2022
1 parent 872c7f5 commit b9ed6df
Show file tree
Hide file tree
Showing 56 changed files with 3,517 additions and 258 deletions.
2 changes: 2 additions & 0 deletions internal/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ func setupRunners(cfg *config.Server) error {
pResources.Namespaces.Close()
pResources.GatewayStatuses.Close()
pResources.HTTPRouteStatuses.Close()
pResources.TLSRoutes.Close()
pResources.TLSRouteStatuses.Close()
xdsIR.Close()
infraIR.Close()
xds.Close()
Expand Down
6 changes: 5 additions & 1 deletion internal/envoygateway/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package envoygateway
import (
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1"
gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/envoyproxy/gateway/api/config/v1alpha1"
)
Expand All @@ -28,6 +29,9 @@ func init() {
if err := gwapiv1b1.AddToScheme(scheme); err != nil {
panic(err)
}
if err := gwapiv1a2.AddToScheme(scheme); err != nil {
panic(err)
}
}

// GetScheme returns a scheme with types supported by the Kubernetes provider.
Expand Down
215 changes: 192 additions & 23 deletions internal/gatewayapi/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/apis/v1beta1"

egv1alpha1 "github.com/envoyproxy/gateway/api/config/v1alpha1"
Expand All @@ -20,6 +22,10 @@ type GatewayContext struct {
listeners []*ListenerContext
}

// GetListenerContext returns the ListenerContext with listenerName.
// If the listener exists in the Gateway Spec but NOT yet in the GatewayContext,
// this creates a new ListenerContext for the listener and attaches it to the
// GatewayContext.
func (g *GatewayContext) GetListenerContext(listenerName v1beta1.SectionName) *ListenerContext {
if g.listeners == nil {
g.listeners = make([]*ListenerContext, 0)
Expand Down Expand Up @@ -169,6 +175,30 @@ func (l *ListenerContext) SetTLSSecret(tlsSecret *v1.Secret) {
l.tlsSecret = tlsSecret
}

// RouteContext represents a generic Route object (HTTPRoute, TLSRoute, etc.)
// that can reference Gateway objects.
type RouteContext interface {
client.Object

// GetRouteType returns the Kind of the Route object, HTTPRoute,
// TLSRoute, TCPRoute, UDPRoute etc.
GetRouteType() string

// TODO: [v1alpha2-v1beta1] This should not be required once all Route
// objects being implemented are of type v1beta1.
// GetHostnames returns the hosts targeted by the Route object.
GetHostnames() []string

// TODO: [v1alpha2-v1beta1] This should not be required once all Route
// objects being implemented are of type v1beta1.
// GetParentReferences returns the ParentReference of the Route object.
GetParentReferences() []v1beta1.ParentReference

// GetRouteParentContext returns RouteParentContext by using the Route
// objects' ParentReference.
GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext
}

// HTTPRouteContext wraps an HTTPRoute and provides helper methods for
// accessing the route's parents.
type HTTPRouteContext struct {
Expand All @@ -177,6 +207,22 @@ type HTTPRouteContext struct {
parentRefs map[v1beta1.ParentReference]*RouteParentContext
}

func (h *HTTPRouteContext) GetRouteType() string {
return KindHTTPRoute
}

func (h *HTTPRouteContext) GetHostnames() []string {
hostnames := make([]string, len(h.Spec.Hostnames))
for idx, s := range h.Spec.Hostnames {
hostnames[idx] = string(s)
}
return hostnames
}

func (h *HTTPRouteContext) GetParentReferences() []v1beta1.ParentReference {
return h.Spec.ParentRefs
}

func (h *HTTPRouteContext) GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext {
if h.parentRefs == nil {
h.parentRefs = make(map[v1beta1.ParentReference]*RouteParentContext)
Expand Down Expand Up @@ -217,20 +263,109 @@ func (h *HTTPRouteContext) GetRouteParentContext(forParentRef v1beta1.ParentRefe
ctx := &RouteParentContext{
ParentReference: parentRef,

route: h.HTTPRoute,
httpRoute: h.HTTPRoute,
routeParentStatusIdx: routeParentStatusIdx,
}
h.parentRefs[forParentRef] = ctx
return ctx
}

// TLSRouteContext wraps a TLSRoute and provides helper methods for
// accessing the route's parents.
type TLSRouteContext struct {
*v1alpha2.TLSRoute

parentRefs map[v1beta1.ParentReference]*RouteParentContext
}

func (t *TLSRouteContext) GetRouteType() string {
return KindTLSRoute
}

func (t *TLSRouteContext) GetHostnames() []string {
hostnames := make([]string, len(t.Spec.Hostnames))
for idx, s := range t.Spec.Hostnames {
hostnames[idx] = string(s)
}
return hostnames
}

func (t *TLSRouteContext) GetParentReferences() []v1beta1.ParentReference {
parentReferences := make([]v1beta1.ParentReference, len(t.Spec.ParentRefs))
for idx, p := range t.Spec.ParentRefs {
parentReferences[idx] = UpgradeParentReference(p)
}
return parentReferences
}

func (t *TLSRouteContext) GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext {
if t.parentRefs == nil {
t.parentRefs = make(map[v1beta1.ParentReference]*RouteParentContext)
}

if ctx := t.parentRefs[forParentRef]; ctx != nil {
return ctx
}

var parentRef *v1beta1.ParentReference
for i, p := range t.Spec.ParentRefs {
p := UpgradeParentReference(p)
if reflect.DeepEqual(p, forParentRef) {
upgraded := UpgradeParentReference(t.Spec.ParentRefs[i])
parentRef = &upgraded
break
}
}
if parentRef == nil {
panic("parentRef not found")
}

routeParentStatusIdx := -1
for i := range t.Status.Parents {
p := UpgradeParentReference(t.Status.Parents[i].ParentRef)
defaultNamespace := v1beta1.Namespace(metav1.NamespaceDefault)
if forParentRef.Namespace == nil {
forParentRef.Namespace = &defaultNamespace
}
if p.Namespace == nil {
p.Namespace = &defaultNamespace
}
if reflect.DeepEqual(p, forParentRef) {
routeParentStatusIdx = i
break
}
}
if routeParentStatusIdx == -1 {
rParentStatus := v1alpha2.RouteParentStatus{
// TODO: get this value from the config
ControllerName: v1alpha2.GatewayController(egv1alpha1.GatewayControllerName),
ParentRef: DowngradeParentReference(forParentRef),
}
t.Status.Parents = append(t.Status.Parents, rParentStatus)
routeParentStatusIdx = len(t.Status.Parents) - 1
}

ctx := &RouteParentContext{
ParentReference: parentRef,

tlsRoute: t.TLSRoute,
routeParentStatusIdx: routeParentStatusIdx,
}
t.parentRefs[forParentRef] = ctx
return ctx
}

// RouteParentContext wraps a ParentReference and provides helper methods for
// setting conditions and other status information on the associated
// HTTPRoute, etc.
// HTTPRoute, TLSRoute etc.
type RouteParentContext struct {
*v1beta1.ParentReference

route *v1beta1.HTTPRoute
// TODO: [v1alpha2-v1beta1] This can probably be replaced with
// a single field pointing to *v1beta1.RouteStatus.
httpRoute *v1beta1.HTTPRoute
tlsRoute *v1alpha2.TLSRoute

routeParentStatusIdx int
listeners []*ListenerContext
}
Expand All @@ -239,43 +374,77 @@ func (r *RouteParentContext) SetListeners(listeners ...*ListenerContext) {
r.listeners = append(r.listeners, listeners...)
}

func (r *RouteParentContext) SetCondition(conditionType v1beta1.RouteConditionType, status metav1.ConditionStatus, reason v1beta1.RouteConditionReason, message string) {
func (r *RouteParentContext) SetCondition(route RouteContext, conditionType v1beta1.RouteConditionType, status metav1.ConditionStatus, reason v1beta1.RouteConditionReason, message string) {
cond := metav1.Condition{
Type: string(conditionType),
Status: status,
Reason: string(reason),
Message: message,
ObservedGeneration: r.route.Generation,
ObservedGeneration: route.GetGeneration(),
LastTransitionTime: metav1.NewTime(time.Now()),
}

idx := -1
for i, existing := range r.route.Status.Parents[r.routeParentStatusIdx].Conditions {
if existing.Type == cond.Type {
// return early if the condition is unchanged
if existing.Status == cond.Status &&
existing.Reason == cond.Reason &&
existing.Message == cond.Message {
return
switch route.GetRouteType() {
case KindHTTPRoute:
for i, existing := range r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions {
if existing.Type == cond.Type {
// return early if the condition is unchanged
if existing.Status == cond.Status &&
existing.Reason == cond.Reason &&
existing.Message == cond.Message {
return
}
idx = i
break
}
idx = i
break
}
}

if idx > -1 {
r.route.Status.Parents[r.routeParentStatusIdx].Conditions[idx] = cond
} else {
r.route.Status.Parents[r.routeParentStatusIdx].Conditions = append(r.route.Status.Parents[r.routeParentStatusIdx].Conditions, cond)
if idx > -1 {
r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions[idx] = cond
} else {
r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions = append(r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions, cond)
}
case KindTLSRoute:
for i, existing := range r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions {
if existing.Type == cond.Type {
// return early if the condition is unchanged
if existing.Status == cond.Status &&
existing.Reason == cond.Reason &&
existing.Message == cond.Message {
return
}
idx = i
break
}
}

if idx > -1 {
r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions[idx] = cond
} else {
r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions = append(r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions, cond)
}
}
}

func (r *RouteParentContext) ResetConditions() {
r.route.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
func (r *RouteParentContext) ResetConditions(route RouteContext) {
switch route.GetRouteType() {
case KindHTTPRoute:
r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
case KindTLSRoute:
r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
}
}

func (r *RouteParentContext) IsAccepted() bool {
for _, cond := range r.route.Status.Parents[r.routeParentStatusIdx].Conditions {
func (r *RouteParentContext) IsAccepted(route RouteContext) bool {
var conditions []metav1.Condition
switch route.GetRouteType() {
case KindHTTPRoute:
conditions = r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions
case KindTLSRoute:
conditions = r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions
}
for _, cond := range conditions {
if cond.Type == string(v1beta1.RouteConditionAccepted) && cond.Status == metav1.ConditionTrue {
return true
}
Expand Down
11 changes: 8 additions & 3 deletions internal/gatewayapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ func FromNamespacesPtr(fromNamespaces v1beta1.FromNamespaces) *v1beta1.FromNames
return &fromNamespaces
}

func SectionNamePtr(name string) *v1beta1.SectionName {
sectionName := v1beta1.SectionName(name)
return &sectionName
}

func StringPtr(val string) *string {
return &val
}
Expand Down Expand Up @@ -138,9 +143,9 @@ func HasReadyListener(listeners []*ListenerContext) bool {
return false
}

// ComputeHosts returns a list of the intersecting hostnames between the route
// computeHosts returns a list of the intersecting hostnames between the route
// and the listener.
func ComputeHosts(routeHostnames []v1beta1.Hostname, listenerHostname *v1beta1.Hostname) []string {
func computeHosts(routeHostnames []string, listenerHostname *v1beta1.Hostname) []string {
var listenerHostnameVal string
if listenerHostname != nil {
listenerHostnameVal = string(*listenerHostname)
Expand All @@ -159,7 +164,7 @@ func ComputeHosts(routeHostnames []v1beta1.Hostname, listenerHostname *v1beta1.H
var hostnames []string

for i := range routeHostnames {
routeHostname := string(routeHostnames[i])
routeHostname := routeHostnames[i]

// TODO ensure routeHostname is a valid hostname

Expand Down
Loading

0 comments on commit b9ed6df

Please sign in to comment.