Skip to content

Commit

Permalink
Support ssl annotations in nextGenRoutes (#2648)
Browse files Browse the repository at this point in the history
  • Loading branch information
arzzon authored Dec 1, 2022
1 parent 973d47c commit e116787
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 246 deletions.
3 changes: 2 additions & 1 deletion docs/RELEASE-NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ Added Functionality
* Next generation routes preview. Refer `Documentation <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes>`_ for more details
* Support for health monitors using route annotations See `Examples <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes/routes>`_
* Policy CR integration with extended ConfigMap
* Support for TLS profiles as K8S secrets in next generation routes. See `Example <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes/configmap>`_
* Support for TLS profiles as K8S secrets in route annotations. See `Examples <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes/routes>`_
* Support Path based A/B deployment for Re-encrypt termination
* Support to create Health Monitor from the pod liveness probe that route exposes. Refer `Documentation <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes>`_ for more details
* Support for Default SSL profiles from baseRouteSpec in extended Configmap
* GSLB support for routes in AS3 mode
* Support for TLS profiles as route annotations. See `Examples <https://github.com/F5Networks/k8s-bigip-ctlr/tree/master/docs/config_examples/next-gen-routes/routes>`_
* CRD
* CIS configures GTM configuration in default partition
* Pool reselect support for VS and TS
Expand Down
17 changes: 0 additions & 17 deletions docs/config_examples/next-gen-routes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,27 +101,10 @@ Base route configuration can be defined in Global ConfigMap. This cannot be over
| namespace | Mandatory | namespace to group the routes | - | Local and Global configMap |
| vsAddress | Mandatory | BigIP Virtual Server IP Address | - | Local and Global configMap |
| vsName | Optional | Name of BigIP Virtual Server | auto | Local and Global configMap |
| tls | Optional | Dictionary of client and server SSL profiles (See next section). | - | Local and Global configMap |

**Note**: 1. namespaceLabel is mutually exclusive with namespace parameter.
2. --namespace-label parameter has to be defined in CIS deployment to use the namespaceLabel in extended configMap.

#### TLS Config Parameters

| Parameter | Required | Description | Default | ConfigMap |
| --------- | -------- | ----------- | ------- | --------- |
| clientSSL | Optional | client SSL profile | - | Local and Global configMap |
| serverSSL | Optional | server SSL profile | - | Local and Global configMap |
| reference | Mandatory | Profile Object type | - | Local and Global configMap |

* tls schema:
```
tls:
clientSSL: /Common/clientssl
serverSSL: /Common/serverssl
reference: bigip
```

## Example Global & Local ConfigMap with namespace parameter
**Example: Global Configmap**
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ data:
vserverAddr: 10.8.3.11
vserverName: nextgenroutes
allowOverride: true
tls:
clientSSL: /Common/clientssl
serverSSL: /Common/serverssl
reference: bigip
- namespace: new
vserverAddr: 10.8.3.12
allowOverride: true

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ data:
vserverAddr: 10.8.0.4
vserverName: nextgenroutes
allowOverride: true
tls:
clientSSL: /Common/clientssl
serverSSL: /Common/serverssl
reference: bigip
- allowOverride: false
namespace: bar
vserverAddr: 10.8.0.5
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: route.openshift.io/v1
kind: Route
metadata:
annotations:
virtual-server.f5.com/clientssl: /Common/foo-clientssl
virtual-server.f5.com/health: '[{"path": "pytest-foo-1.com/","send": "HTTP GET
pytest-foo-1.com/", "recv": "","interval": 2,"timeout": 5, "type": "http"}]'
labels:
f5type: systest
name: svc-pytest-foo-1-com
namespace: foo
spec:
host: pytest-foo-1.com
path: /
tls:
termination: edge
to:
kind: Service
name: svc-pytest-foo-1-com
weight: 100
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: route.openshift.io/v1
kind: Route
metadata:
annotations:
virtual-server.f5.com/clientssl: clientssl-secret
virtual-server.f5.com/health: '[{"path": "pytest-foo-1.com/","send": "HTTP GET
pytest-foo-1.com/", "recv": "","interval": 2,"timeout": 5, "type": "http"}]'
labels:
f5type: systest
name: svc-pytest-foo-1-com
namespace: foo
spec:
host: pytest-foo-1.com
path: /
tls:
termination: edge
to:
kind: Service
name: svc-pytest-foo-1-com
weight: 100
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: route.openshift.io/v1
kind: Route
metadata:
annotations:
virtual-server.f5.com/clientssl: /Common/foo-clientssl
virtual-server.f5.com/serverssl: /Common/foo-serverssl
virtual-server.f5.com/health: '[{"path": "pytest-foo-1.com/","send": "HTTP GET
pytest-foo-1.com/", "recv": "","interval": 2,"timeout": 5, "type": "https"}]'
labels:
f5type: systest
name: svc-pytest-foo-1-com
namespace: foo
spec:
host: pytest-foo-1.com
path: /
tls:
termination: reencrypt
to:
kind: Service
name: svc-pytest-foo-1-com
weight: 100
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: route.openshift.io/v1
kind: Route
metadata:
annotations:
virtual-server.f5.com/clientssl: clientssl-secret
virtual-server.f5.com/serverssl: serverssl-secret
virtual-server.f5.com/health: '[{"path": "pytest-foo-1.com/","send": "HTTP GET
pytest-foo-1.com/", "recv": "","interval": 2,"timeout": 5, "type": "https"}]'
labels:
f5type: systest
name: svc-pytest-foo-1-com
namespace: foo
spec:
host: pytest-foo-1.com
path: /
tls:
termination: reencrypt
to:
kind: Service
name: svc-pytest-foo-1-com
weight: 100
66 changes: 27 additions & 39 deletions pkg/controller/nativeResourceWorker.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (ctlr *Controller) processRoutes(routeGroup string, triggerDelete bool) err
return fmt.Errorf("extended Route Spec not available for RouteGroup/Namespace: %v", routeGroup)
}

routes := ctlr.getGroupedRoutes(routeGroup, extdSpec)
routes := ctlr.getGroupedRoutes(routeGroup)

if triggerDelete || len(routes) == 0 {
// Delete all possible virtuals for this route group
Expand Down Expand Up @@ -108,7 +108,7 @@ func (ctlr *Controller) processRoutes(routeGroup string, triggerDelete bool) err

if isSecureRoute(rt) {
//TLS Logic
processed := ctlr.handleRouteTLS(rsCfg, rt, extdSpec.VServerAddr, servicePort, extdSpec)
processed := ctlr.handleRouteTLS(rsCfg, rt, extdSpec.VServerAddr, servicePort)
if !processed {
// Processing failed
// Stop processing further routes
Expand Down Expand Up @@ -158,7 +158,7 @@ func (ctlr *Controller) processRoutes(routeGroup string, triggerDelete bool) err
return nil
}

func (ctlr *Controller) getGroupedRoutes(routeGroup string, extdSpec *ExtendedRouteGroupSpec) []*routeapi.Route {
func (ctlr *Controller) getGroupedRoutes(routeGroup string) []*routeapi.Route {
var assocRoutes []*routeapi.Route
// Get the route group
for _, namespace := range ctlr.resources.extdSpecMap[routeGroup].namespaces {
Expand All @@ -168,7 +168,7 @@ func (ctlr *Controller) getGroupedRoutes(routeGroup string, extdSpec *ExtendedRo
ctlr.TeemData.Unlock()
for _, route := range orderedRoutes {
// TODO: add combinations for a/b - svc weight ; valid svcs or not
if ctlr.checkValidRoute(route, extdSpec) {
if ctlr.checkValidRoute(route) {
var key string
if route.Spec.Path == "/" || len(route.Spec.Path) == 0 {
key = route.Spec.Host + "/"
Expand Down Expand Up @@ -1308,7 +1308,7 @@ func (ctlr *Controller) fetchRoute(rscKey string) *routeapi.Route {
return obj.(*routeapi.Route)
}

func (ctlr *Controller) checkValidRoute(route *routeapi.Route, extdSpec *ExtendedRouteGroupSpec) bool {
func (ctlr *Controller) checkValidRoute(route *routeapi.Route) bool {
// Validate the hostpath
ctlr.processedHostPath.Lock()
defer ctlr.processedHostPath.Unlock()
Expand All @@ -1327,22 +1327,26 @@ func (ctlr *Controller) checkValidRoute(route *routeapi.Route, extdSpec *Extende
return false
}
}

// If TLS reference of type BigIP/Secret is configured in ConfigMap, fetch Client and Server SSL profile references
if extdSpec != nil && extdSpec.TLS != (TLS{}) && (extdSpec.TLS.Reference == BIGIP || extdSpec.TLS.Reference == Secret) &&
route.Spec.TLS.Termination != routeapi.TLSTerminationPassthrough {
if extdSpec.TLS.ClientSSL == "" {
message := fmt.Sprintf("Missing client SSL profile %s reference in the ConfigMap", extdSpec.TLS.Reference)
sslProfileOption := ctlr.getSSLProfileOption(route)
switch sslProfileOption {
case "":
break
case AnnotationSSLOption:
if _, ok := route.ObjectMeta.Annotations[resource.F5ServerSslProfileAnnotation]; !ok && route.Spec.TLS.Termination == routeapi.TLSTerminationReencrypt {
message := fmt.Sprintf("Missing server SSL profile in the annotation")
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}
if extdSpec.TLS.ServerSSL == "" && route.Spec.TLS.Termination == routeapi.TLSTerminationReencrypt {
message := fmt.Sprintf("Missing server SSL profile %s reference in the ConfigMap", extdSpec.TLS.Reference)
case RouteCertificateSSLOption:
// Validate vsHostname if certificate is not provided in SSL annotations
ok := checkCertificateHost(route.Spec.Host, []byte(route.Spec.TLS.Certificate), []byte(route.Spec.TLS.Key))
if !ok {
//Invalid certificate and key
message := fmt.Sprintf("Invalid certificate and key for route: %v", route.ObjectMeta.Name)
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}
} else if ctlr.resources.baseRouteConfig != (BaseRouteConfig{}) && ctlr.resources.baseRouteConfig.DefaultTLS != (DefaultSSLProfile{}) &&
ctlr.resources.baseRouteConfig.DefaultTLS.Reference == BIGIP && route.Spec.TLS.Termination != routeapi.TLSTerminationPassthrough {
case DefaultSSLOption:
if ctlr.resources.baseRouteConfig.DefaultTLS.ClientSSL == "" {
message := fmt.Sprintf("Missing client SSL profile %s reference in the ConfigMap - BaseRouteSpec", ctlr.resources.baseRouteConfig.DefaultTLS.Reference)
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
Expand All @@ -1353,22 +1357,12 @@ func (ctlr *Controller) checkValidRoute(route *routeapi.Route, extdSpec *Extende
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}
} else if nil != route.Spec.TLS && route.Spec.TLS.Termination != routeapi.TLSTerminationPassthrough {
if route.Spec.TLS.Certificate != "" && route.Spec.TLS.Key != "" {
// Validate vsHostname if certificate is not provided in SSL annotations
ok := checkCertificateHost(route.Spec.Host, []byte(route.Spec.TLS.Certificate), []byte(route.Spec.TLS.Key))
if !ok {
//Invalid certificate and key
message := fmt.Sprintf("Invalid certificate and key for route: %v", route.ObjectMeta.Name)
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}
} else {
message := fmt.Sprintf("Missing certificate/key for route: %v", route.ObjectMeta.Name)
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}
default:
message := fmt.Sprintf("Missing certificate/key/SSL profile annotation/defaultSSL for route: %v", route.ObjectMeta.Name)
go ctlr.updateRouteAdmitStatus(fmt.Sprintf("%v/%v", route.Namespace, route.Name), "ExtendedValidationFailed", message, v1.ConditionFalse)
return false
}

// Validate the route service exists or not
err, _ := ctlr.getServicePort(route)
if err != nil {
Expand Down Expand Up @@ -1474,15 +1468,9 @@ func (ctlr *Controller) getRouteGroupForSecret(secret *v1.Secret) string {
if extdSpec == nil || extdSpec.global == nil {
continue
}
// Skip routeGroups with no TLS and TLS with reference other than secret
if extdSpec.global.TLS == (TLS{}) || extdSpec.global.TLS.Reference != Secret {
continue
}
// Check if the updated secret is used in any RouteGroup and, belongs to the same namespace as the RouteGroup
if extdSpec.global.TLS.ServerSSL == secret.Name || extdSpec.global.TLS.ClientSSL == secret.Name {
if ctlr.resources.invertedNamespaceLabelMap[secret.Namespace] == rg {
return rg
}
// Check if namespace of the secret matches with the namespace of the routes defined in a route group
if ctlr.resources.invertedNamespaceLabelMap[secret.Namespace] == rg {
return rg
}
}
return ""
Expand Down
Loading

0 comments on commit e116787

Please sign in to comment.