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

Don't use the 'legacy' security realm #20

Closed
daniel-beck opened this issue Sep 2, 2020 · 21 comments
Closed

Don't use the 'legacy' security realm #20

daniel-beck opened this issue Sep 2, 2020 · 21 comments

Comments

@daniel-beck
Copy link
Member

I spent a few hours helping a colleague with his Jenkins instance recently that didn't want to let him authenticate using username/API token, complaining about a missing CSRF token/crumb, which shouldn't be needed since Jenkins 2.96.

Turns out this chart configures the legacy security realm which is basically untested, and completely ignored for new development.

<securityRealm class="hudson.security.LegacySecurityRealm"/>

It probably shouldn't declare a security realm whose name already recommends against its use.

@jglick
Copy link
Member

jglick commented Sep 2, 2020

I suppose it would be helpful if there were a security realm for Jenkins which delegates to the Kubernetes cluster’s authentication system, but as far as I can tell there is none. The closest thing I can find is this. Anyway for a default realm to get people started we generally recommend HudsonPrivateSecurityRealm, or simply let the GUI setup wizard run so as to get an initial admin user.

@torstenwalter
Copy link
Member

For production use one probably wants to change both authorizationStrategy and securityRealm:

{{- if and (.Values.master.JCasC.authorizationStrategy) (not (contains "authorizationStrategy:" $configScripts)) }}
authorizationStrategy:
{{- tpl .Values.master.JCasC.authorizationStrategy . | nindent 4 }}
{{- end }}
{{- if and (.Values.master.JCasC.securityRealm) (not (contains "securityRealm:" $configScripts)) }}
securityRealm:
{{- tpl .Values.master.JCasC.securityRealm . | nindent 4 }}
{{- end }}

XML configuration is something we want to get rid of completely see #10.

On the other hand I don't want to have a default configuration which uses no authentication at all.
I think there is nothing like a Kubernetes authentication as that largely dependends on you setup. Could be OIDC or completely out of you control. So sometimes you just want to use good old LDAP or Active Directory authentication.

What would be a reasonable default here instead of legacy?

@jglick
Copy link
Member

jglick commented Sep 2, 2020

What would be a reasonable default here instead of legacy?

As mentioned above, specify nothing, but let the setup wizard run. It will offer an initial admin account with a randomized password, and set the authorization strategy to let authenticated users do anything and anonymous users are forced to log in.

@torstenwalter
Copy link
Member

I like the approach. The downside of the setup wizard is that it requires manual actions and is therefore not completely automated.

On the other hand it would be great to get rid of the secret which contains the admin password.

@daniel-beck
Copy link
Member Author

JCasC can pre-configure a security realm with users defined, that's probably a reasonable approach to fully automate: https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/demos/embedded-userdatabase/README.md

@jglick
Copy link
Member

jglick commented Sep 2, 2020

pre-configure a security realm with users defined

Right but then you have to hard-code an admin password, right?

@timja
Copy link
Member

timja commented Sep 7, 2020

@jglick by default the chart auto generates a password IIRC

you can also specify it yourself and at least with jcasc not sure about this chart you can provide a pre-hashed password.

and providing secrets to helm values isn't normally an issue, sops, sealed-secrets, git-crypt etc are some of the many solutions for hiding secrets values inputs

@torstenwalter
Copy link
Member

I can shed some light on how it's currently implemented:

Admin credentials can be configured via value file:

adminUser: "admin"
# adminPassword: <defaults to random>
admin:
existingSecret: ""
userKey: jenkins-admin-user
passwordKey: jenkins-admin-password

The admin password is empty by default. In that case it will be auto generated. It is possible to provide a password via values file, but I rather recommend to use an existing secret instead as I believe secret values should not be passed via helm values.

If the case an existing secret is not provided this template renders a secret:

{{- if and (not .Values.master.admin.existingSecret) (.Values.master.useSecurity) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "jenkins.fullname" . }}
namespace: {{ template "jenkins.namespace" . }}
labels:
"app.kubernetes.io/name": '{{ template "jenkins.name" .}}'
"helm.sh/chart": "{{ .Chart.Name }}-{{ .Chart.Version }}"
"app.kubernetes.io/managed-by": "{{ .Release.Service }}"
"app.kubernetes.io/instance": "{{ .Release.Name }}"
"app.kubernetes.io/component": "{{ .Values.master.componentName }}"
type: Opaque
data:
{{ if .Values.master.adminPassword -}}
jenkins-admin-password: {{ .Values.master.adminPassword | b64enc | quote }}
{{ else -}}
jenkins-admin-password: {{ randAlphaNum 10 | b64enc | quote }}
{{ end -}}
jenkins-admin-user: {{ .Values.master.adminUser | b64enc | quote }}
{{- end }}

That's also the place where the random password is generated (jenkins-admin-password: {{ randAlphaNum 10 | b64enc | quote }})

Username and password are then exposed as environment variables to the Jenkins container

{{- if .Values.master.useSecurity }}
- name: ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.master.admin.existingSecret | default (include "jenkins.fullname" .) }}
key: {{ .Values.master.admin.password | default "jenkins-admin-password" }}
- name: ADMIN_USER
valueFrom:
secretKeyRef:
name: {{ .Values.master.admin.existingSecret | default (include "jenkins.fullname" .) }}
key: {{ .Values.master.admin.user | default "jenkins-admin-user" }}
{{- end }}

and passed as args to Jeknins

args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)", "--argumentsRealm.roles.$(ADMIN_USER)=admin", "--httpPort={{.Values.master.targetPort}}"]

@hferentschik
Copy link

@daniel-beck,

JCasC can pre-configure a security realm with users defined, that's probably a reasonable approach to fully automate: https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/demos/embedded-userdatabase/README.md

Thanks that actually worked for my use case. I ended up using the following values.yaml:

master:
  JCasC:
    configScripts:
      security: |
        jenkins:
          securityRealm:
            local:
              allowsSignup: false
              users:
                - id: "admin"
                  password: "admin"
          authorizationStrategy: loggedInUsersCanDoAnything

That works fine and using this setup the REST API is working fine :-)

The status message one gets during the Helm install is confusing though:

helm install jenkins jenkinsci/jenkins -f values.yaml
NAME: jenkins
LAST DEPLOYED: Thu Sep 17 11:14:39 2020
NAMESPACE: jenkins
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:
  printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
  export POD_NAME=$(kubectl get pods --namespace jenkins -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=jenkins" -o jsonpath="{.items[0].metadata.name}")
  echo http://127.0.0.1:8080
  kubectl --namespace jenkins port-forward $POD_NAME 8080:8080

3. Login with the password from step 1 and the username: admin

4. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos

For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
For more information about Jenkins Configuration as Code, visit:
https://jenkins.io/projects/jcasc/

It seems there is still the default Secret created which one can read via printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);, however, the actual password for logging in is in the JCasC script. Probably this cannot be easily solved, but maybe a hint/warning could be added to the message?

@daniel-beck
Copy link
Member Author

Looks like it would be a straightforward PR changing https://github.com/jenkinsci/helm-charts/blob/master/charts/jenkins/templates/NOTES.txt

@hferentschik
Copy link

Looks like it would be a straightforward PR changing https://github.com/jenkinsci/helm-charts/blob/master/charts/jenkins/templates/NOTES.txt

Just adding a message would be trivial, yes. Finding out whether the default security context gets overridden might be harder.

@torstenwalter torstenwalter added this to the v3.0.0 release milestone Sep 28, 2020
@stale stale bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Nov 1, 2020
@jenkinsci jenkinsci deleted a comment from stale bot Nov 1, 2020
@stale stale bot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Nov 1, 2020
@torstenwalter
Copy link
Member

I somehow missed this discussion.

It's great that we can configure admin credentials via JCasC. However we should not put the password directly in the config as secrets should not be stored in a ConfigMap.

Configuration as Code plugin allows to reference secrets.
https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/secrets.adoc

So a way forward could be to configure admin via JCasC and reference the generated secret.

@timja What do you think?

@timja
Copy link
Member

timja commented Nov 1, 2020

Makes sense as long as it’s easy to disable, I would expect many users will be configuring SSO after any initial testing is done and don’t need this.

So this should have sensible defaults (not using legacy and creating an admin user) but also have a simple way to not have this run

@torstenwalter
Copy link
Member

If I remember correctly then useSecurity flag is used to configure admin user. If we give it a better name and document it in readme then we could use that mechanism.

@timja
Copy link
Member

timja commented Nov 1, 2020

If I remember correctly then useSecurity flag is used to configure admin user. If we give it a better name and document it in readme then we could use that mechanism.

maybe just security, not using security with jenkins is just asking for trouble.

It likely maps to the old boolean in the jenkins web ui which was use security but that was removed months ago

@torstenwalter
Copy link
Member

I meant the master.useSecurity boolean which controls creation of the secret for the admin user and creation of the admin user via parameters.

I think we need a better name for it like controller.testAdminUser. recommending to disable security (useSecurity: false) feels strange.

We could also render a warn message in NOTES if that setting is used to tell the user that this is just for getting started and they need to configure something better for production use.

Anyhow this would be something for the 3.0.0 release as it's a breaking change. I want to avoid resetting security configuration for instance which configured things manually without JCasC.

@jglick
Copy link
Member

jglick commented Nov 2, 2020

@stale
Copy link

stale bot commented Dec 4, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Any further update will cause the issue/pull request to no longer be considered stale. Thank you for your contributions.

@stale stale bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Dec 4, 2020
@daniel-beck
Copy link
Member Author

stalebot is seriously the worst bot I have ever seen.

@stale stale bot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Dec 4, 2020
@torstenwalter
Copy link
Member

Closed by #158

@torstenwalter
Copy link
Member

stalebot is seriously the worst bot I have ever seen.

@daniel-beck you are welcome to help as contributor to work on issues and create PRs which resolve them.

This issue was closed.
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

No branches or pull requests

5 participants