Introduction
In this article, we’ll explore how to set up a complete DevSecOps environment on a local machine. This environment will enable to test and experiment with various DevSecOps practices that are essential for the modern development of secure applications.
Why a local DevSecOps environment ?
A local environment offers a number of advantages :
- Risk-free experimentation
- Speed of development and testing
- Configurations can be tested before being deployed in production
- Practical learning of DevSecOps tools and practices
Prerequisites
Recommended hardware configuration
- CPU : minimun 4 cores
- RAM : minimum 16 GB
- Disk space : minimum 50 GB free
- Operating system : Linux (Ubuntu/Debian recommended)
Required software
- Git
- Docker
- Docker Compose
- Administrator access (sudo)
Step-by-step installation
- Preparating system
Let’s start by updating the system and installing the necessary prerequisites :
1
2
sudo apt update && sudo apt upgrade -y
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common git
2. Installing Docker and Docker Compose
1
2
3
4
5
6
7
8
9
10
11
12
13
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo usermod -aG docker $USER
sudo systemctl enable docker
sudo systemctl start docker
3. Creating the project structure
1
2
mkdir -p devsecops-env/{jenkins,sonarqube,zap,monitoring,gitleaks,trivy}
cd devsecops-env
First, let’s create the network manually so that our containers (grafana, prometheus, sonarqube and jenkins) can be visible to each other on the same network.
1
docker network create monitoring
4. Service configuration
4.1. Jenkins (CI/CD)
Create a jenkins/docker-compose.yml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3'
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=true
networks:
- monitoring
restart: unless-stopped
volumes:
jenkins_home:
networks:
monitoring:
external: true
4.2. SonarQube (Code analysis)
Create a sonarqube/docker-composer.yaml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
version: '3'
services:
sonarqube:
image: sonarqube:latest
container_name: sonarqube
ports:
- "9000:9000"
- "8081:8081"
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
- ./jmx_exporter:/opt/jmx_exporter
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
- SONAR_WEB_JAVAADDITIONALOPTS=-javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent.jar=8081:/opt/jmx_exporter/config.yaml
depends_on:
- db
networks:
- monitoring
restart: unless-stopped
db:
image: postgres:12
container_name: sonarqube_db
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
networks:
- monitoring
restart: unless-stopped
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
networks:
monitoring:
external: true
Before launching this configuration, you need to :
- Create the jmx_exporter folder and download the agent
1
2
mkdir jmx_exporter
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar -O jmx_exporter/jmx_prometheus_javaagent.jar
2. Create the configuration file jmx_exporter/config.yaml
1
2
3
4
5
---
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
- pattern: ".*"
4.3. OWASP ZAP (Safety test)
Create a zap/docker-composer.yaml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '3'
services:
zap:
image: ghcr.io/zaproxy/zaproxy:stable
container_name: owasp_zap
command: zap.sh -daemon -host 0.0.0.0 -port 8090 -config api.addrs.addr.name=.* -config api.addrs.addr.regex=true -config api.key=changeme123
ports:
- "8090:8090"
volumes:
- zap_data:/zap/wrk/
restart: unless-stopped
volumes:
zap_data:
4.4. Trivy (Container vulnerability analysis)
Create a trivy/docker-composer.yaml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
version: '3'
services:
trivy:
image: aquasec/trivy:latest
container_name: trivy
volumes:
- trivy_cache:/root/.cache
- /var/run/docker.sock:/var/run/docker.sock
environment:
- TRIVY_CACHE_DIR=/root/.cache/
- GITHUB_TOKEN=${GITHUB_TOKEN} # Optional : To avoid the limitation of the GitHub API
command: server --listen 0.0.0.0:4954
ports:
- "4954:4954"
restart: unless-stopped
volumes:
trivy_cache:
To use Trivy directly from the command line
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Trivy installation
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
# Examples of how Trivy is used
# Scan an image
trivy image nginx:latest
# Scanning an image with a JSON output format
trivy image -f json -o results.json nginx:latest
# Scanning an image with critical vulnerability filtering
trivy image --severity CRITICAL nginx:latest
# Scan a local project
trivy fs .
4.5. Monitoring (Prometheus + Grafana)
Create a monitoring/docker-composer.yaml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: '3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
networks:
- monitoring
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- prometheus
networks:
- monitoring
restart: unless-stopped
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
external: true
Create a monitoring/prometheus.yml file :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'jenkins'
metrics_path: '/prometheus'
static_configs:
- targets: ['jenkins:8080']
- job_name: 'sonarqube'
static_configs:
- targets: ['sonarqube:8081']
basic_auth:
username: 'admin'
password: 'admin'
5. Service management script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/bash
check_service() {
if [ $? -eq 0 ]; then
echo "$1 successfully launched"
else
echo "Error when starting $1"
exit 1
fi
}
start_services() {
echo "Starting services..."
cd jenkins
docker-compose up -d
check_service "Jenkins"
cd ../sonarqube
docker-compose up -d
check_service "SonarQube"
cd ../zap
docker-compose up -d
check_service "OWASP ZAP"
cd ../monitoring
docker-compose up -d
check_service "Monitoring Stack"
cd ..
echo "All services are started"
echo "
Services available :
- Jenkins: http://localhost:8080
- SonarQube: http://localhost:9000
- OWASP ZAP: http://localhost:8090
- Grafana: http://localhost:3000
- Prometheus: http://localhost:9090
- Trivy Server: http://localhost:4954
"
}
stop_services() {
echo "Stopping services..."
cd jenkins && docker-compose down
cd ../sonarqube && docker-compose down
cd ../zap && docker-compose down
cd ../trivy && docker-compose down
cd ../monitoring && docker-compose down
cd ..
echo "All services stopped"
}
case "$1" in
start)
start_services
;;
stop)
stop_services
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
Make the script executable :
1
chmod +x manage-services.sh
Post-installation configuration
Jenkins
- Recover the initial password
1
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Installing the prometheus plugin for Jenkins
Go to http://127.0.0.1:8080 and follow the installation wizard
Installing the plugin :
- Go to ‘Manage Jenkins’ > ‘Plugins’ > ‘Available Plugins’.
- Search for ‘Prometheus metrics
- Install the plugin and restart Jenkins if necessary
3. Accès aux métriques : http://127.0.0.1:8080/prometheus/
SonarQube
- Go to http:127.0.0.1:9000(default login : admin/admin)
- Change the admin password
- Create a new and generate an authentication token
Grafana
- Go to http://127.0.0.1:3000(default login : admin/admin)
- Add prometheus as a datasource
- Import a preconfigured dashboards for jenkins and SonarQube
Prometheus
- Go to http://127.0.0.1:9090/target
OWAPS ZAP
- Go to http://127.0.0.1:8090