Skip to content
Open
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ out/
_site/
*.css
!petclinic.css

### Docker ###
*.tar
124 changes: 124 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
pipeline {
agent any

environment {
SONAR_TOKEN = credentials('sonarqube-token')
ANSIBLE_HOST_KEY_CHECKING = 'False'
}

triggers {
pollSCM('H/5 * * * *')
}

tools {
maven 'Maven'
jdk 'JDK17'
}

stages {

stage('Checkout') {
steps {
checkout scm
}
}

stage('Build') {
steps {
sh './mvnw clean package -DskipTests'
}
}

stage('Unit Tests') {
steps {
sh './mvnw test -Pskip-db-tests'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}

stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh '''
chmod +x mvnw || true
./mvnw sonar:sonar \
-Dsonar.projectKey=spring-petclinic \
-Dsonar.host.url=http://sonarqube:9000 \
-Dsonar.login=${SONAR_TOKEN}
'''
}
}
}

stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}

stage('Burp Suite Scan') {
steps {
script {
sh '''
echo "Sending traffic through Burp proxy..."
curl --proxy http://burpsuite:8080 \
--insecure \
http://jenkins:9966/ || true

curl --proxy http://burpsuite:8080 \
--insecure \
http://jenkins:9966/owners || true

curl --proxy http://burpsuite:8080 \
--insecure \
http://jenkins:9966/vets.html || true

echo "Traffic sent to Burp proxy successfully"
'''
}
}
post {
always {
// Publish whatever report Burp generates
publishHTML(target: [
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'burp-reports',
reportFiles: 'burp_report.html',
reportName: 'Burp Suite Report'
])
}
}
}
stage('Deploy to Production (Ansible)') {
steps {
sshagent(['ansible-ssh-key']) {
sh '''
ansible-playbook -i ansible/inventory.ini \
ansible/deploy.yml \
--extra-vars "artifact_path=${WORKSPACE}/target/spring-petclinic-*.jar"
'''
}
}
}
}

post {
always {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed!'
}
}
}
92 changes: 92 additions & 0 deletions docker-compose.devsecops.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
version: '3.8'

networks:
devsecops-net:
external: true

volumes:
jenkins_home:
sonarqube_data:
sonarqube_logs:
sonarqube_extensions:
prometheus_data:
grafana_data:
sonarqube_db_data:

services:

jenkins:
image: minghaohan/devops-team8-jenkins
container_name: jenkins
privileged: true
user: root
ports:
- "8081:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
networks:
- devsecops-net

sonarqube:
image: sonarqube:community
container_name: sonarqube
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://sonarqube-db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
- sonarqube_extensions:/opt/sonarqube/extensions
networks:
- devsecops-net
depends_on:
- sonarqube-db

sonarqube-db:
image: postgres:15
container_name: sonarqube-db
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- sonarqube_db_data:/var/lib/postgresql/data
networks:
- devsecops-net

prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
networks:
- devsecops-net

grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
networks:
- devsecops-net

burpsuite:
image: soham/burpsuite
container_name: burpsuite
ports:
- "8082:8080"
networks:
- devsecops-net
47 changes: 47 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
# services:
# mysql:
# image: mysql:9.6
# ports:
# - "3306:3306"
# environment:
# - MYSQL_ROOT_PASSWORD=
# - MYSQL_ALLOW_EMPTY_PASSWORD=true
# - MYSQL_USER=petclinic
# - MYSQL_PASSWORD=petclinic
# - MYSQL_DATABASE=petclinic
# volumes:
# - "./conf.d:/etc/mysql/conf.d:ro"
# postgres:
# image: postgres:18.3
# ports:
# - "5432:5432"
# environment:
# - POSTGRES_PASSWORD=petclinic
# - POSTGRES_USER=petclinic
# - POSTGRES_DB=petclinic



##############################################################

services:
mysql:
image: mysql:9.6
Expand All @@ -11,6 +37,9 @@ services:
- MYSQL_DATABASE=petclinic
volumes:
- "./conf.d:/etc/mysql/conf.d:ro"
networks:
- devsecops-net

postgres:
image: postgres:18.3
ports:
Expand All @@ -19,3 +48,21 @@ services:
- POSTGRES_PASSWORD=petclinic
- POSTGRES_USER=petclinic
- POSTGRES_DB=petclinic
networks:
- devsecops-net

zap:
image: ghcr.io/zaproxy/zaproxy:stable
container_name: zap
networks:
- devsecops-net
volumes:
- zap-reports:/zap/wrk
command: sleep infinity

networks:
devsecops-net:
external: true

volumes:
zap-reports:
17 changes: 17 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,23 @@
</licenses>

<profiles>
<profile>
<id>skip-db-tests</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/MySqlIntegrationTests.java</exclude>
<exclude>**/PostgresIntegrationTests.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>css</id>
<build>
Expand Down
13 changes: 13 additions & 0 deletions prometheus/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
global:
scrape_interval: 15s

scrape_configs:
- job_name: 'jenkins'
metrics_path: /prometheus
static_configs:
- targets: ['jenkins:8080']

- job_name: 'spring-petclinic'
metrics_path: /actuator/prometheus
static_configs:
- targets: ['10.0.0.50:8080']