Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add trusted CA bundle support for OpenShift #1079

Merged
merged 4 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions pkg/config/ca/ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package ca

import (
"fmt"
"strings"

"github.com/spf13/viper"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)

// Get returns a trusted CA bundle configmap if platform is OpenShift
func Get(jaeger *v1.Jaeger) *corev1.ConfigMap {
// Only configure the trusted CA if running in OpenShift
if viper.GetString("platform") != v1.FlagPlatformOpenShift {
return nil
}

if !deployTrustedCA(jaeger) {
jaeger.Logger().Debug("CA: Skip deploying the Jaeger instance's trustedCABundle configmap")
return nil
}

jaeger.Logger().Debug("CA: Creating the trustedCABundle configmap")
trueVar := true

name := TrustedCAName(jaeger)
labels := util.Labels(name, "ca-configmap", *jaeger)
labels["config.openshift.io/inject-trusted-cabundle"] = "true"

// See https://docs.openshift.com/container-platform/4.4/networking/configuring-a-custom-pki.html#certificate-injection-using-operators_configuring-a-custom-pki
return &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: jaeger.Namespace,
Labels: labels,
OwnerReferences: []metav1.OwnerReference{{
APIVersion: jaeger.APIVersion,
Kind: jaeger.Kind,
Name: jaeger.Name,
UID: jaeger.UID,
Controller: &trueVar,
}},
},
Data: map[string]string{
"ca-bundle.crt": "",
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests are showing that this is fine, but I wonder if there's a reason to have this as empty first? Also, would the reconciliation phase revert this to empty? If so, OpenShift seems to be refilling this quite fast (possibly with a webhook, before changes are actually applied).

Copy link
Contributor

Choose a reason for hiding this comment

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

might be an idea to check with kubectl get -w configmap -o yaml - could be a potencial race?

},
}
}

// Update will modify the supplied common spec, to include
// trusted CA bundle volume and volumeMount, if running on OpenShift
func Update(jaeger *v1.Jaeger, commonSpec *v1.JaegerCommonSpec) {
// Only configure the trusted CA if running in OpenShift
if viper.GetString("platform") != v1.FlagPlatformOpenShift {
return
}

if !deployTrustedCA(jaeger) {
jaeger.Logger().Debug("CA: Skip adding the Jaeger instance's trustedCABundle volume")
return
}

volume := corev1.Volume{
Name: TrustedCAName(jaeger),
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: TrustedCAName(jaeger),
},
Items: []corev1.KeyToPath{{
Key: "ca-bundle.crt",
Path: "tls-ca-bundle.pem",
}},
},
},
}
commonSpec.Volumes = append(commonSpec.Volumes, volume)

volumeMount := corev1.VolumeMount{
Name: TrustedCAName(jaeger),
MountPath: "/etc/pki/ca-trust/extracted/pem",
ReadOnly: true,
}
commonSpec.VolumeMounts = append(commonSpec.VolumeMounts, volumeMount)
}

func deployTrustedCA(jaeger *v1.Jaeger) bool {
for _, vm := range jaeger.Spec.JaegerCommonSpec.VolumeMounts {
if strings.HasPrefix(vm.MountPath, "/etc/pki/ca-trust/extracted/pem") {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jpkrohling Is this a reason approach to disable adding the custom CA bundle/volumes/mounts, if (for example) Service Mesh explicitly adds them into the Jaeger CR? Or do we need a better approach?

Copy link
Contributor

@jpkrohling jpkrohling Jun 10, 2020

Choose a reason for hiding this comment

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

I'll think about it some more, but I think it's good enough. In any case, there's no way to be 100% sure that there won't be a clash, as the path inside the volumes themselves might end up conflicting (/etc/pki with ca-trust inside vs. /etc/pki/ca-trust).

Edit: actually, we could compare the paths, and see if the CA bundle volume mount would clash with an existing volume mount (/etc/pki in the example above).

// Volume Mount already exists, so don't create specific
// one for this Jaeger instance
return false
}
}
return true
}

// TrustedCAName returns the name of the trusted CA
func TrustedCAName(jaeger *v1.Jaeger) string {
return fmt.Sprintf("%s-trusted-ca", jaeger.Name)
}
93 changes: 93 additions & 0 deletions pkg/config/ca/ca_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package ca

import (
"testing"

"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"

v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
)

func TestGetWithoutTrustedCA(t *testing.T) {
viper.Set("platform", "other")
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestGetWithoutTrustedCA"})
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 started using a generic name some time ago. Was it changed recently, to use individual names for the instances in each test? If not, there's no need to change this now.


cm := Get(jaeger)
assert.Nil(t, cm)
}

func TestGetWithTrustedCA(t *testing.T) {
viper.Set("platform", v1.FlagPlatformOpenShift)
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestGetWithTrustedCA"})

cm := Get(jaeger)
assert.NotNil(t, cm)
assert.Equal(t, "true", cm.Labels["config.openshift.io/inject-trusted-cabundle"])
assert.Equal(t, "", cm.Data["ca-bundle.crt"])
}

func TestGetWithExistingTrustedCA(t *testing.T) {
viper.Set("platform", v1.FlagPlatformOpenShift)
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestGetWithExistingTrustedCA"})
jaeger.Spec.JaegerCommonSpec.VolumeMounts = []corev1.VolumeMount{{
MountPath: "/etc/pki/ca-trust/extracted/pem",
Name: "ExistingTrustedCA",
}}

cm := Get(jaeger)
assert.Nil(t, cm)
}

func TestUpdateWithoutTrustedCA(t *testing.T) {
viper.Set("platform", "other")
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestUpdateWithoutTrustedCA"})

commonSpec := v1.JaegerCommonSpec{}

Update(jaeger, &commonSpec)
assert.Len(t, commonSpec.Volumes, 0)
assert.Len(t, commonSpec.VolumeMounts, 0)
}

func TestUpdateWithTrustedCA(t *testing.T) {
viper.Set("platform", v1.FlagPlatformOpenShift)
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestUpdateWithTrustedCA"})

commonSpec := v1.JaegerCommonSpec{}

Update(jaeger, &commonSpec)
assert.Len(t, commonSpec.Volumes, 1)
assert.Equal(t, commonSpec.Volumes[0].Name, TrustedCAName(jaeger))
assert.Len(t, commonSpec.VolumeMounts, 1)
assert.Equal(t, commonSpec.VolumeMounts[0].Name, TrustedCAName(jaeger))
}

func TestUpdateWithExistingTrustedCA(t *testing.T) {
viper.Set("platform", v1.FlagPlatformOpenShift)
defer viper.Reset()

jaeger := v1.NewJaeger(types.NamespacedName{Name: "TestUpdateWithExistingTrustedCA"})
jaeger.Spec.JaegerCommonSpec.VolumeMounts = []corev1.VolumeMount{{
MountPath: "/etc/pki/ca-trust/extracted/pem",
Name: "ExistingTrustedCA",
}}

commonSpec := v1.JaegerCommonSpec{}

Update(jaeger, &commonSpec)
assert.Len(t, commonSpec.Volumes, 0)
assert.Len(t, commonSpec.VolumeMounts, 0)
}
3 changes: 3 additions & 0 deletions pkg/cronjob/es_index_cleaner.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)

Expand Down Expand Up @@ -42,6 +43,8 @@ func CreateEsIndexCleaner(jaeger *v1.Jaeger) *batchv1beta1.CronJob {

commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Storage.EsIndexCleaner.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec, baseCommonSpec})

ca.Update(jaeger, commonSpec)

return &batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Expand Down
3 changes: 3 additions & 0 deletions pkg/cronjob/es_rollover.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)

Expand Down Expand Up @@ -70,6 +71,8 @@ func createTemplate(name, action string, jaeger *v1.Jaeger, envs []corev1.EnvVar

commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Storage.EsRollover.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec, baseCommonSpec})

ca.Update(jaeger, commonSpec)

return &corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: commonSpec.Labels,
Expand Down
3 changes: 3 additions & 0 deletions pkg/cronjob/spark_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)

Expand Down Expand Up @@ -52,6 +53,8 @@ func CreateSparkDependencies(jaeger *v1.Jaeger) *batchv1beta1.CronJob {

commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Storage.Dependencies.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec, baseCommonSpec})

ca.Update(jaeger, commonSpec)

// Cannot use util.ImageName to obtain the correct image, as the spark-dependencies
// image does not get tagged with the jaeger version, so the latest image must
// be used instead.
Expand Down
3 changes: 3 additions & 0 deletions pkg/deployment/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/service"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)
Expand Down Expand Up @@ -78,6 +79,8 @@ func (a *Agent) Get() *appsv1.DaemonSet {

commonSpec := util.Merge([]v1.JaegerCommonSpec{a.jaeger.Spec.Agent.JaegerCommonSpec, a.jaeger.Spec.JaegerCommonSpec, baseCommonSpec})

ca.Update(a.jaeger, commonSpec)

otelConf, err := a.jaeger.Spec.Agent.Config.GetMap()
if err != nil {
a.jaeger.Logger().WithField("error", err).
Expand Down
3 changes: 3 additions & 0 deletions pkg/deployment/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ func TestAgentArgumentsOpenshiftTLS(t *testing.T) {
assert.Greater(t, len(util.FindItem("--reporter.grpc.tls.enabled=true", dep.Spec.Template.Spec.Containers[0].Args)), 0)
assert.Greater(t, len(util.FindItem("--reporter.grpc.tls.ca=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt", dep.Spec.Template.Spec.Containers[0].Args)), 0)
assert.Greater(t, len(util.FindItem("--reporter.grpc.tls.server-name=my-openshift-instance-collector-headless.test.svc.cluster.local", dep.Spec.Template.Spec.Containers[0].Args)), 0)

assert.Len(t, dep.Spec.Template.Spec.Volumes, 1)
assert.Len(t, dep.Spec.Template.Spec.Containers[0].VolumeMounts, 1)
}

func TestAgentOTELConfig(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/deployment/all_in_one.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/config/sampling"
"github.com/jaegertracing/jaeger-operator/pkg/config/tls"
configmap "github.com/jaegertracing/jaeger-operator/pkg/config/ui"
Expand Down Expand Up @@ -61,6 +62,7 @@ func (a *AllInOne) Get() *appsv1.Deployment {
configmap.Update(a.jaeger, commonSpec, &options)
sampling.Update(a.jaeger, commonSpec, &options)
tls.Update(a.jaeger, commonSpec, &options)
ca.Update(a.jaeger, commonSpec)

// Enable tls by default for openshift platform
// even though the agent is in the same process as the collector, they communicate via gRPC, and the collector has TLS enabled,
Expand Down
2 changes: 2 additions & 0 deletions pkg/deployment/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/config/sampling"
"github.com/jaegertracing/jaeger-operator/pkg/config/tls"
"github.com/jaegertracing/jaeger-operator/pkg/service"
Expand Down Expand Up @@ -77,6 +78,7 @@ func (c *Collector) Get() *appsv1.Deployment {

sampling.Update(c.jaeger, commonSpec, &options)
tls.Update(c.jaeger, commonSpec, &options)
ca.Update(c.jaeger, commonSpec)

otelConf, err := c.jaeger.Spec.Collector.Config.GetMap()
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/deployment/ingester.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/storage"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)
Expand Down Expand Up @@ -75,6 +76,8 @@ func (i *Ingester) Get() *appsv1.Deployment {
options := allArgs(i.jaeger.Spec.Ingester.Options,
i.jaeger.Spec.Storage.Options.Filter(storage.OptionsPrefix(i.jaeger.Spec.Storage.Type)))

ca.Update(i.jaeger, commonSpec)

otelConf, err := i.jaeger.Spec.Ingester.Config.GetMap()
if err != nil {
i.jaeger.Logger().WithField("error", err).
Expand Down
3 changes: 3 additions & 0 deletions pkg/deployment/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
configmap "github.com/jaegertracing/jaeger-operator/pkg/config/ui"
"github.com/jaegertracing/jaeger-operator/pkg/service"
"github.com/jaegertracing/jaeger-operator/pkg/storage"
Expand Down Expand Up @@ -61,6 +62,8 @@ func (q *Query) Get() *appsv1.Deployment {
q.jaeger.Spec.Storage.Options.Filter(storage.OptionsPrefix(q.jaeger.Spec.Storage.Type)))

configmap.Update(q.jaeger, commonSpec, &options)
ca.Update(q.jaeger, commonSpec)

var envFromSource []corev1.EnvFromSource
if len(q.jaeger.Spec.Storage.SecretName) > 0 {
envFromSource = append(envFromSource, corev1.EnvFromSource{
Expand Down
15 changes: 13 additions & 2 deletions pkg/inject/oauth_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/jaegertracing/jaeger-operator/pkg/account"
v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1"
"github.com/jaegertracing/jaeger-operator/pkg/config/ca"
"github.com/jaegertracing/jaeger-operator/pkg/service"
"github.com/jaegertracing/jaeger-operator/pkg/util"
)
Expand Down Expand Up @@ -55,12 +56,24 @@ func proxyInitArguments(jaeger *v1.Jaeger) []string {
}

func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container {
commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Ingress.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec})
ca.Update(jaeger, commonSpec)

args := proxyInitArguments(jaeger)
volumeMounts := []corev1.VolumeMount{{
MountPath: "/etc/tls/private",
Name: service.GetTLSSecretNameForQueryService(jaeger),
}}

// if we have the trusted-ca volume, we mount it in the oauth proxy as well
trustedCAVolumeName := ca.TrustedCAName(jaeger)
for _, v := range commonSpec.VolumeMounts {
if v.Name == trustedCAVolumeName {
jaeger.Logger().Debug("found a volume mount with the trusted-ca")
volumeMounts = append(volumeMounts, v)
}
}

if len(jaeger.Spec.Ingress.Openshift.HtpasswdFile) > 0 {
args = append(args, fmt.Sprintf("--htpasswd-file=%s", jaeger.Spec.Ingress.Openshift.HtpasswdFile))
args = append(args, "--display-htpasswd-form=false")
Expand All @@ -81,8 +94,6 @@ func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container {

sort.Strings(args)

commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Ingress.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec})

return corev1.Container{
Image: viper.GetString("openshift-oauth-proxy-image"),
Name: "oauth-proxy",
Expand Down
Loading