diff --git a/.ci/run-e2e-tests.sh b/.ci/run-e2e-tests.sh index c26ecd2ff..0e95f42f7 100755 --- a/.ci/run-e2e-tests.sh +++ b/.ci/run-e2e-tests.sh @@ -39,6 +39,10 @@ elif [ "${TEST_GROUP}" = "examples2" ] then echo "Running Examples2 Tests" make e2e-tests-examples2 +elif [ "${TEST_GROUP}" = "es-token-propagation" ] +then + echo "Running token propagation tests" + make e2e-tests-token-propagation-es else echo "Unknown TEST_GROUP [${TEST_GROUP}]"; exit 1 fi diff --git a/.ci/start-openshift.sh b/.ci/start-openshift.sh index 4831ebe39..4d198eb72 100755 --- a/.ci/start-openshift.sh +++ b/.ci/start-openshift.sh @@ -9,7 +9,10 @@ export KUBERNETES_VERSION=v1.14.0 echo '{"registry-mirrors": ["https://mirror.gcr.io"], "mtu": 1460, "insecure-registries": ["172.30.0.0/16"] }' | sudo tee /etc/docker/daemon.json sudo service docker restart +HOST_IP=$(hostname -I | awk '{ print $1 }') + # install and run OCP sudo docker cp $(docker create docker.io/openshift/origin:$OPENSHIFT_VERSION):/bin/oc /usr/local/bin/oc -oc cluster up --version=$OPENSHIFT_VERSION + +oc cluster up --version=$OPENSHIFT_VERSION --public-hostname=${HOST_IP}.nip.io oc login -u system:admin diff --git a/Makefile b/Makefile index 8dec88c8a..8b5980158 100644 --- a/Makefile +++ b/Makefile @@ -120,6 +120,11 @@ e2e-tests-self-provisioned-es-kafka: prepare-e2e-tests deploy-kafka-operator dep @echo Running Self provisioned Elasticsearch and Kafka end-to-end tests... @STORAGE_NAMESPACE=$(STORAGE_NAMESPACE) ES_OPERATOR_NAMESPACE=$(ES_OPERATOR_NAMESPACE) ES_OPERATOR_IMAGE=$(ES_OPERATOR_IMAGE) go test -tags=self_provisioned_elasticsearch_kafka ./test/e2e/... $(TEST_OPTIONS) +.PHONY: e2e-tests-token-propagation-es +e2e-tests-token-propagation-es: prepare-e2e-tests deploy-es-operator + @echo Running Token Propagation Elasticsearch end-to-end tests... + @STORAGE_NAMESPACE=$(STORAGE_NAMESPACE) ES_OPERATOR_NAMESPACE=$(ES_OPERATOR_NAMESPACE) ES_OPERATOR_IMAGE=$(ES_OPERATOR_IMAGE) go test -tags=token_propagation_elasticsearch ./test/e2e/... $(TEST_OPTIONS) + .PHONY: e2e-tests-streaming e2e-tests-streaming: prepare-e2e-tests es kafka @echo Running Streaming end-to-end tests... diff --git a/deploy/crds/jaegertracing.io_jaegers_crd.yaml b/deploy/crds/jaegertracing.io_jaegers_crd.yaml index 0615e6b65..cf8cec335 100644 --- a/deploy/crds/jaegertracing.io_jaegers_crd.yaml +++ b/deploy/crds/jaegertracing.io_jaegers_crd.yaml @@ -9434,6 +9434,10 @@ spec: sar: type: string type: object + options: + description: Options defines a common options parameter to the different + structs + type: object resources: description: ResourceRequirements describes the compute resource requirements. diff --git a/pkg/apis/jaegertracing/v1/jaeger_types.go b/pkg/apis/jaegertracing/v1/jaeger_types.go index 7d0f7c2c0..1ea2c40c5 100644 --- a/pkg/apis/jaegertracing/v1/jaeger_types.go +++ b/pkg/apis/jaegertracing/v1/jaeger_types.go @@ -240,6 +240,9 @@ type JaegerIngressSpec struct { // +optional JaegerCommonSpec `json:",inline,omitempty"` + + // +optional + Options Options `json:"options,omitempty"` } // JaegerIngressTLSSpec defines the TLS configuration to be used when deploying the query ingress diff --git a/pkg/apis/jaegertracing/v1/zz_generated.deepcopy.go b/pkg/apis/jaegertracing/v1/zz_generated.deepcopy.go index 328c56c84..fb4d01f9b 100644 --- a/pkg/apis/jaegertracing/v1/zz_generated.deepcopy.go +++ b/pkg/apis/jaegertracing/v1/zz_generated.deepcopy.go @@ -405,6 +405,7 @@ func (in *JaegerIngressSpec) DeepCopyInto(out *JaegerIngressSpec) { } } in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + in.Options.DeepCopyInto(&out.Options) return } diff --git a/pkg/apis/jaegertracing/v1/zz_generated.openapi.go b/pkg/apis/jaegertracing/v1/zz_generated.openapi.go index bbf1da80c..bbce3cdc2 100644 --- a/pkg/apis/jaegertracing/v1/zz_generated.openapi.go +++ b/pkg/apis/jaegertracing/v1/zz_generated.openapi.go @@ -1361,11 +1361,16 @@ func schema_pkg_apis_jaegertracing_v1_JaegerIngressSpec(ref common.ReferenceCall Format: "", }, }, + "options": { + SchemaProps: spec.SchemaProps{ + Ref: ref("./pkg/apis/jaegertracing/v1.Options"), + }, + }, }, }, }, Dependencies: []string{ - "./pkg/apis/jaegertracing/v1.JaegerIngressOpenShiftSpec", "./pkg/apis/jaegertracing/v1.JaegerIngressTLSSpec", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "./pkg/apis/jaegertracing/v1.JaegerIngressOpenShiftSpec", "./pkg/apis/jaegertracing/v1.JaegerIngressTLSSpec", "./pkg/apis/jaegertracing/v1.Options", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/inject/oauth_proxy.go b/pkg/inject/oauth_proxy.go index b149bd72b..2b9e60547 100644 --- a/pkg/inject/oauth_proxy.go +++ b/pkg/inject/oauth_proxy.go @@ -14,6 +14,9 @@ import ( "github.com/jaegertracing/jaeger-operator/pkg/util" ) +// #nosec G101 (CWE-798): Potential hardcoded credentials +const defaultProxySecret = "ncNDoqLGrayxXzxTn5ANbOXZp3qXd0LA" + // OAuthProxy injects an appropriate proxy into the given deployment func OAuthProxy(jaeger *v1.Jaeger, dep *appsv1.Deployment) *appsv1.Deployment { if jaeger.Spec.Ingress.Security != v1.IngressSecurityOAuthProxy { @@ -33,9 +36,14 @@ func OAuthProxy(jaeger *v1.Jaeger, dep *appsv1.Deployment) *appsv1.Deployment { return dep } -func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container { +func proxyInitArguments(jaeger *v1.Jaeger) []string { + secret, err := util.GenerateProxySecret() + if err != nil { + jaeger.Logger().WithError(err).Warnf("Error generating secret: %s, fallback to fixed secret", secret) + secret = defaultProxySecret + } args := []string{ - "--cookie-secret=SECRET", + fmt.Sprintf("--cookie-secret=%s", secret), "--https-address=:8443", fmt.Sprintf("--openshift-service-account=%s", account.OAuthProxyAccountNameFor(jaeger)), "--provider=openshift", @@ -43,7 +51,11 @@ func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container { "--tls-key=/etc/tls/private/tls.key", "--upstream=http://localhost:16686", } + return args +} +func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container { + args := proxyInitArguments(jaeger) volumeMounts := []corev1.VolumeMount{{ MountPath: "/etc/tls/private", Name: service.GetTLSSecretNameForQueryService(jaeger), @@ -65,6 +77,8 @@ func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container { args = append(args, fmt.Sprintf("--openshift-delegate-urls=%s", jaeger.Spec.Ingress.Openshift.DelegateUrls)) } + args = append(args, jaeger.Spec.Ingress.Options.ToArgs()...) + sort.Strings(args) commonSpec := util.Merge([]v1.JaegerCommonSpec{jaeger.Spec.Ingress.JaegerCommonSpec, jaeger.Spec.JaegerCommonSpec}) diff --git a/pkg/storage/elasticsearch.go b/pkg/storage/elasticsearch.go index 24fcd4c8c..fb524a5f8 100644 --- a/pkg/storage/elasticsearch.go +++ b/pkg/storage/elasticsearch.go @@ -38,6 +38,55 @@ type ElasticsearchDeployment struct { Secrets []corev1.Secret } +func (ed *ElasticsearchDeployment) injectArguments(container *corev1.Container) { + container.Args = append(container.Args, "--es.server-urls="+elasticsearchURL) + if util.FindItem("--es.tls=", container.Args) == "" { + container.Args = append(container.Args, "--es.tls=true") + } + container.Args = append(container.Args, + "--es.tls.ca="+caPath, + "--es.tls.cert="+certPath, + "--es.tls.key="+keyPath) + + if util.FindItem("--es.timeout", container.Args) == "" { + container.Args = append(container.Args, "--es.timeout=15s") + } + if util.FindItem("--es.num-shards", container.Args) == "" { + // taken from https://github.com/openshift/cluster-logging-operator/blob/32b69e8bcf61a805e8f3c45c664a3c08d1ee62d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/configmaps.go#L38 + // every ES node is a data node + container.Args = append(container.Args, fmt.Sprintf("--es.num-shards=%d", ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)) + } + if util.FindItem("--es.num-replicas", container.Args) == "" { + container.Args = append(container.Args, fmt.Sprintf("--es.num-replicas=%d", + calculateReplicaShards(ed.Jaeger.Spec.Storage.Elasticsearch.RedundancyPolicy, int(ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)))) + } + if strings.EqualFold(util.FindItem("--es-archive.enabled", container.Args), "--es-archive.enabled=true") { + container.Args = append(container.Args, + "--es-archive.server-urls="+elasticsearchURL, + ) + if util.FindItem("--es-archive.tls=", container.Args) == "" { + container.Args = append(container.Args, "--es-archive.tls=true") + } + container.Args = append(container.Args, + "--es-archive.tls.ca="+caPath, + "--es-archive.tls.cert="+certPath, + "--es-archive.tls.key="+keyPath, + ) + if util.FindItem("--es-archive.timeout", container.Args) == "" { + container.Args = append(container.Args, "--es-archive.timeout=15s") + } + if util.FindItem("--es-archive.num-shards", container.Args) == "" { + // taken from https://github.com/openshift/cluster-logging-operator/blob/32b69e8bcf61a805e8f3c45c664a3c08d1ee62d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/configmaps.go#L38 + // every ES node is a data node + container.Args = append(container.Args, fmt.Sprintf("--es-archive.num-shards=%d", ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)) + } + if util.FindItem("--es-archive.num-replicas", container.Args) == "" { + container.Args = append(container.Args, fmt.Sprintf("--es-archive.num-replicas=%d", + calculateReplicaShards(ed.Jaeger.Spec.Storage.Elasticsearch.RedundancyPolicy, int(ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)))) + } + } +} + // InjectStorageConfiguration changes the given spec to include ES-related command line options func (ed *ElasticsearchDeployment) InjectStorageConfiguration(p *corev1.PodSpec) { p.Volumes = append(p.Volumes, corev1.Volume{ @@ -50,45 +99,7 @@ func (ed *ElasticsearchDeployment) InjectStorageConfiguration(p *corev1.PodSpec) }) // we assume jaeger containers are first if len(p.Containers) > 0 { - p.Containers[0].Args = append(p.Containers[0].Args, - "--es.server-urls="+elasticsearchURL, - "--es.tls=true", - "--es.tls.ca="+caPath, - "--es.tls.cert="+certPath, - "--es.tls.key="+keyPath) - if util.FindItem("--es.timeout", p.Containers[0].Args) == "" { - p.Containers[0].Args = append(p.Containers[0].Args, "--es.timeout=15s") - } - if util.FindItem("--es.num-shards", p.Containers[0].Args) == "" { - // taken from https://github.com/openshift/cluster-logging-operator/blob/32b69e8bcf61a805e8f3c45c664a3c08d1ee62d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/configmaps.go#L38 - // every ES node is a data node - p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es.num-shards=%d", ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)) - } - if util.FindItem("--es.num-replicas", p.Containers[0].Args) == "" { - p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es.num-replicas=%d", - calculateReplicaShards(ed.Jaeger.Spec.Storage.Elasticsearch.RedundancyPolicy, int(ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)))) - } - if strings.EqualFold(util.FindItem("--es-archive.enabled", p.Containers[0].Args), "--es-archive.enabled=true") { - p.Containers[0].Args = append(p.Containers[0].Args, - "--es-archive.server-urls="+elasticsearchURL, - "--es-archive.tls=true", - "--es-archive.tls.ca="+caPath, - "--es-archive.tls.cert="+certPath, - "--es-archive.tls.key="+keyPath, - ) - if util.FindItem("--es-archive.timeout", p.Containers[0].Args) == "" { - p.Containers[0].Args = append(p.Containers[0].Args, "--es-archive.timeout=15s") - } - if util.FindItem("--es-archive.num-shards", p.Containers[0].Args) == "" { - // taken from https://github.com/openshift/cluster-logging-operator/blob/32b69e8bcf61a805e8f3c45c664a3c08d1ee62d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/configmaps.go#L38 - // every ES node is a data node - p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es-archive.num-shards=%d", ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)) - } - if util.FindItem("--es-archive.num-replicas", p.Containers[0].Args) == "" { - p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es-archive.num-replicas=%d", - calculateReplicaShards(ed.Jaeger.Spec.Storage.Elasticsearch.RedundancyPolicy, int(ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)))) - } - } + ed.injectArguments(&p.Containers[0]) p.Containers[0].VolumeMounts = append(p.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: volumeName, ReadOnly: true, diff --git a/pkg/util/util.go b/pkg/util/util.go index 7c896f5b3..6abc3b418 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,6 +1,8 @@ package util import ( + "crypto/rand" + "encoding/base64" "fmt" "strconv" "strings" @@ -242,3 +244,17 @@ func CreateEnvsFromSecret(secretName string) []corev1.EnvFromSource { } return envs } + +// GenerateProxySecret generate random secret key for oauth proxy cookie. +func GenerateProxySecret() (string, error) { + const secretLength = 16 + randString := make([]byte, secretLength) + _, err := rand.Read(randString) + if err != nil { + // If we cannot generate random, return fixed. + return "", err + } + base64Secret := base64.StdEncoding.EncodeToString(randString) + return base64Secret, nil + +} diff --git a/test/e2e/elasticsearch_token_propagation_test.go b/test/e2e/elasticsearch_token_propagation_test.go new file mode 100644 index 000000000..ed7bca5ab --- /dev/null +++ b/test/e2e/elasticsearch_token_propagation_test.go @@ -0,0 +1,372 @@ +// +build token_propagation_elasticsearch + +package e2e + +import ( + goctx "context" + "crypto/tls" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" + "testing" + "time" + + framework "github.com/operator-framework/operator-sdk/pkg/test" + "github.com/operator-framework/operator-sdk/pkg/test/e2eutil" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-client-go/config" + corev1 "k8s.io/api/core/v1" + rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + "k8s.io/apimachinery/pkg/types" + + "github.com/jaegertracing/jaeger-operator/pkg/apis" + v1 "github.com/jaegertracing/jaeger-operator/pkg/apis/jaegertracing/v1" + esv1 "github.com/jaegertracing/jaeger-operator/pkg/storage/elasticsearch/v1" +) + +// Test parameters +const name = "token-prop" +const collectorPodImageName = "jaeger-collector" +const testServiceName = "token-propagation" +const testAccount = "token-test-user" + +type TokenTestSuite struct { + suite.Suite + exampleJaeger *v1.Jaeger + queryName string + collectorName string + queryServiceEndPoint string + host string + token string + testServiceAccount *corev1.ServiceAccount + testRoleBinding *rbac.ClusterRoleBinding + delegationRoleBinding *rbac.ClusterRoleBinding +} + +func (suite *TokenTestSuite) SetupSuite() { + t = suite.T() + if !isOpenShift(t) { + t.Skipf("Test %s is currently supported only on OpenShift because es-operator runs only on OpenShift\n", t.Name()) + } + require.NoError(t, framework.AddToFrameworkScheme(apis.AddToScheme, &v1.JaegerList{ + TypeMeta: metav1.TypeMeta{ + Kind: "Jaeger", + APIVersion: "jaegertracing.io/v1", + }, + })) + require.NoError(t, framework.AddToFrameworkScheme(apis.AddToScheme, &esv1.ElasticsearchList{ + TypeMeta: metav1.TypeMeta{ + Kind: "Elasticsearch", + APIVersion: "logging.openshift.io/v1", + }, + })) + var err error + ctx, err = prepare(t) + if err != nil { + if ctx != nil { + ctx.Cleanup() + } + require.FailNow(t, "Failed in prepare") + } + fw = framework.Global + namespace, _ = ctx.GetNamespace() + require.NotNil(t, namespace, "GetNamespace failed") + addToFrameworkSchemeForSmokeTests(t) + suite.deployJaegerWithPropagationEnabled() +} + +func (suite *TokenTestSuite) cleanAccountBindings() { + if !debugMode || !t.Failed() { + err := fw.Client.Delete(goctx.Background(), suite.testServiceAccount) + require.NoError(t, err, "Error deleting test service account") + err = e2eutil.WaitForDeletion(t, fw.Client.Client, suite.testServiceAccount, retryInterval, timeout) + require.NoError(t, err) + + err = fw.Client.Delete(goctx.Background(), suite.testRoleBinding) + require.NoError(t, err, "Error deleting test service account bindings") + err = e2eutil.WaitForDeletion(t, fw.Client.Client, suite.testRoleBinding, retryInterval, timeout) + require.NoError(t, err) + + err = fw.Client.Delete(goctx.Background(), suite.delegationRoleBinding) + require.NoError(t, err, "Error deleting delegation bindings") + err = e2eutil.WaitForDeletion(t, fw.Client.Client, suite.delegationRoleBinding, retryInterval, timeout) + require.NoError(t, err) + } +} + +func (suite *TokenTestSuite) TearDownSuite() { + suite.cleanAccountBindings() + undeployJaegerInstance(suite.exampleJaeger) + handleSuiteTearDown() +} + +func (suite *TokenTestSuite) TestTokenPropagationNoToken() { + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + err := wait.Poll(retryInterval, timeout, func() (done bool, err error) { + req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) + if err != nil { + return false, err + } + resp, err := client.Do(req) + defer resp.Body.Close() + if err != nil { + return false, err + } + if resp.StatusCode == http.StatusForbidden { + return true, nil + } + return false, errors.New(fmt.Sprintf("query service return http code: %d", resp.StatusCode)) + }) + require.NoError(t, err, "Token propagation test failed") +} + +func (suite *TokenTestSuite) TestTokenPropagationValidToken() { + /* Create an span */ + collectorPort := randomPortNumber() + collectorPorts := []string{collectorPort + ":14268"} + portForwColl, closeChanColl := CreatePortForward(namespace, suite.collectorName, collectorPodImageName, collectorPorts, fw.KubeConfig) + defer portForwColl.Close() + defer close(closeChanColl) + collectorEndpoint := fmt.Sprintf("http://localhost:%s/api/traces", collectorPort) + cfg := config.Configuration{ + Reporter: &config.ReporterConfig{CollectorEndpoint: collectorEndpoint}, + Sampler: &config.SamplerConfig{Type: "const", Param: 1}, + ServiceName: testServiceName, + } + tracer, closer, err := cfg.NewTracer() + require.NoError(t, err, "Failed to create tracer in token propagation test") + tStr := time.Now().Format(time.RFC3339Nano) + tracer.StartSpan("TokenTest"). + SetTag("time-RFC3339Nano", tStr). + Finish() + closer.Close() + client := newHTTPSClient() + /* Try to reach query endpoint */ + err = wait.Poll(retryInterval, timeout, func() (done bool, err error) { + req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) + if err != nil { + return false, err + } + req.Header.Add("Authorization", "Bearer "+suite.token) + resp, err := client.Do(req) + defer resp.Body.Close() + if err != nil { + return false, err + } + if resp.StatusCode == http.StatusOK { + bodyBytes, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + bodyString := string(bodyBytes) + if !strings.Contains(bodyString, "errors\":null") { + return false, errors.New("query service returns errors: " + bodyString) + } + return true, nil + } + return false, errors.New(fmt.Sprintf("query service return http code: %d", resp.StatusCode)) + }) + require.NoError(t, err, "Token propagation test failed") +} + +func (suite *TokenTestSuite) deployJaegerWithPropagationEnabled() { + queryName := fmt.Sprintf("%s-query", name) + collectorName := fmt.Sprintf("%s-collector", name) + suite.bindOperatorWithAuthDelegator() + suite.createTestServiceAccount() + suite.token = testAccountToken() + require.NotEmpty(t, suite.token) + println(suite.token) + suite.exampleJaeger = jaegerInstance() + err := fw.Client.Create(goctx.Background(), + suite.exampleJaeger, + &framework.CleanupOptions{ + TestContext: ctx, + Timeout: timeout, + RetryInterval: retryInterval, + }) + require.NoError(t, err, "Error deploying example Jaeger") + + err = e2eutil.WaitForDeployment(t, fw.KubeClient, namespace, collectorName, 1, retryInterval, timeout) + require.NoError(t, err, "Error waiting for collector deployment") + + err = e2eutil.WaitForDeployment(t, fw.KubeClient, namespace, queryName, 1, retryInterval, timeout) + require.NoError(t, err, "Error waiting for query deployment") + + route := findRoute(t, fw, name) + + suite.host = route.Spec.Host + suite.queryServiceEndPoint = fmt.Sprintf("https://%s/api/traces?service=%s", suite.host, testServiceName) +} + +func TestTokenSuite(t *testing.T) { + suite.Run(t, new(TokenTestSuite)) +} + +func (suite *TokenTestSuite) bindOperatorWithAuthDelegator() { + suite.delegationRoleBinding = &rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "jaeger-operator:system:auth-delegator", + Namespace: namespace, + }, + Subjects: []rbac.Subject{{ + Kind: "ServiceAccount", + Name: "jaeger-operator", + Namespace: namespace, + }}, + RoleRef: rbac.RoleRef{ + Kind: "ClusterRole", + Name: "system:auth-delegator", + }, + } + err := fw.Client.Create(goctx.Background(), + suite.delegationRoleBinding, + &framework.CleanupOptions{ + TestContext: ctx, + Timeout: timeout, + RetryInterval: retryInterval, + }) + require.NoError(t, err, "Error binding operator service account with auth-delegator") +} + +func (suite *TokenTestSuite) createTestServiceAccount() { + + suite.testServiceAccount = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: testAccount, + Namespace: namespace, + }, + } + err := fw.Client.Create(goctx.Background(), + suite.testServiceAccount, + &framework.CleanupOptions{ + TestContext: ctx, + Timeout: timeout, + RetryInterval: retryInterval, + }) + require.NoError(t, err, "Error deploying example Jaeger") + + suite.testRoleBinding = &rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: testAccount + "-bind", + Namespace: namespace, + }, + Subjects: []rbac.Subject{{ + Kind: "ServiceAccount", + Name: suite.testServiceAccount.Name, + Namespace: namespace, + }}, + RoleRef: rbac.RoleRef{ + Kind: "ClusterRole", + Name: "cluster-admin", + }, + } + + err = fw.Client.Create(goctx.Background(), + suite.testRoleBinding, + &framework.CleanupOptions{ + TestContext: ctx, + Timeout: timeout, + RetryInterval: retryInterval, + }) + require.NoError(t, err, "Error deploying example Jaeger") + +} + +func testAccountToken() string { + var secretName string + err := wait.Poll(retryInterval, timeout, func() (done bool, err error) { + serviceAccount := corev1.ServiceAccount{} + e := fw.Client.Get(goctx.Background(), types.NamespacedName{ + Namespace: namespace, + Name: testAccount, + }, &serviceAccount) + if e != nil { + return false, e + } + for _, s := range serviceAccount.Secrets { + if strings.HasPrefix(s.Name, testAccount+"-token") { + secretName = s.Name + return true, nil + } + } + return false, nil + }) + require.NoError(t, err, "Error getting service account token") + require.NotEmpty(t, secretName, "secret with token not found") + secret := corev1.Secret{} + err = fw.Client.Get(goctx.Background(), types.NamespacedName{ + Namespace: namespace, + Name: secretName, + }, &secret) + require.NoError(t, err, "Error deploying example Jaeger") + return string(secret.Data["token"]) +} + +func jaegerInstance() *v1.Jaeger { + exampleJaeger := &v1.Jaeger{ + TypeMeta: metav1.TypeMeta{ + Kind: "Jaeger", + APIVersion: "jaegertracing.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "token-prop", + Namespace: namespace, + }, + Spec: v1.JaegerSpec{ + Strategy: "production", + Storage: v1.JaegerStorageSpec{ + Type: "elasticsearch", + Elasticsearch: v1.ElasticsearchSpec{ + NodeCount: 1, + Resources: &corev1.ResourceRequirements{ + Limits: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("1Gi")}, + Requests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("1Gi")}, + }, + }, + }, + Query: v1.JaegerQuerySpec{ + Options: v1.NewOptions(map[string]interface{}{ + "es.version": "5", + "query.bearer-token-propagation": "true", + "es.tls": "false", + }), + }, + Ingress: v1.JaegerIngressSpec{ + Openshift: v1.JaegerIngressOpenShiftSpec{ + SAR: "{\"namespace\": \"default\", \"resource\": \"pods\", \"verb\": \"get\"}", + DelegateUrls: `{"/":{"namespace": "default", "resource": "pods", "verb": "get"}}`, + }, + Options: v1.NewOptions(map[string]interface{}{ + "pass-access-token": "true", + "pass-user-bearer-token": "true", + "scope": "user:info user:check-access user:list-projects", + "pass-basic-auth": "false", + }), + }, + }, + } + return exampleJaeger +} + +func newHTTPSClient() *http.Client { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: tr, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + return client +} diff --git a/test/e2e/utils.go b/test/e2e/utils.go index cf1b70cda..e739c6f6f 100644 --- a/test/e2e/utils.go +++ b/test/e2e/utils.go @@ -36,7 +36,7 @@ import ( var ( retryInterval = time.Second * 5 - timeout = time.Minute * 2 + timeout = time.Minute * 5 storageNamespace = os.Getenv("STORAGE_NAMESPACE") kafkaNamespace = os.Getenv("KAFKA_NAMESPACE") debugMode = getBoolEnv("DEBUG_MODE", false)