Skip to content

Commit

Permalink
publish default-ingress-ca that can be used to verify default routes …
Browse files Browse the repository at this point in the history
…in golang clients
  • Loading branch information
deads2k committed Dec 2, 2019
1 parent 1f864f6 commit 55a94f0
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 17 deletions.
34 changes: 31 additions & 3 deletions pkg/operator/controller/certificate/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"fmt"
"time"

"k8s.io/apimachinery/pkg/types"

logf "github.com/openshift/cluster-ingress-operator/pkg/log"
"github.com/openshift/cluster-ingress-operator/pkg/operator/controller"
ingresscontroller "github.com/openshift/cluster-ingress-operator/pkg/operator/controller/ingress"
Expand All @@ -21,6 +23,7 @@ import (
"k8s.io/client-go/tools/record"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"

operatorv1 "github.com/openshift/api/operator/v1"

Expand Down Expand Up @@ -64,7 +67,7 @@ type reconciler struct {
}

func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
ca, err := r.ensureRouterCASecret()
controllerMaintainedSigningCertKey, err := r.ensureRouterCASecret()
if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to ensure router CA: %v", err)
}
Expand Down Expand Up @@ -103,18 +106,43 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err
UID: deployment.UID,
Controller: &trueVar,
}
if _, err := r.ensureDefaultCertificateForIngress(ca, deployment.Namespace, deploymentRef, ingress); err != nil {
if _, err := r.ensureDefaultCertificateForIngress(controllerMaintainedSigningCertKey, deployment.Namespace, deploymentRef, ingress); err != nil {
errs = append(errs, fmt.Errorf("failed to ensure default cert for %s: %v", ingress.Name, err))
}
}
}

// This section creates the legacy router-ca configmap which only ever contains the operator-managed default signing
// cert used for signing the default wildcard serving cert
ingresses := &operatorv1.IngressControllerList{}
if err := r.cache.List(context.TODO(), ingresses, client.InNamespace(r.operatorNamespace)); err != nil {
errs = append(errs, fmt.Errorf("failed to list ingresscontrollers: %v", err))
} else if err := r.ensureRouterCAConfigMap(ca, ingresses.Items); err != nil {
} else if err := r.ensureRouterCAConfigMap(controllerMaintainedSigningCertKey, ingresses.Items); err != nil {
errs = append(errs, fmt.Errorf("failed to publish router CA: %v", err))
}

// We need to construct the CA bundle that can be used to verify the ingress used to serve the console and the oauth-server.
// In an operator maintained cluster, this is always `oc get -n openshift-ingress-operator ingresscontroller/default`, skip the rest and return here.
// TODO if network-edge wishes to expand the scope of the CA bundle (and you could legitimately see a need/desire to have one CA that verifies all ingress traffic).
// TODO this could be accomplished using union logic similar to the kube-apiserver's join of multiple CAs.
if ingress == nil || ingress.Namespace != "openshift-ingress-operator" || ingress.Name != "default" {
return result, utilerrors.NewAggregate(errs)
}

caBundle := string(controllerMaintainedSigningCertKey.Data["tls.crt"])
// if we have a custom default certificate, it need to be the one used to very router certificates
if ingress.Spec.DefaultCertificate != nil && len(ingress.Spec.DefaultCertificate.Name) > 0 {
secret := &corev1.Secret{}
if err := r.client.Get(context.TODO(), types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Spec.DefaultCertificate.Name}, secret); err != nil {
errs = append(errs, fmt.Errorf("failed to get custom router cert: %v", err))
return result, utilerrors.NewAggregate(errs)
}
caBundle = string(secret.Data["tls.crt"])
}
if err := r.ensureDefaultIngressCAConfigMap(caBundle); err != nil {
errs = append(errs, fmt.Errorf("failed to publish router CA: %v", err))
}

return result, utilerrors.NewAggregate(errs)

}
46 changes: 36 additions & 10 deletions pkg/operator/controller/certificate/publish_ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/types"

operatorv1 "github.com/openshift/api/operator/v1"
"github.com/openshift/cluster-ingress-operator/pkg/operator/controller"

Expand All @@ -12,14 +14,35 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ensureDefaultIngressCAConfigMap will create or update the configmap for verifying the default ingress wildcard certificate
func (r *reconciler) ensureDefaultIngressCAConfigMap(caBundle string) error {
name := controller.DefaultIngressCAConfigMapName()
desired := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: name.Name,
Namespace: name.Namespace,
},
Data: map[string]string{
"ca-bundle.crt": caBundle,
},
}
return r.ensureConfigMap(desired)

}

// ensureRouterCAConfigMap will create, update, or delete the configmap for the
// router CA as appropriate.
func (r *reconciler) ensureRouterCAConfigMap(secret *corev1.Secret, ingresses []operatorv1.IngressController) error {
desired, err := desiredRouterCAConfigMap(secret, ingresses)
if err != nil {
return err
}
current, err := r.currentRouterCAConfigMap()
return r.ensureConfigMap(desired)
}

// ensureConfigMap will create, update, or delete the configmap as appropriate.
func (r *reconciler) ensureConfigMap(desired *corev1.ConfigMap) error {
current, err := r.currentConfigMap(desired)
if err != nil {
return err
}
Expand All @@ -28,25 +51,25 @@ func (r *reconciler) ensureRouterCAConfigMap(secret *corev1.Secret, ingresses []
// Nothing to do.
case desired == nil && current != nil:
if deleted, err := r.deleteRouterCAConfigMap(current); err != nil {
return fmt.Errorf("failed to ensure router CA was unpublished: %v", err)
return fmt.Errorf("failed to ensure %q in %q was unpublished: %v", desired.Name, desired.Namespace, err)
} else if deleted {
r.recorder.Eventf(current, "Normal", "UnpublishedDefaultRouterCA", "Unpublished default router CA")
r.recorder.Eventf(current, "Normal", "UnpublishedRouterCA", "Unpublished %q in %q", desired.Name, desired.Namespace)
}
case desired != nil && current == nil:
if created, err := r.createRouterCAConfigMap(desired); err != nil {
return fmt.Errorf("failed to ensure router CA was published: %v", err)
return fmt.Errorf("failed to ensure %q in %q was published: %v", desired.Name, desired.Namespace, err)
} else if created {
new, err := r.currentRouterCAConfigMap()
new, err := r.currentConfigMap(desired)
if err != nil {
return err
}
r.recorder.Eventf(new, "Normal", "PublishedDefaultRouterCA", "Published default router CA")
r.recorder.Eventf(new, "Normal", "PublishedRouterCA", "Published %q in %q", desired.Name, desired.Namespace)
}
case desired != nil && current != nil:
if updated, err := r.updateRouterCAConfigMap(current, desired); err != nil {
return fmt.Errorf("failed to update published router CA: %v", err)
return fmt.Errorf("failed to update published %q in %q: %v", desired.Name, desired.Namespace, err)
} else if updated {
r.recorder.Eventf(current, "Normal", "UpdatedPublishedDefaultRouterCA", "Updated the published default router CA")
r.recorder.Eventf(current, "Normal", "UpdatedPublishedRouterCA", "Updated the published %q in %q", desired.Name, desired.Namespace)
}
}
return nil
Expand Down Expand Up @@ -83,8 +106,11 @@ func shouldPublishRouterCA(ingresses []operatorv1.IngressController) bool {
}

// currentRouterCAConfigMap returns the current router CA configmap.
func (r *reconciler) currentRouterCAConfigMap() (*corev1.ConfigMap, error) {
name := controller.RouterCAConfigMapName()
func (r *reconciler) currentConfigMap(desired *corev1.ConfigMap) (*corev1.ConfigMap, error) {
name := types.NamespacedName{
Namespace: desired.Namespace,
Name: desired.Name,
}
cm := &corev1.ConfigMap{}
if err := r.client.Get(context.TODO(), name, cm); err != nil {
if errors.IsNotFound(err) {
Expand Down
10 changes: 10 additions & 0 deletions pkg/operator/controller/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ func RouterCAConfigMapName() types.NamespacedName {
}
}

// DefaultIngressCAConfigMapName returns the namespaced name for the default ingress CA configmap.
// The operator uses this configmap to publish the public key that golang clients can use to trust
// the default ingress wildcard serving cert.
func DefaultIngressCAConfigMapName() types.NamespacedName {
return types.NamespacedName{
Namespace: GlobalMachineSpecifiedConfigNamespace,
Name: "default-ingress-ca",
}
}

// RouterCertsGlobalSecretName returns the namespaced name for the router certs
// secret. The operator uses this secret to publish the default certificates and
// their keys, so that the authentication operator can configure the OAuth server
Expand Down
40 changes: 36 additions & 4 deletions test/e2e/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,12 @@ func TestUpdateDefaultIngressController(t *testing.T) {
t.Fatalf("failed to observe expected conditions: %v", err)
}

configmap := &corev1.ConfigMap{}
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), configmap); err != nil {
routerCAConfigmap := &corev1.ConfigMap{}
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), routerCAConfigmap); err != nil {
t.Fatalf("failed to get CA certificate configmap: %v", err)
}
defaultIngressCAConfigmap := &corev1.ConfigMap{}
if err := kclient.Get(context.TODO(), controller.DefaultIngressCAConfigMapName(), defaultIngressCAConfigmap); err != nil {
t.Fatalf("failed to get CA certificate configmap: %v", err)
}

Expand Down Expand Up @@ -244,7 +248,7 @@ func TestUpdateDefaultIngressController(t *testing.T) {

// Wait for the CA certificate configmap to be deleted.
err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), configmap); err != nil {
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), routerCAConfigmap); err != nil {
if errors.IsNotFound(err) {
return true, nil
}
Expand All @@ -255,6 +259,20 @@ func TestUpdateDefaultIngressController(t *testing.T) {
if err != nil {
t.Fatalf("failed to observe clean-up of CA certificate configmap: %v", err)
}
// Wait for the default ingress configmap to be updated
originalDefaultIngressCAConfigmap := defaultIngressCAConfigmap.DeepCopy()
err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
if err := kclient.Get(context.TODO(), controller.DefaultIngressCAConfigMapName(), defaultIngressCAConfigmap); err != nil {
return false, err
}
if defaultIngressCAConfigmap.Data["ca-bundle.crt"] == originalDefaultIngressCAConfigmap.Data["ca-bundle.crt"] {
return false, nil
}
return true, nil
})
if err != nil {
t.Fatalf("failed to observe update of default ingress CA certificate configmap: %v", err)
}

// Reset .spec.defaultCertificate to its original value.
if err := kclient.Get(context.TODO(), defaultName, ic); err != nil {
Expand All @@ -267,7 +285,7 @@ func TestUpdateDefaultIngressController(t *testing.T) {

// Wait for the CA certificate configmap to be recreated.
err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), configmap); err != nil {
if err := kclient.Get(context.TODO(), controller.RouterCAConfigMapName(), routerCAConfigmap); err != nil {
if !errors.IsNotFound(err) {
t.Logf("failed to get CA certificate configmap, will retry: %v", err)
}
Expand All @@ -278,6 +296,20 @@ func TestUpdateDefaultIngressController(t *testing.T) {
if err != nil {
t.Fatalf("failed to get recreated CA certificate configmap: %v", err)
}
// Wait for the default ingress configmap to be updated back to the original
err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
if err := kclient.Get(context.TODO(), controller.DefaultIngressCAConfigMapName(), defaultIngressCAConfigmap); err != nil {
return false, err
}
if defaultIngressCAConfigmap.Data["ca-bundle.crt"] == originalDefaultIngressCAConfigmap.Data["ca-bundle.crt"] {
return true, nil
}
return false, nil
})
if err != nil {
t.Fatalf("failed to observe update of default ingress CA certificate configmap: %v", err)
}

}

// TestIngressControllerScale exercises a simple scale up/down scenario.
Expand Down

0 comments on commit 55a94f0

Please sign in to comment.