14 KiB
| title | linkTitle | weight | date | description |
|---|---|---|---|---|
| Security | Security | 8 | 2019-08-05 | Jenkins security and hardening out of the box |
By default Jenkins Operator performs an initial security hardening of Jenkins instance via groovy scripts to prevent any security gaps.
Jenkins Access Control
Currently Jenkins Operator generates a username and random password and stores them in a Kubernetes Secret. However any other authorization mechanisms are possible and can be done via groovy scripts or configuration as code plugin. For more information take a look at getting-started#jenkins-customization.
Any change to Security Realm or Authorization requires that user called jenkins-operator must have admin rights
because Jenkins Operator calls Jenkins API.
Jenkins Hardening
The list below describes all the default security setting configured by the Jenkins Operator:
- basic settings - use
Mode.EXCLUSIVE- Jobs must specify that they want to run on master node - enable CSRF - Cross Site Request Forgery Protection is enabled
- disable usage stats - Jenkins usage stats submitting is disabled
- enable master access control - Slave to Master Access Control is enabled
- disable old JNLP protocols -
JNLP3-connect,JNLP2-connectandJNLP-connectare disabled - disable CLI - CLI access of
/cliURL is disabled - configure kubernetes-plugin - secure configuration for Kubernetes plugin
If you would like to dig a little bit into the code, take a look here.
Jenkins API
The Jenkins Operator generates and configures Basic Authentication token for Jenkins Go client and stores it in a Kubernetes Secret.
Kubernetes
Kubernetes API permissions are limited by the following roles:
Since Jenkins Operator must be able to grant permission for its deployed Jenkins masters
to spawn pods (the Jenkins Master role above),
the operator itself requires permission to create RBAC resources (the jenkins-operator role above).
Deployed this way, any subject which may create a Pod (including a Jenkins job) may
assume the jenkins-operator role by using its' ServiceAccount, create RBAC rules, and thus escape its granted permissions.
Any namespace to which the jenkins-operator is deployed must be considered to implicitly grant all
possible permissions to any subject which can create a Pod in that namespace.
To mitigate this issue Jenkins Operator should be deployed in one namespace, and the Jenkins CR should be created in a separate namespace. Section below contains instructions on how to do it.
Setup Jenkins Operator and Jenkins in separate namespaces
You need to create two namespaces, for example we'll call them jenkins for Jenkins and jenkins-operator for Jenkins Operator.
$ kubectl create ns jenkins-operator
$ kubectl create ns jenkins
Next, you need to install resources necessary for the Operator to work in the jenkins-operator namespace. To do that,
copy the manifest you see below to jenkins-operator-rbac.yamlfile.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-operator
---
# permissions to do leader election.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader-election-role
rules:
- apiGroups:
- ""
- coordination.k8s.io
resources:
- configmaps
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: leader-election-role
subjects:
- kind: ServiceAccount
name: jenkins-operator
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: jenkins-operator
rules:
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- apps
- jenkins-operator
resources:
- deployments/finalizers
verbs:
- update
- apiGroups:
- build.openshift.io
resources:
- buildconfigs
- builds
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- get
- list
- patch
- watch
- apiGroups:
- ""
resources:
- persistentvolumeclaims
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- pods
- pods/exec
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- image.openshift.io
resources:
- imagestreams
verbs:
- get
- list
- watch
- apiGroups:
- jenkins.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- jenkins.io
resources:
- jenkins
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- jenkins.io
resources:
- jenkins/finalizers
verbs:
- update
- apiGroups:
- jenkins.io
resources:
- jenkins/status
verbs:
- get
- patch
- update
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- roles
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- create
- get
- list
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-operator
subjects:
- kind: ServiceAccount
name: jenkins-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins-operator
Now install the required resources in jenkins-operator namespace with:
kubectl apply -n jenkins-operator -f jenkins-operator-rbac.yaml
There's only one thing left to install in jenkins-operator namespace, and that is the Operator itself. The manifest
below contains the Operator as defined in all-in-one manifest found in Installating the Operator
page, the only difference is that the one here sets WATCH_NAMESPACE to the jenkins namespace we created.
Copy its content to jenkins-operator.yaml file.
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-operator
labels:
control-plane: controller-manager
spec:
selector:
matchLabels:
control-plane: controller-manager
replicas: 1
template:
metadata:
labels:
control-plane: controller-manager
spec:
serviceAccountName: jenkins-operator
securityContext:
runAsUser: 65532
containers:
- command:
- /manager
args:
- --leader-elect
image: virtuslab/jenkins-operator:v0.6.0
name: jenkins-operator
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
env:
- name: WATCH_NAMESPACE
value: jenkins
terminationGracePeriodSeconds: 10
Install the Operator in jenkins-operator namespace with:
kubectl apply -n jenkins-operator -f jenkins-operator.yaml
You have installed the Operator in jenkins-operator namespace, watching for Jenkins in jenkins namespace. Now
there are two things left to do: creating necessary Role and RoleBinding for the Operator in jenkins namespace, and
deploying actual Jenkins instance there.
Below you can find manifest with RBAC that need to be created in jenkins namespace. Copy its content to jenkins-ns-rbac.yaml file.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: jenkins-operator
rules:
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- apps
- jenkins-operator
resources:
- deployments/finalizers
verbs:
- update
- apiGroups:
- build.openshift.io
resources:
- buildconfigs
- builds
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- get
- list
- patch
- watch
- apiGroups:
- ""
resources:
- persistentvolumeclaims
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- pods
- pods/exec
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods/log
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/portforward
verbs:
- create
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- image.openshift.io
resources:
- imagestreams
verbs:
- get
- list
- watch
- apiGroups:
- jenkins.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- jenkins.io
resources:
- jenkins
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- jenkins.io
resources:
- jenkins/finalizers
verbs:
- update
- apiGroups:
- jenkins.io
resources:
- jenkins/status
verbs:
- get
- patch
- update
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
- roles
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- create
- get
- list
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-operator
subjects:
- kind: ServiceAccount
name: jenkins-operator
namespace: jenkins-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: jenkins-operator
Now apply it with:
kubectl apply -n jenkins -f jenkins-ns-rbac.yaml
The last thing to do is to deploy Jenkins. Below you can find an example Jenkins resource manifest.
It's the same as one used in Deploying Jenkins.
Copy it to jenkins-instance.yaml
apiVersion: jenkins.io/v1alpha2
kind: Jenkins
metadata:
name: example
spec:
configurationAsCode:
configurations: []
secret:
name: ""
groovyScripts:
configurations: []
secret:
name: ""
jenkinsAPISettings:
authorizationStrategy: createUser
master:
disableCSRFProtection: false
containers:
- name: jenkins-master
image: jenkins/jenkins:2.277.4-lts-alpine
imagePullPolicy: Always
livenessProbe:
failureThreshold: 12
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 100
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 10
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 80
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 1500m
memory: 3Gi
requests:
cpu: "1"
memory: 500Mi
seedJobs:
- id: jenkins-operator
targets: "cicd/jobs/*.jenkins"
description: "Jenkins Operator repository"
repositoryBranch: master
repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git
Now you can deploy it with:
kubectl apply -n jenkins -f jenkins-instance.yaml
With this, you have just set up Jenkins Operator and Jenkins in separate namespaces. Now the Operator will run in
its own namespace (jenkins-operator), watch for CRs in jenkins namespace, and deploy Jenkins there.
Report a Security Vulnerability
If you find a vulnerability or any misconfiguration in Jenkins, please report it in the issues.