Installation et configuration¶
Prerequis¶
| Composant | Version minimale | Objectif |
|---|---|---|
| Podman | 4.0+ | Runtime conteneurs |
| Podman Compose | 1.0+ | Orchestration multi-conteneurs |
| PostgreSQL | 15+ | Backend de persistance SonarQube |
| Certificat TLS | Let's Encrypt ou PKI interne | Chiffrement HTTPS |
| DNS | Enregistrement sonar.example.com | Accès au service |
| RAM | 4 Go minimum (8 Go recommande) | SonarQube + Elasticsearch + PG |
| Stockage | SSD 50 Go minimum | Elasticsearch performant |
Déploiement SonarQube avec Podman Compose¶
Préparation du système¶
# Creer l'utilisateur dedie
sudo useradd -r -m -s /sbin/nologin sonarqube
# Creer les repertoires
sudo -u sonarqube mkdir -p /home/sonarqube/{certs,data,extensions,logs}
# Ajuster les limites systeme pour Elasticsearch
echo "vm.max_map_count=524288" | sudo tee -a /etc/sysctl.d/99-sonarqube.conf
echo "fs.file-max=131072" | sudo tee -a /etc/sysctl.d/99-sonarqube.conf
sudo sysctl --system
# Copier les certificats TLS
sudo cp /etc/pki/tls/certs/sonar.example.com.{crt,key} /home/sonarqube/certs/
sudo chown -R sonarqube:sonarqube /home/sonarqube/certs/
sudo chmod 600 /home/sonarqube/certs/*.key
vm.max_map_count obligatoire
Elasticsearch necessite vm.max_map_count >= 262144. Sans ce reglage, SonarQube refuse de démarrer. La valeur 524288 offre une marge confortable.
Initialisation des secrets¶
# Creer le repertoire de secrets
sudo -u sonarqube mkdir -p /home/sonarqube/.secrets
sudo chmod 700 /home/sonarqube/.secrets
# Generer les mots de passe
openssl rand -base64 32 | sudo -u sonarqube tee /home/sonarqube/.secrets/db_password > /dev/null
sudo chmod 600 /home/sonarqube/.secrets/*
Fichier Podman Compose¶
# /home/sonarqube/podman-compose.yml
version: "3.9"
services:
postgres:
image: docker.io/library/postgres:15-alpine
container_name: sonarqube-db
environment:
POSTGRES_DB: sonarqube
POSTGRES_USER: sonarqube
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- sonarqube-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U sonarqube"]
interval: 10s
timeout: 5s
retries: 5
sonarqube:
image: docker.io/library/sonarqube:10-community
container_name: sonarqube
environment:
# Base de donnees
SONAR_JDBC_URL: jdbc:postgresql://postgres:5432/sonarqube
SONAR_JDBC_USERNAME: sonarqube
SONAR_JDBC_PASSWORD_FILE: /run/secrets/db_password
# JVM et Elasticsearch
SONAR_WEB_JAVAOPTS: "-Xmx512m -Xms256m"
SONAR_CE_JAVAOPTS: "-Xmx1024m -Xms512m"
SONAR_SEARCH_JAVAOPTS: "-Xmx1024m -Xms512m"
# Configuration web
SONAR_WEB_PORT: "9000"
SONAR_WEB_CONTEXT: "/"
secrets:
- db_password
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
ports:
- "9000:9000"
networks:
- sonarqube-net
depends_on:
postgres:
condition: service_healthy
ulimits:
nofile:
soft: 131072
hard: 131072
traefik:
image: docker.io/library/traefik:v3.1
container_name: sonarqube-proxy
command:
- "--entrypoints.websecure.address=:443"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
ports:
- "443:443"
volumes:
- /run/user/1001/podman/podman.sock:/var/run/docker.sock:ro
- /home/sonarqube/certs:/certs:ro
networks:
- sonarqube-net
volumes:
pgdata:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
networks:
sonarqube-net:
driver: bridge
secrets:
db_password:
file: /home/sonarqube/.secrets/db_password
Demarrage¶
# Demarrer la stack
sudo -u sonarqube podman-compose -f /home/sonarqube/podman-compose.yml up -d
# Verifier les logs (attendre "SonarQube is operational")
sudo -u sonarqube podman logs -f sonarqube
# Verifier l'acces
curl -s http://localhost:9000/api/system/status | jq .
# Attendu : {"id":"...","version":"10.x","status":"UP"}
Service systemd¶
# /etc/systemd/system/sonarqube.service
[Unit]
Description=SonarQube Quality Analysis Stack
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=sonarqube
WorkingDirectory=/home/sonarqube
ExecStartPre=/usr/bin/podman-compose pull
ExecStart=/usr/bin/podman-compose up
ExecStop=/usr/bin/podman-compose down
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Configuration initiale de SonarQube¶
Premier accès et changement du mot de passe admin¶
# Le compte par defaut est admin/admin
# Changer immediatement le mot de passe
curl -u admin:admin -X POST \
"http://localhost:9000/api/users/change_password" \
-d "login=admin&previousPassword=admin&password=NEW_SECURE_PASSWORD"
Changer le mot de passe admin immédiatement
Le compte par defaut admin:admin est un vecteur d'attaque connu. Le changer des le premier demarrage, avant d'exposer le service sur le réseau.
Configurer le quality gate par defaut¶
# Creer un quality gate personnalise
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create" \
-d "name=Organisation"
# Ajouter les conditions
# Couverture sur nouveau code >= 80%
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create_condition" \
-d "gateName=Organisation&metric=new_coverage&op=LT&error=80"
# Pas de nouveau bug critique ou bloquant
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create_condition" \
-d "gateName=Organisation&metric=new_reliability_rating&op=GT&error=1"
# Pas de nouvelle vulnerabilite critique ou bloquante
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create_condition" \
-d "gateName=Organisation&metric=new_security_rating&op=GT&error=1"
# Duplication sur nouveau code < 3%
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create_condition" \
-d "gateName=Organisation&metric=new_duplicated_lines_density&op=GT&error=3"
# Security hotspots revus
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/create_condition" \
-d "gateName=Organisation&metric=new_security_hotspots_reviewed&op=LT&error=100"
# Definir comme quality gate par defaut
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualitygates/set_as_default" \
-d "name=Organisation"
Recapitulatif du quality gate¶
| Condition | Metrique | Opérateur | Seuil |
|---|---|---|---|
| Couverture nouveau code | new_coverage | < (fail) | 80% |
| Fiabilité nouveau code | new_reliability_rating | > (fail) | A |
| Sécurité nouveau code | new_security_rating | > (fail) | A |
| Duplication nouveau code | new_duplicated_lines_density | > (fail) | 3% |
| Hotspots sécurité revus | new_security_hotspots_reviewed | < (fail) | 100% |
Activer les quality profiles¶
# Lister les profils disponibles pour Java
curl -u admin:TOKEN \
"http://localhost:9000/api/qualityprofiles/search?language=java" | jq .
# Creer un profil enfant de Sonar way
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualityprofiles/create" \
-d "name=Organisation-Java&language=java"
# Heriter de Sonar way
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualityprofiles/change_parent" \
-d "qualityProfile=Organisation-Java&language=java&parentQualityProfile=Sonar way"
# Definir comme profil par defaut
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/qualityprofiles/set_default" \
-d "qualityProfile=Organisation-Java&language=java"
Répéter pour chaque langage
Créer un profil Organisation-<langage> pour chaque langage utilise dans l'organisation. Cela permet d'ajouter des regles custom sans modifier le profil built-in.
Générer un token d'analyse¶
# Token pour le scanner CI
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/user_tokens/generate" \
-d "name=ci-scanner&type=GLOBAL_ANALYSIS_TOKEN"
# Stocker le token dans le gestionnaire de secrets du CI
# Ne jamais le committer dans le code source
Configuration Semgrep CI¶
Installation du CLI¶
# Installation via pip (dans l'image CI ou en local)
pip install semgrep
# Verification
semgrep --version
Fichier de configuration Semgrep¶
# .semgrep.yml (a la racine du depot)
rules:
# Utiliser les rulesets Semgrep officiels
- id: semgrep-rules
match:
# Regles de securite generales
- p/security-audit
# Regles OWASP Top 10
- p/owasp-top-ten
# Regles specifiques au langage
- p/python
- p/java
- p/javascript
Configuration avancee par dépôt¶
# .semgrep/settings.yml
# Configuration partagee pour l'organisation
# Rulesets a activer
rules:
include:
- p/security-audit
- p/owasp-top-ten
- p/secrets
# Regles custom de l'organisation
local:
- .semgrep/rules/
# Fichiers a exclure
paths:
exclude:
- "vendor/"
- "node_modules/"
- "*.test.*"
- "*.spec.*"
- "dist/"
- "build/"
Écrire une regle custom Semgrep¶
# .semgrep/rules/no-hardcoded-secrets.yml
rules:
- id: no-hardcoded-password
patterns:
- pattern: |
password = "..."
- pattern-not: |
password = ""
- pattern-not: |
password = "changeme"
message: >
Mot de passe en dur detecte. Utiliser un gestionnaire de secrets
(Vault, variables d'environnement CI).
severity: ERROR
languages: [python, java, javascript, typescript, go]
metadata:
cwe:
- CWE-798
owasp:
- A07:2021
category: security
Execution locale¶
# Scan rapide du depot
semgrep scan --config auto
# Scan avec regles specifiques
semgrep scan --config p/security-audit --config .semgrep/rules/
# Export SARIF pour import dans SonarQube
semgrep scan --config auto --sarif --output semgrep-results.sarif
Vérification post-installation¶
| Vérification | Commande / URL | Résultat attendu |
|---|---|---|
| Statut SonarQube | curl -s http://localhost:9000/api/system/status | {"status": "UP"} |
| Health check | curl -s http://localhost:9000/api/system/health | {"health": "GREEN"} |
| Dashboard | https://sonar.example.com | Page de login |
| Quality gate | curl -s http://localhost:9000/api/qualitygates/list | Quality gate "Organisation" |
| Semgrep version | semgrep --version | Version installee |
| Semgrep scan test | semgrep scan --config auto --dry-run | Liste des regles chargees |
# Test d'analyse complet
# 1. Creer un projet de test
curl -u admin:TOKEN -X POST \
"http://localhost:9000/api/projects/create" \
-d "name=test-project&project=test-project"
# 2. Lancer un scan
cd /chemin/vers/un/projet
sonar-scanner \
-Dsonar.projectKey=test-project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.token=YOUR_TOKEN
# 3. Verifier le resultat
curl -u admin:TOKEN \
"http://localhost:9000/api/qualitygates/project_status?projectKey=test-project"