jenkinsci / oidc-provider-plugin

OpenID Connect Provider Plugin for Jenkins
https://plugins.jenkins.io/oidc-provider/
MIT License
22 stars 13 forks source link

Allow passing in private/public key from casc #19

Open patrickpichler opened 1 year ago

patrickpichler commented 1 year ago

What feature do you want to see added?

In our setup, the jenkins instance is wiped with each redeploy and recreated from casc. This is done to enforce people to configure their jobs/configuration as code.

Of course this doesn't play well with the OIDC provider plugin, as the secret get re-created on each jenkins redeploy.

My proposed solution would be, to allow passing in private/public key via constructor and thus allowing it to be specified in casc. Of course you would not check in the value as plain text, but pass them in via environment variables.

Upstream changes

No response

jglick commented 1 year ago

the secret get re-created on each jenkins redeploy

Why would that be a problem? Jenkins would begin to serve the new public key from the OIDC discovery endpoint, and relying parties should honor the new public key when validating id tokens signed with the new private key.

Put another way, it is a fundamental part of the design of this plugin that when creating credentials you are not prompted for a key, you never see a key, you never manage a key. The keypair is not intended to be part of the visible configuration. #3 would even make it ephemeral.

patrickpichler commented 1 year ago

I forgot to mention that we are using the external issuer option.

Otherwise you are of course correct, the OIDC discovery endpoint would handle it. Do you have any recommendation how to solve this? I thought about a job which pushes the files to a webhost, but this would leave a window in which pipelines run and the exposed public key would be invalid.

jglick commented 1 year ago

Ah external issuer. Yes I think some sort of cron job (whether inside Jenkins or out) which mirrors the current https://jenkins/oidc/.well-known/openid-configuration and (especially) https://jenkins/oidc/jwks to the web server is what you want, but then indeed #3 becomes more important.

bdellegrazie commented 9 months ago

@jglick The way I see it is there is no way to replicate this information (easily) to a separate website so: 1) Can the keys be persisted so that at least they're not automatically rotated during a Jenkins restart? 2) Can we host the public parts in an unauthenticated part of Jenkins (ala #26)? - we can work around that at the moment by using a privileged user with an API key but it's a but it's overkill for what is essentially public information.

This is a great plugin and works quite well for what we're doing - but having it change creds on restart is painful.

jglick commented 9 months ago

they're not automatically rotated during a Jenkins restart?

They are not. This issue is about recreating the controller from scratch, not a restart with configuration in place.

Can we host the public parts in an unauthenticated part of Jenkins

The public key is served from an unauthenticated URL space already.

bdellegrazie commented 9 months ago

@jglick

They are not. This issue is about recreating the controller from scratch, not a restart with configuration in place.

Apologies for being mistaken, where are they stored then? While I recreate the controller from scratch via CasC, Jenkins home is persisted but the credentials were recreated.

The public key is served from an unauthenticated URL space already.

Even when an external issuer is used? (Jenkins is not publicly accessible in my case)

Thanks!

jglick commented 9 months ago

I recreate the controller from scratch via CasC

Hence this issue. Very different from merely restarting a controller, which should not rotate keypairs.

Even when an external issuer is used?

Oh not in that case. https://github.com/jenkinsci/oidc-provider-plugin/blob/30947d57386ddbcfb9195d557576fdc8da93943f/src/main/java/io/jenkins/plugins/oidc_provider/Keys.java#L72-L75 Need to run end-to-end testing of that scenario (#11).

DavidRomao commented 8 months ago

I'm in the same exact scenario as @bdellegrazie. Having the key rotate when Jenkins is recreated doesn't work well when using an External Issuer which is Isolated from Jenkins. Defining the keys through CasC and passing a secret would address this problem.

I understand that at the moment the key is totally managed by the plugin and that's how it was intended. But this really doesn't work well at all when using an external issuer.

bdellegrazie commented 8 months ago

@DavidRomao Just FYI - I've worked around this by using the Jenkins API to get the public key after a re-creation to put the external issuer content from Jenkins. It's annoying but at least it's largely automated.

DavidRomao commented 8 months ago

@DavidRomao Just FYI - I've worked around this by using the Jenkins API to get the public key after a re-creation to put the external issuer content from Jenkins. It's annoying but at least it's largely automated.

Thanks, I'm considering that option !

Louai-Abdelsalam commented 3 months ago

https://github.com/jenkinsci/oidc-provider-plugin/issues/19#issuecomment-1850841870

Apologies for being mistaken, where are they stored then?

I'd like to know that too actually. Where are they stored? Security isn't my area of expertise but if they aren't available only in the runtime code execution, that means they're presumably on a file / db somewhere and so are grab-able, right?

Louai-Abdelsalam commented 3 months ago

#19 (comment)

Apologies for being mistaken, where are they stored then?

I'd like to know that too actually. Where are they stored? Security isn't my area of expertise but if they aren't available only in the runtime code execution, that means they're presumably on a file / db somewhere and so are grab-able, right?

I think I found the private key actually in encrypted form in $JENKINS_HOME/credentials.xml, along with all other credentials. I always wondered where jenkins stored credentials whenever I had to decrypt any of them using the API, but I never bothered looking into it because most of my secrets weren't really that critical, but the OIDC private key definitely is given what I need to use it for.

Now as far as I've been able to tell from this and this, if someone has access to both the encrypted forms of the credentials as well as the files under $JENKINS_HOME/secrets (which are mostly keys), they're able to decrypt their way back to the true values of the credentials.

Technically you can add your line of defense at who is able to access your jenkins-hosting server, but it's probably a good idea to also auto rotate the OIDC keypair frequently; I'm thinking maybe through a job that runs once or twice a day by recreating the id token credential (and updating the publicly served jwks file if you're using an external issuer) though I haven't decided yet. I'm not sure how helpful something like https://github.com/jenkinsci/oidc-provider-plugin/issues/3 would be strictly in this context given that I'm assuming any such additional keypairs would also have their private keys saved on the filesystem by jenkins in the same manner as all other credentials (but like I said before, security isn't my area of expertise so please correct me if I'm wrong)

Anyway, I know all this is kind of a tangent but I thought about sharing my thoughts in case anyone has a similar concern. If anything I've said (where & how creds are stored / concerns / etc) is wrong or incomplete, please correct me for my and anyone else's benefit. Thanks.

jglick commented 3 months ago

who is able to access your jenkins-hosting server

Anyone with direct filesystem (shell) access to $JENKINS_HOME is presumed to be some sort of superuser who could also rewrite any piece of the software however they liked, as well as obtaining the current value of any credentials, insert Trojan horses, etc.

Using this plugin should be safer than storing static credentials in Jenkins, since even if files on $JENKINS_HOME are leaked, the keypair can simply be rotated and any relying parties such as AWS IAM will begin only honoring id tokens signed with the new private key; and you cannot spoof AWS into using a different public key without having control of the web server / domain / TLS / etc. that serves Jenkins content. #3 would help because this rotation would happen routinely, and so a leaked private key (without tampering with any files) could at most be used to mint forged id tokens for a few hours or whatever the rotation period is set to.

These discussions remind me that it would be helpful to publish a clear threat model for the plugin.