Skip to content

Conversation

@jhadvig
Copy link
Member

@jhadvig jhadvig commented Mar 26, 2020

Adding option do specify the custom hostname for console.
When when custom hostname is specified a new console-custom route is created. In case the new hostname doesnt contain the cluster domain as a suffix, secret reference needs to be specified, that points to the secret in the openshift-config namespace, where it needs to be created manually and contain tls cert and its key, which will serve as a serving-cert for the console pod. Also we will maintain the default route so it stays reserved for us.

/assign @benjaminapetersen

API changes - openshift/api#594

@openshift-ci-robot openshift-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 26, 2020
return nil, true, "InvalidCustomRouteConfig", configErr
}

if scscErr := c.SyncCustomServigCertSecret(operatorConfig); scscErr != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: SyncCustomServigCertSecret has Servig, but should be Serving.

c.CheckRouteHealth(operatorConfig, cr)

if _, changed := routesub.Validate(cr); changed {
return nil, true, "InvalidCustomRoute", customerrors.NewSyncError("custom route is invalid, correcting route state")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this deviates a little bit from the Validate() in SyncRoute(). Looking into this a bit to make sure its ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought that since the Validate() is just checking the To, Port, TLS, WildcardPolicy then it cant hurt to check those on the custom route, since these should be the same

}
// if no secret with serving-cert provided, sync an empty source to delete
return c.resourceSyncer.SyncSecret(
resourcesynccontroller.ResourceLocation{Namespace: api.OpenShiftConsoleNamespace, Name: api.OpenShiftCustomLogoConfigMapName},
Copy link
Contributor

@benjaminapetersen benjaminapetersen Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prob should update api.OpenShiftCustomLogoConfigMapName to api.CustomRouteServingCertName.
I dont think we want to call this secret "custom-logo".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep ! will update !

return false
}

func ValidateCustomRoute(operatorConfig *operatorv1.Console, client routeclient.RoutesGetter) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow the logic entirely here.
Its not really validating the custom route its.... checking if the custom is still on the default ingress domain?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, probably bad naming hare. Maybe isDefaultIngressDomain ?

return err
}

if is := isClusterDomain(operatorConfig.Spec.Route.Hostname, defaultRoute.Spec.Host); !is {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question here is, do we trust the default route, or should we pull in the Ingress Config?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think we can trust the default route, since we will be maintaining it anyways. Also when creating we are using the default as the router name. Otherwise we are raising a sync error.
Should be ok then ?

return err
}

func isClusterDomain(customRouteHost, defaultRouteHost string) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is answering "Is the custom host still on the default ingress domain"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

return route, changed
}

func IsCustomRouteSet(operatorConfig *operatorv1.Console) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will work:

func IsCustomRouteSet(operatorConfig *operatorv1.Console) bool {
	return operatorConfig.Spec.Route.Hostname) != 0
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitely 👍

}

// Check if reference for secret holding custom serving-cert is set
func IsCustomRouteSecretSet(operatorConfig *operatorv1.Console) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func IsCustomRouteSecretSet(operatorConfig *operatorv1.Console) bool {
	return len(operatorConfig.Spec.Route.Secret.Name) != 0
}

path: "/var/logo/",
isConfigMap: true}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to overwrite the existing serving cert with the custom, so we don't have to change the impl in the console to look at a different dir, correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes
Thought that we should not need both serving-certs

Copy link
Contributor

@stlaz stlaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find an enhancement for this, but I don't understand why you should keep two routes instead of just one. Are both going to be functional at the same time?

Comment on lines 137 to 141
func (c *RouteSyncController) removeRoute(routeName string) error {
klog.V(2).Infof("deleting %s route", routeName)
defer klog.V(2).Infof("finished deleting %s route", routeName)
return c.routeClient.Routes(c.targetNamespace).Delete(routeName, &metav1.DeleteOptions{})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can keep just this function instead of having two, just

func (c *RouteSyncController) removeRoute(routeName string) error {
    klog.V(2).Infof("deleting %s route", routeName)
    err := c.routeClient.Routes(c.targetNamespace).Delete(routeName, &metav1.DeleteOptions{})
    if apierrors.IsNotFound(err) {
        return nil
    }
    return err
}

You may want to get rid of the defer log because it would appear even in the case of an error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, you would want to remove the other log line as well as that would appear on every sync even if the route was already removed...

// 4. check custom route health
// 5. validate the route
func (c *RouteSyncController) SyncCustomRoute(operatorConfig *operatorsv1.Console) (consoleRoute *routev1.Route, isNew bool, reason string, err error) {
if configErr := routesub.ValidateCustomRoute(operatorConfig, c.routeClient); configErr != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Ben suggested, move the IsCustomRouteSet() check here and return early if false

return cr, crIsNew, "", crErr
}

func (c *RouteSyncController) SyncCustomServigCertSecret(operatorConfig *operatorsv1.Console) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you syncing this? Also, I'd advise to use the name "RouteCert" instead of serving-cert because the latter is more commonly used for the certificate the pod uses for serving, rather than the certificate the route uses.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you syncing this?

we need to since the secret that contains the custom serving-cert from openshift-config, where the user/admin needs to create it manually, into the openshift-console, so we can mount it into the console pod

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you don't want to mount it in the pod, you want to have that inserted into the route

tolerationSeconds := int64(120)
volumeConfig := defaultVolumeConfig()
if routesub.IsCustomRouteSecretSet(operatorConfig) {
volumeConfig = useCustomServingCertSecret(operatorConfig.Spec.Route.Secret.Name, volumeConfig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you mounting this? The contents are supposed to be used in the route's spec.tls.termination.{cert,key,caCertificate} fields

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, you should have a hard requirement on the secret you're syncing to contain all of tls.crt, tls.key and ca.crt fields

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you mounting this? The contents are supposed to be used in the route's spec.tls.termination.{cert,key,caCertificate} fields

I though that the secret needs to be mounted into the console pod.

why are you mounting this? The contents are supposed to be used in the route's spec.tls.termination.{cert,key,caCertificate} fields

also ca.crt?
all of them needs to be used in the route's spec.tls.termination.{cert,key,caCertificate} fields?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you would want to have the CA certificate there as well to prevent issues that the routing team has - openshift/cluster-ingress-operator#329. Generally, it would make your life easier.

Also, you should validate the secret and don't progress further if it's invalid.

Labels: util.SharedLabels(),
Annotations: map[string]string{},
},
Spec: spec(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where's all the fancy certificate stuff you're doing this for?

@jhadvig
Copy link
Member Author

jhadvig commented Mar 27, 2020

@stlaz so to point of having two routes is to have the default one reserved, in case that somebody could create one which would block admitting the default route if recreated. The default one will be active only in case that the custom route suffix will match the cluster domain

@jhadvig jhadvig force-pushed the customURL branch 2 times, most recently from 893d7c8 to f9dee4a Compare March 30, 2020 15:53
@jhadvig
Copy link
Member Author

jhadvig commented Mar 30, 2020

@stlaz @benjaminapetersen I've update the PR based on your comments.
Removed mounting the secret as a serving-cert into the console pod and instead setting the cert and key on the custom route.

PTAL

@jhadvig
Copy link
Member Author

jhadvig commented Mar 30, 2020

The unit tests are failing cause will update them after after we align on the logic, since that will be pretty big change itself

Copy link
Contributor

@stlaz stlaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs tests

@jhadvig jhadvig changed the title [WIP] Custom console route Custom console route Apr 6, 2020
@openshift-ci-robot openshift-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 6, 2020
@jhadvig
Copy link
Member Author

jhadvig commented Apr 6, 2020

@benjaminapetersen @stlaz comments addressed. PTAL

return nil, err
}
// Check if the custom hostname has cluster domain suffix. If it does,
// then the secret that contains TLS certificate and key needs to be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by the comment, it implies something should be done, but it immediately returns nil, nil.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+ what if the user just wanted their custom certificate for the route in cluster domain?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is wrong... if the suffix matches cluster domain then the secret is not necessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the user may still want their certificate to be used instead of the ones that the router adds by default, correct?

operatorConfigClient.OperatorV1().Consoles(),
routesClient.RouteV1(),
kubeClient.CoreV1(),
kubeClient.CoreV1(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only need to pass kubeClient.CoreV1() once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see what we did, we have our clients slightly misnamed. Hmm. And we do it in all our controllers. :)

- route.openshift.io
resources:
- routes
- routes/custom-host
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to set the route.spec.tls fields

Copy link
Contributor

@stlaz stlaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getting there

return nil, true, "InvalidRoute", customerrors.NewSyncError("route is invalid, correcting route state")
return nil, nil
}
customCertSecret, err := c.secretClient.Secrets(api.OpenShiftConfigNamespace).Get(c.ctx, operatorConfig.Spec.Route.Secret.Name, metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just return c.secretClient.Secrets(api.OpenShiftConfigNamespace).Get(c.ctx, operatorConfig.Spec.Route.Secret.Name, metav1.GetOptions{})

}
// abort sync, route changed, let it settle & retry
return nil, true, "InvalidRoute", customerrors.NewSyncError("route is invalid, correcting route state")
return nil, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like I said - I configured my custom route within the cluster domain and configured a custom cert I want it to use, why don't you honor it?

Comment on lines 287 to 285
block = &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you doing this?

func certificateVerifier(customCert []byte) (*pem.Block, error) {
block, _ := pem.Decode([]byte(customCert))
if block == nil {
return nil, fmt.Errorf("failed to parse custom certificate")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

("failed to decode certificate PEM: %v", err) please

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, nvm, pem.Decode does not return error, my bad

Comment on lines 309 to 324
switch t := key.(type) {
case *rsa.PrivateKey:
block = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(t),
}
case *ecdsa.PrivateKey:
block = &pem.Block{
Type: "ECDSA PRIVATE KEY",
}
if block.Bytes, err = x509.MarshalECPrivateKey(t); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("block private key %T is not valid", key)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you doing this?

existingCopy := existing.DeepCopy()

if len(GetCanonicalHost(existingCopy)) == 0 {
return nil, false, customerrors.NewSyncError(fmt.Sprintf("route is not available at canonical host %s", existingCopy.Status.Ingress))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is no place for this check though, you should be able to apply the route no matter what its status is.

Also, are you 100% sure that this will be valid even for routes outside the cluster domain?


const (
validCertificate = `
-----BEGIN CERTIFICATE-----
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I completely forgot you should also be checking at least the expiry date. For example, this certificate appears to have expired 4 years ago.

            Not Before: Apr  5 15:15:55 2013 GMT
            Not After : Apr  4 15:15:55 2015 GM

@jhadvig jhadvig force-pushed the customURL branch 5 times, most recently from d45be3d to 85c0790 Compare April 16, 2020 16:54
- ""
resources:
- configmaps
- secrets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too bad we have to add this, I liked that we didn't read secrets from here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that we ARE reading them all, just that now we can. but we are only doing a GET on the one that matters to us.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other solution would be to have the secret name as API, then we could specify only that single one... but not sure if thats worth it.

Copy link
Contributor

@benjaminapetersen benjaminapetersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/approve

So long as @stlaz is happy, we can tag this!

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 16, 2020
@jhadvig
Copy link
Member Author

jhadvig commented Apr 16, 2020

/retest

Copy link
Contributor

@stlaz stlaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a few more cosmetic changes, otherwise LGTM

v1 "github.com/openshift/client-go/operator/informers/externalversions/operator/v1"
routeclientv1 "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1"
routesinformersv1 "github.com/openshift/client-go/route/informers/externalversions/route/v1"
customerrors "github.com/openshift/console-operator/pkg/console/errors"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should have stayed below

Comment on lines 243 to 245
if len(customCertSecret.Data) == 0 {
return nil, fmt.Errorf("custom cert secret doesn't contain 'Data' field")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably don't need to check this since you're checking the presence of the interesting keys later on

Comment on lines 297 to 298
_, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
_, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
_, err = x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return fmt.Errorf("block %s is not valid", block.Type)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

if _, err := x509.x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
    if _, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
    ...
    }
}

if err != nil {
_, err = x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return fmt.Errorf("block %s is not valid", block.Type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is not a valid private key PEM

@jhadvig
Copy link
Member Author

jhadvig commented Apr 17, 2020

@stlaz comments addressed.. lets merge this baby 😄

@stlaz
Copy link
Contributor

stlaz commented Apr 17, 2020

/lgtm
🥳

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 17, 2020
@openshift-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: benjaminapetersen, jhadvig, stlaz

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@stlaz
Copy link
Contributor

stlaz commented Apr 17, 2020

/retest

2 similar comments
@spadgett
Copy link
Member

/retest

@jhadvig
Copy link
Member Author

jhadvig commented Apr 17, 2020

/retest

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

6 similar comments
@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest

Please review the full test history for this PR and help us cut down flakes.

@openshift-merge-robot openshift-merge-robot merged commit 299b5c4 into openshift:master Apr 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants