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

Enable securing query component by a sidecar #1105

Closed
wants to merge 8 commits into from
Closed

Enable securing query component by a sidecar #1105

wants to merge 8 commits into from

Conversation

basch255
Copy link

Enable and configure a sidecar with arguments by the Jaeger crd and
the service will be redirected.

The UI of the query component has no authentication measure.

Resolves: #1104
Signed-off-by: Bastian Schlegel [email protected]

Enable and configure a sidecar with arguments by the Jaeger crd and
the service will be redirected.

The UI of the query component has no authentication measure.

Resolves: #1104
Signed-off-by: Bastian Schlegel <[email protected]>
@codecov
Copy link

codecov bot commented Jun 26, 2020

Codecov Report

Merging #1105 into master will increase coverage by 0.06%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1105      +/-   ##
==========================================
+ Coverage   88.22%   88.28%   +0.06%     
==========================================
  Files          86       86              
  Lines        5351     5379      +28     
==========================================
+ Hits         4721     4749      +28     
  Misses        466      466              
  Partials      164      164              
Impacted Files Coverage Δ
pkg/apis/jaegertracing/v1/jaeger_types.go 100.00% <ø> (ø)
pkg/inject/oauth_proxy.go 96.90% <100.00%> (+0.96%) ⬆️
pkg/service/query.go 100.00% <100.00%> (ø)
pkg/strategy/controller.go 95.54% <100.00%> (ø)
pkg/apis/jaegertracing/v1/deployment_strategy.go 84.61% <0.00%> (+1.28%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 908ed30...bb398d7. Read the comment docs.

@jpkrohling
Copy link
Contributor

Thank you for this PR! I think it's a great first step and this is something we indeed wanted to implement.

We have a logic already about adding OAuth proxies, which is currently active only when the operator is running in an OpenShift cluster instead of Kubernetes. This code should probably be adapted to use the Keycloak Gatekeeper when the platform isn't OpenShift. Ideally, we could even configure it automatically if the Keycloak Operator is found and a suitable Keycloak instance is found ;-)

func OAuthProxy(jaeger *v1.Jaeger, dep *appsv1.Deployment) *appsv1.Deployment {
if jaeger.Spec.Ingress.Security != v1.IngressSecurityOAuthProxy {
return dep
}
dep.Spec.Template.Spec.ServiceAccountName = account.OAuthProxyAccountNameFor(jaeger)
dep.Spec.Template.Spec.Containers = append(dep.Spec.Template.Spec.Containers, getOAuthProxyContainer(jaeger))
dep.Spec.Template.Spec.Volumes = append(dep.Spec.Template.Spec.Volumes, corev1.Volume{
Name: service.GetTLSSecretNameForQueryService(jaeger),
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: service.GetTLSSecretNameForQueryService(jaeger),
},
},
})
return dep
}

// we always set the value to None, except when we are on OpenShift *and* the user has not explicitly set to 'none'
if viper.GetString("platform") == v1.FlagPlatformOpenShift && jaeger.Spec.Ingress.Security != v1.IngressSecurityNoneExplicit {
jaeger.Spec.Ingress.Security = v1.IngressSecurityOAuthProxy
} else {
// cases:
// - omitted on Kubernetes
// - 'none' on any platform
jaeger.Spec.Ingress.Security = v1.IngressSecurityNoneExplicit
}

basch255 and others added 3 commits July 3, 2020 08:53
To enable using a sidecar for securing query
component (UI) the existing oauth-proxy
function is adapted and expanded to use it
also for a none-OpenShift platform.

Resolves: #1104
Signed-off-by: Bastian Schlegel <[email protected]>
Some smaller reverts of my first pull request which are not necessary anymore

Resolves: #1104
Signed-off-by: Bastian Schlegel <[email protected]>
Again a small revert of change from my first
pull request which are not necessary anymore

Resolves: #1104
Signed-off-by: Bastian Schlegel <[email protected]>
@basch255
Copy link
Author

basch255 commented Jul 3, 2020

Hello Mr. Krohling,

Thanks for your feedback to my pull request!
I tried to adapt the existing oauth-proxy function for OpenShift. The controller handles the explicitly set of jaeger.spec.ingress.security to oauth-proxy with the combination of a none-OpenShift platform to inject the sidecar.

At jaeger.spec.query.oauthProxy.image you´re able to configure another sidecar - default is quay.io/keycloak/keycloak-gatekeeper:10.0.0 at the moment. At jaeger.spec.query.oauthProxy.options you´re able to add arguments to the sidecar. An example is added, too.

Could you check the code and give me feedback again what is needed to confirm my pull request

@basch255
Copy link
Author

basch255 commented Jul 3, 2020

Unfortunately the e2e (es-otel) got a timeout - It worked with the same code already in my other (temp-)branch:
https://github.com/basch255/jaeger-operator/actions/runs/153740536

@jpkrohling
Copy link
Contributor

The same test failed on #1111. It might not be related to the PR at all.

cc @kevinearls

Copy link
Contributor

@jpkrohling jpkrohling left a comment

Choose a reason for hiding this comment

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

This looks great! There are a couple of comments, but I like the direction this is going. I also love that this is quite generic, not really tied to Keycloak specifically.

@stianst, do you have someone on your team that could review this one?

@@ -200,6 +200,21 @@ type JaegerQuerySpec struct {

// +optional
JaegerCommonSpec `json:",inline,omitempty"`

// +optional
OauthProxy JaegerQueryOauthProxySpec `json:"oauthProxy,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be a pointer, so that an empty object isn't persisted in case the user does not specify it.

pkg/apis/jaegertracing/v1/jaeger_types.go Outdated Show resolved Hide resolved
jaeger := v1.NewJaeger(types.NamespacedName{Name: "my-instance"})
jaeger.Spec.Ingress.Security = v1.IngressSecurityOAuthProxy
viper.Set("platform", "kubernetes")
Copy link
Contributor

Choose a reason for hiding this comment

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

This shouldn't be required for the test to pass.

// see https://github.com/openshift/oauth-proxy/issues/95
jaeger := v1.NewJaeger(types.NamespacedName{Name: "my-instance"})
jaeger.Spec.Ingress.Security = v1.IngressSecurityOAuthProxy
viper.Set("platform", "openshift")
Copy link
Contributor

Choose a reason for hiding this comment

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

Most of the tests add this as the first thing in the test, implying that this is set before anything we call ourselves.

Copy link

Choose a reason for hiding this comment

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

What do you suggest we do with this?

pkg/inject/oauth_proxy_test.go Outdated Show resolved Hide resolved
@@ -213,6 +289,40 @@ func TestOAuthProxyResourceLimits(t *testing.T) {
assert.Equal(t, *resource.NewQuantity(512, resource.DecimalSI), dep.Spec.Template.Spec.Containers[1].Resources.Requests[corev1.ResourceRequestsEphemeralStorage])
}

func TestOpenShiftOAuthProxyResourceLimits(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is exactly like the previous test, consider making it a table test, so that it's clear what's different between the tests.

@@ -157,12 +157,21 @@ func TestSetSecurityToOAuthProxyByDefaultOnOpenShift(t *testing.T) {
assert.Equal(t, v1.IngressSecurityOAuthProxy, jaeger.Spec.Ingress.Security)
}

func TestSetSecurityToNoneOnNonOpenShift(t *testing.T) {
func TestSetSecurityOnNonOpenShift(t *testing.T) {
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 not dependent on being on OpenShift anymore, so, this can be renamed, TestSetExplicitSecurity could be appropriate.

@stianst
Copy link

stianst commented Jul 7, 2020

@abstractj could you take a look at this?

@abstractj
Copy link

@stianst sure, no problem

Addressed most review comments and updated example with new format.

Resolves: #1104
Signed-off-by: Nils Böhling <[email protected]>
@nilsboeh
Copy link

nilsboeh commented Jul 8, 2020

Hello everybody,
thank you for your review and comments! I tried to adress all of them in my latest push. Since @basch255 is not available this week, I continue on this PR as best as I can.

@jpkrohling
Copy link
Contributor

jpkrohling commented Jul 8, 2020

@nilsboeh thank you for your work in this PR! There are still quite a few items open, and I would love to re-review this and give it a try once they are addressed.

# The redirection url, essentially the site url, note: /oauth/callback is added at the end
redirection-url: "https://jaeger-gatekeeper.example.com"
# Role restrictions for URL. User must have specified role in order to access.
resources: "uri:/admin/*|white-listed=true"
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you tested this configuration? I'm not sure this will work. We don't have a /admin in the Jaeger UI.

Copy link

Choose a reason for hiding this comment

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

My bad... The /admin does not belong there.

Copy link

@nilsboeh nilsboeh Jul 9, 2020

Choose a reason for hiding this comment

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

I corrected this but found another problem, especially with the resources option. The Gatekeeper accepts this option multiple times so you can define it for different subpages:

--enable-default-deny=true \
--resources="uri=/admin*|roles=test1,test2" \
--resources="uri=/backend*|roles=test1" \

Our current implementation with translation from yaml options to args only accepts this once and other notations (as map or multiple uri's separated with pipes) do not work properly.

Copy link
Author

Choose a reason for hiding this comment

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

We enabled adding volumeMounts to the oauth-proxy to configure it via a configuration file placed in a secret

nilsboeh and others added 2 commits July 9, 2020 13:57
Addressed recent review comments by changing default ContainerPort to 9091 and correcting the resources option in the example.

Resolves: #1104
Signed-off-by: Nils Böhling <[email protected]>
- OauthProxy object is now a pointer.
- OauthProxy configuration via config file in
  volumeMount enabled
- OauthProxy tests optimized
- OauthProxy ports changed for none-OpenShift
  platforms
- deploy example updated

Resolves: #1104
Signed-off-by: Bastian Schlegel <[email protected]>
@basch255
Copy link
Author

Hi everyone,

we tried to solve the remaining review comments. Please have a look.

@basch255
Copy link
Author

Hi,
@jpkrohling how do you handle conflicts here with new commits from master branch?

@jpkrohling
Copy link
Contributor

@basch255 because we need all commits to be signed-off, I can't just resolve them via GitHub's web interface, but looking at the conflict, the resolution is quite simple: just remove the viper.BindPFlag entry, so that it ends up looking like this:

	cmd.Flags().String("jaeger-es-rollover-image", "jaegertracing/jaeger-es-rollover", "The Docker image for the Jaeger Elasticsearch Rollover")
	cmd.Flags().String("oauth-proxy-image", "quay.io/keycloak/keycloak-gatekeeper:10.0.0", "The Docker image location definition for the Default OAuth Proxy")
	cmd.Flags().String("openshift-oauth-proxy-image", "openshift/oauth-proxy:latest", "The Docker image location definition for the OpenShift OAuth Proxy")

@basch255
Copy link
Author

@jpkrohling Thanks for your reply and the resolution. I wasn't sure I should merge the commits. Now the conflict is solved and my branch got an update from the master.

Copy link
Contributor

@jpkrohling jpkrohling left a comment

Choose a reason for hiding this comment

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

Thanks for your hard work, I think we are very close.

Here's a list of things I need before merging:

  1. instructions on how to setup an OAuth Proxy (Keycloak Gatekeeper) for testing. Without that, I don't know how to test this PR, and would make it hard for us to document and write e2e tests.
  2. make check fixing the formatting issues

Things that I would find awesome to have in this PR:

  1. e2e tests
  2. a PR against the documentation repo, showing how to get this working
  3. having the OpenShift OAuth Proxy as the first example of override for the new generic (default: Keycloak) OAuth proxy. I left a comment about it inline in the review.


// +optional
// +listType=set
VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

The indentation here looks odd, but the CI didn't catch this problem, apparently. Running make check locally did report this as a problem.

Anyway: could you please run make check and update the PR?

@@ -20,23 +20,59 @@ 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 {
return dep
if jaeger.Spec.Ingress.Security == v1.IngressSecurityOAuthProxy {
Copy link
Contributor

Choose a reason for hiding this comment

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

The old code was doing a short-circuit here: if the security isn't OAuth Proxy, then just return the dep. Everything else would be handled outside of an if statement. I find that approach more readable, but you can keep it this way if you prefer it.


ports := []corev1.ContainerPort{
{
ContainerPort: 9091,
Copy link
Contributor

Choose a reason for hiding this comment

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

A future improvement: allow the port to be customized, just like the image.

jaeger := v1.NewJaeger(types.NamespacedName{Name: "my-instance"})
jaeger.Spec.Ingress.Security = v1.IngressSecurityOAuthProxy
defer viper.Reset()
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no viper.Set in this method, it doesn't need a Reset.

return dep
}

func getOAuthProxyContainer(jaeger *v1.Jaeger) corev1.Container {
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I just realized while reviewing this PR: we want the generic OAuth feature to allow users to specify their own OAuth Proxies, so that we are not tied to Keycloak specifically.

With that in mind, the first consumer that overrides the default OAuth Proxy could be the operator itself, when running on OpenShift. Meaning: the code path for the generic OAuth proxy could be used for both OpenShift and non-OpenShift, with the OpenShift path only customizing the OAuth parts (image/args).

I know it might sound a bit frustrating to hear this at this point in the PR, but it just clicked with me. For some reason, I believe you'll also realize that :)

@@ -5,6 +5,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/spf13/viper"
Copy link
Contributor

Choose a reason for hiding this comment

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

This import belongs to the second (first in this case) import group.

@basch255
Copy link
Author

basch255 commented Aug 6, 2020

Hi,
updates are ongoing. Currently we have less time but won´t forget.
Sorry about that

@chlunde
Copy link
Contributor

chlunde commented Sep 9, 2020

I think the docs / examples should not refer to Keycloak Gatekeeper if this is approved: Sunsetting Louketo Project louketo/louketo-proxy#683

@jpkrohling
Copy link
Contributor

Back to the drawing board, I guess. I'm closing this, but I'm really looking forward for a new proposal.

@jpkrohling jpkrohling closed this Sep 10, 2020
@chlunde
Copy link
Contributor

chlunde commented Sep 10, 2020

@jpkrohling FYI, I manually set up a sidecar for our jaeger (we have a few customized the deployment a bit) with https://github.com/oauth2-proxy/oauth2-proxy/ connected to keycloak (using keycloak-operator + patches, pending review..)

Here is the kustomize.yaml patch i used for a POC:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger-query
spec:
  template:
    spec:
      containers:
      - name: oauth-proxy
        image: quay.io/oauth2-proxy/oauth2-proxy:v6.1.1
        args:
        - --cookie-secret=AAAAAAAAAAAAAAAA # has different length requirements, 16/32 chars, compared to sidecar in openshift now
        - --tls-cert-file=/etc/tls/private/tls.crt # please note "-file" difference compared to openshift sidecar
        - --tls-key-file=/etc/tls/private/tls.key
        - --provider-ca-file=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem # hack, I injected this using a configmap w/ config.openshift.io/inject-trusted-cabundle: "true". Just note that this is not a RHEL based image
        - --https-address=:8443
        - --upstream=http://localhost:16686
        - --provider=keycloak
        - --email-domain=$DOMAIN
        - --client-id=jaeger-ci
        - --client-secret=...hard-coded-for-testing..
        - --login-url=https://keycloak.internal/auth/realms/$REALM/protocol/openid-connect/auth
        - --redeem-url=https://keycloak.internal/auth/realms/$REALM/protocol/openid-connect/token
        - --validate-url=https://keycloak.internal/auth/realms/$REALM/protocol/openid-connect/userinfo
#        - --keycloak-group=<user_group>

One issue is that the KeycloakClient creates a client secret object, which must be mounted here, but I don't think the keycloak operator looks for KeycloakClients in other namespaces than where keycloak-operator is installed. So there is a dependency to a secret in another namespace. Unless I missed a flag for making it look in all namespaces, I hope so! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

secure query component with a sidecar
6 participants