Aller au contenu principal

Sécurité Jenkins


Introduction

La sécurité de Jenkins est critique car il a accès à :

  • Code source
  • Credentials (mots de passe, tokens, clés)
  • Infrastructure de production
  • Pipelines de déploiement

Authentification

Base de données interne

# jenkins.yaml
jenkins:
securityRealm:
local:
allowsSignup: false # Désactiver l'inscription
users:
- id: "admin"
name: "Administrator"
password: "${JENKINS_ADMIN_PASSWORD}"
- id: "developer"
name: "Developer User"
password: "${DEV_PASSWORD}"

LDAP

jenkins:
securityRealm:
ldap:
configurations:
- server: "ldap.example.com"
rootDN: "dc=example,dc=com"
userSearchBase: "ou=users"
userSearch: "uid={0}"
groupSearchBase: "ou=groups"
groupMembershipStrategy:
fromGroupSearch:
filter: "member={0}"
managerDN: "cn=admin,dc=example,dc=com"
managerPasswordSecret: "${LDAP_PASSWORD}"

Active Directory

jenkins:
securityRealm:
activeDirectory:
domains:
- name: "example.com"
servers: "ad.example.com"
bindName: "[email protected]"
bindPassword: "${AD_PASSWORD}"
groupLookupStrategy: RECURSIVE
removeIrrelevantGroups: true

SAML (SSO)

jenkins:
securityRealm:
saml:
idpMetadataConfiguration:
url: "https://idp.example.com/metadata"
displayNameAttributeName: "displayName"
emailAttributeName: "email"
groupsAttributeName: "groups"
usernameAttributeName: "username"

GitHub OAuth

jenkins:
securityRealm:
github:
githubWebUri: "https://github.com"
githubApiUri: "https://api.github.com"
clientID: "${GITHUB_CLIENT_ID}"
clientSecret: "${GITHUB_CLIENT_SECRET}"
oauthScopes: "read:org,user:email"

Autorisation

Matrix Authorization

jenkins:
authorizationStrategy:
globalMatrix:
permissions:
# Administrateurs
- "Overall/Administer:admin"
- "Overall/Administer:admin-group"

# Développeurs
- "Overall/Read:developers"
- "Job/Build:developers"
- "Job/Read:developers"
- "Job/Workspace:developers"

# Ops
- "Overall/Read:ops"
- "Job/Build:ops"
- "Job/Read:ops"
- "Job/Configure:ops"

# Lecture seule pour authenticated
- "Overall/Read:authenticated"
- "Job/Read:authenticated"

Project-based Matrix

jenkins:
authorizationStrategy:
projectMatrix:
permissions:
# Permissions globales
- "Overall/Administer:admin"
- "Overall/Read:authenticated"

# Permissions par projet définies dans le job

Role-based Strategy

jenkins:
authorizationStrategy:
roleBased:
roles:
global:
- name: "admin"
permissions:
- "Overall/Administer"
entries:
- user: "admin"
- name: "readonly"
permissions:
- "Overall/Read"
entries:
- group: "authenticated"

items:
- name: "frontend-dev"
pattern: "frontend-.*"
permissions:
- "Job/Build"
- "Job/Read"
- "Job/Configure"
entries:
- group: "frontend-team"

- name: "backend-dev"
pattern: "backend-.*"
permissions:
- "Job/Build"
- "Job/Read"
- "Job/Configure"
entries:
- group: "backend-team"

Gestion des Credentials

Types de credentials

TypeUsage
Username/PasswordGit, Docker, API
Secret textTokens API
Secret fileKubeconfig, certificats
SSH Username with keyConnexion serveurs
CertificatemTLS

Créer via JCasC

credentials:
system:
domainCredentials:
- credentials:
# Username/Password
- usernamePassword:
scope: GLOBAL
id: "github-credentials"
description: "GitHub Access"
username: "${GITHUB_USER}"
password: "${GITHUB_TOKEN}"

# Secret text
- string:
scope: GLOBAL
id: "sonar-token"
description: "SonarQube Token"
secret: "${SONAR_TOKEN}"

# Secret file
- file:
scope: GLOBAL
id: "kubeconfig"
description: "Kubernetes Config"
fileName: "kubeconfig"
secretBytes: "${base64:${KUBECONFIG_CONTENT}}"

# SSH Key
- basicSSHUserPrivateKey:
scope: GLOBAL
id: "ssh-deploy-key"
description: "SSH Deploy Key"
username: "deploy"
privateKeySource:
directEntry:
privateKey: "${SSH_PRIVATE_KEY}"

Utilisation sécurisée

pipeline {
agent any

stages {
stage('Deploy') {
steps {
// Les secrets sont masqués dans les logs
withCredentials([
usernamePassword(
credentialsId: 'docker-hub',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)
]) {
sh '''
# Le mot de passe est masqué
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
'''
}
}
}
}
}

Scopes des credentials

ScopeVisibilité
GLOBALTout Jenkins
SYSTEMController uniquement
FolderUn dossier et ses enfants

Sécurisation HTTPS

Avec reverse proxy (recommandé)

# /etc/nginx/sites-available/jenkins
server {
listen 80;
server_name jenkins.example.com;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name jenkins.example.com;

ssl_certificate /etc/ssl/certs/jenkins.crt;
ssl_certificate_key /etc/ssl/private/jenkins.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# WebSocket support (pour agents)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

Jenkins direct

# Générer un keystore
keytool -genkey -keyalg RSA \
-alias jenkins \
-keystore jenkins.jks \
-validity 365

# Lancer Jenkins avec HTTPS
java -jar jenkins.war \
--httpPort=-1 \
--httpsPort=8443 \
--httpsKeyStore=/path/to/jenkins.jks \
--httpsKeyStorePassword=changeit

Sécurité des pipelines

Script Security

// Les scripts non approuvés sont bloqués
// Nécessite approbation dans:
// Manage Jenkins > In-process Script Approval

// Sandbox Groovy - Limité mais sécurisé
@Library('my-lib') _

pipeline {
agent any
stages {
stage('Build') {
steps {
// Scripts dans la sandbox
script {
def result = sh(script: 'echo test', returnStdout: true)
}
}
}
}
}

Limiter les permissions des pipelines

// Désactiver des fonctionnalités dangereuses
properties([
[$class: 'BuildDiscarderProperty',
strategy: [$class: 'LogRotator', numToKeepStr: '10']],
disableConcurrentBuilds()
])

Validation des Jenkinsfiles

# Valider un Jenkinsfile avant commit
curl -X POST \
-u admin:token \
-F "jenkinsfile=<Jenkinsfile" \
http://jenkins:8080/pipeline-model-converter/validate

Audit et logging

Audit Trail Plugin

unclassified:
auditTrail:
logBuildCause: true
pattern: ".*/(?:configSubmit|doDelete|postBuildResult|enable|disable|cancelQueue).*"
loggers:
- syslog:
syslogServerHostname: "syslog.example.com"
syslogServerPort: 514
appName: "jenkins"
facility: "USER"

Logs d'accès

# Dans jenkins.yaml
jenkins:
log:
recorders:
- name: "security-log"
loggers:
- name: "jenkins.security"
level: "FINE"
- name: "hudson.security"
level: "FINE"

Checklist de sécurité

□ HTTPS activé
□ Inscription désactivée (allowsSignup: false)
□ Authentification configurée (LDAP/OAuth)
□ Autorisations Matrix configurées
□ Credentials chiffrés (pas en clair dans les jobs)
□ Agents sécurisés (SSH ou JNLP avec secret)
□ Script Security activé
□ Mise à jour régulière (Jenkins + plugins)
□ Backup des configurations
□ Audit trail activé
□ CSRF Protection activée (par défaut)
□ Agent-to-Controller Security activé

Configuration sécurisée complète

# jenkins.yaml
jenkins:
systemMessage: "Jenkins - Production Environment"

# Désactiver les builds sur le controller
numExecutors: 0
mode: EXCLUSIVE

# Sécurité
securityRealm:
ldap:
configurations:
- server: "ldap.example.com"
rootDN: "dc=example,dc=com"

authorizationStrategy:
roleBased:
roles:
global:
- name: "admin"
permissions: ["Overall/Administer"]
entries:
- group: "jenkins-admins"
- name: "developer"
permissions: ["Overall/Read", "Job/Build", "Job/Read"]
entries:
- group: "developers"

# Protection CSRF
crumbIssuer:
standard:
excludeClientIPFromCrumb: false

# Sécurité Agent-Controller
remotingSecurity:
enabled: true

security:
scriptApproval:
approvedSignatures: [] # Rien d'approuvé par défaut

globalJobDslSecurityConfiguration:
useScriptSecurity: true

unclassified:
location:
url: "https://jenkins.example.com/"
adminAddress: "[email protected]"

← Agents | Bonnes Pratiques →